pbwire 0.1.7 → 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +51 -20
  3. data/ext/pbwire_ext/pbwire_ext.c +348 -17
  4. metadata +1 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 950a41e1912580e8f22dbba9e535f4e8dc616349dd0d3f2df752acda041ac0f2
4
- data.tar.gz: 1b05b36960a70124073ca772c39d0c82232438688effc7b59b84b3fa91ba1891
3
+ metadata.gz: '0481bd05f90b2ad08b8d4df6c1d7e88f6c9e41821fa6eeaa03827342f5876da4'
4
+ data.tar.gz: 5475631f128badcfa9653bb09a8b6cfbce0a2b8f9792ca4061ce4f7729c2af9f
5
5
  SHA512:
6
- metadata.gz: f4ddeab2642defee1bc0cd2cbdb16c17fc2dda2f802df8d3f2f2b8bcce6229c19e4f9ceb0acb030a15e3d5433a323574907294a406a9d8ab2997994c66c0c062
7
- data.tar.gz: f4c56831f56151879ab33c25f9259b83c0129206352a3cdcb0cfce3295a0cc4a4f0a0528a0eecf5639d44ada8a968912ab51744635d8ec4e4bc9d8f5bf8e1dcb
6
+ metadata.gz: 5704241ce640fe958967e6b368b6804901a3956217ecddf0be0661427b4d887bcfabef9b06deb625f79ea95260f4bb7379b7a4bbd26d324a6ef48467df6cbc31
7
+ data.tar.gz: 6cb90723b3fde234173da87eddc04e0b6bf4caa757eedd1de33f64fbf796869ab40f27f6bf1bf2d1de048390433da52a70bebca8c3373f0c36740baf40ad35c6
data/README.md CHANGED
@@ -1,10 +1,33 @@
1
1
  # pbwire Ruby binding
2
2
 
3
3
  `lib/pbwire.rb` loads a compiled C extension (`ext/pbwire_ext/`) that links the
4
- pbwire C core statically. No gems, no `protoc` plugin, no generated code: a
5
- `FileDescriptorSet` (`protoc --descriptor_set_out`) is loaded at runtime and
6
- drives every field lookup, so the field numbers, types and presence rules are the
7
- descriptor's, not a hand-written copy.
4
+ pbwire C core statically. No gems at run time.
5
+
6
+ Most code never touches the dynamic surface: generate a gem-shaped facade
7
+ once (`make -C codegen gen-ruby`) and use it like the library it
8
+ mirrors — same `new`/`encode`/`decode`/`to_h` calls, same observable
9
+ behavior, but the wire format stays in the C core:
10
+
11
+ ```ruby
12
+ require 'addressbook' # generated; embeds its own FileDescriptorSet
13
+
14
+ person = Pbwire::Gen::Tutorial_Person.new(name: 'alice', id: 7)
15
+ Pbwire::Gen::Tutorial_Person.encode(person) # => binary String, no schema
16
+ back = Pbwire::Gen::Tutorial_Person.decode(wire)
17
+ back.name # => "alice"
18
+ back.to_h # => {name: "alice", id: 7, ...}
19
+ back.has_email? # => false
20
+ ```
21
+
22
+ Presence follows the value: a non-nil assignment marks the field, so
23
+ `new(opt_i32: 0)` is written while `new` leaves it absent; oneof keeps
24
+ only its last member. `to_h` uses proto-name symbols and renders enums
25
+ as name symbols, exactly like the gem. The one deliberate difference:
26
+ validation happens at the encode boundary (plain Ruby objects until
27
+ then), where a mistyped field raises `TypeError`.
28
+
29
+ The dynamic API is still there for gateways and schemas known only at
30
+ runtime:
8
31
 
9
32
  ```ruby
10
33
  require 'pbwire'
@@ -135,17 +158,19 @@ parent is closed.
135
158
 
136
159
  Repeated scalars (packed or unpacked on the wire), repeated strings/bytes and
137
160
  repeated messages are supported; a map field reads as an Array of entry
138
- messages. Groups (proto2 legacy) are not exposed: `get`/`set` raise, and the wire
139
- view keeps their bytes as unknown fields (so a round trip still preserves them).
140
- Extensions are read by number, not name.
161
+ messages. Groups (proto2 legacy) map like messages (START_GROUP framing on
162
+ the wire). Extensions are read by number, not name.
141
163
 
142
164
  ## Deliberately skipped
143
165
 
144
166
  - **No `protoc` plugin, no codegen.** The descriptor set is the schema; nothing
145
167
  is generated and there is no hand-built schema path.
146
- - **No JSON/text format, no deterministic encoding, no streaming.** All of those
147
- exist in the C API and other bindings; this one stays the smallest thing that
148
- reads and writes binary messages.
168
+ - **Formats beyond binary/JSON/text.** `to_json`/`from_json` (with an
169
+ optional ignore-unknown flag), `to_text`/`from_text`, deterministic
170
+ encoding (`encode_deterministic`, map entries sorted by key) and delimited
171
+ framing (`encode_delimited`, `decode_delimited` returning `[message, read]`)
172
+ wrap the C API; `to_builder` materializes a decoded view into a canonical
173
+ tree. Unknown fields round-trip byte for byte in every form.
149
174
 
150
175
  ## Benchmark
151
176
 
@@ -183,17 +208,23 @@ The win is architectural, not a micro-optimization:
183
208
 
184
209
  ## Conformance
185
210
 
186
- `conformance/testee.rb` drives the binary wire format of the official
187
- `conformance_test_runner` through this binding (`make conformance-ruby`, reusing
188
- the cached protobuf checkout from `make conformance-setup`):
211
+ `conformance/testee.rb` drives the binary, JSON and text formats of the
212
+ official `conformance_test_runner` through this binding
213
+ (`make conformance-ruby`, reusing the cached protobuf checkout from
214
+ `make conformance-setup`):
189
215
 
190
216
  ```
191
- CONFORMANCE SUITE PASSED: 1199 successes, 1403 skipped, 0 expected failures,
217
+ CONFORMANCE SUITE PASSED: 2744 successes, 0 skipped, 0 expected failures,
218
+ 0 unexpected failures.
219
+ CONFORMANCE SUITE PASSED: 445 successes, 0 skipped, 0 expected failures,
192
220
  0 unexpected failures.
193
221
  ```
194
222
 
195
- JSON/text/JSPB requests answer `skipped` (the binding has no printers); the
196
- binary verdicts match the reference implementation exactly.
223
+ Only JSPB answers `skipped`; every other verdict matches the reference
224
+ implementation exactly. (The binary suite runs without the shared failure
225
+ list: the wire reader's validation rejects the overlong tag varints the
226
+ core tree decoder truncates, so the two listed cases parse-fail like the
227
+ reference. Text output honors the runner's `print_unknown_fields` flag.)
197
228
 
198
229
  ## Layout
199
230
 
@@ -205,7 +236,7 @@ binary verdicts match the reference implementation exactly.
205
236
  | example | [`examples/roundtrip.rb`](examples/roundtrip.rb) |
206
237
  | self-check | [`test/test_pbwire.rb`](test/test_pbwire.rb) |
207
238
  | benchmark | [`benchmarks/bench.rb`](benchmarks/bench.rb) | the four standard tiers against the `google-protobuf` gem |
208
- | conformance | [`conformance/testee.rb`](conformance/testee.rb) | binary wire format through the official runner |
239
+ | conformance | [`conformance/testee.rb`](conformance/testee.rb) | binary, JSON and text through the official runner |
209
240
  | fixture | [`test/data/sample.proto`](test/data/sample.proto) and the checked-in `sample.desc` |
210
241
 
211
242
  Regenerate the descriptor set after editing the fixture (it is checked in, so the
@@ -215,9 +246,9 @@ tests need no `protoc`):
215
246
  (cd bindings/ruby/test/data && protoc --descriptor_set_out=sample.desc sample.proto)
216
247
  ```
217
248
 
218
- ## Typed models
249
+ ## Generated models
219
250
 
220
- `protoc-gen-pbwire-typed` (in `../typed`) generates classes with keyword-argument
251
+ `protoc-gen-pbwire` (in `../../codegen`) generates classes with keyword-argument
221
252
  constructors and accessors, `#encode(schema)` and `.decode(schema, wire)`. The
222
253
  committed output is `test/gen/` and `examples/gen/`; `examples/roundtrip.rb`
223
- uses typed assignments only. See `../typed/README.md`.
254
+ uses typed assignments only. See `../../codegen/README.md`.
@@ -74,7 +74,6 @@ typedef struct {
74
74
  uint8_t packed;
75
75
  uint8_t required;
76
76
  uint8_t oneof; /* 1-based oneof group, 0 = none */
77
- uint8_t unsupported;
78
77
  VALUE name; /* frozen UTF-8 String */
79
78
  VALUE sub; /* Type of a MESSAGE field, else Qnil */
80
79
  } field_t;
@@ -455,7 +454,6 @@ type_for(VALUE schema_obj, const pbw_descriptor *desc)
455
454
  f->packed = pbw_field_is_packed(field) != 0;
456
455
  f->required = pbw_field_label(field) == PBW_LABEL_REQUIRED;
457
456
  f->oneof = (uint8_t)pbw_field_oneof_index(field);
458
- f->unsupported = f->type == PBW_TYPE_GROUP;
459
457
  f->name = rb_utf8_str_new_cstr(pbw_field_name(field));
460
458
  rb_obj_freeze(f->name);
461
459
  f->sub = Qnil;
@@ -471,7 +469,7 @@ type_for(VALUE schema_obj, const pbw_descriptor *desc)
471
469
  const pbw_field *field = pbw_descriptor_field_at(desc, i);
472
470
  field_t *f = &type->fields[i];
473
471
 
474
- if (f->type == PBW_TYPE_MESSAGE) {
472
+ if (f->type == PBW_TYPE_MESSAGE || f->type == PBW_TYPE_GROUP) {
475
473
  f->sub = type_for(schema_obj, pbw_field_message_type(field));
476
474
  }
477
475
  }
@@ -702,6 +700,40 @@ rd_skip(rdr *r, uint32_t fnum, unsigned wt, unsigned depth)
702
700
  }
703
701
  }
704
702
 
703
+ /* Body of a group field: bytes from here to the matching END_GROUP tag.
704
+ * Nested groups (known or skipped) are consumed whole by rd_skip; an
705
+ * END_GROUP for another number is malformed. */
706
+ static int
707
+ rd_group_body(rdr *r, uint32_t fnum, unsigned depth, const uint8_t **body,
708
+ size_t *body_len)
709
+ {
710
+ const uint8_t *start = r->p;
711
+
712
+ if (depth >= RB_PBW_MAX_DEPTH) {
713
+ return 0;
714
+ }
715
+ for (;;) {
716
+ const uint8_t *tag_start = r->p;
717
+ uint32_t inner_fnum;
718
+ unsigned inner_wt;
719
+
720
+ if (!rd_tag(r, &inner_fnum, &inner_wt)) {
721
+ return 0;
722
+ }
723
+ if (inner_wt == 4) {
724
+ if (inner_fnum != fnum) {
725
+ return 0;
726
+ }
727
+ *body = start;
728
+ *body_len = (size_t)(tag_start - start);
729
+ return 1;
730
+ }
731
+ if (!rd_skip(r, inner_fnum, inner_wt, depth + 1)) {
732
+ return 0;
733
+ }
734
+ }
735
+ }
736
+
705
737
  /* A packed run must hold a whole number of complete values; returns the count
706
738
  * or -1. */
707
739
  static long
@@ -853,7 +885,7 @@ scan_message(message_t *m, type_t *type, const uint8_t *data, size_t off,
853
885
  continue;
854
886
  }
855
887
  f = type_field_by_number(type, fnum);
856
- if (f == NULL || f->unsupported ||
888
+ if (f == NULL ||
857
889
  (wt != (unsigned)f->wire &&
858
890
  !(wt == 2 && f->repeated && packable((pbw_type)f->type)))) {
859
891
  if (!rd_skip(&r, fnum, wt, depth)) {
@@ -865,6 +897,34 @@ scan_message(message_t *m, type_t *type, const uint8_t *data, size_t off,
865
897
  if (seen != NULL && f->required) {
866
898
  seen[f->index] = 1;
867
899
  }
900
+ /* Groups share the sub-message scan: the body runs to the matching
901
+ * END_GROUP tag instead of a length prefix. */
902
+ if (f->type == PBW_TYPE_GROUP) {
903
+ const uint8_t *body;
904
+ size_t body_len;
905
+
906
+ if (!rd_group_body(&r, fnum, depth, &body, &body_len)) {
907
+ status = PBW_ERR_INVALID;
908
+ goto done;
909
+ }
910
+ {
911
+ type_t *sub = NULL;
912
+
913
+ TypedData_Get_Struct(f->sub, type_t, &type_data_type, sub);
914
+ status = scan_message(NULL, sub, body, 0, body_len,
915
+ depth + 1, missing);
916
+ if (status != PBW_OK) {
917
+ goto done;
918
+ }
919
+ }
920
+ if (m != NULL &&
921
+ !slice_add(m, f, (size_t)(body - data), body_len, 1, 1,
922
+ oneof_last)) {
923
+ status = PBW_ERR_NOMEM;
924
+ goto done;
925
+ }
926
+ continue;
927
+ }
868
928
  if (wt == 2) {
869
929
  uint64_t plen;
870
930
  size_t payload;
@@ -1109,7 +1169,7 @@ field_value(message_t *m, field_t *f)
1109
1169
  if (last < 0) {
1110
1170
  return Qnil;
1111
1171
  }
1112
- if (f->type == PBW_TYPE_MESSAGE) {
1172
+ if (f->type == PBW_TYPE_MESSAGE || f->type == PBW_TYPE_GROUP) {
1113
1173
  uint32_t n = 0;
1114
1174
 
1115
1175
  for (i = 0; i < m->nslices; i++) {
@@ -1152,7 +1212,8 @@ field_value(message_t *m, field_t *f)
1152
1212
  if (s->fidx != f->index) {
1153
1213
  continue;
1154
1214
  }
1155
- if (f->type == PBW_TYPE_MESSAGE) {
1215
+ if (f->type == PBW_TYPE_MESSAGE ||
1216
+ f->type == PBW_TYPE_GROUP) {
1156
1217
  rb_ary_push(array, message_wire_new(f->sub, m->buffer,
1157
1218
  s->off, s->len,
1158
1219
  m->depth + 1));
@@ -1307,7 +1368,8 @@ validate_value(VALUE owner, field_t *f, VALUE value)
1307
1368
  value_error(owner, f, "a String");
1308
1369
  }
1309
1370
  return;
1310
- case PBW_TYPE_MESSAGE: {
1371
+ case PBW_TYPE_MESSAGE:
1372
+ case PBW_TYPE_GROUP: {
1311
1373
  message_t *sub;
1312
1374
 
1313
1375
  if (!rb_obj_is_kind_of(value, cMessage)) {
@@ -1335,10 +1397,6 @@ validate_value(VALUE owner, field_t *f, VALUE value)
1335
1397
  }
1336
1398
  return;
1337
1399
  }
1338
- case PBW_TYPE_GROUP:
1339
- rb_raise(eError, "%s: group fields are not supported",
1340
- RSTRING_PTR(f->name));
1341
- return;
1342
1400
  case PBW_TYPE_FLOAT:
1343
1401
  case PBW_TYPE_DOUBLE:
1344
1402
  if (!RB_INTEGER_TYPE_P(value) && !RB_FLOAT_TYPE_P(value)) {
@@ -1370,7 +1428,8 @@ value_build(field_t *f, VALUE value, int *moved)
1370
1428
  StringValue(value);
1371
1429
  return pbw_value_new_bytes(RSTRING_PTR(value),
1372
1430
  (size_t)RSTRING_LEN(value));
1373
- case PBW_TYPE_MESSAGE: {
1431
+ case PBW_TYPE_MESSAGE:
1432
+ case PBW_TYPE_GROUP: {
1374
1433
  message_t *sub;
1375
1434
  pbw_message *tree;
1376
1435
  pbw_value *built;
@@ -1415,7 +1474,8 @@ static VALUE
1415
1474
  item_to_ruby(VALUE owner, field_t *f, pbw_value *value)
1416
1475
  {
1417
1476
  switch ((pbw_type)f->type) {
1418
- case PBW_TYPE_MESSAGE: {
1477
+ case PBW_TYPE_MESSAGE:
1478
+ case PBW_TYPE_GROUP: {
1419
1479
  pbw_message *child = pbw_value_get_message(value);
1420
1480
 
1421
1481
  return child == NULL ? Qnil : message_borrowed(owner, f->sub, child);
@@ -1640,10 +1700,6 @@ message_get(VALUE self, VALUE key)
1640
1700
  type_t *type = message_type_of(self);
1641
1701
  field_t *f = field_lookup(type, key);
1642
1702
 
1643
- if (f->unsupported) {
1644
- rb_raise(eError, "%s: group fields are not supported",
1645
- RSTRING_PTR(f->name));
1646
- }
1647
1703
  if (message->kind == MSG_WIRE) {
1648
1704
  return field_value(message, f);
1649
1705
  }
@@ -1746,6 +1802,273 @@ message_decode(VALUE self, VALUE bytes)
1746
1802
  (size_t)RSTRING_LEN(buffer), 0);
1747
1803
  }
1748
1804
 
1805
+ /* The tree behind a message for printing/encoding: builders hold one, wire
1806
+ * views decode their bytes into a fresh tree the caller frees. */
1807
+ static pbw_message *
1808
+ message_print_tree(VALUE self, message_t *message, int *free_tree)
1809
+ {
1810
+ if (message->kind == MSG_BUILDER) {
1811
+ *free_tree = 0;
1812
+ return message_tree(self, NULL);
1813
+ }
1814
+ {
1815
+ type_t *type = message_type_of(self);
1816
+ pbw_message *tree = NULL;
1817
+ pbw_status status = pbw_decode(type->desc,
1818
+ (const uint8_t *)message->data + message->off,
1819
+ message->len, &tree);
1820
+ if (status != PBW_OK) {
1821
+ rb_raise(eError, "%s: %s", type_name(type),
1822
+ pbw_strerror(status));
1823
+ }
1824
+ *free_tree = 1;
1825
+ return tree;
1826
+ }
1827
+ }
1828
+
1829
+ static VALUE
1830
+ message_to_json(VALUE self)
1831
+ {
1832
+ message_t *message;
1833
+ pbw_message *tree;
1834
+ int free_tree = 0;
1835
+ char *text = NULL;
1836
+ size_t len = 0;
1837
+ pbw_status status;
1838
+ VALUE out;
1839
+
1840
+ TypedData_Get_Struct(self, message_t, &message_data_type, message);
1841
+ if (message->kind == MSG_CLOSED) {
1842
+ rb_raise(eError, "%s: message is closed",
1843
+ type_name(message_type_of(self)));
1844
+ }
1845
+ tree = message_print_tree(self, message, &free_tree);
1846
+ status = pbw_json_print(tree, &text, &len);
1847
+ if (free_tree) {
1848
+ pbw_message_free(tree);
1849
+ }
1850
+ if (status != PBW_OK) {
1851
+ rb_raise(eError, "%s: %s", type_name(message_type_of(self)),
1852
+ pbw_strerror(status));
1853
+ }
1854
+ out = rb_utf8_str_new(text, (long)len);
1855
+ pbw_free(text);
1856
+ return out;
1857
+ }
1858
+
1859
+ /* Text format of a message; an optional false argument drops unknown
1860
+ * fields (the runner's print_unknown_fields flag) instead of printing
1861
+ * them numerically. */
1862
+ static VALUE
1863
+ message_to_text(int argc, VALUE *argv, VALUE self)
1864
+ {
1865
+ message_t *message;
1866
+ pbw_message *tree;
1867
+ int free_tree = 0;
1868
+ char *text = NULL;
1869
+ size_t len = 0;
1870
+ pbw_status status;
1871
+ VALUE out;
1872
+ /* Absent means print (the binding's standing behavior). */
1873
+ int print_unknown = 1;
1874
+ VALUE flag;
1875
+
1876
+ if (rb_scan_args(argc, argv, "01", &flag) == 1) {
1877
+ print_unknown = RTEST(flag);
1878
+ }
1879
+ TypedData_Get_Struct(self, message_t, &message_data_type, message);
1880
+ if (message->kind == MSG_CLOSED) {
1881
+ rb_raise(eError, "%s: message is closed",
1882
+ type_name(message_type_of(self)));
1883
+ }
1884
+ tree = message_print_tree(self, message, &free_tree);
1885
+ status = pbw_text_print_opts(tree, print_unknown, &text, &len);
1886
+ if (free_tree) {
1887
+ pbw_message_free(tree);
1888
+ }
1889
+ if (status != PBW_OK) {
1890
+ rb_raise(eError, "%s: %s", type_name(message_type_of(self)),
1891
+ pbw_strerror(status));
1892
+ }
1893
+ out = rb_utf8_str_new(text, (long)len);
1894
+ pbw_free(text);
1895
+ return out;
1896
+ }
1897
+
1898
+ /* Parses text (JSON mapping or text format) into a new builder tree. */
1899
+ static VALUE
1900
+ message_parse(VALUE self, VALUE text, int json, int ignore_unknown)
1901
+ {
1902
+ message_t *message;
1903
+ type_t *type;
1904
+ pbw_message *tree = NULL;
1905
+ pbw_status status;
1906
+
1907
+ TypedData_Get_Struct(self, message_t, &message_data_type, message);
1908
+ if (message->kind == MSG_CLOSED) {
1909
+ rb_raise(eError, "%s: message is closed",
1910
+ type_name(message_type_of(self)));
1911
+ }
1912
+ type = message_type_of(self);
1913
+ StringValue(text);
1914
+ if (json) {
1915
+ pbw_json_parse_options opts;
1916
+
1917
+ opts.ignore_unknown_fields = ignore_unknown;
1918
+ status = pbw_json_parse_opts(type->desc, RSTRING_PTR(text),
1919
+ (size_t)RSTRING_LEN(text), &opts, &tree);
1920
+ } else {
1921
+ status = pbw_text_parse(type->desc, RSTRING_PTR(text),
1922
+ (size_t)RSTRING_LEN(text), &tree);
1923
+ }
1924
+ if (status != PBW_OK) {
1925
+ rb_raise(eError, "%s: %s", type_name(type), pbw_strerror(status));
1926
+ }
1927
+ return message_builder_new(message->type, message->holder, tree, Qnil,
1928
+ 1);
1929
+ }
1930
+
1931
+ /* An optional trailing true ignores unknown JSON fields (the conformance
1932
+ * JSON_IGNORE_UNKNOWN category); by default they fail, like text. */
1933
+ static VALUE
1934
+ message_from_json(int argc, VALUE *argv, VALUE self)
1935
+ {
1936
+ VALUE text;
1937
+ VALUE ignore = Qfalse;
1938
+
1939
+ rb_scan_args(argc, argv, "11", &text, &ignore);
1940
+ return message_parse(self, text, 1, RTEST(ignore));
1941
+ }
1942
+
1943
+ /* Materializes a decoded view into a builder holding the parsed tree, so
1944
+ * re-encoding is canonical (oneof collapses to the last member, like the
1945
+ * reference): wire views hand their own bytes back instead. */
1946
+ static VALUE
1947
+ message_to_builder(VALUE self)
1948
+ {
1949
+ message_t *message;
1950
+ type_t *type;
1951
+ pbw_message *tree = NULL;
1952
+ pbw_status status;
1953
+
1954
+ TypedData_Get_Struct(self, message_t, &message_data_type, message);
1955
+ if (message->kind == MSG_CLOSED) {
1956
+ rb_raise(eError, "%s: message is closed",
1957
+ type_name(message_type_of(self)));
1958
+ }
1959
+ if (message->kind == MSG_BUILDER) {
1960
+ return self;
1961
+ }
1962
+ type = message_type_of(self);
1963
+ status = pbw_decode(type->desc,
1964
+ (const uint8_t *)message->data + message->off,
1965
+ message->len, &tree);
1966
+ if (status != PBW_OK) {
1967
+ rb_raise(eError, "%s: %s", type_name(type), pbw_strerror(status));
1968
+ }
1969
+ return message_builder_new(message->type, message->holder, tree, Qnil,
1970
+ 1);
1971
+ }
1972
+
1973
+ static VALUE
1974
+ message_from_text(VALUE self, VALUE text)
1975
+ {
1976
+ return message_parse(self, text, 0, 0);
1977
+ }
1978
+
1979
+ static VALUE
1980
+ message_encode_deterministic(VALUE self)
1981
+ {
1982
+ message_t *message;
1983
+ pbw_message *tree;
1984
+ int free_tree = 0;
1985
+ uint8_t *wire = NULL;
1986
+ size_t wire_len = 0;
1987
+ pbw_status status;
1988
+ VALUE out;
1989
+
1990
+ TypedData_Get_Struct(self, message_t, &message_data_type, message);
1991
+ if (message->kind == MSG_CLOSED) {
1992
+ rb_raise(eError, "%s: message is closed",
1993
+ type_name(message_type_of(self)));
1994
+ }
1995
+ tree = message_print_tree(self, message, &free_tree);
1996
+ status = pbw_encode_deterministic(tree, &wire, &wire_len);
1997
+ if (free_tree) {
1998
+ pbw_message_free(tree);
1999
+ }
2000
+ if (status != PBW_OK) {
2001
+ rb_raise(eError, "%s: %s", type_name(message_type_of(self)),
2002
+ pbw_strerror(status));
2003
+ }
2004
+ out = rb_str_new((const char *)wire, (long)wire_len);
2005
+ pbw_free(wire);
2006
+ return out;
2007
+ }
2008
+
2009
+ static VALUE
2010
+ message_encode_delimited(VALUE self)
2011
+ {
2012
+ message_t *message;
2013
+ pbw_message *tree;
2014
+ int free_tree = 0;
2015
+ uint8_t *wire = NULL;
2016
+ size_t wire_len = 0;
2017
+ pbw_status status;
2018
+ VALUE out;
2019
+
2020
+ TypedData_Get_Struct(self, message_t, &message_data_type, message);
2021
+ if (message->kind == MSG_CLOSED) {
2022
+ rb_raise(eError, "%s: message is closed",
2023
+ type_name(message_type_of(self)));
2024
+ }
2025
+ tree = message_print_tree(self, message, &free_tree);
2026
+ status = pbw_encode_delimited(tree, &wire, &wire_len);
2027
+ if (free_tree) {
2028
+ pbw_message_free(tree);
2029
+ }
2030
+ if (status != PBW_OK) {
2031
+ rb_raise(eError, "%s: %s", type_name(message_type_of(self)),
2032
+ pbw_strerror(status));
2033
+ }
2034
+ out = rb_str_new((const char *)wire, (long)wire_len);
2035
+ pbw_free(wire);
2036
+ return out;
2037
+ }
2038
+
2039
+ /* One frame from a byte string: [message, bytes consumed]. The message is
2040
+ * a builder holding the decoded tree. */
2041
+ static VALUE
2042
+ message_decode_delimited(VALUE self, VALUE bytes)
2043
+ {
2044
+ message_t *message;
2045
+ type_t *type;
2046
+ pbw_message *tree = NULL;
2047
+ size_t consumed = 0;
2048
+ pbw_status status;
2049
+ VALUE out;
2050
+ VALUE pair[2];
2051
+
2052
+ TypedData_Get_Struct(self, message_t, &message_data_type, message);
2053
+ if (message->kind == MSG_CLOSED) {
2054
+ rb_raise(eError, "%s: message is closed",
2055
+ type_name(message_type_of(self)));
2056
+ }
2057
+ type = message_type_of(self);
2058
+ StringValue(bytes);
2059
+ status = pbw_decode_delimited(type->desc, RSTRING_PTR(bytes),
2060
+ (size_t)RSTRING_LEN(bytes), &tree,
2061
+ &consumed);
2062
+ if (status != PBW_OK) {
2063
+ rb_raise(eError, "%s: %s", type_name(type), pbw_strerror(status));
2064
+ }
2065
+ pair[0] = message_builder_new(message->type, message->holder, tree,
2066
+ Qnil, 1);
2067
+ pair[1] = SIZET2NUM(consumed);
2068
+ out = rb_ary_new_from_values(2, pair);
2069
+ return out;
2070
+ }
2071
+
1749
2072
  static VALUE
1750
2073
  message_close(VALUE self)
1751
2074
  {
@@ -1828,6 +2151,14 @@ Init_pbwire_ext(void)
1828
2151
  rb_define_method(cMessage, "[]", message_get, 1);
1829
2152
  rb_define_method(cMessage, "encode", message_encode, 0);
1830
2153
  rb_define_method(cMessage, "decode", message_decode, 1);
2154
+ rb_define_method(cMessage, "to_json", message_to_json, 0);
2155
+ rb_define_method(cMessage, "to_text", message_to_text, -1);
2156
+ rb_define_method(cMessage, "from_json", message_from_json, -1);
2157
+ rb_define_method(cMessage, "to_builder", message_to_builder, 0);
2158
+ rb_define_method(cMessage, "from_text", message_from_text, 1);
2159
+ rb_define_method(cMessage, "encode_deterministic", message_encode_deterministic, 0);
2160
+ rb_define_method(cMessage, "encode_delimited", message_encode_delimited, 0);
2161
+ rb_define_method(cMessage, "decode_delimited", message_decode_delimited, 1);
1831
2162
  rb_define_method(cMessage, "close", message_close, 0);
1832
2163
  rb_define_method(cMessage, "release", message_close, 0);
1833
2164
  rb_define_method(cMessage, "closed?", message_closed_p, 0);
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pbwire
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.7
4
+ version: 0.1.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - jinganix