Class: BSV::Wallet::Wire::Reader

Inherits:
Object
  • Object
show all
Defined in:
lib/bsv/wallet/wire/reader_writer.rb

Overview

Reader reads sequentially from a binary string.

Instance Method Summary collapse

Constructor Details

#initialize(data) ⇒ Reader

Returns a new instance of Reader.

Parameters:

  • data (String)

    binary data



202
203
204
205
# File 'lib/bsv/wallet/wire/reader_writer.rb', line 202

def initialize(data)
  @data = data.b
  @pos = 0
end

Instance Method Details

#next_negative_one?Boolean

Whether the next varint is the NegativeOne sentinel (0xFFFF…). Peeks at the next byte without consuming it.

Returns:

  • (Boolean)


293
294
295
# File 'lib/bsv/wallet/wire/reader_writer.rb', line 293

def next_negative_one?
  @pos < @data.bytesize && @data.getbyte(@pos) == 0xFF
end

#peek_byteInteger

Look at the next byte without consuming it.

Returns:

  • (Integer)

Raises:

  • (ArgumentError)


219
220
221
222
223
# File 'lib/bsv/wallet/wire/reader_writer.rb', line 219

def peek_byte
  raise ArgumentError, 'unexpected end of data peeking byte' if @pos >= @data.bytesize

  @data.getbyte(@pos)
end

#read_base64_intString

Read a binary value and return it Base64-encoded.

Returns:

  • (String)

    Base64-encoded string



345
346
347
348
# File 'lib/bsv/wallet/wire/reader_writer.rb', line 345

def read_base64_int
  raw = read_int_bytes
  Base64.strict_encode64(raw)
end

#read_byteInteger

Read a single byte.

Returns:

  • (Integer)

Raises:

  • (ArgumentError)


209
210
211
212
213
214
215
# File 'lib/bsv/wallet/wire/reader_writer.rb', line 209

def read_byte
  raise ArgumentError, 'unexpected end of data reading byte' if @pos >= @data.bytesize

  byte = @data.getbyte(@pos)
  @pos += 1
  byte
end

#read_bytes(n) ⇒ String

Read n raw bytes.

Returns:

  • (String)

    binary string

Raises:

  • (ArgumentError)


227
228
229
230
231
232
233
# File 'lib/bsv/wallet/wire/reader_writer.rb', line 227

def read_bytes(n)
  raise ArgumentError, "need #{n} bytes at offset #{@pos}, got #{remaining}" if remaining < n

  slice = @data.byteslice(@pos, n)
  @pos += n
  slice
end

#read_int_bytesString

Read a varint-length-prefixed byte array (ReadIntBytes in Go).

Returns:

  • (String)

    binary string



299
300
301
302
303
304
# File 'lib/bsv/wallet/wire/reader_writer.rb', line 299

def read_int_bytes
  len = read_varint
  return ''.b if len == 0xFFFF_FFFF_FFFF_FFFF || len.zero?

  read_bytes(len)
end

#read_optional_boolBoolean?

Read an optional boolean byte (Go/BRC-103 convention). 0xFF → nil, 0x00 → false, 0x01 → true

Returns:

  • (Boolean, nil)


257
258
259
260
261
262
# File 'lib/bsv/wallet/wire/reader_writer.rb', line 257

def read_optional_bool
  byte = read_byte
  return nil if byte == 0xFF

  byte == 0x01
end

#read_optional_bytes_with_flag(fixed_size: nil) ⇒ String?

Read optional bytes with a 1-byte flag prefix (Go BytesOptionWithFlag). 0x00 → nil; 0x01 → read varint_len + bytes (or fixed_size bytes).

Parameters:

  • fixed_size (Integer, nil) (defaults to: nil)

    if set, read exactly this many bytes (no varint)

Returns:

  • (String, nil)

    binary string or nil



379
380
381
382
383
384
385
386
387
388
# File 'lib/bsv/wallet/wire/reader_writer.rb', line 379

def read_optional_bytes_with_flag(fixed_size: nil)
  flag = read_byte
  return nil if flag.zero?

  if fixed_size
    read_bytes(fixed_size)
  else
    read_int_bytes
  end
end

#read_optional_stringString?

Read an optional string: NegativeOne sentinel → nil; else varint len + bytes.

Returns:

  • (String, nil)


315
316
317
318
319
320
# File 'lib/bsv/wallet/wire/reader_writer.rb', line 315

def read_optional_string
  val = read_varint
  return nil if val == 0xFFFF_FFFF_FFFF_FFFF

  read_bytes(val).force_encoding('UTF-8')
end

#read_optional_uint32Integer?

Read an optional uint32: NegativeOne sentinel → nil; else varint → Integer.

Returns:

  • (Integer, nil)


308
309
310
311
# File 'lib/bsv/wallet/wire/reader_writer.rb', line 308

def read_optional_uint32
  val = read_varint
  val == 0xFFFF_FFFF_FFFF_FFFF ? nil : val
end

#read_outpointHash

Read an outpoint: 32-byte display-order txid + varint vout.

Returns:

  • (Hash)

    { txid_hex: String, vout: Integer }



272
273
274
275
276
# File 'lib/bsv/wallet/wire/reader_writer.rb', line 272

def read_outpoint
  txid_bytes = read_bytes(32)
  vout = read_varint
  { txid_hex: txid_bytes.unpack1('H*'), vout: vout }
end

#read_privileged_paramsArray(Boolean|nil, String|nil)

Read privileged params (Go decodePrivilegedParams).

Returns:

  • (Array(Boolean|nil, String|nil))


352
353
354
355
356
357
358
359
360
361
362
363
# File 'lib/bsv/wallet/wire/reader_writer.rb', line 352

def read_privileged_params
  privileged = read_optional_bool
  first_byte = read_byte
  if first_byte == 0xFF
    [privileged, nil]
  else
    # Back up one byte and read as varint-prefixed string
    @pos -= 1
    reason = read_str_with_varint_len
    [privileged, reason]
  end
end

#read_remainingString

Read all remaining bytes.

Returns:

  • (String)

    binary string



285
286
287
288
289
# File 'lib/bsv/wallet/wire/reader_writer.rb', line 285

def read_remaining
  slice = @data.byteslice(@pos, @data.bytesize - @pos) || ''.b
  @pos = @data.bytesize
  slice
end

#read_satoshisInteger

Read 8-byte little-endian uint64 satoshi amount.

Returns:

  • (Integer)


266
267
268
# File 'lib/bsv/wallet/wire/reader_writer.rb', line 266

def read_satoshis
  read_bytes(8).unpack1('Q<')
end

#read_str_with_varint_lenString

Read a varint-prefixed UTF-8 string.

Returns:

  • (String)

Raises:

  • (ArgumentError)

    if the bytes are not valid UTF-8



246
247
248
249
250
251
252
# File 'lib/bsv/wallet/wire/reader_writer.rb', line 246

def read_str_with_varint_len
  len = read_varint
  str = read_bytes(len).force_encoding('UTF-8')
  raise ArgumentError, 'varint-prefixed string is not valid UTF-8' unless str.valid_encoding?

  str
end

#read_stringString

Read a varint-len string (always present, 0-length = empty string). Matches Go ReadString which returns “” for length 0 or NegativeOne.

Returns:

  • (String)


393
394
395
396
397
398
# File 'lib/bsv/wallet/wire/reader_writer.rb', line 393

def read_string
  len = read_varint
  return '' if len.zero? || len == 0xFFFF_FFFF_FFFF_FFFF

  read_bytes(len).force_encoding('UTF-8')
end

#read_string_mapHash<String,String>

Read a string map: varint count + key/value pairs (each varint-len-prefixed).

Returns:

  • (Hash<String,String>)


334
335
336
337
338
339
340
341
# File 'lib/bsv/wallet/wire/reader_writer.rb', line 334

def read_string_map
  count = read_varint
  count.times.each_with_object({}) do |_, h|
    k = read_str_with_varint_len
    v = read_str_with_varint_len
    h[k] = v
  end
end

#read_string_sliceArray<String>?

Read an array of strings encoded as varint count + each optional string. NegativeOne sentinel count → nil.

Returns:

  • (Array<String>, nil)


325
326
327
328
329
330
# File 'lib/bsv/wallet/wire/reader_writer.rb', line 325

def read_string_slice
  count = read_varint
  return nil if count == 0xFFFF_FFFF_FFFF_FFFF

  count.times.map { read_optional_string || '' }
end

#read_txid_sliceArray<String>?

Read a txid slice: NegativeOne → nil; else varint count + 32 bytes per txid. Go stores txids in wire order (txID) — returned as hex without reversal.

Returns:

  • (Array<String>, nil)


368
369
370
371
372
373
# File 'lib/bsv/wallet/wire/reader_writer.rb', line 368

def read_txid_slice
  count = read_varint
  return nil if count == 0xFFFF_FFFF_FFFF_FFFF

  count.times.map { read_bytes(32).unpack1('H*') }
end

#read_varintInteger

Read a Bitcoin varint.

Returns:

  • (Integer)


237
238
239
240
241
# File 'lib/bsv/wallet/wire/reader_writer.rb', line 237

def read_varint
  value, consumed = BSV::Transaction::VarInt.decode(@data, @pos)
  @pos += consumed
  value
end

#remainingObject

Remaining bytes.



279
280
281
# File 'lib/bsv/wallet/wire/reader_writer.rb', line 279

def remaining
  @data.bytesize - @pos
end