pbwire 0.1.15 → 0.1.16

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 +24 -0
  3. data/ext/pbwire_ext/pbwire_ext.c +1159 -67
  4. metadata +1 -1
@@ -103,7 +103,8 @@ typedef struct {
103
103
  enum {
104
104
  MSG_CLOSED = 0,
105
105
  MSG_BUILDER = 1, /* mutable pbw_message tree */
106
- MSG_WIRE = 2 /* read-only view over decoded bytes */
106
+ MSG_WIRE = 2, /* read-only view over decoded bytes */
107
+ MSG_CTX = 3 /* read-only borrow of a context's arena */
107
108
  };
108
109
 
109
110
  typedef struct {
@@ -115,6 +116,9 @@ typedef struct {
115
116
  VALUE parent; /* borrowed sub-message: the builder that owns tree */
116
117
  int owned;
117
118
  size_t enc_cap;
119
+ /* context borrow */
120
+ VALUE ctx_obj; /* Context object anchoring the arena */
121
+ unsigned ctx_gen; /* generation seen at decode */
118
122
  /* wire view */
119
123
  VALUE buffer; /* frozen String holding the bytes */
120
124
  const uint8_t *data;
@@ -276,6 +280,7 @@ message_mark(void *ptr)
276
280
  rb_gc_mark(message->type);
277
281
  rb_gc_mark(message->parent);
278
282
  rb_gc_mark(message->buffer);
283
+ rb_gc_mark(message->ctx_obj);
279
284
  }
280
285
 
281
286
  static void
@@ -341,6 +346,8 @@ message_alloc(VALUE klass)
341
346
  message->parent = Qnil;
342
347
  message->owned = 0;
343
348
  message->enc_cap = 0;
349
+ message->ctx_obj = Qnil;
350
+ message->ctx_gen = 0;
344
351
  message->buffer = Qnil;
345
352
  message->data = NULL;
346
353
  message->off = 0;
@@ -1155,6 +1162,10 @@ scalar_at(field_t *f, const uint8_t *p, const uint8_t *end, size_t *used)
1155
1162
  static VALUE message_wire_new(VALUE type, VALUE buffer, size_t off, size_t len,
1156
1163
  uint32_t depth);
1157
1164
  static VALUE field_value(message_t *m, field_t *f);
1165
+ static pbw_message *message_read_tree(VALUE obj, message_t **out);
1166
+ static pbw_message *ctx_tree_checked(VALUE obj, message_t *message);
1167
+ static void parse_limits(VALUE opts, pbw_limits *storage,
1168
+ const pbw_limits **out);
1158
1169
 
1159
1170
  /* One repeated slice: a single unpacked value, or a whole packed run. */
1160
1171
  static void
@@ -1562,7 +1573,7 @@ item_to_ruby(VALUE owner, field_t *f, pbw_value *value)
1562
1573
  }
1563
1574
 
1564
1575
  static VALUE
1565
- schema_s_load(VALUE klass, VALUE bytes)
1576
+ schema_do_load(VALUE klass, VALUE bytes, const pbw_limits *limits)
1566
1577
  {
1567
1578
  pbw_schema *schema = NULL;
1568
1579
  pbw_status status;
@@ -1570,8 +1581,9 @@ schema_s_load(VALUE klass, VALUE bytes)
1570
1581
  VALUE obj;
1571
1582
 
1572
1583
  StringValue(bytes);
1573
- status = pbw_descriptor_set_load(RSTRING_PTR(bytes),
1574
- (size_t)RSTRING_LEN(bytes), &schema);
1584
+ status = pbw_descriptor_set_load_limits(RSTRING_PTR(bytes),
1585
+ (size_t)RSTRING_LEN(bytes),
1586
+ limits, &schema);
1575
1587
  if (status != PBW_OK) {
1576
1588
  /* the reason lives inside the schema: copy it before freeing */
1577
1589
  char reason[192];
@@ -1591,11 +1603,31 @@ schema_s_load(VALUE klass, VALUE bytes)
1591
1603
  }
1592
1604
 
1593
1605
  static VALUE
1594
- schema_s_from_file(VALUE klass, VALUE path)
1606
+ schema_s_load(int argc, VALUE *argv, VALUE klass)
1595
1607
  {
1596
- VALUE bytes = rb_funcall(rb_cFile, rb_intern2("binread", 7), 1, path);
1608
+ VALUE bytes;
1609
+ VALUE opts = Qnil;
1610
+ pbw_limits storage;
1611
+ const pbw_limits *limits;
1597
1612
 
1598
- return schema_s_load(klass, bytes);
1613
+ rb_scan_args(argc, argv, "11", &bytes, &opts);
1614
+ parse_limits(opts, &storage, &limits);
1615
+ return schema_do_load(klass, bytes, limits);
1616
+ }
1617
+
1618
+ static VALUE
1619
+ schema_s_from_file(int argc, VALUE *argv, VALUE klass)
1620
+ {
1621
+ VALUE path;
1622
+ VALUE opts = Qnil;
1623
+ VALUE bytes;
1624
+ pbw_limits storage;
1625
+ const pbw_limits *limits;
1626
+
1627
+ rb_scan_args(argc, argv, "11", &path, &opts);
1628
+ parse_limits(opts, &storage, &limits);
1629
+ bytes = rb_funcall(rb_cFile, rb_intern2("binread", 7), 1, path);
1630
+ return schema_do_load(klass, bytes, limits);
1599
1631
  }
1600
1632
 
1601
1633
  static VALUE
@@ -1765,7 +1797,7 @@ message_get(VALUE self, VALUE key)
1765
1797
  {
1766
1798
  /* Also checks that no parent this builder was borrowed from has
1767
1799
  * been closed under it. */
1768
- pbw_message *tree = message_tree(self, &message);
1800
+ pbw_message *tree = message_read_tree(self, &message);
1769
1801
  pbw_value *value = pbw_message_get_field(
1770
1802
  tree, pbw_descriptor_field_at(type->desc, (size_t)f->index));
1771
1803
 
@@ -1810,7 +1842,7 @@ message_encode(VALUE self)
1810
1842
  return out;
1811
1843
  }
1812
1844
  {
1813
- pbw_message *tree = message_tree(self, &message);
1845
+ pbw_message *tree = message_read_tree(self, &message);
1814
1846
  size_t cap = message->enc_cap;
1815
1847
  size_t wire = pbw_message_wire_size(tree);
1816
1848
 
@@ -1845,17 +1877,40 @@ message_encode(VALUE self)
1845
1877
  }
1846
1878
 
1847
1879
  static VALUE
1848
- message_decode(VALUE self, VALUE bytes)
1880
+ message_decode(int argc, VALUE *argv, VALUE self)
1849
1881
  {
1850
1882
  message_t *message;
1883
+ type_t *type;
1884
+ VALUE bytes;
1885
+ VALUE opts = Qnil;
1886
+ pbw_limits storage;
1887
+ const pbw_limits *limits;
1851
1888
  VALUE buffer;
1852
1889
 
1890
+ rb_scan_args(argc, argv, "11", &bytes, &opts);
1853
1891
  TypedData_Get_Struct(self, message_t, &message_data_type, message);
1854
1892
  if (message->kind == MSG_CLOSED) {
1855
1893
  rb_raise(eError, "%s: message is closed",
1856
1894
  type_name(message_type_of(self)));
1857
1895
  }
1896
+ type = message_type_of(self);
1858
1897
  StringValue(bytes);
1898
+ parse_limits(opts, &storage, &limits);
1899
+ if (limits != NULL) {
1900
+ /* The view itself stays lazy: validate eagerly, then hand back
1901
+ * the usual bytes. */
1902
+ pbw_message *tmp = NULL;
1903
+ pbw_status status = pbw_decode_limits(type->desc,
1904
+ RSTRING_PTR(bytes),
1905
+ (size_t)RSTRING_LEN(bytes),
1906
+ limits, &tmp);
1907
+
1908
+ if (status != PBW_OK) {
1909
+ rb_raise(eError, "%s: %s", type_name(type),
1910
+ pbw_strerror(status));
1911
+ }
1912
+ pbw_message_free(tmp);
1913
+ }
1859
1914
  buffer = rb_str_new_frozen(bytes);
1860
1915
  return message_wire_new(message->type, buffer, 0,
1861
1916
  (size_t)RSTRING_LEN(buffer), 0);
@@ -1870,6 +1925,10 @@ message_print_tree(VALUE self, message_t *message, int *free_tree)
1870
1925
  *free_tree = 0;
1871
1926
  return message_tree(self, NULL);
1872
1927
  }
1928
+ if (message->kind == MSG_CTX) {
1929
+ *free_tree = 0;
1930
+ return ctx_tree_checked(self, message);
1931
+ }
1873
1932
  {
1874
1933
  type_t *type = message_type_of(self);
1875
1934
  pbw_message *tree = NULL;
@@ -1956,7 +2015,8 @@ message_to_text(int argc, VALUE *argv, VALUE self)
1956
2015
 
1957
2016
  /* Parses text (JSON mapping or text format) into a new builder tree. */
1958
2017
  static VALUE
1959
- message_parse(VALUE self, VALUE text, int json, int ignore_unknown)
2018
+ message_parse(VALUE self, VALUE text, int json, int ignore_unknown,
2019
+ const pbw_limits *limits)
1960
2020
  {
1961
2021
  message_t *message;
1962
2022
  type_t *type;
@@ -1974,11 +2034,13 @@ message_parse(VALUE self, VALUE text, int json, int ignore_unknown)
1974
2034
  pbw_json_parse_options opts;
1975
2035
 
1976
2036
  opts.ignore_unknown_fields = ignore_unknown;
1977
- status = pbw_json_parse_opts(type->desc, RSTRING_PTR(text),
1978
- (size_t)RSTRING_LEN(text), &opts, &tree);
2037
+ status = pbw_json_parse_limits(type->desc, RSTRING_PTR(text),
2038
+ (size_t)RSTRING_LEN(text), limits,
2039
+ &opts, &tree);
1979
2040
  } else {
1980
- status = pbw_text_parse(type->desc, RSTRING_PTR(text),
1981
- (size_t)RSTRING_LEN(text), &tree);
2041
+ status = pbw_text_parse_limits(type->desc, RSTRING_PTR(text),
2042
+ (size_t)RSTRING_LEN(text), limits,
2043
+ &tree);
1982
2044
  }
1983
2045
  if (status != PBW_OK) {
1984
2046
  rb_raise(eError, "%s: %s", type_name(type), pbw_strerror(status));
@@ -1994,9 +2056,13 @@ message_from_json(int argc, VALUE *argv, VALUE self)
1994
2056
  {
1995
2057
  VALUE text;
1996
2058
  VALUE ignore = Qfalse;
2059
+ VALUE opts = Qnil;
2060
+ pbw_limits storage;
2061
+ const pbw_limits *limits;
1997
2062
 
1998
- rb_scan_args(argc, argv, "11", &text, &ignore);
1999
- return message_parse(self, text, 1, RTEST(ignore));
2063
+ rb_scan_args(argc, argv, "12", &text, &ignore, &opts);
2064
+ parse_limits(opts, &storage, &limits);
2065
+ return message_parse(self, text, 1, RTEST(ignore), limits);
2000
2066
  }
2001
2067
 
2002
2068
  /* Materializes a decoded view into a builder holding the parsed tree, so
@@ -2018,6 +2084,27 @@ message_to_builder(VALUE self)
2018
2084
  if (message->kind == MSG_BUILDER) {
2019
2085
  return self;
2020
2086
  }
2087
+ if (message->kind == MSG_CTX) {
2088
+ pbw_message *borrowed = ctx_tree_checked(self, message);
2089
+ uint8_t *wire = NULL;
2090
+ size_t wire_len = 0;
2091
+ pbw_message *fresh = NULL;
2092
+
2093
+ type = message_type_of(self);
2094
+ status = pbw_encode(borrowed, &wire, &wire_len);
2095
+ if (status != PBW_OK) {
2096
+ rb_raise(eError, "%s: %s", type_name(type),
2097
+ pbw_strerror(status));
2098
+ }
2099
+ status = pbw_decode(type->desc, wire, wire_len, &fresh);
2100
+ pbw_free(wire);
2101
+ if (status != PBW_OK) {
2102
+ rb_raise(eError, "%s: %s", type_name(type),
2103
+ pbw_strerror(status));
2104
+ }
2105
+ return message_builder_new(message->type, message->holder, fresh,
2106
+ Qnil, 1);
2107
+ }
2021
2108
  type = message_type_of(self);
2022
2109
  status = pbw_decode(type->desc,
2023
2110
  (const uint8_t *)message->data + message->off,
@@ -2030,9 +2117,33 @@ message_to_builder(VALUE self)
2030
2117
  }
2031
2118
 
2032
2119
  static VALUE
2033
- message_from_text(VALUE self, VALUE text)
2120
+ message_from_text(int argc, VALUE *argv, VALUE self)
2034
2121
  {
2035
- return message_parse(self, text, 0, 0);
2122
+ VALUE text;
2123
+ VALUE opts = Qnil;
2124
+ pbw_limits storage;
2125
+ const pbw_limits *limits;
2126
+ message_t *message;
2127
+ type_t *type;
2128
+ pbw_message *tree = NULL;
2129
+ pbw_status status;
2130
+
2131
+ rb_scan_args(argc, argv, "11", &text, &opts);
2132
+ TypedData_Get_Struct(self, message_t, &message_data_type, message);
2133
+ if (message->kind == MSG_CLOSED) {
2134
+ rb_raise(eError, "%s: message is closed",
2135
+ type_name(message_type_of(self)));
2136
+ }
2137
+ type = message_type_of(self);
2138
+ StringValue(text);
2139
+ parse_limits(opts, &storage, &limits);
2140
+ status = pbw_text_parse_limits(type->desc, RSTRING_PTR(text),
2141
+ (size_t)RSTRING_LEN(text), limits, &tree);
2142
+ if (status != PBW_OK) {
2143
+ rb_raise(eError, "%s: %s", type_name(type), pbw_strerror(status));
2144
+ }
2145
+ return message_builder_new(message->type, message->holder, tree, Qnil,
2146
+ 1);
2036
2147
  }
2037
2148
 
2038
2149
  static VALUE
@@ -2098,16 +2209,21 @@ message_encode_delimited(VALUE self)
2098
2209
  /* One frame from a byte string: [message, bytes consumed]. The message is
2099
2210
  * a builder holding the decoded tree. */
2100
2211
  static VALUE
2101
- message_decode_delimited(VALUE self, VALUE bytes)
2212
+ message_decode_delimited(int argc, VALUE *argv, VALUE self)
2102
2213
  {
2103
2214
  message_t *message;
2104
2215
  type_t *type;
2216
+ VALUE bytes;
2217
+ VALUE opts = Qnil;
2218
+ pbw_limits storage;
2219
+ const pbw_limits *limits;
2105
2220
  pbw_message *tree = NULL;
2106
2221
  size_t consumed = 0;
2107
2222
  pbw_status status;
2108
2223
  VALUE out;
2109
2224
  VALUE pair[2];
2110
2225
 
2226
+ rb_scan_args(argc, argv, "11", &bytes, &opts);
2111
2227
  TypedData_Get_Struct(self, message_t, &message_data_type, message);
2112
2228
  if (message->kind == MSG_CLOSED) {
2113
2229
  rb_raise(eError, "%s: message is closed",
@@ -2115,9 +2231,10 @@ message_decode_delimited(VALUE self, VALUE bytes)
2115
2231
  }
2116
2232
  type = message_type_of(self);
2117
2233
  StringValue(bytes);
2118
- status = pbw_decode_delimited(type->desc, RSTRING_PTR(bytes),
2119
- (size_t)RSTRING_LEN(bytes), &tree,
2120
- &consumed);
2234
+ parse_limits(opts, &storage, &limits);
2235
+ status = pbw_decode_delimited_limits(type->desc, RSTRING_PTR(bytes),
2236
+ (size_t)RSTRING_LEN(bytes), limits,
2237
+ &tree, &consumed);
2121
2238
  if (status != PBW_OK) {
2122
2239
  rb_raise(eError, "%s: %s", type_name(type), pbw_strerror(status));
2123
2240
  }
@@ -2128,78 +2245,1033 @@ message_decode_delimited(VALUE self, VALUE bytes)
2128
2245
  return out;
2129
2246
  }
2130
2247
 
2248
+ /* Pool/context handles; the message borrow (MSG_CTX, below) anchors a
2249
+ * context object and checks its generation on every read. */
2250
+ typedef struct {
2251
+ pbw_ctx *ctx;
2252
+ pbw_pool *pool; /* NULL for standalone contexts */
2253
+ VALUE pool_obj; /* anchor while pooled */
2254
+ int released;
2255
+ unsigned gen; /* bumped by reset/release; messages hold their own */
2256
+ size_t retain_cap; /* tracked here; the core only takes a setter */
2257
+ } ctx_t;
2258
+
2259
+ typedef struct {
2260
+ pbw_pool *pool;
2261
+ } pool_t;
2262
+
2263
+ static VALUE cPool;
2264
+ static VALUE cContext;
2265
+
2266
+ static const rb_data_type_t ctx_data_type;
2267
+
2268
+ /* Optional limits hash (nil = core defaults): {max_bytes:, max_depth:}. */
2269
+ static void
2270
+ parse_limits(VALUE opts, pbw_limits *storage, const pbw_limits **out)
2271
+ {
2272
+ VALUE v;
2273
+
2274
+ storage->max_bytes = 0;
2275
+ storage->max_depth = 0;
2276
+ if (NIL_P(opts)) {
2277
+ *out = NULL;
2278
+ return;
2279
+ }
2280
+ Check_Type(opts, T_HASH);
2281
+ /* NUM2SIZET/NUM2UINT convert -1 to the unsigned maximum instead of
2282
+ * failing, which would silently turn a cap into "no cap"; check the sign
2283
+ * on a signed value first. */
2284
+ v = rb_hash_lookup(opts, ID2SYM(rb_intern("max_bytes")));
2285
+ if (!NIL_P(v)) {
2286
+ long long n = NUM2LL(v);
2287
+
2288
+ if (n < 0) {
2289
+ rb_raise(eError, "pbwire: max_bytes must not be negative");
2290
+ }
2291
+ storage->max_bytes = (size_t)n;
2292
+ }
2293
+ v = rb_hash_lookup(opts, ID2SYM(rb_intern("max_depth")));
2294
+ if (!NIL_P(v)) {
2295
+ long long n = NUM2LL(v);
2296
+
2297
+ if (n < 0 || n > (long long)UINT32_MAX) {
2298
+ rb_raise(eError, "pbwire: max_depth must be between 0 and %u",
2299
+ UINT32_MAX);
2300
+ }
2301
+ storage->max_depth = (uint32_t)n;
2302
+ }
2303
+ *out = storage;
2304
+ }
2305
+
2306
+ /* The tree behind a read: a builder's own tree, or a context borrow after
2307
+ * a generation check (dies with the context's reset). */
2308
+ static pbw_message *
2309
+ ctx_tree_checked(VALUE obj, message_t *message)
2310
+ {
2311
+ ctx_t *ctx;
2312
+
2313
+ TypedData_Get_Struct(message->ctx_obj, ctx_t, &ctx_data_type, ctx);
2314
+ if (ctx->ctx == NULL || ctx->released || ctx->gen != message->ctx_gen) {
2315
+ rb_raise(eError, "%s: context was reset",
2316
+ type_name(message_type_of(obj)));
2317
+ }
2318
+ return message->tree;
2319
+ }
2320
+
2321
+ /* Builder tree for writes, context borrow or builder tree for reads. */
2322
+ static pbw_message *
2323
+ message_read_tree(VALUE obj, message_t **out)
2324
+ {
2325
+ message_t *message;
2326
+
2327
+ TypedData_Get_Struct(obj, message_t, &message_data_type, message);
2328
+ if (out != NULL) {
2329
+ *out = message;
2330
+ }
2331
+ if (message->kind == MSG_CTX) {
2332
+ return ctx_tree_checked(obj, message);
2333
+ }
2334
+ return message_tree(obj, out);
2335
+ }
2336
+
2337
+ /* Exact number of bytes encode() writes: for callers that must know the
2338
+ * length before encoding (a length-prefixed frame, encode_to). */
2131
2339
  static VALUE
2132
- message_close(VALUE self)
2340
+ message_encoded_size(VALUE self)
2133
2341
  {
2134
2342
  message_t *message;
2343
+ type_t *type;
2344
+ pbw_message *tree;
2345
+ size_t n = 0;
2346
+ pbw_status status;
2135
2347
 
2136
2348
  TypedData_Get_Struct(self, message_t, &message_data_type, message);
2137
- if (message->kind == MSG_BUILDER) {
2138
- if (message->owned && message->tree != NULL) {
2139
- pbw_message_free(message->tree);
2349
+ if (message->kind == MSG_CLOSED) {
2350
+ rb_raise(eError, "%s: message is closed",
2351
+ type_name(message_type_of(self)));
2352
+ }
2353
+ type = message_type_of(self);
2354
+ if (message->kind == MSG_WIRE) {
2355
+ /* Wire views only borrow their bytes: size a throwaway tree. */
2356
+ pbw_message *tmp = NULL;
2357
+
2358
+ status = pbw_decode(type->desc, message->data + message->off,
2359
+ message->len, &tmp);
2360
+ if (status != PBW_OK) {
2361
+ rb_raise(eError, "%s: %s", type_name(type),
2362
+ pbw_strerror(status));
2140
2363
  }
2141
- message->tree = NULL;
2142
- holder_release(message->holder);
2143
- message->holder = NULL;
2144
- message->parent = Qnil;
2364
+ status = pbw_message_encoded_size(tmp, &n);
2365
+ pbw_message_free(tmp);
2366
+ } else {
2367
+ tree = message_read_tree(self, &message);
2368
+ status = pbw_message_encoded_size(tree, &n);
2145
2369
  }
2146
- if (message->slices != message->inline_slices) {
2147
- free(message->slices);
2148
- message->slices = message->inline_slices;
2149
- message->nslices = 0;
2150
- message->cap = SLICE_INLINE;
2370
+ if (status != PBW_OK) {
2371
+ rb_raise(eError, "%s: %s", type_name(type), pbw_strerror(status));
2151
2372
  }
2152
- message->buffer = Qnil;
2153
- message->data = NULL;
2154
- message->kind = MSG_CLOSED;
2155
- return self;
2373
+ return SIZET2NUM(n);
2156
2374
  }
2157
2375
 
2376
+ /* Encode into a fixed cap: no growth, PBW_ERR_LIMIT when it does not fit
2377
+ * (size + PBW_ENCODE_SLACK always fits; the exact size may not, as the
2378
+ * writer over-reserves tag/varint room). */
2158
2379
  static VALUE
2159
- message_closed_p(VALUE self)
2380
+ message_encode_cap(VALUE self, VALUE capval, int delimited)
2160
2381
  {
2161
2382
  message_t *message;
2383
+ pbw_message *tree;
2384
+ size_t cap = (size_t)NUM2SIZET(capval);
2385
+ VALUE out;
2386
+ size_t len = 0;
2387
+ pbw_status status;
2162
2388
 
2163
2389
  TypedData_Get_Struct(self, message_t, &message_data_type, message);
2164
- return message->kind == MSG_CLOSED ? Qtrue : Qfalse;
2390
+ if (message->kind == MSG_CLOSED) {
2391
+ rb_raise(eError, "%s: message is closed",
2392
+ type_name(message_type_of(self)));
2393
+ }
2394
+ if (message->kind == MSG_WIRE) {
2395
+ rb_raise(eError, "%s: decoded messages encode their own bytes",
2396
+ type_name(message_type_of(self)));
2397
+ }
2398
+ tree = message_read_tree(self, &message);
2399
+ out = rb_str_buf_new((long)(cap > 0 ? cap : 1));
2400
+ status = delimited
2401
+ ? pbw_encode_delimited_to(tree, RSTRING_PTR(out), cap, &len)
2402
+ : pbw_encode_to(tree, RSTRING_PTR(out), cap, &len);
2403
+ if (status != PBW_OK) {
2404
+ if (status == PBW_ERR_LIMIT) {
2405
+ rb_raise(eError, "%s: buffer too small",
2406
+ type_name(message_type_of(self)));
2407
+ }
2408
+ rb_raise(eError, "%s: %s", type_name(message_type_of(self)),
2409
+ pbw_strerror(status));
2410
+ }
2411
+ rb_str_set_len(out, (long)len);
2412
+ return out;
2165
2413
  }
2166
2414
 
2167
2415
  static VALUE
2168
- message_inspect(VALUE self)
2416
+ message_encode_to(VALUE self, VALUE cap)
2417
+ {
2418
+ return message_encode_cap(self, cap, 0);
2419
+ }
2420
+
2421
+ static VALUE
2422
+ message_encode_delimited_to(VALUE self, VALUE cap)
2423
+ {
2424
+ return message_encode_cap(self, cap, 1);
2425
+ }
2426
+
2427
+ struct sink_state {
2428
+ int aborted;
2429
+ };
2430
+
2431
+ /* Streams 8 KiB chunks to the block; the block aborts by returning false. */
2432
+ static pbw_status
2433
+ sink_yield(const uint8_t *data, size_t len, void *opaque)
2434
+ {
2435
+ struct sink_state *st = (struct sink_state *)opaque;
2436
+ VALUE chunk = rb_str_new((const char *)data, (long)len);
2437
+ VALUE ret = rb_yield(chunk);
2438
+
2439
+ if (!RTEST(ret)) {
2440
+ st->aborted = 1;
2441
+ return PBW_ERR_INVALID;
2442
+ }
2443
+ return PBW_OK;
2444
+ }
2445
+
2446
+ static VALUE
2447
+ message_encode_sink(VALUE self)
2169
2448
  {
2170
2449
  message_t *message;
2450
+ pbw_message *tree;
2451
+ struct sink_state st;
2452
+ pbw_status status;
2171
2453
 
2454
+ if (!rb_block_given_p()) {
2455
+ rb_raise(eError, "encode_sink needs a block");
2456
+ }
2172
2457
  TypedData_Get_Struct(self, message_t, &message_data_type, message);
2173
2458
  if (message->kind == MSG_CLOSED) {
2174
- return rb_sprintf("#<%" PRIsVALUE " closed>", rb_obj_class(self));
2459
+ rb_raise(eError, "%s: message is closed",
2460
+ type_name(message_type_of(self)));
2175
2461
  }
2176
- return rb_sprintf("#<%" PRIsVALUE " %s>", rb_obj_class(self),
2177
- type_name(message_type_of(self)));
2462
+ if (message->kind == MSG_WIRE) {
2463
+ /* Wire views only borrow their bytes: chunk the copy. */
2464
+ size_t off = 0;
2465
+
2466
+ while (off < message->len || (message->len == 0 && off == 0)) {
2467
+ size_t n = message->len - off < 8192 ? message->len - off : 8192;
2468
+ VALUE chunk = rb_str_new((const char *)message->data +
2469
+ message->off + off, (long)n);
2470
+ if (!RTEST(rb_yield(chunk))) {
2471
+ rb_raise(eError, "%s: sink aborted",
2472
+ type_name(message_type_of(self)));
2473
+ }
2474
+ off += n;
2475
+ if (message->len == 0) {
2476
+ break;
2477
+ }
2478
+ }
2479
+ return self;
2480
+ }
2481
+ tree = message_read_tree(self, &message);
2482
+ st.aborted = 0;
2483
+ /* A raising block longjmps through the core, which holds no
2484
+ * allocations across the sink call (stack buffer only). */
2485
+ status = pbw_encode_sink(tree, sink_yield, &st);
2486
+ if (status != PBW_OK) {
2487
+ if (st.aborted) {
2488
+ rb_raise(eError, "%s: sink aborted",
2489
+ type_name(message_type_of(self)));
2490
+ }
2491
+ rb_raise(eError, "%s: %s", type_name(message_type_of(self)),
2492
+ pbw_strerror(status));
2493
+ }
2494
+ return self;
2178
2495
  }
2179
2496
 
2180
- /* ------------------------------------------------------------- extension */
2497
+ struct visit_state {
2498
+ VALUE events; /* Array of [number, wire_type, value] */
2499
+ VALUE buffer; /* frozen String: the scanned bytes, kept alive */
2500
+ type_t *type;
2501
+ uint32_t depth;
2502
+ pbw_status status;
2503
+ };
2181
2504
 
2182
- void
2183
- Init_pbwire_ext(void)
2505
+ /* Best-effort value: typed through the schema when the number is known
2506
+ * (sub-messages become wire-view Messages), raw otherwise. */
2507
+ static VALUE
2508
+ visit_value(struct visit_state *st, uint32_t number, uint32_t wire_type,
2509
+ const pbw_visit_value *value)
2184
2510
  {
2185
- mPbwire = rb_define_module("Pbwire");
2186
- eError = rb_define_class_under(mPbwire, "Error", rb_eStandardError);
2511
+ field_t *f = type_field_by_number(st->type, number);
2187
2512
 
2188
- rb_define_const(mPbwire, "ABI_VERSION", INT2NUM(PBW_ABI_VERSION));
2513
+ if (f != NULL && f->type == PBW_TYPE_MESSAGE && wire_type == 2) {
2514
+ VALUE sub = rb_str_new_frozen(rb_str_new((const char *)value->data,
2515
+ (long)value->len));
2189
2516
 
2190
- cSchema = rb_define_class_under(mPbwire, "Schema", rb_cObject);
2191
- rb_define_alloc_func(cSchema, schema_alloc);
2192
- rb_define_singleton_method(cSchema, "from_bytes", schema_s_load, 1);
2193
- rb_define_singleton_method(cSchema, "from_file", schema_s_from_file, 1);
2194
- rb_define_method(cSchema, "message", schema_message, 1);
2195
- rb_define_method(cSchema, "close", schema_close, 0);
2196
- rb_define_method(cSchema, "release", schema_close, 0);
2197
- rb_define_method(cSchema, "closed?", schema_closed_p, 0);
2198
- rb_define_method(cSchema, "inspect", schema_inspect, 0);
2199
- rb_define_method(cSchema, "to_s", schema_inspect, 0);
2517
+ return message_wire_new(f->sub, sub, 0, value->len, st->depth + 1);
2518
+ }
2519
+ if (f != NULL && f->type == PBW_TYPE_GROUP && wire_type == 3) {
2520
+ VALUE sub = rb_str_new_frozen(rb_str_new((const char *)value->data,
2521
+ (long)value->len));
2200
2522
 
2201
- cType = rb_define_class_under(mPbwire, "Type", rb_cObject);
2202
- rb_define_alloc_func(cType, type_alloc);
2523
+ return message_wire_new(f->sub, sub, 0, value->len, st->depth + 1);
2524
+ }
2525
+ if (wire_type == 2 || wire_type == 3) {
2526
+ if (f != NULL && (f->type == PBW_TYPE_STRING ||
2527
+ f->type == PBW_TYPE_BYTES)) {
2528
+ return string_from(f->type, value->data, value->len);
2529
+ }
2530
+ return rb_str_new((const char *)value->data, (long)value->len);
2531
+ }
2532
+ if (f != NULL && wire_type == (uint32_t)f->wire) {
2533
+ if (f->type == PBW_TYPE_STRING || f->type == PBW_TYPE_BYTES) {
2534
+ return Qnil; /* cannot happen: strings are LEN */
2535
+ }
2536
+ return scalar_to_ruby(f->type, value->u64);
2537
+ }
2538
+ return ULL2NUM(value->u64);
2539
+ }
2540
+
2541
+ static pbw_status
2542
+ visit_collect(void *ud, const pbw_field *field, uint32_t number,
2543
+ uint32_t wire_type, const pbw_visit_value *value)
2544
+ {
2545
+ struct visit_state *st = (struct visit_state *)ud;
2546
+ VALUE ev;
2547
+
2548
+ (void)field;
2549
+ if (st->status != PBW_OK) {
2550
+ return st->status;
2551
+ }
2552
+ ev = rb_ary_new_capa(3);
2553
+ rb_ary_push(ev, UINT2NUM(number));
2554
+ rb_ary_push(ev, UINT2NUM(wire_type));
2555
+ rb_ary_push(ev, visit_value(st, number, wire_type, value));
2556
+ rb_ary_push(st->events, ev);
2557
+ return PBW_OK;
2558
+ }
2559
+
2560
+ /* Single-level scan of bytes: an Array of [number, wire_type, value] with
2561
+ * no message tree. Scalars arrive typed through the schema (varints
2562
+ * unfolded), LEN payloads and whole group fields as Strings, known
2563
+ * sub-messages as wire-view Messages. */
2564
+ static VALUE
2565
+ message_visit(int argc, VALUE *argv, VALUE self)
2566
+ {
2567
+ message_t *message;
2568
+ type_t *type;
2569
+ VALUE bytes;
2570
+ VALUE opts = Qnil;
2571
+ pbw_limits storage;
2572
+ const pbw_limits *limits;
2573
+ struct visit_state st;
2574
+ size_t fail_off = 0;
2575
+ pbw_status status;
2576
+
2577
+ rb_scan_args(argc, argv, "11", &bytes, &opts);
2578
+ TypedData_Get_Struct(self, message_t, &message_data_type, message);
2579
+ if (message->kind == MSG_CLOSED) {
2580
+ rb_raise(eError, "%s: message is closed",
2581
+ type_name(message_type_of(self)));
2582
+ }
2583
+ type = message_type_of(self);
2584
+ StringValue(bytes);
2585
+ parse_limits(opts, &storage, &limits);
2586
+ st.events = rb_ary_new();
2587
+ st.buffer = rb_str_new_frozen(bytes);
2588
+ st.type = type;
2589
+ st.depth = 0;
2590
+ st.status = PBW_OK;
2591
+ status = pbw_visit_limits(type->desc, RSTRING_PTR(st.buffer),
2592
+ (size_t)RSTRING_LEN(st.buffer), limits,
2593
+ visit_collect, &st, &fail_off);
2594
+ RB_GC_GUARD(st.events);
2595
+ RB_GC_GUARD(st.buffer);
2596
+ if (status != PBW_OK) {
2597
+ rb_raise(eError, "%s @%lu: %s", type_name(type),
2598
+ (unsigned long)fail_off, pbw_strerror(status));
2599
+ }
2600
+ return st.events;
2601
+ }
2602
+
2603
+ /* Loaded service names, for routing. */
2604
+ static VALUE
2605
+ schema_services(VALUE self)
2606
+ {
2607
+ pbw_schema *schema = schema_ptr(self);
2608
+ size_t n = pbw_schema_service_count(schema);
2609
+ size_t i;
2610
+ VALUE out = rb_ary_new_capa((long)n);
2611
+
2612
+ for (i = 0; i < n; i++) {
2613
+ rb_ary_push(out,
2614
+ rb_utf8_str_new_cstr(pbw_service_name(pbw_schema_service_at(schema, i))));
2615
+ }
2616
+ return out;
2617
+ }
2618
+
2619
+ static VALUE
2620
+ method_hash(const pbw_method *m)
2621
+ {
2622
+ VALUE h = rb_hash_new();
2623
+
2624
+ rb_hash_aset(h, ID2SYM(rb_intern("name")),
2625
+ rb_utf8_str_new_cstr(pbw_method_name(m)));
2626
+ rb_hash_aset(h, ID2SYM(rb_intern("input_type")),
2627
+ rb_utf8_str_new_cstr(pbw_method_input_type(m)));
2628
+ rb_hash_aset(h, ID2SYM(rb_intern("output_type")),
2629
+ rb_utf8_str_new_cstr(pbw_method_output_type(m)));
2630
+ rb_hash_aset(h, ID2SYM(rb_intern("client_streaming")),
2631
+ pbw_method_client_streaming(m) ? Qtrue : Qfalse);
2632
+ rb_hash_aset(h, ID2SYM(rb_intern("server_streaming")),
2633
+ pbw_method_server_streaming(m) ? Qtrue : Qfalse);
2634
+ return h;
2635
+ }
2636
+
2637
+ /* One service and its methods. */
2638
+ static VALUE
2639
+ schema_service(VALUE self, VALUE name)
2640
+ {
2641
+ pbw_schema *schema = schema_ptr(self);
2642
+ const pbw_service *svc;
2643
+ size_t n;
2644
+ size_t i;
2645
+ VALUE out;
2646
+ VALUE methods;
2647
+
2648
+ StringValue(name);
2649
+ svc = pbw_schema_service_find(schema, StringValueCStr(name));
2650
+ if (svc == NULL) {
2651
+ rb_raise(eError, "no service %" PRIsVALUE " in the schema", name);
2652
+ }
2653
+ n = pbw_service_method_count(svc);
2654
+ out = rb_hash_new();
2655
+ methods = rb_ary_new_capa((long)n);
2656
+ rb_hash_aset(out, ID2SYM(rb_intern("name")),
2657
+ rb_utf8_str_new_cstr(pbw_service_name(svc)));
2658
+ for (i = 0; i < n; i++) {
2659
+ rb_ary_push(methods, method_hash(pbw_service_method_at(svc, i)));
2660
+ }
2661
+ rb_hash_aset(out, ID2SYM(rb_intern("methods")), methods);
2662
+ return out;
2663
+ }
2664
+
2665
+ /* Resolve a method to its input/output message names for routing. */
2666
+ static VALUE
2667
+ schema_resolve(VALUE self, VALUE svc_name, VALUE method_name)
2668
+ {
2669
+ pbw_schema *schema = schema_ptr(self);
2670
+ const pbw_descriptor *in_desc = NULL;
2671
+ const pbw_descriptor *out_desc = NULL;
2672
+ VALUE out;
2673
+ pbw_status status;
2674
+
2675
+ StringValue(svc_name);
2676
+ StringValue(method_name);
2677
+ status = pbw_service_resolve(schema, StringValueCStr(svc_name),
2678
+ StringValueCStr(method_name), &in_desc,
2679
+ &out_desc);
2680
+ if (status != PBW_OK) {
2681
+ rb_raise(eError, "%" PRIsVALUE "/%" PRIsVALUE ": %s", svc_name,
2682
+ method_name, pbw_strerror(status));
2683
+ }
2684
+ out = rb_hash_new();
2685
+ rb_hash_aset(out, ID2SYM(rb_intern("input")),
2686
+ rb_utf8_str_new_cstr(pbw_descriptor_name(in_desc)));
2687
+ rb_hash_aset(out, ID2SYM(rb_intern("output")),
2688
+ rb_utf8_str_new_cstr(pbw_descriptor_name(out_desc)));
2689
+ return out;
2690
+ }
2691
+
2692
+ /* Pack already-encoded payload into a new Any tree of this type. */
2693
+ static VALUE
2694
+ message_any_pack(VALUE self, VALUE type_url, VALUE payload)
2695
+ {
2696
+ message_t *message;
2697
+ type_t *type;
2698
+ pbw_message *packed = NULL;
2699
+ pbw_status status;
2700
+ VALUE out;
2701
+
2702
+ TypedData_Get_Struct(self, message_t, &message_data_type, message);
2703
+ if (message->kind == MSG_CLOSED) {
2704
+ rb_raise(eError, "%s: message is closed",
2705
+ type_name(message_type_of(self)));
2706
+ }
2707
+ type = message_type_of(self);
2708
+ StringValue(type_url);
2709
+ StringValue(payload);
2710
+ if (memchr(RSTRING_PTR(type_url), '\0',
2711
+ (size_t)RSTRING_LEN(type_url)) != NULL) {
2712
+ rb_raise(eError, "%s: NUL in type_url", type_name(type));
2713
+ }
2714
+ status = pbw_any_pack(type->desc, RSTRING_PTR(type_url),
2715
+ RSTRING_PTR(payload), (size_t)RSTRING_LEN(payload),
2716
+ &packed);
2717
+ if (status != PBW_OK) {
2718
+ rb_raise(eError, "%s: %s", type_name(type), pbw_strerror(status));
2719
+ }
2720
+ out = message_builder_new(message->type, message->holder, packed, Qnil,
2721
+ 1);
2722
+ return out;
2723
+ }
2724
+
2725
+ static VALUE
2726
+ message_any_type_url(VALUE self)
2727
+ {
2728
+ message_t *message;
2729
+ pbw_message *tree = message_read_tree(self, &message);
2730
+ const char *url = pbw_any_type_url(tree);
2731
+
2732
+ if (url == NULL) {
2733
+ return Qnil;
2734
+ }
2735
+ return rb_utf8_str_new_cstr(url);
2736
+ }
2737
+
2738
+ static VALUE
2739
+ message_any_payload(VALUE self)
2740
+ {
2741
+ message_t *message;
2742
+ pbw_message *tree = message_read_tree(self, &message);
2743
+ size_t len = 0;
2744
+ const void *data = pbw_any_payload(tree, &len);
2745
+
2746
+ if (data == NULL) {
2747
+ return Qnil;
2748
+ }
2749
+ return rb_str_new((const char *)data, (long)len);
2750
+ }
2751
+
2752
+ /* A Timestamp/Duration tree of this type holding ms. */
2753
+ static VALUE
2754
+ message_time_from_millis(VALUE self, VALUE ms, int duration)
2755
+ {
2756
+ message_t *message;
2757
+ type_t *type;
2758
+ pbw_message *tree = NULL;
2759
+ int64_t millis = NUM2LL(ms);
2760
+ pbw_status status;
2761
+
2762
+ TypedData_Get_Struct(self, message_t, &message_data_type, message);
2763
+ if (message->kind == MSG_CLOSED) {
2764
+ rb_raise(eError, "%s: message is closed",
2765
+ type_name(message_type_of(self)));
2766
+ }
2767
+ type = message_type_of(self);
2768
+ status = duration ? pbw_duration_from_millis(type->desc, millis, &tree)
2769
+ : pbw_timestamp_from_millis(type->desc, millis, &tree);
2770
+ if (status != PBW_OK) {
2771
+ rb_raise(eError, "%s: %s", type_name(type), pbw_strerror(status));
2772
+ }
2773
+ return message_builder_new(message->type, message->holder, tree, Qnil,
2774
+ 1);
2775
+ }
2776
+
2777
+ static VALUE
2778
+ message_timestamp_from_millis(VALUE self, VALUE ms)
2779
+ {
2780
+ return message_time_from_millis(self, ms, 0);
2781
+ }
2782
+
2783
+ static VALUE
2784
+ message_duration_from_millis(VALUE self, VALUE ms)
2785
+ {
2786
+ return message_time_from_millis(self, ms, 1);
2787
+ }
2788
+
2789
+ static VALUE
2790
+ message_time_millis(VALUE self, int duration)
2791
+ {
2792
+ message_t *message;
2793
+ pbw_message *tree = message_read_tree(self, &message);
2794
+ int64_t ms = 0;
2795
+ pbw_status status = duration ? pbw_duration_to_millis(tree, &ms)
2796
+ : pbw_timestamp_to_millis(tree, &ms);
2797
+
2798
+ if (status != PBW_OK) {
2799
+ rb_raise(eError, "%s: %s", type_name(message_type_of(self)),
2800
+ pbw_strerror(status));
2801
+ }
2802
+ return LL2NUM(ms);
2803
+ }
2804
+
2805
+ static VALUE
2806
+ message_timestamp_millis(VALUE self)
2807
+ {
2808
+ return message_time_millis(self, 0);
2809
+ }
2810
+
2811
+ static VALUE
2812
+ message_duration_millis(VALUE self)
2813
+ {
2814
+ return message_time_millis(self, 1);
2815
+ }
2816
+
2817
+ /* A FieldMask's paths without materializing the list. */
2818
+ static VALUE
2819
+ message_fieldmask_paths(VALUE self)
2820
+ {
2821
+ message_t *message;
2822
+ pbw_message *tree = message_read_tree(self, &message);
2823
+ size_t n = pbw_fieldmask_path_count(tree);
2824
+ size_t i;
2825
+ VALUE out = rb_ary_new_capa((long)n);
2826
+
2827
+ for (i = 0; i < n; i++) {
2828
+ const char *path = pbw_fieldmask_path_at(tree, i);
2829
+
2830
+ rb_ary_push(out, rb_utf8_str_new_cstr(path != NULL ? path : ""));
2831
+ }
2832
+ return out;
2833
+ }
2834
+
2835
+ /* ------------------------------------------------- pool and context */
2836
+
2837
+ static void
2838
+ ctx_mark(void *ptr)
2839
+ {
2840
+ ctx_t *ctx = (ctx_t *)ptr;
2841
+
2842
+ if (ctx != NULL) {
2843
+ rb_gc_mark(ctx->pool_obj);
2844
+ }
2845
+ }
2846
+
2847
+ static void
2848
+ ctx_free(void *ptr)
2849
+ {
2850
+ ctx_t *ctx = (ctx_t *)ptr;
2851
+
2852
+ if (ctx == NULL) {
2853
+ return;
2854
+ }
2855
+ if (ctx->ctx != NULL) {
2856
+ if (ctx->pool != NULL && !ctx->released) {
2857
+ /* Collected while acquired: hand it back so the pool's
2858
+ * freelist never dangles. */
2859
+ pbw_pool_release(ctx->pool, ctx->ctx);
2860
+ } else if (ctx->pool == NULL) {
2861
+ pbw_ctx_free(ctx->ctx);
2862
+ }
2863
+ }
2864
+ xfree(ctx);
2865
+ }
2866
+
2867
+ static const rb_data_type_t ctx_data_type = {
2868
+ "pbwire/context",
2869
+ {ctx_mark, ctx_free, NULL},
2870
+ 0,
2871
+ 0,
2872
+ RUBY_TYPED_FREE_IMMEDIATELY,
2873
+ };
2874
+
2875
+ static void
2876
+ pool_free(void *ptr)
2877
+ {
2878
+ pool_t *pool = (pool_t *)ptr;
2879
+
2880
+ if (pool == NULL) {
2881
+ return;
2882
+ }
2883
+ if (pool->pool != NULL) {
2884
+ pbw_pool_free(pool->pool);
2885
+ }
2886
+ xfree(pool);
2887
+ }
2888
+
2889
+ static const rb_data_type_t pool_data_type = {
2890
+ "pbwire/pool",
2891
+ {NULL, pool_free, NULL},
2892
+ 0,
2893
+ 0,
2894
+ RUBY_TYPED_FREE_IMMEDIATELY,
2895
+ };
2896
+
2897
+ static ctx_t *
2898
+ ctx_ptr(VALUE obj)
2899
+ {
2900
+ ctx_t *ctx;
2901
+
2902
+ TypedData_Get_Struct(obj, ctx_t, &ctx_data_type, ctx);
2903
+ if (ctx->ctx == NULL || ctx->released) {
2904
+ rb_raise(eError, "context was released");
2905
+ }
2906
+ return ctx;
2907
+ }
2908
+
2909
+ /* Bounded freelist of contexts: acquire instead of Context.new, release
2910
+ * instead of reset. A released context is already reset. */
2911
+ static VALUE
2912
+ pool_s_new(int argc, VALUE *argv, VALUE klass)
2913
+ {
2914
+ VALUE max_idle = Qnil;
2915
+ VALUE retain_cap = Qnil;
2916
+ pool_t *pool;
2917
+ VALUE obj;
2918
+
2919
+ rb_scan_args(argc, argv, "02", &max_idle, &retain_cap);
2920
+ obj = typed_object_alloc(klass, &pool_data_type, (void **)&pool);
2921
+ pool->pool = pbw_pool_new(NIL_P(max_idle) ? 0 : (size_t)NUM2SIZET(max_idle),
2922
+ NIL_P(retain_cap) ? 0 : (size_t)NUM2SIZET(retain_cap));
2923
+ if (pool->pool == NULL) {
2924
+ rb_raise(eError, "out of memory");
2925
+ }
2926
+ return obj;
2927
+ }
2928
+
2929
+ /* Exclusive use until release; the context is already reset. */
2930
+ static VALUE
2931
+ pool_acquire(VALUE self)
2932
+ {
2933
+ pool_t *pool;
2934
+ pbw_ctx *ctx;
2935
+ ctx_t *c;
2936
+ VALUE obj;
2937
+
2938
+ TypedData_Get_Struct(self, pool_t, &pool_data_type, pool);
2939
+ if (pool->pool == NULL) {
2940
+ rb_raise(eError, "pool is closed");
2941
+ }
2942
+ ctx = pbw_pool_acquire(pool->pool);
2943
+ if (ctx == NULL) {
2944
+ rb_raise(eError, "out of memory");
2945
+ }
2946
+ obj = typed_object_alloc(cContext, &ctx_data_type, (void **)&c);
2947
+ c->ctx = ctx;
2948
+ c->pool = pool->pool;
2949
+ c->pool_obj = self;
2950
+ c->released = 0;
2951
+ c->gen = 0;
2952
+ c->retain_cap = 0;
2953
+ return obj;
2954
+ }
2955
+
2956
+ static VALUE
2957
+ pool_release(VALUE self, VALUE ctx_obj)
2958
+ {
2959
+ pool_t *pool;
2960
+ ctx_t *ctx;
2961
+
2962
+ TypedData_Get_Struct(self, pool_t, &pool_data_type, pool);
2963
+ TypedData_Get_Struct(ctx_obj, ctx_t, &ctx_data_type, ctx);
2964
+ if (pool->pool == NULL) {
2965
+ rb_raise(eError, "pool is closed");
2966
+ }
2967
+ if (ctx->ctx == NULL || ctx->released || ctx->pool != pool->pool) {
2968
+ rb_raise(eError, "context does not belong to this pool");
2969
+ }
2970
+ pbw_pool_release(pool->pool, ctx->ctx);
2971
+ ctx->released = 1;
2972
+ ctx->gen++;
2973
+ return self;
2974
+ }
2975
+
2976
+ static VALUE
2977
+ pool_idle(VALUE self)
2978
+ {
2979
+ pool_t *pool;
2980
+
2981
+ TypedData_Get_Struct(self, pool_t, &pool_data_type, pool);
2982
+ if (pool->pool == NULL) {
2983
+ rb_raise(eError, "pool is closed");
2984
+ }
2985
+ return SIZET2NUM(pbw_pool_idle(pool->pool));
2986
+ }
2987
+
2988
+ static VALUE
2989
+ pool_close(VALUE self)
2990
+ {
2991
+ pool_t *pool;
2992
+
2993
+ TypedData_Get_Struct(self, pool_t, &pool_data_type, pool);
2994
+ if (pool->pool != NULL) {
2995
+ pbw_pool_free(pool->pool);
2996
+ pool->pool = NULL;
2997
+ }
2998
+ return self;
2999
+ }
3000
+
3001
+ static VALUE
3002
+ context_s_new(VALUE klass)
3003
+ {
3004
+ ctx_t *ctx;
3005
+ VALUE obj = typed_object_alloc(klass, &ctx_data_type, (void **)&ctx);
3006
+
3007
+ ctx->ctx = pbw_ctx_new();
3008
+ if (ctx->ctx == NULL) {
3009
+ rb_raise(eError, "out of memory");
3010
+ }
3011
+ ctx->pool = NULL;
3012
+ ctx->pool_obj = Qnil;
3013
+ ctx->released = 0;
3014
+ ctx->gen = 0;
3015
+ ctx->retain_cap = 0;
3016
+ return obj;
3017
+ }
3018
+
3019
+ /* Release every allocation since the last reset, keeping the buffers.
3020
+ * Bumps the generation: outstanding borrows raise on next use. */
3021
+ static VALUE
3022
+ context_reset(VALUE self)
3023
+ {
3024
+ ctx_t *ctx = ctx_ptr(self);
3025
+
3026
+ pbw_ctx_reset(ctx->ctx);
3027
+ ctx->gen++;
3028
+ return self;
3029
+ }
3030
+
3031
+ static VALUE
3032
+ context_used(VALUE self)
3033
+ {
3034
+ ctx_t *ctx = ctx_ptr(self);
3035
+
3036
+ return SIZET2NUM(pbw_ctx_used(ctx->ctx));
3037
+ }
3038
+
3039
+ static VALUE
3040
+ context_retain_cap(VALUE self)
3041
+ {
3042
+ ctx_t *ctx = ctx_ptr(self);
3043
+
3044
+ return SIZET2NUM(ctx->retain_cap);
3045
+ }
3046
+
3047
+ static VALUE
3048
+ context_set_retain_cap(VALUE self, VALUE cap)
3049
+ {
3050
+ ctx_t *ctx = ctx_ptr(self);
3051
+
3052
+ ctx->retain_cap = (size_t)NUM2SIZET(cap);
3053
+ pbw_ctx_set_retain_cap(ctx->ctx, ctx->retain_cap);
3054
+ return self;
3055
+ }
3056
+
3057
+ static VALUE
3058
+ context_released_p(VALUE self)
3059
+ {
3060
+ ctx_t *ctx;
3061
+
3062
+ TypedData_Get_Struct(self, ctx_t, &ctx_data_type, ctx);
3063
+ return (ctx->ctx == NULL || ctx->released) ? Qtrue : Qfalse;
3064
+ }
3065
+
3066
+ static VALUE
3067
+ context_close(VALUE self)
3068
+ {
3069
+ ctx_t *ctx;
3070
+
3071
+ TypedData_Get_Struct(self, ctx_t, &ctx_data_type, ctx);
3072
+ if (ctx->ctx != NULL && !ctx->released) {
3073
+ if (ctx->pool != NULL) {
3074
+ pbw_pool_release(ctx->pool, ctx->ctx);
3075
+ } else {
3076
+ pbw_ctx_free(ctx->ctx);
3077
+ }
3078
+ ctx->ctx = NULL;
3079
+ ctx->released = 1;
3080
+ ctx->gen++;
3081
+ }
3082
+ return self;
3083
+ }
3084
+
3085
+ /* Decode into the arena: the message borrows the context (read-only, dies
3086
+ * with reset). */
3087
+ static VALUE
3088
+ context_decode(int argc, VALUE *argv, VALUE self)
3089
+ {
3090
+ ctx_t *ctx = ctx_ptr(self);
3091
+ VALUE schema_obj;
3092
+ VALUE type_name_val;
3093
+ VALUE bytes;
3094
+ VALUE opts = Qnil;
3095
+ pbw_schema *schema;
3096
+ const pbw_descriptor *desc;
3097
+ pbw_limits storage;
3098
+ const pbw_limits *limits;
3099
+ pbw_message *tree = NULL;
3100
+ pbw_status status;
3101
+ message_t *message;
3102
+ VALUE obj;
3103
+ VALUE type_obj;
3104
+
3105
+ rb_scan_args(argc, argv, "31", &schema_obj, &type_name_val, &bytes,
3106
+ &opts);
3107
+ schema = schema_ptr(schema_obj);
3108
+ StringValue(type_name_val);
3109
+ desc = pbw_schema_find(schema, StringValueCStr(type_name_val));
3110
+ if (desc == NULL || pbw_descriptor_is_enum(desc)) {
3111
+ rb_raise(eError, "no message %" PRIsVALUE " in the schema",
3112
+ type_name_val);
3113
+ }
3114
+ StringValue(bytes);
3115
+ parse_limits(opts, &storage, &limits);
3116
+ status = pbw_decode_ctx_limits(ctx->ctx, desc, RSTRING_PTR(bytes),
3117
+ (size_t)RSTRING_LEN(bytes), limits, &tree);
3118
+ if (status != PBW_OK) {
3119
+ rb_raise(eError, "%" PRIsVALUE ": %s", type_name_val,
3120
+ pbw_strerror(status));
3121
+ }
3122
+ type_obj = type_for(schema_obj, desc);
3123
+ obj = typed_object_alloc(cMessage, &message_data_type,
3124
+ (void **)&message);
3125
+ message->kind = MSG_CTX;
3126
+ message->type = type_obj;
3127
+ message->tree = tree;
3128
+ {
3129
+ schema_t *own;
3130
+
3131
+ TypedData_Get_Struct(schema_obj, schema_t, &schema_data_type, own);
3132
+ message->holder = own->holder;
3133
+ holder_ref(own->holder);
3134
+ }
3135
+ message->parent = Qnil;
3136
+ message->owned = 0;
3137
+ message->enc_cap = 0;
3138
+ message->buffer = Qnil;
3139
+ message->data = NULL;
3140
+ message->off = 0;
3141
+ message->len = 0;
3142
+ message->depth = 0;
3143
+ message->slices = message->inline_slices;
3144
+ message->nslices = 0;
3145
+ message->cap = SLICE_INLINE;
3146
+ message->ctx_obj = self;
3147
+ message->ctx_gen = ctx->gen;
3148
+ return obj;
3149
+ }
3150
+
3151
+ /* Encode a message through the context's buffer. */
3152
+ static VALUE
3153
+ context_encode(VALUE self, VALUE msg)
3154
+ {
3155
+ ctx_t *ctx = ctx_ptr(self);
3156
+ message_t *message;
3157
+ pbw_message *tree;
3158
+ const uint8_t *out = NULL;
3159
+ size_t len = 0;
3160
+ pbw_status status;
3161
+ VALUE str;
3162
+
3163
+ TypedData_Get_Struct(msg, message_t, &message_data_type, message);
3164
+ if (message->kind == MSG_CLOSED) {
3165
+ rb_raise(eError, "%s: message is closed",
3166
+ type_name(message_type_of(msg)));
3167
+ }
3168
+ if (message->kind == MSG_WIRE) {
3169
+ return rb_str_new((const char *)message->data + message->off,
3170
+ (long)message->len);
3171
+ }
3172
+ tree = message_read_tree(msg, &message);
3173
+ status = pbw_encode_ctx(ctx->ctx, tree, &out, &len);
3174
+ if (status != PBW_OK) {
3175
+ rb_raise(eError, "%s: %s", type_name(message_type_of(msg)),
3176
+ pbw_strerror(status));
3177
+ }
3178
+ str = rb_str_new((const char *)out, (long)len);
3179
+ return str;
3180
+ }
3181
+ static VALUE
3182
+ message_close(VALUE self)
3183
+ {
3184
+ message_t *message;
3185
+
3186
+ TypedData_Get_Struct(self, message_t, &message_data_type, message);
3187
+ if (message->kind == MSG_BUILDER || message->kind == MSG_CTX) {
3188
+ if (message->owned && message->tree != NULL) {
3189
+ pbw_message_free(message->tree);
3190
+ }
3191
+ message->tree = NULL;
3192
+ holder_release(message->holder);
3193
+ message->holder = NULL;
3194
+ message->parent = Qnil;
3195
+ message->ctx_obj = Qnil;
3196
+ }
3197
+ if (message->slices != message->inline_slices) {
3198
+ free(message->slices);
3199
+ message->slices = message->inline_slices;
3200
+ message->nslices = 0;
3201
+ message->cap = SLICE_INLINE;
3202
+ }
3203
+ message->buffer = Qnil;
3204
+ message->data = NULL;
3205
+ message->kind = MSG_CLOSED;
3206
+ return self;
3207
+ }
3208
+
3209
+ static VALUE
3210
+ message_closed_p(VALUE self)
3211
+ {
3212
+ message_t *message;
3213
+
3214
+ TypedData_Get_Struct(self, message_t, &message_data_type, message);
3215
+ return message->kind == MSG_CLOSED ? Qtrue : Qfalse;
3216
+ }
3217
+
3218
+ static VALUE
3219
+ message_inspect(VALUE self)
3220
+ {
3221
+ message_t *message;
3222
+
3223
+ TypedData_Get_Struct(self, message_t, &message_data_type, message);
3224
+ if (message->kind == MSG_CLOSED) {
3225
+ return rb_sprintf("#<%" PRIsVALUE " closed>", rb_obj_class(self));
3226
+ }
3227
+ return rb_sprintf("#<%" PRIsVALUE " %s>", rb_obj_class(self),
3228
+ type_name(message_type_of(self)));
3229
+ }
3230
+
3231
+ /* ------------------------------------------------------------- extension */
3232
+
3233
+ void
3234
+ Init_pbwire_ext(void)
3235
+ {
3236
+ mPbwire = rb_define_module("Pbwire");
3237
+ eError = rb_define_class_under(mPbwire, "Error", rb_eStandardError);
3238
+
3239
+ rb_define_const(mPbwire, "ABI_VERSION", INT2NUM(PBW_ABI_VERSION));
3240
+
3241
+ cSchema = rb_define_class_under(mPbwire, "Schema", rb_cObject);
3242
+ rb_define_alloc_func(cSchema, schema_alloc);
3243
+ rb_define_singleton_method(cSchema, "from_bytes", schema_s_load, -1);
3244
+ rb_define_singleton_method(cSchema, "from_file", schema_s_from_file, -1);
3245
+ rb_define_method(cSchema, "services", schema_services, 0);
3246
+ rb_define_method(cSchema, "service", schema_service, 1);
3247
+ rb_define_method(cSchema, "resolve", schema_resolve, 2);
3248
+ rb_define_method(cSchema, "message", schema_message, 1);
3249
+ rb_define_method(cSchema, "close", schema_close, 0);
3250
+ rb_define_method(cSchema, "release", schema_close, 0);
3251
+ rb_define_method(cSchema, "closed?", schema_closed_p, 0);
3252
+ rb_define_method(cSchema, "inspect", schema_inspect, 0);
3253
+ rb_define_method(cSchema, "to_s", schema_inspect, 0);
3254
+
3255
+ cType = rb_define_class_under(mPbwire, "Type", rb_cObject);
3256
+ rb_define_alloc_func(cType, type_alloc);
3257
+
3258
+ cPool = rb_define_class_under(mPbwire, "Pool", rb_cObject);
3259
+ rb_define_singleton_method(cPool, "new", pool_s_new, -1);
3260
+ rb_define_method(cPool, "acquire", pool_acquire, 0);
3261
+ rb_define_method(cPool, "release", pool_release, 1);
3262
+ rb_define_method(cPool, "idle", pool_idle, 0);
3263
+ rb_define_method(cPool, "close", pool_close, 0);
3264
+
3265
+ cContext = rb_define_class_under(mPbwire, "Context", rb_cObject);
3266
+ rb_define_singleton_method(cContext, "new", context_s_new, 0);
3267
+ rb_define_method(cContext, "decode", context_decode, -1);
3268
+ rb_define_method(cContext, "encode", context_encode, 1);
3269
+ rb_define_method(cContext, "reset", context_reset, 0);
3270
+ rb_define_method(cContext, "used", context_used, 0);
3271
+ rb_define_method(cContext, "retain_cap", context_retain_cap, 0);
3272
+ rb_define_method(cContext, "retain_cap=", context_set_retain_cap, 1);
3273
+ rb_define_method(cContext, "released?", context_released_p, 0);
3274
+ rb_define_method(cContext, "close", context_close, 0);
2203
3275
 
2204
3276
  cMessage = rb_define_class_under(mPbwire, "Message", rb_cObject);
2205
3277
  rb_define_alloc_func(cMessage, message_alloc);
@@ -2209,15 +3281,35 @@ Init_pbwire_ext(void)
2209
3281
  rb_define_method(cMessage, "get", message_get, 1);
2210
3282
  rb_define_method(cMessage, "[]", message_get, 1);
2211
3283
  rb_define_method(cMessage, "encode", message_encode, 0);
2212
- rb_define_method(cMessage, "decode", message_decode, 1);
3284
+ rb_define_method(cMessage, "decode", message_decode, -1);
3285
+ rb_define_method(cMessage, "encoded_size", message_encoded_size, 0);
3286
+ rb_define_method(cMessage, "encode_to", message_encode_to, 1);
3287
+ rb_define_method(cMessage, "encode_delimited_to",
3288
+ message_encode_delimited_to, 1);
3289
+ rb_define_method(cMessage, "encode_sink", message_encode_sink, 0);
3290
+ rb_define_method(cMessage, "visit", message_visit, -1);
3291
+ rb_define_method(cMessage, "any_pack", message_any_pack, 2);
3292
+ rb_define_method(cMessage, "any_type_url", message_any_type_url, 0);
3293
+ rb_define_method(cMessage, "any_payload", message_any_payload, 0);
3294
+ rb_define_method(cMessage, "timestamp_millis", message_timestamp_millis,
3295
+ 0);
3296
+ rb_define_method(cMessage, "duration_millis", message_duration_millis,
3297
+ 0);
3298
+ rb_define_method(cMessage, "timestamp_from_millis",
3299
+ message_timestamp_from_millis, 1);
3300
+ rb_define_method(cMessage, "duration_from_millis",
3301
+ message_duration_from_millis, 1);
3302
+ rb_define_method(cMessage, "fieldmask_paths", message_fieldmask_paths,
3303
+ 0);
2213
3304
  rb_define_method(cMessage, "to_json", message_to_json, 0);
2214
3305
  rb_define_method(cMessage, "to_text", message_to_text, -1);
2215
3306
  rb_define_method(cMessage, "from_json", message_from_json, -1);
2216
3307
  rb_define_method(cMessage, "to_builder", message_to_builder, 0);
2217
- rb_define_method(cMessage, "from_text", message_from_text, 1);
3308
+ rb_define_method(cMessage, "from_text", message_from_text, -1);
2218
3309
  rb_define_method(cMessage, "encode_deterministic", message_encode_deterministic, 0);
2219
3310
  rb_define_method(cMessage, "encode_delimited", message_encode_delimited, 0);
2220
- rb_define_method(cMessage, "decode_delimited", message_decode_delimited, 1);
3311
+ rb_define_method(cMessage, "decode_delimited", message_decode_delimited,
3312
+ -1);
2221
3313
  rb_define_method(cMessage, "close", message_close, 0);
2222
3314
  rb_define_method(cMessage, "release", message_close, 0);
2223
3315
  rb_define_method(cMessage, "closed?", message_closed_p, 0);