BitStructEx 0.0.54

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.
data/Rakefile ADDED
@@ -0,0 +1,53 @@
1
+
2
+ require 'rake/clean'
3
+ require 'rake/gempackagetask'
4
+ require 'rake/version'
5
+
6
+
7
+ PROJECT_NAME='BitStructEx'
8
+ PROJECT_FORGE_ID='bit-struct-ex'
9
+
10
+ SRC_DIR='src/ruby'
11
+ TEST_DIR='test/ruby'
12
+
13
+ PKG_VERSION = Rake::Version.new
14
+ PKG_FILES = FileList[
15
+ 'lib/**/*.rb',
16
+ 'src/**/*.rb',
17
+ 'test/**/*.rb',
18
+ 'Rakefile',
19
+ 'Version'
20
+ ]
21
+
22
+ CLOBBER.include 'pkg'
23
+
24
+
25
+ desc "Run all tests, update build number and build gem"
26
+ task :default => [ :run_tests, :increment_build_number, :build_gem ]
27
+
28
+ desc "Execute the unit tests using the specialized test runner"
29
+ task :run_tests do
30
+ sh "ruby -I#{TEST_DIR} #{TEST_DIR}/runner.rb --verbose=v"
31
+ end
32
+
33
+ desc "Increment the build number and upate the Version file"
34
+ task :increment_build_number => [ :run_tests ] do
35
+ src = Dir.glob "#{SRC_DIR}/**/*"
36
+ target = 'Version'
37
+ unless uptodate?( target, src )
38
+ new_version = PKG_VERSION.increment( :build )
39
+ puts "Version: #{new_version}"
40
+ end
41
+ end
42
+
43
+ desc "Build the gem package"
44
+ task :build_gem => [ :run_tests, :define_gem, :gem ]
45
+
46
+ desc "Define the Gem specification and associated task(s)"
47
+ task :define_gem do
48
+ PKG_SPEC = eval IO.read( 'GemSpec' ), binding
49
+ Rake::GemPackageTask.new( PKG_SPEC ) do |pkg|
50
+ pkg.need_zip = true
51
+ pkg.need_tar = true
52
+ end
53
+ end
data/Version ADDED
@@ -0,0 +1 @@
1
+ 0.0.54
@@ -0,0 +1,71 @@
1
+ module Rake
2
+
3
+ #
4
+ # Simple helper class for maintaining a Version number within a Rakefile.
5
+ # Provides an easy way increase the version tags. Supports purely numeric
6
+ # version tags only.
7
+ #
8
+ class Version
9
+
10
+ def self.increment( symbol )
11
+ Version.new.increment symbol
12
+ end
13
+
14
+ def initialize( config = {} )
15
+ @config = config
16
+ @config[ :version_file ] ||= 'Version'
17
+ @config[ :version_regex ] ||= /\A(\d+)\.(\d+).(\d+)\Z/
18
+ @config[ :version_tags ] ||= [ :release, :version, :build ]
19
+ @tag_values = {}
20
+ read
21
+ end
22
+
23
+ def increment( symbol )
24
+ raise "Unknown tag: #{symbol}" unless version_tags.include? symbol
25
+ read
26
+ @tag_values[ symbol ] += 1
27
+ write
28
+ current_version
29
+ end
30
+
31
+ def current_version
32
+ version_tags.map { |tag| @tag_values[ tag ] }.join( '.' )
33
+ end
34
+
35
+ alias version current_version
36
+
37
+ alias to_s version
38
+
39
+ private
40
+
41
+ def read
42
+ version = IO.read version_file
43
+ raise "Error reading #{version_file}" unless version_regex =~ version
44
+ version_tags.each_index do |idx|
45
+ @tag_values[ version_tags[ idx ] ] = Regexp.last_match[ 1 + idx ].to_i
46
+ end
47
+ self
48
+ end
49
+
50
+ def write
51
+ File.open( version_file, 'w' ) do |file|
52
+ file.write current_version
53
+ end
54
+ self
55
+ end
56
+
57
+ def version_file
58
+ @config[ :version_file ]
59
+ end
60
+
61
+ def version_regex
62
+ @config[ :version_regex ]
63
+ end
64
+
65
+ def version_tags
66
+ @config[ :version_tags ]
67
+ end
68
+
69
+ end
70
+
71
+ end
@@ -0,0 +1,202 @@
1
+
2
+ module BitStruct
3
+
4
+ # Implements access to a 'segment' of a byte string, specified by
5
+ # offset (from bit 0) and length (in bits). Supports little and big
6
+ # endian data input and output.
7
+ #
8
+ # NOTE: This is highly inefficient. If a segment is byte-aligned,
9
+ # please use the optimized ByteSlicer class.
10
+ class BitSlicer
11
+
12
+ module Endianess
13
+ LITTLE_ENDIAN = LITTLE = INTEL = 0
14
+ BIG_ENDIAN = BIG = MOTOROLA = 1
15
+ NETWORK_BYTE_ORDER = BIG_ENDIAN
16
+ end
17
+
18
+ include Endianess
19
+
20
+ def initialize( bit_offset, bit_length, endianess = MOTOROLA )
21
+ raise ArgumentError if bit_offset < 0 || bit_length < 0
22
+
23
+ @bit_offset = bit_offset
24
+ @bit_length = bit_length
25
+ @endianess = endianess
26
+ end
27
+
28
+ def data=( new_data )
29
+ raise ArgumentError if ( @bit_offset + @bit_length ) > new_data.length * 8
30
+ @data = new_data
31
+ end
32
+
33
+ def data
34
+ @data
35
+ end
36
+
37
+ def get_bytes
38
+ align_shift = required_shift
39
+ bytes = get_raw_bytes
40
+
41
+ # Step 1: Shift all bytes to the right to align bit 0
42
+ # (in case bit_offset is not on byte border).
43
+ BitSlicer.shift_bytes_right( bytes, align_shift )
44
+
45
+ # Step 2: Strip highest (unused/empty) byte if necessary
46
+ # (in case bit_length is on byte border, but bit_offset not).
47
+ data_length = length_in_bytes
48
+ while bytes.length > data_length
49
+ bytes = bytes[ 1, bytes.length - 1 ]
50
+ end
51
+
52
+ # Step 3: Apply mask to 'highest' byte
53
+ # (in case bit_length is not on byte border).
54
+ bytes[ 0 ] &= highest_byte_mask
55
+ bytes
56
+ end
57
+
58
+ def set_bytes( bytes )
59
+ align_shift = required_shift
60
+
61
+ # Step 1: Insert empty bytes to allow overflow from shifting.
62
+ data_length = insert_length_in_bytes
63
+ while bytes.length < data_length
64
+ bytes.insert 0, 0x00
65
+ end
66
+ while bytes.length > data_length
67
+ bytes = bytes[ 1, bytes.length - 1 ]
68
+ end
69
+
70
+ # Step 2: Shift all bytes to the left to align bit 0 with the 'destination postion'
71
+ # (in case bit_offset is not on byte border).
72
+ BitSlicer.shift_bytes_left( bytes, align_shift )
73
+
74
+ # Step 3: Mask out unused parts of first and last byte.
75
+ bytes[ 0 ] &= insert_first_byte_mask
76
+ bytes[ bytes.length - 1 ] &= insert_last_byte_mask
77
+
78
+ # Step 4: Merge existing data into first and last byte.
79
+ bytes[ 0 ] |= masked_first_byte
80
+ bytes[ bytes.length - 1 ] |= masked_last_byte
81
+
82
+ # Step 5: Write back the byte data.
83
+ set_raw_bytes bytes
84
+
85
+ data
86
+ end
87
+
88
+ #private
89
+
90
+ def get_data( idx )
91
+ return @data[ idx ] if @endianess == MOTOROLA
92
+ return @data[ -idx ] if @endianess == INTEL
93
+ end
94
+
95
+ def set_data( idx, val )
96
+ @data[ idx ] = val if @endianess == MOTOROLA
97
+ @data[ -idx ] = val if @endianess == INTEL
98
+ end
99
+
100
+ def first_byte
101
+ data.length - ( ( @bit_offset + @bit_length ) / 8.0 ).ceil
102
+ end
103
+
104
+ def last_byte
105
+ data.length - 1 - @bit_offset / 8
106
+ end
107
+
108
+ def length_in_bytes
109
+ ( @bit_length / 8.0 ).ceil
110
+ end
111
+
112
+ def insert_length_in_bytes
113
+ ( ( ( @bit_offset & 7 ) + @bit_length ) / 8.0 ).ceil
114
+ end
115
+
116
+ def masked_first_byte
117
+ get_data( first_byte ) & merge_first_byte_mask
118
+ end
119
+
120
+ def masked_last_byte
121
+ get_data( last_byte ) & merge_last_byte_mask
122
+ end
123
+
124
+ def required_shift
125
+ @bit_offset & 7
126
+ end
127
+
128
+ def get_raw_bytes
129
+ raw_bytes = []
130
+ first_byte.upto( last_byte ) do |idx|
131
+ raw_bytes << get_data( idx )
132
+ end
133
+ raw_bytes
134
+ end
135
+
136
+ def set_raw_bytes( raw_bytes )
137
+ first_byte.upto( last_byte ) do |idx|
138
+ set_data idx, raw_bytes[ idx - first_byte ]
139
+ end
140
+ end
141
+
142
+ def highest_byte_mask
143
+ mask_bits = ( ( @bit_length ) & 7 )
144
+ mask_bits = 8 if mask_bits == 0
145
+ ( 2 ** mask_bits ) - 1
146
+ end
147
+
148
+ def insert_first_byte_mask
149
+ mask_bits = ( ( @bit_offset + @bit_length ) & 7 )
150
+ mask_bits = 8 if mask_bits == 0
151
+ ( 2 ** mask_bits ) - 1
152
+ end
153
+
154
+ def insert_last_byte_mask
155
+ mask_bits = 0xFF
156
+ ( mask_bits << ( @bit_offset & 7 ) ) & 0xFF
157
+ end
158
+
159
+ def merge_first_byte_mask
160
+ 255 - insert_first_byte_mask
161
+ end
162
+
163
+ def merge_last_byte_mask
164
+ 255 - insert_last_byte_mask
165
+ end
166
+
167
+ def self.right_shifted( before, subject, bits )
168
+ ( ( before << 8 | subject ) >> bits ) & 0xFF
169
+ end
170
+
171
+ def self.left_shifted( subject, after, bits )
172
+ ( ( ( subject << 8 | after ) << bits ) >> 8 ) & 0xFF
173
+ end
174
+
175
+ def self.shift_bytes_right( bytes, bit_shift_count )
176
+ return if bit_shift_count == 0
177
+ raise ArgumentError if bit_shift_count < 0 || bit_shift_count > 8
178
+
179
+ from = bytes.length - 1
180
+ from.downto( 0 ) do |idx|
181
+ before = idx > 0 ? bytes[ idx - 1 ] : 0
182
+ bytes[ idx ] = BitSlicer.right_shifted( before, bytes[ idx ], bit_shift_count )
183
+ end
184
+
185
+ bytes
186
+ end
187
+
188
+ def self.shift_bytes_left( bytes, bit_shift_count )
189
+ return if bit_shift_count == 0
190
+ raise ArgumentError if bit_shift_count < 0 || bit_shift_count > 8
191
+
192
+ 0.upto( bytes.length - 1 ) do |idx|
193
+ after = bytes[ idx + 1 ] || 0
194
+ bytes[ idx ] = BitSlicer.left_shifted( bytes[ idx ], after, bit_shift_count )
195
+ end
196
+
197
+ bytes
198
+ end
199
+
200
+ end
201
+
202
+ end
@@ -0,0 +1,18 @@
1
+
2
+ module BitStruct
3
+
4
+ class Field
5
+
6
+ attr_reader :name
7
+ attr_reader :length
8
+ attr_reader :description
9
+
10
+ def initialize( name, length, description )
11
+ @name = name
12
+ @length = length
13
+ @description = description
14
+ end
15
+
16
+ end
17
+
18
+ end
@@ -0,0 +1,32 @@
1
+
2
+ require 'bit_struct/field'
3
+
4
+ module BitStruct
5
+
6
+ class NestedField < Field
7
+
8
+ def initialize( name, nested_class, description = nil )
9
+ super name, nil, description
10
+ @nested_class = nested_class
11
+ end
12
+
13
+ def length
14
+ @nested_class.length
15
+ end
16
+
17
+ def read( slicer )
18
+ bytes = slicer.get_bytes
19
+ @nested_class.new bytes
20
+ end
21
+
22
+ def write( slicer, new_value )
23
+ # TODO: What should be allowed here?
24
+ # TODO: new_value.is_a? String
25
+ # TODO: new_value.is_a? Array
26
+ # TODO: new_value.is_a? StructBase
27
+ raise "NYI"
28
+ end
29
+
30
+ end
31
+
32
+ end
@@ -0,0 +1,124 @@
1
+
2
+ require 'bit_struct/bit_slicer'
3
+ require 'bit_struct/field'
4
+
5
+ # The BitStruct module primarily provides the StructBase class, which in turn
6
+ # allows the definition of bit-based structures that can be used to access
7
+ # byte data on a bit offset / length manner.
8
+ #
9
+ # Example:
10
+ #
11
+ # class Flags < StructBase
12
+ # unsigned :direction, 4
13
+ # unsigned :multiplier, 2
14
+ # unsigned :offset, 2
15
+ # end
16
+ #
17
+ # class Entry < StructBase
18
+ # unsigned :offset, 4
19
+ # nested :flags, Flags
20
+ # unsigned :address, 24
21
+ # unsigned :cache_id, 16
22
+ # end
23
+ #
24
+ # In contrast to the already available
25
+ # http://raa.ruby-lang.org/project/bit-struct/ implementation, BitStructEx
26
+ # allows nested structures which are not aligned on byte boundaries.
27
+ #
28
+ # Author:: Daniel Lukic (mailto:bitstructex@berlinfactor.com)
29
+ # Copyright:: Copyright (c) 2005 The.Berlin.Factor
30
+ # License:: Distributes under the GPL
31
+ #
32
+ module BitStruct
33
+
34
+ # Define a subclass of StructBase and use the available field types (at
35
+ # least "nested" and "unsigned" are available) to define the bit-based
36
+ # structure like this:
37
+ #
38
+ # * unsigned :name_of_field, length_of_field_in_bits
39
+ # * nested :name_of_nested_field, class_of_nested_field
40
+ #
41
+ # Please see the provided examples in the samples folder for.. well..
42
+ # examples.
43
+ #
44
+ class StructBase < String
45
+
46
+ def initialize( data = nil )
47
+ @data = data
48
+ end
49
+
50
+ def data
51
+ @data
52
+ end
53
+
54
+ class << self
55
+
56
+ def fields
57
+ @fields ||= []
58
+ end
59
+
60
+ def length
61
+ @fields.inject( 0 ) { |l,f| l + f.length }
62
+ end
63
+
64
+ private
65
+
66
+ def add_field( field )
67
+ context = self
68
+
69
+ define_method "#{field.name}" do
70
+ slicer = context.send :get_slicer_for, field
71
+ slicer.data = @data
72
+ field.send :read, slicer
73
+ end
74
+ define_method "#{field.name}=" do |val|
75
+ slicer = context.send :get_slicer_for, field
76
+ slicer.data = @data
77
+ field.send :write, slicer, val
78
+ end
79
+
80
+ fields << field
81
+ end
82
+
83
+ def get_slicer_for( field )
84
+ unless slicers[ field ]
85
+ bit_offset, bit_length = get_field_spec field
86
+ slicers[ field ] = BitSlicer.new bit_offset, bit_length
87
+ end
88
+ slicers[ field ]
89
+ end
90
+
91
+ def get_field_spec( field )
92
+ index = fields.index field
93
+ offset = 0
94
+ 0.upto( index - 1 ) { |idx| offset += fields[ idx ].length }
95
+ return offset, field.length
96
+ end
97
+
98
+ def method_missing( symbol, *args )
99
+ require "bit_struct/#{symbol.id2name}"
100
+
101
+ class_name = make_class_name symbol
102
+ clazz = const_get class_name.to_sym
103
+ self.class.send :define_method, symbol do |*args|
104
+ add_field clazz.new( *args )
105
+ end
106
+
107
+ send symbol, *args
108
+ end
109
+
110
+ def make_class_name( symbol )
111
+ words = symbol.id2name.split '_'
112
+ words.map! { |w| w.capitalize }
113
+ words.join + 'Field'
114
+ end
115
+
116
+ def slicers
117
+ @slicers ||= {}
118
+ end
119
+
120
+ end
121
+
122
+ end
123
+
124
+ end
@@ -0,0 +1,27 @@
1
+
2
+ require 'bit_struct/field'
3
+
4
+ module BitStruct
5
+
6
+ class UnsignedField < Field
7
+
8
+ def initialize( name, length, description = nil )
9
+ super name, length, description
10
+ end
11
+
12
+ def read( slicer )
13
+ slicer.get_bytes.inject( 0 ) { |r,b| ( r << 8 ) + b }
14
+ end
15
+
16
+ def write( slicer, new_value )
17
+ bytes = []
18
+ while new_value > 0
19
+ bytes.insert 0, ( new_value & 0xFF )
20
+ new_value >>= 8
21
+ end
22
+ slicer.set_bytes bytes
23
+ end
24
+
25
+ end
26
+
27
+ end
@@ -0,0 +1,299 @@
1
+
2
+ require 'test/unit'
3
+
4
+ require 'bit_struct/bit_slicer'
5
+
6
+
7
+
8
+ class TestBitSlicer < Test::Unit::TestCase
9
+
10
+ def test_endianess
11
+ slicer = BitStruct::BitSlicer.new 0, 16, BitStruct::BitSlicer::MOTOROLA
12
+ slicer.data = test_data
13
+ assert_equal '0x01 0x23 0x45 0x67 0x89', slicer.data.hexify
14
+ end
15
+
16
+ def test_right_shifted
17
+ assert_equal '34', BitStruct::BitSlicer.right_shifted( 0x12, 0x34, 0 ).hexify
18
+ assert_equal '23', BitStruct::BitSlicer.right_shifted( 0x12, 0x34, 4 ).hexify
19
+ assert_equal '12', BitStruct::BitSlicer.right_shifted( 0x12, 0x34, 8 ).hexify
20
+ end
21
+
22
+ def test_left_shifted
23
+ assert_equal '12', BitStruct::BitSlicer.left_shifted( 0x12, 0x34, 0 ).hexify
24
+ assert_equal '23', BitStruct::BitSlicer.left_shifted( 0x12, 0x34, 4 ).hexify
25
+ assert_equal '34', BitStruct::BitSlicer.left_shifted( 0x12, 0x34, 8 ).hexify
26
+
27
+ assert_equal '11000011', BitStruct::BitSlicer.left_shifted( 0xF0, 0xF0, 2 ).binarize
28
+ end
29
+
30
+ def test_first_byte
31
+ assert_equal 4, slicer( 0, 1 ).first_byte
32
+ assert_equal 4, slicer( 0, 7 ).first_byte
33
+ assert_equal 4, slicer( 0, 8 ).first_byte
34
+ assert_equal 3, slicer( 0, 12 ).first_byte
35
+ assert_equal 3, slicer( 0, 16 ).first_byte
36
+ assert_equal 3, slicer( 2, 8 ).first_byte
37
+ assert_equal 4, slicer( 2, 2 ).first_byte
38
+ assert_equal 2, slicer( 2, 16 ).first_byte
39
+ assert_equal 2, slicer( 4, 16 ).first_byte
40
+ assert_equal 2, slicer( 6, 16 ).first_byte
41
+ assert_equal 2, slicer( 7, 16 ).first_byte
42
+ assert_equal 2, slicer( 8, 16 ).first_byte
43
+ assert_equal 1, slicer( 9, 16 ).first_byte
44
+ assert_equal 1, slicer( 15, 16 ).first_byte
45
+ assert_equal 1, slicer( 16, 16 ).first_byte
46
+ assert_equal 0, slicer( 17, 16 ).first_byte
47
+ end
48
+
49
+ def test_last_byte
50
+ assert_equal 4, slicer( 0, 16 ).last_byte
51
+ assert_equal 4, slicer( 2, 16 ).last_byte
52
+ assert_equal 4, slicer( 4, 16 ).last_byte
53
+ assert_equal 4, slicer( 6, 16 ).last_byte
54
+ assert_equal 4, slicer( 7, 16 ).last_byte
55
+ assert_equal 3, slicer( 8, 16 ).last_byte
56
+ assert_equal 3, slicer( 9, 16 ).last_byte
57
+ assert_equal 3, slicer( 15, 16 ).last_byte
58
+ assert_equal 2, slicer( 16, 16 ).last_byte
59
+ assert_equal 2, slicer( 17, 16 ).last_byte
60
+ end
61
+
62
+ def test_length_in_bytes
63
+ assert_equal 1, slicer( 0, 1 ).length_in_bytes
64
+ assert_equal 1, slicer( 0, 4 ).length_in_bytes
65
+ assert_equal 1, slicer( 0, 8 ).length_in_bytes
66
+ assert_equal 2, slicer( 0, 9 ).length_in_bytes
67
+ assert_equal 2, slicer( 0, 14 ).length_in_bytes
68
+ assert_equal 2, slicer( 0, 16 ).length_in_bytes
69
+ assert_equal 3, slicer( 0, 20 ).length_in_bytes
70
+
71
+ assert_equal 1, slicer( 4, 1 ).length_in_bytes
72
+ assert_equal 1, slicer( 4, 4 ).length_in_bytes
73
+ assert_equal 1, slicer( 5, 8 ).length_in_bytes
74
+ assert_equal 2, slicer( 6, 9 ).length_in_bytes
75
+ assert_equal 2, slicer( 7, 14 ).length_in_bytes
76
+ assert_equal 2, slicer( 7, 16 ).length_in_bytes
77
+ assert_equal 3, slicer( 8, 20 ).length_in_bytes
78
+ end
79
+
80
+ def test_insert_length_in_bytes
81
+ assert_equal 1, slicer( 0, 1 ).insert_length_in_bytes
82
+ assert_equal 1, slicer( 0, 4 ).insert_length_in_bytes
83
+ assert_equal 1, slicer( 0, 8 ).insert_length_in_bytes
84
+ assert_equal 2, slicer( 0, 9 ).insert_length_in_bytes
85
+ assert_equal 2, slicer( 0, 14 ).insert_length_in_bytes
86
+ assert_equal 2, slicer( 0, 16 ).insert_length_in_bytes
87
+ assert_equal 3, slicer( 0, 20 ).insert_length_in_bytes
88
+
89
+ assert_equal 1, slicer( 8, 8 ).insert_length_in_bytes
90
+
91
+ assert_equal 1, slicer( 4, 1 ).insert_length_in_bytes
92
+ assert_equal 1, slicer( 4, 4 ).insert_length_in_bytes
93
+ assert_equal 2, slicer( 5, 8 ).insert_length_in_bytes
94
+ assert_equal 2, slicer( 6, 9 ).insert_length_in_bytes
95
+ assert_equal 3, slicer( 7, 14 ).insert_length_in_bytes
96
+ assert_equal 3, slicer( 7, 16 ).insert_length_in_bytes
97
+ assert_equal 3, slicer( 8, 20 ).insert_length_in_bytes
98
+ assert_equal 3, slicer( 8, 24 ).insert_length_in_bytes
99
+ assert_equal 4, slicer( 8, 25 ).insert_length_in_bytes
100
+ assert_equal 3, slicer( 9, 16 ).insert_length_in_bytes
101
+ end
102
+
103
+ def test_highest_byte_mask
104
+ assert_equal '00111111', slicer( 2, 22 ).highest_byte_mask.binarize
105
+ assert_equal '00111111', slicer( 0, 22 ).highest_byte_mask.binarize
106
+ assert_equal '11111111', slicer( 0, 16 ).highest_byte_mask.binarize
107
+ assert_equal '00111111', slicer( 2, 14 ).highest_byte_mask.binarize
108
+ assert_equal '11111111', slicer( 2, 16 ).highest_byte_mask.binarize
109
+ assert_equal '11111111', slicer( 4, 16 ).highest_byte_mask.binarize
110
+ assert_equal '11111111', slicer( 6, 16 ).highest_byte_mask.binarize
111
+ assert_equal '11111111', slicer( 7, 16 ).highest_byte_mask.binarize
112
+ assert_equal '11111111', slicer( 8, 16 ).highest_byte_mask.binarize
113
+ assert_equal '11111111', slicer( 9, 16 ).highest_byte_mask.binarize
114
+ assert_equal '11111111', slicer( 15, 16 ).highest_byte_mask.binarize
115
+ assert_equal '11111111', slicer( 16, 16 ).highest_byte_mask.binarize
116
+ assert_equal '11111111', slicer( 17, 16 ).highest_byte_mask.binarize
117
+ end
118
+
119
+ def test_insert_first_byte_mask
120
+ assert_equal '11111111', slicer( 0, 8 ).insert_first_byte_mask.binarize
121
+ assert_equal '00000011', slicer( 2, 8 ).insert_first_byte_mask.binarize
122
+ assert_equal '00001111', slicer( 4, 8 ).insert_first_byte_mask.binarize
123
+ assert_equal '00111111', slicer( 6, 8 ).insert_first_byte_mask.binarize
124
+ assert_equal '11111111', slicer( 8, 8 ).insert_first_byte_mask.binarize
125
+ assert_equal '00000011', slicer( 10, 8 ).insert_first_byte_mask.binarize
126
+ end
127
+
128
+ def test_insert_last_byte_mask
129
+ assert_equal '11111111', slicer( 0, 8 ).insert_last_byte_mask.binarize
130
+ assert_equal '11111100', slicer( 2, 8 ).insert_last_byte_mask.binarize
131
+ assert_equal '11110000', slicer( 4, 8 ).insert_last_byte_mask.binarize
132
+ assert_equal '11000000', slicer( 6, 8 ).insert_last_byte_mask.binarize
133
+ assert_equal '11111111', slicer( 8, 8 ).insert_last_byte_mask.binarize
134
+ end
135
+
136
+ def test_masked_last_byte
137
+ slicer = slicer( 12, 24 )
138
+ assert_equal '00000001', slicer.data[ slicer.first_byte ].binarize
139
+ assert_equal '11110000', slicer.merge_first_byte_mask.binarize
140
+ assert_equal '00000000', slicer.masked_first_byte.binarize
141
+
142
+ assert_equal '01100111', slicer.data[ slicer.last_byte ].binarize
143
+ assert_equal '00001111', slicer.merge_last_byte_mask.binarize
144
+ assert_equal '00000111', slicer.masked_last_byte.binarize
145
+ end
146
+
147
+ def test_required_shift
148
+ assert_equal 0, slicer( 0, 16 ).required_shift
149
+ assert_equal 2, slicer( 2, 16 ).required_shift
150
+ assert_equal 4, slicer( 4, 16 ).required_shift
151
+ assert_equal 6, slicer( 6, 16 ).required_shift
152
+ assert_equal 0, slicer( 8, 4 ).required_shift
153
+ assert_equal 0, slicer( 8, 16 ).required_shift
154
+ assert_equal 2, slicer( 10, 16 ).required_shift
155
+ end
156
+
157
+ def test_get_raw_bytes
158
+ # test_data = [ 0x01, 0x23, 0x45, 0x67, 0x89 ].pack( 'C*' )
159
+
160
+ assert_equal '0x89', slicer( 0, 8 ).get_raw_bytes.hexify
161
+ assert_equal '0x67 0x89', slicer( 0, 16 ).get_raw_bytes.hexify
162
+ assert_equal '0x45 0x67 0x89', slicer( 0, 24 ).get_raw_bytes.hexify
163
+
164
+ assert_equal '0x45 0x67 0x89', slicer( 4, 16 ).get_raw_bytes.hexify
165
+ assert_equal '0x45 0x67 0x89', slicer( 4, 20 ).get_raw_bytes.hexify
166
+ assert_equal '0x23 0x45 0x67 0x89', slicer( 6, 20 ).get_raw_bytes.hexify
167
+
168
+ assert_equal '0x45 0x67', slicer( 12, 8 ).get_raw_bytes.hexify
169
+
170
+ assert_equal '0x67', slicer( 8, 4 ).get_raw_bytes.hexify
171
+
172
+ assert_equal '0x45 0x67', slicer( 8, 16 ).get_raw_bytes.hexify
173
+ assert_equal '0x23 0x45', slicer( 16, 16 ).get_raw_bytes.hexify
174
+ end
175
+
176
+ def test_shift_bytes_right
177
+ assert_equal '0x01 0x23 0x45', BitStruct::BitSlicer.shift_bytes_right( [ 0x12, 0x34, 0x56 ], 4 ).hexify
178
+ assert_equal '0x00 0x12 0x34', BitStruct::BitSlicer.shift_bytes_right( [ 0x12, 0x34, 0x56 ], 8 ).hexify
179
+ end
180
+
181
+ def test_shift_bytes_left
182
+ assert_equal '0x23 0x45 0x60', BitStruct::BitSlicer.shift_bytes_left( [ 0x12, 0x34, 0x56 ], 4 ).hexify
183
+ assert_equal '0x34 0x56 0x00', BitStruct::BitSlicer.shift_bytes_left( [ 0x12, 0x34, 0x56 ], 8 ).hexify
184
+ end
185
+
186
+ def test_get_bytes
187
+ assert_equal '0x89', slicer( 0, 8 ).get_bytes.hexify
188
+ assert_equal '0x09', slicer( 0, 4 ).get_bytes.hexify
189
+ assert_equal '0x08', slicer( 4, 4 ).get_bytes.hexify
190
+
191
+ assert_equal '0x07', slicer( 8, 4 ).get_bytes.hexify
192
+ assert_equal '0x06', slicer( 12, 4 ).get_bytes.hexify
193
+
194
+ assert_equal '0x67 0x89', slicer( 4, 8 ).get_raw_bytes.hexify
195
+ assert_equal '0x06 0x78', BitStruct::BitSlicer.shift_bytes_right( [ 0x67, 0x89 ], 4 ).hexify
196
+ assert_equal '0x78', slicer( 4, 8 ).get_bytes.hexify
197
+
198
+ assert_equal '0x45 0x67', slicer( 12, 8 ).get_raw_bytes.hexify
199
+ assert_equal '0x04 0x56', BitStruct::BitSlicer.shift_bytes_right( [ 0x45, 0x67 ], 4 ).hexify
200
+ assert_equal '0x56', slicer( 12, 8 ).get_bytes.hexify
201
+
202
+ assert_equal '0x67 0x89', slicer( 0, 16 ).get_bytes.hexify
203
+ assert_equal '0x45 0x67 0x89', slicer( 0, 24 ).get_bytes.hexify
204
+ assert_equal '0x05 0x67 0x89', slicer( 0, 22 ).get_bytes.hexify
205
+
206
+ assert_equal '0x45 0x67', slicer( 8, 16 ).get_bytes.hexify
207
+ assert_equal '0x23 0x45', slicer( 16, 16 ).get_bytes.hexify
208
+
209
+ assert_equal '0x23 0x45 0x67', slicer( 12, 16 ).get_raw_bytes.hexify
210
+ assert_equal '0x34 0x56', slicer( 12, 16 ).get_bytes.hexify
211
+ assert_equal '0x04 0x56', slicer( 12, 12 ).get_bytes.hexify
212
+ assert_equal '0x56', slicer( 12, 8 ).get_bytes.hexify
213
+ assert_equal '0x16', slicer( 12, 6 ).get_bytes.hexify
214
+ assert_equal '0x06', slicer( 12, 4 ).get_bytes.hexify
215
+ assert_equal '0x02', slicer( 12, 2 ).get_bytes.hexify
216
+ end
217
+
218
+ def test_set_bytes
219
+ slicer = slicer( 0, 8 )
220
+ assert_equal 4, slicer.first_byte
221
+ assert_equal 4, slicer.last_byte
222
+ assert_equal 1, slicer.length_in_bytes
223
+ assert_equal 1, slicer.insert_length_in_bytes
224
+ assert_equal 4, slicer.first_byte
225
+ assert_equal 4, slicer.last_byte
226
+ # assert_equal '0x01 0x23 0x45 0x67 0xcd', slicer.set_bytes( [ 0xCD ] ).hexify
227
+
228
+ slicer = slicer( 8, 8 )
229
+ assert_equal 3, slicer.first_byte
230
+ assert_equal 3, slicer.last_byte
231
+ assert_equal 1, slicer.length_in_bytes
232
+ assert_equal 1, slicer.insert_length_in_bytes
233
+ # assert_equal '0x01 0x23 0x45 0xcd 0x89', slicer.set_bytes( [ 0xCD ] ).hexify
234
+
235
+ slicer = slicer( 4, 8 )
236
+ assert_equal 3, slicer.first_byte
237
+ assert_equal 4, slicer.last_byte
238
+ assert_equal 1, slicer.length_in_bytes
239
+ assert_equal 2, slicer.insert_length_in_bytes
240
+ # assert_equal '0x01 0x23 0x45 0x6c 0xd9', slicer.set_bytes( [ 0xCD ] ).hexify
241
+
242
+ slicer = slicer( 4, 2 )
243
+ assert_equal 4, slicer.first_byte
244
+ assert_equal 4, slicer.last_byte
245
+ assert_equal 1, slicer.length_in_bytes
246
+ assert_equal 1, slicer.insert_length_in_bytes
247
+
248
+ assert_equal '00111111', slicer.insert_first_byte_mask.binarize
249
+ assert_equal '11110000', slicer.insert_last_byte_mask.binarize
250
+
251
+ assert_equal '11000000', slicer.merge_first_byte_mask.binarize
252
+ assert_equal '00001111', slicer.merge_last_byte_mask.binarize
253
+
254
+ assert_equal '10000000', slicer.masked_first_byte.binarize
255
+ assert_equal '00001001', slicer.masked_last_byte.binarize
256
+
257
+ assert_equal '0x01 0x23 0x45 0x67 0x89', slicer.data.hexify
258
+ assert_equal '0x01 0x23 0x45 0x67 0x89', slicer.set_bytes( [ 0x00 ] ).hexify
259
+ assert_equal '0x01 0x23 0x45 0x67 0xb9', slicer.set_bytes( [ 0xFF ] ).hexify
260
+
261
+ slicer = slicer( 12, 24 )
262
+ assert_equal 0, slicer.first_byte
263
+ assert_equal 3, slicer.last_byte
264
+ assert_equal 3, slicer.length_in_bytes
265
+ assert_equal 4, slicer.insert_length_in_bytes
266
+
267
+ assert_equal '00001111', slicer.insert_first_byte_mask.binarize
268
+ assert_equal '11110000', slicer.insert_last_byte_mask.binarize
269
+
270
+ assert_equal '11110000', slicer.merge_first_byte_mask.binarize
271
+ assert_equal '00001111', slicer.merge_last_byte_mask.binarize
272
+
273
+ assert_equal '00000001', slicer.data[ slicer.first_byte ].binarize
274
+ assert_equal '00000000', slicer.masked_first_byte.binarize
275
+
276
+ assert_equal '01100111', slicer.data[ slicer.last_byte ].binarize
277
+ assert_equal '00000111', slicer.masked_last_byte.binarize
278
+
279
+ assert_equal '0x01 0x23 0x45 0x67 0x89', slicer.data.hexify
280
+ assert_equal '0x12 0x34 0x56', slicer.get_bytes.hexify
281
+ assert_equal '0x07 0x75 0x53 0x37 0x89', slicer.set_bytes( [ 0x99, 0x77, 0x55, 0x33 ] ).hexify
282
+ assert_equal '0x07 0x75 0x53 0x37 0x89', slicer.set_bytes( [ 0xAA, 0xFF, 0x99, 0x77, 0x55, 0x33 ] ).hexify
283
+ end
284
+
285
+ def slicer( bit_offset, bit_length )
286
+ slicer = BitStruct::BitSlicer.new bit_offset, bit_length
287
+ slicer.data = test_data.dup
288
+ slicer
289
+ end
290
+
291
+ def test_data
292
+ [ 0x01, 0x23, 0x45, 0x67, 0x89 ].pack( 'C*' )
293
+ end
294
+
295
+ def test_data_intel
296
+ [ 0x89, 0x67, 0x45, 0x23, 0x01].pack( 'C*' )
297
+ end
298
+
299
+ end
@@ -0,0 +1,125 @@
1
+
2
+ require 'test/unit'
3
+
4
+ require 'bit_struct/struct_base'
5
+
6
+ include BitStruct
7
+
8
+
9
+
10
+ class Flags < StructBase
11
+ unsigned :direction, 4
12
+ unsigned :multiplier, 2
13
+ unsigned :offset, 2
14
+ end
15
+
16
+ class Entry < StructBase
17
+ unsigned :offset, 4
18
+ nested :flags, Flags
19
+ unsigned :address, 24
20
+ unsigned :cache_id, 16
21
+ end
22
+
23
+ class TestBitStruct < Test::Unit::TestCase
24
+
25
+ def test_flags_class
26
+ fields = Flags.fields
27
+ assert_equal 3, fields.size
28
+
29
+ assert_equal :direction, fields[ 0 ].name
30
+ assert_equal 4, fields[ 0 ].length
31
+ assert_equal :multiplier, fields[ 1 ].name
32
+ assert_equal 2, fields[ 1 ].length
33
+ assert_equal :offset, fields[ 2 ].name
34
+ assert_equal 2, fields[ 2 ].length
35
+ end
36
+
37
+ def test_attributes_class
38
+ fields = Entry.fields
39
+ assert_equal 4, fields.size
40
+
41
+ assert_equal :offset, fields[ 0 ].name
42
+ assert_equal 4, fields[ 0 ].length
43
+ assert_equal :flags, fields[ 1 ].name
44
+ assert_equal 8, fields[ 1 ].length
45
+ assert_equal :address, fields[ 2 ].name
46
+ assert_equal 24, fields[ 2 ].length
47
+ assert_equal :cache_id, fields[ 3 ].name
48
+ assert_equal 16, fields[ 3 ].length
49
+ end
50
+
51
+ def test_flags_read
52
+ flags = Flags.new [ 0xFF ].pack( 'C*' )
53
+ assert_equal 0x0F, flags.direction
54
+ assert_equal 0x03, flags.multiplier
55
+ assert_equal 0x03, flags.offset
56
+
57
+ flags = Flags.new [ 0x0F ].pack( 'C*' )
58
+ assert_equal 0x0F, flags.direction
59
+ assert_equal 0x00, flags.multiplier
60
+ assert_equal 0x00, flags.offset
61
+
62
+ flags = Flags.new [ 0x3F ].pack( 'C*' )
63
+ assert_equal 0x0F, flags.direction
64
+ assert_equal 0x03, flags.multiplier
65
+ assert_equal 0x00, flags.offset
66
+
67
+ flags = Flags.new [ 0xCF ].pack( 'C*' )
68
+ assert_equal 0x0F, flags.direction
69
+ assert_equal 0x00, flags.multiplier
70
+ assert_equal 0x03, flags.offset
71
+ end
72
+
73
+ def test_flags_write
74
+ data = [ 0xFF ].pack( 'C*' )
75
+
76
+ flags = Flags.new data
77
+ assert_equal '0xff', flags.data.hexify
78
+ assert_equal 0x0F, flags.direction
79
+ assert_equal 0x03, flags.multiplier
80
+ assert_equal 0x03, flags.offset
81
+
82
+ flags.multiplier = 0
83
+ assert_equal '0xcf', flags.data.hexify
84
+ assert_equal 0x0F, flags.direction
85
+ assert_equal 0x00, flags.multiplier
86
+ assert_equal 0x03, flags.offset
87
+ end
88
+
89
+ def test_entry_read
90
+ data = [ 0xAA, 0xFF, 0x01, 0x23, 0x45, 0x67, 0x89 ].pack( 'C*' )
91
+ entry = Entry.new data
92
+
93
+ assert_equal 9, entry.offset
94
+ assert_equal '0x12 0x34 0x56', entry.address.bytes.hexify
95
+ assert_equal '0xaf 0xf0', entry.cache_id.bytes.hexify
96
+
97
+ flags = entry.flags
98
+ assert_equal 8, flags.direction
99
+ assert_equal 3, flags.multiplier
100
+ assert_equal 1, flags.offset
101
+ end
102
+
103
+ def test_attributes_write
104
+ data = [ 0xAA, 0xFF, 0x01, 0x23, 0x45, 0x67, 0x89 ].pack( 'C*' )
105
+ entry = Entry.new data
106
+
107
+ entry.offset = 15
108
+ assert_equal '0x0f', entry.offset.bytes.hexify
109
+ assert_equal '0xaa 0xff 0x01 0x23 0x45 0x67 0x8f', entry.data.hexify
110
+
111
+ entry.address = 0x44332211
112
+ assert_equal '0x33 0x22 0x11', entry.address.bytes.hexify
113
+ assert_equal '0xaa 0xff 0x03 0x32 0x21 0x17 0x8f', entry.data.hexify
114
+
115
+ entry.cache_id = 0xAABBCCDD
116
+ assert_equal '0xcc 0xdd', entry.cache_id.bytes.hexify
117
+ assert_equal '0xac 0xcd 0xd3 0x32 0x21 0x17 0x8f', entry.data.hexify
118
+
119
+ flags = entry.flags
120
+ assert_equal 8, flags.direction
121
+ assert_equal 3, flags.multiplier
122
+ assert_equal 1, flags.offset
123
+ end
124
+
125
+ end
@@ -0,0 +1,115 @@
1
+
2
+ require 'logger'
3
+
4
+ require 'test/unit/autorunner'
5
+
6
+ class String
7
+
8
+ def hexify
9
+ result = ''
10
+ each_byte do |b|
11
+ result << "0x#{b.hexify} "
12
+ end
13
+ result.chop
14
+ end
15
+
16
+ end
17
+
18
+ class Array
19
+
20
+ def hexify
21
+ map { |b| "0x#{b && b.hexify}" }.join( ' ' )
22
+ end
23
+
24
+ def binarize
25
+ map { |b| "%#{b && b.binarize}" }.join( ' ' )
26
+ end
27
+
28
+ end
29
+
30
+ class Fixnum
31
+
32
+ def bytes
33
+ bytes = []
34
+ val = to_i
35
+ while val > 0
36
+ bytes.insert 0, ( val & 0xFF )
37
+ val >>= 8
38
+ end
39
+ bytes
40
+ end
41
+
42
+ def hexify
43
+ result = ''
44
+ val = to_i
45
+ while val > 0
46
+ b = val & 0xF
47
+ result = b.to_s( 16 ) + result
48
+ val >>= 4
49
+ end
50
+ padded result, 2
51
+ end
52
+
53
+ def binarize
54
+ result = ''
55
+ val = to_i
56
+ while val > 0
57
+ b = val & 1
58
+ result = b.to_s( 2 ) + result
59
+ val >>= 1
60
+ end
61
+ padded result, 8
62
+ end
63
+
64
+ def padded( val, length_multiplier )
65
+ padded_length = ( val.length / length_multiplier ).ceil * length_multiplier
66
+ padded_length = length_multiplier if padded_length == 0
67
+ pad_size = padded_length - val.length
68
+ '0' * pad_size + val
69
+ end
70
+
71
+ end
72
+
73
+ def hexify( data )
74
+ result = ''
75
+ data.each_byte do |b|
76
+ nibble = ( b >> 4 ) & 15
77
+ result << ( 0x30 + nibble ) if nibble < 10
78
+ result << ( 0x41 + nibble - 10 ) if nibble >= 10
79
+ nibble = ( b >> 0 ) & 15
80
+ result << ( 0x30 + nibble ) if nibble < 10
81
+ result << ( 0x41 + nibble - 10 ) if nibble >= 10
82
+ end
83
+ result
84
+ end
85
+
86
+ def get_resource( basedir, relative_filename )
87
+ basedir = File.dirname basedir unless File.directory? basedir
88
+ filename = File.join( basedir, relative_filename )
89
+ raise "Resource not found: #{filename}" unless File.exists? filename
90
+ return filename
91
+ end
92
+
93
+ def open_resource( basedir, relative_filename )
94
+ File.new get_resource( basedir, relative_filename ), 'rb'
95
+ end
96
+
97
+ def load_resource( basedir, relative_filename )
98
+ File.open( get_resource( basedir, relative_filename ), 'rb' ) do |file|
99
+ return file.read
100
+ end
101
+ end
102
+
103
+ class NullLogger < Logger
104
+ def initialize( *args )
105
+ super STDOUT
106
+ @level = WARN
107
+ end
108
+ end
109
+
110
+ NULL_LOGGER = NullLogger.new
111
+
112
+ args = [ '--runner=console', '--verbose=v' ]
113
+ args.concat ARGV
114
+ success = Test::Unit::AutoRunner.run( true, File.dirname( $0 ), args )
115
+ exit 10 unless success
metadata ADDED
@@ -0,0 +1,55 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.8.11
3
+ specification_version: 1
4
+ name: BitStructEx
5
+ version: !ruby/object:Gem::Version
6
+ version: 0.0.54
7
+ date: 2006-04-13 00:00:00 +02:00
8
+ summary: Simple DSL for defining bit-based structures on byte data.
9
+ require_paths:
10
+ - src/ruby
11
+ email: tfdj {AT} berlinfactor {DOT} com
12
+ homepage: bit-struct-ex.rubyforge.org.
13
+ rubyforge_project: bit-struct-ex
14
+ description: "Allows the specification of bit-based structures and provides an intuitive way of access data. Example: class Flags < StructBase unsigned :direction, 4 unsigned :multiplier, 2 unsigned :offset, 2 end class Entry < StructBase unsigned :offset, 4 nested :flags, Flags unsigned :address, 24 unsigned :cache_id, 16 end In contrast to the already available http://raa.ruby-lang.org/project/bit-struct/ implementation, BitStructEx allows nested structures which are not aligned on byte boundaries."
15
+ autorequire: bit_struct/struct_base
16
+ default_executable:
17
+ bindir: bin
18
+ has_rdoc: true
19
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
20
+ requirements:
21
+ - - ">"
22
+ - !ruby/object:Gem::Version
23
+ version: 0.0.0
24
+ version:
25
+ platform: ruby
26
+ signing_key:
27
+ cert_chain:
28
+ authors:
29
+ - The.French.DJ
30
+ files:
31
+ - lib/ruby/rake/version.rb
32
+ - src/ruby/bit_struct/bit_slicer.rb
33
+ - src/ruby/bit_struct/field.rb
34
+ - src/ruby/bit_struct/nested.rb
35
+ - src/ruby/bit_struct/struct_base.rb
36
+ - src/ruby/bit_struct/unsigned.rb
37
+ - test/ruby/runner.rb
38
+ - test/ruby/bit_struct/test_bit_slicer.rb
39
+ - test/ruby/bit_struct/test_struct_base.rb
40
+ - Rakefile
41
+ - Version
42
+ test_files: []
43
+
44
+ rdoc_options: []
45
+
46
+ extra_rdoc_files: []
47
+
48
+ executables: []
49
+
50
+ extensions: []
51
+
52
+ requirements: []
53
+
54
+ dependencies: []
55
+