Module: BinaryFinery
- Defined in:
- lib/binary_finery.rb
Overview
In order to be plaftorm agnostic, Binary inspects at load time machine’s capabilities and adjust its own operations accordingly.
BinaryFinary performs operations accepted by the following grammar:
operation ::= 'read' | 'write'
integer_type ::= 'uint' | 'int'
bits ::= '8' | '16' | '32' | '64' | '128' | '256'
endianness ::= 'native' | 'little' | 'big' | 'network'
OP_INT ::= operation '_' integer_type bits ('_' endianness)?
str_padding ::= 'null_padded' | 'c_'
string_flavor ::= 'string' | 'fixed_string' | 'binary_string'
str_preposition ::= '_of_'
integer ::= [0-9]+
str_size ::= 'bytes'
OP_STR ::= operation '_' (str_padding '_')?
string_flavor '_' str_preposition integer '_' str_size
Constant Summary collapse
- OP_RE =
/(read|write)_/
- INT_RE =
/(uint|int)(8|16|32|64)_?(native|little|big|network)?/
- STR_RE =
/(null_padded|c_)?((binary_|fixed_)?string)(_of_)[0-9]+(bytes)/
- OP_INT_RE =
Regexp::compile(OP_RE.source + INT_RE.source)
- OP_STR_RE =
Regexp::compile(OP_RE.source + STR_RE.source)
- KNOWN_RE =
Regexp::compile(OP_INT_RE.source + OP_STR_RE.source)
- NUL =
0.chr
- INTEGER_SIZE_IN_BYTES =
Returns the number of bytes used to encode an integer number
module_eval { 1.size }
- NATIVE_BYTE_ORDER =
A machine architecture is said to be little endian if puts first the LSB. We evaluate the first byte of the number 1 packed as an integer. While first_byte is a Fixnum in Ruby 1.8.x, it is a string in 1.9.x; in latter case we employ the ord method to obtain the ordinal number associated,if is the case. Underlying machine is l.e. if the LSB is 1.
module_eval do first_byte = [1].pack('i')[0] first_byte = first_byte.ord if RUBY_VERSION =~ /^1\.9/ first_byte == 1 ? :little : :big end
- BIT_MASK =
{ 4 => 0xF, 8 => 0xFF, 16 => 0xFFFFF, 32 => 0xFFFFFFFF, 64 => 0xFFFFFFFFFFFFFFFF, 128 => 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF }
- DEFAULT_BIT_MASK =
module_eval { (1.size >> 1) * 8 }
- @@pack_mappings =
{ 1 => { :uint => { :native => 'C' }, :int => { :native => 'c' } }, 2 => { :uint => { :native => 'S', :little => 'v', :big => 'n' }, :int => { :native => 's', :little => 'v', :big => 'n' } }, 4 => { :uint => { :native => 'L', :little => 'V', :big => 'N' }, :int => { :native => 'l', :little => 'V', :big => 'N' } }, 8 => { :uint => { :native => 'Q' }, :int => { :native => 'q' } } }
Instance Method Summary collapse
- #big_endian_byte_order ⇒ Object (also: #network_byte_order)
- #big_endian_platform? ⇒ Boolean (also: #network_endian_platform?)
- #concat(msb, lsb, bits) ⇒ Object
-
#format(byte_size, type, byte_order) ⇒ Object
obtains the correct pack format for the arguments.
- #integer_size_in_bytes ⇒ Object (also: #word_size_in_bytes)
- #little_endian_byte_order ⇒ Object
- #little_endian_platform? ⇒ Boolean
- #lsb(num, bits = DEFAULT_BIT_MASK) ⇒ Object
- #method_missing(method_name, *args, &block) ⇒ Object
- #msb(num, bits = DEFAULT_BIT_MASK) ⇒ Object
- #native_byte_order ⇒ Object
- #read_c_string ⇒ Object
- #read_int128_little ⇒ Object
- #read_int256_little ⇒ Object
- #read_null_padded_string(size) ⇒ Object
- #read_string(opt = {:size => nil, :padding => NUL}) ⇒ Object
- #read_uint128_big ⇒ Object (also: #read_uint128_network)
- #read_uint128_little ⇒ Object
- #read_uint128_native ⇒ Object (also: #read_uint128)
- #read_uint256_big ⇒ Object (also: #read_uint256_network)
- #read_uint256_little ⇒ Object
- #read_uint256_native ⇒ Object (also: #read_uint256)
- #read_uint64_big ⇒ Object (also: #read_uint64_network)
- #read_uint64_little ⇒ Object
- #read_uint64_native ⇒ Object (also: #read_uint64)
-
#readn(n) ⇒ Object
read exactly n characters from the buffer, otherwise raise an exception.
-
#readn_unpack(size, template, byte_order = NATIVE_BYTE_ORDER) ⇒ Object
read n bytes and unpack, swapping bytes as per endianness.
- #recognize?(type) ⇒ Boolean
-
#size_of(type, object = nil) ⇒ Object
Tells the byte size required for the method passed as argument.
- #split_msb_lsb(num, bits) ⇒ Object
-
#write_c_string(str) ⇒ Object
writes the string and appends NUL.
- #write_fixed_size_string(content = "") ⇒ Object
- #write_int128_little(n) ⇒ Object
- #write_int256_little(n) ⇒ Object
- #write_null_padded_string(str, opt = {:size => str.size}) ⇒ Object
-
#write_pack(number, template, byte_order = NATIVE_BYTE_ORDER) ⇒ Object
writes a number and pack it, swapping bytes as per endianness.
- #write_string(content, opt = {:padding => nil, :size => content.size}) ⇒ Object
- #write_uint128_big(n) ⇒ Object
- #write_uint128_little(n) ⇒ Object
- #write_uint128_native(n) ⇒ Object (also: #write_uint128)
- #write_uint256_big(n) ⇒ Object
- #write_uint256_little(n) ⇒ Object
- #write_uint256_native(n) ⇒ Object (also: #write_uint256)
- #write_uint64_big(n) ⇒ Object
- #write_uint64_little(n) ⇒ Object
- #write_uint64_native(n) ⇒ Object (also: #write_uint64)
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(method_name, *args, &block) ⇒ Object
293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 |
# File 'lib/binary_finery.rb', line 293 def method_missing(method_name, *args, &block) if method_name.to_s =~ OP_INT_RE op, type, bits, byte_order = Regexp.last_match[1..4] # string → sym op, type, byte_order = [op, type].map!(&:to_sym) # adjust bits to bytes byte_size = (bits.to_i / 8) # normalize endianness byte_order = byte_order.to_sym unless byte_order.nil? byte_order = :big if byte_order == 'network' fmt = format(byte_size,type,byte_order) case op when :read self.class.send :define_method, method_name do readn_unpack(byte_size, fmt, byte_order) end self.send method_name when :write if (args.first.kind_of? Integer) && (args.size == 1) self.class.send :define_method, method_name do |value| write_pack(value, fmt, byte_order) end self.send method_name, args.first end end elsif method_name.to_s =~ OP_STR_RE op, string_flavor = Regexp.last_match[1..2] case op when :read = args.first.indexes(:size, :padding) self.send :read_string, when :write str, = args.shift, args self.send :write_string, str, end else super end end |
Instance Method Details
#big_endian_byte_order ⇒ Object Also known as: network_byte_order
52 |
# File 'lib/binary_finery.rb', line 52 def big_endian_byte_order() :big end |
#big_endian_platform? ⇒ Boolean Also known as: network_endian_platform?
70 |
# File 'lib/binary_finery.rb', line 70 def big_endian_platform?() native_byte_order.equal? :big end |
#concat(msb, lsb, bits) ⇒ Object
96 97 98 |
# File 'lib/binary_finery.rb', line 96 def concat(msb, lsb, bits) lsb + (msb << bits) end |
#format(byte_size, type, byte_order) ⇒ Object
obtains the correct pack format for the arguments
226 227 228 229 230 |
# File 'lib/binary_finery.rb', line 226 def format(byte_size, type, byte_order) byte_order = :native if byte_order.nil? byte_order = :big if byte_order.equal?(:network) @@pack_mappings[byte_size][type][byte_order] end |
#integer_size_in_bytes ⇒ Object Also known as: word_size_in_bytes
48 |
# File 'lib/binary_finery.rb', line 48 def integer_size_in_bytes() INTEGER_SIZE_IN_BYTES end |
#little_endian_byte_order ⇒ Object
51 |
# File 'lib/binary_finery.rb', line 51 def little_endian_byte_order() :little end |
#little_endian_platform? ⇒ Boolean
69 |
# File 'lib/binary_finery.rb', line 69 def little_endian_platform?() native_byte_order.equal? :little end |
#lsb(num, bits = DEFAULT_BIT_MASK) ⇒ Object
90 |
# File 'lib/binary_finery.rb', line 90 def lsb(num, bits=DEFAULT_BIT_MASK) ((num >> bits) & BIT_MASK[bits]) end |
#msb(num, bits = DEFAULT_BIT_MASK) ⇒ Object
89 |
# File 'lib/binary_finery.rb', line 89 def msb(num, bits=DEFAULT_BIT_MASK) (num & BIT_MASK[bits]) end |
#native_byte_order ⇒ Object
67 |
# File 'lib/binary_finery.rb', line 67 def native_byte_order() NATIVE_BYTE_ORDER end |
#read_c_string ⇒ Object
251 252 253 |
# File 'lib/binary_finery.rb', line 251 def read_c_string readline(sep_string = NUL) end |
#read_int128_little ⇒ Object
151 152 153 154 |
# File 'lib/binary_finery.rb', line 151 def read_int128_little val = read_uint128_little val > (2**128 - 1) ? val : -val end |
#read_int256_little ⇒ Object
106 107 108 109 |
# File 'lib/binary_finery.rb', line 106 def read_int256_little val = read_uint256_little val > (2**256 - 1) ? val : -val end |
#read_null_padded_string(size) ⇒ Object
246 247 248 249 |
# File 'lib/binary_finery.rb', line 246 def read_null_padded_string(size) str = readn(size) str.split(/NUL/).first or str end |
#read_string(opt = {:size => nil, :padding => NUL}) ⇒ Object
255 256 257 258 259 260 261 |
# File 'lib/binary_finery.rb', line 255 def read_string(opt={:size => nil, :padding => NUL}) if opt[:size] read_fixed_size_string(opt[:size], opt[:padding]) else read_c_string end end |
#read_uint128_big ⇒ Object Also known as: read_uint128_network
156 157 158 159 160 |
# File 'lib/binary_finery.rb', line 156 def read_uint128_big msb = read_uint64_big lsb = read_uint64_big concat(msb, lsb, 64) end |
#read_uint128_little ⇒ Object
145 146 147 148 149 |
# File 'lib/binary_finery.rb', line 145 def read_uint128_little lsb = read_uint64_little msb = read_uint64_little concat(msb, lsb, 64) end |
#read_uint128_native ⇒ Object Also known as: read_uint128
163 164 165 |
# File 'lib/binary_finery.rb', line 163 def read_uint128_native little_endian_platform? ? read_uint128_little : read_uint128_big end |
#read_uint256_big ⇒ Object Also known as: read_uint256_network
111 112 113 114 115 |
# File 'lib/binary_finery.rb', line 111 def read_uint256_big msb = read_uint64_big lsb = read_uint64_big concat(msb, lsb, 128) end |
#read_uint256_little ⇒ Object
100 101 102 103 104 |
# File 'lib/binary_finery.rb', line 100 def read_uint256_little lsb = read_uint128_little msb = read_uint128_little concat(msb, lsb, 128) end |
#read_uint256_native ⇒ Object Also known as: read_uint256
118 119 120 |
# File 'lib/binary_finery.rb', line 118 def read_uint256_native little_endian_platform? ? read_uint256_little : read_uint256_big end |
#read_uint64_big ⇒ Object Also known as: read_uint64_network
196 197 198 199 200 |
# File 'lib/binary_finery.rb', line 196 def read_uint64_big msb = readn_unpack(4, 'L', :big) lsb = readn_unpack(4, 'L', :big) concat(msb, lsb, 32) end |
#read_uint64_little ⇒ Object
190 191 192 193 194 |
# File 'lib/binary_finery.rb', line 190 def read_uint64_little lsb = readn_unpack(4, 'L', :little) msb = readn_unpack(4, 'L', :little) concat(msb, lsb, 32) end |
#read_uint64_native ⇒ Object Also known as: read_uint64
203 204 205 |
# File 'lib/binary_finery.rb', line 203 def read_uint64_native little_endian_platform? ? read_uint64_little : read_uint64_big end |
#readn(n) ⇒ Object
read exactly n characters from the buffer, otherwise raise an exception.
240 241 242 243 244 |
# File 'lib/binary_finery.rb', line 240 def readn(n) str = read(n) raise "couldn't read #{n} characters." if str.nil? or str.size != n str end |
#readn_unpack(size, template, byte_order = NATIVE_BYTE_ORDER) ⇒ Object
read n bytes and unpack, swapping bytes as per endianness
233 234 235 236 237 |
# File 'lib/binary_finery.rb', line 233 def readn_unpack(size, template, byte_order=NATIVE_BYTE_ORDER) str = readn(size) str.reverse! if not native_byte_order.equal? byte_order # spotted problem in pack str.unpack(template).first end |
#recognize?(type) ⇒ Boolean
344 345 346 |
# File 'lib/binary_finery.rb', line 344 def recognize?(type) type.to_s =~ Regexp.union(INT_RE,STR_RE) end |
#size_of(type, object = nil) ⇒ Object
Tells the byte size required for the method passed as argument. When recognized.
337 338 339 340 341 342 |
# File 'lib/binary_finery.rb', line 337 def size_of(type, object=nil) case when type.to_s =~ INT_RE then Regexp.last_match[2].to_i / 8 when type.to_s =~ STR_RE then Regexp.last_match[-2].to_i end end |
#split_msb_lsb(num, bits) ⇒ Object
92 93 94 |
# File 'lib/binary_finery.rb', line 92 def split_msb_lsb(num, bits) [msb(num, bits), lsb(num,bits) ] end |
#write_c_string(str) ⇒ Object
writes the string and appends NUL
271 272 273 274 275 276 |
# File 'lib/binary_finery.rb', line 271 def write_c_string(str) #TODO: improve input validation raise ArgumentError, "Invalid Ruby string" if str.include?(NUL) write(str) write(NUL) end |
#write_fixed_size_string(content = "") ⇒ Object
285 286 287 |
# File 'lib/binary_finery.rb', line 285 def write_fixed_size_string(content="") write_string(content, :size => content.size) end |
#write_int128_little(n) ⇒ Object
174 175 176 177 |
# File 'lib/binary_finery.rb', line 174 def write_int128_little(n) n = 2**128 - n write_uint128_little(n) end |
#write_int256_little(n) ⇒ Object
129 130 131 132 |
# File 'lib/binary_finery.rb', line 129 def write_int256_little(n) n = 2**256 - n write_uint256_little(n) end |
#write_null_padded_string(str, opt = {:size => str.size}) ⇒ Object
289 290 291 |
# File 'lib/binary_finery.rb', line 289 def write_null_padded_string(str, opt = {:size => str.size}) write_string(str, padding => "\000", :size => str.size) end |
#write_pack(number, template, byte_order = NATIVE_BYTE_ORDER) ⇒ Object
writes a number and pack it, swapping bytes as per endianness
264 265 266 267 268 |
# File 'lib/binary_finery.rb', line 264 def write_pack(number, template, byte_order=NATIVE_BYTE_ORDER) str = [number].pack(template) str.reverse! if not native_byte_order.equal? byte_order # blame Array#pack write(str) end |
#write_string(content, opt = {:padding => nil, :size => content.size}) ⇒ Object
278 279 280 281 282 283 |
# File 'lib/binary_finery.rb', line 278 def write_string(content, opt = {:padding => nil, :size => content.size}) return if not (opt[:size].kind_of? Integer) output_string = content[0..opt[:size]] output_string = output_string.ljust(opt[:size], opt[:padding]) if opt[:padding] write(output_string) end |
#write_uint128_big(n) ⇒ Object
179 180 181 182 183 |
# File 'lib/binary_finery.rb', line 179 def write_uint128_big(n) lsb, msb = split_msb_lsb(n, 64) write_uint64_big(msb) write_uint64_big(lsb) end |
#write_uint128_little(n) ⇒ Object
168 169 170 171 172 |
# File 'lib/binary_finery.rb', line 168 def write_uint128_little(n) lsb, msb = split_msb_lsb(n, 64) write_uint64_little(lsb) write_uint64_little(msb) end |
#write_uint128_native(n) ⇒ Object Also known as: write_uint128
185 186 187 |
# File 'lib/binary_finery.rb', line 185 def write_uint128_native(n) little_endian_platform? ? write_uint128_little(n) : write_uint128_big(n) end |
#write_uint256_big(n) ⇒ Object
134 135 136 137 138 |
# File 'lib/binary_finery.rb', line 134 def write_uint256_big(n) lsb, msb = split_msb_lsb(n, 128) write_uint128_big(msb) write_uint128_big(lsb) end |
#write_uint256_little(n) ⇒ Object
123 124 125 126 127 |
# File 'lib/binary_finery.rb', line 123 def write_uint256_little(n) msb, lsb = split_msb_lsb(n, 128) write_uint128_little(msb) write_uint128_little(lsb) end |
#write_uint256_native(n) ⇒ Object Also known as: write_uint256
140 141 142 |
# File 'lib/binary_finery.rb', line 140 def write_uint256_native(n) little_endian_platform? ? write_uint256_little(n) : write_uint256_big(n) end |
#write_uint64_big(n) ⇒ Object
214 215 216 217 218 |
# File 'lib/binary_finery.rb', line 214 def write_uint64_big(n) lsb, msb = split_msb_lsb(n,32) write_pack(msb, 'L', :big) write_pack(lsb, 'L', :big) end |
#write_uint64_little(n) ⇒ Object
208 209 210 211 212 |
# File 'lib/binary_finery.rb', line 208 def write_uint64_little(n) lsb, msb = split_msb_lsb(n,32) write_pack(lsb, 'L', :little) write_pack(msb, 'L', :little) end |
#write_uint64_native(n) ⇒ Object Also known as: write_uint64
220 221 222 |
# File 'lib/binary_finery.rb', line 220 def write_uint64_native(n) little_endian_platform? ? write_uint64_little(n) : write_uint64_big(n) end |