Lookin_PTProtocol.m 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428
  1. #ifdef SHOULD_COMPILE_LOOKIN_SERVER
  2. #import "Lookin_PTProtocol.h"
  3. #import "Lookin_PTPrivate.h"
  4. #import <objc/runtime.h>
  5. static const uint32_t PTProtocolVersion1 = 1;
  6. NSString * const Lookin_PTProtocolErrorDomain = @"PTProtocolError";
  7. // This is what we send as the header for each frame.
  8. typedef struct _PTFrame {
  9. // The version of the frame and protocol.
  10. uint32_t version;
  11. // Type of frame
  12. uint32_t type;
  13. // Unless zero, a tag is retained in frames that are responses to previous
  14. // frames. Applications can use this to build transactions or request-response
  15. // logic.
  16. uint32_t tag;
  17. // If payloadSize is larger than zero, *payloadSize* number of bytes are
  18. // following, constituting application-specific data.
  19. uint32_t payloadSize;
  20. } PTFrame;
  21. @interface Lookin_PTProtocol () {
  22. uint32_t nextFrameTag_;
  23. @public
  24. dispatch_queue_t queue_;
  25. }
  26. - (dispatch_data_t)createDispatchDataWithFrameOfType:(uint32_t)type frameTag:(uint32_t)frameTag payload:(dispatch_data_t)payload;
  27. @end
  28. static void _release_queue_local_protocol(void *objcobj) {
  29. if (objcobj) {
  30. Lookin_PTProtocol *protocol = (__bridge_transfer id)objcobj;
  31. protocol->queue_ = NULL;
  32. }
  33. }
  34. @interface Lookin_RQueueLocalIOFrameProtocol : Lookin_PTProtocol
  35. @end
  36. @implementation Lookin_RQueueLocalIOFrameProtocol
  37. - (void)setQueue:(dispatch_queue_t)queue {
  38. }
  39. @end
  40. @implementation Lookin_PTProtocol
  41. + (Lookin_PTProtocol*)sharedProtocolForQueue:(dispatch_queue_t)queue {
  42. static const char currentQueueFrameProtocolKey;
  43. //dispatch_queue_t queue = dispatch_get_current_queue();
  44. Lookin_PTProtocol *currentQueueFrameProtocol = (__bridge Lookin_PTProtocol*)dispatch_queue_get_specific(queue, &currentQueueFrameProtocolKey);
  45. if (!currentQueueFrameProtocol) {
  46. currentQueueFrameProtocol = [[Lookin_RQueueLocalIOFrameProtocol alloc] initWithDispatchQueue:NULL];
  47. currentQueueFrameProtocol->queue_ = queue; // reference, no retain, since we would create cyclic references
  48. dispatch_queue_set_specific(queue, &currentQueueFrameProtocolKey, (__bridge_retained void*)currentQueueFrameProtocol, &_release_queue_local_protocol);
  49. return (__bridge Lookin_PTProtocol*)dispatch_queue_get_specific(queue, &currentQueueFrameProtocolKey); // to avoid race conds
  50. } else {
  51. return currentQueueFrameProtocol;
  52. }
  53. }
  54. - (id)initWithDispatchQueue:(dispatch_queue_t)queue {
  55. if (!(self = [super init])) return nil;
  56. queue_ = queue;
  57. #if PT_DISPATCH_RETAIN_RELEASE
  58. if (queue_) dispatch_retain(queue_);
  59. #endif
  60. return self;
  61. }
  62. - (id)init {
  63. return [self initWithDispatchQueue:dispatch_get_main_queue()];
  64. }
  65. - (void)dealloc {
  66. if (queue_) {
  67. #if PT_DISPATCH_RETAIN_RELEASE
  68. dispatch_release(queue_);
  69. #endif
  70. }
  71. }
  72. - (dispatch_queue_t)queue {
  73. return queue_;
  74. }
  75. - (void)setQueue:(dispatch_queue_t)queue {
  76. #if PT_DISPATCH_RETAIN_RELEASE
  77. dispatch_queue_t prev_queue = queue_;
  78. queue_ = queue;
  79. if (queue_) dispatch_retain(queue_);
  80. if (prev_queue) dispatch_release(prev_queue);
  81. #else
  82. queue_ = queue;
  83. #endif
  84. }
  85. - (uint32_t)newTag {
  86. return ++nextFrameTag_;
  87. }
  88. #pragma mark -
  89. #pragma mark Creating frames
  90. - (dispatch_data_t)createDispatchDataWithFrameOfType:(uint32_t)type frameTag:(uint32_t)frameTag payload:(dispatch_data_t)payload {
  91. PTFrame *frame = CFAllocatorAllocate(kCFAllocatorDefault, sizeof(PTFrame), 0);
  92. frame->version = htonl(PTProtocolVersion1);
  93. frame->type = htonl(type);
  94. frame->tag = htonl(frameTag);
  95. if (payload) {
  96. size_t payloadSize = dispatch_data_get_size(payload);
  97. assert(payloadSize <= UINT32_MAX);
  98. frame->payloadSize = htonl((uint32_t)payloadSize);
  99. } else {
  100. frame->payloadSize = 0;
  101. }
  102. dispatch_data_t frameData = dispatch_data_create((const void*)frame, sizeof(PTFrame), queue_, ^{
  103. CFAllocatorDeallocate(kCFAllocatorDefault, (void*)frame);
  104. });
  105. if (payload && frame->payloadSize != 0) {
  106. // chain frame + payload
  107. dispatch_data_t data = dispatch_data_create_concat(frameData, payload);
  108. #if PT_DISPATCH_RETAIN_RELEASE
  109. dispatch_release(frameData);
  110. #endif
  111. frameData = data;
  112. }
  113. return frameData;
  114. }
  115. #pragma mark -
  116. #pragma mark Sending frames
  117. - (void)sendFrameOfType:(uint32_t)frameType tag:(uint32_t)tag withPayload:(dispatch_data_t)payload overChannel:(dispatch_io_t)channel callback:(void(^)(NSError*))callback {
  118. dispatch_data_t frame = [self createDispatchDataWithFrameOfType:frameType frameTag:tag payload:payload];
  119. dispatch_io_write(channel, 0, frame, queue_, ^(bool done, dispatch_data_t data, int _errno) {
  120. if (done && callback) {
  121. callback(_errno == 0 ? nil : [[NSError alloc] initWithDomain:NSPOSIXErrorDomain code:_errno userInfo:nil]);
  122. }
  123. });
  124. #if PT_DISPATCH_RETAIN_RELEASE
  125. dispatch_release(frame);
  126. #endif
  127. }
  128. #pragma mark -
  129. #pragma mark Receiving frames
  130. - (void)readFrameOverChannel:(dispatch_io_t)channel callback:(void(^)(NSError *error, uint32_t frameType, uint32_t frameTag, uint32_t payloadSize))callback {
  131. __block dispatch_data_t allData = NULL;
  132. dispatch_io_read(channel, 0, sizeof(PTFrame), queue_, ^(bool done, dispatch_data_t data, int error) {
  133. //NSLog(@"dispatch_io_read: done=%d data=%p error=%d", done, data, error);
  134. size_t dataSize = data ? dispatch_data_get_size(data) : 0;
  135. if (dataSize) {
  136. if (!allData) {
  137. allData = data;
  138. #if PT_DISPATCH_RETAIN_RELEASE
  139. dispatch_retain(allData);
  140. #endif
  141. } else {
  142. #if PT_DISPATCH_RETAIN_RELEASE
  143. dispatch_data_t allDataPrev = allData;
  144. allData = dispatch_data_create_concat(allData, data);
  145. dispatch_release(allDataPrev);
  146. #else
  147. allData = dispatch_data_create_concat(allData, data);
  148. #endif
  149. }
  150. }
  151. if (done) {
  152. if (error != 0) {
  153. callback([[NSError alloc] initWithDomain:NSPOSIXErrorDomain code:error userInfo:nil], 0, 0, 0);
  154. return;
  155. }
  156. if (dataSize == 0) {
  157. callback(nil, PTFrameTypeEndOfStream, 0, 0);
  158. return;
  159. }
  160. if (!allData || dispatch_data_get_size(allData) < sizeof(PTFrame)) {
  161. #if PT_DISPATCH_RETAIN_RELEASE
  162. if (allData) dispatch_release(allData);
  163. #endif
  164. callback([[NSError alloc] initWithDomain:Lookin_PTProtocolErrorDomain code:0 userInfo:nil], 0, 0, 0);
  165. return;
  166. }
  167. PTFrame *frame = NULL;
  168. size_t size = 0;
  169. 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
  170. #if PT_DISPATCH_RETAIN_RELEASE
  171. dispatch_release(allData);
  172. #endif
  173. if (!contiguousData) {
  174. callback([[NSError alloc] initWithDomain:NSPOSIXErrorDomain code:ENOMEM userInfo:nil], 0, 0, 0);
  175. return;
  176. }
  177. frame->version = ntohl(frame->version);
  178. if (frame->version != PTProtocolVersion1) {
  179. callback([[NSError alloc] initWithDomain:Lookin_PTProtocolErrorDomain code:0 userInfo:nil], 0, 0, 0);
  180. } else {
  181. frame->type = ntohl(frame->type);
  182. frame->tag = ntohl(frame->tag);
  183. frame->payloadSize = ntohl(frame->payloadSize);
  184. callback(nil, frame->type, frame->tag, frame->payloadSize);
  185. }
  186. #if PT_DISPATCH_RETAIN_RELEASE
  187. dispatch_release(contiguousData);
  188. #endif
  189. }
  190. });
  191. }
  192. - (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 {
  193. __block dispatch_data_t allData = NULL;
  194. dispatch_io_read(channel, 0, payloadSize, queue_, ^(bool done, dispatch_data_t data, int error) {
  195. //NSLog(@"dispatch_io_read: done=%d data=%p error=%d", done, data, error);
  196. size_t dataSize = dispatch_data_get_size(data);
  197. if (dataSize) {
  198. if (!allData) {
  199. allData = data;
  200. #if PT_DISPATCH_RETAIN_RELEASE
  201. dispatch_retain(allData);
  202. #endif
  203. } else {
  204. #if PT_DISPATCH_RETAIN_RELEASE
  205. dispatch_data_t allDataPrev = allData;
  206. allData = dispatch_data_create_concat(allData, data);
  207. dispatch_release(allDataPrev);
  208. #else
  209. allData = dispatch_data_create_concat(allData, data);
  210. #endif
  211. }
  212. }
  213. if (done) {
  214. if (error != 0) {
  215. #if PT_DISPATCH_RETAIN_RELEASE
  216. if (allData) dispatch_release(allData);
  217. #endif
  218. callback([[NSError alloc] initWithDomain:NSPOSIXErrorDomain code:error userInfo:nil], NULL, NULL, 0);
  219. return;
  220. }
  221. if (dataSize == 0) {
  222. #if PT_DISPATCH_RETAIN_RELEASE
  223. if (allData) dispatch_release(allData);
  224. #endif
  225. callback(nil, NULL, NULL, 0);
  226. return;
  227. }
  228. uint8_t *buffer = NULL;
  229. size_t bufferSize = 0;
  230. PT_PRECISE_LIFETIME dispatch_data_t contiguousData = NULL;
  231. if (allData) {
  232. contiguousData = dispatch_data_create_map(allData, (const void **)&buffer, &bufferSize);
  233. #if PT_DISPATCH_RETAIN_RELEASE
  234. dispatch_release(allData); allData = NULL;
  235. #endif
  236. if (!contiguousData) {
  237. callback([[NSError alloc] initWithDomain:NSPOSIXErrorDomain code:ENOMEM userInfo:nil], NULL, NULL, 0);
  238. return;
  239. }
  240. }
  241. callback(nil, contiguousData, buffer, bufferSize);
  242. #if PT_DISPATCH_RETAIN_RELEASE
  243. if (contiguousData) dispatch_release(contiguousData);
  244. #endif
  245. }
  246. });
  247. }
  248. - (void)readAndDiscardDataOfSize:(size_t)size overChannel:(dispatch_io_t)channel callback:(void(^)(NSError*, BOOL))callback {
  249. dispatch_io_read(channel, 0, size, queue_, ^(bool done, dispatch_data_t data, int error) {
  250. if (done && callback) {
  251. size_t dataSize = data ? dispatch_data_get_size(data) : 0;
  252. callback(error == 0 ? nil : [[NSError alloc] initWithDomain:NSPOSIXErrorDomain code:error userInfo:nil], dataSize == 0);
  253. }
  254. });
  255. }
  256. - (void)readFramesOverChannel:(dispatch_io_t)channel onFrame:(void(^)(NSError*, uint32_t, uint32_t, uint32_t, dispatch_block_t))onFrame {
  257. [self readFrameOverChannel:channel callback:^(NSError *error, uint32_t type, uint32_t tag, uint32_t payloadSize) {
  258. onFrame(error, type, tag, payloadSize, ^{
  259. if (type != PTFrameTypeEndOfStream) {
  260. [self readFramesOverChannel:channel onFrame:onFrame];
  261. }
  262. });
  263. }];
  264. }
  265. @end
  266. @interface Lookin_PTDispatchData : NSObject {
  267. dispatch_data_t dispatchData_;
  268. }
  269. @end
  270. @implementation Lookin_PTDispatchData
  271. - (id)initWithDispatchData:(dispatch_data_t)dispatchData {
  272. if (!(self = [super init])) return nil;
  273. dispatchData_ = dispatchData;
  274. #if PT_DISPATCH_RETAIN_RELEASE
  275. dispatch_retain(dispatchData_);
  276. #endif
  277. return self;
  278. }
  279. - (void)dealloc {
  280. #if PT_DISPATCH_RETAIN_RELEASE
  281. if (dispatchData_) dispatch_release(dispatchData_);
  282. #endif
  283. }
  284. @end
  285. @implementation NSData (Lookin_PTProtocol)
  286. #pragma clang diagnostic push
  287. #pragma clang diagnostic ignored "-Wunused-getter-return-value"
  288. - (dispatch_data_t)createReferencingDispatchData {
  289. // Note: The queue is used to submit the destructor. Since we only perform an
  290. // atomic release of self, it doesn't really matter which queue is used, thus
  291. // we use the current calling queue.
  292. return dispatch_data_create((const void*)self.bytes, self.length, dispatch_get_main_queue(), ^{
  293. // trick to have the block capture the data, thus retain/releasing
  294. [self length];
  295. });
  296. }
  297. #pragma clang diagnostic pop
  298. + (NSData *)dataWithContentsOfDispatchData:(dispatch_data_t)data {
  299. if (!data) {
  300. return nil;
  301. }
  302. uint8_t *buffer = NULL;
  303. size_t bufferSize = 0;
  304. PT_PRECISE_LIFETIME dispatch_data_t contiguousData = dispatch_data_create_map(data, (const void **)&buffer, &bufferSize);
  305. if (!contiguousData) {
  306. return nil;
  307. }
  308. Lookin_PTDispatchData *dispatchDataRef = [[Lookin_PTDispatchData alloc] initWithDispatchData:contiguousData];
  309. NSData *newData = [NSData dataWithBytesNoCopy:(void*)buffer length:bufferSize freeWhenDone:NO];
  310. #if PT_DISPATCH_RETAIN_RELEASE
  311. dispatch_release(contiguousData);
  312. #endif
  313. static const bool kDispatchDataRefKey;
  314. objc_setAssociatedObject(newData, (const void*)kDispatchDataRefKey, dispatchDataRef, OBJC_ASSOCIATION_RETAIN);
  315. return newData;
  316. }
  317. @end
  318. @implementation NSDictionary (Lookin_PTProtocol)
  319. - (dispatch_data_t)createReferencingDispatchData {
  320. NSError *error = nil;
  321. NSData *plistData = [NSPropertyListSerialization dataWithPropertyList:self format:NSPropertyListBinaryFormat_v1_0 options:0 error:&error];
  322. if (!plistData) {
  323. NSLog(@"Failed to serialize property list: %@", error);
  324. return nil;
  325. } else {
  326. return [plistData createReferencingDispatchData];
  327. }
  328. }
  329. // Decode *data* as a peroperty list-encoded dictionary. Returns nil on failure.
  330. + (NSDictionary*)dictionaryWithContentsOfDispatchData:(dispatch_data_t)data {
  331. if (!data) {
  332. return nil;
  333. }
  334. uint8_t *buffer = NULL;
  335. size_t bufferSize = 0;
  336. PT_PRECISE_LIFETIME dispatch_data_t contiguousData = dispatch_data_create_map(data, (const void **)&buffer, &bufferSize);
  337. if (!contiguousData) {
  338. return nil;
  339. }
  340. NSDictionary *dict = [NSPropertyListSerialization propertyListWithData:[NSData dataWithBytesNoCopy:(void*)buffer length:bufferSize freeWhenDone:NO] options:NSPropertyListImmutable format:NULL error:nil];
  341. #if PT_DISPATCH_RETAIN_RELEASE
  342. dispatch_release(contiguousData);
  343. #endif
  344. return dict;
  345. }
  346. @end
  347. #endif /* SHOULD_COMPILE_LOOKIN_SERVER */