Module: Tem::Builders::Abi::Impl

Defined in:
lib/tem/builders/abi.rb

Overview

Implementation code for the ABI methods.

Class Method Summary collapse

Class Method Details

.check_number_range(number, length, is_signed) ⇒ Object

Checks the range of a number for fixed-length encoding.



444
445
446
447
448
449
450
451
452
453
454
# File 'lib/tem/builders/abi.rb', line 444

def self.check_number_range(number, length, is_signed)
  range = 1 << (8 * length)
  if is_signed      
    min_value, max_value = -(range >> 1), (range >> 1) - 1
  else
    min_value, max_value = 0, range - 1
  end
  
  exception_string = "Number #{number} exceeds #{min_value}-#{max_value}"
  raise exception_string if number < min_value or number > max_value
end

.number_from_array(array, offset, length, is_signed, is_big_endian) ⇒ Object

Reads a variable-length number serialized to an array.

The source is indicated by the array and offset parameters. The number’s length is given in bytes.



381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
# File 'lib/tem/builders/abi.rb', line 381

def self.number_from_array(array, offset, length, is_signed, is_big_endian)
  # Read the raw number from the array.    
  number = 0
  if is_big_endian
    0.upto length - 1 do |i|
      number = (number << 8) | array[offset + i]
    end
  else
    (length - 1).downto 0 do |i|
      number = (number << 8) | array[offset + i]
    end
  end
  
  if is_signed  # Add the sign if necessary.
    range = 1 << (8 * length)
    max_value = (range >> 1) - 1
    number -= range if number > max_value
  end
      
  number
end

.number_to_array(number, length, is_signed, is_big_endian) ⇒ Object

Writes a fixed or variable-length number to an array.

The source is indicated by the array and offset parameters. The number’s length is given in bytes.



407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
# File 'lib/tem/builders/abi.rb', line 407

def self.number_to_array(number, length, is_signed, is_big_endian)
  bytes = []
  if number.kind_of? OpenSSL::BN  # OpenSSL key material
    if is_signed and number < 0  # normalize number
      range = OpenSSL::BN.new("1") << (8 * length)
      number += range
    end
    
    length ||= [number.num_bytes, 1].max
    v = 0
    (length * 8 - 1).downto 0 do |i|
      v = (v << 1) | (number.bit_set?(i) ? 1 : 0)
      if i & 7 == 0
        bytes << v
        v = 0
      end
    end
    bytes.reverse! unless is_big_endian
  else  # Ruby number.
    if length
      length.times do
        bytes << (number & 0xFF)
        number >>= 8
      end
    else
      loop do
        bytes << (number & 0xFF)
        number >>= 8
        break if number == 0
      end
    end
    bytes.reverse! if is_big_endian
  end    
  bytes
end

.string_from_array(array, offset, length) ⇒ Object

Reads a variable-length string serialized to an array.

The source is indicated by the array and offset parameters. The number’s length is given in bytes.



460
461
462
# File 'lib/tem/builders/abi.rb', line 460

def self.string_from_array(array, offset, length)
  array[offset, length].pack('C*')
end

.string_to_array(array_or_string, length) ⇒ Object

Writes a fixed or variable-length string to an array.

The source is indicated by the array and offset parameters. The number’s length is given in bytes.



468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
# File 'lib/tem/builders/abi.rb', line 468

def self.string_to_array(array_or_string, length)
  if array_or_string.respond_to? :to_str
    # array_or_string is String-like
    string = array_or_string.to_str
    array = string.unpack('C*')
  else
    # array_or_string is Array-like
    array = array_or_string
  end
  
  if length and array.length > length
    raise "Cannot fit #{array_or_string.inspect} into a #{length}-byte string"       
  end
  # Pad the array with zeros up to the fixed length.
  length ||= 0
  array << 0 while array.length < length
  array
end