Module: IOStruct

Defined in:
lib/iostruct.rb,
lib/iostruct/version.rb

Defined Under Namespace

Modules: ClassMethods, HexInspect, InstanceMethods Classes: FieldInfo

Constant Summary collapse

FMTSPEC =
{
  'C' => [1, Integer ], # 8-bit unsigned (unsigned char)
  'S' => [2, Integer ], # 16-bit unsigned, native endian (uint16_t)
  'I' => [4, Integer ], # 32-bit unsigned, native endian (uint32_t)
  'L' => [4, Integer ], # 32-bit unsigned, native endian (uint32_t)
  'Q' => [8, Integer ], # 64-bit unsigned, native endian (uint64_t)

  'c' => [1, Integer ], # 8-bit signed (signed char)
  's' => [2, Integer ], # 16-bit signed, native endian (int16_t)
  'i' => [4, Integer ], # 32-bit signed, native endian (int32_t)
  'l' => [4, Integer ], # 32-bit signed, native endian (int32_t)
  'q' => [8, Integer ], # 64-bit signed, native endian (int64_t)

  'n' => [2, Integer ], # 16-bit unsigned, network (big-endian) byte order
  'N' => [4, Integer ], # 32-bit unsigned, network (big-endian) byte order
  'v' => [2, Integer ], # 16-bit unsigned, VAX (little-endian) byte order
  'V' => [4, Integer ], # 32-bit unsigned, VAX (little-endian) byte order

  'A' => [1, String  ], # arbitrary binary string (remove trailing nulls and ASCII spaces)
  'a' => [1, String  ], # arbitrary binary string
  'Z' => [1, String  ], # arbitrary binary string (remove trailing nulls)
  'H' => [1, String  ], # hex string (high nibble first)
  'h' => [1, String  ], # hex string (low nibble first)

  'D' => [8, Float   ], # double-precision, native format
  'd' => [8, Float   ],
  'F' => [4, Float   ], # single-precision, native format
  'f' => [4, Float   ],
  'E' => [8, Float   ], # double-precision, little-endian byte order
  'e' => [4, Float   ], # single-precision, little-endian byte order
  'G' => [8, Float   ], # double-precision, network (big-endian) byte order
  'g' => [4, Float   ], # single-precision, network (big-endian) byte order

  'x' => [1, nil     ], # skip forward one byte
}.freeze
VERSION =
"0.2.0"

Class Method Summary collapse

Class Method Details

.auto_names(fields, size) ⇒ Object



83
84
85
86
87
88
89
90
91
92
# File 'lib/iostruct.rb', line 83

def self.auto_names fields, size
  names = []
  offset = 0
  fields.each do |f|
    names << sprintf("f%x", offset).to_sym
    offset += f.size
  end
  #raise "size mismatch: #{size} != #{offset}" if size != offset
  names
end

.new(fmt, *names, inspect: :hex, **renames) ⇒ Object



44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/iostruct.rb', line 44

def self.new fmt, *names, inspect: :hex, **renames
  fields, size = parse_format(fmt, names)
  names = auto_names(fields, size) if names.empty?
  names.map!{ |n| renames[n] || n } if renames.any?

  Struct.new( *names ).tap do |x|
    x.const_set 'FIELDS', names.zip(fields).to_h
    x.const_set 'FORMAT', fmt
    x.const_set 'SIZE',  size
    x.extend ClassMethods
    x.include InstanceMethods
    x.include HexInspect if inspect == :hex
  end
end

.parse_format(fmt, names) ⇒ Object

self.new



59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/iostruct.rb', line 59

def self.parse_format(fmt, names)
  offset = 0
  fields = []
  fmt.scan(/([a-z])(\d*)/i).map do |type,len|
    size, klass = FMTSPEC[type] || raise("Unknown field type #{type.inspect}")
    len = len.empty? ? 1 : len.to_i
    case type
    when 'A', 'a', 'x', 'Z'
      fields << FieldInfo.new(klass, size*len, offset) if klass
      offset += len
    when 'H', 'h'
      # XXX ruby's String#unpack length for hex strings is in characters, not bytes, i.e. "x".unpack("H2") => ["78"]
      fields << FieldInfo.new(klass, size*len/2, offset) if klass
      offset += len/2
    else
      len.times do |i|
        fields << FieldInfo.new(klass, size, offset)
        offset += size
      end
    end
  end
  [fields, offset]
end