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.
- checksums.yaml +4 -4
- data/README.md +24 -0
- data/ext/pbwire_ext/pbwire_ext.c +1159 -67
- metadata +1 -1
data/ext/pbwire_ext/pbwire_ext.c
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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 =
|
|
1574
|
-
|
|
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
|
-
|
|
1606
|
+
schema_s_load(int argc, VALUE *argv, VALUE klass)
|
|
1595
1607
|
{
|
|
1596
|
-
VALUE bytes
|
|
1608
|
+
VALUE bytes;
|
|
1609
|
+
VALUE opts = Qnil;
|
|
1610
|
+
pbw_limits storage;
|
|
1611
|
+
const pbw_limits *limits;
|
|
1597
1612
|
|
|
1598
|
-
|
|
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 =
|
|
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 =
|
|
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
|
|
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 =
|
|
1978
|
-
|
|
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 =
|
|
1981
|
-
|
|
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, "
|
|
1999
|
-
|
|
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
|
|
2120
|
+
message_from_text(int argc, VALUE *argv, VALUE self)
|
|
2034
2121
|
{
|
|
2035
|
-
|
|
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
|
|
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
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
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
|
-
|
|
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 ==
|
|
2138
|
-
|
|
2139
|
-
|
|
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
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
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 (
|
|
2147
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2459
|
+
rb_raise(eError, "%s: message is closed",
|
|
2460
|
+
type_name(message_type_of(self)));
|
|
2175
2461
|
}
|
|
2176
|
-
|
|
2177
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2183
|
-
|
|
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
|
-
|
|
2186
|
-
eError = rb_define_class_under(mPbwire, "Error", rb_eStandardError);
|
|
2511
|
+
field_t *f = type_field_by_number(st->type, number);
|
|
2187
2512
|
|
|
2188
|
-
|
|
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
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
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
|
-
|
|
2202
|
-
|
|
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,
|
|
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);
|