1
This commit is contained in:
428
Pods/LookinServer/Src/Main/Shared/Peertalk/Lookin_PTProtocol.m
generated
Normal file
428
Pods/LookinServer/Src/Main/Shared/Peertalk/Lookin_PTProtocol.m
generated
Normal file
@@ -0,0 +1,428 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
#import "Lookin_PTProtocol.h"
|
||||
#import "Lookin_PTPrivate.h"
|
||||
#import <objc/runtime.h>
|
||||
|
||||
static const uint32_t PTProtocolVersion1 = 1;
|
||||
|
||||
NSString * const Lookin_PTProtocolErrorDomain = @"PTProtocolError";
|
||||
|
||||
// This is what we send as the header for each frame.
|
||||
typedef struct _PTFrame {
|
||||
// The version of the frame and protocol.
|
||||
uint32_t version;
|
||||
|
||||
// Type of frame
|
||||
uint32_t type;
|
||||
|
||||
// Unless zero, a tag is retained in frames that are responses to previous
|
||||
// frames. Applications can use this to build transactions or request-response
|
||||
// logic.
|
||||
uint32_t tag;
|
||||
|
||||
// If payloadSize is larger than zero, *payloadSize* number of bytes are
|
||||
// following, constituting application-specific data.
|
||||
uint32_t payloadSize;
|
||||
|
||||
} PTFrame;
|
||||
|
||||
|
||||
@interface Lookin_PTProtocol () {
|
||||
uint32_t nextFrameTag_;
|
||||
@public
|
||||
dispatch_queue_t queue_;
|
||||
}
|
||||
- (dispatch_data_t)createDispatchDataWithFrameOfType:(uint32_t)type frameTag:(uint32_t)frameTag payload:(dispatch_data_t)payload;
|
||||
@end
|
||||
|
||||
|
||||
static void _release_queue_local_protocol(void *objcobj) {
|
||||
if (objcobj) {
|
||||
Lookin_PTProtocol *protocol = (__bridge_transfer id)objcobj;
|
||||
protocol->queue_ = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@interface Lookin_RQueueLocalIOFrameProtocol : Lookin_PTProtocol
|
||||
@end
|
||||
@implementation Lookin_RQueueLocalIOFrameProtocol
|
||||
- (void)setQueue:(dispatch_queue_t)queue {
|
||||
}
|
||||
@end
|
||||
|
||||
|
||||
@implementation Lookin_PTProtocol
|
||||
|
||||
|
||||
+ (Lookin_PTProtocol*)sharedProtocolForQueue:(dispatch_queue_t)queue {
|
||||
static const char currentQueueFrameProtocolKey;
|
||||
//dispatch_queue_t queue = dispatch_get_current_queue();
|
||||
Lookin_PTProtocol *currentQueueFrameProtocol = (__bridge Lookin_PTProtocol*)dispatch_queue_get_specific(queue, ¤tQueueFrameProtocolKey);
|
||||
if (!currentQueueFrameProtocol) {
|
||||
currentQueueFrameProtocol = [[Lookin_RQueueLocalIOFrameProtocol alloc] initWithDispatchQueue:NULL];
|
||||
currentQueueFrameProtocol->queue_ = queue; // reference, no retain, since we would create cyclic references
|
||||
dispatch_queue_set_specific(queue, ¤tQueueFrameProtocolKey, (__bridge_retained void*)currentQueueFrameProtocol, &_release_queue_local_protocol);
|
||||
return (__bridge Lookin_PTProtocol*)dispatch_queue_get_specific(queue, ¤tQueueFrameProtocolKey); // to avoid race conds
|
||||
} else {
|
||||
return currentQueueFrameProtocol;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- (id)initWithDispatchQueue:(dispatch_queue_t)queue {
|
||||
if (!(self = [super init])) return nil;
|
||||
queue_ = queue;
|
||||
#if PT_DISPATCH_RETAIN_RELEASE
|
||||
if (queue_) dispatch_retain(queue_);
|
||||
#endif
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)init {
|
||||
return [self initWithDispatchQueue:dispatch_get_main_queue()];
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
if (queue_) {
|
||||
#if PT_DISPATCH_RETAIN_RELEASE
|
||||
dispatch_release(queue_);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
- (dispatch_queue_t)queue {
|
||||
return queue_;
|
||||
}
|
||||
|
||||
- (void)setQueue:(dispatch_queue_t)queue {
|
||||
#if PT_DISPATCH_RETAIN_RELEASE
|
||||
dispatch_queue_t prev_queue = queue_;
|
||||
queue_ = queue;
|
||||
if (queue_) dispatch_retain(queue_);
|
||||
if (prev_queue) dispatch_release(prev_queue);
|
||||
#else
|
||||
queue_ = queue;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
- (uint32_t)newTag {
|
||||
return ++nextFrameTag_;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Creating frames
|
||||
|
||||
|
||||
- (dispatch_data_t)createDispatchDataWithFrameOfType:(uint32_t)type frameTag:(uint32_t)frameTag payload:(dispatch_data_t)payload {
|
||||
PTFrame *frame = CFAllocatorAllocate(kCFAllocatorDefault, sizeof(PTFrame), 0);
|
||||
frame->version = htonl(PTProtocolVersion1);
|
||||
frame->type = htonl(type);
|
||||
frame->tag = htonl(frameTag);
|
||||
|
||||
if (payload) {
|
||||
size_t payloadSize = dispatch_data_get_size(payload);
|
||||
assert(payloadSize <= UINT32_MAX);
|
||||
frame->payloadSize = htonl((uint32_t)payloadSize);
|
||||
} else {
|
||||
frame->payloadSize = 0;
|
||||
}
|
||||
|
||||
dispatch_data_t frameData = dispatch_data_create((const void*)frame, sizeof(PTFrame), queue_, ^{
|
||||
CFAllocatorDeallocate(kCFAllocatorDefault, (void*)frame);
|
||||
});
|
||||
|
||||
if (payload && frame->payloadSize != 0) {
|
||||
// chain frame + payload
|
||||
dispatch_data_t data = dispatch_data_create_concat(frameData, payload);
|
||||
#if PT_DISPATCH_RETAIN_RELEASE
|
||||
dispatch_release(frameData);
|
||||
#endif
|
||||
frameData = data;
|
||||
}
|
||||
|
||||
return frameData;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Sending frames
|
||||
|
||||
|
||||
- (void)sendFrameOfType:(uint32_t)frameType tag:(uint32_t)tag withPayload:(dispatch_data_t)payload overChannel:(dispatch_io_t)channel callback:(void(^)(NSError*))callback {
|
||||
dispatch_data_t frame = [self createDispatchDataWithFrameOfType:frameType frameTag:tag payload:payload];
|
||||
dispatch_io_write(channel, 0, frame, queue_, ^(bool done, dispatch_data_t data, int _errno) {
|
||||
if (done && callback) {
|
||||
callback(_errno == 0 ? nil : [[NSError alloc] initWithDomain:NSPOSIXErrorDomain code:_errno userInfo:nil]);
|
||||
}
|
||||
});
|
||||
#if PT_DISPATCH_RETAIN_RELEASE
|
||||
dispatch_release(frame);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Receiving frames
|
||||
|
||||
|
||||
- (void)readFrameOverChannel:(dispatch_io_t)channel callback:(void(^)(NSError *error, uint32_t frameType, uint32_t frameTag, uint32_t payloadSize))callback {
|
||||
__block dispatch_data_t allData = NULL;
|
||||
|
||||
dispatch_io_read(channel, 0, sizeof(PTFrame), queue_, ^(bool done, dispatch_data_t data, int error) {
|
||||
//NSLog(@"dispatch_io_read: done=%d data=%p error=%d", done, data, error);
|
||||
size_t dataSize = data ? dispatch_data_get_size(data) : 0;
|
||||
|
||||
if (dataSize) {
|
||||
if (!allData) {
|
||||
allData = data;
|
||||
#if PT_DISPATCH_RETAIN_RELEASE
|
||||
dispatch_retain(allData);
|
||||
#endif
|
||||
} else {
|
||||
#if PT_DISPATCH_RETAIN_RELEASE
|
||||
dispatch_data_t allDataPrev = allData;
|
||||
allData = dispatch_data_create_concat(allData, data);
|
||||
dispatch_release(allDataPrev);
|
||||
#else
|
||||
allData = dispatch_data_create_concat(allData, data);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if (done) {
|
||||
if (error != 0) {
|
||||
callback([[NSError alloc] initWithDomain:NSPOSIXErrorDomain code:error userInfo:nil], 0, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (dataSize == 0) {
|
||||
callback(nil, PTFrameTypeEndOfStream, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!allData || dispatch_data_get_size(allData) < sizeof(PTFrame)) {
|
||||
#if PT_DISPATCH_RETAIN_RELEASE
|
||||
if (allData) dispatch_release(allData);
|
||||
#endif
|
||||
callback([[NSError alloc] initWithDomain:Lookin_PTProtocolErrorDomain code:0 userInfo:nil], 0, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
PTFrame *frame = NULL;
|
||||
size_t size = 0;
|
||||
|
||||
PT_PRECISE_LIFETIME dispatch_data_t contiguousData = dispatch_data_create_map(allData, (const void **)&frame, &size); // precise lifetime guarantees bytes in frame will stay valid till the end of scope
|
||||
#if PT_DISPATCH_RETAIN_RELEASE
|
||||
dispatch_release(allData);
|
||||
#endif
|
||||
if (!contiguousData) {
|
||||
callback([[NSError alloc] initWithDomain:NSPOSIXErrorDomain code:ENOMEM userInfo:nil], 0, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
frame->version = ntohl(frame->version);
|
||||
if (frame->version != PTProtocolVersion1) {
|
||||
callback([[NSError alloc] initWithDomain:Lookin_PTProtocolErrorDomain code:0 userInfo:nil], 0, 0, 0);
|
||||
} else {
|
||||
frame->type = ntohl(frame->type);
|
||||
frame->tag = ntohl(frame->tag);
|
||||
frame->payloadSize = ntohl(frame->payloadSize);
|
||||
callback(nil, frame->type, frame->tag, frame->payloadSize);
|
||||
}
|
||||
|
||||
#if PT_DISPATCH_RETAIN_RELEASE
|
||||
dispatch_release(contiguousData);
|
||||
#endif
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
- (void)readPayloadOfSize:(size_t)payloadSize overChannel:(dispatch_io_t)channel callback:(void(^)(NSError *error, dispatch_data_t contiguousData, const uint8_t *buffer, size_t bufferSize))callback {
|
||||
__block dispatch_data_t allData = NULL;
|
||||
dispatch_io_read(channel, 0, payloadSize, queue_, ^(bool done, dispatch_data_t data, int error) {
|
||||
//NSLog(@"dispatch_io_read: done=%d data=%p error=%d", done, data, error);
|
||||
size_t dataSize = dispatch_data_get_size(data);
|
||||
|
||||
if (dataSize) {
|
||||
if (!allData) {
|
||||
allData = data;
|
||||
#if PT_DISPATCH_RETAIN_RELEASE
|
||||
dispatch_retain(allData);
|
||||
#endif
|
||||
} else {
|
||||
#if PT_DISPATCH_RETAIN_RELEASE
|
||||
dispatch_data_t allDataPrev = allData;
|
||||
allData = dispatch_data_create_concat(allData, data);
|
||||
dispatch_release(allDataPrev);
|
||||
#else
|
||||
allData = dispatch_data_create_concat(allData, data);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if (done) {
|
||||
if (error != 0) {
|
||||
#if PT_DISPATCH_RETAIN_RELEASE
|
||||
if (allData) dispatch_release(allData);
|
||||
#endif
|
||||
callback([[NSError alloc] initWithDomain:NSPOSIXErrorDomain code:error userInfo:nil], NULL, NULL, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (dataSize == 0) {
|
||||
#if PT_DISPATCH_RETAIN_RELEASE
|
||||
if (allData) dispatch_release(allData);
|
||||
#endif
|
||||
callback(nil, NULL, NULL, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t *buffer = NULL;
|
||||
size_t bufferSize = 0;
|
||||
PT_PRECISE_LIFETIME dispatch_data_t contiguousData = NULL;
|
||||
|
||||
if (allData) {
|
||||
contiguousData = dispatch_data_create_map(allData, (const void **)&buffer, &bufferSize);
|
||||
#if PT_DISPATCH_RETAIN_RELEASE
|
||||
dispatch_release(allData); allData = NULL;
|
||||
#endif
|
||||
if (!contiguousData) {
|
||||
callback([[NSError alloc] initWithDomain:NSPOSIXErrorDomain code:ENOMEM userInfo:nil], NULL, NULL, 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
callback(nil, contiguousData, buffer, bufferSize);
|
||||
#if PT_DISPATCH_RETAIN_RELEASE
|
||||
if (contiguousData) dispatch_release(contiguousData);
|
||||
#endif
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
- (void)readAndDiscardDataOfSize:(size_t)size overChannel:(dispatch_io_t)channel callback:(void(^)(NSError*, BOOL))callback {
|
||||
dispatch_io_read(channel, 0, size, queue_, ^(bool done, dispatch_data_t data, int error) {
|
||||
if (done && callback) {
|
||||
size_t dataSize = data ? dispatch_data_get_size(data) : 0;
|
||||
callback(error == 0 ? nil : [[NSError alloc] initWithDomain:NSPOSIXErrorDomain code:error userInfo:nil], dataSize == 0);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
- (void)readFramesOverChannel:(dispatch_io_t)channel onFrame:(void(^)(NSError*, uint32_t, uint32_t, uint32_t, dispatch_block_t))onFrame {
|
||||
[self readFrameOverChannel:channel callback:^(NSError *error, uint32_t type, uint32_t tag, uint32_t payloadSize) {
|
||||
onFrame(error, type, tag, payloadSize, ^{
|
||||
if (type != PTFrameTypeEndOfStream) {
|
||||
[self readFramesOverChannel:channel onFrame:onFrame];
|
||||
}
|
||||
});
|
||||
}];
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface Lookin_PTDispatchData : NSObject {
|
||||
dispatch_data_t dispatchData_;
|
||||
}
|
||||
@end
|
||||
@implementation Lookin_PTDispatchData
|
||||
- (id)initWithDispatchData:(dispatch_data_t)dispatchData {
|
||||
if (!(self = [super init])) return nil;
|
||||
dispatchData_ = dispatchData;
|
||||
#if PT_DISPATCH_RETAIN_RELEASE
|
||||
dispatch_retain(dispatchData_);
|
||||
#endif
|
||||
return self;
|
||||
}
|
||||
- (void)dealloc {
|
||||
#if PT_DISPATCH_RETAIN_RELEASE
|
||||
if (dispatchData_) dispatch_release(dispatchData_);
|
||||
#endif
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation NSData (Lookin_PTProtocol)
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunused-getter-return-value"
|
||||
|
||||
- (dispatch_data_t)createReferencingDispatchData {
|
||||
// Note: The queue is used to submit the destructor. Since we only perform an
|
||||
// atomic release of self, it doesn't really matter which queue is used, thus
|
||||
// we use the current calling queue.
|
||||
return dispatch_data_create((const void*)self.bytes, self.length, dispatch_get_main_queue(), ^{
|
||||
// trick to have the block capture the data, thus retain/releasing
|
||||
[self length];
|
||||
});
|
||||
}
|
||||
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
+ (NSData *)dataWithContentsOfDispatchData:(dispatch_data_t)data {
|
||||
if (!data) {
|
||||
return nil;
|
||||
}
|
||||
uint8_t *buffer = NULL;
|
||||
size_t bufferSize = 0;
|
||||
PT_PRECISE_LIFETIME dispatch_data_t contiguousData = dispatch_data_create_map(data, (const void **)&buffer, &bufferSize);
|
||||
if (!contiguousData) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
Lookin_PTDispatchData *dispatchDataRef = [[Lookin_PTDispatchData alloc] initWithDispatchData:contiguousData];
|
||||
NSData *newData = [NSData dataWithBytesNoCopy:(void*)buffer length:bufferSize freeWhenDone:NO];
|
||||
#if PT_DISPATCH_RETAIN_RELEASE
|
||||
dispatch_release(contiguousData);
|
||||
#endif
|
||||
static const bool kDispatchDataRefKey;
|
||||
objc_setAssociatedObject(newData, (const void*)kDispatchDataRefKey, dispatchDataRef, OBJC_ASSOCIATION_RETAIN);
|
||||
|
||||
return newData;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation NSDictionary (Lookin_PTProtocol)
|
||||
|
||||
- (dispatch_data_t)createReferencingDispatchData {
|
||||
NSError *error = nil;
|
||||
NSData *plistData = [NSPropertyListSerialization dataWithPropertyList:self format:NSPropertyListBinaryFormat_v1_0 options:0 error:&error];
|
||||
if (!plistData) {
|
||||
NSLog(@"Failed to serialize property list: %@", error);
|
||||
return nil;
|
||||
} else {
|
||||
return [plistData createReferencingDispatchData];
|
||||
}
|
||||
}
|
||||
|
||||
// Decode *data* as a peroperty list-encoded dictionary. Returns nil on failure.
|
||||
+ (NSDictionary*)dictionaryWithContentsOfDispatchData:(dispatch_data_t)data {
|
||||
if (!data) {
|
||||
return nil;
|
||||
}
|
||||
uint8_t *buffer = NULL;
|
||||
size_t bufferSize = 0;
|
||||
PT_PRECISE_LIFETIME dispatch_data_t contiguousData = dispatch_data_create_map(data, (const void **)&buffer, &bufferSize);
|
||||
if (!contiguousData) {
|
||||
return nil;
|
||||
}
|
||||
NSDictionary *dict = [NSPropertyListSerialization propertyListWithData:[NSData dataWithBytesNoCopy:(void*)buffer length:bufferSize freeWhenDone:NO] options:NSPropertyListImmutable format:NULL error:nil];
|
||||
#if PT_DISPATCH_RETAIN_RELEASE
|
||||
dispatch_release(contiguousData);
|
||||
#endif
|
||||
return dict;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
Reference in New Issue
Block a user