Skip to content

Commit 9ae3475

Browse files
committed
feat: Support other endian
1 parent 8e9ed56 commit 9ae3475

File tree

8 files changed

+102
-27
lines changed

8 files changed

+102
-27
lines changed

spec/binary_parser_spec.cr

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,58 +2,92 @@ require "./spec_helper"
22
require "./fixture/*"
33

44
describe BinaryParser do
5+
describe "#load" do
6+
it "load binary" do
7+
parser = UInt32Parser.new.load(UInt32Fixture)
8+
UInt32Fixture.rewind
9+
10+
expect(parser.value).to eq(42)
11+
end
12+
13+
it "respect to IO::ByteFormat" do
14+
parser = UInt32Parser.new.load(BigEndianUInt32Fixture, IO::ByteFormat::BigEndian)
15+
BigEndianUInt32Fixture.rewind
16+
17+
expect(parser.value).to eq(42)
18+
end
19+
end
20+
21+
describe "#endian" do
22+
it "work correct" do
23+
parser = BigEndianParser.new.load(BigEndianUInt32Fixture)
24+
BigEndianUInt32Fixture.rewind
25+
26+
expect(parser.value).to eq(42)
27+
end
28+
end
29+
530
describe "#uint64" do
631
it "parse correct" do
732
parser = UInt64Parser.new.load(UInt64Fixture)
33+
834
expect(parser.value).to eq(42)
935
end
1036
end
1137

1238
describe "#uint32" do
1339
it "parse correct" do
1440
parser = UInt32Parser.new.load(UInt32Fixture)
41+
UInt32Fixture.rewind
42+
1543
expect(parser.value).to eq(42)
1644
end
1745
end
1846

1947
describe "#uint16" do
2048
it "parse correct" do
2149
parser = UInt16Parser.new.load(UInt16Fixture)
50+
2251
expect(parser.value).to eq(42)
2352
end
2453
end
2554

2655
describe "#uint8" do
2756
it "parse correct" do
2857
parser = UInt8Parser.new.load(UInt8Fixture)
58+
2959
expect(parser.value).to eq(42)
3060
end
3161
end
3262

3363
describe "#int64" do
3464
it "parse correct" do
3565
parser = Int64Parser.new.load(Int64Fixture)
66+
3667
expect(parser.value).to eq(42)
3768
end
3869
end
3970

4071
describe "#int32" do
4172
it "parse correct" do
4273
parser = Int32Parser.new.load(Int32Fixture)
74+
4375
expect(parser.value).to eq(42)
4476
end
4577
end
4678

4779
describe "#int16" do
4880
it "parse correct" do
4981
parser = Int16Parser.new.load(Int16Fixture)
82+
5083
expect(parser.value).to eq(42)
5184
end
5285
end
5386

5487
describe "#int8" do
5588
it "parse correct" do
5689
parser = Int8Parser.new.load(Int8Fixture)
90+
5791
expect(parser.value).to eq(42)
5892
end
5993
end
@@ -62,13 +96,15 @@ describe BinaryParser do
6296
context "when fixed length" do
6397
it "parse correct" do
6498
parser = FixedArrayParser.new.load(FixedArrayFixture)
99+
65100
expect(parser.arr).to eq([42, 43, 44, 45, 46])
66101
end
67102
end
68103

69104
context "when variable length" do
70105
it "parse correct" do
71106
parser = VarArrayParser.new.load(VarArrayFixture)
107+
72108
expect(parser.size).to eq(5)
73109
expect(parser.arr).to eq([42, 43, 44, 45, 46])
74110
end

spec/fixture/endian.cr

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
require "../../src/binary_parser"
2+
3+
class BigEndianParser < BinaryParser
4+
endian :big
5+
uint32 :value
6+
end
7+
8+
9+
BigEndianUInt32Fixture = IO::Memory.new(sizeof(UInt32))
10+
BigEndianUInt32Fixture.write_bytes(42u32, IO::ByteFormat::BigEndian)
11+
BigEndianUInt32Fixture.rewind

src/binary_parser.cr

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,27 +18,27 @@ require "./binary_parser/macros/*"
1818
class BinaryParser
1919
# Load from file with `filename`
2020
#
21-
def load(filename : String)
21+
def load(filename : String, format : IO::ByteFormat = IO::ByteFormat::SystemEndian)
2222
io = File.open(filename)
23-
load(io)
23+
load(io, format)
2424
end
2525

2626
# Load from an IO object
2727
#
28-
def load(io : IO)
28+
def load(io : IO, format : IO::ByteFormat = IO::ByteFormat::SystemEndian)
2929
{% for method in @type.methods %}
3030
{% if method.name.starts_with?("_read_") %}
31-
{{method.name}}(io)
31+
{{method.name}}(io, format)
3232
{% end %}
3333
{% end %}
3434
self
3535
end
3636

3737
# Save to file with `filename`
3838
#
39-
def save(filename : String)
39+
def save(filename : String, format : IO::ByteFormat = IO::ByteFormat::SystemEndian)
4040
io = File.open(filename, "w")
41-
write(io)
41+
write(io, format)
4242
end
4343

4444
# Convert to string
@@ -53,28 +53,26 @@ class BinaryParser
5353

5454
# Write to an IO object
5555
#
56-
def write(io : IO)
56+
def write(io : IO, format : IO::ByteFormat = IO::ByteFormat::SystemEndian)
5757
{% for method in @type.methods %}
5858
{% if method.name.starts_with?("_write_") %}
59-
{{method.name}}(io)
59+
{{method.name}}(io, format)
6060
{% end %}
6161
{% end %}
6262
self
6363
end
6464

6565
# Support for `IO#read_bytes`
6666
#
67-
# NOTE: Currently not respect to `IO::ByteFormat`
6867
def self.from_io(io : IO, format : IO::ByteFormat)
6968
ins = self.new
70-
ins.load(io)
69+
ins.load(io, format)
7170
end
7271

7372
# Support for `IO#write_bytes`
7473
#
75-
# NOTE: Currently not respect to `IO::ByteFormat`
7674
def to_io(io : IO, format : IO::ByteFormat)
77-
write(io)
75+
write(io, format)
7876
end
7977
end
8078

src/binary_parser/macros/_primary_type.cr

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,14 @@ class BinaryParser
1919
property! :{{name.id}}
2020
@{{name.id}} = 0{{suffix.id}}
2121

22-
def _read_{{name.id}}(io : IO)
23-
@{{name.id}} = io.not_nil!.read_bytes({{type.id}}).as({{type.id}})
22+
def _read_{{name.id}}(io : IO, format : IO::ByteFormat = IO::ByteFormat::SystemEndian)
23+
format = endian_config if format == IO::ByteFormat::SystemEndian
24+
@{{name.id}} = io.not_nil!.read_bytes({{type.id}}, format).as({{type.id}})
2425
end
2526

26-
def _write_{{name.id}}(io : IO)
27-
io.not_nil!.write_bytes(@{{name.id}}.not_nil!)
27+
def _write_{{name.id}}(io : IO, format : IO::ByteFormat = IO::ByteFormat::SystemEndian)
28+
format = format == IO::ByteFormat::SystemEndian ? endian_config : format
29+
io.not_nil!.write_bytes(@{{name.id}}.not_nil!, format)
2830
end
2931

3032
def _size_static_{{name.id}}

src/binary_parser/macros/array.cr

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,24 +26,24 @@ class BinaryParser
2626
property! :{{name.id}}
2727
@{{name.id}} = [] of {{opt[:type]}}
2828

29-
def _read_{{name.id}}(io : IO)
29+
def _read_{{name.id}}(io : IO, format : IO::ByteFormat = IO::ByteFormat::SystemEndian)
3030
{% if opt[:count].is_a?(NumberLiteral) %}
3131
@{{name.id}} = Array({{opt[:type]}}).new({{opt[:count]}}) do
32-
io.read_bytes({{opt[:type]}})
32+
io.read_bytes({{opt[:type]}}, format)
3333
end
3434
{% elsif opt[:count].id != "eof" %}
3535
@{{name.id}} = Array({{opt[:type]}}).new(@{{opt[:count].id}}.not_nil!) do
36-
io.read_bytes({{opt[:type]}})
36+
io.read_bytes({{opt[:type]}}, format)
3737
end
3838
{% else %}
3939
@{{name.id}} = [] of {{opt[:type]}}
4040
# TODO: support :eof
4141
{% end %}
4242
end
4343

44-
def _write_{{name.id}}(io : IO)
44+
def _write_{{name.id}}(io : IO, format : IO::ByteFormat = IO::ByteFormat::SystemEndian)
4545
@{{name.id}}.not_nil!.each do |item|
46-
io.write_bytes(item)
46+
io.write_bytes(item, format)
4747
end
4848
end
4949

src/binary_parser/macros/endian.cr

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
class BinaryParser
2+
@@endian : Symbol = :system
3+
4+
# Define what endian to use. `endian` can be either `:little` or `:big`
5+
#
6+
# This macro should be place at the top of class, and can only call **zero** or **one** time.
7+
#
8+
# ```crystal
9+
# endian
10+
# ```
11+
macro endian(type)
12+
{% raise "Endian should be either :big or :little" if type != :big && type != :little %}
13+
@@endian = {{type}}
14+
end
15+
16+
private def endian_config
17+
case @@endian
18+
when :little
19+
IO::ByteFormat::LittleEndian
20+
when :big
21+
IO::ByteFormat::BigEndian
22+
when :system
23+
IO::ByteFormat::SystemEndian
24+
else # This shouldn't happen
25+
raise "Endian type error"
26+
end
27+
end
28+
end

src/binary_parser/macros/string.cr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ class BinaryParser
2525
@{{name.id}} = ""
2626

2727
# TODO: Refactor
28-
def _read_{{name.id}}(io : IO)
28+
def _read_{{name.id}}(io : IO, _format : IO::ByteFormat = IO::ByteFormat::SystemEndian)
2929
{% if opt[:count].is_a?(NumberLiteral) %}
3030
{% if opt[:count] != -1 %}
3131
buf = Slice(UInt8).new({{opt[:count]}})
@@ -45,7 +45,7 @@ class BinaryParser
4545
{% end %}
4646
end
4747

48-
def _write_{{name.id}}(io : IO)
48+
def _write_{{name.id}}(io : IO, _format : IO::ByteFormat = IO::ByteFormat::SystemEndian)
4949
{% if opt[:count].is_a?(NumberLiteral) %}
5050
{% if opt[:count] != -1 %}
5151
slice = Slice(UInt8).new({{opt[:count]}})

src/binary_parser/macros/type.cr

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,13 @@ class BinaryParser
2020
property! :{{name.id}}
2121
@{{name.id}} = {{klass}}.new
2222

23-
def _read_{{name.id}}(io : IO)
23+
def _read_{{name.id}}(io : IO, format : IO::ByteFormat = IO::ByteFormat::SystemEndian)
2424
{% raise "Must inhert BinaryParser" if BinaryParser < klass.resolve %}
25-
@{{name.id}} = io.read_bytes({{klass}}).as({{klass}})
25+
@{{name.id}} = io.read_bytes({{klass}}, format).as({{klass}})
2626
end
2727

28-
def _write_{{name.id}}(io : IO)
29-
io.write_bytes(@{{name.id}}.not_nil!)
28+
def _write_{{name.id}}(io : IO, format : IO::ByteFormat = IO::ByteFormat::SystemEndian)
29+
io.write_bytes(@{{name.id}}.not_nil!, format)
3030
end
3131

3232
def _size_dyn_{{name.id}}

0 commit comments

Comments
 (0)