1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
|
require 'thread'
require 'bindata/base_primitive'
module BinData
# Defines a number of classes that contain a bit based integer.
# The integer is defined by endian and number of bits.
module BitField #:nodoc: all
@@mutex = Mutex.new
class << self
def define_class(name, nbits, endian, signed = :unsigned)
@@mutex.synchronize do
unless BinData.const_defined?(name)
new_class = Class.new(BinData::BasePrimitive)
BitField.define_methods(new_class, nbits, endian.to_sym, signed.to_sym)
RegisteredClasses.register(name, new_class)
BinData.const_set(name, new_class)
end
end
BinData.const_get(name)
end
def define_methods(bit_class, nbits, endian, signed)
bit_class.module_eval <<-END
#{create_params_code(nbits)}
def assign(val)
#{create_nbits_code(nbits)}
#{create_clamp_code(nbits, signed)}
super(val)
end
def do_write(io)
#{create_nbits_code(nbits)}
val = _value
#{create_int2uint_code(nbits, signed)}
io.writebits(val, #{nbits}, :#{endian})
end
def do_num_bytes
#{create_nbits_code(nbits)}
#{create_do_num_bytes_code(nbits)}
end
def bit_aligned?
true
end
#---------------
private
def read_and_return_value(io)
#{create_nbits_code(nbits)}
val = io.readbits(#{nbits}, :#{endian})
#{create_uint2int_code(nbits, signed)}
val
end
def sensible_default
0
end
END
end
def create_params_code(nbits)
if nbits == :nbits
"mandatory_parameter :nbits"
else
""
end
end
def create_nbits_code(nbits)
if nbits == :nbits
"nbits = eval_parameter(:nbits)"
else
""
end
end
def create_do_num_bytes_code(nbits)
if nbits == :nbits
"nbits / 8.0"
else
nbits / 8.0
end
end
def create_clamp_code(nbits, signed)
if nbits == :nbits
create_dynamic_clamp_code(signed)
else
create_fixed_clamp_code(nbits, signed)
end
end
def create_dynamic_clamp_code(signed)
if signed == :signed
max = "(1 << (nbits - 1)) - 1"
min = "-((#{max}) + 1)"
else
max = "(1 << nbits) - 1"
min = "0"
end
"val = val.clamp(#{min}, #{max})"
end
def create_fixed_clamp_code(nbits, signed)
if nbits == 1 && signed == :signed
raise "signed bitfield must have more than one bit"
end
if signed == :signed
max = "(1 << (#{nbits} - 1)) - 1"
min = "-((#{max}) + 1)"
else
min = "0"
max = "(1 << #{nbits}) - 1"
end
clamp = "(val = val.clamp(#{min}, #{max}))"
if nbits == 1
# allow single bits to be used as booleans
clamp = "(val == true) ? 1 : (not val) ? 0 : #{clamp}"
end
"val = #{clamp}"
end
def create_int2uint_code(nbits, signed)
if signed != :signed
""
elsif nbits == :nbits
"val &= (1 << nbits) - 1"
else
"val &= #{(1 << nbits) - 1}"
end
end
def create_uint2int_code(nbits, signed)
if signed != :signed
""
elsif nbits == :nbits
"val -= (1 << nbits) if (val >= (1 << (nbits - 1)))"
else
"val -= #{1 << nbits} if (val >= #{1 << (nbits - 1)})"
end
end
end
end
# Create classes for dynamic bitfields
{
"Bit" => :big,
"BitLe" => :little,
"Sbit" => [:big, :signed],
"SbitLe" => [:little, :signed],
}.each_pair { |name, args| BitField.define_class(name, :nbits, *args) }
# Create classes on demand
module BitFieldFactory
def const_missing(name)
mappings = {
/^Bit(\d+)$/ => :big,
/^Bit(\d+)le$/ => :little,
/^Sbit(\d+)$/ => [:big, :signed],
/^Sbit(\d+)le$/ => [:little, :signed]
}
mappings.each_pair do |regex, args|
if regex =~ name.to_s
nbits = $1.to_i
return BitField.define_class(name, nbits, *args)
end
end
super(name)
end
end
BinData.extend BitFieldFactory
end
|