PageRenderTime 27ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/bundle/command-t/ruby/command-t/watchman.c

https://gitlab.com/lemonxah/vim
C | 675 lines | 458 code | 81 blank | 136 comment | 76 complexity | 9c74256581f35273607344b43518e782 MD5 | raw file
  1. // Copyright 2014 Greg Hurrell. All rights reserved.
  2. // Licensed under the terms of the BSD 2-clause license.
  3. #include "watchman.h"
  4. #ifdef WATCHMAN_BUILD
  5. #if defined(HAVE_RUBY_ST_H)
  6. #include <ruby/st.h>
  7. #elif defined(HAVE_ST_H)
  8. #include <st.h>
  9. #else
  10. #error no st.h header found
  11. #endif
  12. #include <stdint.h> /* for uint8_t */
  13. #include <fcntl.h> /* for fcntl() */
  14. #include <sys/errno.h> /* for errno */
  15. #include <sys/socket.h> /* for recv(), MSG_PEEK */
  16. typedef struct {
  17. uint8_t *data; // payload
  18. size_t cap; // total capacity
  19. size_t len; // current length
  20. } watchman_t;
  21. // Forward declarations:
  22. VALUE watchman_load(char **ptr, char *end);
  23. void watchman_dump(watchman_t *w, VALUE serializable);
  24. /**
  25. * Inspect a ruby object (debugging aid)
  26. */
  27. #define ruby_inspect(obj) rb_funcall(rb_mKernel, rb_intern("p"), 1, obj)
  28. /**
  29. * Print `count` bytes of memory at `address` (debugging aid)
  30. */
  31. #define dump(address, count) \
  32. do { \
  33. for (int i = 0; i < count; i++) { \
  34. printf("%02x ", ((unsigned char *)address)[i]); printf("\n"); \
  35. } \
  36. } while(0)
  37. #define WATCHMAN_DEFAULT_STORAGE 4096
  38. #define WATCHMAN_BINARY_MARKER "\x00\x01"
  39. #define WATCHMAN_ARRAY_MARKER 0x00
  40. #define WATCHMAN_HASH_MARKER 0x01
  41. #define WATCHMAN_STRING_MARKER 0x02
  42. #define WATCHMAN_INT8_MARKER 0x03
  43. #define WATCHMAN_INT16_MARKER 0x04
  44. #define WATCHMAN_INT32_MARKER 0x05
  45. #define WATCHMAN_INT64_MARKER 0x06
  46. #define WATCHMAN_FLOAT_MARKER 0x07
  47. #define WATCHMAN_TRUE 0x08
  48. #define WATCHMAN_FALSE 0x09
  49. #define WATCHMAN_NIL 0x0a
  50. #define WATCHMAN_TEMPLATE_MARKER 0x0b
  51. #define WATCHMAN_SKIP_MARKER 0x0c
  52. #define WATCHMAN_HEADER \
  53. WATCHMAN_BINARY_MARKER \
  54. "\x06" \
  55. "\x00\x00\x00\x00\x00\x00\x00\x00"
  56. static const char watchman_array_marker = WATCHMAN_ARRAY_MARKER;
  57. static const char watchman_hash_marker = WATCHMAN_HASH_MARKER;
  58. static const char watchman_string_marker = WATCHMAN_STRING_MARKER;
  59. static const char watchman_true = WATCHMAN_TRUE;
  60. static const char watchman_false = WATCHMAN_FALSE;
  61. static const char watchman_nil = WATCHMAN_NIL;
  62. /**
  63. * Appends `len` bytes, starting at `data`, to the watchman_t struct `w`
  64. *
  65. * Will attempt to reallocate the underlying storage if it is not sufficient.
  66. */
  67. void watchman_append(watchman_t *w, const char *data, size_t len) {
  68. if (w->len + len > w->cap) {
  69. w->cap += w->len + WATCHMAN_DEFAULT_STORAGE;
  70. REALLOC_N(w->data, uint8_t, w->cap);
  71. }
  72. memcpy(w->data + w->len, data, len);
  73. w->len += len;
  74. }
  75. /**
  76. * Allocate a new watchman_t struct
  77. *
  78. * The struct has a small amount of extra capacity preallocated, and a blank
  79. * header that can be filled in later to describe the PDU.
  80. */
  81. watchman_t *watchman_init() {
  82. watchman_t *w = ALLOC(watchman_t);
  83. w->cap = WATCHMAN_DEFAULT_STORAGE;
  84. w->len = 0;
  85. w->data = ALLOC_N(uint8_t, WATCHMAN_DEFAULT_STORAGE);
  86. watchman_append(w, WATCHMAN_HEADER, sizeof(WATCHMAN_HEADER) - 1);
  87. return w;
  88. }
  89. /**
  90. * Free a watchman_t struct `w` that was previously allocated with
  91. * `watchman_init`
  92. */
  93. void watchman_free(watchman_t *w) {
  94. xfree(w->data);
  95. xfree(w);
  96. }
  97. /**
  98. * Encodes and appends the integer `num` to `w`
  99. */
  100. void watchman_dump_int(watchman_t *w, int64_t num) {
  101. char encoded[1 + sizeof(int64_t)];
  102. if (num == (int8_t)num) {
  103. encoded[0] = WATCHMAN_INT8_MARKER;
  104. encoded[1] = (int8_t)num;
  105. watchman_append(w, encoded, 1 + sizeof(int8_t));
  106. } else if (num == (int16_t)num) {
  107. encoded[0] = WATCHMAN_INT16_MARKER;
  108. *(int16_t *)(encoded + 1) = (int16_t)num;
  109. watchman_append(w, encoded, 1 + sizeof(int16_t));
  110. } else if (num == (int32_t)num) {
  111. encoded[0] = WATCHMAN_INT32_MARKER;
  112. *(int32_t *)(encoded + 1) = (int32_t)num;
  113. watchman_append(w, encoded, 1 + sizeof(int32_t));
  114. } else {
  115. encoded[0] = WATCHMAN_INT64_MARKER;
  116. *(int64_t *)(encoded + 1) = (int64_t)num;
  117. watchman_append(w, encoded, 1 + sizeof(int64_t));
  118. }
  119. }
  120. /**
  121. * Encodes and appends the string `string` to `w`
  122. */
  123. void watchman_dump_string(watchman_t *w, VALUE string) {
  124. watchman_append(w, &watchman_string_marker, sizeof(watchman_string_marker));
  125. watchman_dump_int(w, RSTRING_LEN(string));
  126. watchman_append(w, RSTRING_PTR(string), RSTRING_LEN(string));
  127. }
  128. /**
  129. * Encodes and appends the double `num` to `w`
  130. */
  131. void watchman_dump_double(watchman_t *w, double num) {
  132. char encoded[1 + sizeof(double)];
  133. encoded[0] = WATCHMAN_FLOAT_MARKER;
  134. *(double *)(encoded + 1) = num;
  135. watchman_append(w, encoded, sizeof(encoded));
  136. }
  137. /**
  138. * Encodes and appends the array `array` to `w`
  139. */
  140. void watchman_dump_array(watchman_t *w, VALUE array) {
  141. long i;
  142. watchman_append(w, &watchman_array_marker, sizeof(watchman_array_marker));
  143. watchman_dump_int(w, RARRAY_LEN(array));
  144. for (i = 0; i < RARRAY_LEN(array); i++) {
  145. watchman_dump(w, rb_ary_entry(array, i));
  146. }
  147. }
  148. /**
  149. * Helper method that encodes and appends a key/value pair (`key`, `value`) from
  150. * a hash to the watchman_t struct passed in via `data`
  151. */
  152. int watchman_dump_hash_iterator(VALUE key, VALUE value, VALUE data) {
  153. watchman_t *w = (watchman_t *)data;
  154. watchman_dump_string(w, StringValue(key));
  155. watchman_dump(w, value);
  156. return ST_CONTINUE;
  157. }
  158. /**
  159. * Encodes and appends the hash `hash` to `w`
  160. */
  161. void watchman_dump_hash(watchman_t *w, VALUE hash) {
  162. watchman_append(w, &watchman_hash_marker, sizeof(watchman_hash_marker));
  163. watchman_dump_int(w, RHASH_SIZE(hash));
  164. rb_hash_foreach(hash, watchman_dump_hash_iterator, (VALUE)w);
  165. }
  166. /**
  167. * Encodes and appends the serialized Ruby object `serializable` to `w`
  168. *
  169. * Examples of serializable objects include arrays, hashes, strings, numbers
  170. * (integers, floats), booleans, and nil.
  171. */
  172. void watchman_dump(watchman_t *w, VALUE serializable) {
  173. switch (TYPE(serializable)) {
  174. case T_ARRAY:
  175. return watchman_dump_array(w, serializable);
  176. case T_HASH:
  177. return watchman_dump_hash(w, serializable);
  178. case T_STRING:
  179. return watchman_dump_string(w, serializable);
  180. case T_FIXNUM: // up to 63 bits
  181. return watchman_dump_int(w, FIX2LONG(serializable));
  182. case T_BIGNUM:
  183. return watchman_dump_int(w, NUM2LL(serializable));
  184. case T_FLOAT:
  185. return watchman_dump_double(w, NUM2DBL(serializable));
  186. case T_TRUE:
  187. return watchman_append(w, &watchman_true, sizeof(watchman_true));
  188. case T_FALSE:
  189. return watchman_append(w, &watchman_false, sizeof(watchman_false));
  190. case T_NIL:
  191. return watchman_append(w, &watchman_nil, sizeof(watchman_nil));
  192. default:
  193. rb_raise(rb_eTypeError, "unsupported type");
  194. }
  195. }
  196. /**
  197. * Extract and return the int encoded at `ptr`
  198. *
  199. * Moves `ptr` past the extracted int.
  200. *
  201. * Will raise an ArgumentError if extracting the int would take us beyond the
  202. * end of the buffer indicated by `end`, or if there is no int encoded at `ptr`.
  203. *
  204. * @returns The extracted int
  205. */
  206. int64_t watchman_load_int(char **ptr, char *end) {
  207. char *val_ptr = *ptr + sizeof(int8_t);
  208. int64_t val = 0;
  209. if (val_ptr >= end) {
  210. rb_raise(rb_eArgError, "insufficient int storage");
  211. }
  212. switch (*ptr[0]) {
  213. case WATCHMAN_INT8_MARKER:
  214. if (val_ptr + sizeof(int8_t) > end) {
  215. rb_raise(rb_eArgError, "overrun extracting int8_t");
  216. }
  217. val = *(int8_t *)val_ptr;
  218. *ptr = val_ptr + sizeof(int8_t);
  219. break;
  220. case WATCHMAN_INT16_MARKER:
  221. if (val_ptr + sizeof(int16_t) > end) {
  222. rb_raise(rb_eArgError, "overrun extracting int16_t");
  223. }
  224. val = *(int16_t *)val_ptr;
  225. *ptr = val_ptr + sizeof(int16_t);
  226. break;
  227. case WATCHMAN_INT32_MARKER:
  228. if (val_ptr + sizeof(int32_t) > end) {
  229. rb_raise(rb_eArgError, "overrun extracting int32_t");
  230. }
  231. val = *(int32_t *)val_ptr;
  232. *ptr = val_ptr + sizeof(int32_t);
  233. break;
  234. case WATCHMAN_INT64_MARKER:
  235. if (val_ptr + sizeof(int64_t) > end) {
  236. rb_raise(rb_eArgError, "overrun extracting int64_t");
  237. }
  238. val = *(int64_t *)val_ptr;
  239. *ptr = val_ptr + sizeof(int64_t);
  240. break;
  241. default:
  242. rb_raise(rb_eArgError, "bad integer marker 0x%02x", (unsigned int)*ptr[0]);
  243. break;
  244. }
  245. return val;
  246. }
  247. /**
  248. * Reads and returns a string encoded in the Watchman binary protocol format,
  249. * starting at `ptr` and finishing at or before `end`
  250. */
  251. VALUE watchman_load_string(char **ptr, char *end) {
  252. int64_t len;
  253. VALUE string;
  254. if (*ptr >= end) {
  255. rb_raise(rb_eArgError, "unexpected end of input");
  256. }
  257. if (*ptr[0] != WATCHMAN_STRING_MARKER) {
  258. rb_raise(rb_eArgError, "not a number");
  259. }
  260. *ptr += sizeof(int8_t);
  261. if (*ptr >= end) {
  262. rb_raise(rb_eArgError, "invalid string header");
  263. }
  264. len = watchman_load_int(ptr, end);
  265. if (len == 0) { // special case for zero-length strings
  266. return rb_str_new2("");
  267. } else if (*ptr + len > end) {
  268. rb_raise(rb_eArgError, "insufficient string storage");
  269. }
  270. string = rb_str_new(*ptr, len);
  271. *ptr += len;
  272. return string;
  273. }
  274. /**
  275. * Reads and returns a double encoded in the Watchman binary protocol format,
  276. * starting at `ptr` and finishing at or before `end`
  277. */
  278. double watchman_load_double(char **ptr, char *end) {
  279. double val;
  280. *ptr += sizeof(int8_t); // caller has already verified the marker
  281. if (*ptr + sizeof(double) > end) {
  282. rb_raise(rb_eArgError, "insufficient double storage");
  283. }
  284. val = *(double *)*ptr;
  285. *ptr += sizeof(double);
  286. return val;
  287. }
  288. /**
  289. * Helper method which returns length of the array encoded in the Watchman
  290. * binary protocol format, starting at `ptr` and finishing at or before `end`
  291. */
  292. int64_t watchman_load_array_header(char **ptr, char *end) {
  293. if (*ptr >= end) {
  294. rb_raise(rb_eArgError, "unexpected end of input");
  295. }
  296. // verify and consume marker
  297. if (*ptr[0] != WATCHMAN_ARRAY_MARKER) {
  298. rb_raise(rb_eArgError, "not an array");
  299. }
  300. *ptr += sizeof(int8_t);
  301. // expect a count
  302. if (*ptr + sizeof(int8_t) * 2 > end) {
  303. rb_raise(rb_eArgError, "incomplete array header");
  304. }
  305. return watchman_load_int(ptr, end);
  306. }
  307. /**
  308. * Reads and returns an array encoded in the Watchman binary protocol format,
  309. * starting at `ptr` and finishing at or before `end`
  310. */
  311. VALUE watchman_load_array(char **ptr, char *end) {
  312. int64_t count, i;
  313. VALUE array;
  314. count = watchman_load_array_header(ptr, end);
  315. array = rb_ary_new2(count);
  316. for (i = 0; i < count; i++) {
  317. rb_ary_push(array, watchman_load(ptr, end));
  318. }
  319. return array;
  320. }
  321. /**
  322. * Reads and returns a hash encoded in the Watchman binary protocol format,
  323. * starting at `ptr` and finishing at or before `end`
  324. */
  325. VALUE watchman_load_hash(char **ptr, char *end) {
  326. int64_t count, i;
  327. VALUE hash, key, value;
  328. *ptr += sizeof(int8_t); // caller has already verified the marker
  329. // expect a count
  330. if (*ptr + sizeof(int8_t) * 2 > end) {
  331. rb_raise(rb_eArgError, "incomplete hash header");
  332. }
  333. count = watchman_load_int(ptr, end);
  334. hash = rb_hash_new();
  335. for (i = 0; i < count; i++) {
  336. key = watchman_load_string(ptr, end);
  337. value = watchman_load(ptr, end);
  338. rb_hash_aset(hash, key, value);
  339. }
  340. return hash;
  341. }
  342. /**
  343. * Reads and returns a templated array encoded in the Watchman binary protocol
  344. * format, starting at `ptr` and finishing at or before `end`
  345. *
  346. * Templated arrays are arrays of hashes which have repetitive key information
  347. * pulled out into a separate "headers" prefix.
  348. *
  349. * @see https://github.com/facebook/watchman/blob/master/BSER.markdown
  350. */
  351. VALUE watchman_load_template(char **ptr, char *end) {
  352. int64_t header_items_count, i, row_count;
  353. VALUE array, hash, header, key, value;
  354. *ptr += sizeof(int8_t); // caller has already verified the marker
  355. // process template header array
  356. header_items_count = watchman_load_array_header(ptr, end);
  357. header = rb_ary_new2(header_items_count);
  358. for (i = 0; i < header_items_count; i++) {
  359. rb_ary_push(header, watchman_load_string(ptr, end));
  360. }
  361. // process row items
  362. row_count = watchman_load_int(ptr, end);
  363. array = rb_ary_new2(header_items_count);
  364. while (row_count--) {
  365. hash = rb_hash_new();
  366. for (i = 0; i < header_items_count; i++) {
  367. if (*ptr >= end) {
  368. rb_raise(rb_eArgError, "unexpected end of input");
  369. }
  370. if (*ptr[0] == WATCHMAN_SKIP_MARKER) {
  371. *ptr += sizeof(uint8_t);
  372. } else {
  373. value = watchman_load(ptr, end);
  374. key = rb_ary_entry(header, i);
  375. rb_hash_aset(hash, key, value);
  376. }
  377. }
  378. rb_ary_push(array, hash);
  379. }
  380. return array;
  381. }
  382. /**
  383. * Reads and returns an object encoded in the Watchman binary protocol format,
  384. * starting at `ptr` and finishing at or before `end`
  385. */
  386. VALUE watchman_load(char **ptr, char *end) {
  387. if (*ptr >= end) {
  388. rb_raise(rb_eArgError, "unexpected end of input");
  389. }
  390. switch (*ptr[0]) {
  391. case WATCHMAN_ARRAY_MARKER:
  392. return watchman_load_array(ptr, end);
  393. case WATCHMAN_HASH_MARKER:
  394. return watchman_load_hash(ptr, end);
  395. case WATCHMAN_STRING_MARKER:
  396. return watchman_load_string(ptr, end);
  397. case WATCHMAN_INT8_MARKER:
  398. case WATCHMAN_INT16_MARKER:
  399. case WATCHMAN_INT32_MARKER:
  400. case WATCHMAN_INT64_MARKER:
  401. return LL2NUM(watchman_load_int(ptr, end));
  402. case WATCHMAN_FLOAT_MARKER:
  403. return rb_float_new(watchman_load_double(ptr, end));
  404. case WATCHMAN_TRUE:
  405. *ptr += 1;
  406. return Qtrue;
  407. case WATCHMAN_FALSE:
  408. *ptr += 1;
  409. return Qfalse;
  410. case WATCHMAN_NIL:
  411. *ptr += 1;
  412. return Qnil;
  413. case WATCHMAN_TEMPLATE_MARKER:
  414. return watchman_load_template(ptr, end);
  415. default:
  416. rb_raise(rb_eTypeError, "unsupported type");
  417. }
  418. return Qnil; // keep the compiler happy
  419. }
  420. /**
  421. * CommandT::Watchman::Utils.load(serialized)
  422. *
  423. * Converts the binary object, `serialized`, from the Watchman binary protocol
  424. * format into a normal Ruby object.
  425. */
  426. VALUE CommandTWatchmanUtils_load(VALUE self, VALUE serialized) {
  427. char *ptr, *end;
  428. long len;
  429. uint64_t payload_size;
  430. VALUE loaded;
  431. serialized = StringValue(serialized);
  432. len = RSTRING_LEN(serialized);
  433. ptr = RSTRING_PTR(serialized);
  434. end = ptr + len;
  435. // expect at least the binary marker and a int8_t length counter
  436. if ((size_t)len < sizeof(WATCHMAN_BINARY_MARKER) - 1 + sizeof(int8_t) * 2) {
  437. rb_raise(rb_eArgError, "undersized header");
  438. }
  439. if (memcmp(ptr, WATCHMAN_BINARY_MARKER, sizeof(WATCHMAN_BINARY_MARKER) - 1)) {
  440. rb_raise(rb_eArgError, "missing binary marker");
  441. }
  442. // get size marker
  443. ptr += sizeof(WATCHMAN_BINARY_MARKER) - 1;
  444. payload_size = watchman_load_int(&ptr, end);
  445. if (!payload_size) {
  446. rb_raise(rb_eArgError, "empty payload");
  447. }
  448. // sanity check length
  449. if (ptr + payload_size != end) {
  450. rb_raise(
  451. rb_eArgError,
  452. "payload size mismatch (%lu)",
  453. (unsigned long)(end - (ptr + payload_size))
  454. );
  455. }
  456. loaded = watchman_load(&ptr, end);
  457. // one more sanity check
  458. if (ptr != end) {
  459. rb_raise(
  460. rb_eArgError,
  461. "payload termination mismatch (%lu)",
  462. (unsigned long)(end - ptr)
  463. );
  464. }
  465. return loaded;
  466. }
  467. /**
  468. * CommandT::Watchman::Utils.dump(serializable)
  469. *
  470. * Converts the Ruby object, `serializable`, into a binary string in the
  471. * Watchman binary protocol format.
  472. *
  473. * Examples of serializable objects include arrays, hashes, strings, numbers
  474. * (integers, floats), booleans, and nil.
  475. */
  476. VALUE CommandTWatchmanUtils_dump(VALUE self, VALUE serializable) {
  477. uint64_t *len;
  478. VALUE serialized;
  479. watchman_t *w = watchman_init();
  480. watchman_dump(w, serializable);
  481. // update header with final length information
  482. len = (uint64_t *)(w->data + sizeof(WATCHMAN_HEADER) - sizeof(uint64_t) - 1);
  483. *len = w->len - sizeof(WATCHMAN_HEADER) + 1;
  484. // prepare final return value
  485. serialized = rb_str_buf_new(w->len);
  486. rb_str_buf_cat(serialized, (const char*)w->data, w->len);
  487. watchman_free(w);
  488. return serialized;
  489. }
  490. /**
  491. * Helper method for raising a SystemCallError wrapping a lower-level error code
  492. * coming from the `errno` global variable.
  493. */
  494. void watchman_raise_system_call_error(int number) {
  495. VALUE error = INT2FIX(number);
  496. rb_exc_raise(rb_class_new_instance(1, &error, rb_eSystemCallError));
  497. }
  498. // How far we have to look to figure out the size of the PDU header
  499. #define WATCHMAN_SNIFF_BUFFER_SIZE sizeof(WATCHMAN_BINARY_MARKER) - 1 + sizeof(int8_t)
  500. // How far we have to peek, at most, to figure out the size of the PDU itself
  501. #define WATCHMAN_PEEK_BUFFER_SIZE \
  502. sizeof(WATCHMAN_BINARY_MARKER) - 1 + \
  503. sizeof(WATCHMAN_INT64_MARKER) + \
  504. sizeof(int64_t)
  505. /**
  506. * CommandT::Watchman::Utils.query(query, socket)
  507. *
  508. * Converts `query`, a Watchman query comprising Ruby objects, into the Watchman
  509. * binary protocol format, transmits it over socket, and unserializes and
  510. * returns the result.
  511. */
  512. VALUE CommandTWatchmanUtils_query(VALUE self, VALUE query, VALUE socket) {
  513. char *payload;
  514. int fileno, flags;
  515. int8_t peek[WATCHMAN_PEEK_BUFFER_SIZE];
  516. int8_t sizes[] = { 0, 0, 0, 1, 2, 4, 8 };
  517. int8_t sizes_idx;
  518. int8_t *pdu_size_ptr;
  519. int64_t payload_size;
  520. long query_len;
  521. ssize_t peek_size, sent, received;
  522. void *buffer;
  523. VALUE loaded, serialized;
  524. fileno = NUM2INT(rb_funcall(socket, rb_intern("fileno"), 0));
  525. // do blocking I/O to simplify the following logic
  526. flags = fcntl(fileno, F_GETFL);
  527. if (fcntl(fileno, F_SETFL, flags & ~O_NONBLOCK) == -1) {
  528. rb_raise(rb_eRuntimeError, "unable to clear O_NONBLOCK flag");
  529. }
  530. // send the message
  531. serialized = CommandTWatchmanUtils_dump(self, query);
  532. query_len = RSTRING_LEN(serialized);
  533. sent = send(fileno, RSTRING_PTR(serialized), query_len, 0);
  534. if (sent == -1) {
  535. watchman_raise_system_call_error(errno);
  536. } else if (sent != query_len) {
  537. rb_raise(rb_eRuntimeError, "expected to send %ld bytes but sent %ld",
  538. query_len, sent);
  539. }
  540. // sniff to see how large the header is
  541. received = recv(fileno, peek, WATCHMAN_SNIFF_BUFFER_SIZE, MSG_PEEK | MSG_WAITALL);
  542. if (received == -1) {
  543. watchman_raise_system_call_error(errno);
  544. } else if (received != WATCHMAN_SNIFF_BUFFER_SIZE) {
  545. rb_raise(rb_eRuntimeError, "failed to sniff PDU header");
  546. }
  547. // peek at size of PDU
  548. sizes_idx = peek[sizeof(WATCHMAN_BINARY_MARKER) - 1];
  549. if (sizes_idx < WATCHMAN_INT8_MARKER || sizes_idx > WATCHMAN_INT64_MARKER) {
  550. rb_raise(rb_eRuntimeError, "bad PDU size marker");
  551. }
  552. peek_size = sizeof(WATCHMAN_BINARY_MARKER) - 1 + sizeof(int8_t) +
  553. sizes[sizes_idx];
  554. received = recv(fileno, peek, peek_size, MSG_PEEK);
  555. if (received == -1) {
  556. watchman_raise_system_call_error(errno);
  557. } else if (received != peek_size) {
  558. rb_raise(rb_eRuntimeError, "failed to peek at PDU header");
  559. }
  560. pdu_size_ptr = peek + sizeof(WATCHMAN_BINARY_MARKER) - sizeof(int8_t);
  561. payload_size =
  562. peek_size +
  563. watchman_load_int((char **)&pdu_size_ptr, (char *)peek + peek_size);
  564. // actually read the PDU
  565. buffer = xmalloc(payload_size);
  566. if (!buffer) {
  567. rb_raise(
  568. rb_eNoMemError,
  569. "failed to allocate %lld bytes",
  570. (long long int)payload_size
  571. );
  572. }
  573. received = recv(fileno, buffer, payload_size, MSG_WAITALL);
  574. if (received == -1) {
  575. watchman_raise_system_call_error(errno);
  576. } else if (received != payload_size) {
  577. rb_raise(rb_eRuntimeError, "failed to load PDU");
  578. }
  579. payload = (char *)buffer + peek_size;
  580. loaded = watchman_load(&payload, payload + payload_size);
  581. free(buffer);
  582. return loaded;
  583. }
  584. #else /* don't build Watchman utils; supply stubs only*/
  585. VALUE CommandTWatchmanUtils_load(VALUE self, VALUE serialized) {
  586. rb_raise(rb_eRuntimeError, "unsupported operation");
  587. }
  588. VALUE CommandTWatchmanUtils_dump(VALUE self, VALUE serializable) {
  589. rb_raise(rb_eRuntimeError, "unsupported operation");
  590. }
  591. VALUE CommandTWatchmanUtils_query(VALUE self, VALUE query, VALUE socket) {
  592. rb_raise(rb_eRuntimeError, "unsupported operation");
  593. }
  594. #endif