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 +4 -4
- data/CHANGELOG.md +14 -0
- data/Gemfile.lock +1 -1
- data/lib/iostruct/version.rb +1 -1
- data/lib/iostruct.rb +21 -9
- data/spec/iostruct_spec.rb +37 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4a17b782202e0503788fd3b16dffdebddecd62becb30c52ba28161ebde1e300d
|
4
|
+
data.tar.gz: d42484281a468a378902d4589b67201ad4f134660559766690e48849b9f2a46c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: db34080f66b2815628e20a6e7b9331089d55a011dd35ae90b9e8f7758cb97332fad7b9caadfcc977a93e104cb0d9b9e3c932e82a2070e54205ba3bb7a52337d4
|
7
|
+
data.tar.gz: d25f3804da77a52630bb4492548c3ab71124c06610558dde277ea2c769a826ce8a720a5df99b2ab3691791b090f6ec4774b65e8072117383ab389e4095451a0f
|
data/CHANGELOG.md
ADDED
data/Gemfile.lock
CHANGED
data/lib/iostruct/version.rb
CHANGED
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
|
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
|
-
|
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
|
data/spec/iostruct_spec.rb
CHANGED
@@ -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.
|
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.
|
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
|