Module: BTC::WireFormat
Constant Summary collapse
- PACK_FORMAT_UINT8 =
"C".freeze
- PACK_FORMAT_INT8 =
"c".freeze
- PACK_FORMAT_UINT16LE =
"S<".freeze
- PACK_FORMAT_INT16LE =
"s<".freeze
- PACK_FORMAT_UINT32LE =
"L<".freeze
- PACK_FORMAT_INT32LE =
"l<".freeze
- PACK_FORMAT_UINT32BE =
used in BIP32
"L>".freeze
- PACK_FORMAT_INT32BE =
"l>".freeze
- PACK_FORMAT_UINT64LE =
"Q<".freeze
- PACK_FORMAT_INT64LE =
"q<".freeze
Instance Method Summary collapse
- #encode_array(array, &block) ⇒ Object
- #encode_int16le(int) ⇒ Object
- #encode_int32be(int) ⇒ Object
- #encode_int32le(int) ⇒ Object
- #encode_int64le(int) ⇒ Object
- #encode_int8(int) ⇒ Object
-
#encode_string(string) ⇒ Object
Returns the binary representation of the var-length string.
- #encode_uint16le(int) ⇒ Object
-
#encode_uint32be(int) ⇒ Object
used in BIP32.
- #encode_uint32le(int) ⇒ Object
- #encode_uint64le(int) ⇒ Object
-
#encode_uint8(int) ⇒ Object
Encode int into one of the formats.
-
#encode_uleb128(value) ⇒ Object
Encodes an unsigned integer using LEB128.
-
#encode_varint(i) ⇒ Object
Encodes integer and returns its binary varint representation.
-
#read_array(data: nil, stream: nil, offset: 0) ⇒ Object
Reads varint length prefix, then calls the block appropriate number of times to read the items.
- #read_int16le(data: nil, stream: nil, offset: 0) ⇒ Object
- #read_int32be(data: nil, stream: nil, offset: 0) ⇒ Object
- #read_int32le(data: nil, stream: nil, offset: 0) ⇒ Object
- #read_int64le(data: nil, stream: nil, offset: 0) ⇒ Object
- #read_int8(data: nil, stream: nil, offset: 0) ⇒ Object
-
#read_string(data: nil, stream: nil, offset: 0) ⇒ Object
Reads variable-length string from data buffer or IO stream.
- #read_uint16le(data: nil, stream: nil, offset: 0) ⇒ Object
-
#read_uint32be(data: nil, stream: nil, offset: 0) ⇒ Object
used in BIP32.
- #read_uint32le(data: nil, stream: nil, offset: 0) ⇒ Object
- #read_uint64le(data: nil, stream: nil, offset: 0) ⇒ Object
-
#read_uint8(data: nil, stream: nil, offset: 0) ⇒ Object
These read fixed-length integer in appropriate format ("le" stands for "little-endian") Return [value, length] or [nil, length] just like #read_varint method (see above).
-
#read_uleb128(data: nil, stream: nil, offset: 0) ⇒ Object
Decodes an unsigned integer encoded in LEB128.
-
#read_varint(data: nil, stream: nil, offset: 0) ⇒ Object
Reads varint from data or stream.
- #write_array(array, data: nil, stream: nil, &block) ⇒ Object
-
#write_string(string, data: nil, stream: nil) ⇒ Object
Writes variable-length string to a data buffer or IO stream.
-
#write_uleb128(value, data: nil, stream: nil) ⇒ Object
Writes an unsigned integer encoded in LEB128 to a data buffer or a stream.
-
#write_varint(i, data: nil, stream: nil) ⇒ Object
Encodes integer and returns its binary varint representation.
Instance Method Details
#encode_array(array, &block) ⇒ Object
199 200 201 |
# File 'lib/btcruby/wire_format.rb', line 199 def encode_array(array, &block) write_array(array, &block) end |
#encode_int16le(int) ⇒ Object
347 |
# File 'lib/btcruby/wire_format.rb', line 347 def encode_int16le(int); [int].pack(PACK_FORMAT_INT16LE); end |
#encode_int32be(int) ⇒ Object
348 |
# File 'lib/btcruby/wire_format.rb', line 348 def encode_int32be(int); [int].pack(PACK_FORMAT_INT32BE); end |
#encode_int32le(int) ⇒ Object
350 |
# File 'lib/btcruby/wire_format.rb', line 350 def encode_int32le(int); [int].pack(PACK_FORMAT_INT32LE); end |
#encode_int64le(int) ⇒ Object
353 |
# File 'lib/btcruby/wire_format.rb', line 353 def encode_int64le(int); [int].pack(PACK_FORMAT_INT64LE); end |
#encode_int8(int) ⇒ Object
345 |
# File 'lib/btcruby/wire_format.rb', line 345 def encode_int8(int); [int].pack(PACK_FORMAT_INT8); end |
#encode_string(string) ⇒ Object
Returns the binary representation of the var-length string.
166 167 168 169 |
# File 'lib/btcruby/wire_format.rb', line 166 def encode_string(string) raise ArgumentError, "String must be present" if !string encode_varint(string.bytesize) + BTC::Data.ensure_binary_encoding(string) end |
#encode_uint16le(int) ⇒ Object
346 |
# File 'lib/btcruby/wire_format.rb', line 346 def encode_uint16le(int); [int].pack(PACK_FORMAT_UINT16LE); end |
#encode_uint32be(int) ⇒ Object
used in BIP32
351 |
# File 'lib/btcruby/wire_format.rb', line 351 def encode_uint32be(int); [int].pack(PACK_FORMAT_UINT32BE); end |
#encode_uint32le(int) ⇒ Object
349 |
# File 'lib/btcruby/wire_format.rb', line 349 def encode_uint32le(int); [int].pack(PACK_FORMAT_UINT32LE); end |
#encode_uint64le(int) ⇒ Object
352 |
# File 'lib/btcruby/wire_format.rb', line 352 def encode_uint64le(int); [int].pack(PACK_FORMAT_UINT64LE); end |
#encode_uint8(int) ⇒ Object
Encode int into one of the formats
344 |
# File 'lib/btcruby/wire_format.rb', line 344 def encode_uint8(int); [int].pack(PACK_FORMAT_UINT8); end |
#encode_uleb128(value) ⇒ Object
Encodes an unsigned integer using LEB128.
263 264 265 266 267 268 269 270 271 272 273 274 275 276 |
# File 'lib/btcruby/wire_format.rb', line 263 def encode_uleb128(value) raise ArgumentError, "Signed integers are not supported" if value < 0 return "\x00" if value == 0 bytes = [] while value != 0 byte = value & 0b01111111 # 0x7f value >>= 7 if value != 0 byte |= 0b10000000 # 0x80 end bytes << byte end return BTC::Data.data_from_bytes(bytes) end |
#encode_varint(i) ⇒ Object
Encodes integer and returns its binary varint representation.
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
# File 'lib/btcruby/wire_format.rb', line 101 def encode_varint(i) raise ArgumentError, "int must be present" if !i raise ArgumentError, "int must be non-negative" if i < 0 buf = if i < 0xfd [i].pack("C") elsif i <= 0xffff [0xfd, i].pack("Cv") elsif i <= 0xffffffff [0xfe, i].pack("CV") elsif i <= 0xffffffffffffffff [0xff, i].pack("CQ<") else raise ArgumentError, "Does not support integers larger 0xffffffffffffffff (i = 0x#{i.to_s(16)})" end buf end |
#read_array(data: nil, stream: nil, offset: 0) ⇒ Object
Reads varint length prefix, then calls the block appropriate number of times to read the items. Returns an array of items.
191 192 193 194 195 196 197 |
# File 'lib/btcruby/wire_format.rb', line 191 def read_array(data: nil, stream: nil, offset: 0) count, len = read_varint(data: data, stream: stream, offset: offset) return [nil, len] if !count (0...count).map do |i| yield end end |
#read_int16le(data: nil, stream: nil, offset: 0) ⇒ Object
315 316 317 |
# File 'lib/btcruby/wire_format.rb', line 315 def read_int16le(data: nil, stream: nil, offset: 0) _read_fixint(name: :int16le, length: 2, pack_format: PACK_FORMAT_INT16LE, data: data, stream: stream, offset: offset) end |
#read_int32be(data: nil, stream: nil, offset: 0) ⇒ Object
331 332 333 |
# File 'lib/btcruby/wire_format.rb', line 331 def read_int32be(data: nil, stream: nil, offset: 0) _read_fixint(name: :int32be, length: 4, pack_format: PACK_FORMAT_INT32BE, data: data, stream: stream, offset: offset) end |
#read_int32le(data: nil, stream: nil, offset: 0) ⇒ Object
323 324 325 |
# File 'lib/btcruby/wire_format.rb', line 323 def read_int32le(data: nil, stream: nil, offset: 0) _read_fixint(name: :int32le, length: 4, pack_format: PACK_FORMAT_INT32LE, data: data, stream: stream, offset: offset) end |
#read_int64le(data: nil, stream: nil, offset: 0) ⇒ Object
339 340 341 |
# File 'lib/btcruby/wire_format.rb', line 339 def read_int64le(data: nil, stream: nil, offset: 0) _read_fixint(name: :int64le, length: 8, pack_format: PACK_FORMAT_INT64LE, data: data, stream: stream, offset: offset) end |
#read_int8(data: nil, stream: nil, offset: 0) ⇒ Object
307 308 309 |
# File 'lib/btcruby/wire_format.rb', line 307 def read_int8(data: nil, stream: nil, offset: 0) _read_fixint(name: :int8, length: 1, pack_format: PACK_FORMAT_INT8, data: data, stream: stream, offset: offset) end |
#read_string(data: nil, stream: nil, offset: 0) ⇒ Object
Reads variable-length string from data buffer or IO stream. Either data or stream must be present (and only one of them). Returns [string, length] where length is a number of bytes read (includes length prefix and offset bytes). In case of failure, returns [nil, length] where length is a number of bytes read before the error was encountered.
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 |
# File 'lib/btcruby/wire_format.rb', line 134 def read_string(data: nil, stream: nil, offset: 0) if data && !stream string_length, read_length = read_varint(data: data, offset: offset) # If failed to read the length prefix, return nil. return [nil, read_length] if !string_length # Check if we have enough bytes to read the string itself return [nil, read_length] if data.bytesize < read_length + string_length string = BTC::Data.ensure_binary_encoding(data)[read_length, string_length] return [string, read_length + string_length] elsif stream && !data string_length, read_length = read_varint(stream: stream, offset: offset) return [nil, read_length] if !string_length buf = stream.read(string_length) return [nil, read_length] if !buf return [nil, read_length + buf.bytesize] if buf.bytesize < string_length return [buf, read_length + buf.bytesize] else raise ArgumentError, "Either data or stream must be specified." end end |
#read_uint16le(data: nil, stream: nil, offset: 0) ⇒ Object
311 312 313 |
# File 'lib/btcruby/wire_format.rb', line 311 def read_uint16le(data: nil, stream: nil, offset: 0) _read_fixint(name: :uint16le, length: 2, pack_format: PACK_FORMAT_UINT16LE, data: data, stream: stream, offset: offset) end |
#read_uint32be(data: nil, stream: nil, offset: 0) ⇒ Object
used in BIP32
327 328 329 |
# File 'lib/btcruby/wire_format.rb', line 327 def read_uint32be(data: nil, stream: nil, offset: 0) # used in BIP32 _read_fixint(name: :uint32be, length: 4, pack_format: PACK_FORMAT_UINT32BE, data: data, stream: stream, offset: offset) end |
#read_uint32le(data: nil, stream: nil, offset: 0) ⇒ Object
319 320 321 |
# File 'lib/btcruby/wire_format.rb', line 319 def read_uint32le(data: nil, stream: nil, offset: 0) _read_fixint(name: :uint32le, length: 4, pack_format: PACK_FORMAT_UINT32LE, data: data, stream: stream, offset: offset) end |
#read_uint64le(data: nil, stream: nil, offset: 0) ⇒ Object
335 336 337 |
# File 'lib/btcruby/wire_format.rb', line 335 def read_uint64le(data: nil, stream: nil, offset: 0) _read_fixint(name: :uint64le, length: 8, pack_format: PACK_FORMAT_UINT64LE, data: data, stream: stream, offset: offset) end |
#read_uint8(data: nil, stream: nil, offset: 0) ⇒ Object
These read fixed-length integer in appropriate format ("le" stands for "little-endian") Return [value, length] or [nil, length] just like #read_varint method (see above).
303 304 305 |
# File 'lib/btcruby/wire_format.rb', line 303 def read_uint8(data: nil, stream: nil, offset: 0) _read_fixint(name: :uint8, length: 1, pack_format: PACK_FORMAT_UINT8, data: data, stream: stream, offset: offset) end |
#read_uleb128(data: nil, stream: nil, offset: 0) ⇒ Object
Decodes an unsigned integer encoded in LEB128.
Returns [value, length] where value is an integer decoded from LEB128 and length
is a number of bytes read (includes length prefix and offset bytes).
226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 |
# File 'lib/btcruby/wire_format.rb', line 226 def read_uleb128(data: nil, stream: nil, offset: 0) if (data && stream) || (!data && !stream) raise ArgumentError, "Either data or stream must be specified." end if data data = BTC::Data.ensure_binary_encoding(data) end if stream if stream.eof? raise ArgumentError, "Can't read LEB128 from stream because it is already closed." end if offset > 0 buf = stream.read(offset) return [nil, 0] if !buf return [nil, buf.bytesize] if buf.bytesize < offset end end result = 0 shift = 0 while true byte = if data return [nil, offset] if data.bytesize < 1 + offset BTC::Data.bytes_from_data(data, offset: offset, limit: 1)[0] elsif stream buf = stream.read(1) return [nil, offset] if !buf || buf.bytesize == 0 buf.bytes[0] end result |= (byte & 0x7f) << shift break if byte & 0x80 == 0 shift += 7 offset += 1 end [result, offset + 1] end |
#read_varint(data: nil, stream: nil, offset: 0) ⇒ Object
Reads varint from data or stream. Either data or stream must be present (and only one of them). Optional offset is useful when reading from data. Returns [value, length] where value is a decoded integer value and length is number of bytes read (including offset bytes). Value may be nil when decoding failed (length might be zero or greater, depending on how much data was consumed before failing). Usage:
i, _ = read_varint(data: buffer, offset: 42)
i, _ = read_varint(stream: File.open('someblock','r'))
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
# File 'lib/btcruby/wire_format.rb', line 26 def read_varint(data: nil, stream: nil, offset: 0) if data && !stream return [nil, 0] if data.bytesize < 1 + offset bytes = BTC::Data.bytes_from_data(data, offset: offset, limit: 9) # we don't need more than 9 bytes. byte = bytes[0] if byte < 0xfd return [byte, offset + 1] elsif byte == 0xfd return [nil, 1] if data.bytesize < 3 + offset # 1 byte prefix, 2 bytes uint16 return [bytes[1] + bytes[2]*256, offset + 3] elsif byte == 0xfe return [nil, 1] if data.bytesize < 5 + offset # 1 byte prefix, 4 bytes uint32 return [bytes[1] + bytes[2]*256 + bytes[3]*256*256 + bytes[4]*256*256*256, offset + 5] elsif byte == 0xff return [nil, 1] if data.bytesize < 9 + offset # 1 byte prefix, 8 bytes uint64 return [bytes[1] + bytes[2]*256 + bytes[3]*256*256 + bytes[4]*256*256*256 + bytes[5]*256*256*256*256 + bytes[6]*256*256*256*256*256 + bytes[7]*256*256*256*256*256*256 + bytes[8]*256*256*256*256*256*256*256, offset + 9] end elsif stream && !data if stream.eof? raise ArgumentError, "Can't parse varint from stream because it is already closed." end if offset > 0 buf = stream.read(offset) return [nil, 0] if !buf return [nil, buf.bytesize] if buf.bytesize < offset end prefix = stream.read(1) return [nil, offset] if !prefix || prefix.bytesize == 0 byte = prefix.bytes[0] if byte < 0xfd return [byte, offset + 1] elsif byte == 0xfd buf = stream.read(2) return [nil, offset + 1] if !buf return [nil, offset + 1 + buf.bytesize] if buf.bytesize < 2 return [buf.unpack("v").first, offset + 3] elsif byte == 0xfe buf = stream.read(4) return [nil, offset + 1] if !buf return [nil, offset + 1 + buf.bytesize] if buf.bytesize < 4 return [buf.unpack("V").first, offset + 5] elsif byte == 0xff buf = stream.read(8) return [nil, offset + 1] if !buf return [nil, offset + 1 + buf.bytesize] if buf.bytesize < 8 return [buf.unpack("Q<").first, offset + 9] end else raise ArgumentError, "Either data or stream must be specified." end end |
#write_array(array, data: nil, stream: nil, &block) ⇒ Object
203 204 205 206 207 208 209 210 211 212 213 214 215 |
# File 'lib/btcruby/wire_format.rb', line 203 def write_array(array, data: nil, stream: nil, &block) raise ArgumentError, "Array must be present" if !array raise ArgumentError, "Parsing block must be present" if !block intbuf = write_varint(array.size, data: data, stream: stream) array.inject(intbuf) do |buf, e| string = block.call(e) stringbuf = BTC::Data.ensure_binary_encoding(string) data << stringbuf if data stream.write(stringbuf) if stream buf << stringbuf buf end end |
#write_string(string, data: nil, stream: nil) ⇒ Object
Writes variable-length string to a data buffer or IO stream. If data is given, appends to a data. If stream is given, writes to a stream. Returns the binary representation of the var-length string.
175 176 177 178 179 180 181 182 183 184 185 186 |
# File 'lib/btcruby/wire_format.rb', line 175 def write_string(string, data: nil, stream: nil) raise ArgumentError, "String must be present" if !string intbuf = write_varint(string.bytesize, data: data, stream: stream) stringbuf = BTC::Data.ensure_binary_encoding(string) data << stringbuf if data stream.write(stringbuf) if stream intbuf + stringbuf end |
#write_uleb128(value, data: nil, stream: nil) ⇒ Object
Writes an unsigned integer encoded in LEB128 to a data buffer or a stream. Returns LEB128-encoded binary string.
280 281 282 283 284 285 286 |
# File 'lib/btcruby/wire_format.rb', line 280 def write_uleb128(value, data: nil, stream: nil) raise ArgumentError, "Integer must be present" if !value buf = encode_uleb128(value) data << buf if data stream.write(buf) if stream buf end |
#write_varint(i, data: nil, stream: nil) ⇒ Object
Encodes integer and returns its binary varint representation. If data is given, appends to a data. If stream is given, writes to a stream.
123 124 125 126 127 128 |
# File 'lib/btcruby/wire_format.rb', line 123 def write_varint(i, data: nil, stream: nil) buf = encode_varint(i) data << buf if data stream.write(buf) if stream buf end |