iostruct 0.1.3 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 535a978c6d95f5213d7a7e63523b46788adc2d6ca83c714758e9f30f927f546f
4
- data.tar.gz: 5f52aea99a2615221a62e9b7d3228f129f1320e2a21c8b65eae3fec644e4007a
3
+ metadata.gz: 4a17b782202e0503788fd3b16dffdebddecd62becb30c52ba28161ebde1e300d
4
+ data.tar.gz: d42484281a468a378902d4589b67201ad4f134660559766690e48849b9f2a46c
5
5
  SHA512:
6
- metadata.gz: 5a7489c0f185fb36a4e54c51273c2b87bd344754522e6584b329f529ed0ae00c8c287a1abf224b4cd3bfc1ed44fa5694c8036c89b3ef881231f8cfb4f773b6b0
7
- data.tar.gz: 4fa626885e4446c846594c1be13b67649928e45e919aabc660cc2c414c7cb225efb29841e5bd856737d905d988f5dd2219dd3c1622c976cb2e656799beb2a0fe
6
+ metadata.gz: db34080f66b2815628e20a6e7b9331089d55a011dd35ae90b9e8f7758cb97332fad7b9caadfcc977a93e104cb0d9b9e3c932e82a2070e54205ba3bb7a52337d4
7
+ data.tar.gz: d25f3804da77a52630bb4492548c3ab71124c06610558dde277ea2c769a826ce8a720a5df99b2ab3691791b090f6ec4774b65e8072117383ab389e4095451a0f
data/CHANGELOG.md ADDED
@@ -0,0 +1,14 @@
1
+ # 0.3.0
2
+
3
+ - added `__offset` field:
4
+
5
+ ```ruby
6
+ X = IOStruct.new('LL')
7
+ io = StringIO.new('x'*1000)
8
+
9
+ X.read(io).__offset # 0
10
+ X.read(io).__offset # 8
11
+ X.read(io).__offset # 16
12
+
13
+ X.read('abcd').__offset # nil
14
+ ```
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- iostruct (0.1.0)
4
+ iostruct (0.3.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module IOStruct
4
- VERSION = "0.1.3"
4
+ VERSION = "0.3.0"
5
5
  end
data/lib/iostruct.rb CHANGED
@@ -23,6 +23,9 @@ module IOStruct
23
23
 
24
24
  'A' => [1, String ], # arbitrary binary string (remove trailing nulls and ASCII spaces)
25
25
  'a' => [1, String ], # arbitrary binary string
26
+ 'Z' => [1, String ], # arbitrary binary string (remove trailing nulls)
27
+ 'H' => [1, String ], # hex string (high nibble first)
28
+ 'h' => [1, String ], # hex string (low nibble first)
26
29
 
27
30
  'D' => [8, Float ], # double-precision, native format
28
31
  'd' => [8, Float ],
@@ -38,9 +41,10 @@ module IOStruct
38
41
 
39
42
  FieldInfo = Struct.new :type, :size, :offset
40
43
 
41
- def self.new fmt, *names, inspect: :hex
44
+ def self.new fmt, *names, inspect: :hex, **renames
42
45
  fields, size = parse_format(fmt, names)
43
- names = auto_names(fields, size) if names.empty?
46
+ names = auto_names(fields, size) if names.empty?
47
+ names.map!{ |n| renames[n] || n } if renames.any?
44
48
 
45
49
  Struct.new( *names ).tap do |x|
46
50
  x.const_set 'FIELDS', names.zip(fields).to_h
@@ -59,9 +63,13 @@ module IOStruct
59
63
  size, klass = FMTSPEC[type] || raise("Unknown field type #{type.inspect}")
60
64
  len = len.empty? ? 1 : len.to_i
61
65
  case type
62
- when 'A', 'a', 'x'
66
+ when 'A', 'a', 'x', 'Z'
63
67
  fields << FieldInfo.new(klass, size*len, offset) if klass
64
68
  offset += len
69
+ when 'H', 'h'
70
+ # XXX ruby's String#unpack length for hex strings is in characters, not bytes, i.e. "x".unpack("H2") => ["78"]
71
+ fields << FieldInfo.new(klass, size*len/2, offset) if klass
72
+ offset += len/2
65
73
  else
66
74
  len.times do |i|
67
75
  fields << FieldInfo.new(klass, size, offset)
@@ -86,23 +94,27 @@ module IOStruct
86
94
  module ClassMethods
87
95
  # src can be IO or String, or anything that responds to :read or :unpack
88
96
  def read src, size = nil
97
+ pos = nil
89
98
  size ||= const_get 'SIZE'
90
99
  data =
91
100
  if src.respond_to?(:read)
101
+ pos = src.tell
92
102
  src.read(size).to_s
93
103
  elsif src.respond_to?(:unpack)
94
104
  src
95
105
  else
96
106
  raise "[?] don't know how to read from #{src.inspect}"
97
107
  end
98
- if data.size < size
99
- $stderr.puts "[!] #{self.to_s} want #{size} bytes, got #{data.size}"
100
- end
101
- new(*data.unpack(const_get('FORMAT')))
108
+ # if data.size < size
109
+ # $stderr.puts "[!] #{self.to_s} want #{size} bytes, got #{data.size}"
110
+ # end
111
+ new(*data.unpack(const_get('FORMAT'))).tap{ |x| x.__offset = pos }
102
112
  end
103
113
  end # ClassMethods
104
114
 
105
115
  module InstanceMethods
116
+ attr_accessor :__offset
117
+
106
118
  def pack
107
119
  to_a.pack self.class.const_get('FORMAT')
108
120
  end
@@ -130,7 +142,7 @@ module IOStruct
130
142
 
131
143
  module HexInspect
132
144
  def to_s
133
- s = "<#{self.class.to_s} " + to_h.map do |k, v|
145
+ "<#{self.class.to_s} " + to_h.map do |k, v|
134
146
  if v.is_a?(Integer) && v > 9
135
147
  "#{k}=0x%x" % v
136
148
  else
@@ -152,7 +164,7 @@ module IOStruct
152
164
  end
153
165
  "#{name}=#{fmt}"
154
166
  end.join(' ') + ">"
155
- sprintf @fmtstr_tbl, *to_a.map{ |v| v.is_a?(String) ? v.inspect : v }
167
+ sprintf @fmtstr_tbl, *to_a.map{ |v| v.is_a?(String) ? v.inspect : (v||0) } # "||0" to avoid "`sprintf': can't convert nil into Integer" error
156
168
  end
157
169
 
158
170
  def inspect
@@ -59,6 +59,20 @@ describe IOStruct do
59
59
  expect(x.y).to eq a[1]
60
60
  end
61
61
 
62
+ it "unpacks hex-string (H)" do
63
+ data = "1234"
64
+ struct = IOStruct.new('H8', :x).read(data)
65
+ expect(struct.x).to eq "31323334"
66
+ expect(struct.pack).to eq data
67
+ end
68
+
69
+ it "unpacks reverse-nibbled hex-string (h)" do
70
+ data = "1234"
71
+ struct = IOStruct.new('h8', :x).read(data)
72
+ expect(struct.x).to eq "13233343"
73
+ expect(struct.pack).to eq data
74
+ end
75
+
62
76
  ['n', 'N', 'S>', 'L>', 'I>'].each do |fmt|
63
77
  it "unpacks unsigned big-endian '#{fmt}'" do
64
78
  a = [12345]
@@ -82,4 +96,27 @@ describe IOStruct do
82
96
  it "throws exception on unknown format" do
83
97
  expect { IOStruct.new('K', :x) }.to raise_error('Unknown field type "K"')
84
98
  end
99
+
100
+ context '__offset field' do
101
+ let(:data) { 0x100.times.to_a.pack('L*') }
102
+ let(:io) { StringIO.new(data) }
103
+ let(:struct) { IOStruct.new('LLLL', :a, :b, :c, :d) }
104
+
105
+ context 'when src is an IO' do
106
+ it 'is set to the current IO position' do
107
+ a = []
108
+ while !io.eof?
109
+ a << struct.read(io)
110
+ end
111
+ expect(a.map(&:__offset)).to eq (0...0x400).step(0x10).to_a
112
+ end
113
+ end
114
+
115
+ context 'when src is a string' do
116
+ it 'is nil' do
117
+ x = struct.read(data)
118
+ expect(x.__offset).to be_nil
119
+ end
120
+ end
121
+ end
85
122
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: iostruct
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrey "Zed" Zaikin
@@ -60,6 +60,7 @@ extra_rdoc_files: []
60
60
  files:
61
61
  - ".gitignore"
62
62
  - ".rspec"
63
+ - CHANGELOG.md
63
64
  - Gemfile
64
65
  - Gemfile.lock
65
66
  - LICENSE.txt
@@ -89,7 +90,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
89
90
  - !ruby/object:Gem::Version
90
91
  version: '0'
91
92
  requirements: []
92
- rubygems_version: 3.5.3
93
+ rubygems_version: 3.5.22
93
94
  signing_key:
94
95
  specification_version: 4
95
96
  summary: A Struct that can read/write itself from/to IO-like objects