iostruct 0.0.4 → 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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 9933717b7fe8a0b269d297587f3c15c53896c71c15ace8151bea990a3e65b45f
4
+ data.tar.gz: c4953e161f96a2a02e1fef1544e80d049ea2c1b759f2d782eb4c482edb758796
5
+ SHA512:
6
+ metadata.gz: cae61a11f83095187649f1204e07a020315ea5ce5f1276e1b082f8dae5f75c77d82a69de2cb25b775cfd39bae8fee497ea1dc65562e60618450b78069d2cd132
7
+ data.tar.gz: 20f25a1f518db2ed314f52d4603617a98ff4c93081be83f02f6e053ffb09917dbb29306d0e56ce695aa54ea0b6b3720f0a60bf52614db0117710c7be480d268f
data/Gemfile.lock CHANGED
@@ -1,17 +1,35 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- iostruct (0.0.2)
4
+ iostruct (0.1.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
8
8
  specs:
9
- rake (10.0.3)
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
13
27
 
14
28
  DEPENDENCIES
15
- bundler (~> 1.3)
29
+ bundler
16
30
  iostruct!
17
31
  rake
32
+ rspec
33
+
34
+ BUNDLED WITH
35
+ 2.5.6
data/iostruct.gemspec CHANGED
@@ -17,7 +17,8 @@ Gem::Specification.new do |s|
17
17
  s.require_paths = ["lib"]
18
18
  s.summary = "A Struct that can read/write itself from/to IO-like objects"
19
19
 
20
- s.add_development_dependency "bundler", "~> 1.3"
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,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module IOStruct
2
- VERSION = "0.0.4"
4
+ VERSION = "0.1.0"
3
5
  end
data/lib/iostruct.rb CHANGED
@@ -1,26 +1,88 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module IOStruct
2
- def self.new fmt, *args
3
- size = fmt.scan(/([a-z])(\d*)/i).map do |f,len|
4
- [len.to_i, 1].max *
5
- case f
6
- when /[AaCcx]/ then 1
7
- when 'v' then 2
8
- when 'V','l','L' then 4
9
- when 'Q' then 8
10
- else raise "unknown fmt #{f.inspect}"
11
- end
12
- end.inject(&:+)
13
4
 
14
- Struct.new( *args ).tap do |x|
5
+ # https://apidock.com/ruby/String/unpack
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
37
+ }.freeze
38
+
39
+ FieldInfo = Struct.new :type, :size, :offset
40
+
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
15
47
  x.const_set 'FORMAT', fmt
16
48
  x.const_set 'SIZE', size
17
- x.class_eval do
18
- include InstanceMethods
19
- end
20
49
  x.extend ClassMethods
50
+ x.include InstanceMethods
51
+ x.include HexInspect if inspect == :hex
21
52
  end
22
53
  end # self.new
23
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
+
24
86
  module ClassMethods
25
87
  # src can be IO or String, or anything that responds to :read or :unpack
26
88
  def read src, size = nil
@@ -65,4 +127,22 @@ module IOStruct
65
127
  end
66
128
  end
67
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
68
148
  end # IOStruct
@@ -3,27 +3,56 @@ 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
- x.x.should == a[0]
11
- x.y.should == a[1]
11
+ expect(x.x).to eq a[0]
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
- x.x.should == a[0]
19
- x.y.should == a[1]
17
+ expect(x.x).to eq a[0]
18
+ expect(x.y).to eq a[1]
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
20
25
  end
21
26
  end
22
27
 
23
28
  it "skips on 'x'" do
24
- a = [12345,56789]
29
+ a = [12345, 56789]
25
30
  data = a.pack('L2')
26
31
  x = IOStruct.new('x4L', :y).read(data)
27
- x.y.should == a[1]
32
+ expect(x.y).to eq a[1]
33
+ end
34
+
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
43
+ end
44
+
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
53
+ end
54
+
55
+ it "throws exception on unknown format" do
56
+ expect { IOStruct.new('K', :x) }.to raise_error('Unknown field type "K"')
28
57
  end
29
58
  end
data/spec/spec_helper.rb CHANGED
@@ -2,6 +2,5 @@ require File.join(File.dirname(__FILE__), '../lib/iostruct.rb')
2
2
 
3
3
  # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
4
4
  RSpec.configure do |config|
5
- config.treat_symbols_as_metadata_keys_with_true_values = true
6
5
  config.run_all_when_everything_filtered = true
7
6
  end
metadata CHANGED
@@ -1,12 +1,11 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: iostruct
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
5
- prerelease:
4
+ version: 0.1.0
6
5
  platform: ruby
7
6
  authors:
8
7
  - Andrey "Zed" Zaikin
9
- autorequire:
8
+ autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
11
  date: 2013-01-08 00:00:00.000000000 Z
@@ -14,43 +13,53 @@ dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: bundler
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
- - - ~>
17
+ - - ">="
20
18
  - !ruby/object:Gem::Version
21
- version: '1.3'
19
+ version: '0'
22
20
  type: :development
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
- - - ~>
24
+ - - ">="
28
25
  - !ruby/object:Gem::Version
29
- version: '1.3'
26
+ version: '0'
30
27
  - !ruby/object:Gem::Dependency
31
28
  name: rake
32
29
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
30
  requirements:
35
- - - ! '>='
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
36
46
  - !ruby/object:Gem::Version
37
47
  version: '0'
38
48
  type: :development
39
49
  prerelease: false
40
50
  version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
51
  requirements:
43
- - - ! '>='
52
+ - - ">="
44
53
  - !ruby/object:Gem::Version
45
54
  version: '0'
46
- description:
55
+ description:
47
56
  email: zed.0xff@gmail.com
48
57
  executables: []
49
58
  extensions: []
50
59
  extra_rdoc_files: []
51
60
  files:
52
- - .gitignore
53
- - .rspec
61
+ - ".gitignore"
62
+ - ".rspec"
54
63
  - Gemfile
55
64
  - Gemfile.lock
56
65
  - LICENSE.txt
@@ -64,26 +73,24 @@ files:
64
73
  homepage: http://github.com/zed-0xff/iostruct
65
74
  licenses:
66
75
  - MIT
67
- post_install_message:
76
+ metadata: {}
77
+ post_install_message:
68
78
  rdoc_options: []
69
79
  require_paths:
70
80
  - lib
71
81
  required_ruby_version: !ruby/object:Gem::Requirement
72
- none: false
73
82
  requirements:
74
- - - ! '>='
83
+ - - ">="
75
84
  - !ruby/object:Gem::Version
76
85
  version: '0'
77
86
  required_rubygems_version: !ruby/object:Gem::Requirement
78
- none: false
79
87
  requirements:
80
- - - ! '>='
88
+ - - ">="
81
89
  - !ruby/object:Gem::Version
82
90
  version: '0'
83
91
  requirements: []
84
- rubyforge_project:
85
- rubygems_version: 1.8.24
86
- signing_key:
87
- specification_version: 3
92
+ rubygems_version: 3.5.6
93
+ signing_key:
94
+ specification_version: 4
88
95
  summary: A Struct that can read/write itself from/to IO-like objects
89
96
  test_files: []