bindef 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 1b724be52120b58e47c96e9f4b07571290196299c8a2a3c8b09dceee182516ee
4
+ data.tar.gz: 5d3953bb96c9622dddbd31590931b181fac46c20e942a8b51e23ac34db41d4b5
5
+ SHA512:
6
+ metadata.gz: d1d74f2d7167ec0eac702b3a57daae8e36da2f46b3877578abd5a33e9c7d1779a593ac2f812a704869356b55003c290d5e1592e2d1b1ac32e70a9a56ddce4689
7
+ data.tar.gz: c19b67e1c2908207e2cd447e38d7943225b804acfb0698c28cf4d31f55f0e197583435964be4c13b737994ab8768fc4e9f3d8430af8e1cbc5fdf656804922462
data/.yardopts ADDED
@@ -0,0 +1 @@
1
+ --no-private --markup=markdown - *.md LICENSE
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2018 William Woodruff <william @ yossarian.net>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,85 @@
1
+ bindef
2
+ ========
3
+
4
+ `bindef` is a DSL and command-line tool for building binary files.
5
+
6
+ It's inspired by [t2b](https://github.com/thosakwe/t2b), but with a few crucial differences:
7
+
8
+ * `bindef` scripts run within a Ruby process, making the DSL a strict superset of Ruby
9
+ * Support for different (and multiple!) endians, string encodings, etc, is baked into the language
10
+ * Reports common mistakes loudly as warnings (or errors, if severe enough)
11
+ * Comes with a collection of user-selectable extensions for common binary formats (TLVs, control
12
+ codes, etc.)
13
+
14
+ ## Syntax
15
+
16
+ `bindef`'s syntax is stream-oriented, with two primary expressions: commands and pragmas.
17
+
18
+ Commands cause `bindef` to emit data, while pragmas influence *how* commands act.
19
+
20
+ Here's a simple `bindef` script that emits a unsigned 32-bit integer twice, in different endians:
21
+
22
+ ```ruby
23
+ # `bindef` starts in little-endian, so this is redundant
24
+ pragma endian: :little
25
+
26
+ u32 0xFF000000
27
+
28
+ # or `pragma :endian => :big`, if you prefer
29
+ pragma endian: :big
30
+
31
+ u32 0xFF000000
32
+ ```
33
+
34
+ The [examples directory](examples/) has more. Read the [SYNTAX](SYNTAX.md) file for a
35
+ complete listing of commands and pragmas.
36
+
37
+ ## Installation
38
+
39
+ `bindef` is available via RubyGems:
40
+
41
+ ```bash
42
+ $ gem install bindef
43
+ $ bd -h
44
+ ```
45
+
46
+ You can also run it directly from this repository:
47
+
48
+ ```bash
49
+ $ ruby -Ilib ./bin/bd -h
50
+ ```
51
+
52
+ ## Usage
53
+
54
+ In general, running a `bindef` script is as simple as:
55
+
56
+ ```bash
57
+ $ bd < path/to/input.bd > path/to/output.bin
58
+ ```
59
+
60
+ or:
61
+
62
+ ```bash
63
+ $ bd -i path/to/input.bd -o /path/to/output.bin
64
+ ```
65
+
66
+ You can also choose to enable one or more *extra* command sets via the `-e`, `--extra` flag:
67
+
68
+ ```bash
69
+ # extra commands for building TLVs
70
+ $ bd -e tlv < input.bd > output.bin
71
+
72
+ # extra commands for ASCII control codes and string manipulation
73
+ $ bd -e ctrl,string < input.bd > output.bin
74
+ ```
75
+
76
+ ## Design goals
77
+
78
+ `bindef` should...
79
+
80
+ * ...have no runtime dependencies other than the current stable Ruby
81
+ * ...be easy to read, even without knowledge of Ruby's syntax
82
+ * ...be easy to write, even without knowledge of Ruby's syntax
83
+ * ...be easy for other programs to emit without being (too) aware of Ruby's syntax
84
+ * ...be easy to debug, with warnings and errors for common mistakes (overflows, negative
85
+ unsigned integers, etc.)
data/SYNTAX.md ADDED
@@ -0,0 +1,130 @@
1
+ `bindef` syntax
2
+ ===============
3
+
4
+ As mentioned in the [README](README.md), `bindef` has two primary expressions: *commands* and
5
+ *pragmas*.
6
+
7
+ This page documents the default commands and all pragmas.
8
+
9
+ ## Commands
10
+
11
+ `bindef` has commands for emitting integers, floating-point numbers, and strings.
12
+
13
+ All commands take a *value*. That value can be literal or a Ruby expression.
14
+
15
+ ### Integers
16
+
17
+ The integer commands map directly to their `stdint.h` types: a `u8` is a `uint8_t`,
18
+ an `i64` is an `int64_t`, and so on.
19
+
20
+ ```ruby
21
+ # Emits a uint8_t with the value 127.
22
+ u8 127
23
+
24
+ # Emits a int32_t with the value 0.
25
+ i32 0
26
+
27
+ # Produces a warning (negative value used with unsigned command), but still works.
28
+ u16 -100
29
+
30
+ # Produces an error (value too large for command).
31
+ i16 (2**32) - 1
32
+ ```
33
+
34
+ ### Floating-point numbers
35
+
36
+ There are two floating-point commands: `f32` for single-precision, and `f64` for double-precision.
37
+
38
+ ```ruby
39
+ # Transparent promotion of an integer to a float.
40
+ f32 0
41
+
42
+ # Ruby floating-point constants work..
43
+ f64 Math::PI
44
+ f64 Float::INFINITY
45
+ ```
46
+
47
+ ### Strings
48
+
49
+ There is only one string command: `str`. `str` does *not* add a terminating NUL, a newline, or
50
+ anything else to the specified string.
51
+
52
+ ```ruby
53
+ str "hello world!"
54
+
55
+ # Add a terminating NUL.
56
+ str "look ma, a C-string!\x00"
57
+
58
+ # Ruby's string interpolation works.
59
+ foo = "bar baz quux"
60
+ str "#{foo}!\n"
61
+ ```
62
+
63
+ ## Pragmas
64
+
65
+ Pragmas tell `bindef` *how* to emit commands. They tell commands what endianness they should
66
+ use, what encoding strings are in, and how loud to be about warnings and informational messages.
67
+
68
+ Pragmas are global settings, meaning that setting one will cause it to remain set until explicitly
69
+ changed to another value. This can be tedious to keep track of (and makes it easy to introduce
70
+ transposition bugs), so the `pragma` expression provides a block form:
71
+
72
+ ```ruby
73
+ # Changes the verbosity setting to true, but only for the duration of the block.
74
+ pragma verbose: true do
75
+ str "foo"
76
+ end
77
+ ```
78
+
79
+ ### `verbose`
80
+
81
+ Default: `false`.
82
+
83
+ Tells `bindef` how verbose to be. Verbose messages are prefixed with `V: ` and are logged to
84
+ `stderr`.
85
+
86
+ ### `warnings`
87
+
88
+ Default: `true`.
89
+
90
+ Tells `bindef` whether or not to emit warnings, which are nonfatal (unlike errors). Warning
91
+ messages are prefixed with `W: ` and are logged to `stderr`.
92
+
93
+ ```ruby
94
+ # Or use the block form above.
95
+ pragma warnings: false
96
+ u8 -127
97
+ pragma warnings: true
98
+ ```
99
+
100
+ ### `endian`
101
+
102
+ Default: `:little`.
103
+
104
+ Tells `bindef` which endianness to use when emitting integers and floats.
105
+
106
+ ```ruby
107
+ pragma endian: :big
108
+ u16 0x00FF
109
+ u16 0xFF00
110
+ ```
111
+
112
+ ### `encoding`
113
+
114
+ Default: `"utf-8"`
115
+
116
+ Tells `bindef` how to encode the strings it emits via `str`.
117
+
118
+ ```ruby
119
+ pragma encoding: "ascii"
120
+ str "this is a plain old ASCII string"
121
+
122
+ pragma encoding: "utf-8"
123
+ str "this👏is👏a👏utf-8👏string"
124
+
125
+ pragma encoding: "utf-32"
126
+ str "this is a waste of space"
127
+ ```
128
+
129
+ `encoding` expects a lowercase string, and can take any of the
130
+ [encodings supported by Ruby](https://ruby-doc.org/core/Encoding.html#method-c-name_list).
data/bin/bd ADDED
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "optparse"
5
+
6
+ require "bindef"
7
+
8
+ options = {
9
+ input: STDIN,
10
+ output: STDOUT,
11
+ error: STDERR,
12
+ verbose: false,
13
+ warnings: true,
14
+ }
15
+
16
+ at_exit do
17
+ %i[input output error].each { |io| options[io].close }
18
+ end
19
+
20
+ OptionParser.new do |parser|
21
+ parser.banner = "Usage: bd [options]"
22
+
23
+ parser.on "-i", "--input FILE", String, "Read input from FILE (default: stdin)" do |input|
24
+ abort("Error: No such file: #{input}") unless File.file?(input)
25
+ options[:input] = File.open(input, "r")
26
+ end
27
+
28
+ parser.on "-o", "--output FILE", String, "Write output to FILE (default: stderr)" do |output|
29
+ abort("Error: #{output} exists, not overwriting") if File.file?(output)
30
+ options[:output] = File.open(output, "wb")
31
+ end
32
+
33
+ parser.on "-v", "--verbose", "Write verbose/debugging information to stderr" do
34
+ options[:verbose] = true
35
+ end
36
+
37
+ parser.on "-W", "--no-warnings", "Suppress warning messages" do
38
+ options[:warnings] = false
39
+ end
40
+
41
+ parser.on "-e", "--extra ext1,ext2,ext3", Array, "Extra command set(s) to load" do |extras|
42
+ extras.each { |e| require "bindef/extras/#{e}" }
43
+ end
44
+ end.parse!
45
+
46
+ bindef = Bindef.new(options[:output],
47
+ options[:error],
48
+ verbose: options[:verbose],
49
+ warnings: options[:warnings])
50
+
51
+ options[:error].puts "W: output looks like a tty" if options[:output].tty? && options[:verbose]
52
+
53
+ begin
54
+ bindef.instance_eval options[:input].read, "input", 1
55
+ rescue Bindef::EvaluationError => e
56
+ options[:error].puts "E: #{e.message}"
57
+ # Pop the main and instance_eval scopes off of the backtrace.
58
+ e.backtrace.pop 2
59
+ raise e
60
+ end
data/lib/bindef.rb ADDED
@@ -0,0 +1,208 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "bindef/version"
4
+ require_relative "bindef/schemas"
5
+ require_relative "bindef/exceptions"
6
+
7
+ # The primary namespace for {Bindef}.
8
+ class Bindef
9
+ include Bindef::Schemas
10
+
11
+ # @return [Hash] the map of current pragma settings
12
+ attr_reader :pragmas
13
+
14
+ # @api private
15
+ def initialize(output = STDOUT, error = STDERR, verbose: false, warnings: true)
16
+ @output = output
17
+ @error = error
18
+ @pragmas = DEFAULT_PRAGMAS.dup.update(verbose: verbose, warnings: warnings)
19
+ end
20
+
21
+ # Writes a message to the error I/O if verbose mode is enabled.
22
+ # @note Uses the `:verbose` {#pragma}
23
+ # @param msg [String] the message to write
24
+ # @return [void]
25
+ # @api private
26
+ def verbose(msg)
27
+ @error.puts "V: #{msg}" if @pragmas[:verbose]
28
+ end
29
+
30
+ # Writes a warning message to the error I/O.
31
+ # @param msg [String] the warning message to write
32
+ # @return [void]
33
+ # @api private
34
+ def warning(msg)
35
+ @error.puts "W: #{msg}" if @pragmas[:warnings]
36
+ end
37
+
38
+ # Ensures that the given integer number can be represented within the given width of bits.
39
+ # @param num [Integer] the number to test
40
+ # @param width [Integer] the bit width to test against
41
+ # @return [void]
42
+ # @raise [CommandError] if the number is wider than `width` bits
43
+ # @api private
44
+ def validate_int_width!(num, width)
45
+ raise CommandError, "width of #{num} exceeds #{width} bits" if num.bit_length > width
46
+ end
47
+
48
+ # Builds a string containing the given value packed into the given format.
49
+ # @param value [Object] the value to emit
50
+ # @param fmt [String] the `Array#pack` format to emit it in
51
+ def blobify(value, fmt)
52
+ [value].pack(fmt)
53
+ end
54
+
55
+ # Emits the given blob of data.
56
+ # @param blob [String] the data to emit
57
+ # @return [void]
58
+ # @api private
59
+ def emit(blob)
60
+ @output << blob
61
+ end
62
+
63
+ # Captures unknown commands and raises an appropriate error.
64
+ # @api private
65
+ def method_missing(*args)
66
+ raise CommandError, "unknown command: #{args.join(" ")}"
67
+ end
68
+
69
+ # Changes the values of the given pragma keys.
70
+ # @see PRAGMA_SCHEMA
71
+ # @param hsh [Hash] the keys and values to update the pragma state with
72
+ # @yield [void] A temporary scope for the pragma changes, if a block is given
73
+ # @return [void]
74
+ # @example
75
+ # pragma verbose: true # changes the `:verbose` pragma to `true` for the remainder of the script
76
+ # pragma encoding: "utf-16" { str "foobar" } # changes the `:encoding` pragma for the block only
77
+ def pragma(**hsh)
78
+ old_pragmas = pragmas.dup
79
+
80
+ hsh.each do |key, value|
81
+ raise PragmaError, "unknown pragma: #{key}" unless @pragmas.key? key
82
+ raise PragmaError, "bad pragma value: #{value}" unless PRAGMA_SCHEMA[key].include?(value)
83
+
84
+ pragmas[key] = value
85
+ end
86
+
87
+ if block_given?
88
+ yield
89
+ pragmas.replace old_pragmas
90
+ end
91
+ end
92
+
93
+ # Emits a string.
94
+ # @note Uses the `:encoding` {#pragma}
95
+ # @param string [String] the string to emit
96
+ # @return [void]
97
+ def str(string)
98
+ enc_string = string.encode pragmas[:encoding]
99
+ blob = blobify enc_string, "a#{enc_string.bytesize}"
100
+
101
+ yield blob if block_given?
102
+
103
+ emit blob
104
+ end
105
+
106
+ # Emits a `float`.
107
+ # @param num [Numeric] the number to emit
108
+ # @return [void]
109
+ def f32(num)
110
+ # NOTE: All floats are double-precision in Ruby, so I don't have a good
111
+ # (read: simple) way to validate single-precision floats yet.
112
+
113
+ fmt = pragmas[:endian] == :big ? "g" : "e"
114
+ blob = blobify num, fmt
115
+
116
+ yield blob if block_given?
117
+
118
+ emit blob
119
+ end
120
+
121
+ # Emits a `double`.
122
+ # @param num [Numeric] the number to emit
123
+ # @return [void]
124
+ def f64(num)
125
+ raise CommandError, "#{num} is an invalid double-precision float" if num.to_f.nan?
126
+
127
+ fmt = pragmas[:endian] == :big ? "G" : "E"
128
+
129
+ blob = blobify num, fmt
130
+
131
+ yield blob if block_given?
132
+
133
+ emit blob
134
+ end
135
+
136
+ # Emits a `uint8_t`.
137
+ # @param num [Integer] the number to emit
138
+ # @return [void]
139
+ def u8(num)
140
+ warning "#{num} in u8 command is negative" if num.negative?
141
+ validate_int_width! num, 8
142
+
143
+ blob = blobify num, "C"
144
+
145
+ yield blob if block_given?
146
+
147
+ emit blob
148
+ end
149
+
150
+ # Emits a `int8_t`.
151
+ # @param num [Integer] the number to emit
152
+ # @return [void]
153
+ def i8(num)
154
+ validate_int_width! num, 8
155
+
156
+ blob = blobify num, "c"
157
+
158
+ yield blob if block_given?
159
+
160
+ emit blob
161
+ end
162
+
163
+ # @!method u16(num)
164
+ # Emits a `uint16_t`.
165
+ # @note Uses the `:endian` {#pragma}
166
+ # @param num [Integer] the number to emit
167
+ # @return [void]
168
+ # @!method u32(num)
169
+ # Emits a `uint32_t`.
170
+ # @note Uses the `:endian` {#pragma}
171
+ # @param num [Integer] the number to emit
172
+ # @return [void]
173
+ # @!method u64(num)
174
+ # Emits a `uint64_t`.
175
+ # @note Uses the `:endian` {#pragma}
176
+ # @param num [Integer] the number to emit
177
+ # @return [void]
178
+ # @!method i16(num)
179
+ # Emits a `int16_t`.
180
+ # @note Uses the `:endian` {#pragma}
181
+ # @param num [Integer] the number to emit
182
+ # @return [void]
183
+ # @!method i32(num)
184
+ # Emits a `int32_t`.
185
+ # @note Uses the `:endian` {#pragma}
186
+ # @param num [Integer] the number to emit
187
+ # @return [void]
188
+ # @!method i64(num)
189
+ # Emits a `int64_t`.
190
+ # @note Uses the `:endian` {#pragma}
191
+ # @param num [Integer] the number to emit
192
+ # @return [void]
193
+ ENDIANDED_INTEGER_COMMAND_MAP.each do |cmd, fmt|
194
+ define_method cmd do |num, &block|
195
+ warning "#{num} in #{cmd} command is negative" if num.negative? && cmd[0] == "u"
196
+ validate_int_width! num, cmd[1..-1].to_i
197
+
198
+ end_fmt = pragmas[:endian] == :big ? "#{fmt}>" : "#{fmt}<"
199
+
200
+ blob = blobify num, end_fmt
201
+
202
+ # Fun fact: You can't use `yield` in `define_method`.
203
+ block&.call(blob)
204
+
205
+ emit blob
206
+ end
207
+ end
208
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Bindef
4
+ # The base exception for all {Bindef} errors.
5
+ class BindefError < RuntimeError
6
+ end
7
+
8
+ # Raised during an error in evaluation.
9
+ class EvaluationError < BindefError
10
+ end
11
+
12
+ # Raised during an error in pragma evaluation.
13
+ class PragmaError < EvaluationError
14
+ end
15
+
16
+ # Raised during an error in command evaluation.
17
+ class CommandError < EvaluationError
18
+ end
19
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "extras/string"
4
+ require_relative "extras/int128"
5
+ require_relative "extras/tlv"
6
+ require_relative "extras/ctrl"
7
+
8
+ class Bindef
9
+ # Extra (optional) commands.
10
+ module Extras
11
+ end
12
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Bindef
4
+ module Extras
5
+ # Helpers for {Extras::Ctrl}.
6
+ module CtrlHelper
7
+ # A sequential list of symbolic names for control codes.
8
+ # @api private
9
+ CONTROL_NAMES = %i[
10
+ nul soh stx etx eot enq ack bel bs ht lf vt ff cr so si dle dc1 dc2 dc3 dc4 nak syn etb
11
+ can em sub esc fs gs rs us
12
+ ].freeze
13
+
14
+ # A mapping of symbolic names for control codes to their numeric values.
15
+ # @api private
16
+ CONTROL_MAP = CONTROL_NAMES.zip(0x00..0x1F).to_h.freeze
17
+ end
18
+
19
+ # Potentially useful control character commands.
20
+ module Ctrl
21
+ CtrlHelper::CONTROL_MAP.each do |cmd, value|
22
+ define_method cmd do
23
+ u8 value
24
+ end
25
+ end
26
+ end
27
+ end
28
+
29
+ include Extras::Ctrl
30
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Bindef
4
+ module Extras
5
+ # Potentially useful 128-bit integer emission commands.
6
+ module Int128
7
+ # Emits a `__uint128_t`.
8
+ # @note Uses the `:endian` {Bindef#pragma}
9
+ # @param num [Integer] the number to emit
10
+ # @return [void]
11
+ def u128(num)
12
+ upper = num >> 64
13
+ lower = num & (2**64 - 1)
14
+
15
+ if pragmas[:endian] == big
16
+ u64 upper
17
+ u64 lower
18
+ else
19
+ u64 lower
20
+ u64 upper
21
+ end
22
+ end
23
+
24
+ # Emits a `__int128_t`.
25
+ # @note Uses the `:endian` {Bindef#pragma}
26
+ # @param num [Integer] the number to emit
27
+ # @return [void]
28
+ def i128(num)
29
+ upper = num >> 64
30
+ lower = num & (2**64 - 1)
31
+
32
+ if pragmas[:endian] == big
33
+ i64 upper
34
+ u64 lower
35
+ else
36
+ u64 lower
37
+ i64 upper
38
+ end
39
+ end
40
+ end
41
+ end
42
+
43
+ include Extras::Int128
44
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Bindef
4
+ module Extras
5
+ # Potentially useful extra string emission commands.
6
+ module String
7
+ # Emits a null-terminated string.
8
+ # @note Like {Bindef#str}, uses the `:encoding` {Bindef#pragma}
9
+ # @param string [String] the string to emit
10
+ # @return [void]
11
+ # @example
12
+ # strz "foobar" # Emits "foobar\x00"
13
+ def strz(string)
14
+ str string
15
+ str "\0"
16
+ end
17
+
18
+ # Emits a string, NUL-padded up to the given length in bytes.
19
+ # @note Like {Bindef#str}, uses the `:encoding` {Bindef#pragma}
20
+ # @param string [String] the string to emit
21
+ # @param maxpad [Integer] the maximum number of padding NULs
22
+ # @return [void]
23
+ # @example
24
+ # strnz "foo", 5 # Emits "foo\x00\x00"
25
+ def strnz(string, maxpad)
26
+ pad = maxpad
27
+
28
+ str string do |enc_string|
29
+ pad = maxpad - enc_string.bytesize
30
+ end
31
+
32
+ raise CommandError, "maxpad < encoded string len" if pad.negative?
33
+
34
+ # Reset our encoding temporarily, to make sure we emit the right number of NULs.
35
+ pragma encoding: "utf-8" do
36
+ str("\x00" * pad)
37
+ end
38
+ end
39
+
40
+ # Emits a length-prefixed string.
41
+ # @note Like {Bindef#str}, uses the `:encoding` {Bindef#pragma}
42
+ # @note Like {Bindef#u16} and wider, the `:endian` {Bindef#pragma}
43
+ # @param int_fmt [Symbol] the width of the length prefix, as one of the integer commands
44
+ # @param string [String] the string to emit
45
+ # @return [void]
46
+ # @example
47
+ # lstr :u8, "foo" # Emits "\x03foo"
48
+ def lstr(int_fmt, string)
49
+ str string do |enc_string|
50
+ send int_fmt, enc_string.bytesize
51
+ end
52
+ end
53
+ end
54
+ end
55
+
56
+ include Extras::String
57
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Bindef
4
+ module Extras
5
+ # Potentially useful TLV (tag-length-value) commands.
6
+ #
7
+ # Writing a single sufficiently generic TLV command without
8
+ # really abusing Ruby's syntax is hard. Instead, we provide
9
+ # specialized commands for empirically common TLV widths.
10
+ module TLV
11
+ # Emit a `uint8_t` type, a `uint8_t` length, and a value.
12
+ # @param type [Integer] the type number
13
+ # @param hsh [Hash] a mapping of `command => value`
14
+ # @example
15
+ # tlv_u8 1, u32: 0xFF00FF00 # Emits: "\x01\x04\xFF\x00\xFF\x00"
16
+ # tlv_u8 2, str: "hello" # Emits: "\x02\x05hello"
17
+ def tlv_u8(type, hsh)
18
+ u8 type
19
+
20
+ cmd, value = hsh.shift
21
+
22
+ send cmd, value do |blob|
23
+ u8 blob.bytesize
24
+ end
25
+ end
26
+
27
+ # Emit a `uint16_t` type, a `uint16_t` length, and a value.
28
+ # @param type [Integer] the type number
29
+ # @param hsh [Hash] a mapping of `command => value`
30
+ # @see tlv_u8
31
+ def tlv_u16(type, hsh)
32
+ u16 type
33
+
34
+ cmd, value = hsh.shift
35
+
36
+ send cmd, value do |blob|
37
+ u16 blob.bytesize
38
+ end
39
+ end
40
+
41
+ # Emit a `uint32_t` type, a `uint32_t` length, and a value.
42
+ # @param type [Integer] the type number
43
+ # @param hsh [Hash] a mapping of `command => value`
44
+ # @see tlv_u8
45
+ def tlv_u32(type, hsh)
46
+ u32 type
47
+
48
+ cmd, value = hsh.shift
49
+
50
+ send cmd, value do |blob|
51
+ u32 blob.bytesize
52
+ end
53
+ end
54
+ end
55
+ end
56
+
57
+ include Extras::TLV
58
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Bindef
4
+ # Schemas used to validate commands and pragmas throughout {Bindef}.
5
+ module Schemas
6
+ # A mapping of valid pragma keys to lists of valid pragma values.
7
+ PRAGMA_SCHEMA = {
8
+ verbose: [true, false],
9
+ warnings: [true, false],
10
+ endian: %i[big little],
11
+ encoding: Encoding.name_list.map(&:downcase),
12
+ }.freeze
13
+
14
+ # The default pragma settings.
15
+ DEFAULT_PRAGMAS = {
16
+ verbose: false,
17
+ warnings: true,
18
+ endian: :little,
19
+ encoding: "utf-8",
20
+ }.freeze
21
+
22
+ # A map of endianded integer commands to `Array#pack` formats.
23
+ # @api private
24
+ ENDIANDED_INTEGER_COMMAND_MAP = {
25
+ u16: "S",
26
+ u32: "L",
27
+ u64: "Q",
28
+ i16: "s",
29
+ i32: "l",
30
+ i64: "q",
31
+ }.freeze
32
+ end
33
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Bindef
4
+ # The current {Bindef} version.
5
+ VERSION = "0.0.1"
6
+ end
metadata ADDED
@@ -0,0 +1,72 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bindef
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - William Woodruff
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-08-25 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: yard
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.9.9
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.9.9
27
+ description:
28
+ email: william@yossarian.net.com
29
+ executables:
30
+ - bd
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - ".yardopts"
35
+ - LICENSE
36
+ - README.md
37
+ - SYNTAX.md
38
+ - bin/bd
39
+ - lib/bindef.rb
40
+ - lib/bindef/exceptions.rb
41
+ - lib/bindef/extras.rb
42
+ - lib/bindef/extras/ctrl.rb
43
+ - lib/bindef/extras/int128.rb
44
+ - lib/bindef/extras/string.rb
45
+ - lib/bindef/extras/tlv.rb
46
+ - lib/bindef/schemas.rb
47
+ - lib/bindef/version.rb
48
+ homepage:
49
+ licenses:
50
+ - MIT
51
+ metadata: {}
52
+ post_install_message:
53
+ rdoc_options: []
54
+ require_paths:
55
+ - lib
56
+ required_ruby_version: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: 2.3.0
61
+ required_rubygems_version: !ruby/object:Gem::Requirement
62
+ requirements:
63
+ - - ">="
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ requirements: []
67
+ rubyforge_project:
68
+ rubygems_version: 2.7.6
69
+ signing_key:
70
+ specification_version: 4
71
+ summary: bindef - A DSL and command-line tool for generating binary files
72
+ test_files: []