1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2014 Google Inc. All rights reserved.
3 //
4 // Use of this source code is governed by a BSD-style
5 // license that can be found in the LICENSE file or at
6 // https://developers.google.com/open-source/licenses/bsd
7
8 #include "protobuf.h"
9
10 #include "defs.h"
11 #include "map.h"
12 #include "message.h"
13 #include "repeated_field.h"
14
15 VALUE cParseError;
16 VALUE cTypeError;
17
map_field_key(const upb_FieldDef * field)18 const upb_FieldDef *map_field_key(const upb_FieldDef *field) {
19 const upb_MessageDef *entry = upb_FieldDef_MessageSubDef(field);
20 return upb_MessageDef_FindFieldByNumber(entry, 1);
21 }
22
map_field_value(const upb_FieldDef * field)23 const upb_FieldDef *map_field_value(const upb_FieldDef *field) {
24 const upb_MessageDef *entry = upb_FieldDef_MessageSubDef(field);
25 return upb_MessageDef_FindFieldByNumber(entry, 2);
26 }
27
28 // -----------------------------------------------------------------------------
29 // StringBuilder, for inspect
30 // -----------------------------------------------------------------------------
31
32 struct StringBuilder {
33 size_t size;
34 size_t cap;
35 char *data;
36 };
37
38 typedef struct StringBuilder StringBuilder;
39
StringBuilder_SizeOf(size_t cap)40 static size_t StringBuilder_SizeOf(size_t cap) {
41 return sizeof(StringBuilder) + cap;
42 }
43
StringBuilder_New()44 StringBuilder *StringBuilder_New() {
45 const size_t cap = 128;
46 StringBuilder *builder = malloc(sizeof(*builder));
47 builder->size = 0;
48 builder->cap = cap;
49 builder->data = malloc(builder->cap);
50 return builder;
51 }
52
StringBuilder_Free(StringBuilder * b)53 void StringBuilder_Free(StringBuilder *b) {
54 free(b->data);
55 free(b);
56 }
57
StringBuilder_Printf(StringBuilder * b,const char * fmt,...)58 void StringBuilder_Printf(StringBuilder *b, const char *fmt, ...) {
59 size_t have = b->cap - b->size;
60 size_t n;
61 va_list args;
62
63 va_start(args, fmt);
64 n = vsnprintf(&b->data[b->size], have, fmt, args);
65 va_end(args);
66
67 if (have <= n) {
68 while (have <= n) {
69 b->cap *= 2;
70 have = b->cap - b->size;
71 }
72 b->data = realloc(b->data, StringBuilder_SizeOf(b->cap));
73 va_start(args, fmt);
74 n = vsnprintf(&b->data[b->size], have, fmt, args);
75 va_end(args);
76 PBRUBY_ASSERT(n < have);
77 }
78
79 b->size += n;
80 }
81
StringBuilder_ToRubyString(StringBuilder * b)82 VALUE StringBuilder_ToRubyString(StringBuilder *b) {
83 VALUE ret = rb_str_new(b->data, b->size);
84 rb_enc_associate(ret, rb_utf8_encoding());
85 return ret;
86 }
87
StringBuilder_PrintEnum(StringBuilder * b,int32_t val,const upb_EnumDef * e)88 static void StringBuilder_PrintEnum(StringBuilder *b, int32_t val,
89 const upb_EnumDef *e) {
90 const upb_EnumValueDef *ev = upb_EnumDef_FindValueByNumber(e, val);
91 if (ev) {
92 StringBuilder_Printf(b, ":%s", upb_EnumValueDef_Name(ev));
93 } else {
94 StringBuilder_Printf(b, "%" PRId32, val);
95 }
96 }
97
StringBuilder_PrintMsgval(StringBuilder * b,upb_MessageValue val,TypeInfo info)98 void StringBuilder_PrintMsgval(StringBuilder *b, upb_MessageValue val,
99 TypeInfo info) {
100 switch (info.type) {
101 case kUpb_CType_Bool:
102 StringBuilder_Printf(b, "%s", val.bool_val ? "true" : "false");
103 break;
104 case kUpb_CType_Float: {
105 VALUE str = rb_inspect(DBL2NUM(val.float_val));
106 StringBuilder_Printf(b, "%s", RSTRING_PTR(str));
107 break;
108 }
109 case kUpb_CType_Double: {
110 VALUE str = rb_inspect(DBL2NUM(val.double_val));
111 StringBuilder_Printf(b, "%s", RSTRING_PTR(str));
112 break;
113 }
114 case kUpb_CType_Int32:
115 StringBuilder_Printf(b, "%" PRId32, val.int32_val);
116 break;
117 case kUpb_CType_UInt32:
118 StringBuilder_Printf(b, "%" PRIu32, val.uint32_val);
119 break;
120 case kUpb_CType_Int64:
121 StringBuilder_Printf(b, "%" PRId64, val.int64_val);
122 break;
123 case kUpb_CType_UInt64:
124 StringBuilder_Printf(b, "%" PRIu64, val.uint64_val);
125 break;
126 case kUpb_CType_String:
127 StringBuilder_Printf(b, "\"%.*s\"", (int)val.str_val.size,
128 val.str_val.data);
129 break;
130 case kUpb_CType_Bytes:
131 StringBuilder_Printf(b, "\"%.*s\"", (int)val.str_val.size,
132 val.str_val.data);
133 break;
134 case kUpb_CType_Enum:
135 StringBuilder_PrintEnum(b, val.int32_val, info.def.enumdef);
136 break;
137 case kUpb_CType_Message:
138 Message_PrintMessage(b, val.msg_val, info.def.msgdef);
139 break;
140 }
141 }
142
143 // -----------------------------------------------------------------------------
144 // Arena
145 // -----------------------------------------------------------------------------
146
147 typedef struct {
148 upb_Arena *arena;
149 // IMPORTANT: WB_PROTECTED objects must only use the RB_OBJ_WRITE()
150 // macro to update VALUE references, as to trigger write barriers.
151 VALUE pinned_objs;
152 } Arena;
153
Arena_mark(void * data)154 static void Arena_mark(void *data) {
155 Arena *arena = data;
156 rb_gc_mark(arena->pinned_objs);
157 }
158
Arena_free(void * data)159 static void Arena_free(void *data) {
160 Arena *arena = data;
161 upb_Arena_Free(arena->arena);
162 xfree(arena);
163 }
164
Arena_memsize(const void * data)165 static size_t Arena_memsize(const void *data) {
166 const Arena *arena = data;
167 size_t fused_count;
168 size_t memsize = upb_Arena_SpaceAllocated(arena->arena, &fused_count);
169 if (fused_count > 1) {
170 // If other arena were fused we attribute an equal
171 // share of memory usage to each one.
172 memsize /= fused_count;
173 }
174 return memsize + sizeof(Arena);
175 }
176
177 static VALUE cArena;
178
179 const rb_data_type_t Arena_type = {
180 "Google::Protobuf::Internal::Arena",
181 {Arena_mark, Arena_free, Arena_memsize},
182 .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
183 };
184
ruby_upb_allocfunc(upb_alloc * alloc,void * ptr,size_t oldsize,size_t size)185 static void *ruby_upb_allocfunc(upb_alloc *alloc, void *ptr, size_t oldsize,
186 size_t size) {
187 if (size == 0) {
188 xfree(ptr);
189 return NULL;
190 } else {
191 return xrealloc(ptr, size);
192 }
193 }
194
195 upb_alloc ruby_upb_alloc = {&ruby_upb_allocfunc};
196
Arena_alloc(VALUE klass)197 static VALUE Arena_alloc(VALUE klass) {
198 Arena *arena = ALLOC(Arena);
199 arena->arena = upb_Arena_Init(NULL, 0, &ruby_upb_alloc);
200 arena->pinned_objs = Qnil;
201 return TypedData_Wrap_Struct(klass, &Arena_type, arena);
202 }
203
Arena_get(VALUE _arena)204 upb_Arena *Arena_get(VALUE _arena) {
205 Arena *arena;
206 TypedData_Get_Struct(_arena, Arena, &Arena_type, arena);
207 return arena->arena;
208 }
209
Arena_fuse(VALUE _arena,upb_Arena * other)210 void Arena_fuse(VALUE _arena, upb_Arena *other) {
211 Arena *arena;
212 TypedData_Get_Struct(_arena, Arena, &Arena_type, arena);
213 if (!upb_Arena_Fuse(arena->arena, other)) {
214 rb_raise(rb_eRuntimeError,
215 "Unable to fuse arenas. This should never happen since Ruby does "
216 "not use initial blocks");
217 }
218 }
219
Arena_new()220 VALUE Arena_new() { return Arena_alloc(cArena); }
221
Arena_register(VALUE module)222 void Arena_register(VALUE module) {
223 VALUE internal = rb_define_module_under(module, "Internal");
224 VALUE klass = rb_define_class_under(internal, "Arena", rb_cObject);
225 rb_define_alloc_func(klass, Arena_alloc);
226 rb_gc_register_address(&cArena);
227 cArena = klass;
228 }
229
230 // -----------------------------------------------------------------------------
231 // Object Cache
232 // -----------------------------------------------------------------------------
233
234 // Public ObjectCache API.
235
236 VALUE weak_obj_cache = Qnil;
237 ID item_get;
238 ID item_try_add;
239
ObjectCache_Init(VALUE protobuf)240 static void ObjectCache_Init(VALUE protobuf) {
241 item_get = rb_intern("get");
242 item_try_add = rb_intern("try_add");
243
244 rb_gc_register_address(&weak_obj_cache);
245 VALUE internal = rb_const_get(protobuf, rb_intern("Internal"));
246 #if SIZEOF_LONG >= SIZEOF_VALUE
247 VALUE cache_class = rb_const_get(internal, rb_intern("ObjectCache"));
248 #else
249 VALUE cache_class = rb_const_get(internal, rb_intern("LegacyObjectCache"));
250 #endif
251
252 weak_obj_cache = rb_class_new_instance(0, NULL, cache_class);
253 rb_const_set(internal, rb_intern("OBJECT_CACHE"), weak_obj_cache);
254 rb_const_set(internal, rb_intern("SIZEOF_LONG"), INT2NUM(SIZEOF_LONG));
255 rb_const_set(internal, rb_intern("SIZEOF_VALUE"), INT2NUM(SIZEOF_VALUE));
256 }
257
ObjectCache_GetKey(const void * key)258 static VALUE ObjectCache_GetKey(const void *key) {
259 VALUE key_val = (VALUE)key;
260 PBRUBY_ASSERT((key_val & 3) == 0);
261 // Ensure the key can be stored as a Fixnum since 1 bit is needed for
262 // FIXNUM_FLAG and 1 bit is needed for the sign bit.
263 VALUE new_key = LL2NUM(key_val >> 2);
264 PBRUBY_ASSERT(FIXNUM_P(new_key));
265 return new_key;
266 }
267
ObjectCache_TryAdd(const void * key,VALUE val)268 VALUE ObjectCache_TryAdd(const void *key, VALUE val) {
269 VALUE key_val = ObjectCache_GetKey(key);
270 return rb_funcall(weak_obj_cache, item_try_add, 2, key_val, val);
271 }
272
273 // Returns the cached object for this key, if any. Otherwise returns Qnil.
ObjectCache_Get(const void * key)274 VALUE ObjectCache_Get(const void *key) {
275 VALUE key_val = ObjectCache_GetKey(key);
276 return rb_funcall(weak_obj_cache, item_get, 1, key_val);
277 }
278
279 /*
280 * call-seq:
281 * Google::Protobuf.discard_unknown(msg)
282 *
283 * Discard unknown fields in the given message object and recursively discard
284 * unknown fields in submessages.
285 */
Google_Protobuf_discard_unknown(VALUE self,VALUE msg_rb)286 static VALUE Google_Protobuf_discard_unknown(VALUE self, VALUE msg_rb) {
287 const upb_MessageDef *m;
288 upb_Message *msg = Message_GetMutable(msg_rb, &m);
289 if (!upb_Message_DiscardUnknown(msg, m, 128)) {
290 rb_raise(rb_eRuntimeError, "Messages nested too deeply.");
291 }
292
293 return Qnil;
294 }
295
296 /*
297 * call-seq:
298 * Google::Protobuf.deep_copy(obj) => copy_of_obj
299 *
300 * Performs a deep copy of a RepeatedField instance, a Map instance, or a
301 * message object, recursively copying its members.
302 */
Google_Protobuf_deep_copy(VALUE self,VALUE obj)303 VALUE Google_Protobuf_deep_copy(VALUE self, VALUE obj) {
304 VALUE klass = CLASS_OF(obj);
305 if (klass == cRepeatedField) {
306 return RepeatedField_deep_copy(obj);
307 } else if (klass == cMap) {
308 return Map_deep_copy(obj);
309 } else {
310 VALUE new_arena_rb = Arena_new();
311 upb_Arena *new_arena = Arena_get(new_arena_rb);
312 const upb_MessageDef *m;
313 const upb_Message *msg = Message_Get(obj, &m);
314 upb_Message *new_msg = Message_deep_copy(msg, m, new_arena);
315 return Message_GetRubyWrapper(new_msg, m, new_arena_rb);
316 }
317 }
318
319 // -----------------------------------------------------------------------------
320 // Initialization/entry point.
321 // -----------------------------------------------------------------------------
322
323 // This must be named "Init_protobuf_c" because the Ruby module is named
324 // "protobuf_c" -- the VM looks for this symbol in our .so.
Init_protobuf_c()325 __attribute__((visibility("default"))) void Init_protobuf_c() {
326 VALUE google = rb_define_module("Google");
327 VALUE protobuf = rb_define_module_under(google, "Protobuf");
328
329 ObjectCache_Init(protobuf);
330 Arena_register(protobuf);
331 Defs_register(protobuf);
332 RepeatedField_register(protobuf);
333 Map_register(protobuf);
334 Message_register(protobuf);
335
336 cParseError = rb_const_get(protobuf, rb_intern("ParseError"));
337 rb_gc_register_mark_object(cParseError);
338 cTypeError = rb_const_get(protobuf, rb_intern("TypeError"));
339 rb_gc_register_mark_object(cTypeError);
340
341 rb_define_singleton_method(protobuf, "discard_unknown",
342 Google_Protobuf_discard_unknown, 1);
343 rb_define_singleton_method(protobuf, "deep_copy", Google_Protobuf_deep_copy,
344 1);
345 }
346
347 // -----------------------------------------------------------------------------
348 // Utilities
349 // -----------------------------------------------------------------------------
350
351 // Raises a Ruby error if val is frozen in Ruby or UPB.
Protobuf_CheckNotFrozen(VALUE val,bool upb_frozen)352 void Protobuf_CheckNotFrozen(VALUE val, bool upb_frozen) {
353 if (RB_UNLIKELY(rb_obj_frozen_p(val)||upb_frozen)) {
354 rb_error_frozen_object(val);
355 }
356 }
357