123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428 |
- #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 */
|