Initial project import

This commit is contained in:
Agila Forge Importer committed 2026-07-13 15:20:32 +00:00
commit 23f2d29e32
11957 files changed
+1754577

No files matched your search

+142
View File
@@ -0,0 +1,142 @@
## Description
`encoding.binary` contains utility functions for converting between an array of bytes (`[]u8`)
and unsigned integers of various widths (`u16`, `u32`, and `u64`).
Also, it provide functions `encode_binary[T]()` and `decode_binary[T]()` which can converting
between an array of bytes (`[]u8`) and generic type `T`.
There are two ways in which bytes can be encoded:
1. Little endian: The least significant bytes are stored first, followed by the most
significant bytes.
2. Big endian: The most significant bytes are stored first, opposite to the little endian
convention.
For example, let us take the number `0x12345678`. In little endian, the bytes are extracted as
`0x78`, `0x56`, `0x34`, and `0x12`. In big endian, the bytes are `0x12`, `0x34`, `0x56`,
and `0x78`.
We follow a similar procedure when we want to go the other way around. Consider the second
sequence of bytes in the previous example: `0x12`, `0x34`, `0x56`, and `0x78`. If we encode
this sequence in little endian format, we get the integer `0x78563412`. If we encode this
sequence in big endian, we get `0x12345678`.
> **Note**
> The functions in this module assume appropriately sized u8 arrays. If the sizes
> are not valid, the functions will panic.
For generic `T` data encoding/decoding, you can use `encode_binary[T]()` and `decode_binary[T]()`:
```v oksyntax
module main
import encoding.binary
struct MyStruct {
g_u8 u8
}
struct ComplexStruct {
mut:
f_u8 u8
f_u32 u32 @[serialize: '-'] // this field will be skipped
f_u64 u64
f_string string
f_structs []MyStruct
f_maps []map[string]string
}
fn main() {
a := ComplexStruct{
f_u8: u8(10)
f_u32: u32(1024)
f_u64: u64(2048)
f_string: 'serialize me'
f_structs: [
MyStruct{
g_u8: u8(1)
},
MyStruct{
g_u8: u8(2)
},
MyStruct{
g_u8: u8(3)
},
]
f_maps: [
{
'abc': 'def'
},
{
'123': '456'
},
{
',./': '!@#'
},
]
}
b := binary.encode_binary(a)!
mut c := binary.decode_binary[ComplexStruct](b)!
// because there skipped field in `a`, a != c
assert a != c
c.f_u32 = u32(1024)
assert a == c
}
```
For Go-style fixed-size stream I/O, you can use `read()`, `write()`, and `size()` with
`binary.little_endian` or `binary.big_endian`:
```v
module main
import encoding.binary
import io
struct Header {
version u16
flags u16
length u32
}
struct Buffer {
mut:
data []u8
pos int
}
fn (mut b Buffer) read(mut out []u8) !int {
if b.pos >= b.data.len {
return io.Eof{}
}
n := if out.len < b.data.len - b.pos { out.len } else { b.data.len - b.pos }
copy(mut out[..n], b.data[b.pos..b.pos + n])
b.pos += n
return n
}
fn (mut b Buffer) write(src []u8) !int {
b.data << src
return src.len
}
fn main() {
header := Header{
version: 1
flags: 2
length: 32
}
mut buf := Buffer{}
binary.write(mut buf, binary.big_endian, header)!
assert binary.size(header) == 8
buf.pos = 0
mut decoded := Header{}
binary.read(mut buf, binary.big_endian, mut decoded)!
assert decoded == header
}
```
+276
View File
@@ -0,0 +1,276 @@
// Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
module binary
// big_endian_u16 creates a u16 from the first two bytes in the array b in big endian order.
@[direct_array_access; inline]
pub fn big_endian_u16(b []u8) u16 {
_ = b[1] // bounds check
unsafe {
mut u := U16{}
$if big_endian {
u.b[0], u.b[1] = b[0], b[1]
} $else {
u.b[0], u.b[1] = b[1], b[0]
}
return u.u
}
}
// big_endian_u16_at creates a u16 from two bytes in the array b at the specified offset in big endian order.
@[direct_array_access; inline]
pub fn big_endian_u16_at(b []u8, o int) u16 {
_ = b[o] // bounds check
_ = b[o + 1] // bounds check
unsafe {
mut u := U16{}
$if big_endian {
u.b[0], u.b[1] = b[o], b[o + 1]
} $else {
u.b[0], u.b[1] = b[o + 1], b[o]
}
return u.u
}
}
// big_endian_u16_end creates a u16 from two bytes in the array b at the specified offset in big endian order.
@[direct_array_access; inline]
pub fn big_endian_u16_end(b []u8) u16 {
return big_endian_u16_at(b, b.len - 2)
}
// big_endian_put_u16 writes a u16 to the first two bytes in the array b in big endian order.
@[direct_array_access; inline]
pub fn big_endian_put_u16(mut b []u8, v u16) {
_ = b[1] // bounds check
unsafe {
mut u := U16{
u: v
}
$if big_endian {
b[0], b[1] = u.b[0], u.b[1]
} $else {
b[0], b[1] = u.b[1], u.b[0]
}
}
}
// big_endian_put_u16_at writes a u16 to the two bytes in the array b at the specified offset in big endian order.
@[direct_array_access; inline]
pub fn big_endian_put_u16_at(mut b []u8, v u16, o int) {
_ = b[o] // bounds check
_ = b[o + 1] // bounds check
unsafe {
mut u := U16{
u: v
}
$if big_endian {
b[o], b[o + 1] = u.b[0], u.b[1]
} $else {
b[o], b[o + 1] = u.b[1], u.b[0]
}
}
}
// big_endian_put_u16_end writes a u16 to the last two bytes in the array b in big endian order.
@[direct_array_access; inline]
pub fn big_endian_put_u16_end(mut b []u8, v u16) {
big_endian_put_u16_at(mut b, v, b.len - 2)
}
// big_endian_get_u16 creates u8 array from the unsigned 16-bit integer v in big endian order.
@[direct_array_access; inline]
pub fn big_endian_get_u16(v u16) []u8 {
unsafe {
mut u := U16{
u: v
}
$if little_endian {
u.b[0], u.b[1] = u.b[1], u.b[0]
}
return u.b[..]
}
}
// big_endian_u32 creates a u32 from four bytes in the array b in big endian order.
@[direct_array_access; inline]
pub fn big_endian_u32(b []u8) u32 {
_ = b[3] // bounds check
unsafe {
mut u := U32{}
$if big_endian {
u.b[0], u.b[1], u.b[2], u.b[3] = b[0], b[1], b[2], b[3]
} $else {
u.b[0], u.b[1], u.b[2], u.b[3] = b[3], b[2], b[1], b[0]
}
return u.u
}
}
// big_endian_u32_at creates a u32 from four bytes in the array b at the specified offset in big endian order.
@[direct_array_access; inline]
pub fn big_endian_u32_at(b []u8, o int) u32 {
_ = b[o] // bounds check
_ = b[o + 3] // bounds check
unsafe {
mut u := U32{}
$if big_endian {
u.b[0], u.b[1], u.b[2], u.b[3] = b[o], b[o + 1], b[o + 2], b[o + 3]
} $else {
u.b[0], u.b[1], u.b[2], u.b[3] = b[o + 3], b[o + 2], b[o + 1], b[o]
}
return u.u
}
}
// big_endian_u32_end creates a u32 from the last four bytes in the array b in big endian order.
@[direct_array_access; inline]
pub fn big_endian_u32_end(b []u8) u32 {
return big_endian_u32_at(b, b.len - 4)
}
// big_endian_put_u32 writes a u32 to the first four bytes in the array b in big endian order.
@[direct_array_access; inline]
pub fn big_endian_put_u32(mut b []u8, v u32) {
_ = b[3] // bounds check
unsafe {
mut u := U32{
u: v
}
$if big_endian {
b[0], b[1], b[2], b[3] = u.b[0], u.b[1], u.b[2], u.b[3]
} $else {
b[0], b[1], b[2], b[3] = u.b[3], u.b[2], u.b[1], u.b[0]
}
}
}
// big_endian_put_u32_at writes a u32 to four bytes in the array b at the specified offset in big endian order.
@[direct_array_access; inline]
pub fn big_endian_put_u32_at(mut b []u8, v u32, o int) {
_ = b[o] // bounds check
_ = b[o + 3] // bounds check
unsafe {
mut u := U32{
u: v
}
$if big_endian {
b[o], b[o + 1], b[o + 2], b[o + 3] = u.b[0], u.b[1], u.b[2], u.b[3]
} $else {
b[o], b[o + 1], b[o + 2], b[o + 3] = u.b[3], u.b[2], u.b[1], u.b[0]
}
}
}
// big_endian_put_u32_end writes a u32 to the last four bytes in the array b in big endian order.
@[direct_array_access; inline]
pub fn big_endian_put_u32_end(mut b []u8, v u32) {
big_endian_put_u32_at(mut b, v, b.len - 4)
}
// big_endian_get_u32 creates u8 array from the unsigned 32-bit integer v in big endian order.
@[direct_array_access; inline]
pub fn big_endian_get_u32(v u32) []u8 {
unsafe {
mut u := U32{
u: v
}
$if little_endian {
u.b[0], u.b[1], u.b[2], u.b[3] = u.b[3], u.b[2], u.b[1], u.b[0]
}
return u.b[..]
}
}
// big_endian_u64 creates a u64 from the first eight bytes in the array b in big endian order.
@[direct_array_access; inline]
pub fn big_endian_u64(b []u8) u64 {
_ = b[7] // bounds check
unsafe {
mut u := U64{}
$if big_endian {
u.b[0], u.b[1], u.b[2], u.b[3], u.b[4], u.b[5], u.b[6], u.b[7] = b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]
} $else {
u.b[0], u.b[1], u.b[2], u.b[3], u.b[4], u.b[5], u.b[6], u.b[7] = b[7], b[6], b[5], b[4], b[3], b[2], b[1], b[0]
}
return u.u
}
}
// big_endian_u64_at creates a u64 from eight bytes in the array b at the specified offset in big endian order.
@[direct_array_access; inline]
pub fn big_endian_u64_at(b []u8, o int) u64 {
_ = b[o] // bounds check
_ = b[o + 7] // bounds check
unsafe {
mut u := U64{}
$if big_endian {
u.b[0], u.b[1], u.b[2], u.b[3], u.b[4], u.b[5], u.b[6], u.b[7] = b[o], b[o + 1], b[o + 2], b[
o + 3], b[o + 4], b[o + 5], b[o + 6], b[o + 7]
} $else {
u.b[0], u.b[1], u.b[2], u.b[3], u.b[4], u.b[5], u.b[6], u.b[7] = b[o + 7], b[o + 6], b[
o + 5], b[o + 4], b[o + 3], b[o + 2], b[o + 1], b[o]
}
return u.u
}
}
// big_endian_u64_end creates a u64 from the last eight bytes in the array b in big endian order.
@[direct_array_access; inline]
pub fn big_endian_u64_end(b []u8) u64 {
return big_endian_u64_at(b, b.len - 8)
}
// big_endian_put_u64 writes a u64 to the first eight bytes in the array b in big endian order.
@[direct_array_access; inline]
pub fn big_endian_put_u64(mut b []u8, v u64) {
_ = b[7] // bounds check
unsafe {
mut u := U64{
u: v
}
$if big_endian {
b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7] = u.b[0], u.b[1], u.b[2], u.b[3], u.b[4], u.b[5], u.b[6], u.b[7]
} $else {
b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7] = u.b[7], u.b[6], u.b[5], u.b[4], u.b[3], u.b[2], u.b[1], u.b[0]
}
}
}
// big_endian_put_u64_at writes a u64 to eight bytes in the array b at the specified offset in big endian order.
@[direct_array_access; inline]
pub fn big_endian_put_u64_at(mut b []u8, v u64, o int) {
_ = b[o] // bounds check
_ = b[o + 7] // bounds check
unsafe {
mut u := U64{
u: v
}
$if big_endian {
b[o], b[o + 1], b[o + 2], b[o + 3], b[o + 4], b[o + 5], b[o + 6], b[o + 7] = u.b[0], u.b[1], u.b[2], u.b[3], u.b[4], u.b[5], u.b[6], u.b[7]
} $else {
b[o], b[o + 1], b[o + 2], b[o + 3], b[o + 4], b[o + 5], b[o + 6], b[o + 7] = u.b[7], u.b[6], u.b[5], u.b[4], u.b[3], u.b[2], u.b[1], u.b[0]
}
}
}
// big_endian_put_u64_end writes a u64 to the last eight bytes in the array b at the specified offset in big endian order.
@[direct_array_access; inline]
pub fn big_endian_put_u64_end(mut b []u8, v u64) {
big_endian_put_u64_at(mut b, v, b.len - 8)
}
// big_endian_get_u64 creates u8 array from the unsigned 64-bit integer v in big endian order.
@[direct_array_access; inline]
pub fn big_endian_get_u64(v u64) []u8 {
unsafe {
mut u := U64{
u: v
}
$if little_endian {
u.b[0], u.b[1], u.b[2], u.b[3], u.b[4], u.b[5], u.b[6], u.b[7] = u.b[7], u.b[6], u.b[5], u.b[4], u.b[3], u.b[2], u.b[1], u.b[0]
}
return u.b[..]
}
}
@@ -0,0 +1,91 @@
// Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
module binary
// big_endian_u16_fixed creates a u16 from the first two bytes in the fixed array b in big endian order.
@[direct_array_access; inline]
pub fn big_endian_u16_fixed(b [2]u8) u16 {
unsafe {
mut u := U16{}
$if big_endian {
u.b[0], u.b[1] = b[0], b[1]
} $else {
u.b[0], u.b[1] = b[1], b[0]
}
return u.u
}
}
// big_endian_put_u16_fixed writes a u16 to the fixed array b in big endian order.
@[direct_array_access; inline]
pub fn big_endian_put_u16_fixed(mut b [2]u8, v u16) {
unsafe {
mut u := U16{
u: v
}
$if big_endian {
b[0], b[1] = u.b[0], u.b[1]
} $else {
b[0], b[1] = u.b[1], u.b[0]
}
}
}
// big_endian_u32_fixed creates a u32 from four bytes in the fixed array b in big endian order.
@[direct_array_access; inline]
pub fn big_endian_u32_fixed(b [4]u8) u32 {
unsafe {
mut u := U32{}
$if big_endian {
u.b[0], u.b[1], u.b[2], u.b[3] = b[0], b[1], b[2], b[3]
} $else {
u.b[0], u.b[1], u.b[2], u.b[3] = b[3], b[2], b[1], b[0]
}
return u.u
}
}
// big_endian_put_u32_fixed writes a u32 to the fixed array b in big endian order.
@[direct_array_access; inline]
pub fn big_endian_put_u32_fixed(mut b [4]u8, v u32) {
unsafe {
mut u := U32{
u: v
}
$if big_endian {
b[0], b[1], b[2], b[3] = u.b[0], u.b[1], u.b[2], u.b[3]
} $else {
b[0], b[1], b[2], b[3] = u.b[3], u.b[2], u.b[1], u.b[0]
}
}
}
// big_endian_u64_fixed creates a u64 from the fixed array b in big endian order.
@[direct_array_access; inline]
pub fn big_endian_u64_fixed(b [8]u8) u64 {
unsafe {
mut u := U64{}
$if big_endian {
u.b[0], u.b[1], u.b[2], u.b[3], u.b[4], u.b[5], u.b[6], u.b[7] = b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]
} $else {
u.b[0], u.b[1], u.b[2], u.b[3], u.b[4], u.b[5], u.b[6], u.b[7] = b[7], b[6], b[5], b[4], b[3], b[2], b[1], b[0]
}
return u.u
}
}
// big_endian_put_u64_fixed writes a u64 to the fixed array b in big endian order.
@[direct_array_access; inline]
pub fn big_endian_put_u64_fixed(mut b [8]u8, v u64) {
unsafe {
mut u := U64{
u: v
}
$if big_endian {
b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7] = u.b[0], u.b[1], u.b[2], u.b[3], u.b[4], u.b[5], u.b[6], u.b[7]
} $else {
b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7] = u.b[7], u.b[6], u.b[5], u.b[4], u.b[3], u.b[2], u.b[1], u.b[0]
}
}
}
@@ -0,0 +1,53 @@
module binary
// Big Endian Tests
fn test_big_endian_u16_fixed() {
assert big_endian_u16_fixed([u8(0), 1]!) == u16(1)
assert big_endian_u16_fixed([u8(5), 4]!) == u16(0x0504)
assert big_endian_u16_fixed([u8(0x35), 0x57]!) == u16(0x3557)
assert big_endian_u16_fixed([u8(0x35), 0x57]!) != u16(0x5735)
}
fn test_big_endian_put_u16_fixed() {
mut buf := [2]u8{}
big_endian_put_u16_fixed(mut buf, 0x8725)
assert buf == [u8(0x87), 0x25]!
big_endian_put_u16_fixed(mut buf, 0)
assert buf == [u8(0), 0]!
big_endian_put_u16_fixed(mut buf, 0xfdff)
assert buf == [u8(0xfd), 0xff]!
}
fn test_big_endian_u32_fixed() {
assert big_endian_u32_fixed([u8(0), 0, 0, 1]!) == u32(1)
assert big_endian_u32_fixed([u8(5), 4, 9, 1]!) == u32(0x05040901)
assert big_endian_u32_fixed([u8(0xf8), 0xa2, 0x9e, 0x21]!) == u32(0xf8a29e21)
assert big_endian_u32_fixed([u8(0xf8), 0xa2, 0x9e, 0x21]!) != u32(0x2192a2f8)
}
fn test_big_endian_put_u32_fixed() {
mut buf := [4]u8{}
big_endian_put_u32_fixed(mut buf, 0x872fea95)
assert buf == [u8(0x87), 0x2f, 0xea, 0x95]!
big_endian_put_u32_fixed(mut buf, 0)
assert buf == [u8(0), 0, 0, 0]!
big_endian_put_u32_fixed(mut buf, 0xfdf2e68f)
assert buf == [u8(0xfd), 0xf2, 0xe6, 0x8f]!
}
fn test_big_endian_u64_fixed() {
assert big_endian_u64_fixed([u8(0), 0, 0, 0, 0, 0, 0, 1]!) == u64(1)
assert big_endian_u64_fixed([u8(5), 4, 9, 1, 7, 3, 6, 8]!) == u64(0x0504090107030608)
assert big_endian_u64_fixed([u8(0xf8), 0xa2, 0x9e, 0x21, 0x7f, 0x9f, 0x8e, 0x8f]!) == u64(0xf8a29e217f9f8e8f)
assert big_endian_u64_fixed([u8(0xf8), 0xa2, 0x9e, 0x21, 0x7f, 0x9f, 0x8e, 0x8f]!) != u64(0x8f8e9f7f219ea2f8)
}
fn test_big_endian_put_u64_fixed() {
mut buf := [8]u8{}
big_endian_put_u64_fixed(mut buf, 0x872fea95fdf2e68f)
assert buf == [u8(0x87), 0x2f, 0xea, 0x95, 0xfd, 0xf2, 0xe6, 0x8f]!
big_endian_put_u64_fixed(mut buf, 0)
assert buf == [u8(0), 0, 0, 0, 0, 0, 0, 0]!
big_endian_put_u64_fixed(mut buf, 0xfdf2e68f8e9f7f21)
assert buf == [u8(0xfd), 0xf2, 0xe6, 0x8f, 0x8e, 0x9f, 0x7f, 0x21]!
}
@@ -0,0 +1,192 @@
module binary
// Big Endian Tests
fn test_big_endian_u16() {
assert big_endian_u16([u8(0), 1]) == u16(1)
assert big_endian_u16([u8(5), 4]) == u16(0x0504)
assert big_endian_u16([u8(0x35), 0x57]) == u16(0x3557)
assert big_endian_u16([u8(0x35), 0x57]) != u16(0x5735)
}
fn test_big_endian_u16_at() {
assert big_endian_u16_at([u8(0), 0, 1, 0], 1) == u16(1)
assert big_endian_u16_at([u8(0), 5, 4, 0], 1) == u16(0x0504)
assert big_endian_u16_at([u8(0), 0x35, 0x57, 0], 1) == u16(0x3557)
assert big_endian_u16_at([u8(0), 0x35, 0x57, 0], 1) != u16(0x5735)
}
fn test_big_endian_u16_end() {
assert big_endian_u16_end([u8(0), 0, 0, 1]) == u16(1)
assert big_endian_u16_end([u8(0), 0, 5, 4]) == u16(0x0504)
assert big_endian_u16_end([u8(0), 0, 0x35, 0x57]) == u16(0x3557)
assert big_endian_u16_end([u8(0), 0, 0x35, 0x57]) != u16(0x5735)
}
fn test_big_endian_put_u16() {
mut buf := []u8{len: 2}
big_endian_put_u16(mut buf, 0x8725)
assert buf == [u8(0x87), 0x25]
big_endian_put_u16(mut buf, 0)
assert buf == [u8(0), 0]
big_endian_put_u16(mut buf, 0xfdff)
assert buf == [u8(0xfd), 0xff]
}
fn test_big_endian_put_u16_at() {
mut buf := []u8{len: 4}
big_endian_put_u16_at(mut buf, 0x8725, 1)
assert buf == [u8(0), 0x87, 0x25, 0]
buf = []u8{len: 4}
big_endian_put_u16_at(mut buf, 1, 1)
assert buf == [u8(0), 0, 1, 0]
buf = []u8{len: 4}
big_endian_put_u16_at(mut buf, 0xfdff, 1)
assert buf == [u8(0), 0xfd, 0xff, 0]
}
fn test_big_endian_get_u16() {
assert big_endian_get_u16(u16(1)) == [u8(0), 1]
assert big_endian_get_u16(u16(0x0504)) == [u8(5), 4]
assert big_endian_get_u16(u16(0x3557)) == [u8(0x35), 0x57]
assert big_endian_get_u16(u16(0x5735)) != [u8(0x35), 0x57]
}
fn test_big_endian_u32() {
assert big_endian_u32([u8(0), 0, 0, 1]) == u32(1)
assert big_endian_u32([u8(5), 4, 9, 1]) == u32(0x05040901)
assert big_endian_u32([u8(0xf8), 0xa2, 0x9e, 0x21]) == u32(0xf8a29e21)
assert big_endian_u32([u8(0xf8), 0xa2, 0x9e, 0x21]) != u32(0x2192a2f8)
}
fn test_big_endian_u32_at() {
assert big_endian_u32_at([u8(0), 0, 0, 0, 1, 0, 0, 0], 1) == u32(1)
assert big_endian_u32_at([u8(0), 5, 4, 9, 1, 0, 0, 0], 1) == u32(0x05040901)
assert big_endian_u32_at([u8(0), 0xf8, 0xa2, 0x9e, 0x21, 0, 0, 0], 1) == u32(0xf8a29e21)
assert big_endian_u32_at([u8(0), 0xf8, 0xa2, 0x9e, 0x21, 0, 0, 0], 1) != u32(0x2192a2f8)
}
fn test_big_endian_u32_end() {
assert big_endian_u32_end([u8(0), 0, 0, 0, 0, 0, 1]) == u32(1)
assert big_endian_u32_end([u8(0), 0, 0, 0, 5, 4, 9, 1]) == u32(0x05040901)
assert big_endian_u32_end([u8(0), 0, 0, 0, 0xf8, 0xa2, 0x9e, 0x21]) == u32(0xf8a29e21)
assert big_endian_u32_end([u8(0), 0, 0, 0, 0xf8, 0xa2, 0x9e, 0x21]) != u32(0x2192a2f8)
}
fn test_big_endian_put_u32() {
mut buf := []u8{len: 4}
big_endian_put_u32(mut buf, 0x872fea95)
assert buf == [u8(0x87), 0x2f, 0xea, 0x95]
big_endian_put_u32(mut buf, 0)
assert buf == [u8(0), 0, 0, 0]
big_endian_put_u32(mut buf, 0xfdf2e68f)
assert buf == [u8(0xfd), 0xf2, 0xe6, 0x8f]
}
fn test_big_endian_put_u32_at() {
mut buf := []u8{len: 8}
big_endian_put_u32_at(mut buf, 0x872fea95, 1)
assert buf == [u8(0), 0x87, 0x2f, 0xea, 0x95, 0, 0, 0]
buf = []u8{len: 8}
big_endian_put_u32_at(mut buf, 1, 1)
assert buf == [u8(0), 0, 0, 0, 1, 0, 0, 0]
buf = []u8{len: 8}
big_endian_put_u32_at(mut buf, 0xfdf2e68f, 1)
assert buf == [u8(0), 0xfd, 0xf2, 0xe6, 0x8f, 0, 0, 0]
}
fn test_big_endian_put_u32_end() {
mut buf := []u8{len: 8}
big_endian_put_u32_end(mut buf, 0x872fea95)
assert buf == [u8(0), 0, 0, 0, 0x87, 0x2f, 0xea, 0x95]
buf = []u8{len: 8}
big_endian_put_u32_end(mut buf, 1)
assert buf == [u8(0), 0, 0, 0, 0, 0, 0, 1]
buf = []u8{len: 8}
big_endian_put_u32_end(mut buf, 0xfdf2e68f)
assert buf == [u8(0), 0, 0, 0, 0xfd, 0xf2, 0xe6, 0x8f]
}
fn test_big_endian_get_u32() {
assert big_endian_get_u32(u32(1)) == [u8(0), 0, 0, 1]
assert big_endian_get_u32(u32(0x05040901)) == [u8(5), 4, 9, 1]
assert big_endian_get_u32(u32(0xf8a29e21)) == [u8(0xf8), 0xa2, 0x9e, 0x21]
assert big_endian_get_u32(u32(0x2192a2f8)) != [u8(0xf8), 0xa2, 0x9e, 0x21]
}
fn test_big_endian_u64() {
assert big_endian_u64([u8(0), 0, 0, 0, 0, 0, 0, 1]) == u64(1)
assert big_endian_u64([u8(5), 4, 9, 1, 7, 3, 6, 8]) == u64(0x0504090107030608)
assert big_endian_u64([u8(0xf8), 0xa2, 0x9e, 0x21, 0x7f, 0x9f, 0x8e, 0x8f]) == u64(0xf8a29e217f9f8e8f)
assert big_endian_u64([u8(0xf8), 0xa2, 0x9e, 0x21, 0x7f, 0x9f, 0x8e, 0x8f]) != u64(0x8f8e9f7f219ea2f8)
}
fn test_big_endian_u64_at() {
assert big_endian_u64_at([u8(0), 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0], 1) == u64(1)
assert big_endian_u64_at([u8(0), 5, 4, 9, 1, 7, 3, 6, 8, 0, 0, 0, 0, 0, 0, 0], 1) == u64(0x0504090107030608)
assert big_endian_u64_at([u8(0), 0xf8, 0xa2, 0x9e, 0x21, 0x7f, 0x9f, 0x8e, 0x8f, 0, 0, 0, 0,
0, 0, 0], 1) == u64(0xf8a29e217f9f8e8f)
assert big_endian_u64_at([u8(0), 0xf8, 0xa2, 0x9e, 0x21, 0x7f, 0x9f, 0x8e, 0x8f, 0, 0, 0, 0,
0, 0, 0], 1) != u64(0x8f8e9f7f219ea2f8)
}
fn test_big_endian_u64_end() {
assert big_endian_u64_end([u8(0), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]) == u64(1)
assert big_endian_u64_end([u8(0), 0, 0, 0, 0, 0, 0, 0, 5, 4, 9, 1, 7, 3, 6, 8]) == u64(0x0504090107030608)
assert big_endian_u64_end([u8(0), 0, 0, 0, 0, 0, 0, 0, 0xf8, 0xa2, 0x9e, 0x21, 0x7f, 0x9f,
0x8e, 0x8f]) == u64(0xf8a29e217f9f8e8f)
assert big_endian_u64_end([u8(0), 0, 0, 0, 0, 0, 0, 0, 0xf8, 0xa2, 0x9e, 0x21, 0x7f, 0x9f,
0x8e, 0x8f]) != u64(0x8f8e9f7f219ea2f8)
}
fn test_big_endian_put_u64() {
mut buf := []u8{len: 8}
big_endian_put_u64(mut buf, 0x872fea95fdf2e68f)
assert buf == [u8(0x87), 0x2f, 0xea, 0x95, 0xfd, 0xf2, 0xe6, 0x8f]
big_endian_put_u64(mut buf, 0)
assert buf == [u8(0), 0, 0, 0, 0, 0, 0, 0]
big_endian_put_u64(mut buf, 0xfdf2e68f8e9f7f21)
assert buf == [u8(0xfd), 0xf2, 0xe6, 0x8f, 0x8e, 0x9f, 0x7f, 0x21]
}
fn test_big_endian_put_u64_at() {
mut buf := []u8{len: 16}
big_endian_put_u64_at(mut buf, 0x872fea95fdf2e68f, 1)
assert buf == [u8(0), 0x87, 0x2f, 0xea, 0x95, 0xfd, 0xf2, 0xe6, 0x8f, 0, 0, 0, 0, 0, 0, 0]
buf = []u8{len: 16}
big_endian_put_u64_at(mut buf, 1, 1)
assert buf == [u8(0), 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0]
buf = []u8{len: 16}
big_endian_put_u64_at(mut buf, 0xfdf2e68f8e9f7f21, 1)
assert buf == [u8(0), 0xfd, 0xf2, 0xe6, 0x8f, 0x8e, 0x9f, 0x7f, 0x21, 0, 0, 0, 0, 0, 0, 0]
}
fn test_big_endian_put_u64_end() {
mut buf := []u8{len: 16}
big_endian_put_u64_end(mut buf, 0x872fea95fdf2e68f)
assert buf == [u8(0), 0, 0, 0, 0, 0, 0, 0, 0x87, 0x2f, 0xea, 0x95, 0xfd, 0xf2, 0xe6, 0x8f]
buf = []u8{len: 16}
big_endian_put_u64_end(mut buf, 1)
assert buf == [u8(0), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]
buf = []u8{len: 16}
big_endian_put_u64_end(mut buf, 0xfdf2e68f8e9f7f21)
assert buf == [u8(0), 0, 0, 0, 0, 0, 0, 0, 0xfd, 0xf2, 0xe6, 0x8f, 0x8e, 0x9f, 0x7f, 0x21]
}
fn test_big_endian_get_u64() {
assert big_endian_get_u64(u64(1)) == [u8(0), 0, 0, 0, 0, 0, 0, 1]
assert big_endian_get_u64(u64(0x0504090107030608)) == [u8(5), 4, 9, 1, 7, 3, 6, 8]
assert big_endian_get_u64(u64(0xf8a29e217f9f8e8f)) == [u8(0xf8), 0xa2, 0x9e, 0x21, 0x7f, 0x9f,
0x8e, 0x8f]
assert big_endian_get_u64(u64(0x8f8e9f7f219ea2f8)) != [u8(0xf8), 0xa2, 0x9e, 0x21, 0x7f, 0x9f,
0x8e, 0x8f]
}
@@ -0,0 +1,288 @@
// Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
module binary
// little_endian_u16 creates a u16 from the first two bytes in the array b in little endian order.
@[direct_array_access; inline]
pub fn little_endian_u16(b []u8) u16 {
_ = b[1] // bounds check
unsafe {
mut u := U16{}
$if little_endian {
u.b[0], u.b[1] = b[0], b[1]
} $else {
u.b[0], u.b[1] = b[1], b[0]
}
return u.u
}
}
// little_endian_u16_at creates a u16 from two bytes in the array b at the specified offset in little endian order.
@[direct_array_access; inline]
pub fn little_endian_u16_at(b []u8, o int) u16 {
_ = b[o] // bounds check
_ = b[o + 1] // bounds check
unsafe {
mut u := U16{}
$if little_endian {
u.b[0], u.b[1] = b[o], b[o + 1]
} $else {
u.b[0], u.b[1] = b[o + 1], b[o]
}
return u.u
}
}
// little_endian_u16_end creates a u16 from the last two bytes of the array b in little endian order.
@[direct_array_access; inline]
pub fn little_endian_u16_end(b []u8) u16 {
return little_endian_u16_at(b, b.len - 2)
}
// little_endian_put_u16 writes a u16 to the first two bytes in the array b in little endian order.
@[direct_array_access; inline]
pub fn little_endian_put_u16(mut b []u8, v u16) {
_ = b[1] // bounds check
unsafe {
mut u := U16{
u: v
}
$if little_endian {
b[0], b[1] = u.b[0], u.b[1]
} $else {
b[0], b[1] = u.b[1], u.b[0]
}
}
}
// little_endian_put_u16_at writes a u16 to the two bytes in the array b at the specified offset in little endian order.
@[direct_array_access; inline]
pub fn little_endian_put_u16_at(mut b []u8, v u16, o int) {
_ = b[o] // bounds check
_ = b[o + 1] // bounds check
unsafe {
mut u := U16{
u: v
}
$if little_endian {
b[o], b[o + 1] = u.b[0], u.b[1]
} $else {
b[o], b[o + 1] = u.b[1], u.b[0]
}
}
}
// little_endian_put_u16_end writes a u16 to the last two bytes of the array b in little endian order.
@[direct_array_access; inline]
pub fn little_endian_put_u16_end(mut b []u8, v u16) {
little_endian_put_u16_at(mut b, v, b.len - 2)
}
// little_endian_get_u16 creates u8 array from the unsigned 16-bit integer v in little endian order.
pub fn little_endian_get_u16(v u16) []u8 {
unsafe {
mut u := U16{
u: v
}
$if big_endian {
u.b[0], u.b[1] = u.b[1], u.b[0]
}
return u.b[..]
}
}
// little_endian_u32 creates a u32 from the first four bytes in the array b in little endian order.
@[direct_array_access; inline]
pub fn little_endian_u32(b []u8) u32 {
_ = b[3] // bounds check
unsafe {
mut u := U32{}
$if little_endian {
u.b[0], u.b[1], u.b[2], u.b[3] = b[0], b[1], b[2], b[3]
} $else {
u.b[0], u.b[1], u.b[2], u.b[3] = b[3], b[2], b[1], b[0]
}
return u.u
}
}
// little_endian_u32_at creates a u32 from four bytes in the array b at the specified offset in little endian order.
@[direct_array_access; inline]
pub fn little_endian_u32_at(b []u8, o int) u32 {
_ = b[o] // bounds check
_ = b[o + 3] // bounds check
unsafe {
mut u := U32{}
$if little_endian {
u.b[0], u.b[1], u.b[2], u.b[3] = b[o], b[o + 1], b[o + 2], b[o + 3]
} $else {
u.b[0], u.b[1], u.b[2], u.b[3] = b[o + 3], b[o + 2], b[o + 1], b[o]
}
return u.u
}
}
// little_endian_u32_end creates a u32 from the last four bytes in the array b in little endian order.
@[direct_array_access; inline]
pub fn little_endian_u32_end(b []u8) u32 {
return little_endian_u32_at(b, b.len - 4)
}
// little_endian_put_u32 writes a u32 to the first four bytes in the array b in little endian order.
@[direct_array_access; inline]
pub fn little_endian_put_u32(mut b []u8, v u32) {
_ = b[3] // bounds check
unsafe {
mut u := U32{
u: v
}
$if little_endian {
b[0], b[1], b[2], b[3] = u.b[0], u.b[1], u.b[2], u.b[3]
} $else {
b[0], b[1], b[2], b[3] = u.b[3], u.b[2], u.b[1], u.b[0]
}
}
}
// little_endian_put_u32_at writes a u32 to the four bytes in the array b at the specified offset in little endian order.
@[direct_array_access; inline]
pub fn little_endian_put_u32_at(mut b []u8, v u32, o int) {
_ = b[o] // bounds check
_ = b[o + 3] // bounds check
unsafe {
mut u := U32{
u: v
}
$if little_endian {
b[o], b[o + 1], b[o + 2], b[o + 3] = u.b[0], u.b[1], u.b[2], u.b[3]
} $else {
b[o], b[o + 1], b[o + 2], b[o + 3] = u.b[3], u.b[2], u.b[1], u.b[0]
}
}
}
// little_endian_put_u32_end writes a u32 to the last four bytes in the array b in little endian order.
@[direct_array_access; inline]
pub fn little_endian_put_u32_end(mut b []u8, v u32) {
little_endian_put_u32_at(mut b, v, b.len - 4)
}
// little_endian_get_u32 creates u8 array from the unsigned 32-bit integer v in little endian order.
pub fn little_endian_get_u32(v u32) []u8 {
unsafe {
mut u := U32{
u: v
}
$if big_endian {
u.b[0], u.b[1], u.b[2], u.b[3] = u.b[3], u.b[2], u.b[1], u.b[0]
}
return u.b[..]
}
}
// little_endian_u64 creates a u64 from the first eight bytes in the array b in little endian order.
@[direct_array_access; inline]
pub fn little_endian_u64(b []u8) u64 {
_ = b[7] // bounds check
unsafe {
mut u := U64{}
$if little_endian {
u.b[0], u.b[1], u.b[2], u.b[3], u.b[4], u.b[5], u.b[6], u.b[7] = b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]
} $else {
u.b[0], u.b[1], u.b[2], u.b[3], u.b[4], u.b[5], u.b[6], u.b[7] = b[7], b[6], b[5], b[4], b[3], b[2], b[1], b[0]
}
return u.u
}
}
// little_endian_u64_at creates a u64 from eight bytes in the array b at the specified offset in little endian order.
@[direct_array_access; inline]
pub fn little_endian_u64_at(b []u8, o int) u64 {
_ = b[o] // bounds check
_ = b[o + 7] // bounds check
unsafe {
mut u := U64{}
$if little_endian {
u.b[0], u.b[1], u.b[2], u.b[3], u.b[4], u.b[5], u.b[6], u.b[7] = b[o], b[o + 1], b[o + 2], b[
o + 3], b[o + 4], b[o + 5], b[o + 6], b[o + 7]
} $else {
u.b[0], u.b[1], u.b[2], u.b[3], u.b[4], u.b[5], u.b[6], u.b[7] = b[o + 7], b[o + 6], b[
o + 5], b[o + 4], b[o + 3], b[o + 2], b[o + 1], b[o]
}
return u.u
}
}
// little_endian_u64_end creates a u64 from the last eight bytes in the array b in little endian order.
@[direct_array_access; inline]
pub fn little_endian_u64_end(b []u8) u64 {
return little_endian_u64_at(b, b.len - 8)
}
// little_endian_put_u64 writes a u64 to the first eight bytes in the array b in little endian order.
@[direct_array_access; inline]
pub fn little_endian_put_u64(mut b []u8, v u64) {
_ = b[7] // bounds check
unsafe {
mut u := U64{
u: v
}
$if little_endian {
b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7] = u.b[0], u.b[1], u.b[2], u.b[3], u.b[4], u.b[5], u.b[6], u.b[7]
} $else {
b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7] = u.b[7], u.b[6], u.b[5], u.b[4], u.b[3], u.b[2], u.b[1], u.b[0]
}
}
}
// little_endian_put_u64_at writes a u64 to the eight bytes in the array b at the specified offset in little endian order.
@[direct_array_access; inline]
pub fn little_endian_put_u64_at(mut b []u8, v u64, o int) {
_ = b[o] // bounds check
_ = b[o + 7] // bounds check
unsafe {
mut u := U64{
u: v
}
$if little_endian {
b[o], b[o + 1], b[o + 2], b[o + 3], b[o + 4], b[o + 5], b[o + 6], b[o + 7] = u.b[0], u.b[1], u.b[2], u.b[3], u.b[4], u.b[5], u.b[6], u.b[7]
} $else {
b[o], b[o + 1], b[o + 2], b[o + 3], b[o + 4], b[o + 5], b[o + 6], b[o + 7] = u.b[7], u.b[6], u.b[5], u.b[4], u.b[3], u.b[2], u.b[1], u.b[0]
}
}
}
// little_endian_put_u64_end writes a u64 to the last eight bytes in the array b at in little endian order.
@[direct_array_access; inline]
pub fn little_endian_put_u64_end(mut b []u8, v u64) {
little_endian_put_u64_at(mut b, v, b.len - 8)
}
@[direct_array_access; inline]
pub fn little_endian_f32_at(b []u8, o int) f32 {
_ = b[o] // bounds check
_ = b[o + 3] // bounds check
unsafe {
mut u := F32{}
$if little_endian {
u.b[0], u.b[1], u.b[2], u.b[3] = b[o], b[o + 1], b[o + 2], b[o + 3]
} $else {
u.b[0], u.b[1], u.b[2], u.b[3] = b[o + 3], b[o + 2], b[o + 1], b[o]
}
return u.u
}
}
// little_endian_get_u64 creates u8 array from the unsigned 64-bit integer v in little endian order.
pub fn little_endian_get_u64(v u64) []u8 {
unsafe {
mut u := U64{
u: v
}
$if big_endian {
u.b[0], u.b[1], u.b[2], u.b[3], u.b[4], u.b[5], u.b[6], u.b[7] = u.b[7], u.b[6], u.b[5], u.b[4], u.b[3], u.b[2], u.b[1], u.b[0]
}
return u.b[..]
}
}
@@ -0,0 +1,91 @@
// Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
module binary
// little_endian_u16_fixed creates a u16 from the fixed array b in little endian order.
@[direct_array_access; inline]
pub fn little_endian_u16_fixed(b [2]u8) u16 {
unsafe {
mut u := U16{}
$if little_endian {
u.b[0], u.b[1] = b[0], b[1]
} $else {
u.b[0], u.b[1] = b[1], b[0]
}
return u.u
}
}
// little_endian_put_u16_fixed writes a u16 to the fixed array b in little endian order.
@[direct_array_access; inline]
pub fn little_endian_put_u16_fixed(mut b [2]u8, v u16) {
unsafe {
mut u := U16{
u: v
}
$if little_endian {
b[0], b[1] = u.b[0], u.b[1]
} $else {
b[0], b[1] = u.b[1], u.b[0]
}
}
}
// little_endian_u32_fixed creates a u32 from the fixed array b in little endian order.
@[direct_array_access; inline]
pub fn little_endian_u32_fixed(b [4]u8) u32 {
unsafe {
mut u := U32{}
$if little_endian {
u.b[0], u.b[1], u.b[2], u.b[3] = b[0], b[1], b[2], b[3]
} $else {
u.b[0], u.b[1], u.b[2], u.b[3] = b[3], b[2], b[1], b[0]
}
return u.u
}
}
// little_endian_put_u32_fixed writes a u32 to the fixed array b in little endian order.
@[direct_array_access; inline]
pub fn little_endian_put_u32_fixed(mut b [4]u8, v u32) {
unsafe {
mut u := U32{
u: v
}
$if little_endian {
b[0], b[1], b[2], b[3] = u.b[0], u.b[1], u.b[2], u.b[3]
} $else {
b[0], b[1], b[2], b[3] = u.b[3], u.b[2], u.b[1], u.b[0]
}
}
}
// little_endian_u64_fixed creates a u64 from the fixed array b in little endian order.
@[direct_array_access; inline]
pub fn little_endian_u64_fixed(b [8]u8) u64 {
unsafe {
mut u := U64{}
$if little_endian {
u.b[0], u.b[1], u.b[2], u.b[3], u.b[4], u.b[5], u.b[6], u.b[7] = b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]
} $else {
u.b[0], u.b[1], u.b[2], u.b[3], u.b[4], u.b[5], u.b[6], u.b[7] = b[7], b[6], b[5], b[4], b[3], b[2], b[1], b[0]
}
return u.u
}
}
// little_endian_put_u64_fixed writes a u64 to the fixed array b in little endian order.
@[direct_array_access; inline]
pub fn little_endian_put_u64_fixed(mut b [8]u8, v u64) {
unsafe {
mut u := U64{
u: v
}
$if little_endian {
b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7] = u.b[0], u.b[1], u.b[2], u.b[3], u.b[4], u.b[5], u.b[6], u.b[7]
} $else {
b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7] = u.b[7], u.b[6], u.b[5], u.b[4], u.b[3], u.b[2], u.b[1], u.b[0]
}
}
}
@@ -0,0 +1,54 @@
module binary
// Little Endian Tests
fn test_little_endian_u16_fixed() {
assert little_endian_u16_fixed([u8(0), 0]!) == u16(0)
assert little_endian_u16_fixed([u8(5), 4]!) == u16(0x0405)
assert little_endian_u16_fixed([u8(0x35), 0x57]!) == u16(0x5735)
assert little_endian_u16_fixed([u8(0x35), 0x57]!) != u16(0x3557)
}
fn test_little_endian_put_u16_fixed() {
mut buf := [2]u8{}
little_endian_put_u16_fixed(mut buf, 0x8725)
assert buf == [u8(0x25), 0x87]!
little_endian_put_u16_fixed(mut buf, 0)
assert buf == [u8(0), 0]!
little_endian_put_u16_fixed(mut buf, 0xfdff)
assert buf == [u8(0xff), 0xfd]!
}
fn test_little_endian_u32_fixed() {
assert little_endian_u32_fixed([u8(0), 0, 0, 0]!) == u32(0)
assert little_endian_u32_fixed([u8(5), 4, 9, 1]!) == u32(0x01090405)
assert little_endian_u32_fixed([u8(0xf8), 0xa2, 0x9e, 0x21]!) == u32(0x219ea2f8)
assert little_endian_u32_fixed([u8(0xf8), 0xa2, 0x9e, 0x21]!) != u32(0xf8a29e21)
}
fn test_little_endian_put_u32_fixed() {
mut buf := [4]u8{}
little_endian_put_u32_fixed(mut buf, 0x872fea95)
assert buf == [u8(0x95), 0xea, 0x2f, 0x87]!
little_endian_put_u32_fixed(mut buf, 0)
assert buf == [u8(0), 0, 0, 0]!
little_endian_put_u32_fixed(mut buf, 0xfdf2e68f)
assert buf == [u8(0x8f), 0xe6, 0xf2, 0xfd]!
}
fn test_little_endian_u64_fixed() {
assert little_endian_u64_fixed([u8(0), 0, 0, 0, 0, 0, 0, 0]!) == u64(0)
assert little_endian_u64_fixed([u8(5), 4, 9, 1, 7, 3, 6, 8]!) == u64(0x0806030701090405)
assert little_endian_u64_fixed([u8(0xf8), 0xa2, 0x9e, 0x21, 0x7f, 0x9f, 0x8e, 0x8f]!) == u64(0x8f8e9f7f219ea2f8)
assert little_endian_u64_fixed([u8(0xf8), 0xa2, 0x9e, 0x21, 0x7f, 0x9f, 0x8e, 0x8f]!) != u64(0xf8a29e217f9f8e8f)
}
fn test_little_endian_put_u64_fixed() {
mut buf := [8]u8{}
little_endian_put_u64_fixed(mut buf, 0x872fea95fdf2e68f)
assert buf == [u8(0x8f), 0xe6, 0xf2, 0xfd, 0x95, 0xea, 0x2f, 0x87]!
little_endian_put_u64_fixed(mut buf, 0)
assert buf == [u8(0), 0, 0, 0, 0, 0, 0, 0]!
little_endian_put_u64_fixed(mut buf, 0xfdf2e68f8e9f7f21)
assert buf == [u8(0x21), 0x7f, 0x9f, 0x8e, 0x8f, 0xe6, 0xf2, 0xfd]!
}
@@ -0,0 +1,215 @@
module binary
// Little Endian Tests
fn test_little_endian_u16() {
assert little_endian_u16([u8(0), 0]) == u16(0)
assert little_endian_u16([u8(5), 4]) == u16(0x0405)
assert little_endian_u16([u8(0x35), 0x57]) == u16(0x5735)
assert little_endian_u16([u8(0x35), 0x57]) != u16(0x3557)
}
fn test_little_endian_u16_at() {
assert little_endian_u16_at([u8(1), 0, 0, 1], 1) == u16(0)
assert little_endian_u16_at([u8(5), 4, 9, 1], 1) == u16(0x0904)
assert little_endian_u16_at([u8(0xf8), 0xa2, 0x9e, 0x21], 1) == u16(0x9ea2)
assert little_endian_u16_at([u8(0xf8), 0xa2, 0x9e, 0x21], 1) != u16(0xa29e)
}
fn test_little_endian_u16_end() {
assert little_endian_u16_end([u8(1), 0, 0, 1]) == u16(0x0100)
assert little_endian_u16_end([u8(5), 4, 9, 1]) == u16(0x0109)
assert little_endian_u16_end([u8(0xf8), 0xa2, 0x9e, 0x21]) == u16(0x219e)
assert little_endian_u16_end([u8(0xf8), 0xa2, 0x9e, 0x21]) != u16(0x9e21)
}
fn test_little_endian_put_u16() {
mut buf := []u8{len: 2}
little_endian_put_u16(mut buf, 0x8725)
assert buf == [u8(0x25), 0x87]
little_endian_put_u16(mut buf, 0)
assert buf == [u8(0), 0]
little_endian_put_u16(mut buf, 0xfdff)
assert buf == [u8(0xff), 0xfd]
}
fn test_little_endian_put_u16_at() {
mut buf := []u8{len: 4}
little_endian_put_u16_at(mut buf, 0x8725, 1)
assert buf == [u8(0), 0x25, 0x87, 0]
buf = []u8{len: 4}
little_endian_put_u16_at(mut buf, 1, 1)
assert buf == [u8(0), 1, 0, 0]
buf = []u8{len: 4}
little_endian_put_u16_at(mut buf, 0xfdff, 1)
assert buf == [u8(0), 0xff, 0xfd, 0]
}
fn test_little_endian_put_u16_end() {
mut buf := []u8{len: 4}
little_endian_put_u16_end(mut buf, 0x8725)
assert buf == [u8(0), 0, 0x25, 0x87]
buf = []u8{len: 4}
little_endian_put_u16_end(mut buf, 1)
assert buf == [u8(0), 0, 1, 0]
buf = []u8{len: 4}
little_endian_put_u16_end(mut buf, 0xfdff)
assert buf == [u8(0), 0, 0xff, 0xfd]
}
fn test_little_endian_get_u16() {
assert little_endian_get_u16(u16(256)) == [u8(0), 1]
assert little_endian_get_u16(u16(0x0405)) == [u8(5), 4]
assert little_endian_get_u16(u16(0x5735)) == [u8(0x35), 0x57]
assert little_endian_get_u16(u16(0x3557)) != [u8(0x35), 0x57]
}
fn test_little_endian_u32() {
assert little_endian_u32([u8(0), 0, 0, 0]) == u32(0)
assert little_endian_u32([u8(5), 4, 9, 1]) == u32(0x01090405)
assert little_endian_u32([u8(0xf8), 0xa2, 0x9e, 0x21]) == u32(0x219ea2f8)
assert little_endian_u32([u8(0xf8), 0xa2, 0x9e, 0x21]) != u32(0xf8a29e21)
}
fn test_little_endian_u32_at() {
assert little_endian_u32_at([u8(1), 0, 0, 0, 0, 0, 0, 0], 1) == u32(0)
assert little_endian_u32_at([u8(5), 4, 9, 1, 7, 3, 6, 8], 1) == u32(0x07010904)
assert little_endian_u32_at([u8(0xf8), 0xa2, 0x9e, 0x21, 0x7f, 0x9f, 0x8e, 0x8f], 1) == u32(0x7f219ea2)
assert little_endian_u32_at([u8(0xf8), 0xa2, 0x9e, 0x21, 0x7f, 0x9f, 0x8e, 0x8f], 1) != u32(0xa29e217f)
}
fn test_little_endian_u32_end() {
assert little_endian_u32_end([u8(1), 0, 0, 0, 0, 0, 0, 0]) == u32(0)
assert little_endian_u32_end([u8(5), 4, 9, 1, 7, 3, 6, 8]) == u32(0x08060307)
assert little_endian_u32_end([u8(0xf8), 0xa2, 0x9e, 0x21, 0x7f, 0x9f, 0x8e, 0x8f]) == u32(0x8f8e9f7f)
assert little_endian_u32_end([u8(0xf8), 0xa2, 0x9e, 0x21, 0x7f, 0x9f, 0x8e, 0x8f]) != u32(0x7f9f8e8f)
}
fn test_little_endian_put_u32() {
mut buf := []u8{len: 4}
little_endian_put_u32(mut buf, 0x872fea95)
assert buf == [u8(0x95), 0xea, 0x2f, 0x87]
little_endian_put_u32(mut buf, 0)
assert buf == [u8(0), 0, 0, 0]
little_endian_put_u32(mut buf, 0xfdf2e68f)
assert buf == [u8(0x8f), 0xe6, 0xf2, 0xfd]
}
fn test_little_endian_put_u32_at() {
mut buf := []u8{len: 8}
little_endian_put_u32_at(mut buf, 0x872fea95, 1)
assert buf == [u8(0), 0x95, 0xea, 0x2f, 0x87, 0, 0, 0]
buf = []u8{len: 8}
little_endian_put_u32_at(mut buf, 1, 1)
assert buf == [u8(0), 1, 0, 0, 0, 0, 0, 0]
buf = []u8{len: 8}
little_endian_put_u32_at(mut buf, 0xfdf2e68f, 1)
assert buf == [u8(0), 0x8f, 0xe6, 0xf2, 0xfd, 0, 0, 0]
}
fn test_little_endian_put_u32_end() {
mut buf := []u8{len: 8}
little_endian_put_u32_end(mut buf, 0x872fea95)
assert buf == [u8(0), 0, 0, 0, 0x95, 0xea, 0x2f, 0x87]
buf = []u8{len: 8}
little_endian_put_u32_end(mut buf, 1)
assert buf == [u8(0), 0, 0, 0, 1, 0, 0, 0]
buf = []u8{len: 8}
little_endian_put_u32_end(mut buf, 0xfdf2e68f)
assert buf == [u8(0), 0, 0, 0, 0x8f, 0xe6, 0xf2, 0xfd]
}
fn test_little_endian_get_u32() {
assert little_endian_get_u32(u32(16777216)) == [u8(0), 0, 0, 1]
assert little_endian_get_u32(u32(0x01090405)) == [u8(5), 4, 9, 1]
assert little_endian_get_u32(u32(0x2192a2f8)) == [u8(0xf8), 0xa2, 0x92, 0x21]
assert little_endian_get_u32(u32(0xf8a29e21)) != [u8(0xf8), 0xa2, 0x9e, 0x21]
}
fn test_little_endian_u64() {
assert little_endian_u64([u8(0), 0, 0, 0, 0, 0, 0, 0]) == u64(0)
assert little_endian_u64([u8(5), 4, 9, 1, 7, 3, 6, 8]) == u64(0x0806030701090405)
assert little_endian_u64([u8(0xf8), 0xa2, 0x9e, 0x21, 0x7f, 0x9f, 0x8e, 0x8f]) == u64(0x8f8e9f7f219ea2f8)
assert little_endian_u64([u8(0xf8), 0xa2, 0x9e, 0x21, 0x7f, 0x9f, 0x8e, 0x8f]) != u64(0xf8a29e217f9f8e8f)
}
fn test_little_endian_u64_at() {
assert little_endian_u64_at([u8(1), 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1], 1) == u64(0)
assert little_endian_u64_at([u8(0), 5, 4, 9, 1, 7, 3, 6, 8, 0, 0, 0, 0, 0, 0, 0], 1) == u64(0x0806030701090405)
assert little_endian_u64_at([u8(0), 0xf8, 0xa2, 0x9e, 0x21, 0x7f, 0x9f, 0x8e, 0x8f, 0, 0, 0,
0, 0, 0, 0], 1) == u64(0x8f8e9f7f219ea2f8)
assert little_endian_u64_at([u8(0), 0xf8, 0xa2, 0x9e, 0x21, 0x7f, 0x9f, 0x8e, 0x8f, 0, 0, 0,
0, 0, 0, 0], 1) != u64(0xf8a29e217f9f8e8f)
}
fn test_little_endian_f32_at() {
assert little_endian_f32_at([u8(1), 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1], 1) == f32(0)
/*
assert little_endian_f32_at([u8(0), 5, 4, 9, 1, 7, 3, 6, 8, 0, 0, 0, 0, 0, 0, 0],
1).eq_epsilon(0.00000000000000000000000000000000002516)
*/
}
fn test_little_endian_u64_end() {
assert little_endian_u64_end([u8(1), 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0]) == u64(0)
assert little_endian_u64_end([u8(0), 0, 0, 0, 0, 0, 0, 0, 5, 4, 9, 1, 7, 3, 6, 8]) == u64(0x0806030701090405)
assert little_endian_u64_end([u8(0), 0, 0, 0, 0, 0, 0, 0, 0xf8, 0xa2, 0x9e, 0x21, 0x7f, 0x9f,
0x8e, 0x8f]) == u64(0x8f8e9f7f219ea2f8)
assert little_endian_u64_end([u8(0), 0, 0, 0, 0, 0, 0, 0, 0xf8, 0xa2, 0x9e, 0x21, 0x7f, 0x9f,
0x8e, 0x8f]) != u64(0xf8a29e217f9f8e8f)
}
fn test_little_endian_put_u64() {
mut buf := []u8{len: 8}
little_endian_put_u64(mut buf, 0x872fea95fdf2e68f)
assert buf == [u8(0x8f), 0xe6, 0xf2, 0xfd, 0x95, 0xea, 0x2f, 0x87]
little_endian_put_u64(mut buf, 0)
assert buf == [u8(0), 0, 0, 0, 0, 0, 0, 0]
little_endian_put_u64(mut buf, 0xfdf2e68f8e9f7f21)
assert buf == [u8(0x21), 0x7f, 0x9f, 0x8e, 0x8f, 0xe6, 0xf2, 0xfd]
}
fn test_little_endian_put_u64_at() {
mut buf := []u8{len: 16}
little_endian_put_u64_at(mut buf, 0x872fea95fdf2e68f, 1)
assert buf == [u8(0), 0x8f, 0xe6, 0xf2, 0xfd, 0x95, 0xea, 0x2f, 0x87, 0, 0, 0, 0, 0, 0, 0]
buf = []u8{len: 16}
little_endian_put_u64_at(mut buf, 1, 1)
assert buf == [u8(0), 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
buf = []u8{len: 16}
little_endian_put_u64_at(mut buf, 0xfdf2e68f8e9f7f21, 1)
assert buf == [u8(0), 0x21, 0x7f, 0x9f, 0x8e, 0x8f, 0xe6, 0xf2, 0xfd, 0, 0, 0, 0, 0, 0, 0]
}
fn test_little_endian_put_u64_end() {
mut buf := []u8{len: 16}
little_endian_put_u64_end(mut buf, 0x872fea95fdf2e68f)
assert buf == [u8(0), 0, 0, 0, 0, 0, 0, 0, 0x8f, 0xe6, 0xf2, 0xfd, 0x95, 0xea, 0x2f, 0x87]
buf = []u8{len: 16}
little_endian_put_u64_end(mut buf, 1)
assert buf == [u8(0), 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0]
buf = []u8{len: 16}
little_endian_put_u64_end(mut buf, 0xfdf2e68f8e9f7f21)
assert buf == [u8(0), 0, 0, 0, 0, 0, 0, 0, 0x21, 0x7f, 0x9f, 0x8e, 0x8f, 0xe6, 0xf2, 0xfd]
}
fn test_little_endian_get_u64() {
assert little_endian_get_u64(u64(72057594037927936)) == [u8(0), 0, 0, 0, 0, 0, 0, 1]
assert little_endian_get_u64(u64(0x0806030701090405)) == [u8(5), 4, 9, 1, 7, 3, 6, 8]
assert little_endian_get_u64(u64(0x8f8e9f7f219ea2f8)) == [u8(0xf8), 0xa2, 0x9e, 0x21, 0x7f,
0x9f, 0x8e, 0x8f]
assert little_endian_get_u64(u64(0xf8a29e217f9f8e8f)) != [u8(0xf8), 0xa2, 0x9e, 0x21, 0x7f,
0x9f, 0x8e, 0x8f]
}
+524
View File
@@ -0,0 +1,524 @@
module binary
struct EncodeState {
mut:
b []u8
// pre-allocated buffers
b2 []u8 = [u8(0), 0]
b4 []u8 = [u8(0), 0, 0, 0]
b8 []u8 = [u8(0), 0, 0, 0, 0, 0, 0, 0]
big_endian bool
}
@[params]
pub struct EncodeConfig {
pub mut:
buffer_len int = 1024
big_endian bool // use big endian encoding the data
}
// encode_binary encode a T type data into u8 array.
// for encoding struct, you can use `@[serialize: '-']` to skip field.
// Note: `shared` fields in struct will be skipped.
pub fn encode_binary[T](obj T, config EncodeConfig) ![]u8 {
mut s := EncodeState{
b: []u8{cap: config.buffer_len}
big_endian: config.big_endian
}
$if T is $array {
encode_array(mut s, obj)!
} $else $if T is $string {
encode_string(mut s, obj)!
} $else $if T is $struct {
encode_struct(mut s, obj)!
} $else $if T is $map {
encode_map(mut s, obj)!
} $else {
encode_primitive(mut s, obj)!
}
return s.b
}
@[inline]
fn normalize_attr_arg(value string) string {
mut normalized := value.trim_space()
if normalized.len > 1 {
if (normalized[0] == `'` && normalized[normalized.len - 1] == `'`)
|| (normalized[0] == `"` && normalized[normalized.len - 1] == `"`) {
normalized = normalized[1..normalized.len - 1]
}
}
return normalized
}
fn encode_struct[T](mut s EncodeState, obj T) ! {
$for field in T.fields {
mut is_skip := false
for attr in field.attrs {
f := attr.split_any(':')
if f.len == 2 {
match f[0].trim_space() {
'serialize' {
// @[serialize:'-']
if normalize_attr_arg(f[1]) == '-' {
is_skip = true
}
}
else {}
}
}
}
if !is_skip {
// TODO: support shared field in struct, currently skip.
$if field.typ !is $shared {
value := obj.$(field.name)
$if field.typ is $array {
encode_array(mut s, value)!
} $else $if field.typ is $string {
encode_string(mut s, value)!
} $else $if field.typ is $struct {
encode_struct(mut s, value)!
} $else $if field.typ is $map {
encode_map(mut s, value)!
} $else {
encode_primitive(mut s, value)!
}
}
}
}
}
// help unions for bypass `-cstrict`/ `-Wstrict-aliasing` check.
union U32_F32 {
u u32
f f32
}
union U64_F64 {
u u64
f f64
}
fn encode_primitive[T](mut s EncodeState, value T) ! {
$if T is int {
// NOTE: `int` always use 64bit
s.put_u64(u64(value))
} $else $if T is u8 {
s.put_u8(u8(value))
} $else $if T is u16 {
s.put_u16(u16(value))
} $else $if T is u32 {
s.put_u32(u32(value))
} $else $if T is u64 {
s.put_u64(u64(value))
} $else $if T is i8 {
s.put_u8(u8(value))
} $else $if T is i16 {
s.put_u16(u16(value))
} $else $if T is i32 {
s.put_u32(u32(value))
} $else $if T is i64 {
s.put_u64(u64(value))
} $else $if T is f32 {
unsafe { s.put_u32(U32_F32{ f: value }.u) }
} $else $if T is f64 {
unsafe { s.put_u64(U64_F64{ f: value }.u) }
} $else $if T is bool {
s.put_u8(u8(value))
} $else $if T is rune {
s.put_u32(u32(value))
} $else $if T is isize {
if sizeof(isize) == 4 {
s.put_u32(u32(value))
} else {
s.put_u64(u64(value))
}
} $else $if T is usize {
if sizeof(usize) == 4 {
s.put_u32(u32(value))
} else {
s.put_u64(u64(value))
}
} $else $if T is voidptr {
s.put_u64(u64(value))
} $else {
// TODO: `any` type support?
return error('${@FN}(): unsupported type ${typeof(value).name}')
}
}
fn encode_array[T](mut s EncodeState, arr []T) ! {
s.put_u64(u64(arr.len))
$if T is u8 {
// optimization for `[]u8`
s.b << arr
} $else {
for element in arr {
$if T is $string {
encode_string(mut s, element)!
} $else $if T is $struct {
encode_struct(mut s, element)!
} $else $if T is $map {
encode_map(mut s, element)!
} $else {
encode_primitive(mut s, element)!
}
}
}
}
fn encode_string(mut s EncodeState, str string) ! {
s.put_u64(u64(str.len))
s.b << str.bytes()
}
fn encode_map[K, V](mut s EncodeState, m map[K]V) ! {
s.put_u64(u64(m.len))
for k, v in m {
// encode key first
// Maps can have keys of type string, rune, integer, float or voidptr.
$if K is $string {
encode_string(mut s, k)!
} $else {
encode_primitive(mut s, k)!
}
// encode value
$if V is $string {
encode_string(mut s, v)!
} $else $if V is $struct {
encode_struct(mut s, v)!
} $else $if V is $map {
encode_map(mut s, v)!
} $else {
encode_primitive(mut s, v)!
}
}
}
struct DecodeState {
mut:
b []u8
b2 []u8 = [u8(0), 0]
b4 []u8 = [u8(0), 0, 0, 0]
b8 []u8 = [u8(0), 0, 0, 0, 0, 0, 0, 0]
offset int
big_endian bool
}
@[params]
pub struct DecodeConfig {
pub mut:
buffer_len int = 1024
big_endian bool // use big endian decode the data
}
// decode_binary decode a u8 array into T type data.
// for decoding struct, you can use `@[serialize: '-']` to skip field.
// Note: `shared` fields in struct will be skipped.
pub fn decode_binary[T](b []u8, config DecodeConfig) !T {
mut s := DecodeState{
b: b
big_endian: config.big_endian
}
$if T is $array {
return decode_array(mut s, T{})!
} $else $if T is $string {
return decode_string(mut s)!
} $else $if T is $struct {
return decode_struct(mut s, T{})!
} $else $if T is $map {
return decode_map(mut s, T{})!
} $else {
return decode_primitive(mut s, unsafe { T(0) })!
}
}
fn decode_struct[T](mut s DecodeState, _ T) !T {
mut obj := T{}
$for field in T.fields {
mut is_skip := false
for attr in field.attrs {
f := attr.split_any(':')
if f.len == 2 {
match f[0].trim_space() {
'serialize' {
// @[serialize:'-']
if normalize_attr_arg(f[1]) == '-' {
is_skip = true
}
}
else {}
}
}
}
if !is_skip {
// TODO: support shared field in struct, currently skip.
$if field.typ !is $shared {
$if field.typ is $array {
obj.$(field.name) = decode_array(mut s, obj.$(field.name))!
} $else $if field.typ is $string {
obj.$(field.name) = decode_string(mut s)!
} $else $if field.typ is $struct {
obj.$(field.name) = decode_struct(mut s, obj.$(field.name))!
} $else $if field.typ is $map {
obj.$(field.name) = decode_map(mut s, obj.$(field.name))!
} $else {
obj.$(field.name) = decode_primitive(mut s, obj.$(field.name))!
}
}
}
}
return obj
}
fn decode_primitive[T](mut s DecodeState, value T) !T {
$if T is int {
// NOTE: int always use 64bit
return T(s.get_u64()!)
} $else $if T is u8 {
return T(s.get_u8()!)
} $else $if T is u16 {
return T(s.get_u16()!)
} $else $if T is u32 {
return T(s.get_u32()!)
} $else $if T is u64 {
return T(s.get_u64()!)
} $else $if T is i8 {
return T(s.get_u8()!)
} $else $if T is i16 {
return T(s.get_u16()!)
} $else $if T is i32 {
return T(s.get_u32()!)
} $else $if T is i64 {
return T(s.get_u64()!)
} $else $if T is f32 {
v := s.get_u32()!
return unsafe {
U32_F32{
u: v
}.f
}
} $else $if T is f64 {
v := s.get_u64()!
return unsafe {
U64_F64{
u: v
}.f
}
} $else $if T is bool {
return s.get_u8()! != 0
} $else $if T is rune {
return T(s.get_u32()!)
} $else $if T is isize {
if sizeof(isize) == 4 {
return T(s.get_u32()!)
} else {
return T(s.get_u64()!)
}
} $else $if T is usize {
if sizeof(usize) == 4 {
return T(s.get_u32()!)
} else {
return T(s.get_u64()!)
}
} $else $if T is voidptr {
return T(s.get_u64()!)
} $else {
// TODO: `any` type support?
return error('${@FN}(): unsupported type ${typeof(value).name}')
}
return error('${@FN}(): impossible error')
}
fn decode_array[T](mut s DecodeState, _ []T) ![]T {
len := int(s.get_u64()!)
if len <= 0 || s.offset + len > s.b.len {
return error('${@FN}(): invalid array length decode from stream')
}
mut arr := []T{cap: len}
$if T is u8 {
// optimization for `[]u8`
arr << s.b[s.offset..s.offset + len]
s.offset += len
} $else {
for _ in 0 .. len {
if s.offset >= s.b.len {
return error('${@FN}(): unexpected end of data')
}
$if T is $array {
arr << decode_array(mut s, T{})!
} $else $if T is $string {
arr << decode_string(mut s)!
} $else $if T is $struct {
arr << decode_struct(mut s, T{})!
} $else $if T is $map {
arr << decode_map(mut s, T{})!
} $else {
arr << decode_primitive(mut s, unsafe { T(0) })!
}
}
}
return arr
}
fn decode_string(mut s DecodeState) !string {
len := int(s.get_u64()!)
if len <= 0 || s.offset + len > s.b.len {
return error('${@FN}(): invalid string length decode from stream')
}
str := unsafe { s.b[s.offset..s.offset + len].bytestr() }
s.offset += len
return str
}
// `Any` is a sum type that lists the possible types to be decoded and used.
type Any = int
| bool
| f64
| f32
| i64
| i32
| i16
| i8
| map[string]Any
| map[int]Any
| string
| u64
| u32
| u16
| u8
| rune
| isize
| usize
| []Any
fn decode_map[K, V](mut s DecodeState, _ map[K]V) !map[K]V {
len := int(s.get_u64()!)
if len <= 0 || s.offset + len > s.b.len {
return error('${@FN}(): invalid map length decode from stream')
}
mut m := map[K]V{}
for _ in 0 .. len {
// decode key first
// Maps can have keys of type string, rune, integer, float or voidptr.
mut k := Any(0)
$if K is $string {
k = decode_string(mut s)!
} $else {
k = decode_primitive(mut s, unsafe { K(0) })!
}
// decode value
$if V is $struct {
v := decode_struct(mut s, V{})!
m[k as K] = v
} $else $if V is $map {
v := decode_map(mut s, V{})!
m[k as K] = v
} $else $if V is $string {
v := decode_string(mut s)!
m[k as K] = v
} $else {
v := decode_primitive(mut s, unsafe { V(0) })!
m[k as K] = v
}
}
return m
}
@[inline]
fn (mut s DecodeState) get_u64() !u64 {
if s.offset + 8 > s.b.len {
return error('${@FN}(): bytes length is not enough for u64')
}
defer {
s.offset += 8
}
if s.big_endian {
return big_endian_u64_at(s.b, s.offset)
} else {
return little_endian_u64_at(s.b, s.offset)
}
}
@[inline]
fn (mut s DecodeState) get_u32() !u32 {
if s.offset + 4 > s.b.len {
return error('${@FN}(): bytes length is not enough for u32')
}
defer {
s.offset += 4
}
if s.big_endian {
return big_endian_u32_at(s.b, s.offset)
} else {
return little_endian_u32_at(s.b, s.offset)
}
}
@[inline]
fn (mut s DecodeState) get_u16() !u16 {
if s.offset + 2 > s.b.len {
return error('${@FN}(): bytes length is not enough for u16')
}
defer {
s.offset += 2
}
if s.big_endian {
return big_endian_u16_at(s.b, s.offset)
} else {
return little_endian_u16_at(s.b, s.offset)
}
}
@[inline]
fn (mut s DecodeState) get_u8() !u8 {
if s.offset + 1 > s.b.len {
return error('${@FN}(): bytes length is not enough for u8')
}
defer {
s.offset += 1
}
return s.b[s.offset]
}
@[inline]
fn (mut s EncodeState) put_u64(value u64) {
if s.big_endian {
big_endian_put_u64(mut s.b8, value)
} else {
little_endian_put_u64(mut s.b8, value)
}
s.b << s.b8
}
@[inline]
fn (mut s EncodeState) put_u32(value u32) {
if s.big_endian {
big_endian_put_u32(mut s.b4, value)
} else {
little_endian_put_u32(mut s.b4, value)
}
s.b << s.b4
}
@[inline]
fn (mut s EncodeState) put_u16(value u16) {
if s.big_endian {
big_endian_put_u16(mut s.b2, value)
} else {
little_endian_put_u16(mut s.b2, value)
}
s.b << s.b2
}
@[inline]
fn (mut s EncodeState) put_u8(value u8) {
s.b << value
}
@@ -0,0 +1,559 @@
module binary
fn test_encode_decode_primitive_string() ! {
a_u8 := u8(137)
a_u16 := u16(5325)
a_u32 := u32(255421)
a_u64 := u64(2483294832)
a_i8 := i8(-11)
a_i16 := i16(-2321)
a_i32 := i32(-54322)
a_i64 := i64(-54212245)
a_int := int(-32135)
a_f32 := f32(1.37)
a_f64 := f64(-32144.3133)
a_bool := bool(true)
a_rune := `♥`
a_isize := isize(-45433)
a_usize := usize(432211)
a_string := '♥🖊dsser333100'
b_u8 := encode_binary(a_u8)!
b_u16 := encode_binary(a_u16)!
b_u32 := encode_binary(a_u32)!
b_u64 := encode_binary(a_u64)!
b_i8 := encode_binary(a_i8)!
b_i16 := encode_binary(a_i16)!
b_i32 := encode_binary(a_i32)!
b_i64 := encode_binary(a_i64)!
b_int := encode_binary(a_int)!
b_f32 := encode_binary(a_f32)!
b_f64 := encode_binary(a_f64)!
b_bool := encode_binary(a_bool)!
b_rune := encode_binary(a_rune)!
b_isize := encode_binary(a_isize)!
b_usize := encode_binary(a_usize)!
b_string := encode_binary(a_string)!
c_u8 := decode_binary[u8](b_u8)!
c_u16 := decode_binary[u16](b_u16)!
c_u32 := decode_binary[u32](b_u32)!
c_u64 := decode_binary[u64](b_u64)!
c_i8 := decode_binary[i8](b_i8)!
c_i16 := decode_binary[i16](b_i16)!
c_i32 := decode_binary[i32](b_i32)!
c_i64 := decode_binary[i64](b_i64)!
c_int := decode_binary[int](b_int)!
c_f32 := decode_binary[f32](b_f32)!
c_f64 := decode_binary[f64](b_f64)!
c_bool := decode_binary[bool](b_bool)!
c_rune := decode_binary[rune](b_rune)!
c_isize := decode_binary[isize](b_isize)!
c_usize := decode_binary[usize](b_usize)!
c_string := decode_binary[string](b_string)!
assert a_u8 == c_u8
assert a_u16 == c_u16
assert a_u32 == c_u32
assert a_u64 == c_u64
assert a_i8 == c_i8
assert a_i16 == c_i16
assert a_i32 == c_i32
assert a_i64 == c_i64
assert a_int == c_int
assert a_f32 == c_f32
assert a_f64 == c_f64
assert a_bool == c_bool
assert a_rune == c_rune
assert a_isize == c_isize
assert a_usize == c_usize
assert a_string == c_string
assert b_u8 == [u8(137)]
assert b_u16 == [u8(205), 20]
assert b_u32 == [u8(189), 229, 3, 0]
assert b_u64 == [u8(112), 18, 4, 148, 0, 0, 0, 0]
assert b_i8 == [u8(245)]
assert b_i16 == [u8(239), 246]
assert b_i32 == [u8(206), 43, 255, 255]
assert b_i64 == [u8(107), 201, 196, 252, 255, 255, 255, 255]
assert b_int == [u8(121), 130, 255, 255, 255, 255, 255, 255]
assert b_f32 == [u8(41), 92, 175, 63]
assert b_f64 == [u8(118), 113, 27, 13, 20, 100, 223, 192]
assert b_bool == [u8(1)]
assert b_rune == [u8(101), 38, 0, 0]
assert b_string == [u8(18), 0, 0, 0, 0, 0, 0, 0, 226, 153, 165, 240, 159, 150, 138, 100, 115,
115, 101, 114, 51, 51, 51, 49, 48, 48]
$if x64 {
assert b_isize == [u8(135), 78, 255, 255, 255, 255, 255, 255]
assert b_usize == [u8(83), 152, 6, 0, 0, 0, 0, 0]
} $else {
assert b_isize == [u8(135), 78, 255, 255]
assert b_usize == [u8(83), 152, 6, 0]
}
}
fn test_encode_decode_array() {
a_u8 := [u8(137), 21]
a_u16 := [u16(5325), 322]
a_u32 := [u32(255421), 34255]
a_u64 := [u64(2483294832), 321554321]
a_i8 := [i8(-11), 17]
a_i16 := [i16(-2321), 6543]
a_i32 := [i32(-54322), 23326]
a_i64 := [i64(-54212245), 54223333]
a_int := [int(-32135), 732561]
a_f32 := [f32(1.37), -5442.3]
a_f64 := [f64(-32144.3133), 432e-13]
a_bool := [bool(true), false]
a_rune := [`♥`, `🖊`]
a_isize := [isize(-45433), 24342]
a_usize := [usize(432211), 888533]
a_string := ['♥', '🖊', 'dfd21']
b_u8 := encode_binary(a_u8)!
b_u16 := encode_binary(a_u16)!
b_u32 := encode_binary(a_u32)!
b_u64 := encode_binary(a_u64)!
b_i8 := encode_binary(a_i8)!
b_i16 := encode_binary(a_i16)!
b_i32 := encode_binary(a_i32)!
b_i64 := encode_binary(a_i64)!
b_int := encode_binary(a_int)!
b_f32 := encode_binary(a_f32)!
b_f64 := encode_binary(a_f64)!
b_bool := encode_binary(a_bool)!
b_rune := encode_binary(a_rune)!
b_isize := encode_binary(a_isize)!
b_usize := encode_binary(a_usize)!
b_string := encode_binary(a_string)!
c_u8 := decode_binary[[]u8](b_u8)!
c_u16 := decode_binary[[]u16](b_u16)!
c_u32 := decode_binary[[]u32](b_u32)!
c_u64 := decode_binary[[]u64](b_u64)!
c_i8 := decode_binary[[]i8](b_i8)!
c_i16 := decode_binary[[]i16](b_i16)!
c_i32 := decode_binary[[]i32](b_i32)!
c_i64 := decode_binary[[]i64](b_i64)!
c_int := decode_binary[[]int](b_int)!
c_f32 := decode_binary[[]f32](b_f32)!
c_f64 := decode_binary[[]f64](b_f64)!
c_bool := decode_binary[[]bool](b_bool)!
c_rune := decode_binary[[]rune](b_rune)!
c_isize := decode_binary[[]isize](b_isize)!
c_usize := decode_binary[[]usize](b_usize)!
c_string := decode_binary[[]string](b_string)!
assert a_u8 == c_u8
assert a_u16 == c_u16
assert a_u32 == c_u32
assert a_u64 == c_u64
assert a_i8 == c_i8
assert a_i16 == c_i16
assert a_i32 == c_i32
assert a_i64 == c_i64
assert a_int == c_int
assert a_f32 == c_f32
assert a_f64 == c_f64
assert a_bool == c_bool
assert a_rune == c_rune
assert a_isize == c_isize
assert a_usize == c_usize
assert a_string == c_string
assert b_u8 == [u8(2), 0, 0, 0, 0, 0, 0, 0, 137, 21]
assert b_u16 == [u8(2), 0, 0, 0, 0, 0, 0, 0, 205, 20, 66, 1]
assert b_u32 == [u8(2), 0, 0, 0, 0, 0, 0, 0, 189, 229, 3, 0, 207, 133, 0, 0]
assert b_u64 == [u8(2), 0, 0, 0, 0, 0, 0, 0, 112, 18, 4, 148, 0, 0, 0, 0, 145, 135, 42, 19,
0, 0, 0, 0]
assert b_i8 == [u8(2), 0, 0, 0, 0, 0, 0, 0, 245, 17]
assert b_i16 == [u8(2), 0, 0, 0, 0, 0, 0, 0, 239, 246, 143, 25]
assert b_i32 == [u8(2), 0, 0, 0, 0, 0, 0, 0, 206, 43, 255, 255, 30, 91, 0, 0]
assert b_i64 == [u8(2), 0, 0, 0, 0, 0, 0, 0, 107, 201, 196, 252, 255, 255, 255, 255, 229, 97,
59, 3, 0, 0, 0, 0]
assert b_int == [u8(2), 0, 0, 0, 0, 0, 0, 0, 121, 130, 255, 255, 255, 255, 255, 255, 145, 45,
11, 0, 0, 0, 0, 0]
assert b_f32 == [u8(2), 0, 0, 0, 0, 0, 0, 0, 41, 92, 175, 63, 102, 18, 170, 197]
assert b_f64 == [u8(2), 0, 0, 0, 0, 0, 0, 0, 118, 113, 27, 13, 20, 100, 223, 192, 253, 251,
253, 7, 220, 191, 199, 61]
assert b_bool == [u8(2), 0, 0, 0, 0, 0, 0, 0, 1, 0]
assert b_rune == [u8(2), 0, 0, 0, 0, 0, 0, 0, 101, 38, 0, 0, 138, 245, 1, 0]
assert b_string == [u8(3), 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 226, 153, 165, 4, 0,
0, 0, 0, 0, 0, 0, 240, 159, 150, 138, 5, 0, 0, 0, 0, 0, 0, 0, 100, 102, 100, 50, 49]
$if x64 {
assert b_isize == [u8(2), 0, 0, 0, 0, 0, 0, 0, 135, 78, 255, 255, 255, 255, 255, 255, 22,
95, 0, 0, 0, 0, 0, 0]
assert b_usize == [u8(2), 0, 0, 0, 0, 0, 0, 0, 83, 152, 6, 0, 0, 0, 0, 0, 213, 142, 13,
0, 0, 0, 0, 0]
} $else {
assert b_isize == [u8(2), 0, 0, 0, 0, 0, 0, 0, 135, 78, 255, 255, 22, 95, 0, 0]
assert b_usize == [u8(2), 0, 0, 0, 0, 0, 0, 0, 83, 152, 6, 0, 213, 142, 13, 0]
}
}
struct St {
i int
}
fn test_encode_decode_map() {
a_map_string_string := {
'abc': 'def'
}
a_map_string_int := {
'abc': int(21343)
}
a_map_string_u8 := {
'abc': u8(37)
}
a_map_string_u16 := {
'abc': u16(3347)
}
a_map_string_u32 := {
'abc': u32(333347)
}
a_map_string_u64 := {
'abc': u64(64423)
}
a_map_string_i8 := {
'abc': i8(-37)
}
a_map_string_i16 := {
'abc': i16(-3347)
}
a_map_string_i32 := {
'abc': i32(-333347)
}
a_map_string_i64 := {
'abc': i64(-64423)
}
a_map_string_f32 := {
'abc': f32(1.543)
}
a_map_string_f64 := {
'abc': f64(1.54e31)
}
a_map_string_bool := {
'abc': true
}
a_map_string_rune := {
'abc': `♥`
}
a_map_string_isize := {
'abc': isize(-45433)
}
a_map_string_usize := {
'abc': usize(432211)
}
a_map_string_struct := {
's': St{1}
}
b_map_string_string := encode_binary(a_map_string_string)!
b_map_string_int := encode_binary(a_map_string_int)!
b_map_string_u8 := encode_binary(a_map_string_u8)!
b_map_string_u16 := encode_binary(a_map_string_u16)!
b_map_string_u32 := encode_binary(a_map_string_u32)!
b_map_string_u64 := encode_binary(a_map_string_u64)!
b_map_string_i8 := encode_binary(a_map_string_i8)!
b_map_string_i16 := encode_binary(a_map_string_i16)!
b_map_string_i32 := encode_binary(a_map_string_i32)!
b_map_string_i64 := encode_binary(a_map_string_i64)!
b_map_string_f32 := encode_binary(a_map_string_f32)!
b_map_string_f64 := encode_binary(a_map_string_f64)!
b_map_string_bool := encode_binary(a_map_string_bool)!
b_map_string_rune := encode_binary(a_map_string_rune)!
b_map_string_isize := encode_binary(a_map_string_isize)!
b_map_string_usize := encode_binary(a_map_string_usize)!
b_map_string_struct := encode_binary(a_map_string_struct)!
c_map_string_string := decode_binary[map[string]string](b_map_string_string)!
c_map_string_int := decode_binary[map[string]int](b_map_string_int)!
c_map_string_u8 := decode_binary[map[string]u8](b_map_string_u8)!
c_map_string_u16 := decode_binary[map[string]u16](b_map_string_u16)!
c_map_string_u32 := decode_binary[map[string]u32](b_map_string_u32)!
c_map_string_u64 := decode_binary[map[string]u64](b_map_string_u64)!
c_map_string_i8 := decode_binary[map[string]i8](b_map_string_i8)!
c_map_string_i16 := decode_binary[map[string]i16](b_map_string_i16)!
c_map_string_i32 := decode_binary[map[string]i32](b_map_string_i32)!
c_map_string_i64 := decode_binary[map[string]i64](b_map_string_i64)!
c_map_string_f32 := decode_binary[map[string]f32](b_map_string_f32)!
c_map_string_f64 := decode_binary[map[string]f64](b_map_string_f64)!
c_map_string_bool := decode_binary[map[string]bool](b_map_string_bool)!
c_map_string_rune := decode_binary[map[string]rune](b_map_string_rune)!
c_map_string_isize := decode_binary[map[string]isize](b_map_string_isize)!
c_map_string_usize := decode_binary[map[string]usize](b_map_string_usize)!
c_map_string_struct := decode_binary[map[string]St](b_map_string_struct)!
assert a_map_string_string == c_map_string_string
assert a_map_string_int == c_map_string_int
assert a_map_string_u8 == c_map_string_u8
assert a_map_string_u16 == c_map_string_u16
assert a_map_string_u32 == c_map_string_u32
assert a_map_string_i8 == c_map_string_i8
assert a_map_string_i16 == c_map_string_i16
assert a_map_string_i32 == c_map_string_i32
assert a_map_string_i64 == c_map_string_i64
assert a_map_string_f32 == c_map_string_f32
assert a_map_string_f64 == c_map_string_f64
assert a_map_string_bool == c_map_string_bool
assert a_map_string_rune == c_map_string_rune
assert a_map_string_isize == c_map_string_isize
assert a_map_string_usize == c_map_string_usize
assert a_map_string_struct == c_map_string_struct
assert b_map_string_string == [u8(1), 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 97, 98,
99, 3, 0, 0, 0, 0, 0, 0, 0, 100, 101, 102]
assert b_map_string_int == [u8(1), 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 97, 98, 99,
95, 83, 0, 0, 0, 0, 0, 0]
assert b_map_string_u8 == [u8(1), 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 97, 98, 99,
37]
assert b_map_string_u16 == [u8(1), 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 97, 98, 99,
19, 13]
assert b_map_string_u32 == [u8(1), 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 97, 98, 99,
35, 22, 5, 0]
assert b_map_string_u64 == [u8(1), 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 97, 98, 99,
167, 251, 0, 0, 0, 0, 0, 0]
assert b_map_string_i8 == [u8(1), 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 97, 98, 99,
219]
assert b_map_string_i16 == [u8(1), 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 97, 98, 99,
237, 242]
assert b_map_string_i32 == [u8(1), 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 97, 98, 99,
221, 233, 250, 255]
assert b_map_string_i64 == [u8(1), 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 97, 98, 99,
89, 4, 255, 255, 255, 255, 255, 255]
assert b_map_string_f32 == [u8(1), 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 97, 98, 99,
6, 129, 197, 63]
assert b_map_string_f64 == [u8(1), 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 97, 98, 99,
212, 186, 221, 173, 2, 76, 104, 70]
assert b_map_string_bool == [u8(1), 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 97, 98, 99,
1]
assert b_map_string_rune == [u8(1), 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 97, 98, 99,
101, 38, 0, 0]
$if x64 {
assert b_map_string_isize == [u8(1), 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 97, 98,
99, 135, 78, 255, 255, 255, 255, 255, 255]
assert b_map_string_usize == [u8(1), 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 97, 98,
99, 83, 152, 6, 0, 0, 0, 0, 0]
} $else {
assert b_map_string_isize == [u8(1), 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 97, 98,
99, 135, 78, 255, 255]
assert b_map_string_usize == [u8(1), 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 97, 98,
99, 83, 152, 6, 0]
}
assert b_map_string_struct == [u8(1), 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 115, 1,
0, 0, 0, 0, 0, 0, 0]
}
struct MyStruct {
f_u8 u8
f_u16 u16
f_u32 u32
f_u64 u64
f_i8 i8
f_i16 i16
f_i32 i32
f_i64 i64
f_int int
f_f32 f32
f_f64 f64
f_bool bool
f_rune rune
f_isize isize
f_usize usize
f_string string
f_array_u8 []u8
f_array_string []string
}
fn test_encode_decode_struct() {
a_struct := MyStruct{
f_u8: u8(31)
f_u16: u16(57)
f_u32: u32(6432)
f_u64: u64(7896423)
f_i8: i8(-22)
f_i16: i16(-5433)
f_i32: i32(-54244)
f_i64: i64(-8322234)
f_int: int(4235)
f_f32: f32(1.5382)
f_f64: f64(22421.32)
f_bool: bool(true)
f_rune: rune(`♥`)
f_isize: isize(42323)
f_usize: usize(83842)
f_string: 'fds♥323s'
f_array_u8: [u8(32), 22, 55, 72]
f_array_string: ['dfdss', 'dfsd3', '54344']
}
b_struct := encode_binary(a_struct)!
c_struct := decode_binary[MyStruct](b_struct)!
assert a_struct == c_struct
}
struct MyStruct_SkipFields {
mut:
f_u8 u8
f_u16 u16 @[serialize: '-']
f_u32 u32
f_u64 u64
f_i8 i8
f_i16 i16
f_i32 i32
f_i64 i64
f_int int
f_f32 f32
f_f64 f64
f_bool bool
f_rune rune
f_isize isize
f_usize usize
f_string string
f_array_u8 []u8 @[serialize: '-']
f_array_string []string
}
fn test_encode_decode_struct_skip_fields() {
a_struct := MyStruct_SkipFields{
f_u8: u8(31)
f_u16: u16(57)
f_u32: u32(6432)
f_u64: u64(7896423)
f_i8: i8(-22)
f_i16: i16(-5433)
f_i32: i32(-54244)
f_i64: i64(-8322234)
f_int: int(4235)
f_f32: f32(1.5382)
f_f64: f64(22421.32)
f_bool: bool(true)
f_rune: rune(`♥`)
f_isize: isize(42323)
f_usize: usize(83842)
f_string: 'fds♥323s'
f_array_u8: [u8(32), 22, 55, 72]
f_array_string: ['dfdss', 'dfsd3', '54344']
}
b_struct := encode_binary(a_struct)!
mut c_struct := decode_binary[MyStruct_SkipFields](b_struct)!
assert a_struct != c_struct
c_struct.f_u16 = u16(57)
c_struct.f_array_u8 = [u8(32), 22, 55, 72]
assert a_struct == c_struct
}
struct ComplexStruct {
f_u8 u8
f_u32 u32
f_u64 u64
f_string string
f_structs []MyStruct
f_maps []map[string]string
}
fn test_encode_decode_complex() {
a_complex := ComplexStruct{
f_u8: u8(78)
f_u32: u32(0x11223344)
f_u64: u64(0x55667788)
f_string: 'complex'
f_structs: [
MyStruct{
f_u8: u8(31)
f_u16: u16(57)
f_u32: u32(6432)
f_u64: u64(7896423)
f_i8: i8(-22)
f_i16: i16(-5433)
f_i32: i32(-54244)
f_i64: i64(-8322234)
f_int: int(4235)
f_f32: f32(1.5382)
f_f64: f64(22421.32)
f_bool: bool(true)
f_rune: rune(`♥`)
f_isize: isize(42323)
f_usize: usize(83842)
f_string: 'fds♥323s'
f_array_u8: [u8(32), 22, 55, 72]
f_array_string: ['dfdss', 'dfsd3', '54344']
},
MyStruct{
f_u8: u8(41)
f_u16: u16(67)
f_u32: u32(7432)
f_u64: u64(8896423)
f_i8: i8(-32)
f_i16: i16(-6433)
f_i32: i32(-64244)
f_i64: i64(-9322234)
f_int: int(5235)
f_f32: f32(2.5382)
f_f64: f64(32421.32)
f_bool: bool(true)
f_rune: rune(`♥`)
f_isize: isize(52323)
f_usize: usize(93842)
f_string: 'ads♥323s'
f_array_u8: [u8(22), 22, 55, 72]
f_array_string: ['dxfdss', 'dsefsd3', 'po54344']
},
]
f_maps: [
{
'abc': 'def'
},
{
'123': '456'
},
{
',./': '!@#'
},
]
}
b_complex := encode_binary(a_complex)!
c_complex := decode_binary[ComplexStruct](b_complex)!
assert a_complex == c_complex
// big endian test
b_complex_big_endian := encode_binary(a_complex, big_endian: true)!
c_complex_big_endian := decode_binary[ComplexStruct](b_complex_big_endian, big_endian: true)!
assert b_complex != b_complex_big_endian
assert a_complex == c_complex_big_endian
}
struct SharedFieldStruct {
pub mut:
a int
b shared map[string]string
c int
}
fn test_skip_shared_field() {
x := SharedFieldStruct{
a: 100
b: {
'a': 'a'
'b': 'b'
}
c: 200
}
x_u8 := encode_binary(x)!
assert x_u8 == [u8(100), 0, 0, 0, 0, 0, 0, 0, 200, 0, 0, 0, 0, 0, 0, 0]
y := decode_binary[SharedFieldStruct](x_u8)!
assert '${y}' == 'binary.SharedFieldStruct{
a: 100
b: {}
c: 200
}'
}
+316
View File
@@ -0,0 +1,316 @@
module binary
import io
// ByteOrder decodes and encodes fixed-size integers using a specific byte order.
pub interface ByteOrder {
u16(b []u8) u16
u32(b []u8) u32
u64(b []u8) u64
put_u16(mut b []u8, value u16)
put_u32(mut b []u8, value u32)
put_u64(mut b []u8, value u64)
}
// LittleEndian implements ByteOrder using little-endian encoding.
pub struct LittleEndian {}
// BigEndian implements ByteOrder using big-endian encoding.
pub struct BigEndian {}
pub const little_endian = LittleEndian{}
pub const big_endian = BigEndian{}
// u16 decodes a 16-bit unsigned integer from b using little-endian byte order.
pub fn (_ LittleEndian) u16(b []u8) u16 {
return little_endian_u16(b)
}
// u32 decodes a 32-bit unsigned integer from b using little-endian byte order.
pub fn (_ LittleEndian) u32(b []u8) u32 {
return little_endian_u32(b)
}
// u64 decodes a 64-bit unsigned integer from b using little-endian byte order.
pub fn (_ LittleEndian) u64(b []u8) u64 {
return little_endian_u64(b)
}
// put_u16 encodes value into b using little-endian byte order.
pub fn (_ LittleEndian) put_u16(mut b []u8, value u16) {
little_endian_put_u16(mut b, value)
}
// put_u32 encodes value into b using little-endian byte order.
pub fn (_ LittleEndian) put_u32(mut b []u8, value u32) {
little_endian_put_u32(mut b, value)
}
// put_u64 encodes value into b using little-endian byte order.
pub fn (_ LittleEndian) put_u64(mut b []u8, value u64) {
little_endian_put_u64(mut b, value)
}
// u16 decodes a 16-bit unsigned integer from b using big-endian byte order.
pub fn (_ BigEndian) u16(b []u8) u16 {
return big_endian_u16(b)
}
// u32 decodes a 32-bit unsigned integer from b using big-endian byte order.
pub fn (_ BigEndian) u32(b []u8) u32 {
return big_endian_u32(b)
}
// u64 decodes a 64-bit unsigned integer from b using big-endian byte order.
pub fn (_ BigEndian) u64(b []u8) u64 {
return big_endian_u64(b)
}
// put_u16 encodes value into b using big-endian byte order.
pub fn (_ BigEndian) put_u16(mut b []u8, value u16) {
big_endian_put_u16(mut b, value)
}
// put_u32 encodes value into b using big-endian byte order.
pub fn (_ BigEndian) put_u32(mut b []u8, value u32) {
big_endian_put_u32(mut b, value)
}
// put_u64 encodes value into b using big-endian byte order.
pub fn (_ BigEndian) put_u64(mut b []u8, value u64) {
big_endian_put_u64(mut b, value)
}
// read decodes fixed-size values from reader using order.
pub fn read[T](mut reader io.Reader, order ByteOrder, mut data T) ! {
read_value(mut reader, order, mut data)!
}
// write encodes fixed-size values to writer using order.
pub fn write[T](mut writer io.Writer, order ByteOrder, data T) ! {
write_value(mut writer, order, data)!
}
// size returns the number of bytes needed to encode data, or `-1` for unsupported types.
pub fn size[T](data T) int {
return size_value(data)
}
fn size_value[T](data T) int {
$if T is bool || T is u8 || T is i8 {
return 1
} $else $if T is u16 || T is i16 {
return 2
} $else $if T is u32 || T is i32 || T is f32 {
return 4
} $else $if T is u64 || T is i64 || T is f64 {
return 8
} $else $if T is $array_fixed {
mut total := 0
for i in 0 .. data.len {
value_size := size_value(data[i])
if value_size < 0 {
return -1
}
total += value_size
}
return total
} $else $if T is $array {
mut total := 0
for value in data {
value_size := size(value)
if value_size < 0 {
return -1
}
total += value_size
}
return total
} $else $if T is $struct {
mut total := 0
$for field in T.fields {
if field.name == '_' {
return -1
}
field_size := size(data.$(field.name))
if field_size < 0 {
return -1
}
total += field_size
}
return total
} $else {
return -1
}
}
fn read_value[T](mut reader io.Reader, order ByteOrder, mut data T) ! {
$if T is bool {
mut buf := []u8{len: 1}
read_full(mut reader, mut buf)!
data = buf[0] != 0
} $else $if T is u8 {
mut buf := []u8{len: 1}
read_full(mut reader, mut buf)!
data = buf[0]
} $else $if T is i8 {
mut buf := []u8{len: 1}
read_full(mut reader, mut buf)!
data = i8(buf[0])
} $else $if T is u16 {
mut buf := []u8{len: 2}
read_full(mut reader, mut buf)!
data = order.u16(buf)
} $else $if T is i16 {
mut buf := []u8{len: 2}
read_full(mut reader, mut buf)!
data = i16(order.u16(buf))
} $else $if T is u32 {
mut buf := []u8{len: 4}
read_full(mut reader, mut buf)!
data = order.u32(buf)
} $else $if T is i32 {
mut buf := []u8{len: 4}
read_full(mut reader, mut buf)!
data = i32(order.u32(buf))
} $else $if T is u64 {
mut buf := []u8{len: 8}
read_full(mut reader, mut buf)!
data = order.u64(buf)
} $else $if T is i64 {
mut buf := []u8{len: 8}
read_full(mut reader, mut buf)!
data = i64(order.u64(buf))
} $else $if T is f32 {
mut buf := []u8{len: 4}
read_full(mut reader, mut buf)!
data = unsafe {
U32_F32{
u: order.u32(buf)
}.f
}
} $else $if T is f64 {
mut buf := []u8{len: 8}
read_full(mut reader, mut buf)!
data = unsafe {
U64_F64{
u: order.u64(buf)
}.f
}
} $else $if T is $array_fixed {
for i in 0 .. data.len {
read_value(mut reader, order, mut data[i])!
}
} $else $if T is $array {
$if T is []u8 {
read_full(mut reader, mut data)!
} $else {
for i in 0 .. data.len {
read_value(mut reader, order, mut data[i])!
}
}
} $else $if T is $struct {
$for field in T.fields {
if field.name == '_' {
return error('binary.read: structs with `_` fields are not supported')
}
read_value(mut reader, order, mut data.$(field.name))!
}
} $else {
return error('binary.read: unsupported type ${typeof(data).name}')
}
}
fn write_value[T](mut writer io.Writer, order ByteOrder, data T) ! {
$if T is bool {
write_full(mut writer, [u8(data)])!
} $else $if T is u8 {
write_full(mut writer, [u8(data)])!
} $else $if T is i8 {
write_full(mut writer, [u8(data)])!
} $else $if T is u16 {
mut buf := []u8{len: 2}
order.put_u16(mut buf, data)
write_full(mut writer, buf)!
} $else $if T is i16 {
mut buf := []u8{len: 2}
order.put_u16(mut buf, u16(data))
write_full(mut writer, buf)!
} $else $if T is u32 {
mut buf := []u8{len: 4}
order.put_u32(mut buf, data)
write_full(mut writer, buf)!
} $else $if T is i32 {
mut buf := []u8{len: 4}
order.put_u32(mut buf, u32(data))
write_full(mut writer, buf)!
} $else $if T is u64 {
mut buf := []u8{len: 8}
order.put_u64(mut buf, data)
write_full(mut writer, buf)!
} $else $if T is i64 {
mut buf := []u8{len: 8}
order.put_u64(mut buf, u64(data))
write_full(mut writer, buf)!
} $else $if T is f32 {
mut buf := []u8{len: 4}
bits := unsafe {
U32_F32{
f: data
}.u
}
order.put_u32(mut buf, bits)
write_full(mut writer, buf)!
} $else $if T is f64 {
mut buf := []u8{len: 8}
bits := unsafe {
U64_F64{
f: data
}.u
}
order.put_u64(mut buf, bits)
write_full(mut writer, buf)!
} $else $if T is $array_fixed {
for i in 0 .. data.len {
write_value(mut writer, order, data[i])!
}
} $else $if T is $array {
$if T is []u8 {
write_full(mut writer, data)!
} $else {
for value in data {
write_value(mut writer, order, value)!
}
}
} $else $if T is $struct {
$for field in T.fields {
if field.name == '_' {
return error('binary.write: structs with `_` fields are not supported')
}
write_value(mut writer, order, data.$(field.name))!
}
} $else {
return error('binary.write: unsupported type ${typeof(data).name}')
}
}
fn read_full(mut reader io.Reader, mut buf []u8) ! {
mut offset := 0
for offset < buf.len {
n := reader.read(mut buf[offset..])!
if n <= 0 || n > buf.len - offset {
return error('binary.read: reader returned an invalid number of bytes')
}
offset += n
}
}
fn write_full(mut writer io.Writer, buf []u8) ! {
mut offset := 0
for offset < buf.len {
n := writer.write(buf[offset..])!
if n <= 0 || n > buf.len - offset {
return error('binary.write: writer returned an invalid number of bytes')
}
offset += n
}
}
+132
View File
@@ -0,0 +1,132 @@
module binary
import io
struct ChunkedReader {
data []u8
chunk int = 1
mut:
offset int
}
fn (mut r ChunkedReader) read(mut buf []u8) !int {
if r.offset >= r.data.len {
return io.Eof{}
}
mut n := if r.chunk > 0 && r.chunk < buf.len { r.chunk } else { buf.len }
remaining := r.data.len - r.offset
if remaining < n {
n = remaining
}
copy(mut buf[..n], r.data[r.offset..r.offset + n])
r.offset += n
return n
}
struct ChunkedWriter {
chunk int = 1
mut:
data []u8
}
fn (mut w ChunkedWriter) write(buf []u8) !int {
if buf.len == 0 {
return 0
}
n := if w.chunk > 0 && w.chunk < buf.len { w.chunk } else { buf.len }
w.data << buf[..n]
return n
}
struct Packet {
flag bool
count u16
values [2]u32
sample f32
trailer [3]u8
}
struct UnsupportedPacket {
name string
}
fn test_stream_write_and_read_struct() {
packet := Packet{
flag: true
count: 0x1234
values: [u32(0x11223344), 0x55667788]!
sample: f32(12.5)
trailer: [u8(1), 2, 3]!
}
mut writer := ChunkedWriter{
chunk: 3
}
write(mut writer, big_endian, packet)!
assert size(packet) == writer.data.len
mut reader := ChunkedReader{
data: writer.data.clone()
chunk: 2
}
mut decoded := Packet{}
read(mut reader, big_endian, mut decoded)!
assert decoded == packet
}
fn test_stream_read_and_write_slice() {
values := [u16(0x0102), 0x0304, 0x0506]
mut writer := ChunkedWriter{
chunk: 2
}
write(mut writer, little_endian, values)!
assert size(values) == 6
mut reader := ChunkedReader{
data: writer.data.clone()
chunk: 1
}
mut decoded := []u16{len: values.len}
read(mut reader, little_endian, mut decoded)!
assert decoded == values
}
fn test_stream_read_and_write_bytes() {
bytes := [u8(9), 8, 7, 6, 5]
mut writer := ChunkedWriter{
chunk: 4
}
write(mut writer, little_endian, bytes)!
mut reader := ChunkedReader{
data: writer.data.clone()
chunk: 2
}
mut decoded := []u8{len: bytes.len}
read(mut reader, little_endian, mut decoded)!
assert decoded == bytes
}
fn test_stream_short_read_returns_eof() {
mut reader := ChunkedReader{
data: [u8(1), 2]
chunk: 1
}
mut value := u32(0)
if _ := read(mut reader, little_endian, mut value) {
assert false
} else {
assert err is io.Eof
}
}
fn test_stream_reports_unsupported_types() {
assert size('hello') == -1
assert size(UnsupportedPacket{}) == -1
mut writer := ChunkedWriter{}
if _ := write(mut writer, little_endian, 'hello') {
assert false
} else {
assert err.msg() == 'binary.write: unsupported type string'
}
}
+30
View File
@@ -0,0 +1,30 @@
// Copyright (c) 2025 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
module binary
// Unions used across the little endian/big endian conversion routines.
union U16 {
mut:
b [2]u8
u u16
}
union U32 {
mut:
b [4]u8
u u32
}
union U64 {
mut:
b [8]u8
u u64
}
union F32 {
mut:
b [4]u8
u f32
}