PageRenderTime 56ms CodeModel.GetById 24ms RepoModel.GetById 1ms app.codeStats 0ms

/vim_runtime/sources_non_forked/command-t/ruby/command-t/watchman.c

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