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

rubocop:disable Style/Documentation

Defined Under Namespace

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

Constant Summary collapse

VERSION =

The current Bindef version.

"0.0.6"

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)


115
116
117
118
119
120
121
122
123
124
125
# File 'lib/bindef.rb', line 115

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:



130
131
132
133
134
135
136
137
138
139
140
# File 'lib/bindef.rb', line 130

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



199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
# File 'lib/bindef.rb', line 199

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



199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
# File 'lib/bindef.rb', line 199

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



199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
# File 'lib/bindef.rb', line 199

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)


159
160
161
162
163
164
165
166
167
# File 'lib/bindef.rb', line 159

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:



83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# File 'lib/bindef.rb', line 83

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

  return unless block_given?

  yield
  pragmas.replace old_pragmas
end

#respond_to_missing?(*_args) ⇒ Boolean

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:

  • (Boolean)


71
72
73
# File 'lib/bindef.rb', line 71

def respond_to_missing?(*_args)
  true
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)


103
104
105
106
107
108
109
110
# File 'lib/bindef.rb', line 103

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



199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
# File 'lib/bindef.rb', line 199

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



199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
# File 'lib/bindef.rb', line 199

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



199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
# File 'lib/bindef.rb', line 199

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)


145
146
147
148
149
150
151
152
153
154
# File 'lib/bindef.rb', line 145

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