Skip to content

Commit

Permalink
Showing 2 changed files with 258 additions and 100 deletions.
270 changes: 171 additions & 99 deletions spec/std/io/byte_format_spec.cr
Original file line number Diff line number Diff line change
@@ -22,90 +22,134 @@ end
describe IO::ByteFormat do
describe "little endian" do
describe "encode" do
it "writes int8" do
io = IO::Memory.new
io.write_bytes 0x12_i8, IO::ByteFormat::LittleEndian
assert_bytes io, 0x12
end
describe "to io" do
it "writes int8" do
io = IO::Memory.new
io.write_bytes 0x12_i8, IO::ByteFormat::LittleEndian
assert_bytes io, 0x12
end

it "writes int16" do
io = IO::Memory.new
io.write_bytes 0x1234_i16, IO::ByteFormat::LittleEndian
assert_bytes io, 0x34, 0x12
end
it "writes int16" do
io = IO::Memory.new
io.write_bytes 0x1234_i16, IO::ByteFormat::LittleEndian
assert_bytes io, 0x34, 0x12
end

it "writes uint16" do
io = IO::Memory.new
io.write_bytes 0x1234_u16, IO::ByteFormat::LittleEndian
assert_bytes io, 0x34, 0x12
end
it "writes uint16" do
io = IO::Memory.new
io.write_bytes 0x1234_u16, IO::ByteFormat::LittleEndian
assert_bytes io, 0x34, 0x12
end

it "writes int32" do
io = IO::Memory.new
io.write_bytes 0x12345678_i32, IO::ByteFormat::LittleEndian
assert_bytes io, 0x78, 0x56, 0x34, 0x12
end
it "writes int32" do
io = IO::Memory.new
io.write_bytes 0x12345678_i32, IO::ByteFormat::LittleEndian
assert_bytes io, 0x78, 0x56, 0x34, 0x12
end

it "writes int64" do
io = IO::Memory.new
io.write_bytes 0x123456789ABCDEF0_i64, IO::ByteFormat::LittleEndian
assert_bytes io, 0xF0, 0xDE, 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12
end
it "writes int64" do
io = IO::Memory.new
io.write_bytes 0x123456789ABCDEF0_i64, IO::ByteFormat::LittleEndian
assert_bytes io, 0xF0, 0xDE, 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12
end

it "writes float32" do
io = IO::Memory.new
io.write_bytes 1.234_f32, IO::ByteFormat::LittleEndian
assert_bytes io, 0xB6, 0xF3, 0x9D, 0x3F
it "writes float32" do
io = IO::Memory.new
io.write_bytes 1.234_f32, IO::ByteFormat::LittleEndian
assert_bytes io, 0xB6, 0xF3, 0x9D, 0x3F
end

it "writes float64" do
io = IO::Memory.new
io.write_bytes 1.234, IO::ByteFormat::LittleEndian
assert_bytes io, 0x58, 0x39, 0xB4, 0xC8, 0x76, 0xBE, 0xF3, 0x3F
end
end

it "writes float64" do
io = IO::Memory.new
io.write_bytes 1.234, IO::ByteFormat::LittleEndian
assert_bytes io, 0x58, 0x39, 0xB4, 0xC8, 0x76, 0xBE, 0xF3, 0x3F
describe "to slice" do
it "writes int8" do
bytes = Bytes[0]
IO::ByteFormat::LittleEndian.encode(0x12_i8, bytes)
bytes.should eq(Bytes[0x12_i8])
end

it "writes int16" do
bytes = Bytes[0, 0]
IO::ByteFormat::LittleEndian.encode(0x1234_i16, bytes)
bytes.should eq(Bytes[0x34, 0x12])
end
end
end

describe "decode" do
it "reads int8" do
io = new_string_io(0x12)
int = io.read_bytes Int8, IO::ByteFormat::LittleEndian
int.should eq(0x12_i8)
end
describe "from io" do
it "reads int8" do
io = new_string_io(0x12)
int = io.read_bytes Int8, IO::ByteFormat::LittleEndian
int.should eq(0x12_i8)
end

it "reads int16" do
io = new_string_io(0x34, 0x12)
int = io.read_bytes Int16, IO::ByteFormat::LittleEndian
int.should eq(0x1234_i16)
end
it "reads int16" do
io = new_string_io(0x34, 0x12)
int = io.read_bytes Int16, IO::ByteFormat::LittleEndian
int.should eq(0x1234_i16)
end

it "reads unt16" do
io = new_string_io(0x34, 0x12)
int = io.read_bytes UInt16, IO::ByteFormat::LittleEndian
int.should eq(0x1234_u16)
end
it "reads unt16" do
io = new_string_io(0x34, 0x12)
int = io.read_bytes UInt16, IO::ByteFormat::LittleEndian
int.should eq(0x1234_u16)
end

it "reads int32" do
io = new_string_io(0x78, 0x56, 0x34, 0x12)
int = io.read_bytes Int32, IO::ByteFormat::LittleEndian
int.should eq(0x12345678_i32)
end
it "reads int32" do
io = new_string_io(0x78, 0x56, 0x34, 0x12)
int = io.read_bytes Int32, IO::ByteFormat::LittleEndian
int.should eq(0x12345678_i32)
end

it "reads int64" do
io = new_string_io(0xF0, 0xDE, 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12)
int = io.read_bytes Int64, IO::ByteFormat::LittleEndian
int.should eq(0x123456789ABCDEF0_i64)
end
it "reads int64" do
io = new_string_io(0xF0, 0xDE, 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12)
int = io.read_bytes Int64, IO::ByteFormat::LittleEndian
int.should eq(0x123456789ABCDEF0_i64)
end

it "reads float32" do
io = new_string_io(0xB6, 0xF3, 0x9D, 0x3F)
float = io.read_bytes Float32, IO::ByteFormat::LittleEndian
float.should be_close(1.234, 0.0001)
end

it "reads float32" do
io = new_string_io(0xB6, 0xF3, 0x9D, 0x3F)
float = io.read_bytes Float32, IO::ByteFormat::LittleEndian
float.should be_close(1.234, 0.0001)
it "reads float64" do
io = new_string_io(0x58, 0x39, 0xB4, 0xC8, 0x76, 0xBE, 0xF3, 0x3F)
float = io.read_bytes Float64, IO::ByteFormat::LittleEndian
float.should be_close(1.234, 0.0001)
end
end

it "reads float64" do
io = new_string_io(0x58, 0x39, 0xB4, 0xC8, 0x76, 0xBE, 0xF3, 0x3F)
float = io.read_bytes Float64, IO::ByteFormat::LittleEndian
float.should be_close(1.234, 0.0001)
describe "from slice" do
it "reads int8" do
bytes = Bytes[0x12]
int = IO::ByteFormat::LittleEndian.decode(Int8, bytes)
int.should eq(0x12_i8)
end

it "reads int16" do
bytes = Bytes[0x34, 0x12]
int = IO::ByteFormat::LittleEndian.decode(Int16, bytes)
int.should eq(0x1234_i16)
end

it "reads float32" do
bytes = Bytes[0xB6, 0xF3, 0x9D, 0x3F]
float = IO::ByteFormat::LittleEndian.decode(Float32, bytes)
float.should be_close(1.234, 0.0001)
end

it "reads float64" do
bytes = Bytes[0x58, 0x39, 0xB4, 0xC8, 0x76, 0xBE, 0xF3, 0x3F]
float = IO::ByteFormat::LittleEndian.decode(Float64, bytes)
float.should be_close(1.234, 0.0001)
end
end
end
end
@@ -150,46 +194,74 @@ describe IO::ByteFormat do
end

describe "decode" do
it "reads int8" do
io = new_string_io(0x12)
int = io.read_bytes Int8, IO::ByteFormat::BigEndian
int.should eq(0x12_i8)
end
describe "from io" do
it "reads int8" do
io = new_string_io(0x12)
int = io.read_bytes Int8, IO::ByteFormat::BigEndian
int.should eq(0x12_i8)
end

it "reads int16" do
io = new_string_io(0x12, 0x34)
int = io.read_bytes Int16, IO::ByteFormat::BigEndian
int.should eq(0x1234_i16)
end
it "reads int16" do
io = new_string_io(0x12, 0x34)
int = io.read_bytes Int16, IO::ByteFormat::BigEndian
int.should eq(0x1234_i16)
end

it "reads unt16" do
io = new_string_io(0x12, 0x34)
int = io.read_bytes UInt16, IO::ByteFormat::BigEndian
int.should eq(0x1234_u16)
end
it "reads unt16" do
io = new_string_io(0x12, 0x34)
int = io.read_bytes UInt16, IO::ByteFormat::BigEndian
int.should eq(0x1234_u16)
end

it "reads int32" do
io = new_string_io(0x12, 0x34, 0x56, 0x78)
int = io.read_bytes Int32, IO::ByteFormat::BigEndian
int.should eq(0x12345678_i32)
end
it "reads int32" do
io = new_string_io(0x12, 0x34, 0x56, 0x78)
int = io.read_bytes Int32, IO::ByteFormat::BigEndian
int.should eq(0x12345678_i32)
end

it "reads int64" do
io = new_string_io(0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0)
int = io.read_bytes Int64, IO::ByteFormat::BigEndian
int.should eq(0x123456789ABCDEF0_i64)
end
it "reads int64" do
io = new_string_io(0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0)
int = io.read_bytes Int64, IO::ByteFormat::BigEndian
int.should eq(0x123456789ABCDEF0_i64)
end

it "reads float32" do
io = new_string_io(0x3F, 0x9D, 0xF3, 0xB6)
float = io.read_bytes Float32, IO::ByteFormat::BigEndian
float.should be_close(1.234, 0.0001)
it "reads float32" do
io = new_string_io(0x3F, 0x9D, 0xF3, 0xB6)
float = io.read_bytes Float32, IO::ByteFormat::BigEndian
float.should be_close(1.234, 0.0001)
end

it "reads float64" do
io = new_string_io(0x3F, 0xF3, 0xBE, 0x76, 0xC8, 0xB4, 0x39, 0x58)
float = io.read_bytes Float64, IO::ByteFormat::BigEndian
float.should be_close(1.234, 0.0001)
end
end

it "reads float64" do
io = new_string_io(0x3F, 0xF3, 0xBE, 0x76, 0xC8, 0xB4, 0x39, 0x58)
float = io.read_bytes Float64, IO::ByteFormat::BigEndian
float.should be_close(1.234, 0.0001)
describe "from slice" do
it "reads int8" do
bytes = Bytes[0x12]
int = IO::ByteFormat::BigEndian.decode(Int8, bytes)
int.should eq(0x12_i8)
end

it "reads int16" do
bytes = Bytes[0x12, 0x34]
int = IO::ByteFormat::BigEndian.decode(Int16, bytes)
int.should eq(0x1234_i16)
end

it "reads float32" do
bytes = Bytes[0x3F, 0x9D, 0xF3, 0xB6]
float = IO::ByteFormat::BigEndian.decode(Float32, bytes)
float.should be_close(1.234, 0.0001)
end

it "reads float64" do
bytes = Bytes[0x3F, 0xF3, 0xBE, 0x76, 0xC8, 0xB4, 0x39, 0x58]
float = IO::ByteFormat::BigEndian.decode(Float64, bytes)
float.should be_close(1.234, 0.0001)
end
end
end
end
88 changes: 87 additions & 1 deletion src/io/byte_format.cr
Original file line number Diff line number Diff line change
@@ -1,4 +1,37 @@
# Defines a byte format to encode integers and floats.
# Defines a byte format to encode integers and floats
# from/to `Bytes` and `IO`.
#
# ### Decode from bytes
#
# ```
# bytes = Bytes[0x34, 0x12]
# int16 = IO::ByteFormat::LittleEndian.decode(Int16, bytes)
# int16 # => 0x1234_i16
# ```
#
# ### Decode from an IO
#
# ```
# io = MemoryIO.new(Bytes[0x34, 0x12])

This comment has been minimized.

Copy link
@luislavena

luislavena Nov 23, 2016

Contributor

We might need to rename MemoryIO to IO::Memory (guess old habits) 😄

This comment has been minimized.

Copy link
@asterite

asterite Nov 23, 2016

Member

Oh... didn't notice. Thanks!

# int16 = io.read_bytes(Int16, IO::ByteFormat::LittleEndian)
# int16 # => 0x1234_i16
# ```
#
# ### Encode to bytes
#
# ```
# bytes = uninitialized UInt8[2]
# IO::ByteFormat::LittleEndian.encode(0x1234_i16, bytes.to_slice)
# bytes # => Bytes[0x34, 0x12]
# ```
#
# ### Encode to IO
#
# ```
# io = MemoryIO.new
# io.write_bytes(0x1234_i16, IO::ByteFormat::LittleEndian)
# io.to_slice # => Bytes[0x34, 0x12]
# ```
module IO::ByteFormat
abstract def encode(int : Int8, io : IO)
abstract def encode(int : UInt8, io : IO)
@@ -11,6 +44,17 @@ module IO::ByteFormat
abstract def encode(int : Float32, io : IO)
abstract def encode(int : Float64, io : IO)

abstract def encode(int : Int8, bytes : Bytes)
abstract def encode(int : UInt8, bytes : Bytes)
abstract def encode(int : Int16, bytes : Bytes)
abstract def encode(int : UInt16, bytes : Bytes)
abstract def encode(int : Int32, bytes : Bytes)
abstract def encode(int : UInt32, bytes : Bytes)
abstract def encode(int : Int64, bytes : Bytes)
abstract def encode(int : UInt64, bytes : Bytes)
abstract def encode(int : Float32, bytes : Bytes)
abstract def encode(int : Float64, bytes : Bytes)

abstract def decode(int : Int8.class, io : IO)
abstract def decode(int : UInt8.class, io : IO)
abstract def decode(int : Int16.class, io : IO)
@@ -22,24 +66,53 @@ module IO::ByteFormat
abstract def decode(int : Float32.class, io : IO)
abstract def decode(int : Float64.class, io : IO)

abstract def decode(int : Int8.class, bytes : Bytes)
abstract def decode(int : UInt8.class, bytes : Bytes)
abstract def decode(int : Int16.class, bytes : Bytes)
abstract def decode(int : UInt16.class, bytes : Bytes)
abstract def decode(int : Int32.class, bytes : Bytes)
abstract def decode(int : UInt32.class, bytes : Bytes)
abstract def decode(int : Int64.class, bytes : Bytes)
abstract def decode(int : UInt64.class, bytes : Bytes)
abstract def decode(int : Float32.class, bytes : Bytes)
abstract def decode(int : Float64.class, bytes : Bytes)

def encode(float : Float32, io : IO)
encode(pointerof(float).as(Int32*).value, io)
end

def encode(float : Float32, bytes : Bytes)
encode(pointerof(float).as(Int32*).value, bytes)
end

def decode(type : Float32.class, io : IO)
int = decode(Int32, io)
pointerof(int).as(Float32*).value
end

def decode(type : Float32.class, bytes : Bytes)
int = decode(Int32, bytes)
pointerof(int).as(Float32*).value
end

def encode(float : Float64, io : IO)
encode(pointerof(float).as(Int64*).value, io)
end

def encode(float : Float64, bytes : Bytes)
encode(pointerof(float).as(Int64*).value, bytes)
end

def decode(type : Float64.class, io : IO)
int = decode(Int64, io)
pointerof(int).as(Float64*).value
end

def decode(type : Float64.class, bytes : Bytes)
int = decode(Int64, bytes)
pointerof(int).as(Float64*).value
end

module LittleEndian
extend ByteFormat
end
@@ -60,12 +133,25 @@ module IO::ByteFormat
io.write(buffer.to_slice)
end

def self.encode(int : {{type.id}}, bytes : Bytes)
buffer = pointerof(int).as(UInt8[{{2 ** (i / 2)}}]*).value
buffer.reverse! unless SystemEndian == self
buffer.to_slice.copy_to(bytes)
end

def self.decode(type : {{type.id}}.class, io : IO)
buffer = uninitialized UInt8[{{2 ** (i / 2)}}]
io.read_fully(buffer.to_slice)
buffer.reverse! unless SystemEndian == self
buffer.to_unsafe.as(Pointer({{type.id}})).value
end

def self.decode(type : {{type.id}}.class, bytes : Bytes)
buffer = uninitialized UInt8[{{2 ** (i / 2)}}]
bytes.copy_to(buffer.to_slice)
buffer.reverse! unless SystemEndian == self
buffer.to_unsafe.as(Pointer({{type.id}})).value
end
{% end %}
end
{% end %}

0 comments on commit d7b4bd3

Please sign in to comment.