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.
@@ -0,0 +1,387 @@
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 V2
10
+ NULL_TAG = 0xC0
11
+ FALSE_TAG = 0xC1
12
+ TRUE_TAG = 0xC2
13
+ F64_TAG = 0xC3
14
+ U8_TAG = 0xC4
15
+ U16_TAG = 0xC5
16
+ U32_TAG = 0xC6
17
+ U64_TAG = 0xC7
18
+ I8_TAG = 0xC8
19
+ I16_TAG = 0xC9
20
+ I32_TAG = 0xCA
21
+ I64_TAG = 0xCB
22
+ BIN8_TAG = 0xCC
23
+ BIN16_TAG = 0xCD
24
+ BIN32_TAG = 0xCE
25
+ STR8_TAG = 0xCF
26
+ STR16_TAG = 0xD0
27
+ STR32_TAG = 0xD1
28
+ ARRAY16_TAG = 0xD2
29
+ ARRAY32_TAG = 0xD3
30
+ MAP16_TAG = 0xD4
31
+ MAP32_TAG = 0xD5
32
+ SHAPE_DEF_TAG = 0xD6
33
+ KEY_REF_TAG = 0xD8
34
+ STR_REF_TAG = 0xD9
35
+
36
+ class EncodeState
37
+ attr_accessor :key_ids, :str_ids, :shape_ids, :next_key_id, :next_str_id, :next_shape_id
38
+
39
+ def initialize
40
+ @key_ids = {}
41
+ @str_ids = {}
42
+ @shape_ids = {}
43
+ @next_key_id = 0
44
+ @next_str_id = 0
45
+ @next_shape_id = 0
46
+ end
47
+ end
48
+
49
+ class DecodeState
50
+ attr_accessor :keys, :strings, :shapes
51
+
52
+ def initialize
53
+ @keys = []
54
+ @strings = []
55
+ @shapes = []
56
+ end
57
+ end
58
+
59
+ module_function
60
+
61
+ def encode_v2(value)
62
+ out = +""
63
+ state = EncodeState.new
64
+ encode_v2_value(value, out, state)
65
+ out
66
+ end
67
+
68
+ def decode_v2(bytes)
69
+ reader = Wire::Reader.new(bytes)
70
+ state = DecodeState.new
71
+ value = decode_v2_value(reader, state)
72
+ raise Errors.invalid_data("trailing bytes in v2 decode") unless reader.eof?
73
+
74
+ value
75
+ end
76
+
77
+ def encode_v2_value(value, out, state)
78
+ case value.kind
79
+ when Model::ValueKind::NULL
80
+ out << NULL_TAG.chr
81
+ when Model::ValueKind::BOOL
82
+ out << (value.bool ? TRUE_TAG : FALSE_TAG).chr
83
+ when Model::ValueKind::I64
84
+ encode_v2_i64(value.i64, out)
85
+ when Model::ValueKind::U64
86
+ encode_v2_u64(value.u64, out)
87
+ when Model::ValueKind::F64
88
+ out << F64_TAG.chr
89
+ Wire.append_f64_le(out, value.f64)
90
+ when Model::ValueKind::STRING
91
+ if state.str_ids.key?(value.str)
92
+ out << STR_REF_TAG.chr
93
+ Wire.encode_varuint(state.str_ids[value.str], out)
94
+ else
95
+ encode_v2_string_literal(value.str, out)
96
+ state.str_ids[value.str] = state.next_str_id
97
+ state.next_str_id += 1
98
+ end
99
+ when Model::ValueKind::BINARY
100
+ encode_v2_binary(value.bin, out)
101
+ when Model::ValueKind::ARRAY
102
+ encode_v2_array(value.arr, out, state)
103
+ when Model::ValueKind::MAP
104
+ encode_v2_map(value.map, out, state)
105
+ else
106
+ raise Errors.invalid_data("unsupported value kind")
107
+ end
108
+ end
109
+
110
+ def encode_v2_array(values, out, state)
111
+ shape_keys = detect_shape_keys(values)
112
+ if shape_keys
113
+ sk = shape_keys.join("\0")
114
+ shape_id = state.shape_ids[sk]
115
+ unless shape_id
116
+ shape_id = state.next_shape_id
117
+ state.next_shape_id += 1
118
+ state.shape_ids[sk] = shape_id
119
+ end
120
+ write_v2_array_header(values.length, out)
121
+ out << SHAPE_DEF_TAG.chr
122
+ Wire.encode_varuint(shape_id, out)
123
+ Wire.encode_varuint(shape_keys.length, out)
124
+ shape_keys.each { |key| encode_v2_key(key, out, state) }
125
+ values.each do |value|
126
+ raise Errors.invalid_data("shape array row must be map") unless value.kind == Model::ValueKind::MAP
127
+
128
+ value.map.each { |field| encode_v2_value(field.value, out, state) }
129
+ end
130
+ return
131
+ end
132
+ write_v2_array_header(values.length, out)
133
+ values.each { |value| encode_v2_value(value, out, state) }
134
+ end
135
+
136
+ def encode_v2_map(entries, out, state)
137
+ write_v2_map_header(entries.length, out)
138
+ entries.each do |entry|
139
+ encode_v2_key(entry.key, out, state)
140
+ encode_v2_value(entry.value, out, state)
141
+ end
142
+ end
143
+
144
+ def encode_v2_key(key, out, state)
145
+ if state.key_ids.key?(key)
146
+ out << KEY_REF_TAG.chr
147
+ Wire.encode_varuint(state.key_ids[key], out)
148
+ return
149
+ end
150
+ encode_v2_string_literal(key, out)
151
+ state.key_ids[key] = state.next_key_id
152
+ state.next_key_id += 1
153
+ end
154
+
155
+ def encode_v2_string_literal(value, out)
156
+ bytes = value.b
157
+ if bytes.bytesize <= 31
158
+ out << (0x80 | bytes.bytesize).chr
159
+ elsif bytes.bytesize <= 0xFF
160
+ out << STR8_TAG.chr << bytes.bytesize.chr
161
+ elsif bytes.bytesize <= 0xFFFF
162
+ out << STR16_TAG.chr << [bytes.bytesize].pack("v")
163
+ else
164
+ out << STR32_TAG.chr << [bytes.bytesize].pack("V")
165
+ end
166
+ out << bytes
167
+ end
168
+
169
+ def encode_v2_binary(value, out)
170
+ if value.bytesize <= 0xFF
171
+ out << BIN8_TAG.chr << value.bytesize.chr
172
+ elsif value.bytesize <= 0xFFFF
173
+ out << BIN16_TAG.chr << [value.bytesize].pack("v")
174
+ else
175
+ out << BIN32_TAG.chr << [value.bytesize].pack("V")
176
+ end
177
+ out << value
178
+ end
179
+
180
+ def encode_v2_u64(value, out)
181
+ if value <= 127
182
+ out << value.chr
183
+ elsif value <= 0xFF
184
+ out << U8_TAG.chr << value.chr
185
+ elsif value <= 0xFFFF
186
+ out << U16_TAG.chr << [value].pack("v")
187
+ elsif value <= 0xFFFFFFFF
188
+ out << U32_TAG.chr << [value].pack("V")
189
+ else
190
+ out << U64_TAG.chr
191
+ Wire.append_u64_le(out, value)
192
+ end
193
+ end
194
+
195
+ def encode_v2_i64(value, out)
196
+ if value >= -32 && value <= -1
197
+ out << (value & 0xFF).chr
198
+ elsif value >= 0 && value <= 127
199
+ out << value.chr
200
+ elsif value >= -128 && value <= 127
201
+ out << I8_TAG.chr << [value].pack("c")
202
+ elsif value >= -32_768 && value <= 32_767
203
+ out << I16_TAG.chr << [value].pack("s<")
204
+ elsif value >= -2_147_483_648 && value <= 2_147_483_647
205
+ out << I32_TAG.chr << [value].pack("l<")
206
+ else
207
+ out << I64_TAG.chr
208
+ Wire.append_u64_le(out, value & 0xFFFFFFFFFFFFFFFF)
209
+ end
210
+ end
211
+
212
+ def write_v2_array_header(length, out)
213
+ if length <= 15
214
+ out << (0xA0 | length).chr
215
+ elsif length <= 0xFFFF
216
+ out << ARRAY16_TAG.chr << [length].pack("v")
217
+ else
218
+ out << ARRAY32_TAG.chr << [length].pack("V")
219
+ end
220
+ end
221
+
222
+ def write_v2_map_header(length, out)
223
+ if length <= 15
224
+ out << (0xB0 | length).chr
225
+ elsif length <= 0xFFFF
226
+ out << MAP16_TAG.chr << [length].pack("v")
227
+ else
228
+ out << MAP32_TAG.chr << [length].pack("V")
229
+ end
230
+ end
231
+
232
+ def detect_shape_keys(values)
233
+ return nil if values.length < 2
234
+ return nil unless values[0].kind == Model::ValueKind::MAP && !values[0].map.empty?
235
+
236
+ keys = values[0].map.map(&:key)
237
+ values[1..].each do |value|
238
+ return nil unless value.kind == Model::ValueKind::MAP && value.map.length == keys.length
239
+
240
+ value.map.each_with_index { |e, i| return nil unless e.key == keys[i] }
241
+ end
242
+ keys
243
+ end
244
+
245
+ def decode_v2_value(reader, state)
246
+ tag = reader.read_u8
247
+ decode_v2_value_from_tag(reader, state, tag)
248
+ end
249
+
250
+ def decode_v2_value_from_tag(reader, state, tag)
251
+ case tag
252
+ when 0..0x7F
253
+ Model.u64_value(tag)
254
+ when 0x80..0x9F
255
+ length = tag & 0x1F
256
+ s = reader.read_exact(length).force_encoding(Encoding::UTF_8)
257
+ state.strings << s
258
+ Model.string_value(s)
259
+ when 0xA0..0xAF
260
+ decode_v2_array_body(reader, state, tag & 0x0F)
261
+ when 0xB0..0xBF
262
+ decode_v2_map_body(reader, state, tag & 0x0F)
263
+ when 0xE0..0xFF
264
+ Model.i64_value(tag - 256)
265
+ when NULL_TAG
266
+ Model.null_value
267
+ when FALSE_TAG
268
+ Model.bool_value(false)
269
+ when TRUE_TAG
270
+ Model.bool_value(true)
271
+ when F64_TAG
272
+ Model.f64_value(reader.read_f64_le)
273
+ when U8_TAG
274
+ Model.u64_value(reader.read_u8)
275
+ when U16_TAG
276
+ Model.u64_value(reader.read_exact(2).unpack1("v"))
277
+ when U32_TAG
278
+ Model.u64_value(reader.read_exact(4).unpack1("V"))
279
+ when U64_TAG
280
+ Model.u64_value(reader.read_u64_le)
281
+ when I8_TAG
282
+ Model.i64_value(reader.read_exact(1).unpack1("c"))
283
+ when I16_TAG
284
+ Model.i64_value(reader.read_exact(2).unpack1("s<"))
285
+ when I32_TAG
286
+ Model.i64_value(reader.read_exact(4).unpack1("l<"))
287
+ when I64_TAG
288
+ Model.i64_value(reader.read_exact(8).unpack1("q<"))
289
+ when BIN8_TAG
290
+ Model.binary_value(reader.read_exact(reader.read_u8))
291
+ when BIN16_TAG
292
+ Model.binary_value(reader.read_exact(reader.read_exact(2).unpack1("v")))
293
+ when BIN32_TAG
294
+ Model.binary_value(reader.read_exact(reader.read_exact(4).unpack1("V")))
295
+ when STR8_TAG, STR16_TAG, STR32_TAG
296
+ decode_v2_string_tag(reader, state, tag)
297
+ when ARRAY16_TAG
298
+ decode_v2_array_body(reader, state, reader.read_exact(2).unpack1("v"))
299
+ when ARRAY32_TAG
300
+ decode_v2_array_body(reader, state, reader.read_exact(4).unpack1("V"))
301
+ when MAP16_TAG
302
+ decode_v2_map_body(reader, state, reader.read_exact(2).unpack1("v"))
303
+ when MAP32_TAG
304
+ decode_v2_map_body(reader, state, reader.read_exact(4).unpack1("V"))
305
+ when STR_REF_TAG
306
+ id = reader.read_varuint
307
+ raise Errors.invalid_data("unknown str_ref id") if id >= state.strings.length
308
+
309
+ Model.string_value(state.strings[id])
310
+ else
311
+ raise Errors.invalid_tag(tag)
312
+ end
313
+ end
314
+
315
+ def decode_v2_string_tag(reader, state, tag)
316
+ length = case tag
317
+ when STR8_TAG then reader.read_u8
318
+ when STR16_TAG then reader.read_exact(2).unpack1("v")
319
+ when STR32_TAG then reader.read_exact(4).unpack1("V")
320
+ else raise Errors.invalid_data("invalid string tag")
321
+ end
322
+ s = reader.read_exact(length).force_encoding(Encoding::UTF_8)
323
+ state.strings << s
324
+ Model.string_value(s)
325
+ end
326
+
327
+ def decode_v2_array_body(reader, state, length)
328
+ return Model.array_value([]) if length.zero?
329
+
330
+ first_tag = reader.read_u8
331
+ if first_tag == SHAPE_DEF_TAG
332
+ shape_id = reader.read_varuint
333
+ key_count = reader.read_varuint
334
+ keys = Array.new(key_count) { decode_v2_key(reader, state) }
335
+ while state.shapes.length <= shape_id
336
+ state.shapes << nil
337
+ end
338
+ state.shapes[shape_id] = keys
339
+ values = Array.new(length) do
340
+ row = keys.map do |key|
341
+ val = decode_v2_value(reader, state)
342
+ Model.entry(key, val)
343
+ end
344
+ Model.map_value(row)
345
+ end
346
+ return Model.array_value(values)
347
+ end
348
+ values = Array.new(length)
349
+ values[0] = decode_v2_value_from_tag(reader, state, first_tag)
350
+ (1...length).each { |i| values[i] = decode_v2_value(reader, state) }
351
+ Model.array_value(values)
352
+ end
353
+
354
+ def decode_v2_map_body(reader, state, length)
355
+ entries = Array.new(length) do
356
+ key = decode_v2_key(reader, state)
357
+ value = decode_v2_value(reader, state)
358
+ Model.entry(key, value)
359
+ end
360
+ Model.map_value(entries)
361
+ end
362
+
363
+ def decode_v2_key(reader, state)
364
+ tag = reader.read_u8
365
+ if tag == KEY_REF_TAG
366
+ id = reader.read_varuint
367
+ raise Errors.invalid_data("unknown key_ref id") if id >= state.keys.length
368
+
369
+ return state.keys[id]
370
+ end
371
+ if (0x80..0x9F).cover?(tag)
372
+ key = reader.read_exact(tag & 0x1F).force_encoding(Encoding::UTF_8)
373
+ state.keys << key
374
+ return key
375
+ end
376
+ if [STR8_TAG, STR16_TAG, STR32_TAG].include?(tag)
377
+ v = decode_v2_value_from_tag(reader, state, tag)
378
+ raise Errors.invalid_data("expected string key") unless v.kind == Model::ValueKind::STRING
379
+
380
+ state.keys << v.str
381
+ return v.str
382
+ end
383
+ raise Errors.invalid_data("map key must be key_ref or string")
384
+ end
385
+ end
386
+ end
387
+ end
@@ -0,0 +1,158 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "twilic/core/errors"
4
+
5
+ module Twilic
6
+ module Core
7
+ module Wire
8
+ module_function
9
+
10
+ def encode_varuint(value, out)
11
+ if value < 0x80
12
+ out << value.chr
13
+ return
14
+ end
15
+ loop do
16
+ b = value & 0x7F
17
+ value >>= 7
18
+ b |= 0x80 unless value.zero?
19
+ out << b.chr
20
+ break if value.zero?
21
+ end
22
+ end
23
+
24
+ def encode_zigzag(value)
25
+ ((value << 1) ^ (value >> 63)) & 0xFFFFFFFFFFFFFFFF
26
+ end
27
+
28
+ def decode_zigzag(value)
29
+ ((value >> 1) ^ (-(value & 1))) & 0xFFFFFFFFFFFFFFFF
30
+ v = (value >> 1) ^ (-(value & 1))
31
+ v >= 0x8000000000000000 ? v - 0x10000000000000000 : v
32
+ end
33
+
34
+ def encode_bytes(bytes, out)
35
+ encode_varuint(bytes.bytesize, out)
36
+ out << bytes
37
+ end
38
+
39
+ def encode_string(value, out)
40
+ encode_bytes(value.b, out)
41
+ end
42
+
43
+ def encode_bitmap(bits, out)
44
+ encode_varuint(bits.length, out)
45
+ current = 0
46
+ bits.each_with_index do |bit, i|
47
+ current |= (1 << (i % 8)) if bit
48
+ if (i % 8) == 7
49
+ out << current.chr
50
+ current = 0
51
+ end
52
+ end
53
+ out << current.chr unless bits.empty? || (bits.length % 8).zero?
54
+ end
55
+
56
+ class Reader
57
+ attr_reader :offset
58
+
59
+ def initialize(input)
60
+ @input = input.b
61
+ @offset = 0
62
+ end
63
+
64
+ def position
65
+ @offset
66
+ end
67
+
68
+ def eof?
69
+ @offset >= @input.bytesize
70
+ end
71
+
72
+ def read_u8
73
+ raise Errors.unexpected_eof if @offset >= @input.bytesize
74
+
75
+ b = @input.getbyte(@offset)
76
+ @offset += 1
77
+ b
78
+ end
79
+
80
+ def read_exact(n)
81
+ raise Errors.unexpected_eof if @offset + n > @input.bytesize
82
+
83
+ slice = @input.byteslice(@offset, n)
84
+ @offset += n
85
+ slice
86
+ end
87
+
88
+ def read_varuint
89
+ shift = 0
90
+ result = 0
91
+ loop do
92
+ raise Errors.invalid_data("varuint too large") if shift >= 64
93
+
94
+ b = read_u8
95
+ result |= (b & 0x7F) << shift
96
+ return result if (b & 0x80).zero?
97
+
98
+ shift += 7
99
+ end
100
+ end
101
+
102
+ def read_i64_zigzag
103
+ encoded = read_varuint
104
+ Wire.decode_zigzag(encoded)
105
+ end
106
+
107
+ def read_bytes
108
+ n = read_varuint
109
+ read_exact(n)
110
+ end
111
+
112
+ def read_string
113
+ n = read_varuint
114
+ bytes = read_exact(n)
115
+ raise Errors.utf8_error unless bytes.valid_encoding? && bytes.force_encoding(Encoding::UTF_8).valid_encoding?
116
+
117
+ bytes.force_encoding(Encoding::UTF_8)
118
+ end
119
+
120
+ def read_bitmap
121
+ bit_count = read_varuint
122
+ byte_count = (bit_count + 7) / 8
123
+ bytes = read_exact(byte_count)
124
+ bits = Array.new(bit_count)
125
+ bit_count.times do |i|
126
+ bits[i] = ((bytes.getbyte(i / 8) >> (i % 8)) & 1) == 1
127
+ end
128
+ bits
129
+ end
130
+
131
+ def read_u64_le
132
+ b = read_exact(8)
133
+ b.unpack1("Q<")
134
+ end
135
+
136
+ def read_f64_le
137
+ [read_u64_le].pack("Q<").unpack1("E")
138
+ end
139
+ end
140
+
141
+ def read_u64_le(reader)
142
+ reader.read_u64_le
143
+ end
144
+
145
+ def read_f64_le(reader)
146
+ reader.read_f64_le
147
+ end
148
+
149
+ def append_u64_le(out, v)
150
+ out << [v].pack("Q<")
151
+ end
152
+
153
+ def append_f64_le(out, v)
154
+ append_u64_le(out, [v].pack("E").unpack1("Q<"))
155
+ end
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Twilic
4
+ VERSION = "3.0.0"
5
+ end
data/lib/twilic.rb ADDED
@@ -0,0 +1,147 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "twilic/version"
4
+ require "twilic/core/errors"
5
+ require "twilic/core/model"
6
+ require "twilic/core/wire"
7
+ require "twilic/core/codec"
8
+ require "twilic/core/session"
9
+ require "twilic/core/dictionary"
10
+ require "twilic/core/v2"
11
+ require "twilic/core/protocol"
12
+ require "twilic/core/api"
13
+
14
+ module Twilic
15
+ # Types
16
+ MessageKind = Core::Model::MessageKind
17
+ ValueKind = Core::Model::ValueKind
18
+ Value = Core::Model::Value
19
+ MapEntry = Core::Model::MapEntry
20
+ MessageMapEntry = Core::Model::MessageMapEntry
21
+ KeyRef = Core::Model::KeyRef
22
+ StringMode = Core::Model::StringMode
23
+ StringValue = Core::Model::StringValue
24
+ ElementType = Core::Model::ElementType
25
+ VectorCodec = Core::Model::VectorCodec
26
+ TypedVectorData = Core::Model::TypedVectorData
27
+ TypedVector = Core::Model::TypedVector
28
+ SchemaField = Core::Model::SchemaField
29
+ Schema = Core::Model::Schema
30
+ NullStrategy = Core::Model::NullStrategy
31
+ Column = Core::Model::Column
32
+ ControlOpcode = Core::Model::ControlOpcode
33
+ ControlMessage = Core::Model::ControlMessage
34
+ RegisterShapeControl = Core::Model::RegisterShapeControl
35
+ PromoteEnumControl = Core::Model::PromoteEnumControl
36
+ PatchOpcode = Core::Model::PatchOpcode
37
+ BaseRef = Core::Model::BaseRef
38
+ PatchOperation = Core::Model::PatchOperation
39
+ ControlStreamCodec = Core::Model::ControlStreamCodec
40
+ Message = Core::Model::Message
41
+ ShapedObjectMessage = Core::Model::ShapedObjectMessage
42
+ SchemaObjectMessage = Core::Model::SchemaObjectMessage
43
+ RowBatchMessage = Core::Model::RowBatchMessage
44
+ ColumnBatchMessage = Core::Model::ColumnBatchMessage
45
+ ExtMessage = Core::Model::ExtMessage
46
+ StatePatchMessage = Core::Model::StatePatchMessage
47
+ TemplateBatchMessage = Core::Model::TemplateBatchMessage
48
+ ControlStreamMessage = Core::Model::ControlStreamMessage
49
+ BaseSnapshotMessage = Core::Model::BaseSnapshotMessage
50
+ TemplateDescriptor = Core::Model::TemplateDescriptor
51
+ TwilicError = Core::Errors::TwilicError
52
+ UnknownReferencePolicy = Core::Session::UnknownReferencePolicy
53
+ DictionaryFallback = Core::Session::DictionaryFallback
54
+ DictionaryProfile = Core::Session::DictionaryProfile
55
+ SessionOptions = Core::Session::SessionOptions
56
+ SessionState = Core::Session::MutableSessionState
57
+ TwilicCodec = Core::Protocol::TwilicCodec
58
+ SessionEncoder = Core::Protocol::SessionEncoder
59
+
60
+ # Error constants
61
+ ERR_UNEXPECTED_EOF = Core::Errors::UNEXPECTED_EOF
62
+ ERR_INVALID_KIND = Core::Errors::INVALID_KIND
63
+ ERR_INVALID_TAG = Core::Errors::INVALID_TAG
64
+ ERR_INVALID_DATA = Core::Errors::INVALID_DATA
65
+ ERR_UTF8 = Core::Errors::UTF8
66
+ ERR_UNKNOWN_REFERENCE = Core::Errors::UNKNOWN_REFERENCE
67
+ ERR_STATELESS_RETRY_REQUIRED = Core::Errors::STATELESS_RETRY_REQUIRED
68
+
69
+ class << self
70
+ def encode(value)
71
+ Core::API.encode(value)
72
+ end
73
+
74
+ def decode(bytes)
75
+ Core::API.decode(bytes)
76
+ end
77
+
78
+ def encode_with_schema(schema, value)
79
+ Core::API.encode_with_schema(schema, value)
80
+ end
81
+
82
+ def encode_batch(values)
83
+ Core::API.encode_batch(values)
84
+ end
85
+
86
+ def null
87
+ Core::Model.null_value
88
+ end
89
+
90
+ def bool(b)
91
+ Core::Model.bool_value(b)
92
+ end
93
+
94
+ def i64(n)
95
+ Core::Model.i64_value(n)
96
+ end
97
+
98
+ def u64(n)
99
+ Core::Model.u64_value(n)
100
+ end
101
+
102
+ def f64(n)
103
+ Core::Model.f64_value(n)
104
+ end
105
+
106
+ def string(s)
107
+ Core::Model.string_value(s)
108
+ end
109
+
110
+ def binary(b)
111
+ Core::Model.binary_value(b)
112
+ end
113
+
114
+ def array(items)
115
+ Core::Model.array_value(items)
116
+ end
117
+
118
+ def map(**kwargs)
119
+ Core::Model.map_value(kwargs)
120
+ end
121
+
122
+ def equal(a, b)
123
+ Core::Model.equal(a, b)
124
+ end
125
+
126
+ def default_session_options
127
+ Core::Session::SessionOptions.default
128
+ end
129
+
130
+ def new_twilic_codec
131
+ TwilicCodec.new
132
+ end
133
+
134
+ def twilic_codec_with_options(options)
135
+ TwilicCodec.new(options)
136
+ end
137
+
138
+ def new_session_encoder(options = default_session_options)
139
+ SessionEncoder.new(options)
140
+ end
141
+
142
+ def reset_encode_shape_observation(codec, keys)
143
+ key = codec.shape_key(keys)
144
+ codec.state.encode_shape_observations.delete(key)
145
+ end
146
+ end
147
+ end
data/package.json ADDED
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "twilic-ruby",
3
+ "private": true,
4
+ "packageManager": "pnpm@10.33.2",
5
+ "scripts": {
6
+ "format": "prettier --write \"{README.md,docs/**/*.md,.github/**/*.md}\"",
7
+ "format:check": "prettier --check \"{README.md,docs/**/*.md,.github/**/*.md}\"",
8
+ "lint": "markdownlint-cli2 \"{README.md,docs/**/*.md}\""
9
+ },
10
+ "devDependencies": {
11
+ "markdownlint-cli2": "^0.22.1",
12
+ "prettier": "^3.8.3"
13
+ }
14
+ }