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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 78f534f107abf828f64d62134f93a553cb5af56af1860c338e0844b49cb482ba
4
- data.tar.gz: cdd0dfc48cf8a171bfcfc25fbe630182424008376e245b6a4816b12ed183940e
3
+ metadata.gz: 9933717b7fe8a0b269d297587f3c15c53896c71c15ace8151bea990a3e65b45f
4
+ data.tar.gz: c4953e161f96a2a02e1fef1544e80d049ea2c1b759f2d782eb4c482edb758796
5
5
  SHA512:
6
- metadata.gz: 534ccfd92eadd911de930d08cc787dab929d0a2af5210da3700b5f6ea98f444896d9cdc674b657216f03da753d902acf80a55ae21cb4943b34fd7e065ab8a0af
7
- data.tar.gz: 660427b2d4652c94791a8552dd2542614fd6b7e04a895b0ab7d1ffeeabfc508b77c02d61788bd1a9a46f9c2880257cddf274297885e4b0c11d8e6f141632a518
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.5)
4
+ iostruct (0.1.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
8
8
  specs:
9
- rake (13.0.6)
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.1.4
35
+ 2.5.6
data/iostruct.gemspec CHANGED
@@ -19,5 +19,6 @@ Gem::Specification.new do |s|
19
19
 
20
20
  s.add_development_dependency "bundler"
21
21
  s.add_development_dependency "rake"
22
+ s.add_development_dependency "rspec"
22
23
  end
23
24
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module IOStruct
4
- VERSION = "0.0.5"
4
+ VERSION = "0.1.0"
5
5
  end
data/lib/iostruct.rb CHANGED
@@ -3,55 +3,86 @@
3
3
  module IOStruct
4
4
 
5
5
  # https://apidock.com/ruby/String/unpack
6
- FIELD_SIZES = {
7
- 'C' => 1, # Integer | 8-bit unsigned (unsigned char)
8
- 'S' => 2, # Integer | 16-bit unsigned, native endian (uint16_t)
9
- 'L' => 4, # Integer | 32-bit unsigned, native endian (uint32_t)
10
- 'Q' => 8, # Integer | 64-bit unsigned, native endian (uint64_t)
11
- 'c' => 1, # Integer | 8-bit signed (signed char)
12
- 's' => 2, # Integer | 16-bit signed, native endian (int16_t)
13
- 'l' => 4, # Integer | 32-bit signed, native endian (int32_t)
14
- 'q' => 8, # Integer | 64-bit signed, native endian (int64_t)
15
-
16
- 'n' => 2, # Integer | 16-bit unsigned, network (big-endian) byte order
17
- 'N' => 4, # Integer | 32-bit unsigned, network (big-endian) byte order
18
- 'v' => 2, # Integer | 16-bit unsigned, VAX (little-endian) byte order
19
- 'V' => 4, # Integer | 32-bit unsigned, VAX (little-endian) byte order
20
-
21
- 'A' => 1, # String | arbitrary binary string (remove trailing nulls and ASCII spaces)
22
- 'a' => 1, # String | arbitrary binary string
23
-
24
- 'D' => 8, # Float | double-precision, native format
25
- 'd' => 8,
26
- 'F' => 4, # Float | single-precision, native format
27
- 'f' => 4,
28
- 'E' => 8, # Float | double-precision, little-endian byte order
29
- 'e' => 4, # Float | single-precision, little-endian byte order
30
- 'G' => 8, # Float | double-precision, network (big-endian) byte order
31
- 'g' => 4, # Float | single-precision, network (big-endian) byte order
32
-
33
- 'x' => 1, # --- | skip forward one byte
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
- def self.new fmt, *args
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
- Struct.new( *args ).tap do |x|
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
@@ -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
- it "unpacks big-endian" do
31
- a = [12345, 56789]
32
- data = a.pack('nN')
33
- x = IOStruct.new('nN', :x, :y).read(data)
34
- expect(x.x).to eq a[0]
35
- expect(x.y).to eq a[1]
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
- it "unpacks little-endian" do
39
- a = [12345, 56789]
40
- data = a.pack('vV')
41
- x = IOStruct.new('vV', :x, :y).read(data)
42
- expect(x.x).to eq a[0]
43
- expect(x.y).to eq a[1]
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 fmt "K"')
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.5
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.3.7
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