iostruct 0.1.3 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|