iostruct 0.0.5 → 0.1.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/Gemfile.lock +18 -3
- data/iostruct.gemspec +1 -0
- data/lib/iostruct/version.rb +1 -1
- data/lib/iostruct.rb +89 -40
- data/spec/iostruct_spec.rb +26 -17
- metadata +16 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9933717b7fe8a0b269d297587f3c15c53896c71c15ace8151bea990a3e65b45f
|
4
|
+
data.tar.gz: c4953e161f96a2a02e1fef1544e80d049ea2c1b759f2d782eb4c482edb758796
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cae61a11f83095187649f1204e07a020315ea5ce5f1276e1b082f8dae5f75c77d82a69de2cb25b775cfd39bae8fee497ea1dc65562e60618450b78069d2cd132
|
7
|
+
data.tar.gz: 20f25a1f518db2ed314f52d4603617a98ff4c93081be83f02f6e053ffb09917dbb29306d0e56ce695aa54ea0b6b3720f0a60bf52614db0117710c7be480d268f
|
data/Gemfile.lock
CHANGED
@@ -1,12 +1,26 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
iostruct (0.0
|
4
|
+
iostruct (0.1.0)
|
5
5
|
|
6
6
|
GEM
|
7
7
|
remote: https://rubygems.org/
|
8
8
|
specs:
|
9
|
-
|
9
|
+
diff-lcs (1.5.1)
|
10
|
+
rake (13.2.1)
|
11
|
+
rspec (3.13.0)
|
12
|
+
rspec-core (~> 3.13.0)
|
13
|
+
rspec-expectations (~> 3.13.0)
|
14
|
+
rspec-mocks (~> 3.13.0)
|
15
|
+
rspec-core (3.13.0)
|
16
|
+
rspec-support (~> 3.13.0)
|
17
|
+
rspec-expectations (3.13.0)
|
18
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
19
|
+
rspec-support (~> 3.13.0)
|
20
|
+
rspec-mocks (3.13.1)
|
21
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
22
|
+
rspec-support (~> 3.13.0)
|
23
|
+
rspec-support (3.13.1)
|
10
24
|
|
11
25
|
PLATFORMS
|
12
26
|
ruby
|
@@ -15,6 +29,7 @@ DEPENDENCIES
|
|
15
29
|
bundler
|
16
30
|
iostruct!
|
17
31
|
rake
|
32
|
+
rspec
|
18
33
|
|
19
34
|
BUNDLED WITH
|
20
|
-
2.
|
35
|
+
2.5.6
|
data/iostruct.gemspec
CHANGED
data/lib/iostruct/version.rb
CHANGED
data/lib/iostruct.rb
CHANGED
@@ -3,55 +3,86 @@
|
|
3
3
|
module IOStruct
|
4
4
|
|
5
5
|
# https://apidock.com/ruby/String/unpack
|
6
|
-
|
7
|
-
'C' => 1,
|
8
|
-
'S' => 2,
|
9
|
-
'
|
10
|
-
'
|
11
|
-
'
|
12
|
-
|
13
|
-
'
|
14
|
-
'
|
15
|
-
|
16
|
-
'
|
17
|
-
'
|
18
|
-
|
19
|
-
'
|
20
|
-
|
21
|
-
'
|
22
|
-
'
|
23
|
-
|
24
|
-
'
|
25
|
-
'
|
26
|
-
|
27
|
-
'
|
28
|
-
'
|
29
|
-
'
|
30
|
-
'
|
31
|
-
'
|
32
|
-
|
33
|
-
'
|
6
|
+
FMTSPEC = {
|
7
|
+
'C' => [1, Integer ], # 8-bit unsigned (unsigned char)
|
8
|
+
'S' => [2, Integer ], # 16-bit unsigned, native endian (uint16_t)
|
9
|
+
'I' => [4, Integer ], # 32-bit unsigned, native endian (uint32_t)
|
10
|
+
'L' => [4, Integer ], # 32-bit unsigned, native endian (uint32_t)
|
11
|
+
'Q' => [8, Integer ], # 64-bit unsigned, native endian (uint64_t)
|
12
|
+
|
13
|
+
'c' => [1, Integer ], # 8-bit signed (signed char)
|
14
|
+
's' => [2, Integer ], # 16-bit signed, native endian (int16_t)
|
15
|
+
'i' => [4, Integer ], # 32-bit signed, native endian (int32_t)
|
16
|
+
'l' => [4, Integer ], # 32-bit signed, native endian (int32_t)
|
17
|
+
'q' => [8, Integer ], # 64-bit signed, native endian (int64_t)
|
18
|
+
|
19
|
+
'n' => [2, Integer ], # 16-bit unsigned, network (big-endian) byte order
|
20
|
+
'N' => [4, Integer ], # 32-bit unsigned, network (big-endian) byte order
|
21
|
+
'v' => [2, Integer ], # 16-bit unsigned, VAX (little-endian) byte order
|
22
|
+
'V' => [4, Integer ], # 32-bit unsigned, VAX (little-endian) byte order
|
23
|
+
|
24
|
+
'A' => [1, String ], # arbitrary binary string (remove trailing nulls and ASCII spaces)
|
25
|
+
'a' => [1, String ], # arbitrary binary string
|
26
|
+
|
27
|
+
'D' => [8, Float ], # double-precision, native format
|
28
|
+
'd' => [8, Float ],
|
29
|
+
'F' => [4, Float ], # single-precision, native format
|
30
|
+
'f' => [4, Float ],
|
31
|
+
'E' => [8, Float ], # double-precision, little-endian byte order
|
32
|
+
'e' => [4, Float ], # single-precision, little-endian byte order
|
33
|
+
'G' => [8, Float ], # double-precision, network (big-endian) byte order
|
34
|
+
'g' => [4, Float ], # single-precision, network (big-endian) byte order
|
35
|
+
|
36
|
+
'x' => [1, nil ], # skip forward one byte
|
34
37
|
}.freeze
|
35
38
|
|
36
|
-
|
37
|
-
size = fmt.scan(/([a-z])(\d*)/i).map do |f,len|
|
38
|
-
if (field_size = FIELD_SIZES[f])
|
39
|
-
[len.to_i, 1].max * field_size
|
40
|
-
else
|
41
|
-
raise "Unknown fmt #{f.inspect}"
|
42
|
-
end
|
43
|
-
end.inject(&:+)
|
39
|
+
FieldInfo = Struct.new :type, :size, :offset
|
44
40
|
|
45
|
-
|
41
|
+
def self.new fmt, *names, inspect: :hex
|
42
|
+
fields, size = parse_format(fmt, names)
|
43
|
+
names = auto_names(fields, size) if names.empty?
|
44
|
+
|
45
|
+
Struct.new( *names ).tap do |x|
|
46
|
+
x.const_set 'FIELDS', names.zip(fields).to_h
|
46
47
|
x.const_set 'FORMAT', fmt
|
47
48
|
x.const_set 'SIZE', size
|
48
|
-
x.class_eval do
|
49
|
-
include InstanceMethods
|
50
|
-
end
|
51
49
|
x.extend ClassMethods
|
50
|
+
x.include InstanceMethods
|
51
|
+
x.include HexInspect if inspect == :hex
|
52
52
|
end
|
53
53
|
end # self.new
|
54
54
|
|
55
|
+
def self.parse_format(fmt, names)
|
56
|
+
offset = 0
|
57
|
+
fields = []
|
58
|
+
fmt.scan(/([a-z])(\d*)/i).map do |type,len|
|
59
|
+
size, klass = FMTSPEC[type] || raise("Unknown field type #{type.inspect}")
|
60
|
+
len = [len.to_i, 1].max
|
61
|
+
case type
|
62
|
+
when 'A', 'a', 'x'
|
63
|
+
fields << FieldInfo.new(klass, size*len, offset) if klass
|
64
|
+
offset += len
|
65
|
+
else
|
66
|
+
len.times do |i|
|
67
|
+
fields << FieldInfo.new(klass, size, offset)
|
68
|
+
offset += size
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
[fields, offset]
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.auto_names fields, size
|
76
|
+
names = []
|
77
|
+
offset = 0
|
78
|
+
fields.each do |f|
|
79
|
+
names << sprintf("f%x", offset).to_sym
|
80
|
+
offset += f.size
|
81
|
+
end
|
82
|
+
#raise "size mismatch: #{size} != #{offset}" if size != offset
|
83
|
+
names
|
84
|
+
end
|
85
|
+
|
55
86
|
module ClassMethods
|
56
87
|
# src can be IO or String, or anything that responds to :read or :unpack
|
57
88
|
def read src, size = nil
|
@@ -96,4 +127,22 @@ module IOStruct
|
|
96
127
|
end
|
97
128
|
end
|
98
129
|
end # InstanceMethods
|
130
|
+
|
131
|
+
module HexInspect
|
132
|
+
def inspect
|
133
|
+
@fmtstr ||= "<#{self.class.to_s} " + self.class.const_get('FIELDS').map do |name, f|
|
134
|
+
fmt =
|
135
|
+
case
|
136
|
+
when f.type == Integer
|
137
|
+
"%#{f.size*2}x"
|
138
|
+
when f.type == Float
|
139
|
+
"%8.3f"
|
140
|
+
else
|
141
|
+
"%s"
|
142
|
+
end
|
143
|
+
"#{name}=#{fmt}"
|
144
|
+
end.join(' ') + ">"
|
145
|
+
sprintf @fmtstr, *to_a.map{ |v| v.is_a?(String) ? v.inspect : v }
|
146
|
+
end
|
147
|
+
end
|
99
148
|
end # IOStruct
|
data/spec/iostruct_spec.rb
CHANGED
@@ -3,21 +3,26 @@ require 'stringio'
|
|
3
3
|
|
4
4
|
describe IOStruct do
|
5
5
|
describe "#read" do
|
6
|
+
let(:a) { [12345, 56789] }
|
7
|
+
let(:data) { a.pack('L2') }
|
8
|
+
|
6
9
|
it "reads from IO" do
|
7
|
-
a = [12345, 56789]
|
8
|
-
data = a.pack('L2')
|
9
10
|
x = IOStruct.new('LL', :x, :y).read(StringIO.new(data))
|
10
11
|
expect(x.x).to eq a[0]
|
11
12
|
expect(x.y).to eq a[1]
|
12
13
|
end
|
13
14
|
|
14
15
|
it "reads from String" do
|
15
|
-
a = [12345, 56789]
|
16
|
-
data = a.pack('L2')
|
17
16
|
x = IOStruct.new('LL', :x, :y).read(data)
|
18
17
|
expect(x.x).to eq a[0]
|
19
18
|
expect(x.y).to eq a[1]
|
20
19
|
end
|
20
|
+
|
21
|
+
it "creates a new instance of a subclass" do
|
22
|
+
klass = Class.new( IOStruct.new('LL', :x, :y) )
|
23
|
+
x = klass.read(data)
|
24
|
+
expect(x).to be_a klass
|
25
|
+
end
|
21
26
|
end
|
22
27
|
|
23
28
|
it "skips on 'x'" do
|
@@ -27,23 +32,27 @@ describe IOStruct do
|
|
27
32
|
expect(x.y).to eq a[1]
|
28
33
|
end
|
29
34
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
35
|
+
['n', 'N', 'S>', 'L>', 'I>'].each do |fmt|
|
36
|
+
it "unpacks unsigned big-endian '#{fmt}'" do
|
37
|
+
a = [12345]
|
38
|
+
data = a.pack(fmt)
|
39
|
+
x = IOStruct.new(fmt, :x).read(data)
|
40
|
+
expect(x.x).to eq a[0]
|
41
|
+
expect(x.pack).to eq data
|
42
|
+
end
|
36
43
|
end
|
37
44
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
45
|
+
['v', 'V', 'S<', 'L<', 'I<'].each do |fmt|
|
46
|
+
it "unpacks unsigned little-endian '#{fmt}'" do
|
47
|
+
a = [12345]
|
48
|
+
data = a.pack(fmt)
|
49
|
+
x = IOStruct.new(fmt, :x).read(data)
|
50
|
+
expect(x.x).to eq a[0]
|
51
|
+
expect(x.pack).to eq data
|
52
|
+
end
|
44
53
|
end
|
45
54
|
|
46
55
|
it "throws exception on unknown format" do
|
47
|
-
expect { IOStruct.new('K', :x) }.to raise_error('Unknown
|
56
|
+
expect { IOStruct.new('K', :x) }.to raise_error('Unknown field type "K"')
|
48
57
|
end
|
49
58
|
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.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrey "Zed" Zaikin
|
@@ -38,6 +38,20 @@ dependencies:
|
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
41
55
|
description:
|
42
56
|
email: zed.0xff@gmail.com
|
43
57
|
executables: []
|
@@ -75,7 +89,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
75
89
|
- !ruby/object:Gem::Version
|
76
90
|
version: '0'
|
77
91
|
requirements: []
|
78
|
-
rubygems_version: 3.
|
92
|
+
rubygems_version: 3.5.6
|
79
93
|
signing_key:
|
80
94
|
specification_version: 4
|
81
95
|
summary: A Struct that can read/write itself from/to IO-like objects
|