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