tinybits 0.9.0 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d36aff126ea60de67bc4050896a329f39e53a155cf50f4b12f35602757dc4ab1
4
- data.tar.gz: 610909057ad8ae5f8fa0dab0e1dd5ee03105b7fb0036b11e3ac22eea97522cee
3
+ metadata.gz: b196201c8c0379a74ae23e62d10022ed7deae323cba4d9281025941b1a8b5d77
4
+ data.tar.gz: 47acdb4977e7cbf79a48c74ea85a8917be38357f886a88ab33f483b8d396c34d
5
5
  SHA512:
6
- metadata.gz: fb4ce2802d01a5a62a119edc8f8c7ea06aff12edf0eff1d9b0d3a758d096e36c40c49c3ca52c22368da199b65b75900db1a5296580e38b9d2c00da62b80d6535
7
- data.tar.gz: 16355ff8ae048bf3ae8d50eb2067cc42e01e21da1a396713d3a74e831e163658f3631dc19c09fc52c867fcf4b24bd29399029f44fabe9ac92be99eaccaa66a9b
6
+ metadata.gz: ad078aa3bf66b321bf208d679393ba9856a553d321bacc8d3b62728feca300bf42fa5e497ecfd87c6b2bb80050dfbf61d70b8edd2378e1650095b4314303ccbb
7
+ data.tar.gz: 9b441821ee20ddaa0fbbecced7de678bf4dd1e0f7874c3174a73d843bee7b0ed720a37aa08a622117f24ab3a03acd15d448b90e325b1da9f662825ac85128c94
@@ -326,23 +326,59 @@ static VALUE rb_reset(VALUE self){
326
326
  return self;
327
327
  }
328
328
 
329
+ #define KEY_CACHE_CAPA 63
330
+ #define KEY_CACHE_MAX_MISSES 1024
331
+
329
332
  // Unpacker structure
330
333
  typedef struct {
331
334
  tiny_bits_unpacker* unpacker;
332
335
  int symbolize_keys; // map keys that are strings come back as symbols
333
336
  VALUE strings; // Ruby strings by dedupe id, dictionary strings first
334
337
  VALUE buffer; // the buffer set by #buffer=, kept alive while popping
338
+ int keys_count;
339
+ int keys_misses; // misses while the cache is full
340
+ VALUE keys[KEY_CACHE_CAPA]; // interned map keys sorted by (length, bytes), kept across unpacks
335
341
  } UnpackerData;
336
342
 
337
343
  static void unpacker_mark(void* data) {
338
344
  UnpackerData* unpacker_data = (UnpackerData*)data;
339
345
  rb_gc_mark_movable(unpacker_data->strings);
340
346
  rb_gc_mark(unpacker_data->buffer); // pinned, the C unpacker points into it (embedded strings move)
347
+ for (int i = 0; i < unpacker_data->keys_count; i++) rb_gc_mark_movable(unpacker_data->keys[i]);
341
348
  }
342
349
 
343
350
  static void unpacker_compact(void* data) {
344
351
  UnpackerData* unpacker_data = (UnpackerData*)data;
345
352
  unpacker_data->strings = rb_gc_location(unpacker_data->strings);
353
+ for (int i = 0; i < unpacker_data->keys_count; i++) unpacker_data->keys[i] = rb_gc_location(unpacker_data->keys[i]);
354
+ }
355
+
356
+ // Map keys repeat across unpacks, a small sorted cache saves the global fstring table lookup
357
+ // (same approach as the json gem's name cache, but persistent; when full, misses are not
358
+ // inserted, and after KEY_CACHE_MAX_MISSES of them it restarts empty to follow new keys)
359
+ static inline VALUE key_cache_fetch(UnpackerData* unpacker_data, const char* str, long length) {
360
+ if (length < 2 || length > 128) // same range as string dedupe
361
+ return rb_enc_interned_str(str, length, rb_utf8_encoding());
362
+ VALUE* keys = unpacker_data->keys;
363
+ int low = 0, high = unpacker_data->keys_count - 1;
364
+ while (low <= high) {
365
+ int mid = (low + high) >> 1;
366
+ long cmp = length - RSTRING_LEN(keys[mid]);
367
+ if (cmp == 0) cmp = memcmp(str, RSTRING_PTR(keys[mid]), length);
368
+ if (cmp == 0) return keys[mid];
369
+ if (cmp > 0) low = mid + 1; else high = mid - 1;
370
+ }
371
+ VALUE key = rb_enc_interned_str(str, length, rb_utf8_encoding());
372
+ if (unpacker_data->keys_count == KEY_CACHE_CAPA) {
373
+ if (++unpacker_data->keys_misses < KEY_CACHE_MAX_MISSES) return key;
374
+ unpacker_data->keys_misses = 0;
375
+ unpacker_data->keys_count = 0;
376
+ low = 0;
377
+ }
378
+ MEMMOVE(&keys[low + 1], &keys[low], VALUE, unpacker_data->keys_count - low);
379
+ keys[low] = key;
380
+ unpacker_data->keys_count++;
381
+ return key;
346
382
  }
347
383
 
348
384
  static void unpacker_free(void* data) {
@@ -369,6 +405,8 @@ static VALUE rb_unpacker_alloc(VALUE klass) {
369
405
  unpacker_data->symbolize_keys = 0;
370
406
  unpacker_data->strings = Qnil;
371
407
  unpacker_data->buffer = Qnil;
408
+ unpacker_data->keys_count = 0;
409
+ unpacker_data->keys_misses = 0;
372
410
  return TypedData_Wrap_Struct(klass, &unpacker_data_type, unpacker_data);
373
411
  }
374
412
 
@@ -421,7 +459,7 @@ static inline VALUE rb_unpack_str(VALUE strings, UnpackerData* unpacker_data, ti
421
459
  else if(id <= 0){
422
460
  VALUE str;
423
461
  if(interned > 0){
424
- str = rb_enc_interned_str(value.str_blob_val.data, value.str_blob_val.length, rb_utf8_encoding());
462
+ str = key_cache_fetch(unpacker_data, value.str_blob_val.data, value.str_blob_val.length);
425
463
  } else {
426
464
  str = rb_utf8_str_new(value.str_blob_val.data, value.str_blob_val.length);
427
465
  rb_obj_freeze(str);
@@ -464,6 +502,14 @@ static VALUE unpack_ruby_object(VALUE strings, UnpackerData* unpacker_data, size
464
502
  case TINY_BITS_FALSE:
465
503
  return Qfalse;
466
504
  case TINY_BITS_ARRAY: {
505
+ if (value.length <= 16) { // small arrays are created in one call
506
+ VALUE items[16];
507
+ for (size_t i = 0; i < value.length; i++) {
508
+ items[i] = unpack_ruby_object(strings, unpacker_data, 0);
509
+ if (items[i] == Qundef) return Qundef; // Error
510
+ }
511
+ return rb_ary_new_from_values(value.length, items);
512
+ }
467
513
  VALUE arr = rb_ary_new_capa(value.length);
468
514
  for (size_t i = 0; i < value.length; i++) {
469
515
  VALUE element = unpack_ruby_object(strings, unpacker_data, 0);
@@ -1,3 +1,3 @@
1
1
  module TinyBits
2
- VERSION = '0.9.0'.freeze
2
+ VERSION = '0.9.1'.freeze
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tinybits
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.0
4
+ version: 0.9.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mohamed Hassan