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 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