Module: Twilic::Core::V2
- Defined in:
- lib/twilic/core/v2.rb
Defined Under Namespace
Classes: DecodeState, EncodeState
Constant Summary collapse
- NULL_TAG =
0xC0- FALSE_TAG =
0xC1- TRUE_TAG =
0xC2- F64_TAG =
0xC3- U8_TAG =
0xC4- U16_TAG =
0xC5- U32_TAG =
0xC6- U64_TAG =
0xC7- I8_TAG =
0xC8- I16_TAG =
0xC9- I32_TAG =
0xCA- I64_TAG =
0xCB- BIN8_TAG =
0xCC- BIN16_TAG =
0xCD- BIN32_TAG =
0xCE- STR8_TAG =
0xCF- STR16_TAG =
0xD0- STR32_TAG =
0xD1- ARRAY16_TAG =
0xD2- ARRAY32_TAG =
0xD3- MAP16_TAG =
0xD4- MAP32_TAG =
0xD5- SHAPE_DEF_TAG =
0xD6- KEY_REF_TAG =
0xD8- STR_REF_TAG =
0xD9
Class Method Summary collapse
- .decode_v2(bytes) ⇒ Object
- .decode_v2_array_body(reader, state, length) ⇒ Object
- .decode_v2_key(reader, state) ⇒ Object
- .decode_v2_map_body(reader, state, length) ⇒ Object
- .decode_v2_string_tag(reader, state, tag) ⇒ Object
- .decode_v2_value(reader, state) ⇒ Object
- .decode_v2_value_from_tag(reader, state, tag) ⇒ Object
- .detect_shape_keys(values) ⇒ Object
- .encode_v2(value) ⇒ Object
- .encode_v2_array(values, out, state) ⇒ Object
- .encode_v2_binary(value, out) ⇒ Object
- .encode_v2_i64(value, out) ⇒ Object
- .encode_v2_key(key, out, state) ⇒ Object
- .encode_v2_map(entries, out, state) ⇒ Object
- .encode_v2_string_literal(value, out) ⇒ Object
- .encode_v2_u64(value, out) ⇒ Object
- .encode_v2_value(value, out, state) ⇒ Object
- .write_v2_array_header(length, out) ⇒ Object
- .write_v2_map_header(length, out) ⇒ Object
Class Method Details
.decode_v2(bytes) ⇒ Object
68 69 70 71 72 73 74 75 |
# File 'lib/twilic/core/v2.rb', line 68 def decode_v2(bytes) reader = Wire::Reader.new(bytes) state = DecodeState.new value = decode_v2_value(reader, state) raise Errors.invalid_data("trailing bytes in v2 decode") unless reader.eof? value end |
.decode_v2_array_body(reader, state, length) ⇒ Object
327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 |
# File 'lib/twilic/core/v2.rb', line 327 def decode_v2_array_body(reader, state, length) return Model.array_value([]) if length.zero? first_tag = reader.read_u8 if first_tag == SHAPE_DEF_TAG shape_id = reader.read_varuint key_count = reader.read_varuint keys = Array.new(key_count) { decode_v2_key(reader, state) } while state.shapes.length <= shape_id state.shapes << nil end state.shapes[shape_id] = keys values = Array.new(length) do row = keys.map do |key| val = decode_v2_value(reader, state) Model.entry(key, val) end Model.map_value(row) end return Model.array_value(values) end values = Array.new(length) values[0] = decode_v2_value_from_tag(reader, state, first_tag) (1...length).each { |i| values[i] = decode_v2_value(reader, state) } Model.array_value(values) end |
.decode_v2_key(reader, state) ⇒ Object
363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 |
# File 'lib/twilic/core/v2.rb', line 363 def decode_v2_key(reader, state) tag = reader.read_u8 if tag == KEY_REF_TAG id = reader.read_varuint raise Errors.invalid_data("unknown key_ref id") if id >= state.keys.length return state.keys[id] end if (0x80..0x9F).cover?(tag) key = reader.read_exact(tag & 0x1F).force_encoding(Encoding::UTF_8) state.keys << key return key end if [STR8_TAG, STR16_TAG, STR32_TAG].include?(tag) v = decode_v2_value_from_tag(reader, state, tag) raise Errors.invalid_data("expected string key") unless v.kind == Model::ValueKind::STRING state.keys << v.str return v.str end raise Errors.invalid_data("map key must be key_ref or string") end |
.decode_v2_map_body(reader, state, length) ⇒ Object
354 355 356 357 358 359 360 361 |
# File 'lib/twilic/core/v2.rb', line 354 def decode_v2_map_body(reader, state, length) entries = Array.new(length) do key = decode_v2_key(reader, state) value = decode_v2_value(reader, state) Model.entry(key, value) end Model.map_value(entries) end |
.decode_v2_string_tag(reader, state, tag) ⇒ Object
315 316 317 318 319 320 321 322 323 324 325 |
# File 'lib/twilic/core/v2.rb', line 315 def decode_v2_string_tag(reader, state, tag) length = case tag when STR8_TAG then reader.read_u8 when STR16_TAG then reader.read_exact(2).unpack1("v") when STR32_TAG then reader.read_exact(4).unpack1("V") else raise Errors.invalid_data("invalid string tag") end s = reader.read_exact(length).force_encoding(Encoding::UTF_8) state.strings << s Model.string_value(s) end |
.decode_v2_value(reader, state) ⇒ Object
245 246 247 248 |
# File 'lib/twilic/core/v2.rb', line 245 def decode_v2_value(reader, state) tag = reader.read_u8 decode_v2_value_from_tag(reader, state, tag) end |
.decode_v2_value_from_tag(reader, state, tag) ⇒ Object
250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 |
# File 'lib/twilic/core/v2.rb', line 250 def decode_v2_value_from_tag(reader, state, tag) case tag when 0..0x7F Model.u64_value(tag) when 0x80..0x9F length = tag & 0x1F s = reader.read_exact(length).force_encoding(Encoding::UTF_8) state.strings << s Model.string_value(s) when 0xA0..0xAF decode_v2_array_body(reader, state, tag & 0x0F) when 0xB0..0xBF decode_v2_map_body(reader, state, tag & 0x0F) when 0xE0..0xFF Model.i64_value(tag - 256) when NULL_TAG Model.null_value when FALSE_TAG Model.bool_value(false) when TRUE_TAG Model.bool_value(true) when F64_TAG Model.f64_value(reader.read_f64_le) when U8_TAG Model.u64_value(reader.read_u8) when U16_TAG Model.u64_value(reader.read_exact(2).unpack1("v")) when U32_TAG Model.u64_value(reader.read_exact(4).unpack1("V")) when U64_TAG Model.u64_value(reader.read_u64_le) when I8_TAG Model.i64_value(reader.read_exact(1).unpack1("c")) when I16_TAG Model.i64_value(reader.read_exact(2).unpack1("s<")) when I32_TAG Model.i64_value(reader.read_exact(4).unpack1("l<")) when I64_TAG Model.i64_value(reader.read_exact(8).unpack1("q<")) when BIN8_TAG Model.binary_value(reader.read_exact(reader.read_u8)) when BIN16_TAG Model.binary_value(reader.read_exact(reader.read_exact(2).unpack1("v"))) when BIN32_TAG Model.binary_value(reader.read_exact(reader.read_exact(4).unpack1("V"))) when STR8_TAG, STR16_TAG, STR32_TAG decode_v2_string_tag(reader, state, tag) when ARRAY16_TAG decode_v2_array_body(reader, state, reader.read_exact(2).unpack1("v")) when ARRAY32_TAG decode_v2_array_body(reader, state, reader.read_exact(4).unpack1("V")) when MAP16_TAG decode_v2_map_body(reader, state, reader.read_exact(2).unpack1("v")) when MAP32_TAG decode_v2_map_body(reader, state, reader.read_exact(4).unpack1("V")) when STR_REF_TAG id = reader.read_varuint raise Errors.invalid_data("unknown str_ref id") if id >= state.strings.length Model.string_value(state.strings[id]) else raise Errors.invalid_tag(tag) end end |
.detect_shape_keys(values) ⇒ Object
232 233 234 235 236 237 238 239 240 241 242 243 |
# File 'lib/twilic/core/v2.rb', line 232 def detect_shape_keys(values) return nil if values.length < 2 return nil unless values[0].kind == Model::ValueKind::MAP && !values[0].map.empty? keys = values[0].map.map(&:key) values[1..].each do |value| return nil unless value.kind == Model::ValueKind::MAP && value.map.length == keys.length value.map.each_with_index { |e, i| return nil unless e.key == keys[i] } end keys end |
.encode_v2(value) ⇒ Object
61 62 63 64 65 66 |
# File 'lib/twilic/core/v2.rb', line 61 def encode_v2(value) out = +"" state = EncodeState.new encode_v2_value(value, out, state) out end |
.encode_v2_array(values, out, state) ⇒ Object
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 |
# File 'lib/twilic/core/v2.rb', line 110 def encode_v2_array(values, out, state) shape_keys = detect_shape_keys(values) if shape_keys sk = shape_keys.join("\0") shape_id = state.shape_ids[sk] unless shape_id shape_id = state.next_shape_id state.next_shape_id += 1 state.shape_ids[sk] = shape_id end write_v2_array_header(values.length, out) out << SHAPE_DEF_TAG.chr Wire.encode_varuint(shape_id, out) Wire.encode_varuint(shape_keys.length, out) shape_keys.each { |key| encode_v2_key(key, out, state) } values.each do |value| raise Errors.invalid_data("shape array row must be map") unless value.kind == Model::ValueKind::MAP value.map.each { |field| encode_v2_value(field.value, out, state) } end return end write_v2_array_header(values.length, out) values.each { |value| encode_v2_value(value, out, state) } end |
.encode_v2_binary(value, out) ⇒ Object
169 170 171 172 173 174 175 176 177 178 |
# File 'lib/twilic/core/v2.rb', line 169 def encode_v2_binary(value, out) if value.bytesize <= 0xFF out << BIN8_TAG.chr << value.bytesize.chr elsif value.bytesize <= 0xFFFF out << BIN16_TAG.chr << [value.bytesize].pack("v") else out << BIN32_TAG.chr << [value.bytesize].pack("V") end out << value end |
.encode_v2_i64(value, out) ⇒ Object
195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 |
# File 'lib/twilic/core/v2.rb', line 195 def encode_v2_i64(value, out) if value >= -32 && value <= -1 out << (value & 0xFF).chr elsif value >= 0 && value <= 127 out << value.chr elsif value >= -128 && value <= 127 out << I8_TAG.chr << [value].pack("c") elsif value >= -32_768 && value <= 32_767 out << I16_TAG.chr << [value].pack("s<") elsif value >= -2_147_483_648 && value <= 2_147_483_647 out << I32_TAG.chr << [value].pack("l<") else out << I64_TAG.chr Wire.append_u64_le(out, value & 0xFFFFFFFFFFFFFFFF) end end |
.encode_v2_key(key, out, state) ⇒ Object
144 145 146 147 148 149 150 151 152 153 |
# File 'lib/twilic/core/v2.rb', line 144 def encode_v2_key(key, out, state) if state.key_ids.key?(key) out << KEY_REF_TAG.chr Wire.encode_varuint(state.key_ids[key], out) return end encode_v2_string_literal(key, out) state.key_ids[key] = state.next_key_id state.next_key_id += 1 end |
.encode_v2_map(entries, out, state) ⇒ Object
136 137 138 139 140 141 142 |
# File 'lib/twilic/core/v2.rb', line 136 def encode_v2_map(entries, out, state) write_v2_map_header(entries.length, out) entries.each do |entry| encode_v2_key(entry.key, out, state) encode_v2_value(entry.value, out, state) end end |
.encode_v2_string_literal(value, out) ⇒ Object
155 156 157 158 159 160 161 162 163 164 165 166 167 |
# File 'lib/twilic/core/v2.rb', line 155 def encode_v2_string_literal(value, out) bytes = value.b if bytes.bytesize <= 31 out << (0x80 | bytes.bytesize).chr elsif bytes.bytesize <= 0xFF out << STR8_TAG.chr << bytes.bytesize.chr elsif bytes.bytesize <= 0xFFFF out << STR16_TAG.chr << [bytes.bytesize].pack("v") else out << STR32_TAG.chr << [bytes.bytesize].pack("V") end out << bytes end |
.encode_v2_u64(value, out) ⇒ Object
180 181 182 183 184 185 186 187 188 189 190 191 192 193 |
# File 'lib/twilic/core/v2.rb', line 180 def encode_v2_u64(value, out) if value <= 127 out << value.chr elsif value <= 0xFF out << U8_TAG.chr << value.chr elsif value <= 0xFFFF out << U16_TAG.chr << [value].pack("v") elsif value <= 0xFFFFFFFF out << U32_TAG.chr << [value].pack("V") else out << U64_TAG.chr Wire.append_u64_le(out, value) end end |
.encode_v2_value(value, out, state) ⇒ Object
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
# File 'lib/twilic/core/v2.rb', line 77 def encode_v2_value(value, out, state) case value.kind when Model::ValueKind::NULL out << NULL_TAG.chr when Model::ValueKind::BOOL out << (value.bool ? TRUE_TAG : FALSE_TAG).chr when Model::ValueKind::I64 encode_v2_i64(value.i64, out) when Model::ValueKind::U64 encode_v2_u64(value.u64, out) when Model::ValueKind::F64 out << F64_TAG.chr Wire.append_f64_le(out, value.f64) when Model::ValueKind::STRING if state.str_ids.key?(value.str) out << STR_REF_TAG.chr Wire.encode_varuint(state.str_ids[value.str], out) else encode_v2_string_literal(value.str, out) state.str_ids[value.str] = state.next_str_id state.next_str_id += 1 end when Model::ValueKind::BINARY encode_v2_binary(value.bin, out) when Model::ValueKind::ARRAY encode_v2_array(value.arr, out, state) when Model::ValueKind::MAP encode_v2_map(value.map, out, state) else raise Errors.invalid_data("unsupported value kind") end end |
.write_v2_array_header(length, out) ⇒ Object
212 213 214 215 216 217 218 219 220 |
# File 'lib/twilic/core/v2.rb', line 212 def write_v2_array_header(length, out) if length <= 15 out << (0xA0 | length).chr elsif length <= 0xFFFF out << ARRAY16_TAG.chr << [length].pack("v") else out << ARRAY32_TAG.chr << [length].pack("V") end end |
.write_v2_map_header(length, out) ⇒ Object
222 223 224 225 226 227 228 229 230 |
# File 'lib/twilic/core/v2.rb', line 222 def write_v2_map_header(length, out) if length <= 15 out << (0xB0 | length).chr elsif length <= 0xFFFF out << MAP16_TAG.chr << [length].pack("v") else out << MAP32_TAG.chr << [length].pack("V") end end |