Skip to content

Commit

Permalink
feat: simplify syntax and deprecate old helpers
Browse files Browse the repository at this point in the history
  • Loading branch information
stakach committed Feb 1, 2024
1 parent 2575786 commit ca35ec0
Show file tree
Hide file tree
Showing 9 changed files with 200 additions and 118 deletions.
33 changes: 15 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ This means the programmer specifies what the format of the binary data is, and B

[![Build Status](https://github.com/spider-gazelle/bindata/actions/workflows/CI.yml/badge.svg?branch=master)](https://github.com/spider-gazelle/bindata/actions/workflows/CI.yml)


## Usage

Firstly, it's recommended that you specify the datas endian.
Expand All @@ -20,8 +19,8 @@ end
Then you can specify the structures fields. There are a few different field types:

1. Core types
* `uint8`, `int128` which would accept `UInt8` and `Int128` values respectively
* You can use endian-aware types to mix endianess: ex `uint32le` will read `UInt32` regardless of `endian` set in class
* `UInt8` to `UInt128` values respectively
* You can use endian-aware types to mix endianess: `, endian: IO::ByteFormat::LittleEndian`
2. Custom types
* anything that is [io serialisable](https://crystal-lang.org/api/0.27.2/IO.html#write_bytes%28object%2Cformat%3AIO%3A%3AByteFormat%3DIO%3A%3AByteFormat%3A%3ASystemEndian%29-instance-method)
3. Bit Fields
Expand All @@ -32,7 +31,7 @@ Then you can specify the structures fields. There are a few different field type
* Useful when a group of fields are related or optional
5. Enums
6. Bools
6. Arrays (fixed size and dynamic)
6. Arrays and Sets (fixed size and dynamic)


### Examples
Expand All @@ -50,35 +49,35 @@ see the [spec helper](https://github.com/spider-gazelle/bindata/blob/master/spec
endian big
# Default sets the value at initialisation.
uint8 :start, default: 0xFF_u8
field start : UInt8 = 0xFF_u8
# Value procs assign these values before writing to an IO, overwriting any
# existing value
uint16 :size, value: ->{ text.bytesize + 1 }
field size : UInt16, value: ->{ text.bytesize + 1 }
# String fields without a length use `\0` null byte termination
# Length is being calculated by the size field above
string :text, length: ->{ size - 1 }
field text : String, length: ->{ size - 1 }
# Bit fields should only be used when one or more fields are not byte aligned
# The sum of the bits in a bit field must be divisible by 8
bit_field do
# a bits value can be between 1 and 128 bits long
bits 5, :reserved
bits 5, reserved
# Bool values are a single bit
bool :set_input, default: false
bool set_input = false
# This enum is represented by 2 bits
enum_bits 2, input : Inputs = Inputs::HDMI2
bits 2, input : Inputs = Inputs::HDMI2
end
# isolated namespace
group :extended, onlyif: ->{ start == 0xFF } do
uint8 :start, default: 0xFF_u8
field start : UInt8 = 0xFF_u8
# Supports custom objects as long as they implement `from_io`
custom header : ExtHeader = ExtHeader.new
field header : ExtHeader = ExtHeader.new
end
# optionally read the remaining bytes out of io
Expand All @@ -103,9 +102,9 @@ Additionally, BinData fields support a `verify` proc, which allows data to be ve
class VerifyData < BinData
endian big
uint8 :size
bytes :bytes, length: ->{ size }
uint8 :checksum, verify: ->{ checksum == bytes.reduce(0) { |acc, i| acc + i } }
field size : UInt8
field bytes : Bytes, length: ->{ size }
field checksum : UInt8, verify: ->{ checksum == bytes.reduce(0) { |acc, i| acc + i } }
end
```

Expand All @@ -117,7 +116,6 @@ Failed to verify reading basic at VerifyData.checksum

Inheritance is also supported


## ASN.1 Helpers

Included in this library are helpers for decoding and writing ASN.1 data, such as those used in SNMP and LDAP
Expand All @@ -139,8 +137,7 @@ ber.tag_class # => ASN1::BER::TagClass::Universal
```


## Real World Examples:
## Real World Examples

* ASN.1
* https://github.com/crystal-community/jwt/blob/master/src/jwt.cr#L251
Expand Down
2 changes: 1 addition & 1 deletion shard.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ version: 2.0
shards:
ameba:
git: https://github.com/veelenga/ameba.git
version: 1.2.0
version: 1.6.1

2 changes: 1 addition & 1 deletion shard.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: bindata
version: 1.11.1
version: 2.0.0
crystal: ">= 1.0.0"

development_dependencies:
Expand Down
2 changes: 1 addition & 1 deletion spec/bindata_enum_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -53,5 +53,5 @@ class Packet < BinData
Reply = 0x0111
end

enum_field UInt16, type : Type = Type::Command
field type : Type = Type::Command
end
68 changes: 36 additions & 32 deletions spec/helper.cr
Original file line number Diff line number Diff line change
Expand Up @@ -5,63 +5,68 @@ require "../src/bindata/asn1"
class Header < BinData
endian little

int32 :size, value: ->{ name.bytesize }
string :name, length: ->{ size }
field size : Int32, value: ->{ name.bytesize }
field name : String, length: ->{ size }
end

class Body < BinData
endian big

uint8 :start, value: ->{ 0_u8 }
field start : UInt8, value: ->{ 0_u8 }

bit_field onlyif: ->{ start == 0 } do
bits 6, :six, value: ->{ 0b1110_11_u8 }
bits 3, :three, default: 0b011
bits 4, :four, value: ->{ 0b1001_u8 }
# bits 3, :three, default: 0b011
bits 3, three = 0b011
bits 4, four, value: ->{ 0b1001_u8 }
bits 11, :teen, value: ->{ 0b1101_1111_101_u16 }
end

uint8 :mid, value: ->{ 0_u8 }
field mid : UInt8, value: ->{ 0_u8 }

bit_field do
bits 52, :five, value: ->{ 0xF0_E0_D0_C0_B0_A0_9_u64 }
bits 12, :eight, value: ->{ 0x104_u16 }
end

uint8 :end, value: ->{ 0_u8 }
field end : UInt8, value: ->{ 0_u8 }
end

class Wow < BinData
endian big

uint8 :start, value: ->{ 0_u8 }
field start : UInt8, value: ->{ 0_u8 }

# this is a shortcut for the `Header < BinData` class
header :head

group :body, onlyif: ->{ head.size > 0 } do
uint8 :start, value: ->{ 1_u8 }, onlyif: ->{ parent.start == 0 }
uint8 :end, value: ->{ 3_u8 }
field start : UInt8, value: ->{ 1_u8 }, onlyif: ->{ parent.start == 0 }
field end : UInt8, value: ->{ 3_u8 }
end

uint8 :end, value: ->{ 0_u8 }
field end : UInt8, value: ->{ 0_u8 }
end

class EnumData < BinData
endian big

enum Inputs
enum Inputs : UInt16
VGA
HDMI
HDMI2
end

uint8 :start, value: ->{ 0_u8 }
enum_field UInt16, inputs : Inputs = Inputs::HDMI
field start : UInt8, value: ->{ 0_u8 }
field inputs : Inputs = Inputs::HDMI

bit_field do
bits 5, :reserved
bool enabled, default: false
enum_bits 2, input : Inputs = Inputs::HDMI2
bool enabled = false
bits 2, input : Inputs = Inputs::HDMI2
end
uint8 :end, value: ->{ 0_u8 }

field end : UInt8, value: ->{ 0_u8 }
end

class Inherited < EnumData
Expand Down Expand Up @@ -94,36 +99,36 @@ end
class ArrayData < BinData
endian big

uint8 :flen, default: 1, value: ->{ first.size }
array first : Int16 = [15_i16], length: ->{ flen }
uint8 :slen, value: ->{ 0_u8 | second.size }
array second : Int8, length: ->{ slen }
field flen : UInt8 = 1, value: ->{ first.size }
field first : Array(Int16) = [15_i16], length: ->{ flen }
field slen : UInt8, value: ->{ 0_u8 | second.size }
field second : Array(Int8), length: ->{ slen }
end

class VariableArrayData < BinData
endian big

uint8 :total_size
variable_array test : UInt8, read_next: ->{
field total_size : UInt8
field test : Array(Int8), read_next: ->{
# Will continue reading data into the array until
# the array size + 2 buffer bytes equals the total size
(test.size + 2) < total_size
}
uint8 :afterdata, default: 1
field afterdata : UInt8 = 1
end

class VerifyData < BinData
endian big

uint8 :size
bytes :bytes, length: ->{ size }
uint8 :checksum, verify: ->{ checksum == bytes.reduce(0) { |acc, i| acc + i } }
field size : UInt8
field bytes : Bytes, length: ->{ size }
field checksum : UInt8, verify: ->{ checksum == bytes.reduce(0) { |acc, i| acc + i } }
end

class RemainingBytesData < BinData
endian big

uint8 :first
field first : UInt8
remaining_bytes :rest, onlyif: ->{ first == 0x02 }, verify: ->{ rest.size % 2 == 0 }
end

Expand All @@ -139,8 +144,7 @@ end
class MixedEndianLittle < BinData
endian :little

int16be :big
int32le :little

int128 :default
field big : Int16, endian: IO::ByteFormat::BigEndian
field little : Int32, endian: IO::ByteFormat::LittleEndian
field default : Int128
end
Loading

0 comments on commit ca35ec0

Please sign in to comment.