• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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