twilic 3.0.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 +7 -0
- data/.editorconfig +18 -0
- data/.gitattributes +1 -0
- data/.gitignore +9 -0
- data/.markdownlint.jsonc +22 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +53 -0
- data/LICENSE +21 -0
- data/README.md +119 -0
- data/Rakefile +12 -0
- data/docs/CHANGELOG.md +31 -0
- data/docs/CONTRIBUTING.md +51 -0
- data/docs/SPEC-TEST-TRACEABILITY.md +87 -0
- data/lib/twilic/core/api.rb +30 -0
- data/lib/twilic/core/codec.rb +766 -0
- data/lib/twilic/core/dictionary.rb +236 -0
- data/lib/twilic/core/errors.rb +87 -0
- data/lib/twilic/core/interop_fixtures.rb +340 -0
- data/lib/twilic/core/model.rb +506 -0
- data/lib/twilic/core/protocol.rb +2044 -0
- data/lib/twilic/core/protocol_helpers.rb +512 -0
- data/lib/twilic/core/session.rb +461 -0
- data/lib/twilic/core/v2.rb +387 -0
- data/lib/twilic/core/wire.rb +158 -0
- data/lib/twilic/version.rb +5 -0
- data/lib/twilic.rb +147 -0
- data/package.json +14 -0
- data/pnpm-lock.yaml +723 -0
- data/twilic.gemspec +32 -0
- metadata +118 -0
|
@@ -0,0 +1,766 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "twilic/core/errors"
|
|
4
|
+
require "twilic/core/model"
|
|
5
|
+
require "twilic/core/wire"
|
|
6
|
+
|
|
7
|
+
module Twilic
|
|
8
|
+
module Core
|
|
9
|
+
module Codec
|
|
10
|
+
MAX_U64 = 0xFFFFFFFFFFFFFFFF
|
|
11
|
+
MAX_I64 = 0x7FFFFFFFFFFFFFFF
|
|
12
|
+
MIN_I64 = -0x8000000000000000
|
|
13
|
+
|
|
14
|
+
SIMPLE8B_SLOTS = [
|
|
15
|
+
{ count: 60, width: 1 },
|
|
16
|
+
{ count: 30, width: 2 },
|
|
17
|
+
{ count: 20, width: 3 },
|
|
18
|
+
{ count: 15, width: 4 },
|
|
19
|
+
{ count: 12, width: 5 },
|
|
20
|
+
{ count: 10, width: 6 },
|
|
21
|
+
{ count: 8, width: 7 },
|
|
22
|
+
{ count: 7, width: 8 },
|
|
23
|
+
{ count: 6, width: 10 },
|
|
24
|
+
{ count: 5, width: 12 },
|
|
25
|
+
{ count: 4, width: 15 },
|
|
26
|
+
{ count: 3, width: 20 },
|
|
27
|
+
{ count: 2, width: 30 },
|
|
28
|
+
{ count: 1, width: 60 }
|
|
29
|
+
].freeze
|
|
30
|
+
|
|
31
|
+
module_function
|
|
32
|
+
|
|
33
|
+
def encode_i64_vector(values, codec, out)
|
|
34
|
+
case codec
|
|
35
|
+
when Model::VectorCodec::RLE
|
|
36
|
+
encode_i64_rle(values, out)
|
|
37
|
+
when Model::VectorCodec::DIRECT_BITPACK
|
|
38
|
+
encode_i64_direct_bitpack(values, out)
|
|
39
|
+
when Model::VectorCodec::DELTA_BITPACK
|
|
40
|
+
deltas = delta(values)
|
|
41
|
+
encode_i64_direct_bitpack(deltas, out)
|
|
42
|
+
when Model::VectorCodec::FOR_BITPACK
|
|
43
|
+
if values.empty?
|
|
44
|
+
Wire.encode_varuint(0, out)
|
|
45
|
+
return
|
|
46
|
+
end
|
|
47
|
+
min_value = values[0]
|
|
48
|
+
values[1..].each do |v|
|
|
49
|
+
min_value = v if v < min_value
|
|
50
|
+
end
|
|
51
|
+
Wire.encode_varuint(Wire.encode_zigzag(min_value), out)
|
|
52
|
+
shifted = values.map { |v| v - min_value }
|
|
53
|
+
encode_i64_direct_bitpack(shifted, out)
|
|
54
|
+
when Model::VectorCodec::DELTA_FOR_BITPACK
|
|
55
|
+
deltas = delta(values)
|
|
56
|
+
if deltas.empty?
|
|
57
|
+
Wire.encode_varuint(0, out)
|
|
58
|
+
return
|
|
59
|
+
end
|
|
60
|
+
min_value = deltas[0]
|
|
61
|
+
deltas[1..].each do |v|
|
|
62
|
+
min_value = v if v < min_value
|
|
63
|
+
end
|
|
64
|
+
Wire.encode_varuint(Wire.encode_zigzag(min_value), out)
|
|
65
|
+
shifted = deltas.map { |v| v - min_value }
|
|
66
|
+
encode_i64_direct_bitpack(shifted, out)
|
|
67
|
+
when Model::VectorCodec::DELTA_DELTA_BITPACK
|
|
68
|
+
encode_i64_delta_delta(values, out)
|
|
69
|
+
when Model::VectorCodec::PATCHED_FOR
|
|
70
|
+
encode_i64_patched_for(values, out)
|
|
71
|
+
when Model::VectorCodec::SIMPLE8B
|
|
72
|
+
encode_i64_simple8b(values, out)
|
|
73
|
+
when Model::VectorCodec::PLAIN, Model::VectorCodec::DICTIONARY, Model::VectorCodec::STRING_REF,
|
|
74
|
+
Model::VectorCodec::PREFIX_DELTA, Model::VectorCodec::XOR_FLOAT
|
|
75
|
+
encode_i64_plain(values, out)
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def decode_i64_vector(reader, codec)
|
|
80
|
+
case codec
|
|
81
|
+
when Model::VectorCodec::RLE
|
|
82
|
+
decode_i64_rle(reader)
|
|
83
|
+
when Model::VectorCodec::DIRECT_BITPACK
|
|
84
|
+
decode_i64_direct_bitpack(reader)
|
|
85
|
+
when Model::VectorCodec::DELTA_BITPACK
|
|
86
|
+
values = decode_i64_direct_bitpack(reader)
|
|
87
|
+
undelta(values)
|
|
88
|
+
when Model::VectorCodec::FOR_BITPACK
|
|
89
|
+
encoded_min = reader.read_varuint
|
|
90
|
+
min_value = Wire.decode_zigzag(encoded_min)
|
|
91
|
+
return [] if reader.eof?
|
|
92
|
+
|
|
93
|
+
shifted = decode_i64_direct_bitpack(reader)
|
|
94
|
+
shifted.map { |v| v + min_value }
|
|
95
|
+
when Model::VectorCodec::DELTA_FOR_BITPACK
|
|
96
|
+
encoded_min = reader.read_varuint
|
|
97
|
+
min_value = Wire.decode_zigzag(encoded_min)
|
|
98
|
+
return [] if reader.eof?
|
|
99
|
+
|
|
100
|
+
shifted = decode_i64_direct_bitpack(reader)
|
|
101
|
+
deltas = shifted.map { |v| v + min_value }
|
|
102
|
+
undelta(deltas)
|
|
103
|
+
when Model::VectorCodec::DELTA_DELTA_BITPACK
|
|
104
|
+
decode_i64_delta_delta(reader)
|
|
105
|
+
when Model::VectorCodec::PATCHED_FOR
|
|
106
|
+
decode_i64_patched_for(reader)
|
|
107
|
+
when Model::VectorCodec::SIMPLE8B
|
|
108
|
+
decode_i64_simple8b(reader)
|
|
109
|
+
when Model::VectorCodec::PLAIN, Model::VectorCodec::DICTIONARY, Model::VectorCodec::STRING_REF,
|
|
110
|
+
Model::VectorCodec::PREFIX_DELTA, Model::VectorCodec::XOR_FLOAT
|
|
111
|
+
decode_i64_plain(reader)
|
|
112
|
+
else
|
|
113
|
+
raise Errors.invalid_data("unsupported vector codec")
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def encode_u64_vector(values, codec, out)
|
|
118
|
+
case codec
|
|
119
|
+
when Model::VectorCodec::RLE
|
|
120
|
+
encode_u64_rle(values, out)
|
|
121
|
+
when Model::VectorCodec::DIRECT_BITPACK
|
|
122
|
+
encode_u64_direct_bitpack(values, out)
|
|
123
|
+
when Model::VectorCodec::FOR_BITPACK
|
|
124
|
+
if values.empty?
|
|
125
|
+
Wire.encode_varuint(0, out)
|
|
126
|
+
return
|
|
127
|
+
end
|
|
128
|
+
min_value = values[0]
|
|
129
|
+
values[1..].each do |v|
|
|
130
|
+
min_value = v if v < min_value
|
|
131
|
+
end
|
|
132
|
+
Wire.encode_varuint(min_value, out)
|
|
133
|
+
shifted = values.map { |v| v - min_value }
|
|
134
|
+
encode_u64_direct_bitpack(shifted, out)
|
|
135
|
+
when Model::VectorCodec::PLAIN
|
|
136
|
+
encode_u64_plain(values, out)
|
|
137
|
+
when Model::VectorCodec::SIMPLE8B
|
|
138
|
+
encode_u64_simple8b(values, out)
|
|
139
|
+
when Model::VectorCodec::DICTIONARY, Model::VectorCodec::STRING_REF, Model::VectorCodec::PREFIX_DELTA,
|
|
140
|
+
Model::VectorCodec::XOR_FLOAT, Model::VectorCodec::DELTA_BITPACK, Model::VectorCodec::DELTA_FOR_BITPACK,
|
|
141
|
+
Model::VectorCodec::DELTA_DELTA_BITPACK, Model::VectorCodec::PATCHED_FOR
|
|
142
|
+
encode_u64_plain(values, out)
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def decode_u64_vector(reader, codec)
|
|
147
|
+
case codec
|
|
148
|
+
when Model::VectorCodec::RLE
|
|
149
|
+
decode_u64_rle(reader)
|
|
150
|
+
when Model::VectorCodec::DIRECT_BITPACK
|
|
151
|
+
decode_u64_direct_bitpack(reader)
|
|
152
|
+
when Model::VectorCodec::FOR_BITPACK
|
|
153
|
+
min_value = reader.read_varuint
|
|
154
|
+
return [] if reader.eof?
|
|
155
|
+
|
|
156
|
+
shifted = decode_u64_direct_bitpack(reader)
|
|
157
|
+
out = []
|
|
158
|
+
shifted.each do |v|
|
|
159
|
+
sum, ok = checked_add_u64(v, min_value)
|
|
160
|
+
raise Errors.invalid_data("u64 FOR overflow") unless ok
|
|
161
|
+
|
|
162
|
+
out << sum
|
|
163
|
+
end
|
|
164
|
+
out
|
|
165
|
+
when Model::VectorCodec::PLAIN
|
|
166
|
+
decode_u64_plain(reader)
|
|
167
|
+
when Model::VectorCodec::SIMPLE8B
|
|
168
|
+
decode_u64_simple8b(reader)
|
|
169
|
+
when Model::VectorCodec::DICTIONARY, Model::VectorCodec::STRING_REF, Model::VectorCodec::PREFIX_DELTA,
|
|
170
|
+
Model::VectorCodec::XOR_FLOAT, Model::VectorCodec::DELTA_BITPACK, Model::VectorCodec::DELTA_FOR_BITPACK,
|
|
171
|
+
Model::VectorCodec::DELTA_DELTA_BITPACK, Model::VectorCodec::PATCHED_FOR
|
|
172
|
+
decode_u64_plain(reader)
|
|
173
|
+
else
|
|
174
|
+
raise Errors.invalid_data("unsupported vector codec")
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def encode_f64_vector(values, codec, out)
|
|
179
|
+
if codec == Model::VectorCodec::XOR_FLOAT
|
|
180
|
+
encode_xor_float(values, out)
|
|
181
|
+
return
|
|
182
|
+
end
|
|
183
|
+
Wire.encode_varuint(values.length, out)
|
|
184
|
+
values.each { |v| Wire.append_f64_le(out, v) }
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def decode_f64_vector(reader, codec)
|
|
188
|
+
return decode_xor_float(reader) if codec == Model::VectorCodec::XOR_FLOAT
|
|
189
|
+
|
|
190
|
+
length = reader.read_varuint
|
|
191
|
+
out = []
|
|
192
|
+
length.times do
|
|
193
|
+
out << Wire.read_f64_le(reader)
|
|
194
|
+
end
|
|
195
|
+
out
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def encode_u64_plain(values, out)
|
|
199
|
+
Wire.encode_varuint(values.length, out)
|
|
200
|
+
values.each { |value| Wire.encode_varuint(value, out) }
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def decode_u64_plain(reader)
|
|
204
|
+
length = reader.read_varuint
|
|
205
|
+
out = []
|
|
206
|
+
length.times do
|
|
207
|
+
out << reader.read_varuint
|
|
208
|
+
end
|
|
209
|
+
out
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
def encode_u64_rle(values, out)
|
|
213
|
+
runs = []
|
|
214
|
+
values.each do |value|
|
|
215
|
+
if !runs.empty? && runs[-1][:value] == value
|
|
216
|
+
runs[-1][:count] += 1
|
|
217
|
+
else
|
|
218
|
+
runs << { value: value, count: 1 }
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
Wire.encode_varuint(runs.length, out)
|
|
222
|
+
runs.each do |run|
|
|
223
|
+
Wire.encode_varuint(run[:value], out)
|
|
224
|
+
Wire.encode_varuint(run[:count], out)
|
|
225
|
+
end
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
def decode_u64_rle(reader)
|
|
229
|
+
runs_len = reader.read_varuint
|
|
230
|
+
out = []
|
|
231
|
+
runs_len.times do
|
|
232
|
+
value = reader.read_varuint
|
|
233
|
+
count = reader.read_varuint
|
|
234
|
+
count.times { out << value }
|
|
235
|
+
end
|
|
236
|
+
out
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
def encode_u64_direct_bitpack(values, out)
|
|
240
|
+
Wire.encode_varuint(values.length, out)
|
|
241
|
+
if values.empty?
|
|
242
|
+
out << 0.chr
|
|
243
|
+
return
|
|
244
|
+
end
|
|
245
|
+
width = 1
|
|
246
|
+
values.each do |v|
|
|
247
|
+
bw = bit_width(v)
|
|
248
|
+
width = bw if bw > width
|
|
249
|
+
end
|
|
250
|
+
out << width.chr
|
|
251
|
+
pack_u64_values(values, width, out)
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
def decode_u64_direct_bitpack(reader)
|
|
255
|
+
length = reader.read_varuint
|
|
256
|
+
width = reader.read_u8
|
|
257
|
+
return [] if length.zero?
|
|
258
|
+
|
|
259
|
+
raise Errors.invalid_data("bitpack width") if width.zero? || width > 64
|
|
260
|
+
|
|
261
|
+
unpack_u64_values(reader, length, width)
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
def encode_i64_plain(values, out)
|
|
265
|
+
Wire.encode_varuint(values.length, out)
|
|
266
|
+
values.each { |value| Wire.encode_varuint(Wire.encode_zigzag(value), out) }
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
def decode_i64_plain(reader)
|
|
270
|
+
length = reader.read_varuint
|
|
271
|
+
out = []
|
|
272
|
+
length.times do
|
|
273
|
+
v = reader.read_varuint
|
|
274
|
+
out << Wire.decode_zigzag(v)
|
|
275
|
+
end
|
|
276
|
+
out
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
def encode_i64_simple8b(values, out)
|
|
280
|
+
encoded = values.map { |v| Wire.encode_zigzag(v) }
|
|
281
|
+
encode_u64_simple8b_inner(encoded, out)
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
def decode_i64_simple8b(reader)
|
|
285
|
+
encoded = decode_u64_simple8b_inner(reader)
|
|
286
|
+
encoded.map { |v| Wire.decode_zigzag(v) }
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
def encode_u64_simple8b(values, out)
|
|
290
|
+
encode_u64_simple8b_inner(values, out)
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
def decode_u64_simple8b(reader)
|
|
294
|
+
decode_u64_simple8b_inner(reader)
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
def encode_u64_simple8b_inner(values, out)
|
|
298
|
+
Wire.encode_varuint(values.length, out)
|
|
299
|
+
return if values.empty?
|
|
300
|
+
|
|
301
|
+
max_value = 0
|
|
302
|
+
values.each { |v| max_value = v if v > max_value }
|
|
303
|
+
if max_value > ((1 << 60) - 1)
|
|
304
|
+
out << 0.chr
|
|
305
|
+
values.each { |value| Wire.encode_varuint(value, out) }
|
|
306
|
+
return
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
out << 1.chr
|
|
310
|
+
idx = 0
|
|
311
|
+
while idx < values.length
|
|
312
|
+
zero_run = 0
|
|
313
|
+
while idx + zero_run < values.length && values[idx + zero_run].zero? && zero_run < 240
|
|
314
|
+
zero_run += 1
|
|
315
|
+
end
|
|
316
|
+
if zero_run >= 120
|
|
317
|
+
take = zero_run >= 240 ? 240 : 120
|
|
318
|
+
word = (take == 240 ? 0 : (1 << 60))
|
|
319
|
+
Wire.append_u64_le(out, word)
|
|
320
|
+
idx += take
|
|
321
|
+
next
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
packed = false
|
|
325
|
+
SIMPLE8B_SLOTS.each_with_index do |slot, selector_idx|
|
|
326
|
+
next if idx + slot[:count] > values.length
|
|
327
|
+
|
|
328
|
+
max_encodable = (1 << slot[:width]) - 1
|
|
329
|
+
all_fit = true
|
|
330
|
+
values[idx, slot[:count]].each do |value|
|
|
331
|
+
if value > max_encodable
|
|
332
|
+
all_fit = false
|
|
333
|
+
break
|
|
334
|
+
end
|
|
335
|
+
end
|
|
336
|
+
next unless all_fit
|
|
337
|
+
|
|
338
|
+
selector = selector_idx + 2
|
|
339
|
+
payload = 0
|
|
340
|
+
shift = 0
|
|
341
|
+
values[idx, slot[:count]].each do |value|
|
|
342
|
+
payload |= (value << shift)
|
|
343
|
+
shift += slot[:width]
|
|
344
|
+
end
|
|
345
|
+
word = (selector << 60) | payload
|
|
346
|
+
Wire.append_u64_le(out, word & MAX_U64)
|
|
347
|
+
idx += slot[:count]
|
|
348
|
+
packed = true
|
|
349
|
+
break
|
|
350
|
+
end
|
|
351
|
+
next if packed
|
|
352
|
+
|
|
353
|
+
selector = 15
|
|
354
|
+
word = (selector << 60) | (values[idx] & ((1 << 60) - 1))
|
|
355
|
+
Wire.append_u64_le(out, word & MAX_U64)
|
|
356
|
+
idx += 1
|
|
357
|
+
end
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
def decode_u64_simple8b_inner(reader)
|
|
361
|
+
length = reader.read_varuint
|
|
362
|
+
return [] if length.zero?
|
|
363
|
+
|
|
364
|
+
mode = reader.read_u8
|
|
365
|
+
if mode.zero?
|
|
366
|
+
out = []
|
|
367
|
+
length.times do
|
|
368
|
+
out << reader.read_varuint
|
|
369
|
+
end
|
|
370
|
+
return out
|
|
371
|
+
end
|
|
372
|
+
raise Errors.invalid_data("simple8b mode") unless mode == 1
|
|
373
|
+
|
|
374
|
+
out = []
|
|
375
|
+
while out.length < length
|
|
376
|
+
packed = Wire.read_u64_le(reader)
|
|
377
|
+
selector = packed >> 60
|
|
378
|
+
payload = packed & ((1 << 60) - 1)
|
|
379
|
+
if selector == 0 || selector == 1
|
|
380
|
+
count = selector == 1 ? 120 : 240
|
|
381
|
+
remain = length - out.length
|
|
382
|
+
limit = remain < count ? remain : count
|
|
383
|
+
limit.times { out << 0 }
|
|
384
|
+
elsif selector >= 2 && selector <= 15
|
|
385
|
+
if selector == 15
|
|
386
|
+
count = 1
|
|
387
|
+
width = 60
|
|
388
|
+
else
|
|
389
|
+
slot = SIMPLE8B_SLOTS[selector - 2]
|
|
390
|
+
count = slot[:count]
|
|
391
|
+
width = slot[:width]
|
|
392
|
+
end
|
|
393
|
+
mask = (1 << width) - 1
|
|
394
|
+
shift = 0
|
|
395
|
+
remain = length - out.length
|
|
396
|
+
limit = remain < count ? remain : count
|
|
397
|
+
limit.times do
|
|
398
|
+
out << ((payload >> shift) & mask)
|
|
399
|
+
shift += width
|
|
400
|
+
end
|
|
401
|
+
else
|
|
402
|
+
raise Errors.invalid_data("simple8b selector")
|
|
403
|
+
end
|
|
404
|
+
end
|
|
405
|
+
out
|
|
406
|
+
end
|
|
407
|
+
|
|
408
|
+
def delta(values)
|
|
409
|
+
out = []
|
|
410
|
+
prev = 0
|
|
411
|
+
values.each_with_index do |value, i|
|
|
412
|
+
out << (i.zero? ? value : (value - prev))
|
|
413
|
+
prev = value
|
|
414
|
+
end
|
|
415
|
+
out
|
|
416
|
+
end
|
|
417
|
+
|
|
418
|
+
def undelta(values)
|
|
419
|
+
out = []
|
|
420
|
+
prev = 0
|
|
421
|
+
values.each_with_index do |value, i|
|
|
422
|
+
if i.zero?
|
|
423
|
+
out << value
|
|
424
|
+
prev = value
|
|
425
|
+
next
|
|
426
|
+
end
|
|
427
|
+
next_value, ok = checked_add_i64(prev, value)
|
|
428
|
+
raise Errors.invalid_data("delta overflow") unless ok
|
|
429
|
+
|
|
430
|
+
out << next_value
|
|
431
|
+
prev = next_value
|
|
432
|
+
end
|
|
433
|
+
out
|
|
434
|
+
end
|
|
435
|
+
|
|
436
|
+
def encode_i64_rle(values, out)
|
|
437
|
+
runs = []
|
|
438
|
+
values.each do |value|
|
|
439
|
+
if !runs.empty? && runs[-1][:value] == value
|
|
440
|
+
runs[-1][:count] += 1
|
|
441
|
+
else
|
|
442
|
+
runs << { value: value, count: 1 }
|
|
443
|
+
end
|
|
444
|
+
end
|
|
445
|
+
Wire.encode_varuint(runs.length, out)
|
|
446
|
+
runs.each do |run|
|
|
447
|
+
Wire.encode_varuint(Wire.encode_zigzag(run[:value]), out)
|
|
448
|
+
Wire.encode_varuint(run[:count], out)
|
|
449
|
+
end
|
|
450
|
+
end
|
|
451
|
+
|
|
452
|
+
def decode_i64_rle(reader)
|
|
453
|
+
runs_len = reader.read_varuint
|
|
454
|
+
out = []
|
|
455
|
+
runs_len.times do
|
|
456
|
+
value_encoded = reader.read_varuint
|
|
457
|
+
value = Wire.decode_zigzag(value_encoded)
|
|
458
|
+
count = reader.read_varuint
|
|
459
|
+
count.times { out << value }
|
|
460
|
+
end
|
|
461
|
+
out
|
|
462
|
+
end
|
|
463
|
+
|
|
464
|
+
def encode_i64_patched_for(values, out)
|
|
465
|
+
if values.empty?
|
|
466
|
+
Wire.encode_varuint(0, out)
|
|
467
|
+
return
|
|
468
|
+
end
|
|
469
|
+
base = values[0]
|
|
470
|
+
values[1..].each do |v|
|
|
471
|
+
base = v if v < base
|
|
472
|
+
end
|
|
473
|
+
shifted = values.map { |v| v - base }
|
|
474
|
+
Wire.encode_varuint(shifted.length, out)
|
|
475
|
+
Wire.encode_varuint(Wire.encode_zigzag(base), out)
|
|
476
|
+
|
|
477
|
+
max_value = 0
|
|
478
|
+
shifted.each { |value| max_value = value if value > max_value }
|
|
479
|
+
bw = bit_width(max_value & MAX_U64)
|
|
480
|
+
base_width = bw > 2 ? bw - 2 : 0
|
|
481
|
+
out << base_width.chr
|
|
482
|
+
|
|
483
|
+
patch_positions = []
|
|
484
|
+
main_values = []
|
|
485
|
+
shifted.each_with_index do |value, idx|
|
|
486
|
+
if bit_width(value & MAX_U64) > base_width
|
|
487
|
+
patch_positions << { pos: idx, value: value }
|
|
488
|
+
main = 0
|
|
489
|
+
if base_width.positive?
|
|
490
|
+
mask = (1 << base_width) - 1
|
|
491
|
+
main = value & mask
|
|
492
|
+
main = 0 if main.negative?
|
|
493
|
+
end
|
|
494
|
+
main_values << main
|
|
495
|
+
else
|
|
496
|
+
main_values << value
|
|
497
|
+
end
|
|
498
|
+
end
|
|
499
|
+
main_values.each do |value|
|
|
500
|
+
Wire.encode_varuint(value & MAX_U64, out)
|
|
501
|
+
end
|
|
502
|
+
Wire.encode_varuint(patch_positions.length, out)
|
|
503
|
+
patch_positions.each do |patch|
|
|
504
|
+
Wire.encode_varuint(patch[:pos], out)
|
|
505
|
+
Wire.encode_varuint(patch[:value] & MAX_U64, out)
|
|
506
|
+
end
|
|
507
|
+
end
|
|
508
|
+
|
|
509
|
+
def decode_i64_patched_for(reader)
|
|
510
|
+
length = reader.read_varuint
|
|
511
|
+
return [] if length.zero?
|
|
512
|
+
|
|
513
|
+
base_encoded = reader.read_varuint
|
|
514
|
+
base = Wire.decode_zigzag(base_encoded)
|
|
515
|
+
reader.read_u8
|
|
516
|
+
values = []
|
|
517
|
+
length.times do
|
|
518
|
+
v = reader.read_varuint
|
|
519
|
+
values << u64_to_i64(v)
|
|
520
|
+
end
|
|
521
|
+
patch_count = reader.read_varuint
|
|
522
|
+
patch_count.times do
|
|
523
|
+
pos = reader.read_varuint
|
|
524
|
+
patch = reader.read_varuint
|
|
525
|
+
values[pos] = u64_to_i64(patch) if pos < values.length
|
|
526
|
+
end
|
|
527
|
+
values.map { |v| v + base }
|
|
528
|
+
end
|
|
529
|
+
|
|
530
|
+
def encode_xor_float(values, out)
|
|
531
|
+
Wire.encode_varuint(values.length, out)
|
|
532
|
+
return if values.empty?
|
|
533
|
+
|
|
534
|
+
first_bits = f64_to_u64(values[0])
|
|
535
|
+
Wire.append_u64_le(out, first_bits)
|
|
536
|
+
prev = first_bits
|
|
537
|
+
values[1..].each do |value|
|
|
538
|
+
bits_value = f64_to_u64(value)
|
|
539
|
+
x = prev ^ bits_value
|
|
540
|
+
if x.zero?
|
|
541
|
+
out << 0.chr
|
|
542
|
+
else
|
|
543
|
+
out << 1.chr
|
|
544
|
+
leading = leading_zeros64(x)
|
|
545
|
+
trailing = trailing_zeros64(x)
|
|
546
|
+
width = 64 - (leading + trailing)
|
|
547
|
+
Wire.encode_varuint(leading, out)
|
|
548
|
+
Wire.encode_varuint(trailing, out)
|
|
549
|
+
Wire.encode_varuint(width, out)
|
|
550
|
+
payload = if width == 64
|
|
551
|
+
x
|
|
552
|
+
else
|
|
553
|
+
(x >> trailing) & ((1 << width) - 1)
|
|
554
|
+
end
|
|
555
|
+
Wire.encode_varuint(payload, out)
|
|
556
|
+
end
|
|
557
|
+
prev = bits_value
|
|
558
|
+
end
|
|
559
|
+
end
|
|
560
|
+
|
|
561
|
+
def decode_xor_float(reader)
|
|
562
|
+
length = reader.read_varuint
|
|
563
|
+
return [] if length.zero?
|
|
564
|
+
|
|
565
|
+
first_bits = Wire.read_u64_le(reader)
|
|
566
|
+
out = [u64_to_f64(first_bits)]
|
|
567
|
+
prev = first_bits
|
|
568
|
+
(length - 1).times do
|
|
569
|
+
flag = reader.read_u8
|
|
570
|
+
bits_value = prev
|
|
571
|
+
unless flag.zero?
|
|
572
|
+
leading = reader.read_varuint
|
|
573
|
+
trailing = reader.read_varuint
|
|
574
|
+
width = reader.read_varuint
|
|
575
|
+
payload = reader.read_varuint
|
|
576
|
+
raise Errors.invalid_data("xor-float bit widths") if leading + trailing + width > 64
|
|
577
|
+
|
|
578
|
+
x = width == 64 ? payload : (payload << trailing)
|
|
579
|
+
bits_value = prev ^ x
|
|
580
|
+
end
|
|
581
|
+
out << u64_to_f64(bits_value)
|
|
582
|
+
prev = bits_value
|
|
583
|
+
end
|
|
584
|
+
out
|
|
585
|
+
end
|
|
586
|
+
|
|
587
|
+
def encode_i64_direct_bitpack(values, out)
|
|
588
|
+
Wire.encode_varuint(values.length, out)
|
|
589
|
+
if values.empty?
|
|
590
|
+
out << 0.chr
|
|
591
|
+
return
|
|
592
|
+
end
|
|
593
|
+
encoded = []
|
|
594
|
+
width = 1
|
|
595
|
+
values.each do |v|
|
|
596
|
+
enc = Wire.encode_zigzag(v)
|
|
597
|
+
encoded << enc
|
|
598
|
+
bw = bit_width(enc)
|
|
599
|
+
width = bw if bw > width
|
|
600
|
+
end
|
|
601
|
+
out << width.chr
|
|
602
|
+
pack_u64_values(encoded, width, out)
|
|
603
|
+
end
|
|
604
|
+
|
|
605
|
+
def decode_i64_direct_bitpack(reader)
|
|
606
|
+
length = reader.read_varuint
|
|
607
|
+
width = reader.read_u8
|
|
608
|
+
return [] if length.zero?
|
|
609
|
+
|
|
610
|
+
raise Errors.invalid_data("bitpack width") if width.zero? || width > 64
|
|
611
|
+
|
|
612
|
+
encoded = unpack_u64_values(reader, length, width)
|
|
613
|
+
encoded.map { |v| Wire.decode_zigzag(v) }
|
|
614
|
+
end
|
|
615
|
+
|
|
616
|
+
def encode_i64_delta_delta(values, out)
|
|
617
|
+
Wire.encode_varuint(values.length, out)
|
|
618
|
+
return if values.empty?
|
|
619
|
+
|
|
620
|
+
Wire.encode_varuint(Wire.encode_zigzag(values[0]), out)
|
|
621
|
+
return if values.length == 1
|
|
622
|
+
|
|
623
|
+
d1 = values[1] - values[0]
|
|
624
|
+
Wire.encode_varuint(Wire.encode_zigzag(d1), out)
|
|
625
|
+
dd = []
|
|
626
|
+
prev_delta = d1
|
|
627
|
+
(1...(values.length - 1)).each do |i|
|
|
628
|
+
d = values[i + 1] - values[i]
|
|
629
|
+
dd << (d - prev_delta)
|
|
630
|
+
prev_delta = d
|
|
631
|
+
end
|
|
632
|
+
encode_i64_direct_bitpack(dd, out)
|
|
633
|
+
end
|
|
634
|
+
|
|
635
|
+
def decode_i64_delta_delta(reader)
|
|
636
|
+
length = reader.read_varuint
|
|
637
|
+
return [] if length.zero?
|
|
638
|
+
|
|
639
|
+
first_encoded = reader.read_varuint
|
|
640
|
+
first = Wire.decode_zigzag(first_encoded)
|
|
641
|
+
return [first] if length == 1
|
|
642
|
+
|
|
643
|
+
first_delta_encoded = reader.read_varuint
|
|
644
|
+
first_delta = Wire.decode_zigzag(first_delta_encoded)
|
|
645
|
+
dd = decode_i64_direct_bitpack(reader)
|
|
646
|
+
raise Errors.invalid_data("delta-delta length") if dd.length != length - 2
|
|
647
|
+
|
|
648
|
+
out = [first]
|
|
649
|
+
prev = first
|
|
650
|
+
second, ok = checked_add_i64(prev, first_delta)
|
|
651
|
+
raise Errors.invalid_data("delta-delta overflow") unless ok
|
|
652
|
+
|
|
653
|
+
out << second
|
|
654
|
+
prev = second
|
|
655
|
+
prev_delta = first_delta
|
|
656
|
+
dd.each do |ddv|
|
|
657
|
+
d, ok = checked_add_i64(prev_delta, ddv)
|
|
658
|
+
raise Errors.invalid_data("delta-delta overflow") unless ok
|
|
659
|
+
|
|
660
|
+
nxt, ok = checked_add_i64(prev, d)
|
|
661
|
+
raise Errors.invalid_data("delta-delta overflow") unless ok
|
|
662
|
+
|
|
663
|
+
out << nxt
|
|
664
|
+
prev = nxt
|
|
665
|
+
prev_delta = d
|
|
666
|
+
end
|
|
667
|
+
out
|
|
668
|
+
end
|
|
669
|
+
|
|
670
|
+
def pack_u64_values(values, width, out)
|
|
671
|
+
total_bits = values.length * width
|
|
672
|
+
byte_len = (total_bits + 7) / 8
|
|
673
|
+
bytes = Array.new(byte_len, 0)
|
|
674
|
+
bit_pos = 0
|
|
675
|
+
values.each do |value|
|
|
676
|
+
written = 0
|
|
677
|
+
while written < width
|
|
678
|
+
byte_idx = bit_pos / 8
|
|
679
|
+
bit_off = bit_pos % 8
|
|
680
|
+
room = 8 - bit_off
|
|
681
|
+
take = width - written
|
|
682
|
+
take = room if take > room
|
|
683
|
+
mask = (1 << take) - 1
|
|
684
|
+
part = (value >> written) & mask
|
|
685
|
+
bytes[byte_idx] |= (part << bit_off)
|
|
686
|
+
bit_pos += take
|
|
687
|
+
written += take
|
|
688
|
+
end
|
|
689
|
+
end
|
|
690
|
+
out << bytes.pack("C*")
|
|
691
|
+
end
|
|
692
|
+
|
|
693
|
+
def unpack_u64_values(reader, length, width)
|
|
694
|
+
total_bits = length * width
|
|
695
|
+
byte_len = (total_bits + 7) / 8
|
|
696
|
+
bytes = reader.read_exact(byte_len)
|
|
697
|
+
out = []
|
|
698
|
+
bit_pos = 0
|
|
699
|
+
length.times do
|
|
700
|
+
value = 0
|
|
701
|
+
written = 0
|
|
702
|
+
while written < width
|
|
703
|
+
byte_idx = bit_pos / 8
|
|
704
|
+
raise Errors.invalid_data("bitpack underflow") if byte_idx >= bytes.bytesize
|
|
705
|
+
|
|
706
|
+
bit_off = bit_pos % 8
|
|
707
|
+
room = 8 - bit_off
|
|
708
|
+
take = width - written
|
|
709
|
+
take = room if take > room
|
|
710
|
+
mask = (1 << take) - 1
|
|
711
|
+
part = (bytes.getbyte(byte_idx) >> bit_off) & mask
|
|
712
|
+
value |= (part << written)
|
|
713
|
+
bit_pos += take
|
|
714
|
+
written += take
|
|
715
|
+
end
|
|
716
|
+
out << value
|
|
717
|
+
end
|
|
718
|
+
out
|
|
719
|
+
end
|
|
720
|
+
|
|
721
|
+
def bit_width(v)
|
|
722
|
+
v &= MAX_U64
|
|
723
|
+
return 1 if v.zero?
|
|
724
|
+
|
|
725
|
+
64 - leading_zeros64(v)
|
|
726
|
+
end
|
|
727
|
+
|
|
728
|
+
def checked_add_u64(a, b)
|
|
729
|
+
sum = a + b
|
|
730
|
+
[sum & MAX_U64, sum <= MAX_U64]
|
|
731
|
+
end
|
|
732
|
+
|
|
733
|
+
def checked_add_i64(a, b)
|
|
734
|
+
sum = a + b
|
|
735
|
+
return [0, false] if (b.positive? && sum < a) || (b.negative? && sum > a)
|
|
736
|
+
return [0, false] if sum < MIN_I64 || sum > MAX_I64
|
|
737
|
+
|
|
738
|
+
[sum, true]
|
|
739
|
+
end
|
|
740
|
+
|
|
741
|
+
def u64_to_i64(v)
|
|
742
|
+
(v & (1 << 63)).zero? ? v : (v - (1 << 64))
|
|
743
|
+
end
|
|
744
|
+
|
|
745
|
+
def f64_to_u64(value)
|
|
746
|
+
[value].pack("E").unpack1("Q<")
|
|
747
|
+
end
|
|
748
|
+
|
|
749
|
+
def u64_to_f64(bits)
|
|
750
|
+
[bits].pack("Q<").unpack1("E")
|
|
751
|
+
end
|
|
752
|
+
|
|
753
|
+
def leading_zeros64(v)
|
|
754
|
+
return 64 if v.zero?
|
|
755
|
+
|
|
756
|
+
64 - v.bit_length
|
|
757
|
+
end
|
|
758
|
+
|
|
759
|
+
def trailing_zeros64(v)
|
|
760
|
+
return 64 if v.zero?
|
|
761
|
+
|
|
762
|
+
(v & -v).bit_length - 1
|
|
763
|
+
end
|
|
764
|
+
end
|
|
765
|
+
end
|
|
766
|
+
end
|