BitStructEx 0.0.54

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