bindef 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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: []