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.3"

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:



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

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

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



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

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



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

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)


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

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:



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

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



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

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



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

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



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

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)


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

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:



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

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)


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

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



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

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



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

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



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

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)


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

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