Class: Bindef

Inherits:
Object
  • Object
show all
Includes:
Extras::Ctrl, Extras::Int128, Extras::String, Extras::TLV, Schemas
Defined in:
lib/bindef.rb,
lib/bindef/extras.rb,
lib/bindef/schemas.rb,
lib/bindef/version.rb,
lib/bindef/exceptions.rb,
lib/bindef/extras/tlv.rb,
lib/bindef/extras/ctrl.rb,
lib/bindef/extras/int128.rb,
lib/bindef/extras/string.rb

Overview

The primary namespace for Bindef.

Defined Under Namespace

Modules: Extras, Schemas Classes: BindefError, CommandError, EvaluationError, PragmaError

Constant Summary collapse

VERSION =

The current Bindef version.

"0.0.5"

Constants included from Schemas

Schemas::DEFAULT_PRAGMAS, Schemas::ENDIANDED_INTEGER_COMMAND_MAP, Schemas::PRAGMA_SCHEMA

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Extras::String

#lstr, #strnz, #strz

Methods included from Extras::Int128

#i128, #u128

Methods included from Extras::TLV

#tlv_u16, #tlv_u32, #tlv_u8

Constructor Details

#initialize(output = STDOUT, error = STDERR, verbose: false, warnings: true) ⇒ Bindef

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns a new instance of Bindef.



15
16
17
18
19
# File 'lib/bindef.rb', line 15

def initialize(output = STDOUT, error = STDERR, verbose: false, warnings: true)
  @output = output
  @error = error
  @pragmas = DEFAULT_PRAGMAS.dup.update(verbose: verbose, warnings: warnings)
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(*args) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Captures unknown commands and raises an appropriate error.

Raises:



66
67
68
# File 'lib/bindef.rb', line 66

def method_missing(*args)
  raise CommandError, "unknown command: #{args.join(" ")}"
end

Instance Attribute Details

#pragmasHash (readonly)

Returns the map of current pragma settings.

Returns:

  • (Hash)

    the map of current pragma settings



12
13
14
# File 'lib/bindef.rb', line 12

def pragmas
  @pragmas
end

Instance Method Details

#blobify(value, fmt) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Builds a string containing the given value packed into the given format.

Parameters:

  • value (Object)

    the value to emit

  • fmt (String)

    the Array#pack format to emit it in



52
53
54
# File 'lib/bindef.rb', line 52

def blobify(value, fmt)
  [value].pack(fmt)
end

#emit(blob) ⇒ void

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

This method returns an undefined value.

Emits the given blob of data.

Parameters:

  • blob (String)

    the data to emit



60
61
62
# File 'lib/bindef.rb', line 60

def emit(blob)
  @output << blob
end

#f32(num) {|blob| ... } ⇒ void

This method returns an undefined value.

Emits a float.

Parameters:

  • num (Numeric)

    the number to emit

Yields:

  • (blob)


110
111
112
113
114
115
116
117
118
119
120
# File 'lib/bindef.rb', line 110

def f32(num)
  # NOTE: All floats are double-precision in Ruby, so I don't have a good
  # (read: simple) way to validate single-precision floats yet.

  fmt = pragmas[:endian] == :big ? "g" : "e"
  blob = blobify num, fmt

  yield blob if block_given?

  emit blob
end

#f64(num) {|blob| ... } ⇒ void

This method returns an undefined value.

Emits a double.

Parameters:

  • num (Numeric)

    the number to emit

Yields:

  • (blob)

Raises:



125
126
127
128
129
130
131
132
133
134
135
# File 'lib/bindef.rb', line 125

def f64(num)
  raise CommandError, "#{num} is an invalid double-precision float" if num.to_f.nan?

  fmt = pragmas[:endian] == :big ? "G" : "E"

  blob = blobify num, fmt

  yield blob if block_given?

  emit blob
end

#i16(num) ⇒ void

Note:

Uses the :endian #pragma

This method returns an undefined value.

Emits a int16_t.

Parameters:

  • num (Integer)

    the number to emit



194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
# File 'lib/bindef.rb', line 194

ENDIANDED_INTEGER_COMMAND_MAP.each do |cmd, fmt|
  define_method cmd do |num, &block|
    warning "#{num} in #{cmd} command is negative" if num.negative? && cmd[0] == "u"
    validate_int_width! num, cmd[1..-1].to_i

    end_fmt = pragmas[:endian] == :big ? "#{fmt}>" : "#{fmt}<"

    blob = blobify num, end_fmt

    # Fun fact: You can't use `yield` in `define_method`.
    block&.call(blob)

    emit blob
  end
end

#i32(num) ⇒ void

Note:

Uses the :endian #pragma

This method returns an undefined value.

Emits a int32_t.

Parameters:

  • num (Integer)

    the number to emit



194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
# File 'lib/bindef.rb', line 194

ENDIANDED_INTEGER_COMMAND_MAP.each do |cmd, fmt|
  define_method cmd do |num, &block|
    warning "#{num} in #{cmd} command is negative" if num.negative? && cmd[0] == "u"
    validate_int_width! num, cmd[1..-1].to_i

    end_fmt = pragmas[:endian] == :big ? "#{fmt}>" : "#{fmt}<"

    blob = blobify num, end_fmt

    # Fun fact: You can't use `yield` in `define_method`.
    block&.call(blob)

    emit blob
  end
end

#i64(num) ⇒ void

Note:

Uses the :endian #pragma

This method returns an undefined value.

Emits a int64_t.

Parameters:

  • num (Integer)

    the number to emit



194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
# File 'lib/bindef.rb', line 194

ENDIANDED_INTEGER_COMMAND_MAP.each do |cmd, fmt|
  define_method cmd do |num, &block|
    warning "#{num} in #{cmd} command is negative" if num.negative? && cmd[0] == "u"
    validate_int_width! num, cmd[1..-1].to_i

    end_fmt = pragmas[:endian] == :big ? "#{fmt}>" : "#{fmt}<"

    blob = blobify num, end_fmt

    # Fun fact: You can't use `yield` in `define_method`.
    block&.call(blob)

    emit blob
  end
end

#i8(num) {|blob| ... } ⇒ void

This method returns an undefined value.

Emits a int8_t.

Parameters:

  • num (Integer)

    the number to emit

Yields:

  • (blob)


154
155
156
157
158
159
160
161
162
# File 'lib/bindef.rb', line 154

def i8(num)
  validate_int_width! num, 8

  blob = blobify num, "c"

  yield blob if block_given?

  emit blob
end

#pragma(**hsh) {|void| ... } ⇒ void

This method returns an undefined value.

Changes the values of the given pragma keys.

Examples:

pragma verbose: true # changes the `:verbose` pragma to `true` for the remainder of the script
pragma encoding: "utf-16" { str "foobar" } # changes the `:encoding` pragma for the block only

Parameters:

  • hsh (Hash)

    the keys and values to update the pragma state with

Yields:

  • (void)

    A temporary scope for the pragma changes, if a block is given

See Also:



78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/bindef.rb', line 78

def pragma(**hsh)
  old_pragmas = pragmas.dup

  hsh.each do |key, value|
    raise PragmaError, "unknown pragma: #{key}" unless @pragmas.key? key
    raise PragmaError, "bad pragma value: #{value}" unless PRAGMA_SCHEMA[key].include?(value)

    pragmas[key] = value
  end

  if block_given?
    yield
    pragmas.replace old_pragmas
  end
end

#str(string) {|blob| ... } ⇒ void

Note:

Uses the :encoding #pragma

This method returns an undefined value.

Emits a string.

Parameters:

  • string (String)

    the string to emit

Yields:

  • (blob)


98
99
100
101
102
103
104
105
# File 'lib/bindef.rb', line 98

def str(string)
  enc_string = string.encode pragmas[:encoding]
  blob = blobify enc_string, "a#{enc_string.bytesize}"

  yield blob if block_given?

  emit blob
end

#u16(num) ⇒ void

Note:

Uses the :endian #pragma

This method returns an undefined value.

Emits a uint16_t.

Parameters:

  • num (Integer)

    the number to emit



194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
# File 'lib/bindef.rb', line 194

ENDIANDED_INTEGER_COMMAND_MAP.each do |cmd, fmt|
  define_method cmd do |num, &block|
    warning "#{num} in #{cmd} command is negative" if num.negative? && cmd[0] == "u"
    validate_int_width! num, cmd[1..-1].to_i

    end_fmt = pragmas[:endian] == :big ? "#{fmt}>" : "#{fmt}<"

    blob = blobify num, end_fmt

    # Fun fact: You can't use `yield` in `define_method`.
    block&.call(blob)

    emit blob
  end
end

#u32(num) ⇒ void

Note:

Uses the :endian #pragma

This method returns an undefined value.

Emits a uint32_t.

Parameters:

  • num (Integer)

    the number to emit



194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
# File 'lib/bindef.rb', line 194

ENDIANDED_INTEGER_COMMAND_MAP.each do |cmd, fmt|
  define_method cmd do |num, &block|
    warning "#{num} in #{cmd} command is negative" if num.negative? && cmd[0] == "u"
    validate_int_width! num, cmd[1..-1].to_i

    end_fmt = pragmas[:endian] == :big ? "#{fmt}>" : "#{fmt}<"

    blob = blobify num, end_fmt

    # Fun fact: You can't use `yield` in `define_method`.
    block&.call(blob)

    emit blob
  end
end

#u64(num) ⇒ void

Note:

Uses the :endian #pragma

This method returns an undefined value.

Emits a uint64_t.

Parameters:

  • num (Integer)

    the number to emit



194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
# File 'lib/bindef.rb', line 194

ENDIANDED_INTEGER_COMMAND_MAP.each do |cmd, fmt|
  define_method cmd do |num, &block|
    warning "#{num} in #{cmd} command is negative" if num.negative? && cmd[0] == "u"
    validate_int_width! num, cmd[1..-1].to_i

    end_fmt = pragmas[:endian] == :big ? "#{fmt}>" : "#{fmt}<"

    blob = blobify num, end_fmt

    # Fun fact: You can't use `yield` in `define_method`.
    block&.call(blob)

    emit blob
  end
end

#u8(num) {|blob| ... } ⇒ void

This method returns an undefined value.

Emits a uint8_t.

Parameters:

  • num (Integer)

    the number to emit

Yields:

  • (blob)


140
141
142
143
144
145
146
147
148
149
# File 'lib/bindef.rb', line 140

def u8(num)
  warning "#{num} in u8 command is negative" if num.negative?
  validate_int_width! num, 8

  blob = blobify num, "C"

  yield blob if block_given?

  emit blob
end

#validate_int_width!(num, width) ⇒ void

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

This method returns an undefined value.

Ensures that the given integer number can be represented within the given width of bits.

Parameters:

  • num (Integer)

    the number to test

  • width (Integer)

    the bit width to test against

Raises:



44
45
46
# File 'lib/bindef.rb', line 44

def validate_int_width!(num, width)
  raise CommandError, "width of #{num} exceeds #{width} bits" if num.bit_length > width
end

#verbose(msg) ⇒ void

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Note:

Uses the :verbose #pragma

This method returns an undefined value.

Writes a message to the error I/O if verbose mode is enabled.

Parameters:

  • msg (String)

    the message to write



26
27
28
# File 'lib/bindef.rb', line 26

def verbose(msg)
  @error.puts "V: #{msg}" if @pragmas[:verbose]
end

#warning(msg) ⇒ void

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

This method returns an undefined value.

Writes a warning message to the error I/O.

Parameters:

  • msg (String)

    the warning message to write



34
35
36
# File 'lib/bindef.rb', line 34

def warning(msg)
  @error.puts "W: #{msg}" if @pragmas[:warnings]
end