|
|
|
@ -167,30 +167,55 @@ void StringBuilder_PrintMsgval(StringBuilder* b, upb_msgval val, |
|
|
|
|
// Arena
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
void Arena_free(void* data) { upb_arena_free(data); } |
|
|
|
|
typedef struct { |
|
|
|
|
upb_arena *arena; |
|
|
|
|
VALUE pinned_objs; |
|
|
|
|
} Arena; |
|
|
|
|
|
|
|
|
|
static void Arena_mark(void *data) { |
|
|
|
|
Arena *arena = data; |
|
|
|
|
rb_gc_mark(arena->pinned_objs); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void Arena_free(void *data) { |
|
|
|
|
Arena *arena = data; |
|
|
|
|
upb_arena_free(arena->arena); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static VALUE cArena; |
|
|
|
|
|
|
|
|
|
const rb_data_type_t Arena_type = { |
|
|
|
|
"Google::Protobuf::Internal::Arena", |
|
|
|
|
{ NULL, Arena_free, NULL }, |
|
|
|
|
{ Arena_mark, Arena_free, NULL }, |
|
|
|
|
.flags = RUBY_TYPED_FREE_IMMEDIATELY, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
static VALUE Arena_alloc(VALUE klass) { |
|
|
|
|
upb_arena *arena = upb_arena_new(); |
|
|
|
|
Arena *arena = ALLOC(Arena); |
|
|
|
|
arena->arena = upb_arena_new(); |
|
|
|
|
arena->pinned_objs = Qnil; |
|
|
|
|
return TypedData_Wrap_Struct(klass, &Arena_type, arena); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
upb_arena *Arena_get(VALUE _arena) { |
|
|
|
|
upb_arena *arena; |
|
|
|
|
TypedData_Get_Struct(_arena, upb_arena, &Arena_type, arena); |
|
|
|
|
return arena; |
|
|
|
|
Arena *arena; |
|
|
|
|
TypedData_Get_Struct(_arena, Arena, &Arena_type, arena); |
|
|
|
|
return arena->arena; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
VALUE Arena_new() { |
|
|
|
|
return Arena_alloc(cArena); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void Arena_Pin(VALUE _arena, VALUE obj) { |
|
|
|
|
Arena *arena; |
|
|
|
|
TypedData_Get_Struct(_arena, Arena, &Arena_type, arena); |
|
|
|
|
if (arena->pinned_objs == Qnil) { |
|
|
|
|
arena->pinned_objs = rb_ary_new(); |
|
|
|
|
} |
|
|
|
|
rb_ary_push(arena->pinned_objs, obj); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void Arena_register(VALUE module) { |
|
|
|
|
VALUE internal = rb_define_module_under(module, "Internal"); |
|
|
|
|
VALUE klass = rb_define_class_under(internal, "Arena", rb_cObject); |
|
|
|
@ -209,122 +234,79 @@ void Arena_register(VALUE module) { |
|
|
|
|
// different wrapper objects for the same C object, which saves memory and
|
|
|
|
|
// preserves object identity.
|
|
|
|
|
//
|
|
|
|
|
// We use Hash and/or WeakMap for the cache. WeakMap is faster overall
|
|
|
|
|
// (probably due to removal being integrated with GC) but doesn't work for Ruby
|
|
|
|
|
// <2.7 (see note below). We need Hash for Ruby <2.7 and for cases where we
|
|
|
|
|
// need to GC-root the object (notably when the object has been frozen).
|
|
|
|
|
// We use WeakMap for the cache. For Ruby <2.7 we also need a secondary Hash
|
|
|
|
|
// to store WeakMap keys because Ruby <2.7 WeakMap doesn't allow non-finalizable
|
|
|
|
|
// keys.
|
|
|
|
|
|
|
|
|
|
#if RUBY_API_VERSION_CODE >= 20700 |
|
|
|
|
#define USE_WEAK_MAP 1 |
|
|
|
|
#define USE_SECONDARY_MAP 0 |
|
|
|
|
#else |
|
|
|
|
#define USE_WEAK_MAP 0 |
|
|
|
|
#define USE_SECONDARY_MAP 1 |
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
static VALUE ObjectCache_GetKey(const void* key) { |
|
|
|
|
char buf[sizeof(key)]; |
|
|
|
|
memcpy(&buf, &key, sizeof(key)); |
|
|
|
|
intptr_t key_int = (intptr_t)key; |
|
|
|
|
PBRUBY_ASSERT((key_int & 3) == 0); |
|
|
|
|
return LL2NUM(key_int >> 2); |
|
|
|
|
} |
|
|
|
|
#if USE_SECONDARY_MAP |
|
|
|
|
|
|
|
|
|
// Strong object cache, uses regular Hash and GC-roots objects.
|
|
|
|
|
// - For Ruby <2.7, used for all objects.
|
|
|
|
|
// - For Ruby >=2.7, used only for frozen objects, so we preserve the "frozen"
|
|
|
|
|
// bit (since this information is not preserved at the upb level).
|
|
|
|
|
// Maps Numeric -> Object. The object is then used as a key into the WeakMap.
|
|
|
|
|
// This is needed for Ruby <2.7 where a number cannot be a key to WeakMap.
|
|
|
|
|
// The object is used only for its identity; it does not contain any data.
|
|
|
|
|
VALUE secondary_map = Qnil; |
|
|
|
|
|
|
|
|
|
VALUE strong_obj_cache = Qnil; |
|
|
|
|
|
|
|
|
|
static void StrongObjectCache_Init() { |
|
|
|
|
rb_gc_register_address(&strong_obj_cache); |
|
|
|
|
strong_obj_cache = rb_hash_new(); |
|
|
|
|
static void SecondaryMap_Init() { |
|
|
|
|
rb_gc_register_address(&secondary_map); |
|
|
|
|
secondary_map = rb_hash_new(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void StrongObjectCache_Remove(void* key) { |
|
|
|
|
VALUE key_rb = ObjectCache_GetKey(key); |
|
|
|
|
PBRUBY_ASSERT(rb_hash_lookup(strong_obj_cache, key_rb) != Qnil); |
|
|
|
|
rb_hash_delete(strong_obj_cache, key_rb); |
|
|
|
|
static VALUE SecondaryMap_Get(VALUE key) { |
|
|
|
|
VALUE ret = rb_hash_lookup(secondary_map, key); |
|
|
|
|
if (ret == Qnil) { |
|
|
|
|
ret = rb_eval_string("Object.new"); |
|
|
|
|
rb_hash_aset(secondary_map, key, ret); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static VALUE StrongObjectCache_Get(const void* key) { |
|
|
|
|
VALUE key_rb = ObjectCache_GetKey(key); |
|
|
|
|
return rb_hash_lookup(strong_obj_cache, key_rb); |
|
|
|
|
return ret; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void StrongObjectCache_Add(const void* key, VALUE val, |
|
|
|
|
upb_arena* arena) { |
|
|
|
|
PBRUBY_ASSERT(StrongObjectCache_Get(key) == Qnil); |
|
|
|
|
VALUE key_rb = ObjectCache_GetKey(key); |
|
|
|
|
rb_hash_aset(strong_obj_cache, key_rb, val); |
|
|
|
|
upb_arena_addcleanup(arena, (void*)key, StrongObjectCache_Remove); |
|
|
|
|
} |
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
// Weak object cache. This speeds up the test suite significantly, so we
|
|
|
|
|
// presume it speeds up real code also. However we can only use it in Ruby
|
|
|
|
|
// >=2.7 due to:
|
|
|
|
|
// https://bugs.ruby-lang.org/issues/16035
|
|
|
|
|
static VALUE ObjectCache_GetKey(const void* key) { |
|
|
|
|
char buf[sizeof(key)]; |
|
|
|
|
memcpy(&buf, &key, sizeof(key)); |
|
|
|
|
intptr_t key_int = (intptr_t)key; |
|
|
|
|
PBRUBY_ASSERT((key_int & 3) == 0); |
|
|
|
|
VALUE ret = LL2NUM(key_int >> 2); |
|
|
|
|
#if USE_SECONDARY_MAP |
|
|
|
|
ret = SecondaryMap_Get(ret); |
|
|
|
|
#endif |
|
|
|
|
return ret; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#if USE_WEAK_MAP |
|
|
|
|
// Public ObjectCache API.
|
|
|
|
|
|
|
|
|
|
VALUE weak_obj_cache = Qnil; |
|
|
|
|
ID item_get; |
|
|
|
|
ID item_set; |
|
|
|
|
|
|
|
|
|
static void WeakObjectCache_Init() { |
|
|
|
|
static void ObjectCache_Init() { |
|
|
|
|
rb_gc_register_address(&weak_obj_cache); |
|
|
|
|
VALUE klass = rb_eval_string("ObjectSpace::WeakMap"); |
|
|
|
|
weak_obj_cache = rb_class_new_instance(0, NULL, klass); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static VALUE WeakObjectCache_Get(const void* key) { |
|
|
|
|
VALUE key_rb = ObjectCache_GetKey(key); |
|
|
|
|
VALUE ret = rb_funcall(weak_obj_cache, rb_intern("[]"), 1, key_rb); |
|
|
|
|
return ret; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void WeakObjectCache_Add(const void* key, VALUE val) { |
|
|
|
|
PBRUBY_ASSERT(WeakObjectCache_Get(key) == Qnil); |
|
|
|
|
VALUE key_rb = ObjectCache_GetKey(key); |
|
|
|
|
rb_funcall(weak_obj_cache, rb_intern("[]="), 2, key_rb, val); |
|
|
|
|
PBRUBY_ASSERT(WeakObjectCache_Get(key) == val); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
// Public ObjectCache API.
|
|
|
|
|
|
|
|
|
|
static void ObjectCache_Init() { |
|
|
|
|
StrongObjectCache_Init(); |
|
|
|
|
#if USE_WEAK_MAP |
|
|
|
|
WeakObjectCache_Init(); |
|
|
|
|
item_get = rb_intern("[]"); |
|
|
|
|
item_set = rb_intern("[]="); |
|
|
|
|
#if USE_SECONDARY_MAP |
|
|
|
|
SecondaryMap_Init(); |
|
|
|
|
#endif |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void ObjectCache_Add(const void* key, VALUE val, upb_arena *arena) { |
|
|
|
|
#if USE_WEAK_MAP |
|
|
|
|
(void)arena; |
|
|
|
|
WeakObjectCache_Add(key, val); |
|
|
|
|
#else |
|
|
|
|
StrongObjectCache_Add(key, val, arena); |
|
|
|
|
#endif |
|
|
|
|
void ObjectCache_Add(const void* key, VALUE val) { |
|
|
|
|
PBRUBY_ASSERT(ObjectCache_Get(key) == Qnil); |
|
|
|
|
VALUE key_rb = ObjectCache_GetKey(key); |
|
|
|
|
rb_funcall(weak_obj_cache, item_set, 2, key_rb, val); |
|
|
|
|
PBRUBY_ASSERT(ObjectCache_Get(key) == val); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Returns the cached object for this key, if any. Otherwise returns Qnil.
|
|
|
|
|
VALUE ObjectCache_Get(const void* key) { |
|
|
|
|
#if USE_WEAK_MAP |
|
|
|
|
return WeakObjectCache_Get(key); |
|
|
|
|
#else |
|
|
|
|
return StrongObjectCache_Get(key); |
|
|
|
|
#endif |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void ObjectCache_Pin(const void* key, VALUE val, upb_arena *arena) { |
|
|
|
|
#if USE_WEAK_MAP |
|
|
|
|
PBRUBY_ASSERT(WeakObjectCache_Get(key) == val); |
|
|
|
|
// This will GC-root the object, but we'll still use the weak map for
|
|
|
|
|
// actual lookup.
|
|
|
|
|
StrongObjectCache_Add(key, val, arena); |
|
|
|
|
#else |
|
|
|
|
// Value is already pinned, nothing to do.
|
|
|
|
|
#endif |
|
|
|
|
VALUE key_rb = ObjectCache_GetKey(key); |
|
|
|
|
return rb_funcall(weak_obj_cache, item_get, 1, key_rb); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|