Chris Fallin | 973f425 | 2014-11-18 14:19:58 -0800 | [diff] [blame] | 1 | // Protocol Buffers - Google's data interchange format |
| 2 | // Copyright 2014 Google Inc. All rights reserved. |
| 3 | // https://developers.google.com/protocol-buffers/ |
| 4 | // |
| 5 | // Redistribution and use in source and binary forms, with or without |
| 6 | // modification, are permitted provided that the following conditions are |
| 7 | // met: |
| 8 | // |
| 9 | // * Redistributions of source code must retain the above copyright |
| 10 | // notice, this list of conditions and the following disclaimer. |
| 11 | // * Redistributions in binary form must reproduce the above |
| 12 | // copyright notice, this list of conditions and the following disclaimer |
| 13 | // in the documentation and/or other materials provided with the |
| 14 | // distribution. |
| 15 | // * Neither the name of Google Inc. nor the names of its |
| 16 | // contributors may be used to endorse or promote products derived from |
| 17 | // this software without specific prior written permission. |
| 18 | // |
| 19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 20 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| 21 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| 22 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| 23 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 24 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 25 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 26 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 27 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 28 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 29 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 30 | |
| 31 | #include "protobuf.h" |
| 32 | |
Josh Haberman | 8c717ad | 2015-07-08 14:14:58 -0700 | [diff] [blame] | 33 | // This function is equivalent to rb_str_cat(), but unlike the real |
| 34 | // rb_str_cat(), it doesn't leak memory in some versions of Ruby. |
| 35 | // For more information, see: |
| 36 | // https://bugs.ruby-lang.org/issues/11328 |
| 37 | VALUE noleak_rb_str_cat(VALUE rb_str, const char *str, long len) { |
Josh Haberman | 14e2b4f | 2015-09-28 08:56:14 -0700 | [diff] [blame] | 38 | char *p; |
Josh Haberman | 8c717ad | 2015-07-08 14:14:58 -0700 | [diff] [blame] | 39 | size_t oldlen = RSTRING_LEN(rb_str); |
| 40 | rb_str_modify_expand(rb_str, len); |
Josh Haberman | 14e2b4f | 2015-09-28 08:56:14 -0700 | [diff] [blame] | 41 | p = RSTRING_PTR(rb_str); |
Josh Haberman | 8c717ad | 2015-07-08 14:14:58 -0700 | [diff] [blame] | 42 | memcpy(p + oldlen, str, len); |
| 43 | rb_str_set_len(rb_str, oldlen + len); |
Josh Haberman | 14e2b4f | 2015-09-28 08:56:14 -0700 | [diff] [blame] | 44 | return rb_str; |
Josh Haberman | 8c717ad | 2015-07-08 14:14:58 -0700 | [diff] [blame] | 45 | } |
| 46 | |
Chris Fallin | 973f425 | 2014-11-18 14:19:58 -0800 | [diff] [blame] | 47 | // ----------------------------------------------------------------------------- |
| 48 | // Parsing. |
| 49 | // ----------------------------------------------------------------------------- |
| 50 | |
| 51 | #define DEREF(msg, ofs, type) *(type*)(((uint8_t *)msg) + ofs) |
| 52 | |
| 53 | // Creates a handlerdata that simply contains the offset for this field. |
| 54 | static const void* newhandlerdata(upb_handlers* h, uint32_t ofs) { |
| 55 | size_t* hd_ofs = ALLOC(size_t); |
| 56 | *hd_ofs = ofs; |
| 57 | upb_handlers_addcleanup(h, hd_ofs, free); |
| 58 | return hd_ofs; |
| 59 | } |
| 60 | |
| 61 | typedef struct { |
| 62 | size_t ofs; |
| 63 | const upb_msgdef *md; |
| 64 | } submsg_handlerdata_t; |
| 65 | |
| 66 | // Creates a handlerdata that contains offset and submessage type information. |
| 67 | static const void *newsubmsghandlerdata(upb_handlers* h, uint32_t ofs, |
| 68 | const upb_fielddef* f) { |
| 69 | submsg_handlerdata_t *hd = ALLOC(submsg_handlerdata_t); |
| 70 | hd->ofs = ofs; |
| 71 | hd->md = upb_fielddef_msgsubdef(f); |
| 72 | upb_handlers_addcleanup(h, hd, free); |
| 73 | return hd; |
| 74 | } |
| 75 | |
Chris Fallin | fcd8889 | 2015-01-13 18:14:39 -0800 | [diff] [blame] | 76 | typedef struct { |
Chris Fallin | e1b7d38 | 2015-01-14 17:14:05 -0800 | [diff] [blame] | 77 | size_t ofs; // union data slot |
| 78 | size_t case_ofs; // oneof_case field |
| 79 | uint32_t oneof_case_num; // oneof-case number to place in oneof_case field |
| 80 | const upb_msgdef *md; // msgdef, for oneof submessage handler |
Chris Fallin | fcd8889 | 2015-01-13 18:14:39 -0800 | [diff] [blame] | 81 | } oneof_handlerdata_t; |
| 82 | |
| 83 | static const void *newoneofhandlerdata(upb_handlers *h, |
| 84 | uint32_t ofs, |
| 85 | uint32_t case_ofs, |
| 86 | const upb_fielddef *f) { |
| 87 | oneof_handlerdata_t *hd = ALLOC(oneof_handlerdata_t); |
| 88 | hd->ofs = ofs; |
| 89 | hd->case_ofs = case_ofs; |
Chris Fallin | e1b7d38 | 2015-01-14 17:14:05 -0800 | [diff] [blame] | 90 | // We reuse the field tag number as a oneof union discriminant tag. Note that |
| 91 | // we don't expose these numbers to the user, so the only requirement is that |
| 92 | // we have some unique ID for each union case/possibility. The field tag |
| 93 | // numbers are already present and are easy to use so there's no reason to |
Chris Fallin | e2debef | 2015-01-14 18:02:27 -0800 | [diff] [blame] | 94 | // create a separate ID space. In addition, using the field tag number here |
| 95 | // lets us easily look up the field in the oneof accessor. |
Chris Fallin | e1b7d38 | 2015-01-14 17:14:05 -0800 | [diff] [blame] | 96 | hd->oneof_case_num = upb_fielddef_number(f); |
Chris Fallin | fcd8889 | 2015-01-13 18:14:39 -0800 | [diff] [blame] | 97 | if (upb_fielddef_type(f) == UPB_TYPE_MESSAGE) { |
| 98 | hd->md = upb_fielddef_msgsubdef(f); |
| 99 | } else { |
| 100 | hd->md = NULL; |
| 101 | } |
| 102 | upb_handlers_addcleanup(h, hd, free); |
| 103 | return hd; |
| 104 | } |
| 105 | |
Chris Fallin | 973f425 | 2014-11-18 14:19:58 -0800 | [diff] [blame] | 106 | // A handler that starts a repeated field. Gets the Repeated*Field instance for |
| 107 | // this field (such an instance always exists even in an empty message). |
| 108 | static void *startseq_handler(void* closure, const void* hd) { |
| 109 | MessageHeader* msg = closure; |
| 110 | const size_t *ofs = hd; |
Chris Fallin | 4c92289 | 2015-01-09 15:29:45 -0800 | [diff] [blame] | 111 | return (void*)DEREF(msg, *ofs, VALUE); |
Chris Fallin | 973f425 | 2014-11-18 14:19:58 -0800 | [diff] [blame] | 112 | } |
| 113 | |
Chris Fallin | fcd8889 | 2015-01-13 18:14:39 -0800 | [diff] [blame] | 114 | // Handlers that append primitive values to a repeated field. |
Chris Fallin | 973f425 | 2014-11-18 14:19:58 -0800 | [diff] [blame] | 115 | #define DEFINE_APPEND_HANDLER(type, ctype) \ |
| 116 | static bool append##type##_handler(void *closure, const void *hd, \ |
| 117 | ctype val) { \ |
| 118 | VALUE ary = (VALUE)closure; \ |
| 119 | RepeatedField_push_native(ary, &val); \ |
| 120 | return true; \ |
| 121 | } |
| 122 | |
| 123 | DEFINE_APPEND_HANDLER(bool, bool) |
| 124 | DEFINE_APPEND_HANDLER(int32, int32_t) |
| 125 | DEFINE_APPEND_HANDLER(uint32, uint32_t) |
| 126 | DEFINE_APPEND_HANDLER(float, float) |
| 127 | DEFINE_APPEND_HANDLER(int64, int64_t) |
| 128 | DEFINE_APPEND_HANDLER(uint64, uint64_t) |
| 129 | DEFINE_APPEND_HANDLER(double, double) |
| 130 | |
Chris Fallin | fcd8889 | 2015-01-13 18:14:39 -0800 | [diff] [blame] | 131 | // Appends a string to a repeated field. |
Chris Fallin | 973f425 | 2014-11-18 14:19:58 -0800 | [diff] [blame] | 132 | static void* appendstr_handler(void *closure, |
| 133 | const void *hd, |
| 134 | size_t size_hint) { |
| 135 | VALUE ary = (VALUE)closure; |
| 136 | VALUE str = rb_str_new2(""); |
| 137 | rb_enc_associate(str, kRubyStringUtf8Encoding); |
| 138 | RepeatedField_push(ary, str); |
| 139 | return (void*)str; |
| 140 | } |
| 141 | |
Chris Fallin | fcd8889 | 2015-01-13 18:14:39 -0800 | [diff] [blame] | 142 | // Appends a 'bytes' string to a repeated field. |
Chris Fallin | 973f425 | 2014-11-18 14:19:58 -0800 | [diff] [blame] | 143 | static void* appendbytes_handler(void *closure, |
| 144 | const void *hd, |
| 145 | size_t size_hint) { |
| 146 | VALUE ary = (VALUE)closure; |
| 147 | VALUE str = rb_str_new2(""); |
| 148 | rb_enc_associate(str, kRubyString8bitEncoding); |
| 149 | RepeatedField_push(ary, str); |
| 150 | return (void*)str; |
| 151 | } |
| 152 | |
| 153 | // Sets a non-repeated string field in a message. |
| 154 | static void* str_handler(void *closure, |
| 155 | const void *hd, |
| 156 | size_t size_hint) { |
| 157 | MessageHeader* msg = closure; |
| 158 | const size_t *ofs = hd; |
| 159 | VALUE str = rb_str_new2(""); |
| 160 | rb_enc_associate(str, kRubyStringUtf8Encoding); |
Chris Fallin | 4c92289 | 2015-01-09 15:29:45 -0800 | [diff] [blame] | 161 | DEREF(msg, *ofs, VALUE) = str; |
Chris Fallin | 973f425 | 2014-11-18 14:19:58 -0800 | [diff] [blame] | 162 | return (void*)str; |
| 163 | } |
| 164 | |
| 165 | // Sets a non-repeated 'bytes' field in a message. |
| 166 | static void* bytes_handler(void *closure, |
| 167 | const void *hd, |
| 168 | size_t size_hint) { |
| 169 | MessageHeader* msg = closure; |
| 170 | const size_t *ofs = hd; |
| 171 | VALUE str = rb_str_new2(""); |
| 172 | rb_enc_associate(str, kRubyString8bitEncoding); |
Chris Fallin | 4c92289 | 2015-01-09 15:29:45 -0800 | [diff] [blame] | 173 | DEREF(msg, *ofs, VALUE) = str; |
Chris Fallin | 973f425 | 2014-11-18 14:19:58 -0800 | [diff] [blame] | 174 | return (void*)str; |
| 175 | } |
| 176 | |
| 177 | static size_t stringdata_handler(void* closure, const void* hd, |
| 178 | const char* str, size_t len, |
| 179 | const upb_bufhandle* handle) { |
| 180 | VALUE rb_str = (VALUE)closure; |
Josh Haberman | 8c717ad | 2015-07-08 14:14:58 -0700 | [diff] [blame] | 181 | noleak_rb_str_cat(rb_str, str, len); |
Chris Fallin | 973f425 | 2014-11-18 14:19:58 -0800 | [diff] [blame] | 182 | return len; |
| 183 | } |
| 184 | |
| 185 | // Appends a submessage to a repeated field (a regular Ruby array for now). |
| 186 | static void *appendsubmsg_handler(void *closure, const void *hd) { |
| 187 | VALUE ary = (VALUE)closure; |
| 188 | const submsg_handlerdata_t *submsgdata = hd; |
| 189 | VALUE subdesc = |
| 190 | get_def_obj((void*)submsgdata->md); |
| 191 | VALUE subklass = Descriptor_msgclass(subdesc); |
Josh Haberman | a1daeab | 2015-07-10 11:56:06 -0700 | [diff] [blame] | 192 | MessageHeader* submsg; |
Chris Fallin | 973f425 | 2014-11-18 14:19:58 -0800 | [diff] [blame] | 193 | |
| 194 | VALUE submsg_rb = rb_class_new_instance(0, NULL, subklass); |
| 195 | RepeatedField_push(ary, submsg_rb); |
| 196 | |
Chris Fallin | 973f425 | 2014-11-18 14:19:58 -0800 | [diff] [blame] | 197 | TypedData_Get_Struct(submsg_rb, MessageHeader, &Message_type, submsg); |
| 198 | return submsg; |
| 199 | } |
| 200 | |
| 201 | // Sets a non-repeated submessage field in a message. |
| 202 | static void *submsg_handler(void *closure, const void *hd) { |
| 203 | MessageHeader* msg = closure; |
| 204 | const submsg_handlerdata_t* submsgdata = hd; |
| 205 | VALUE subdesc = |
| 206 | get_def_obj((void*)submsgdata->md); |
| 207 | VALUE subklass = Descriptor_msgclass(subdesc); |
Josh Haberman | a1daeab | 2015-07-10 11:56:06 -0700 | [diff] [blame] | 208 | VALUE submsg_rb; |
| 209 | MessageHeader* submsg; |
Chris Fallin | 973f425 | 2014-11-18 14:19:58 -0800 | [diff] [blame] | 210 | |
Chris Fallin | 4c92289 | 2015-01-09 15:29:45 -0800 | [diff] [blame] | 211 | if (DEREF(msg, submsgdata->ofs, VALUE) == Qnil) { |
| 212 | DEREF(msg, submsgdata->ofs, VALUE) = |
Chris Fallin | 973f425 | 2014-11-18 14:19:58 -0800 | [diff] [blame] | 213 | rb_class_new_instance(0, NULL, subklass); |
| 214 | } |
| 215 | |
Josh Haberman | a1daeab | 2015-07-10 11:56:06 -0700 | [diff] [blame] | 216 | submsg_rb = DEREF(msg, submsgdata->ofs, VALUE); |
Chris Fallin | 973f425 | 2014-11-18 14:19:58 -0800 | [diff] [blame] | 217 | TypedData_Get_Struct(submsg_rb, MessageHeader, &Message_type, submsg); |
| 218 | return submsg; |
| 219 | } |
| 220 | |
Chris Fallin | fd1a3ff | 2015-01-06 15:44:09 -0800 | [diff] [blame] | 221 | // Handler data for startmap/endmap handlers. |
| 222 | typedef struct { |
| 223 | size_t ofs; |
Chris Fallin | 80276ac | 2015-01-06 18:01:32 -0800 | [diff] [blame] | 224 | upb_fieldtype_t key_field_type; |
| 225 | upb_fieldtype_t value_field_type; |
Chris Fallin | 315b93f | 2015-02-13 14:32:09 -0800 | [diff] [blame] | 226 | |
| 227 | // We know that we can hold this reference because the handlerdata has the |
| 228 | // same lifetime as the upb_handlers struct, and the upb_handlers struct holds |
| 229 | // a reference to the upb_msgdef, which in turn has references to its subdefs. |
Chris Fallin | a2bea0a | 2015-02-12 16:08:01 -0800 | [diff] [blame] | 230 | const upb_def* value_field_subdef; |
Chris Fallin | fd1a3ff | 2015-01-06 15:44:09 -0800 | [diff] [blame] | 231 | } map_handlerdata_t; |
| 232 | |
| 233 | // Temporary frame for map parsing: at the beginning of a map entry message, a |
| 234 | // submsg handler allocates a frame to hold (i) a reference to the Map object |
| 235 | // into which this message will be inserted and (ii) storage slots to |
| 236 | // temporarily hold the key and value for this map entry until the end of the |
| 237 | // submessage. When the submessage ends, another handler is called to insert the |
| 238 | // value into the map. |
| 239 | typedef struct { |
| 240 | VALUE map; |
| 241 | char key_storage[NATIVE_SLOT_MAX_SIZE]; |
| 242 | char value_storage[NATIVE_SLOT_MAX_SIZE]; |
| 243 | } map_parse_frame_t; |
| 244 | |
Chris Fallin | fd1a3ff | 2015-01-06 15:44:09 -0800 | [diff] [blame] | 245 | // Handler to begin a map entry: allocates a temporary frame. This is the |
| 246 | // 'startsubmsg' handler on the msgdef that contains the map field. |
| 247 | static void *startmapentry_handler(void *closure, const void *hd) { |
| 248 | MessageHeader* msg = closure; |
| 249 | const map_handlerdata_t* mapdata = hd; |
Chris Fallin | 4c92289 | 2015-01-09 15:29:45 -0800 | [diff] [blame] | 250 | VALUE map_rb = DEREF(msg, mapdata->ofs, VALUE); |
Chris Fallin | fd1a3ff | 2015-01-06 15:44:09 -0800 | [diff] [blame] | 251 | |
| 252 | map_parse_frame_t* frame = ALLOC(map_parse_frame_t); |
| 253 | frame->map = map_rb; |
| 254 | |
Chris Fallin | 80276ac | 2015-01-06 18:01:32 -0800 | [diff] [blame] | 255 | native_slot_init(mapdata->key_field_type, &frame->key_storage); |
| 256 | native_slot_init(mapdata->value_field_type, &frame->value_storage); |
Chris Fallin | fd1a3ff | 2015-01-06 15:44:09 -0800 | [diff] [blame] | 257 | |
| 258 | return frame; |
| 259 | } |
| 260 | |
| 261 | // Handler to end a map entry: inserts the value defined during the message into |
| 262 | // the map. This is the 'endmsg' handler on the map entry msgdef. |
| 263 | static bool endmap_handler(void *closure, const void *hd, upb_status* s) { |
| 264 | map_parse_frame_t* frame = closure; |
| 265 | const map_handlerdata_t* mapdata = hd; |
| 266 | |
| 267 | VALUE key = native_slot_get( |
Chris Fallin | 80276ac | 2015-01-06 18:01:32 -0800 | [diff] [blame] | 268 | mapdata->key_field_type, Qnil, |
Chris Fallin | fd1a3ff | 2015-01-06 15:44:09 -0800 | [diff] [blame] | 269 | &frame->key_storage); |
Chris Fallin | a2bea0a | 2015-02-12 16:08:01 -0800 | [diff] [blame] | 270 | |
| 271 | VALUE value_field_typeclass = Qnil; |
Josh Haberman | a1daeab | 2015-07-10 11:56:06 -0700 | [diff] [blame] | 272 | VALUE value; |
| 273 | |
Chris Fallin | a2bea0a | 2015-02-12 16:08:01 -0800 | [diff] [blame] | 274 | if (mapdata->value_field_type == UPB_TYPE_MESSAGE || |
| 275 | mapdata->value_field_type == UPB_TYPE_ENUM) { |
| 276 | value_field_typeclass = get_def_obj(mapdata->value_field_subdef); |
| 277 | } |
| 278 | |
Josh Haberman | a1daeab | 2015-07-10 11:56:06 -0700 | [diff] [blame] | 279 | value = native_slot_get( |
Chris Fallin | a2bea0a | 2015-02-12 16:08:01 -0800 | [diff] [blame] | 280 | mapdata->value_field_type, value_field_typeclass, |
Chris Fallin | fd1a3ff | 2015-01-06 15:44:09 -0800 | [diff] [blame] | 281 | &frame->value_storage); |
| 282 | |
| 283 | Map_index_set(frame->map, key, value); |
| 284 | free(frame); |
| 285 | |
| 286 | return true; |
| 287 | } |
| 288 | |
| 289 | // Allocates a new map_handlerdata_t given the map entry message definition. If |
| 290 | // the offset of the field within the parent message is also given, that is |
| 291 | // added to the handler data as well. Note that this is called *twice* per map |
| 292 | // field: once in the parent message handler setup when setting the startsubmsg |
| 293 | // handler and once in the map entry message handler setup when setting the |
| 294 | // key/value and endmsg handlers. The reason is that there is no easy way to |
| 295 | // pass the handlerdata down to the sub-message handler setup. |
| 296 | static map_handlerdata_t* new_map_handlerdata( |
| 297 | size_t ofs, |
Chris Fallin | 4c92289 | 2015-01-09 15:29:45 -0800 | [diff] [blame] | 298 | const upb_msgdef* mapentry_def, |
| 299 | Descriptor* desc) { |
Josh Haberman | a1daeab | 2015-07-10 11:56:06 -0700 | [diff] [blame] | 300 | const upb_fielddef* key_field; |
| 301 | const upb_fielddef* value_field; |
Chris Fallin | fd1a3ff | 2015-01-06 15:44:09 -0800 | [diff] [blame] | 302 | map_handlerdata_t* hd = ALLOC(map_handlerdata_t); |
| 303 | hd->ofs = ofs; |
Josh Haberman | a1daeab | 2015-07-10 11:56:06 -0700 | [diff] [blame] | 304 | key_field = upb_msgdef_itof(mapentry_def, MAP_KEY_FIELD); |
Chris Fallin | 80276ac | 2015-01-06 18:01:32 -0800 | [diff] [blame] | 305 | assert(key_field != NULL); |
| 306 | hd->key_field_type = upb_fielddef_type(key_field); |
Josh Haberman | a1daeab | 2015-07-10 11:56:06 -0700 | [diff] [blame] | 307 | value_field = upb_msgdef_itof(mapentry_def, MAP_VALUE_FIELD); |
Chris Fallin | 80276ac | 2015-01-06 18:01:32 -0800 | [diff] [blame] | 308 | assert(value_field != NULL); |
| 309 | hd->value_field_type = upb_fielddef_type(value_field); |
Chris Fallin | a2bea0a | 2015-02-12 16:08:01 -0800 | [diff] [blame] | 310 | hd->value_field_subdef = upb_fielddef_subdef(value_field); |
Chris Fallin | 4c92289 | 2015-01-09 15:29:45 -0800 | [diff] [blame] | 311 | |
Chris Fallin | fd1a3ff | 2015-01-06 15:44:09 -0800 | [diff] [blame] | 312 | return hd; |
| 313 | } |
| 314 | |
Chris Fallin | fcd8889 | 2015-01-13 18:14:39 -0800 | [diff] [blame] | 315 | // Handlers that set primitive values in oneofs. |
| 316 | #define DEFINE_ONEOF_HANDLER(type, ctype) \ |
| 317 | static bool oneof##type##_handler(void *closure, const void *hd, \ |
| 318 | ctype val) { \ |
| 319 | const oneof_handlerdata_t *oneofdata = hd; \ |
Chris Fallin | e1b7d38 | 2015-01-14 17:14:05 -0800 | [diff] [blame] | 320 | DEREF(closure, oneofdata->case_ofs, uint32_t) = \ |
| 321 | oneofdata->oneof_case_num; \ |
Chris Fallin | fcd8889 | 2015-01-13 18:14:39 -0800 | [diff] [blame] | 322 | DEREF(closure, oneofdata->ofs, ctype) = val; \ |
| 323 | return true; \ |
| 324 | } |
| 325 | |
| 326 | DEFINE_ONEOF_HANDLER(bool, bool) |
| 327 | DEFINE_ONEOF_HANDLER(int32, int32_t) |
| 328 | DEFINE_ONEOF_HANDLER(uint32, uint32_t) |
| 329 | DEFINE_ONEOF_HANDLER(float, float) |
| 330 | DEFINE_ONEOF_HANDLER(int64, int64_t) |
| 331 | DEFINE_ONEOF_HANDLER(uint64, uint64_t) |
| 332 | DEFINE_ONEOF_HANDLER(double, double) |
| 333 | |
| 334 | #undef DEFINE_ONEOF_HANDLER |
| 335 | |
| 336 | // Handlers for strings in a oneof. |
| 337 | static void *oneofstr_handler(void *closure, |
| 338 | const void *hd, |
| 339 | size_t size_hint) { |
| 340 | MessageHeader* msg = closure; |
| 341 | const oneof_handlerdata_t *oneofdata = hd; |
| 342 | VALUE str = rb_str_new2(""); |
| 343 | rb_enc_associate(str, kRubyStringUtf8Encoding); |
Chris Fallin | e1b7d38 | 2015-01-14 17:14:05 -0800 | [diff] [blame] | 344 | DEREF(msg, oneofdata->case_ofs, uint32_t) = |
| 345 | oneofdata->oneof_case_num; |
Chris Fallin | fcd8889 | 2015-01-13 18:14:39 -0800 | [diff] [blame] | 346 | DEREF(msg, oneofdata->ofs, VALUE) = str; |
| 347 | return (void*)str; |
| 348 | } |
| 349 | |
| 350 | static void *oneofbytes_handler(void *closure, |
| 351 | const void *hd, |
| 352 | size_t size_hint) { |
| 353 | MessageHeader* msg = closure; |
| 354 | const oneof_handlerdata_t *oneofdata = hd; |
| 355 | VALUE str = rb_str_new2(""); |
| 356 | rb_enc_associate(str, kRubyString8bitEncoding); |
Chris Fallin | e1b7d38 | 2015-01-14 17:14:05 -0800 | [diff] [blame] | 357 | DEREF(msg, oneofdata->case_ofs, uint32_t) = |
| 358 | oneofdata->oneof_case_num; |
Chris Fallin | fcd8889 | 2015-01-13 18:14:39 -0800 | [diff] [blame] | 359 | DEREF(msg, oneofdata->ofs, VALUE) = str; |
| 360 | return (void*)str; |
| 361 | } |
| 362 | |
| 363 | // Handler for a submessage field in a oneof. |
| 364 | static void *oneofsubmsg_handler(void *closure, |
| 365 | const void *hd) { |
| 366 | MessageHeader* msg = closure; |
| 367 | const oneof_handlerdata_t *oneofdata = hd; |
| 368 | uint32_t oldcase = DEREF(msg, oneofdata->case_ofs, uint32_t); |
Chris Fallin | fcd8889 | 2015-01-13 18:14:39 -0800 | [diff] [blame] | 369 | |
| 370 | VALUE subdesc = |
| 371 | get_def_obj((void*)oneofdata->md); |
| 372 | VALUE subklass = Descriptor_msgclass(subdesc); |
Josh Haberman | a1daeab | 2015-07-10 11:56:06 -0700 | [diff] [blame] | 373 | VALUE submsg_rb; |
| 374 | MessageHeader* submsg; |
Chris Fallin | fcd8889 | 2015-01-13 18:14:39 -0800 | [diff] [blame] | 375 | |
Chris Fallin | e1b7d38 | 2015-01-14 17:14:05 -0800 | [diff] [blame] | 376 | if (oldcase != oneofdata->oneof_case_num || |
Chris Fallin | fcd8889 | 2015-01-13 18:14:39 -0800 | [diff] [blame] | 377 | DEREF(msg, oneofdata->ofs, VALUE) == Qnil) { |
| 378 | DEREF(msg, oneofdata->ofs, VALUE) = |
| 379 | rb_class_new_instance(0, NULL, subklass); |
| 380 | } |
Chris Fallin | eb33f9d | 2015-02-02 13:03:08 -0800 | [diff] [blame] | 381 | // Set the oneof case *after* allocating the new class instance -- otherwise, |
| 382 | // if the Ruby GC is invoked as part of a call into the VM, it might invoke |
| 383 | // our mark routines, and our mark routines might see the case value |
| 384 | // indicating a VALUE is present and expect a valid VALUE. See comment in |
| 385 | // layout_set() for more detail: basically, the change to the value and the |
| 386 | // case must be atomic w.r.t. the Ruby VM. |
Chris Fallin | 07b8b0f | 2015-01-26 13:52:51 -0800 | [diff] [blame] | 387 | DEREF(msg, oneofdata->case_ofs, uint32_t) = |
| 388 | oneofdata->oneof_case_num; |
Chris Fallin | fcd8889 | 2015-01-13 18:14:39 -0800 | [diff] [blame] | 389 | |
Josh Haberman | a1daeab | 2015-07-10 11:56:06 -0700 | [diff] [blame] | 390 | submsg_rb = DEREF(msg, oneofdata->ofs, VALUE); |
Chris Fallin | fcd8889 | 2015-01-13 18:14:39 -0800 | [diff] [blame] | 391 | TypedData_Get_Struct(submsg_rb, MessageHeader, &Message_type, submsg); |
| 392 | return submsg; |
| 393 | } |
| 394 | |
Chris Fallin | fd1a3ff | 2015-01-06 15:44:09 -0800 | [diff] [blame] | 395 | // Set up handlers for a repeated field. |
| 396 | static void add_handlers_for_repeated_field(upb_handlers *h, |
| 397 | const upb_fielddef *f, |
| 398 | size_t offset) { |
| 399 | upb_handlerattr attr = UPB_HANDLERATTR_INITIALIZER; |
| 400 | upb_handlerattr_sethandlerdata(&attr, newhandlerdata(h, offset)); |
| 401 | upb_handlers_setstartseq(h, f, startseq_handler, &attr); |
| 402 | upb_handlerattr_uninit(&attr); |
| 403 | |
| 404 | switch (upb_fielddef_type(f)) { |
| 405 | |
| 406 | #define SET_HANDLER(utype, ltype) \ |
| 407 | case utype: \ |
| 408 | upb_handlers_set##ltype(h, f, append##ltype##_handler, NULL); \ |
| 409 | break; |
| 410 | |
| 411 | SET_HANDLER(UPB_TYPE_BOOL, bool); |
| 412 | SET_HANDLER(UPB_TYPE_INT32, int32); |
| 413 | SET_HANDLER(UPB_TYPE_UINT32, uint32); |
| 414 | SET_HANDLER(UPB_TYPE_ENUM, int32); |
| 415 | SET_HANDLER(UPB_TYPE_FLOAT, float); |
| 416 | SET_HANDLER(UPB_TYPE_INT64, int64); |
| 417 | SET_HANDLER(UPB_TYPE_UINT64, uint64); |
| 418 | SET_HANDLER(UPB_TYPE_DOUBLE, double); |
| 419 | |
| 420 | #undef SET_HANDLER |
| 421 | |
| 422 | case UPB_TYPE_STRING: |
| 423 | case UPB_TYPE_BYTES: { |
| 424 | bool is_bytes = upb_fielddef_type(f) == UPB_TYPE_BYTES; |
| 425 | upb_handlers_setstartstr(h, f, is_bytes ? |
| 426 | appendbytes_handler : appendstr_handler, |
| 427 | NULL); |
| 428 | upb_handlers_setstring(h, f, stringdata_handler, NULL); |
Chris Fallin | 80276ac | 2015-01-06 18:01:32 -0800 | [diff] [blame] | 429 | break; |
Chris Fallin | fd1a3ff | 2015-01-06 15:44:09 -0800 | [diff] [blame] | 430 | } |
| 431 | case UPB_TYPE_MESSAGE: { |
| 432 | upb_handlerattr attr = UPB_HANDLERATTR_INITIALIZER; |
| 433 | upb_handlerattr_sethandlerdata(&attr, newsubmsghandlerdata(h, 0, f)); |
| 434 | upb_handlers_setstartsubmsg(h, f, appendsubmsg_handler, &attr); |
| 435 | upb_handlerattr_uninit(&attr); |
| 436 | break; |
| 437 | } |
| 438 | } |
| 439 | } |
| 440 | |
| 441 | // Set up handlers for a singular field. |
| 442 | static void add_handlers_for_singular_field(upb_handlers *h, |
| 443 | const upb_fielddef *f, |
| 444 | size_t offset) { |
| 445 | switch (upb_fielddef_type(f)) { |
| 446 | case UPB_TYPE_BOOL: |
| 447 | case UPB_TYPE_INT32: |
| 448 | case UPB_TYPE_UINT32: |
| 449 | case UPB_TYPE_ENUM: |
| 450 | case UPB_TYPE_FLOAT: |
| 451 | case UPB_TYPE_INT64: |
| 452 | case UPB_TYPE_UINT64: |
| 453 | case UPB_TYPE_DOUBLE: |
Chris Fallin | 4c92289 | 2015-01-09 15:29:45 -0800 | [diff] [blame] | 454 | upb_shim_set(h, f, offset, -1); |
Chris Fallin | fd1a3ff | 2015-01-06 15:44:09 -0800 | [diff] [blame] | 455 | break; |
| 456 | case UPB_TYPE_STRING: |
| 457 | case UPB_TYPE_BYTES: { |
| 458 | bool is_bytes = upb_fielddef_type(f) == UPB_TYPE_BYTES; |
| 459 | upb_handlerattr attr = UPB_HANDLERATTR_INITIALIZER; |
| 460 | upb_handlerattr_sethandlerdata(&attr, newhandlerdata(h, offset)); |
| 461 | upb_handlers_setstartstr(h, f, |
| 462 | is_bytes ? bytes_handler : str_handler, |
| 463 | &attr); |
| 464 | upb_handlers_setstring(h, f, stringdata_handler, &attr); |
| 465 | upb_handlerattr_uninit(&attr); |
| 466 | break; |
| 467 | } |
| 468 | case UPB_TYPE_MESSAGE: { |
| 469 | upb_handlerattr attr = UPB_HANDLERATTR_INITIALIZER; |
| 470 | upb_handlerattr_sethandlerdata(&attr, newsubmsghandlerdata(h, offset, f)); |
| 471 | upb_handlers_setstartsubmsg(h, f, submsg_handler, &attr); |
| 472 | upb_handlerattr_uninit(&attr); |
| 473 | break; |
| 474 | } |
| 475 | } |
| 476 | } |
| 477 | |
| 478 | // Adds handlers to a map field. |
| 479 | static void add_handlers_for_mapfield(upb_handlers* h, |
| 480 | const upb_fielddef* fielddef, |
Chris Fallin | 4c92289 | 2015-01-09 15:29:45 -0800 | [diff] [blame] | 481 | size_t offset, |
| 482 | Descriptor* desc) { |
Chris Fallin | fd1a3ff | 2015-01-06 15:44:09 -0800 | [diff] [blame] | 483 | const upb_msgdef* map_msgdef = upb_fielddef_msgsubdef(fielddef); |
Chris Fallin | 4c92289 | 2015-01-09 15:29:45 -0800 | [diff] [blame] | 484 | map_handlerdata_t* hd = new_map_handlerdata(offset, map_msgdef, desc); |
Chris Fallin | fd1a3ff | 2015-01-06 15:44:09 -0800 | [diff] [blame] | 485 | upb_handlerattr attr = UPB_HANDLERATTR_INITIALIZER; |
Josh Haberman | a1daeab | 2015-07-10 11:56:06 -0700 | [diff] [blame] | 486 | |
| 487 | upb_handlers_addcleanup(h, hd, free); |
Chris Fallin | fd1a3ff | 2015-01-06 15:44:09 -0800 | [diff] [blame] | 488 | upb_handlerattr_sethandlerdata(&attr, hd); |
Chris Fallin | fd1a3ff | 2015-01-06 15:44:09 -0800 | [diff] [blame] | 489 | upb_handlers_setstartsubmsg(h, fielddef, startmapentry_handler, &attr); |
| 490 | upb_handlerattr_uninit(&attr); |
| 491 | } |
| 492 | |
| 493 | // Adds handlers to a map-entry msgdef. |
| 494 | static void add_handlers_for_mapentry(const upb_msgdef* msgdef, |
Chris Fallin | 4c92289 | 2015-01-09 15:29:45 -0800 | [diff] [blame] | 495 | upb_handlers* h, |
| 496 | Descriptor* desc) { |
Chris Fallin | 80276ac | 2015-01-06 18:01:32 -0800 | [diff] [blame] | 497 | const upb_fielddef* key_field = map_entry_key(msgdef); |
| 498 | const upb_fielddef* value_field = map_entry_value(msgdef); |
Chris Fallin | 4c92289 | 2015-01-09 15:29:45 -0800 | [diff] [blame] | 499 | map_handlerdata_t* hd = new_map_handlerdata(0, msgdef, desc); |
Chris Fallin | fd1a3ff | 2015-01-06 15:44:09 -0800 | [diff] [blame] | 500 | upb_handlerattr attr = UPB_HANDLERATTR_INITIALIZER; |
Josh Haberman | a1daeab | 2015-07-10 11:56:06 -0700 | [diff] [blame] | 501 | |
| 502 | upb_handlers_addcleanup(h, hd, free); |
Chris Fallin | fd1a3ff | 2015-01-06 15:44:09 -0800 | [diff] [blame] | 503 | upb_handlerattr_sethandlerdata(&attr, hd); |
| 504 | upb_handlers_setendmsg(h, endmap_handler, &attr); |
| 505 | |
| 506 | add_handlers_for_singular_field( |
Chris Fallin | 80276ac | 2015-01-06 18:01:32 -0800 | [diff] [blame] | 507 | h, key_field, |
Chris Fallin | 4c92289 | 2015-01-09 15:29:45 -0800 | [diff] [blame] | 508 | offsetof(map_parse_frame_t, key_storage)); |
Chris Fallin | fd1a3ff | 2015-01-06 15:44:09 -0800 | [diff] [blame] | 509 | add_handlers_for_singular_field( |
Chris Fallin | 80276ac | 2015-01-06 18:01:32 -0800 | [diff] [blame] | 510 | h, value_field, |
Chris Fallin | 4c92289 | 2015-01-09 15:29:45 -0800 | [diff] [blame] | 511 | offsetof(map_parse_frame_t, value_storage)); |
Chris Fallin | fd1a3ff | 2015-01-06 15:44:09 -0800 | [diff] [blame] | 512 | } |
| 513 | |
Chris Fallin | fcd8889 | 2015-01-13 18:14:39 -0800 | [diff] [blame] | 514 | // Set up handlers for a oneof field. |
| 515 | static void add_handlers_for_oneof_field(upb_handlers *h, |
| 516 | const upb_fielddef *f, |
| 517 | size_t offset, |
| 518 | size_t oneof_case_offset) { |
| 519 | |
| 520 | upb_handlerattr attr = UPB_HANDLERATTR_INITIALIZER; |
| 521 | upb_handlerattr_sethandlerdata( |
| 522 | &attr, newoneofhandlerdata(h, offset, oneof_case_offset, f)); |
| 523 | |
| 524 | switch (upb_fielddef_type(f)) { |
| 525 | |
| 526 | #define SET_HANDLER(utype, ltype) \ |
| 527 | case utype: \ |
| 528 | upb_handlers_set##ltype(h, f, oneof##ltype##_handler, &attr); \ |
| 529 | break; |
| 530 | |
| 531 | SET_HANDLER(UPB_TYPE_BOOL, bool); |
| 532 | SET_HANDLER(UPB_TYPE_INT32, int32); |
| 533 | SET_HANDLER(UPB_TYPE_UINT32, uint32); |
| 534 | SET_HANDLER(UPB_TYPE_ENUM, int32); |
| 535 | SET_HANDLER(UPB_TYPE_FLOAT, float); |
| 536 | SET_HANDLER(UPB_TYPE_INT64, int64); |
| 537 | SET_HANDLER(UPB_TYPE_UINT64, uint64); |
| 538 | SET_HANDLER(UPB_TYPE_DOUBLE, double); |
| 539 | |
| 540 | #undef SET_HANDLER |
| 541 | |
| 542 | case UPB_TYPE_STRING: |
| 543 | case UPB_TYPE_BYTES: { |
| 544 | bool is_bytes = upb_fielddef_type(f) == UPB_TYPE_BYTES; |
| 545 | upb_handlers_setstartstr(h, f, is_bytes ? |
| 546 | oneofbytes_handler : oneofstr_handler, |
| 547 | &attr); |
| 548 | upb_handlers_setstring(h, f, stringdata_handler, NULL); |
| 549 | break; |
| 550 | } |
| 551 | case UPB_TYPE_MESSAGE: { |
| 552 | upb_handlers_setstartsubmsg(h, f, oneofsubmsg_handler, &attr); |
| 553 | break; |
| 554 | } |
| 555 | } |
| 556 | |
| 557 | upb_handlerattr_uninit(&attr); |
| 558 | } |
| 559 | |
| 560 | |
Chris Fallin | 973f425 | 2014-11-18 14:19:58 -0800 | [diff] [blame] | 561 | static void add_handlers_for_message(const void *closure, upb_handlers *h) { |
Chris Fallin | fd1a3ff | 2015-01-06 15:44:09 -0800 | [diff] [blame] | 562 | const upb_msgdef* msgdef = upb_handlers_msgdef(h); |
| 563 | Descriptor* desc = ruby_to_Descriptor(get_def_obj((void*)msgdef)); |
Josh Haberman | a1daeab | 2015-07-10 11:56:06 -0700 | [diff] [blame] | 564 | upb_msg_field_iter i; |
Chris Fallin | fd1a3ff | 2015-01-06 15:44:09 -0800 | [diff] [blame] | 565 | |
| 566 | // If this is a mapentry message type, set up a special set of handlers and |
| 567 | // bail out of the normal (user-defined) message type handling. |
| 568 | if (upb_msgdef_mapentry(msgdef)) { |
Chris Fallin | 4c92289 | 2015-01-09 15:29:45 -0800 | [diff] [blame] | 569 | add_handlers_for_mapentry(msgdef, h, desc); |
Chris Fallin | fd1a3ff | 2015-01-06 15:44:09 -0800 | [diff] [blame] | 570 | return; |
| 571 | } |
| 572 | |
Chris Fallin | 973f425 | 2014-11-18 14:19:58 -0800 | [diff] [blame] | 573 | // Ensure layout exists. We may be invoked to create handlers for a given |
| 574 | // message if we are included as a submsg of another message type before our |
| 575 | // class is actually built, so to work around this, we just create the layout |
| 576 | // (and handlers, in the class-building function) on-demand. |
| 577 | if (desc->layout == NULL) { |
| 578 | desc->layout = create_layout(desc->msgdef); |
| 579 | } |
| 580 | |
Chris Fallin | fcd8889 | 2015-01-13 18:14:39 -0800 | [diff] [blame] | 581 | for (upb_msg_field_begin(&i, desc->msgdef); |
| 582 | !upb_msg_field_done(&i); |
| 583 | upb_msg_field_next(&i)) { |
Chris Fallin | 973f425 | 2014-11-18 14:19:58 -0800 | [diff] [blame] | 584 | const upb_fielddef *f = upb_msg_iter_field(&i); |
Chris Fallin | fcd8889 | 2015-01-13 18:14:39 -0800 | [diff] [blame] | 585 | size_t offset = desc->layout->fields[upb_fielddef_index(f)].offset + |
| 586 | sizeof(MessageHeader); |
Chris Fallin | 973f425 | 2014-11-18 14:19:58 -0800 | [diff] [blame] | 587 | |
Chris Fallin | fcd8889 | 2015-01-13 18:14:39 -0800 | [diff] [blame] | 588 | if (upb_fielddef_containingoneof(f)) { |
Chris Fallin | 9de35e7 | 2015-01-26 11:23:19 -0800 | [diff] [blame] | 589 | size_t oneof_case_offset = |
| 590 | desc->layout->fields[upb_fielddef_index(f)].case_offset + |
| 591 | sizeof(MessageHeader); |
Chris Fallin | fcd8889 | 2015-01-13 18:14:39 -0800 | [diff] [blame] | 592 | add_handlers_for_oneof_field(h, f, offset, oneof_case_offset); |
| 593 | } else if (is_map_field(f)) { |
Chris Fallin | 4c92289 | 2015-01-09 15:29:45 -0800 | [diff] [blame] | 594 | add_handlers_for_mapfield(h, f, offset, desc); |
Chris Fallin | fd1a3ff | 2015-01-06 15:44:09 -0800 | [diff] [blame] | 595 | } else if (upb_fielddef_isseq(f)) { |
| 596 | add_handlers_for_repeated_field(h, f, offset); |
| 597 | } else { |
| 598 | add_handlers_for_singular_field(h, f, offset); |
Chris Fallin | 973f425 | 2014-11-18 14:19:58 -0800 | [diff] [blame] | 599 | } |
| 600 | } |
| 601 | } |
| 602 | |
| 603 | // Creates upb handlers for populating a message. |
| 604 | static const upb_handlers *new_fill_handlers(Descriptor* desc, |
| 605 | const void* owner) { |
| 606 | // TODO(cfallin, haberman): once upb gets a caching/memoization layer for |
| 607 | // handlers, reuse subdef handlers so that e.g. if we already parse |
| 608 | // B-with-field-of-type-C, we don't have to rebuild the whole hierarchy to |
| 609 | // parse A-with-field-of-type-B-with-field-of-type-C. |
| 610 | return upb_handlers_newfrozen(desc->msgdef, owner, |
| 611 | add_handlers_for_message, NULL); |
| 612 | } |
| 613 | |
| 614 | // Constructs the handlers for filling a message's data into an in-memory |
| 615 | // object. |
| 616 | const upb_handlers* get_fill_handlers(Descriptor* desc) { |
| 617 | if (!desc->fill_handlers) { |
| 618 | desc->fill_handlers = |
| 619 | new_fill_handlers(desc, &desc->fill_handlers); |
| 620 | } |
| 621 | return desc->fill_handlers; |
| 622 | } |
| 623 | |
| 624 | // Constructs the upb decoder method for parsing messages of this type. |
| 625 | // This is called from the message class creation code. |
| 626 | const upb_pbdecodermethod *new_fillmsg_decodermethod(Descriptor* desc, |
| 627 | const void* owner) { |
| 628 | const upb_handlers* handlers = get_fill_handlers(desc); |
| 629 | upb_pbdecodermethodopts opts; |
| 630 | upb_pbdecodermethodopts_init(&opts, handlers); |
| 631 | |
Josh Haberman | a1daeab | 2015-07-10 11:56:06 -0700 | [diff] [blame] | 632 | return upb_pbdecodermethod_new(&opts, owner); |
Chris Fallin | 973f425 | 2014-11-18 14:19:58 -0800 | [diff] [blame] | 633 | } |
| 634 | |
| 635 | static const upb_pbdecodermethod *msgdef_decodermethod(Descriptor* desc) { |
| 636 | if (desc->fill_method == NULL) { |
| 637 | desc->fill_method = new_fillmsg_decodermethod( |
| 638 | desc, &desc->fill_method); |
| 639 | } |
| 640 | return desc->fill_method; |
| 641 | } |
| 642 | |
Josh Haberman | 78da666 | 2016-01-13 19:05:43 -0800 | [diff] [blame] | 643 | static const upb_json_parsermethod *msgdef_jsonparsermethod(Descriptor* desc) { |
| 644 | if (desc->json_fill_method == NULL) { |
| 645 | desc->json_fill_method = |
| 646 | upb_json_parsermethod_new(desc->msgdef, &desc->json_fill_method); |
| 647 | } |
| 648 | return desc->json_fill_method; |
| 649 | } |
| 650 | |
Chris Fallin | d326277 | 2015-05-14 18:24:26 -0700 | [diff] [blame] | 651 | |
| 652 | // Stack-allocated context during an encode/decode operation. Contains the upb |
| 653 | // environment and its stack-based allocator, an initial buffer for allocations |
| 654 | // to avoid malloc() when possible, and a template for Ruby exception messages |
| 655 | // if any error occurs. |
| 656 | #define STACK_ENV_STACKBYTES 4096 |
| 657 | typedef struct { |
| 658 | upb_env env; |
| 659 | upb_seededalloc alloc; |
| 660 | const char* ruby_error_template; |
| 661 | char allocbuf[STACK_ENV_STACKBYTES]; |
| 662 | } stackenv; |
| 663 | |
| 664 | static void stackenv_init(stackenv* se, const char* errmsg); |
| 665 | static void stackenv_uninit(stackenv* se); |
| 666 | |
| 667 | // Callback invoked by upb if any error occurs during parsing or serialization. |
| 668 | static bool env_error_func(void* ud, const upb_status* status) { |
| 669 | stackenv* se = ud; |
| 670 | // Free the env -- rb_raise will longjmp up the stack past the encode/decode |
| 671 | // function so it would not otherwise have been freed. |
| 672 | stackenv_uninit(se); |
Josh Haberman | 181c7f2 | 2015-07-15 11:05:10 -0700 | [diff] [blame] | 673 | |
| 674 | // TODO(haberman): have a way to verify that this is actually a parse error, |
| 675 | // instead of just throwing "parse error" unconditionally. |
| 676 | rb_raise(cParseError, se->ruby_error_template, upb_status_errmsg(status)); |
Chris Fallin | d326277 | 2015-05-14 18:24:26 -0700 | [diff] [blame] | 677 | // Never reached: rb_raise() always longjmp()s up the stack, past all of our |
| 678 | // code, back to Ruby. |
| 679 | return false; |
| 680 | } |
| 681 | |
| 682 | static void stackenv_init(stackenv* se, const char* errmsg) { |
| 683 | se->ruby_error_template = errmsg; |
| 684 | upb_env_init(&se->env); |
| 685 | upb_seededalloc_init(&se->alloc, &se->allocbuf, STACK_ENV_STACKBYTES); |
| 686 | upb_env_setallocfunc( |
| 687 | &se->env, upb_seededalloc_getallocfunc(&se->alloc), &se->alloc); |
| 688 | upb_env_seterrorfunc(&se->env, env_error_func, se); |
| 689 | } |
| 690 | |
| 691 | static void stackenv_uninit(stackenv* se) { |
| 692 | upb_env_uninit(&se->env); |
| 693 | upb_seededalloc_uninit(&se->alloc); |
| 694 | } |
| 695 | |
Chris Fallin | 973f425 | 2014-11-18 14:19:58 -0800 | [diff] [blame] | 696 | /* |
| 697 | * call-seq: |
| 698 | * MessageClass.decode(data) => message |
| 699 | * |
| 700 | * Decodes the given data (as a string containing bytes in protocol buffers wire |
| 701 | * format) under the interpretration given by this message class's definition |
| 702 | * and returns a message object with the corresponding field values. |
| 703 | */ |
| 704 | VALUE Message_decode(VALUE klass, VALUE data) { |
Chris Fallin | 231886f | 2015-05-19 15:33:48 -0700 | [diff] [blame] | 705 | VALUE descriptor = rb_ivar_get(klass, descriptor_instancevar_interned); |
Chris Fallin | 973f425 | 2014-11-18 14:19:58 -0800 | [diff] [blame] | 706 | Descriptor* desc = ruby_to_Descriptor(descriptor); |
| 707 | VALUE msgklass = Descriptor_msgclass(descriptor); |
Josh Haberman | a1daeab | 2015-07-10 11:56:06 -0700 | [diff] [blame] | 708 | VALUE msg_rb; |
| 709 | MessageHeader* msg; |
Chris Fallin | 973f425 | 2014-11-18 14:19:58 -0800 | [diff] [blame] | 710 | |
| 711 | if (TYPE(data) != T_STRING) { |
| 712 | rb_raise(rb_eArgError, "Expected string for binary protobuf data."); |
| 713 | } |
| 714 | |
Josh Haberman | a1daeab | 2015-07-10 11:56:06 -0700 | [diff] [blame] | 715 | msg_rb = rb_class_new_instance(0, NULL, msgklass); |
Chris Fallin | 973f425 | 2014-11-18 14:19:58 -0800 | [diff] [blame] | 716 | TypedData_Get_Struct(msg_rb, MessageHeader, &Message_type, msg); |
| 717 | |
Josh Haberman | a1daeab | 2015-07-10 11:56:06 -0700 | [diff] [blame] | 718 | { |
| 719 | const upb_pbdecodermethod* method = msgdef_decodermethod(desc); |
| 720 | const upb_handlers* h = upb_pbdecodermethod_desthandlers(method); |
| 721 | stackenv se; |
| 722 | upb_sink sink; |
| 723 | upb_pbdecoder* decoder; |
| 724 | stackenv_init(&se, "Error occurred during parsing: %s"); |
Chris Fallin | d326277 | 2015-05-14 18:24:26 -0700 | [diff] [blame] | 725 | |
Josh Haberman | a1daeab | 2015-07-10 11:56:06 -0700 | [diff] [blame] | 726 | upb_sink_reset(&sink, h, msg); |
| 727 | decoder = upb_pbdecoder_create(&se.env, method, &sink); |
| 728 | upb_bufsrc_putbuf(RSTRING_PTR(data), RSTRING_LEN(data), |
| 729 | upb_pbdecoder_input(decoder)); |
Chris Fallin | 973f425 | 2014-11-18 14:19:58 -0800 | [diff] [blame] | 730 | |
Josh Haberman | a1daeab | 2015-07-10 11:56:06 -0700 | [diff] [blame] | 731 | stackenv_uninit(&se); |
| 732 | } |
Chris Fallin | 973f425 | 2014-11-18 14:19:58 -0800 | [diff] [blame] | 733 | |
| 734 | return msg_rb; |
| 735 | } |
| 736 | |
| 737 | /* |
| 738 | * call-seq: |
| 739 | * MessageClass.decode_json(data) => message |
| 740 | * |
| 741 | * Decodes the given data (as a string containing bytes in protocol buffers wire |
| 742 | * format) under the interpretration given by this message class's definition |
| 743 | * and returns a message object with the corresponding field values. |
| 744 | */ |
| 745 | VALUE Message_decode_json(VALUE klass, VALUE data) { |
Chris Fallin | 231886f | 2015-05-19 15:33:48 -0700 | [diff] [blame] | 746 | VALUE descriptor = rb_ivar_get(klass, descriptor_instancevar_interned); |
Chris Fallin | 973f425 | 2014-11-18 14:19:58 -0800 | [diff] [blame] | 747 | Descriptor* desc = ruby_to_Descriptor(descriptor); |
| 748 | VALUE msgklass = Descriptor_msgclass(descriptor); |
Josh Haberman | a1daeab | 2015-07-10 11:56:06 -0700 | [diff] [blame] | 749 | VALUE msg_rb; |
| 750 | MessageHeader* msg; |
Chris Fallin | 973f425 | 2014-11-18 14:19:58 -0800 | [diff] [blame] | 751 | |
| 752 | if (TYPE(data) != T_STRING) { |
| 753 | rb_raise(rb_eArgError, "Expected string for JSON data."); |
| 754 | } |
| 755 | // TODO(cfallin): Check and respect string encoding. If not UTF-8, we need to |
| 756 | // convert, because string handlers pass data directly to message string |
| 757 | // fields. |
| 758 | |
Josh Haberman | a1daeab | 2015-07-10 11:56:06 -0700 | [diff] [blame] | 759 | msg_rb = rb_class_new_instance(0, NULL, msgklass); |
Chris Fallin | 973f425 | 2014-11-18 14:19:58 -0800 | [diff] [blame] | 760 | TypedData_Get_Struct(msg_rb, MessageHeader, &Message_type, msg); |
| 761 | |
Josh Haberman | a1daeab | 2015-07-10 11:56:06 -0700 | [diff] [blame] | 762 | { |
Josh Haberman | 78da666 | 2016-01-13 19:05:43 -0800 | [diff] [blame] | 763 | const upb_json_parsermethod* method = msgdef_jsonparsermethod(desc); |
Josh Haberman | a1daeab | 2015-07-10 11:56:06 -0700 | [diff] [blame] | 764 | stackenv se; |
| 765 | upb_sink sink; |
| 766 | upb_json_parser* parser; |
| 767 | stackenv_init(&se, "Error occurred during parsing: %s"); |
Chris Fallin | 973f425 | 2014-11-18 14:19:58 -0800 | [diff] [blame] | 768 | |
Josh Haberman | a1daeab | 2015-07-10 11:56:06 -0700 | [diff] [blame] | 769 | upb_sink_reset(&sink, get_fill_handlers(desc), msg); |
Josh Haberman | 78da666 | 2016-01-13 19:05:43 -0800 | [diff] [blame] | 770 | parser = upb_json_parser_create(&se.env, method, &sink); |
Josh Haberman | a1daeab | 2015-07-10 11:56:06 -0700 | [diff] [blame] | 771 | upb_bufsrc_putbuf(RSTRING_PTR(data), RSTRING_LEN(data), |
| 772 | upb_json_parser_input(parser)); |
Chris Fallin | 973f425 | 2014-11-18 14:19:58 -0800 | [diff] [blame] | 773 | |
Josh Haberman | a1daeab | 2015-07-10 11:56:06 -0700 | [diff] [blame] | 774 | stackenv_uninit(&se); |
| 775 | } |
Chris Fallin | 973f425 | 2014-11-18 14:19:58 -0800 | [diff] [blame] | 776 | |
| 777 | return msg_rb; |
| 778 | } |
| 779 | |
| 780 | // ----------------------------------------------------------------------------- |
| 781 | // Serializing. |
| 782 | // ----------------------------------------------------------------------------- |
| 783 | // |
| 784 | // The code below also comes from upb's prototype Ruby binding, developed by |
| 785 | // haberman@. |
| 786 | |
| 787 | /* stringsink *****************************************************************/ |
| 788 | |
| 789 | // This should probably be factored into a common upb component. |
| 790 | |
| 791 | typedef struct { |
| 792 | upb_byteshandler handler; |
| 793 | upb_bytessink sink; |
| 794 | char *ptr; |
| 795 | size_t len, size; |
| 796 | } stringsink; |
| 797 | |
| 798 | static void *stringsink_start(void *_sink, const void *hd, size_t size_hint) { |
| 799 | stringsink *sink = _sink; |
| 800 | sink->len = 0; |
| 801 | return sink; |
| 802 | } |
| 803 | |
| 804 | static size_t stringsink_string(void *_sink, const void *hd, const char *ptr, |
| 805 | size_t len, const upb_bufhandle *handle) { |
Chris Fallin | 973f425 | 2014-11-18 14:19:58 -0800 | [diff] [blame] | 806 | stringsink *sink = _sink; |
| 807 | size_t new_size = sink->size; |
| 808 | |
Josh Haberman | a1daeab | 2015-07-10 11:56:06 -0700 | [diff] [blame] | 809 | UPB_UNUSED(hd); |
| 810 | UPB_UNUSED(handle); |
| 811 | |
Chris Fallin | 973f425 | 2014-11-18 14:19:58 -0800 | [diff] [blame] | 812 | while (sink->len + len > new_size) { |
| 813 | new_size *= 2; |
| 814 | } |
| 815 | |
| 816 | if (new_size != sink->size) { |
| 817 | sink->ptr = realloc(sink->ptr, new_size); |
| 818 | sink->size = new_size; |
| 819 | } |
| 820 | |
| 821 | memcpy(sink->ptr + sink->len, ptr, len); |
| 822 | sink->len += len; |
| 823 | |
| 824 | return len; |
| 825 | } |
| 826 | |
| 827 | void stringsink_init(stringsink *sink) { |
| 828 | upb_byteshandler_init(&sink->handler); |
| 829 | upb_byteshandler_setstartstr(&sink->handler, stringsink_start, NULL); |
| 830 | upb_byteshandler_setstring(&sink->handler, stringsink_string, NULL); |
| 831 | |
| 832 | upb_bytessink_reset(&sink->sink, &sink->handler, sink); |
| 833 | |
| 834 | sink->size = 32; |
| 835 | sink->ptr = malloc(sink->size); |
| 836 | sink->len = 0; |
| 837 | } |
| 838 | |
| 839 | void stringsink_uninit(stringsink *sink) { |
| 840 | free(sink->ptr); |
| 841 | } |
| 842 | |
| 843 | /* msgvisitor *****************************************************************/ |
| 844 | |
| 845 | // TODO: If/when we support proto2 semantics in addition to the current proto3 |
| 846 | // semantics, which means that we have true field presence, we will want to |
| 847 | // modify msgvisitor so that it emits all present fields rather than all |
| 848 | // non-default-value fields. |
| 849 | // |
| 850 | // Likewise, when implementing JSON serialization, we may need to have a |
| 851 | // 'verbose' mode that outputs all fields and a 'concise' mode that outputs only |
| 852 | // those with non-default values. |
| 853 | |
| 854 | static void putmsg(VALUE msg, const Descriptor* desc, |
| 855 | upb_sink *sink, int depth); |
| 856 | |
| 857 | static upb_selector_t getsel(const upb_fielddef *f, upb_handlertype_t type) { |
| 858 | upb_selector_t ret; |
| 859 | bool ok = upb_handlers_getselector(f, type, &ret); |
| 860 | UPB_ASSERT_VAR(ok, ok); |
| 861 | return ret; |
| 862 | } |
| 863 | |
| 864 | static void putstr(VALUE str, const upb_fielddef *f, upb_sink *sink) { |
Josh Haberman | a1daeab | 2015-07-10 11:56:06 -0700 | [diff] [blame] | 865 | upb_sink subsink; |
| 866 | |
Chris Fallin | 973f425 | 2014-11-18 14:19:58 -0800 | [diff] [blame] | 867 | if (str == Qnil) return; |
| 868 | |
| 869 | assert(BUILTIN_TYPE(str) == RUBY_T_STRING); |
Chris Fallin | 973f425 | 2014-11-18 14:19:58 -0800 | [diff] [blame] | 870 | |
| 871 | // Ensure that the string has the correct encoding. We also check at field-set |
| 872 | // time, but the user may have mutated the string object since then. |
| 873 | native_slot_validate_string_encoding(upb_fielddef_type(f), str); |
| 874 | |
| 875 | upb_sink_startstr(sink, getsel(f, UPB_HANDLER_STARTSTR), RSTRING_LEN(str), |
| 876 | &subsink); |
| 877 | upb_sink_putstring(&subsink, getsel(f, UPB_HANDLER_STRING), RSTRING_PTR(str), |
| 878 | RSTRING_LEN(str), NULL); |
| 879 | upb_sink_endstr(sink, getsel(f, UPB_HANDLER_ENDSTR)); |
| 880 | } |
| 881 | |
| 882 | static void putsubmsg(VALUE submsg, const upb_fielddef *f, upb_sink *sink, |
| 883 | int depth) { |
Josh Haberman | a1daeab | 2015-07-10 11:56:06 -0700 | [diff] [blame] | 884 | upb_sink subsink; |
| 885 | VALUE descriptor; |
| 886 | Descriptor* subdesc; |
| 887 | |
Chris Fallin | 973f425 | 2014-11-18 14:19:58 -0800 | [diff] [blame] | 888 | if (submsg == Qnil) return; |
| 889 | |
Josh Haberman | a1daeab | 2015-07-10 11:56:06 -0700 | [diff] [blame] | 890 | descriptor = rb_ivar_get(submsg, descriptor_instancevar_interned); |
| 891 | subdesc = ruby_to_Descriptor(descriptor); |
Chris Fallin | 973f425 | 2014-11-18 14:19:58 -0800 | [diff] [blame] | 892 | |
| 893 | upb_sink_startsubmsg(sink, getsel(f, UPB_HANDLER_STARTSUBMSG), &subsink); |
| 894 | putmsg(submsg, subdesc, &subsink, depth + 1); |
| 895 | upb_sink_endsubmsg(sink, getsel(f, UPB_HANDLER_ENDSUBMSG)); |
| 896 | } |
| 897 | |
| 898 | static void putary(VALUE ary, const upb_fielddef *f, upb_sink *sink, |
| 899 | int depth) { |
Chris Fallin | 973f425 | 2014-11-18 14:19:58 -0800 | [diff] [blame] | 900 | upb_sink subsink; |
Josh Haberman | a1daeab | 2015-07-10 11:56:06 -0700 | [diff] [blame] | 901 | upb_fieldtype_t type = upb_fielddef_type(f); |
| 902 | upb_selector_t sel = 0; |
| 903 | int size; |
| 904 | |
| 905 | if (ary == Qnil) return; |
Chris Fallin | 973f425 | 2014-11-18 14:19:58 -0800 | [diff] [blame] | 906 | |
| 907 | upb_sink_startseq(sink, getsel(f, UPB_HANDLER_STARTSEQ), &subsink); |
| 908 | |
Chris Fallin | 973f425 | 2014-11-18 14:19:58 -0800 | [diff] [blame] | 909 | if (upb_fielddef_isprimitive(f)) { |
| 910 | sel = getsel(f, upb_handlers_getprimitivehandlertype(f)); |
| 911 | } |
| 912 | |
Josh Haberman | a1daeab | 2015-07-10 11:56:06 -0700 | [diff] [blame] | 913 | size = NUM2INT(RepeatedField_length(ary)); |
Chris Fallin | 973f425 | 2014-11-18 14:19:58 -0800 | [diff] [blame] | 914 | for (int i = 0; i < size; i++) { |
| 915 | void* memory = RepeatedField_index_native(ary, i); |
| 916 | switch (type) { |
| 917 | #define T(upbtypeconst, upbtype, ctype) \ |
| 918 | case upbtypeconst: \ |
| 919 | upb_sink_put##upbtype(&subsink, sel, *((ctype *)memory)); \ |
| 920 | break; |
| 921 | |
| 922 | T(UPB_TYPE_FLOAT, float, float) |
| 923 | T(UPB_TYPE_DOUBLE, double, double) |
| 924 | T(UPB_TYPE_BOOL, bool, int8_t) |
| 925 | case UPB_TYPE_ENUM: |
| 926 | T(UPB_TYPE_INT32, int32, int32_t) |
| 927 | T(UPB_TYPE_UINT32, uint32, uint32_t) |
| 928 | T(UPB_TYPE_INT64, int64, int64_t) |
| 929 | T(UPB_TYPE_UINT64, uint64, uint64_t) |
| 930 | |
| 931 | case UPB_TYPE_STRING: |
| 932 | case UPB_TYPE_BYTES: |
| 933 | putstr(*((VALUE *)memory), f, &subsink); |
| 934 | break; |
| 935 | case UPB_TYPE_MESSAGE: |
| 936 | putsubmsg(*((VALUE *)memory), f, &subsink, depth); |
| 937 | break; |
| 938 | |
| 939 | #undef T |
| 940 | |
| 941 | } |
| 942 | } |
| 943 | upb_sink_endseq(sink, getsel(f, UPB_HANDLER_ENDSEQ)); |
| 944 | } |
| 945 | |
Chris Fallin | fd1a3ff | 2015-01-06 15:44:09 -0800 | [diff] [blame] | 946 | static void put_ruby_value(VALUE value, |
| 947 | const upb_fielddef *f, |
| 948 | VALUE type_class, |
| 949 | int depth, |
| 950 | upb_sink *sink) { |
| 951 | upb_selector_t sel = 0; |
| 952 | if (upb_fielddef_isprimitive(f)) { |
| 953 | sel = getsel(f, upb_handlers_getprimitivehandlertype(f)); |
| 954 | } |
| 955 | |
| 956 | switch (upb_fielddef_type(f)) { |
| 957 | case UPB_TYPE_INT32: |
| 958 | upb_sink_putint32(sink, sel, NUM2INT(value)); |
| 959 | break; |
| 960 | case UPB_TYPE_INT64: |
| 961 | upb_sink_putint64(sink, sel, NUM2LL(value)); |
| 962 | break; |
| 963 | case UPB_TYPE_UINT32: |
| 964 | upb_sink_putuint32(sink, sel, NUM2UINT(value)); |
| 965 | break; |
| 966 | case UPB_TYPE_UINT64: |
| 967 | upb_sink_putuint64(sink, sel, NUM2ULL(value)); |
| 968 | break; |
| 969 | case UPB_TYPE_FLOAT: |
| 970 | upb_sink_putfloat(sink, sel, NUM2DBL(value)); |
| 971 | break; |
| 972 | case UPB_TYPE_DOUBLE: |
| 973 | upb_sink_putdouble(sink, sel, NUM2DBL(value)); |
| 974 | break; |
| 975 | case UPB_TYPE_ENUM: { |
| 976 | if (TYPE(value) == T_SYMBOL) { |
| 977 | value = rb_funcall(type_class, rb_intern("resolve"), 1, value); |
| 978 | } |
| 979 | upb_sink_putint32(sink, sel, NUM2INT(value)); |
| 980 | break; |
| 981 | } |
| 982 | case UPB_TYPE_BOOL: |
| 983 | upb_sink_putbool(sink, sel, value == Qtrue); |
| 984 | break; |
| 985 | case UPB_TYPE_STRING: |
| 986 | case UPB_TYPE_BYTES: |
| 987 | putstr(value, f, sink); |
| 988 | break; |
| 989 | case UPB_TYPE_MESSAGE: |
| 990 | putsubmsg(value, f, sink, depth); |
| 991 | } |
| 992 | } |
| 993 | |
| 994 | static void putmap(VALUE map, const upb_fielddef *f, upb_sink *sink, |
| 995 | int depth) { |
Josh Haberman | a1daeab | 2015-07-10 11:56:06 -0700 | [diff] [blame] | 996 | Map* self; |
Chris Fallin | fd1a3ff | 2015-01-06 15:44:09 -0800 | [diff] [blame] | 997 | upb_sink subsink; |
Josh Haberman | a1daeab | 2015-07-10 11:56:06 -0700 | [diff] [blame] | 998 | const upb_fielddef* key_field; |
| 999 | const upb_fielddef* value_field; |
| 1000 | Map_iter it; |
| 1001 | |
| 1002 | if (map == Qnil) return; |
| 1003 | self = ruby_to_Map(map); |
Chris Fallin | fd1a3ff | 2015-01-06 15:44:09 -0800 | [diff] [blame] | 1004 | |
| 1005 | upb_sink_startseq(sink, getsel(f, UPB_HANDLER_STARTSEQ), &subsink); |
| 1006 | |
| 1007 | assert(upb_fielddef_type(f) == UPB_TYPE_MESSAGE); |
Josh Haberman | a1daeab | 2015-07-10 11:56:06 -0700 | [diff] [blame] | 1008 | key_field = map_field_key(f); |
| 1009 | value_field = map_field_value(f); |
Chris Fallin | fd1a3ff | 2015-01-06 15:44:09 -0800 | [diff] [blame] | 1010 | |
Chris Fallin | fd1a3ff | 2015-01-06 15:44:09 -0800 | [diff] [blame] | 1011 | for (Map_begin(map, &it); !Map_done(&it); Map_next(&it)) { |
| 1012 | VALUE key = Map_iter_key(&it); |
| 1013 | VALUE value = Map_iter_value(&it); |
Josh Haberman | a1daeab | 2015-07-10 11:56:06 -0700 | [diff] [blame] | 1014 | upb_status status; |
Chris Fallin | fd1a3ff | 2015-01-06 15:44:09 -0800 | [diff] [blame] | 1015 | |
| 1016 | upb_sink entry_sink; |
Chris Fallin | 231886f | 2015-05-19 15:33:48 -0700 | [diff] [blame] | 1017 | upb_sink_startsubmsg(&subsink, getsel(f, UPB_HANDLER_STARTSUBMSG), |
| 1018 | &entry_sink); |
Chris Fallin | fd1a3ff | 2015-01-06 15:44:09 -0800 | [diff] [blame] | 1019 | upb_sink_startmsg(&entry_sink); |
| 1020 | |
| 1021 | put_ruby_value(key, key_field, Qnil, depth + 1, &entry_sink); |
| 1022 | put_ruby_value(value, value_field, self->value_type_class, depth + 1, |
| 1023 | &entry_sink); |
| 1024 | |
Chris Fallin | fd1a3ff | 2015-01-06 15:44:09 -0800 | [diff] [blame] | 1025 | upb_sink_endmsg(&entry_sink, &status); |
| 1026 | upb_sink_endsubmsg(&subsink, getsel(f, UPB_HANDLER_ENDSUBMSG)); |
| 1027 | } |
| 1028 | |
| 1029 | upb_sink_endseq(sink, getsel(f, UPB_HANDLER_ENDSEQ)); |
| 1030 | } |
| 1031 | |
Chris Fallin | 973f425 | 2014-11-18 14:19:58 -0800 | [diff] [blame] | 1032 | static void putmsg(VALUE msg_rb, const Descriptor* desc, |
| 1033 | upb_sink *sink, int depth) { |
Josh Haberman | a1daeab | 2015-07-10 11:56:06 -0700 | [diff] [blame] | 1034 | MessageHeader* msg; |
| 1035 | upb_msg_field_iter i; |
| 1036 | upb_status status; |
| 1037 | |
Chris Fallin | 973f425 | 2014-11-18 14:19:58 -0800 | [diff] [blame] | 1038 | upb_sink_startmsg(sink); |
| 1039 | |
| 1040 | // Protect against cycles (possible because users may freely reassign message |
| 1041 | // and repeated fields) by imposing a maximum recursion depth. |
Chris Fallin | d326277 | 2015-05-14 18:24:26 -0700 | [diff] [blame] | 1042 | if (depth > ENCODE_MAX_NESTING) { |
Chris Fallin | 973f425 | 2014-11-18 14:19:58 -0800 | [diff] [blame] | 1043 | rb_raise(rb_eRuntimeError, |
| 1044 | "Maximum recursion depth exceeded during encoding."); |
| 1045 | } |
| 1046 | |
Chris Fallin | 973f425 | 2014-11-18 14:19:58 -0800 | [diff] [blame] | 1047 | TypedData_Get_Struct(msg_rb, MessageHeader, &Message_type, msg); |
Chris Fallin | 973f425 | 2014-11-18 14:19:58 -0800 | [diff] [blame] | 1048 | |
Chris Fallin | fcd8889 | 2015-01-13 18:14:39 -0800 | [diff] [blame] | 1049 | for (upb_msg_field_begin(&i, desc->msgdef); |
| 1050 | !upb_msg_field_done(&i); |
| 1051 | upb_msg_field_next(&i)) { |
Chris Fallin | 973f425 | 2014-11-18 14:19:58 -0800 | [diff] [blame] | 1052 | upb_fielddef *f = upb_msg_iter_field(&i); |
Josh Haberman | 78da666 | 2016-01-13 19:05:43 -0800 | [diff] [blame] | 1053 | bool is_matching_oneof = false; |
Chris Fallin | 4c92289 | 2015-01-09 15:29:45 -0800 | [diff] [blame] | 1054 | uint32_t offset = |
Chris Fallin | fcd8889 | 2015-01-13 18:14:39 -0800 | [diff] [blame] | 1055 | desc->layout->fields[upb_fielddef_index(f)].offset + |
| 1056 | sizeof(MessageHeader); |
Chris Fallin | fcd8889 | 2015-01-13 18:14:39 -0800 | [diff] [blame] | 1057 | |
| 1058 | if (upb_fielddef_containingoneof(f)) { |
Chris Fallin | 07b8b0f | 2015-01-26 13:52:51 -0800 | [diff] [blame] | 1059 | uint32_t oneof_case_offset = |
| 1060 | desc->layout->fields[upb_fielddef_index(f)].case_offset + |
| 1061 | sizeof(MessageHeader); |
Chris Fallin | fcd8889 | 2015-01-13 18:14:39 -0800 | [diff] [blame] | 1062 | // For a oneof, check that this field is actually present -- skip all the |
| 1063 | // below if not. |
| 1064 | if (DEREF(msg, oneof_case_offset, uint32_t) != |
| 1065 | upb_fielddef_number(f)) { |
| 1066 | continue; |
| 1067 | } |
| 1068 | // Otherwise, fall through to the appropriate singular-field handler |
| 1069 | // below. |
Josh Haberman | 78da666 | 2016-01-13 19:05:43 -0800 | [diff] [blame] | 1070 | is_matching_oneof = true; |
Chris Fallin | fcd8889 | 2015-01-13 18:14:39 -0800 | [diff] [blame] | 1071 | } |
Chris Fallin | 973f425 | 2014-11-18 14:19:58 -0800 | [diff] [blame] | 1072 | |
Chris Fallin | fd1a3ff | 2015-01-06 15:44:09 -0800 | [diff] [blame] | 1073 | if (is_map_field(f)) { |
Chris Fallin | 4c92289 | 2015-01-09 15:29:45 -0800 | [diff] [blame] | 1074 | VALUE map = DEREF(msg, offset, VALUE); |
Chris Fallin | fd1a3ff | 2015-01-06 15:44:09 -0800 | [diff] [blame] | 1075 | if (map != Qnil) { |
| 1076 | putmap(map, f, sink, depth); |
| 1077 | } |
| 1078 | } else if (upb_fielddef_isseq(f)) { |
Chris Fallin | 4c92289 | 2015-01-09 15:29:45 -0800 | [diff] [blame] | 1079 | VALUE ary = DEREF(msg, offset, VALUE); |
Chris Fallin | 973f425 | 2014-11-18 14:19:58 -0800 | [diff] [blame] | 1080 | if (ary != Qnil) { |
| 1081 | putary(ary, f, sink, depth); |
| 1082 | } |
| 1083 | } else if (upb_fielddef_isstring(f)) { |
Chris Fallin | 4c92289 | 2015-01-09 15:29:45 -0800 | [diff] [blame] | 1084 | VALUE str = DEREF(msg, offset, VALUE); |
Josh Haberman | 78da666 | 2016-01-13 19:05:43 -0800 | [diff] [blame] | 1085 | if (is_matching_oneof || RSTRING_LEN(str) > 0) { |
Chris Fallin | 973f425 | 2014-11-18 14:19:58 -0800 | [diff] [blame] | 1086 | putstr(str, f, sink); |
| 1087 | } |
| 1088 | } else if (upb_fielddef_issubmsg(f)) { |
Chris Fallin | 4c92289 | 2015-01-09 15:29:45 -0800 | [diff] [blame] | 1089 | putsubmsg(DEREF(msg, offset, VALUE), f, sink, depth); |
Chris Fallin | 973f425 | 2014-11-18 14:19:58 -0800 | [diff] [blame] | 1090 | } else { |
| 1091 | upb_selector_t sel = getsel(f, upb_handlers_getprimitivehandlertype(f)); |
| 1092 | |
| 1093 | #define T(upbtypeconst, upbtype, ctype, default_value) \ |
| 1094 | case upbtypeconst: { \ |
Chris Fallin | 4c92289 | 2015-01-09 15:29:45 -0800 | [diff] [blame] | 1095 | ctype value = DEREF(msg, offset, ctype); \ |
Josh Haberman | 78da666 | 2016-01-13 19:05:43 -0800 | [diff] [blame] | 1096 | if (is_matching_oneof || value != default_value) { \ |
Chris Fallin | 973f425 | 2014-11-18 14:19:58 -0800 | [diff] [blame] | 1097 | upb_sink_put##upbtype(sink, sel, value); \ |
| 1098 | } \ |
| 1099 | } \ |
| 1100 | break; |
| 1101 | |
| 1102 | switch (upb_fielddef_type(f)) { |
| 1103 | T(UPB_TYPE_FLOAT, float, float, 0.0) |
| 1104 | T(UPB_TYPE_DOUBLE, double, double, 0.0) |
| 1105 | T(UPB_TYPE_BOOL, bool, uint8_t, 0) |
| 1106 | case UPB_TYPE_ENUM: |
| 1107 | T(UPB_TYPE_INT32, int32, int32_t, 0) |
| 1108 | T(UPB_TYPE_UINT32, uint32, uint32_t, 0) |
| 1109 | T(UPB_TYPE_INT64, int64, int64_t, 0) |
| 1110 | T(UPB_TYPE_UINT64, uint64, uint64_t, 0) |
| 1111 | |
| 1112 | case UPB_TYPE_STRING: |
| 1113 | case UPB_TYPE_BYTES: |
| 1114 | case UPB_TYPE_MESSAGE: rb_raise(rb_eRuntimeError, "Internal error."); |
| 1115 | } |
| 1116 | |
| 1117 | #undef T |
| 1118 | |
| 1119 | } |
| 1120 | } |
| 1121 | |
Chris Fallin | 973f425 | 2014-11-18 14:19:58 -0800 | [diff] [blame] | 1122 | upb_sink_endmsg(sink, &status); |
| 1123 | } |
| 1124 | |
| 1125 | static const upb_handlers* msgdef_pb_serialize_handlers(Descriptor* desc) { |
| 1126 | if (desc->pb_serialize_handlers == NULL) { |
| 1127 | desc->pb_serialize_handlers = |
| 1128 | upb_pb_encoder_newhandlers(desc->msgdef, &desc->pb_serialize_handlers); |
| 1129 | } |
| 1130 | return desc->pb_serialize_handlers; |
| 1131 | } |
| 1132 | |
Josh Haberman | 194ad62 | 2016-04-14 18:33:17 -0700 | [diff] [blame] | 1133 | static const upb_handlers* msgdef_json_serialize_handlers( |
| 1134 | Descriptor* desc, bool preserve_proto_fieldnames) { |
| 1135 | if (preserve_proto_fieldnames) { |
| 1136 | if (desc->json_serialize_handlers == NULL) { |
| 1137 | desc->json_serialize_handlers = |
| 1138 | upb_json_printer_newhandlers( |
| 1139 | desc->msgdef, true, &desc->json_serialize_handlers); |
| 1140 | } |
| 1141 | return desc->json_serialize_handlers; |
| 1142 | } else { |
| 1143 | if (desc->json_serialize_handlers_preserve == NULL) { |
| 1144 | desc->json_serialize_handlers_preserve = |
| 1145 | upb_json_printer_newhandlers( |
| 1146 | desc->msgdef, false, &desc->json_serialize_handlers_preserve); |
| 1147 | } |
| 1148 | return desc->json_serialize_handlers_preserve; |
Chris Fallin | 973f425 | 2014-11-18 14:19:58 -0800 | [diff] [blame] | 1149 | } |
Chris Fallin | 973f425 | 2014-11-18 14:19:58 -0800 | [diff] [blame] | 1150 | } |
| 1151 | |
| 1152 | /* |
| 1153 | * call-seq: |
| 1154 | * MessageClass.encode(msg) => bytes |
| 1155 | * |
| 1156 | * Encodes the given message object to its serialized form in protocol buffers |
| 1157 | * wire format. |
| 1158 | */ |
| 1159 | VALUE Message_encode(VALUE klass, VALUE msg_rb) { |
Chris Fallin | 231886f | 2015-05-19 15:33:48 -0700 | [diff] [blame] | 1160 | VALUE descriptor = rb_ivar_get(klass, descriptor_instancevar_interned); |
Chris Fallin | 973f425 | 2014-11-18 14:19:58 -0800 | [diff] [blame] | 1161 | Descriptor* desc = ruby_to_Descriptor(descriptor); |
| 1162 | |
| 1163 | stringsink sink; |
| 1164 | stringsink_init(&sink); |
| 1165 | |
Josh Haberman | a1daeab | 2015-07-10 11:56:06 -0700 | [diff] [blame] | 1166 | { |
| 1167 | const upb_handlers* serialize_handlers = |
| 1168 | msgdef_pb_serialize_handlers(desc); |
Chris Fallin | 973f425 | 2014-11-18 14:19:58 -0800 | [diff] [blame] | 1169 | |
Josh Haberman | a1daeab | 2015-07-10 11:56:06 -0700 | [diff] [blame] | 1170 | stackenv se; |
| 1171 | upb_pb_encoder* encoder; |
| 1172 | VALUE ret; |
Chris Fallin | 973f425 | 2014-11-18 14:19:58 -0800 | [diff] [blame] | 1173 | |
Josh Haberman | a1daeab | 2015-07-10 11:56:06 -0700 | [diff] [blame] | 1174 | stackenv_init(&se, "Error occurred during encoding: %s"); |
| 1175 | encoder = upb_pb_encoder_create(&se.env, serialize_handlers, &sink.sink); |
Chris Fallin | 973f425 | 2014-11-18 14:19:58 -0800 | [diff] [blame] | 1176 | |
Josh Haberman | a1daeab | 2015-07-10 11:56:06 -0700 | [diff] [blame] | 1177 | putmsg(msg_rb, desc, upb_pb_encoder_input(encoder), 0); |
Chris Fallin | 973f425 | 2014-11-18 14:19:58 -0800 | [diff] [blame] | 1178 | |
Josh Haberman | a1daeab | 2015-07-10 11:56:06 -0700 | [diff] [blame] | 1179 | ret = rb_str_new(sink.ptr, sink.len); |
Chris Fallin | 973f425 | 2014-11-18 14:19:58 -0800 | [diff] [blame] | 1180 | |
Josh Haberman | a1daeab | 2015-07-10 11:56:06 -0700 | [diff] [blame] | 1181 | stackenv_uninit(&se); |
| 1182 | stringsink_uninit(&sink); |
| 1183 | |
| 1184 | return ret; |
| 1185 | } |
Chris Fallin | 973f425 | 2014-11-18 14:19:58 -0800 | [diff] [blame] | 1186 | } |
| 1187 | |
| 1188 | /* |
| 1189 | * call-seq: |
| 1190 | * MessageClass.encode_json(msg) => json_string |
| 1191 | * |
| 1192 | * Encodes the given message object into its serialized JSON representation. |
| 1193 | */ |
Josh Haberman | 194ad62 | 2016-04-14 18:33:17 -0700 | [diff] [blame] | 1194 | VALUE Message_encode_json(int argc, VALUE* argv, VALUE klass) { |
Chris Fallin | 231886f | 2015-05-19 15:33:48 -0700 | [diff] [blame] | 1195 | VALUE descriptor = rb_ivar_get(klass, descriptor_instancevar_interned); |
Chris Fallin | 973f425 | 2014-11-18 14:19:58 -0800 | [diff] [blame] | 1196 | Descriptor* desc = ruby_to_Descriptor(descriptor); |
Josh Haberman | 194ad62 | 2016-04-14 18:33:17 -0700 | [diff] [blame] | 1197 | VALUE msg_rb; |
| 1198 | VALUE preserve_proto_fieldnames = Qfalse; |
Chris Fallin | 973f425 | 2014-11-18 14:19:58 -0800 | [diff] [blame] | 1199 | stringsink sink; |
Josh Haberman | 194ad62 | 2016-04-14 18:33:17 -0700 | [diff] [blame] | 1200 | |
| 1201 | if (argc < 1 || argc > 2) { |
| 1202 | rb_raise(rb_eArgError, "Expected 1 or 2 arguments."); |
| 1203 | } |
| 1204 | |
| 1205 | msg_rb = argv[0]; |
| 1206 | |
| 1207 | if (argc == 2) { |
| 1208 | VALUE hash_args = argv[1]; |
| 1209 | if (TYPE(hash_args) != T_HASH) { |
| 1210 | rb_raise(rb_eArgError, "Expected hash arguments."); |
| 1211 | } |
| 1212 | preserve_proto_fieldnames = rb_hash_lookup2( |
| 1213 | hash_args, ID2SYM(rb_intern("preserve_proto_fieldnames")), Qfalse); |
| 1214 | } |
| 1215 | |
Chris Fallin | 973f425 | 2014-11-18 14:19:58 -0800 | [diff] [blame] | 1216 | stringsink_init(&sink); |
| 1217 | |
Josh Haberman | a1daeab | 2015-07-10 11:56:06 -0700 | [diff] [blame] | 1218 | { |
| 1219 | const upb_handlers* serialize_handlers = |
Josh Haberman | 194ad62 | 2016-04-14 18:33:17 -0700 | [diff] [blame] | 1220 | msgdef_json_serialize_handlers(desc, RTEST(preserve_proto_fieldnames)); |
Josh Haberman | a1daeab | 2015-07-10 11:56:06 -0700 | [diff] [blame] | 1221 | upb_json_printer* printer; |
| 1222 | stackenv se; |
| 1223 | VALUE ret; |
Chris Fallin | 973f425 | 2014-11-18 14:19:58 -0800 | [diff] [blame] | 1224 | |
Josh Haberman | a1daeab | 2015-07-10 11:56:06 -0700 | [diff] [blame] | 1225 | stackenv_init(&se, "Error occurred during encoding: %s"); |
| 1226 | printer = upb_json_printer_create(&se.env, serialize_handlers, &sink.sink); |
Chris Fallin | 973f425 | 2014-11-18 14:19:58 -0800 | [diff] [blame] | 1227 | |
Josh Haberman | a1daeab | 2015-07-10 11:56:06 -0700 | [diff] [blame] | 1228 | putmsg(msg_rb, desc, upb_json_printer_input(printer), 0); |
Chris Fallin | 973f425 | 2014-11-18 14:19:58 -0800 | [diff] [blame] | 1229 | |
Josh Haberman | 78da666 | 2016-01-13 19:05:43 -0800 | [diff] [blame] | 1230 | ret = rb_enc_str_new(sink.ptr, sink.len, rb_utf8_encoding()); |
Chris Fallin | 973f425 | 2014-11-18 14:19:58 -0800 | [diff] [blame] | 1231 | |
Josh Haberman | a1daeab | 2015-07-10 11:56:06 -0700 | [diff] [blame] | 1232 | stackenv_uninit(&se); |
| 1233 | stringsink_uninit(&sink); |
Chris Fallin | 973f425 | 2014-11-18 14:19:58 -0800 | [diff] [blame] | 1234 | |
Josh Haberman | a1daeab | 2015-07-10 11:56:06 -0700 | [diff] [blame] | 1235 | return ret; |
| 1236 | } |
Chris Fallin | 973f425 | 2014-11-18 14:19:58 -0800 | [diff] [blame] | 1237 | } |
| 1238 | |