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 +4 -4
- data/ext/tinybits/tinybits.h +122 -25
- data/ext/tinybits/tinybits_ext.c +83 -28
- data/lib/tinybits/dpacker.rb +57 -0
- data/lib/tinybits/dunpacker.rb +35 -0
- data/lib/tinybits/version.rb +1 -1
- data/lib/tinybits.rb +16 -2
- metadata +5 -4
- data/ext/tinybits/test_date.rb +0 -62
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9ce29d814129fc925832887a9a4d83efd6dff20a614aab92e2dc80d10197d5aa
|
4
|
+
data.tar.gz: 137f8181f9d0f4c4e3d6d5abe3cb3c0014de07149e08a6e1f4d6c134cbc5c003
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ea777d8a1c1230512e9865e6cababb992f45d70b6c1c8d03d65567c82bda4a514ea1759341decf4c2b5da6934c23d75bc4f01ca7682a32136557382ba368d256
|
7
|
+
data.tar.gz: f16d288249b7cdb434e23d80823a5c5816a7b3be3989f0d0c0ce2513c7255c7f3e1df06b48f1a57681d4ccb3678328f8aa207fcdd5cbe2289cf7372f647aa52a
|
data/ext/tinybits/tinybits.h
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
/**
|
2
2
|
* TinyBits Amalgamated Header
|
3
|
-
* Generated on:
|
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
|
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
|
-
|
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
|
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
|
-
|
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
|
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
|
-
|
977
|
-
|
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
|
-
|
988
|
-
|
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
|
-
|
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
|
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
|
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
|
1125
|
+
decoder->current_pos += len;
|
1041
1126
|
} else if (tag == 0x5F) { // Large string
|
1042
|
-
|
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
|
1134
|
+
decoder->current_pos += (read + len);
|
1046
1135
|
} else { // Deduplicated (small: < 0x7F, large: 0x7F)
|
1047
|
-
|
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;
|
data/ext/tinybits/tinybits_ext.c
CHANGED
@@ -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
|
-
|
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
|
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
|
-
|
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,
|
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
|
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
|
-
|
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
|
data/lib/tinybits/version.rb
CHANGED
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
|
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
|
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
|
+
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:
|
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.
|
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: []
|
data/ext/tinybits/test_date.rb
DELETED
@@ -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
|
-
|