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 +53 -0
- data/Version +1 -0
- data/lib/ruby/rake/version.rb +71 -0
- data/src/ruby/bit_struct/bit_slicer.rb +202 -0
- data/src/ruby/bit_struct/field.rb +18 -0
- data/src/ruby/bit_struct/nested.rb +32 -0
- data/src/ruby/bit_struct/struct_base.rb +124 -0
- data/src/ruby/bit_struct/unsigned.rb +27 -0
- data/test/ruby/bit_struct/test_bit_slicer.rb +299 -0
- data/test/ruby/bit_struct/test_struct_base.rb +125 -0
- data/test/ruby/runner.rb +115 -0
- metadata +55 -0
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
|
data/test/ruby/runner.rb
ADDED
|
@@ -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
|
+
|