Lookin_PTProtocol.m 13 KB

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