iostruct 0.0.4 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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: []