tinybits 0.4.0 → 0.6.0

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: 579aa574466ff4f72f4e9fa59940d3d14fc773404cb73296b2b1498551da6d2f
4
- data.tar.gz: 5edf98872ab8d59a3868b0dc6e3b05271d8dbdba7664afc039ca219820a7b247
3
+ metadata.gz: 9ce29d814129fc925832887a9a4d83efd6dff20a614aab92e2dc80d10197d5aa
4
+ data.tar.gz: 137f8181f9d0f4c4e3d6d5abe3cb3c0014de07149e08a6e1f4d6c134cbc5c003
5
5
  SHA512:
6
- metadata.gz: 887b0b5c435cd7da039af2b370f680c5266c06cad1db665e54436f81a22172837495739c051ccea3050a446e055ca24a9198269a51bdb7005fec79d398c37758
7
- data.tar.gz: 44c1cd7a40ccf330d8ca96a5c7203b6ce60c07c00c3580fe28440ef0b62c6c083ad4417143a52afce3ae0553eefbc7a9d9035e7011d13557f682704be6f5ad5d
6
+ metadata.gz: ea777d8a1c1230512e9865e6cababb992f45d70b6c1c8d03d65567c82bda4a514ea1759341decf4c2b5da6934c23d75bc4f01ca7682a32136557382ba368d256
7
+ data.tar.gz: f16d288249b7cdb434e23d80823a5c5816a7b3be3989f0d0c0ce2513c7255c7f3e1df06b48f1a57681d4ccb3678328f8aa207fcdd5cbe2289cf7372f647aa52a
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * TinyBits Amalgamated Header
3
- * Generated on: Sun May 4 05:43:30 PM CEST 2025
3
+ * Generated on: Tue May 13 11:39:13 PM CEST 2025
4
4
  */
5
5
 
6
6
  #ifndef TINY_BITS_H
@@ -176,46 +176,111 @@ static inline int varint_size(uint64_t value){
176
176
  return 9;
177
177
  }
178
178
 
179
- static inline uint64_t decode_varint(const uint8_t* buffer, size_t size, size_t *pos) {
179
+ static inline int8_t decode_varint(const uint8_t* buffer, size_t size, size_t pos, uint64_t *value) {
180
+ if(pos >= size) return 0;
181
+ uint8_t prefix = buffer[pos];
182
+ if (prefix <= 240) {
183
+ *value = prefix;
184
+ return 1;
185
+ } else if (prefix >= 241 && prefix <= 248) {
186
+ if (pos + 1 >= size) return 0; // Not enough bytes
187
+ *value = 240 + 256 * (prefix - 241) + buffer[pos+1];
188
+ return 2;
189
+ } else if (prefix == 249){
190
+ if (pos + 2 >= size) return 0; // Not enough bytes
191
+ *value = 2288 + 256 * buffer[pos+1] + buffer[pos+2];
192
+ return 3;
193
+ } else if (prefix == 250){
194
+ if (pos + 3 >= size) return 0; // Not enough bytes
195
+ *value = ((uint64_t)buffer[pos+1] << 16) | ((uint64_t)buffer[pos+2] << 8) | buffer[pos+3];
196
+ return 4;
197
+ } else if (prefix == 251){
198
+ if (pos + 4 >= size) return 0; // Not enough bytes
199
+ *value = ((uint64_t)buffer[pos+1] << 24) | ((uint64_t)buffer[pos+2] << 16) |
200
+ ((uint64_t)buffer[pos+3] << 8) | buffer[pos+4];
201
+ return 5;
202
+ } else if (prefix == 252){
203
+ if (pos + 5 >= size) return 0; // Not enough bytes
204
+ *value = ((uint64_t)buffer[pos+1] << 32) | ((uint64_t)buffer[pos+2] << 24) |
205
+ ((uint64_t)buffer[pos+3] << 16) | ((uint64_t)buffer[pos+4] << 8) | buffer[pos+5];
206
+ return 6;
207
+ } else if (prefix == 253){
208
+ if (pos + 6 >= size) return 0; // Not enough bytes
209
+ *value = ((uint64_t)buffer[pos+1] << 40) | ((uint64_t)buffer[pos+2] << 32) |
210
+ ((uint64_t)buffer[pos+3] << 24) | ((uint64_t)buffer[pos+4] << 16) |
211
+ ((uint64_t)buffer[pos+5] << 8) | buffer[pos+6];
212
+ return 7;
213
+ } else if (prefix == 254){
214
+ if (pos + 7 >= size) return 0; // Not enough bytes
215
+ *value = ((uint64_t)buffer[pos+1] << 48) | ((uint64_t)buffer[pos+2] << 40) |
216
+ ((uint64_t)buffer[pos+3] << 32) | ((uint64_t)buffer[pos+4] << 24) |
217
+ ((uint64_t)buffer[pos+5] << 16) | ((uint64_t)buffer[pos+6] << 8) | buffer[pos+7];
218
+ return 8;
219
+ } else if (prefix == 255){
220
+ if (pos + 8 >= size) return 0; // Not enough bytes
221
+ *value = ((uint64_t)buffer[pos+1] << 56) | ((uint64_t)buffer[pos+2] << 48) |
222
+ ((uint64_t)buffer[pos+3] << 40) | ((uint64_t)buffer[pos+4] << 32) |
223
+ ((uint64_t)buffer[pos+5] << 24) | ((uint64_t)buffer[pos+6] << 16) |
224
+ ((uint64_t)buffer[pos+7] << 8) | buffer[pos+8];
225
+ return 9;
226
+ } else {
227
+ return 0;
228
+ }
229
+
230
+ }
231
+
232
+ static inline uint64_t decode_varint_old(const uint8_t* buffer, size_t size, size_t *pos) {
233
+ if (*pos >= size) {
234
+ return 0; // not enough buffer
235
+ }
236
+
180
237
  uint8_t prefix = buffer[*pos];
181
238
  if (prefix <= 240) {
182
239
  *pos += 1;
183
240
  return prefix;
184
241
  } else if (prefix >= 241 && prefix <= 248) {
242
+ if (*pos + 1 >= size) return 0; // Not enough bytes
185
243
  uint64_t value = 240 + 256 * (prefix - 241) + buffer[*pos+1];
186
244
  *pos += 2;
187
245
  return value;
188
246
  } else if (prefix == 249) {
247
+ if (*pos + 2 >= size) return 0; // Not enough bytes
189
248
  uint64_t value = 2288 + 256 * buffer[*pos+1] + buffer[*pos+2];
190
249
  *pos += 3;
191
250
  return value;
192
251
  } else if (prefix == 250) {
252
+ if (*pos + 3 >= size) return 0; // Not enough bytes
193
253
  uint64_t value = ((uint64_t)buffer[*pos+1] << 16) | ((uint64_t)buffer[*pos+2] << 8) | buffer[*pos+3];
194
254
  *pos += 4;
195
255
  return value;
196
256
  } else if (prefix == 251) {
257
+ if (*pos + 4 >= size) return 0; // Not enough bytes
197
258
  uint64_t value = ((uint64_t)buffer[*pos+1] << 24) | ((uint64_t)buffer[*pos+2] << 16) |
198
259
  ((uint64_t)buffer[*pos+3] << 8) | buffer[*pos+4];
199
260
  *pos += 5;
200
261
  return value;
201
262
  } else if (prefix == 252) {
263
+ if (*pos + 5 >= size) return 0; // Not enough bytes
202
264
  uint64_t value = ((uint64_t)buffer[*pos+1] << 32) | ((uint64_t)buffer[*pos+2] << 24) |
203
265
  ((uint64_t)buffer[*pos+3] << 16) | ((uint64_t)buffer[*pos+4] << 8) | buffer[*pos+5];
204
266
  *pos += 6;
205
267
  return value;
206
268
  } else if (prefix == 253) {
269
+ if (*pos + 6 >= size) return 0; // Not enough bytes
207
270
  uint64_t value = ((uint64_t)buffer[*pos+1] << 40) | ((uint64_t)buffer[*pos+2] << 32) |
208
271
  ((uint64_t)buffer[*pos+3] << 24) | ((uint64_t)buffer[*pos+4] << 16) |
209
272
  ((uint64_t)buffer[*pos+5] << 8) | buffer[*pos+6];
210
273
  *pos += 7;
211
274
  return value;
212
275
  } else if (prefix == 254) {
276
+ if (*pos + 7 >= size) return 0; // Not enough bytes
213
277
  uint64_t value = ((uint64_t)buffer[*pos+1] << 48) | ((uint64_t)buffer[*pos+2] << 40) |
214
278
  ((uint64_t)buffer[*pos+3] << 32) | ((uint64_t)buffer[*pos+4] << 24) |
215
279
  ((uint64_t)buffer[*pos+5] << 16) | ((uint64_t)buffer[*pos+6] << 8) | buffer[*pos+7];
216
280
  *pos += 8;
217
281
  return value;
218
282
  } else if (prefix == 255) {
283
+ if (*pos + 8 >= size) return 0; // Not enough bytes
219
284
  uint64_t value = ((uint64_t)buffer[*pos+1] << 56) | ((uint64_t)buffer[*pos+2] << 48) |
220
285
  ((uint64_t)buffer[*pos+3] << 40) | ((uint64_t)buffer[*pos+4] << 32) |
221
286
  ((uint64_t)buffer[*pos+5] << 24) | ((uint64_t)buffer[*pos+6] << 16) |
@@ -399,6 +464,7 @@ tiny_bits_packer *tiny_bits_packer_create(size_t initial_capacity, uint8_t featu
399
464
  encoder->encode_table.cache_size = TB_HASH_CACHE_SIZE;
400
465
  encoder->encode_table.cache_pos = 0;
401
466
  encoder->encode_table.next_id = 0;
467
+ memset(encoder->encode_table.bins, 0, TB_HASH_SIZE * sizeof(uint8_t));
402
468
  } else {
403
469
  encoder->encode_table.cache = NULL;
404
470
  encoder->encode_table.cache_size = 0;
@@ -953,17 +1019,23 @@ static inline enum tiny_bits_type _unpack_int(tiny_bits_unpacker *decoder, uint8
953
1019
  value->int_val = tag - 128;
954
1020
  return TINY_BITS_INT;
955
1021
  } else if (tag == 248) { // Positive with continuation
956
- uint64_t val = decode_varint(decoder->buffer, decoder->size, &pos);
1022
+ uint8_t read;
1023
+ uint64_t val;
1024
+ read = decode_varint(decoder->buffer, decoder->size, pos, &val);
1025
+ if(read == 0) return TINY_BITS_ERROR;
957
1026
  value->int_val = val + 120;
958
- decoder->current_pos = pos;
1027
+ decoder->current_pos += read;
959
1028
  return TINY_BITS_INT;
960
1029
  } else if (tag > 248 && tag < 255) { // Small negative (248-254)
961
1030
  value->int_val = -(tag - 248);
962
1031
  return TINY_BITS_INT;
963
1032
  } else { // 255: Negative with continuation
964
- uint64_t val = decode_varint(decoder->buffer, decoder->size, &pos);
1033
+ uint8_t read;
1034
+ uint64_t val;
1035
+ read = decode_varint(decoder->buffer, decoder->size, pos, &val);
1036
+ if(read == 0) return TINY_BITS_ERROR;
965
1037
  value->int_val = -(val + 7);
966
- decoder->current_pos = pos;
1038
+ decoder->current_pos += read;
967
1039
  return TINY_BITS_INT;
968
1040
  }
969
1041
  }
@@ -973,8 +1045,12 @@ static inline enum tiny_bits_type _unpack_arr(tiny_bits_unpacker *decoder, uint8
973
1045
  if (tag < 0b00001111) { // Small array (0-30)
974
1046
  value->length = tag & 0b00000111;
975
1047
  } else { // Large array
976
- value->length = decode_varint(decoder->buffer, decoder->size, &pos) + 7;
977
- decoder->current_pos = pos;
1048
+ uint8_t read;
1049
+ uint64_t val;
1050
+ read = decode_varint(decoder->buffer, decoder->size, pos, &val);
1051
+ if(read == 0) return TINY_BITS_ERROR;
1052
+ value->length = val + 7;
1053
+ decoder->current_pos += read;
978
1054
  }
979
1055
  return TINY_BITS_ARRAY;
980
1056
  }
@@ -984,8 +1060,12 @@ static inline enum tiny_bits_type _unpack_map(tiny_bits_unpacker *decoder, uint8
984
1060
  if (tag < 0x1F) { // Small map (0-14)
985
1061
  value->length = tag & 0x0F;
986
1062
  } else { // Large map
987
- value->length = decode_varint(decoder->buffer, decoder->size, &pos) + 15;
988
- decoder->current_pos = pos;
1063
+ uint8_t read;
1064
+ uint64_t val;
1065
+ read = decode_varint(decoder->buffer, decoder->size, pos, &val);
1066
+ if(read == 0) return TINY_BITS_ERROR;
1067
+ value->length = val + 15;
1068
+ decoder->current_pos += read;
989
1069
  }
990
1070
  return TINY_BITS_MAP;
991
1071
  }
@@ -993,28 +1073,28 @@ static inline enum tiny_bits_type _unpack_map(tiny_bits_unpacker *decoder, uint8
993
1073
  static inline enum tiny_bits_type _unpack_double(tiny_bits_unpacker *decoder, uint8_t tag, tiny_bits_value *value){
994
1074
  size_t pos = decoder->current_pos;
995
1075
  if (tag == TB_F64_TAG) { // Raw double
1076
+ if(pos + 8 > decoder->size) return TINY_BITS_ERROR;
996
1077
  uint64_t number = decode_uint64(decoder->buffer + pos);
997
1078
  value->double_val = itod_bits(number);
998
1079
  decoder->current_pos += 8;
999
1080
  } else { // Compressed double
1000
- uint64_t number = decode_varint(decoder->buffer, decoder->size, &pos);
1081
+ uint8_t read;
1082
+ uint64_t number;
1083
+ read = decode_varint(decoder->buffer, decoder->size, pos, &number);
1084
+ if(read == 0) return TINY_BITS_ERROR;
1001
1085
  int order = (tag & 0x0F);
1002
1086
  double fractional = (double)number / powers[order];
1003
- //fractional /= powers[order];
1004
1087
  if(tag & 0x10) fractional = -fractional;
1005
1088
  value->double_val = fractional;
1006
- decoder->current_pos = pos;
1089
+ decoder->current_pos += read;
1007
1090
  }
1008
1091
  return TINY_BITS_DOUBLE;
1009
1092
  }
1010
1093
 
1011
1094
  static inline enum tiny_bits_type _unpack_datetime(tiny_bits_unpacker *decoder, uint8_t tag, tiny_bits_value *value){
1012
1095
  size_t pos = decoder->current_pos;
1096
+ if(pos + 8 > decoder->size) return TINY_BITS_ERROR;
1013
1097
  value->datetime_val.offset = decoder->buffer[pos] * (60*15); // convert offset back to seconds (from multiples of 15 minutes)
1014
- //uint8_t dbl_tag = decoder->buffer[decoder->current_pos++];
1015
- //tiny_bits_value dbl_val;
1016
- //_unpack_double(decoder, dbl_tag, &dbl_val);
1017
- //value->datetime_val.unixtime = dbl_val.double_val;
1018
1098
  uint64_t unixtime = decode_uint64(decoder->buffer + pos + 1);
1019
1099
  value->datetime_val.unixtime = itod_bits(unixtime);
1020
1100
  decoder->current_pos += 9;
@@ -1023,10 +1103,14 @@ static inline enum tiny_bits_type _unpack_datetime(tiny_bits_unpacker *decoder,
1023
1103
 
1024
1104
  static inline enum tiny_bits_type _unpack_blob(tiny_bits_unpacker *decoder, uint8_t tag, tiny_bits_value *value){
1025
1105
  size_t pos = decoder->current_pos;
1026
- size_t len = decode_varint(decoder->buffer, decoder->size, &pos);
1106
+ size_t len;
1107
+ size_t read;
1108
+ read = decode_varint(decoder->buffer, decoder->size, pos, &len);
1109
+ if(read == 0) return TINY_BITS_ERROR;
1110
+ if((pos + read + len) > decoder->size) return TINY_BITS_ERROR;
1027
1111
  value->str_blob_val.data = (const char *)decoder->buffer + pos;
1028
1112
  value->str_blob_val.length = len;
1029
- decoder->current_pos = pos + len;
1113
+ decoder->current_pos = pos + read + len;
1030
1114
  return TINY_BITS_BLOB;
1031
1115
  }
1032
1116
 
@@ -1035,22 +1119,35 @@ static inline enum tiny_bits_type _unpack_str(tiny_bits_unpacker *decoder, uint8
1035
1119
  size_t len;
1036
1120
  if (tag < 0x5F) { // Small string (0-30)
1037
1121
  len = tag & 0x1F;
1122
+ if(pos + len > decoder->size) return TINY_BITS_ERROR;
1038
1123
  value->str_blob_val.data = (const char *)decoder->buffer + pos;
1039
1124
  value->str_blob_val.length = len;
1040
- decoder->current_pos = pos + len;
1125
+ decoder->current_pos += len;
1041
1126
  } else if (tag == 0x5F) { // Large string
1042
- len = decode_varint(decoder->buffer, decoder->size, &pos) + 31;
1127
+ size_t read;
1128
+ read = decode_varint(decoder->buffer, decoder->size, pos, &len);
1129
+ if(read == 0) return TINY_BITS_ERROR;
1130
+ len += 31;
1131
+ if(pos + read + len > decoder->size) return TINY_BITS_ERROR;
1043
1132
  value->str_blob_val.data = (const char *)decoder->buffer + pos;
1044
1133
  value->str_blob_val.length = len;
1045
- decoder->current_pos = pos + len;
1134
+ decoder->current_pos += (read + len);
1046
1135
  } else { // Deduplicated (small: < 0x7F, large: 0x7F)
1047
- uint32_t id = (tag < 0x7F) ? (tag & 0x1F) : decode_varint(decoder->buffer, decoder->size, &pos) + 31;
1136
+ size_t id;
1137
+ size_t read;
1138
+ if(tag < 0x7F){
1139
+ id = tag & 0x1F;
1140
+ }else {
1141
+ read = decode_varint(decoder->buffer, decoder->size, pos, &id);
1142
+ if(read == 0) return TINY_BITS_ERROR;
1143
+ id += 31;
1144
+ decoder->current_pos += read; // Update pos after varint
1145
+ }
1048
1146
  if (id >= decoder->strings_count) return TINY_BITS_ERROR;
1049
1147
  len = decoder->strings[id].length;
1050
1148
  value->str_blob_val.data = decoder->strings[id].str;
1051
1149
  value->str_blob_val.length = len;
1052
- value->str_blob_val.id = id+1;
1053
- decoder->current_pos = pos; // Update pos after varint
1150
+ value->str_blob_val.id = id + 1;
1054
1151
  return TINY_BITS_STR;
1055
1152
  }
1056
1153
  value->str_blob_val.id = 0;
@@ -225,13 +225,13 @@ static VALUE rb_push(VALUE self, VALUE obj) {
225
225
  context.packer = packer_data->packer; // Pass the current packer
226
226
  context.error_occurred = 0; // Initialize error flag
227
227
 
228
-
228
+ /*
229
229
  if(initial_pos > 0){
230
230
  if(!pack_separator(packer_data->packer)){
231
231
  rb_raise(rb_eRuntimeError, "Failed to pack object (multi-object packing error)");
232
232
  }
233
233
  }
234
-
234
+ */
235
235
  // Call the optimized recursive function
236
236
  if (!pack_ruby_object_recursive(packer_data->packer, obj, (VALUE)&context)) {
237
237
  // Error occurred during packing (might be unsupported type or tiny_bits error)
@@ -264,6 +264,13 @@ static VALUE rb_to_s(VALUE self){
264
264
  return result;
265
265
  }
266
266
 
267
+ /*
268
+ * Document-method: reset
269
+ *
270
+ * Resets the packer's buffer to empty.
271
+ *
272
+ * @return [Packer] self
273
+ */
267
274
  static VALUE rb_reset(VALUE self){
268
275
  PackerData* packer_data;
269
276
  TypedData_Get_Struct(self, PackerData, &packer_data_type, packer_data);
@@ -281,8 +288,8 @@ static VALUE rb_reset(VALUE self){
281
288
  // Unpacker structure
282
289
  typedef struct {
283
290
  tiny_bits_unpacker* unpacker;
284
- size_t strings_index;
285
- VALUE ruby_strings[TB_HASH_CACHE_SIZE];
291
+ //size_t strings_index;
292
+ //VALUE ruby_strings[TB_HASH_CACHE_SIZE];
286
293
  } UnpackerData;
287
294
 
288
295
  static void unpacker_free(void* data) {
@@ -290,9 +297,9 @@ static void unpacker_free(void* data) {
290
297
  if (unpacker_data->unpacker) {
291
298
  tiny_bits_unpacker_destroy(unpacker_data->unpacker);
292
299
  }
293
- for (size_t i = 0; i < TB_HASH_CACHE_SIZE; i++) {
294
- unpacker_data->ruby_strings[i] = Qnil;
295
- }
300
+ //for (size_t i = 0; i < TB_HASH_CACHE_SIZE; i++) {
301
+ //unpacker_data->ruby_strings[i] = Qnil;
302
+ //}
296
303
  free(unpacker_data);
297
304
  }
298
305
 
@@ -312,6 +319,13 @@ static VALUE rb_unpacker_alloc(VALUE klass) {
312
319
  return TypedData_Wrap_Struct(klass, &unpacker_data_type, unpacker_data);
313
320
  }
314
321
 
322
+ /*
323
+ * Document-method: initialize
324
+ *
325
+ * Initializes a new Unpacker.
326
+ *
327
+ * @return [Unpacker] The initialized unpacker object.
328
+ */
315
329
  static VALUE rb_unpacker_init(VALUE self) {
316
330
  UnpackerData* unpacker_data;
317
331
  TypedData_Get_Struct(self, UnpackerData, &unpacker_data_type, unpacker_data);
@@ -320,14 +334,15 @@ static VALUE rb_unpacker_init(VALUE self) {
320
334
  if (!unpacker_data->unpacker) {
321
335
  rb_raise(rb_eRuntimeError, "Failed to initialize unpacker");
322
336
  }
323
- unpacker_data->strings_index = 0;
337
+ VALUE strings = rb_ary_new_capa(TB_HASH_CACHE_SIZE);
338
+ rb_iv_set(self, "@strings", strings);
324
339
  return self;
325
340
  }
326
341
 
327
- static inline VALUE rb_unpack_str(UnpackerData* unpacker_data, tiny_bits_value value, size_t interned){
342
+ static inline VALUE rb_unpack_str(VALUE strings, UnpackerData* unpacker_data, tiny_bits_value value, size_t interned){
328
343
  int32_t id = value.str_blob_val.id;
329
344
  if(id > 0)
330
- return unpacker_data->ruby_strings[id-1];
345
+ return rb_ary_entry(strings, id-1);
331
346
  else if(id <= 0){
332
347
  VALUE str;
333
348
  if(interned > 0){
@@ -337,14 +352,14 @@ static inline VALUE rb_unpack_str(UnpackerData* unpacker_data, tiny_bits_value v
337
352
  rb_obj_freeze(str);
338
353
  }
339
354
  if(id < 0){
340
- unpacker_data->ruby_strings[abs(id)-1] = str;
355
+ rb_ary_push(strings, str);
341
356
  }
342
357
  return str;
343
358
  }
344
359
  return Qundef;
345
360
  }
346
361
 
347
- static VALUE unpack_ruby_object(UnpackerData* unpacker_data, size_t interned) {
362
+ static VALUE unpack_ruby_object(VALUE strings, UnpackerData* unpacker_data, size_t interned) {
348
363
  tiny_bits_unpacker* unpacker = unpacker_data->unpacker;
349
364
  tiny_bits_value value;
350
365
  enum tiny_bits_type type = unpack_value(unpacker, &value);
@@ -355,7 +370,7 @@ static VALUE unpack_ruby_object(UnpackerData* unpacker_data, size_t interned) {
355
370
 
356
371
  switch (type) {
357
372
  case TINY_BITS_STR: {
358
- return rb_unpack_str(unpacker_data, value, interned);
373
+ return rb_unpack_str(strings, unpacker_data, value, interned);
359
374
  }
360
375
  case TINY_BITS_DOUBLE:
361
376
  return DBL2NUM(value.double_val);
@@ -370,7 +385,7 @@ static VALUE unpack_ruby_object(UnpackerData* unpacker_data, size_t interned) {
370
385
  case TINY_BITS_ARRAY: {
371
386
  VALUE arr = rb_ary_new_capa(value.length);
372
387
  for (size_t i = 0; i < value.length; i++) {
373
- VALUE element = unpack_ruby_object(unpacker_data, 0);
388
+ VALUE element = unpack_ruby_object(strings, unpacker_data, 0);
374
389
  if (element == Qundef) return Qundef; // Error
375
390
  rb_ary_push(arr, element);
376
391
  }
@@ -379,9 +394,9 @@ static VALUE unpack_ruby_object(UnpackerData* unpacker_data, size_t interned) {
379
394
  case TINY_BITS_MAP: {
380
395
  VALUE hash = rb_hash_new_capa(value.length);
381
396
  for (size_t i = 0; i < value.length; i++) {
382
- VALUE key = unpack_ruby_object(unpacker_data, 1);
397
+ VALUE key = unpack_ruby_object(strings, unpacker_data, 0);
383
398
  if (key == Qundef) return Qundef; // Error
384
- VALUE val = unpack_ruby_object(unpacker_data, 0);
399
+ VALUE val = unpack_ruby_object(strings, unpacker_data, 0);
385
400
  if (val == Qundef) return Qundef; // Error
386
401
  rb_hash_aset(hash, key, val);
387
402
  }
@@ -400,6 +415,15 @@ static VALUE unpack_ruby_object(UnpackerData* unpacker_data, size_t interned) {
400
415
  }
401
416
  }
402
417
 
418
+ /*
419
+ * Document-method: unpack
420
+ *
421
+ * Unpacks a binary string into a Ruby object.
422
+ *
423
+ * @param buffer [String] The binary string to unpack.
424
+ * @return [Object] The unpacked Ruby object.
425
+ * @raise [RuntimeError] If unpacking fails.
426
+ */
403
427
  static VALUE rb_unpack(VALUE self, VALUE buffer) {
404
428
  UnpackerData* unpacker_data;
405
429
  TypedData_Get_Struct(self, UnpackerData, &unpacker_data_type, unpacker_data);
@@ -412,14 +436,27 @@ static VALUE rb_unpack(VALUE self, VALUE buffer) {
412
436
 
413
437
  tiny_bits_unpacker_set_buffer(unpacker_data->unpacker, (const unsigned char*)RSTRING_PTR(buffer), RSTRING_LEN(buffer));
414
438
 
415
- VALUE result = unpack_ruby_object(unpacker_data, 0);
439
+ VALUE array = rb_iv_get(self, "@strings");
440
+ VALUE result = unpack_ruby_object(array, unpacker_data, 0);
441
+
416
442
  if (result == Qundef) {
417
443
  rb_raise(rb_eRuntimeError, "Failed to unpack data");
418
444
  }
419
445
 
446
+ rb_ary_clear(array);
447
+
420
448
  return result;
421
449
  }
422
450
 
451
+ /*
452
+ * Document-method: buffer=
453
+ *
454
+ * Sets the buffer for incremental unpacking.
455
+ *
456
+ * @param buffer [String] The binary buffer to unpack incrementally.
457
+ * @return [Unpacker] self
458
+ * @note Stores a reference to the buffer internally.
459
+ */
423
460
  static VALUE rb_set_buffer(VALUE self, VALUE buffer){
424
461
  UnpackerData* unpacker_data;
425
462
  TypedData_Get_Struct(self, UnpackerData, &unpacker_data_type, unpacker_data);
@@ -434,13 +471,25 @@ static VALUE rb_set_buffer(VALUE self, VALUE buffer){
434
471
 
435
472
  // set the buffer as an instance variable to mainatin a reference to it
436
473
  rb_iv_set(self, "@buffer", buffer);
437
-
474
+ VALUE array = rb_iv_get(self, "@strings");
475
+ rb_ary_clear(array);
438
476
  return self;
439
477
  }
440
478
 
479
+ /*
480
+ * Document-method: pop
481
+ *
482
+ * Extracts the next object from the buffer.
483
+ *
484
+ * @return [Object, nil] The next unpacked object or nil if buffer is exhausted.
485
+ * @raise [RuntimeError] If no buffer is set or if unpacking fails.
486
+ * @note Requires #buffer= to be called first.
487
+ */
441
488
  static VALUE rb_pop(VALUE self) {
442
489
 
443
490
  VALUE buffer = rb_iv_get(self, "@buffer");
491
+ VALUE array = rb_iv_get(self, "@strings");
492
+
444
493
  if(buffer == Qnil){
445
494
  rb_raise(rb_eRuntimeError, "No buffer is set");
446
495
  }
@@ -453,27 +502,33 @@ static VALUE rb_pop(VALUE self) {
453
502
  }
454
503
 
455
504
  tiny_bits_unpacker* unpacker = unpacker_data->unpacker;
456
- tiny_bits_value value;
457
505
 
458
506
  if(unpacker->current_pos >= unpacker->size - 1){
507
+ rb_ary_clear(array);
459
508
  return Qnil;
460
509
  }
461
-
462
- if(unpacker->current_pos > 0){
463
- enum tiny_bits_type type = unpack_value(unpacker, &value);
464
- if(type != TINY_BITS_SEP){
465
- rb_raise(rb_eRuntimeError, "Malformed multi-object buffer");
466
- }
467
- }
468
-
469
- VALUE result = unpack_ruby_object(unpacker_data, 0);
510
+
511
+ VALUE result = unpack_ruby_object(array, unpacker_data, 0);
470
512
  if (result == Qundef) {
471
513
  rb_raise(rb_eRuntimeError, "Failed to unpack data");
472
514
  }
473
515
 
516
+ if(unpacker->current_pos >= (unpacker->size - 1)){
517
+ rb_ary_clear(array);
518
+ }
519
+
474
520
  return result;
475
521
  }
476
522
 
523
+ /*
524
+ * Document-method: finished?
525
+ *
526
+ * Checks if the buffer has been fully consumed.
527
+ *
528
+ * @return [Boolean] true if there are no more objects to unpack.
529
+ * @raise [RuntimeError] If no buffer is set.
530
+ * @note Requires #buffer= to be called first.
531
+ */
477
532
  static VALUE rb_finished(VALUE self){
478
533
  VALUE buffer = rb_iv_get(self, "@buffer");
479
534
  if(buffer == Qnil){
@@ -0,0 +1,57 @@
1
+ module TinyBits
2
+
3
+ class DPacker
4
+
5
+ # creates a new TinyBits::DPacker object
6
+ # @param dict [[String]] an array of strings to use as a dictiontary (order is significant).
7
+ # @return [TinyBits::DPacker] the dpacker object.
8
+ # @raise [RuntimeError] the dict is nil or if any member doesn't respnd to to_s.
9
+ def initialize(dict)
10
+ @dict = dict.collect{|a| a.to_s }
11
+ @dict_set = false
12
+ @packer = Packer.new
13
+ end
14
+
15
+ def reset
16
+ @packer.reset
17
+ @dict_set = false
18
+ self
19
+ end
20
+
21
+ # packs an object to a binary string using the stored dictionary
22
+ # @param obj [Object] The Ruby object to pack.
23
+ # @return [String] The packed binary string (frozen).
24
+ # @raise [RuntimeError] If packing fails due to unsupported types or other errors.
25
+ def pack(object)
26
+ reset
27
+ self << object
28
+ to_s
29
+ end
30
+
31
+ # push an object to an inceremental buffer
32
+ # @param obj [Object] The Ruby object to pack.
33
+ # @return [Integer] The number of bytes written to the buffer.
34
+ # @raise [RuntimeError] If packing fails due to unsupported types or other errors.
35
+ def <<(object)
36
+ if !@dict_set
37
+ #@packer.reset
38
+ @dict_size = @packer << @dict
39
+ @dict_set = true
40
+ end
41
+ res = @packer << object
42
+ res
43
+ end
44
+
45
+ # return the packed buffer
46
+ # @return [String] The packed buffer (minus the dictionary).
47
+ def to_s
48
+ res = @packer.to_s
49
+ if @dict_set
50
+ res[@dict_size, res.bytesize - @dict_size]
51
+ else
52
+ res
53
+ end
54
+ end
55
+
56
+ end
57
+ end
@@ -0,0 +1,35 @@
1
+ module TinyBits
2
+
3
+ class DUnpacker
4
+
5
+ def initialize(dict)
6
+ @dict = Packer.new.pack(dict.collect{|a| a.to_s })
7
+ @unpacker = Unpacker.new
8
+ @dict_popped = false
9
+ end
10
+
11
+ def unpack(buffer)
12
+ self.buffer = buffer
13
+ pop
14
+ end
15
+
16
+ def buffer=(buffer)
17
+ @unpacker.buffer = "#{@dict}#{buffer}"
18
+ @dict_popped = false
19
+ end
20
+
21
+ def pop
22
+ if !@dict_popped
23
+ @unpacker.pop
24
+ @dict_popped = true
25
+ end
26
+ @unpacker.pop
27
+ end
28
+
29
+ def finished?
30
+ @unpacker.finished?
31
+ end
32
+
33
+ end
34
+
35
+ end
@@ -1,3 +1,3 @@
1
1
  module TinyBits
2
- VERSION = '0.4.0'.freeze
2
+ VERSION = '0.6.0'.freeze
3
3
  end
data/lib/tinybits.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  require_relative './tinybits/version'
2
2
  require 'tinybits_ext'
3
+ require_relative './tinybits/dpacker'
4
+ require_relative './tinybits/dunpacker'
3
5
 
4
6
  module TinyBits
5
7
  # packs an object to a binary string
@@ -8,7 +10,13 @@ module TinyBits
8
10
  # @raise [RuntimeError] If packing fails due to unsupported types or other errors.
9
11
  # this is a convinience interface, a better way is to instantiate a TinyBits::Packer
10
12
  # object and use its #pack method
11
- def self.pack(object) = Packer.new.pack(object)
13
+ def self.pack(object, dict = nil)
14
+ if dict
15
+ DPacker.new(dict).pack(object)
16
+ else
17
+ Packer.new.pack(object)
18
+ end
19
+ end
12
20
 
13
21
  # unpacks an object from a binary string
14
22
  # @param buffer [String] The Ruby string holding the packed buffer.
@@ -16,5 +24,11 @@ module TinyBits
16
24
  # @raise [RuntimeError] If unpacking fails due to unsupported types or malformed data.
17
25
  # this is a convinience interface, a better way is to instantiate a TinyBits::Unpacker
18
26
  # object and use its #unpack method
19
- def self.unpack(buffer) = Unpacker.new.unpack(buffer)
27
+ def self.unpack(buffer, dict = nil)
28
+ if dict
29
+ DUnpacker.new(dict).unpack(buffer)
30
+ else
31
+ Unpacker.new.unpack(buffer)
32
+ end
33
+ end
20
34
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tinybits
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mohamed Hassan
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-05-04 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies: []
12
12
  description: TinyBits is a Ruby gem that wraps the TinyBits C serializartion library,
13
13
  offering Rubyists the power of serializion with intger/float compression and string
@@ -19,10 +19,11 @@ extensions:
19
19
  extra_rdoc_files: []
20
20
  files:
21
21
  - ext/tinybits/extconf.rb
22
- - ext/tinybits/test_date.rb
23
22
  - ext/tinybits/tinybits.h
24
23
  - ext/tinybits/tinybits_ext.c
25
24
  - lib/tinybits.rb
25
+ - lib/tinybits/dpacker.rb
26
+ - lib/tinybits/dunpacker.rb
26
27
  - lib/tinybits/version.rb
27
28
  homepage: https://github.com/oldmoe/tinybits-rb
28
29
  licenses:
@@ -43,7 +44,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
43
44
  - !ruby/object:Gem::Version
44
45
  version: '0'
45
46
  requirements: []
46
- rubygems_version: 3.6.0.dev
47
+ rubygems_version: 3.6.9
47
48
  specification_version: 4
48
49
  summary: Very fast and compact serialization for Ruby!
49
50
  test_files: []
@@ -1,62 +0,0 @@
1
- require './tinybits_ext'
2
-
3
- packer = TinyBits::Packer.new
4
- unpacker = TinyBits::Unpacker.new
5
-
6
- t = [Time.now, nil, true, false]
7
-
8
-
9
-
10
- =begin
11
- puts packer.dump(t).bytesize
12
-
13
- puts t
14
- puts t2 = unpacker.unpack(packer.pack(t))
15
- puts t == t2
16
- =end
17
-
18
- class User
19
- def initialize(name:, title:)
20
- @name = name
21
- @title = title
22
- end
23
-
24
- def to_tinybits
25
- {"name" => @name, "title" => @title}
26
- end
27
- end
28
-
29
- objects = [{"abc": 123}, {"abc": [1, 2, "abc"]}, ["xyz", "abc", "xyz", 7.6] ]
30
-
31
- puts "----------------"
32
-
33
- #packer.reset
34
-
35
- objects.each do |obj|
36
- puts packer << obj
37
- end
38
-
39
- buffer = packer.to_s
40
-
41
- puts buffer.bytesize
42
-
43
- unpacker.buffer = buffer
44
-
45
-
46
- while(value = unpacker.pop)
47
- pp value
48
- puts "+++++++++++++++++++++++++"
49
- end
50
-
51
- packer.reset
52
-
53
- user = User.new(name: "Mohamed", title: "Father")
54
-
55
- data = { "user" => user, "tags" => ["user", "Father"] }
56
-
57
- pp user
58
- pp user.to_tinybits
59
- packed = packer.pack(data)
60
- puts packed.bytesize
61
- pp unpacker.unpack(packed)
62
-