Skip to content

Commit

Permalink
add: binary representations of Int/Int64/Double
Browse files Browse the repository at this point in the history
  • Loading branch information
shamiao committed Apr 29, 2024
1 parent 09f2c1c commit 620370e
Show file tree
Hide file tree
Showing 11 changed files with 614 additions and 12 deletions.
64 changes: 64 additions & 0 deletions double/double.mbt
Original file line number Diff line number Diff line change
Expand Up @@ -174,3 +174,67 @@ test "min_normal" {
pub fn hash(self : Double) -> Int {
self.reinterpret_as_i64().hash()
}

/// Write bytes representation of a `Double` by specified endianness into target view of `Byte`.
///
/// Note: `into_byte_view_le()` and `into_byte_view_be()` can be used as a shorthand
/// to avoid writing `Endianness` enum variant explicitly.
///
/// # Panics
///
/// 8 bytes of data are required in view. Panics when insufficient.
pub fn into_byte_view[V: @byte.ByteMutView](self: Double, view: V, endian: @representation.Endianness) -> Unit {
self.reinterpret_as_i64().into_byte_view(view, endian)
}

/// Write bytes representation of a `Double` by little-endian into target view of `Byte`. A shorthand
/// of `into_byte_view(V, @representation.Endianness::LittleEndian)`.
///
/// # Panics
///
/// 8 bytes of data are required in view. Panics when insufficient.
pub fn into_byte_view_le[V: @byte.ByteMutView](self: Double, view: V) -> Unit {
self.reinterpret_as_i64().into_byte_view(view, @representation.Endianness::LittleEndian)
}

/// Write bytes representation of a `Double` by little-endian into target view of `Byte`. A shorthand
/// of `into_byte_view(V, @representation.Endianness::LittleEndian)`.
///
/// # Panics
///
/// 4 bytes of data are required in view. Panics when insufficient.
pub fn into_byte_view_be[V: @byte.ByteMutView](self: Double, view: V) -> Unit {
self.reinterpret_as_i64().into_byte_view(view, @representation.Endianness::BigEndian)
}

/// Extract a `Double` from the source view of `Byte` by specified endianness.
///
/// Note: `from_byte_view_le()` and `from_byte_view_be()` can be used as a shorthand
/// to avoid writing `Endianness` enum variant explicitly.
///
/// # Panics
///
/// 4 bytes of data are required in view. Panics when insufficient.
pub fn Double::from_byte_view[V: @byte.ByteView](view: V, endian: @representation.Endianness) -> Double {
Int64::from_byte_view(view, endian).reinterpret_as_double()
}

/// Extract a `Double` from the source view of `Byte` by little-endian. A shorthand
/// of `from_byte_view(V, @representation.Endianness::LittleEndian)`.
///
/// # Panics
///
/// 8 bytes of data are required in view. Panics when insufficient.
pub fn Double::from_byte_view_le[V: @byte.ByteView](view: V) -> Double {
Int64::from_byte_view(view, @representation.Endianness::LittleEndian).reinterpret_as_double()
}

/// Extract a `Double` from the source view of `Byte` by big-endian. A shorthand
/// of `from_byte_view(V, @representation.Endianness::BigEndian)`.
///
/// # Panics
///
/// 8 bytes of data are required in view. Panics when insufficient.
pub fn Double::from_byte_view_be[V: @byte.ByteView](view: V) -> Double {
Int64::from_byte_view(view, @representation.Endianness::BigEndian).reinterpret_as_double()
}
89 changes: 89 additions & 0 deletions double/double_test.mbt
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,92 @@ test "double.num" {
let y = 792.0
test_num(x, y, x + y, x * y, x - y, x / y, -1.0)?
}

test "double.into_byte_view(series)" {
let test_bytes_eq = fn (l: Bytes, r: Bytes) -> Result[Unit, String] {
if l.length() == r.length() {
for i = 0; i < l.length(); i = i + 1 {
if l[i] != r[i] {
let (lv, rv) = (l[i], r[i])
return Err("byte mismatch at \(i): \(lv) != \(rv)")
}
}
Ok(())
} else {
let (ll, rl) = (l.length(), r.length())
Err("length mismatch: \(ll) != \(rl)")
}
}

let cases = [
(
-5.882781560676213e+94, // 0xd39c33c7f8175d9c (IEEE 754)
0,
Bytes::[0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc],
Bytes::[0x9c, 0x5d, 0x17, 0xf8, 0xc7, 0x33, 0x9c, 0xd3, 0xcc, 0xcc],
Bytes::[0xd3, 0x9c, 0x33, 0xc7, 0xf8, 0x17, 0x5d, 0x9c, 0xcc, 0xcc],
),
(
5.2795633159472795e-219, // 0x129dd1b1e9f37d1a (IEEE 754)
1,
Bytes::[0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa],
Bytes::[0xaa, 0x1a, 0x7d, 0xf3, 0xe9, 0xb1, 0xd1, 0x9d, 0x12, 0xaa],
Bytes::[0xaa, 0x12, 0x9d, 0xd1, 0xb1, 0xe9, 0xf3, 0x7d, 0x1a, 0xaa],
),
(
-2.752623365577015e-164, // 0x9df95caf8b51fc5e (IEEE 754)
2,
Bytes::[0xdf, 0xfd, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf],
Bytes::[0xdf, 0xfd, 0x5e, 0xfc, 0x51, 0x8b, 0xaf, 0x5c, 0xf9, 0x9d],
Bytes::[0xdf, 0xfd, 0x9d, 0xf9, 0x5c, 0xaf, 0x8b, 0x51, 0xfc, 0x5e],
),
]

for icase = 0; icase < cases.length(); icase = icase + 1 {
let (input, pos, original, le_expected, be_expected) = cases[icase];

let buffer = original.copy()
let view = buffer[pos..(pos + 8)]
input.into_byte_view(view, @representation.Endianness::LittleEndian)
test_bytes_eq(buffer, le_expected)?

let buffer = original.copy()
let view = buffer[pos..(pos + 8)]
input.into_byte_view(view, @representation.Endianness::BigEndian)
test_bytes_eq(buffer, be_expected)?

let buffer = original.copy()
let view = buffer[pos..(pos + 8)]
input.into_byte_view(view, @representation.Endianness::NetworkEndian)
test_bytes_eq(buffer, be_expected)?

let buffer = original.copy()
let view = buffer[pos..(pos + 8)]
input.into_byte_view_le(view)
test_bytes_eq(buffer, le_expected)?

let buffer = original.copy()
let view = buffer[pos..(pos + 8)]
input.into_byte_view_be(view)
test_bytes_eq(buffer, be_expected)?
}
}

test "double.from_byte_view(series)" {
let input = Bytes::[0x23, 0xb6, 0x1e, 0xc2, 0xa3, 0xbb, 0x19, 0x0f, 0x6d, 0x27]
let cases = [
(0, 6.322855833834645e-236, 1.1888038185948251e-136),
(1, 2.144245627950293e+217, -5.261764689278282e-48),
(2, 9.002687664123903e-119, 1.657251015555868e-160),
]
for icase = 0; icase < cases.length(); icase = icase + 1 {
let (pos, le_expected, be_expected) = cases[icase];
let view = input[pos..(pos + 8)]

@assertion.assert_eq(Double::from_byte_view(view, @representation.Endianness::LittleEndian), le_expected)?
@assertion.assert_eq(Double::from_byte_view_le(view), le_expected)?
@assertion.assert_eq(Double::from_byte_view(view, @representation.Endianness::BigEndian), be_expected)?
@assertion.assert_eq(Double::from_byte_view(view, @representation.Endianness::NetworkEndian), be_expected)?
@assertion.assert_eq(Double::from_byte_view_be(view), be_expected)?
}
}
9 changes: 7 additions & 2 deletions double/moon.pkg.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@
"moonbitlang/core/assertion",
"moonbitlang/core/bool",
"moonbitlang/core/int64",
"moonbitlang/core/coverage"
"moonbitlang/core/coverage",
"moonbitlang/core/byte",
"moonbitlang/core/representation"
],
"test_import": ["moonbitlang/core/num"]
"test_import": [
"moonbitlang/core/num",
"moonbitlang/core/bytes"
]
}
101 changes: 99 additions & 2 deletions int/int.mbt
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,107 @@ pub fn Int::min_value() -> Int {
min_val
}

pub fn hash(self : Int) -> Int {
self
}

/// Write bytes representation of an `Int` by specified endianness into target view of `Byte`.
///
/// # Example
///
/// ```
/// let bytes = Bytes::[0x4D, 0x00, 0x7A, 0x00, 0x7A, 0x00, 0x42, 0x00]
/// 0x51546708.into_view(@representation.Endianness::LittleEndian, bytes[2..6])
/// @assertion.assert_eq(bytes.to_string(), "M\u{6708}\u{5154}B")?
/// // "M\u{6708}\u{5154}B"
/// // => "M月兔B"
/// // => hex:4D,00,08,67,54,51,42,00 (UTF-16)
/// ```
///
/// Note: `into_byte_view_le()` and `into_byte_view_be()` can be used as a shorthand
/// to avoid writing `Endianness` enum variant explicitly.
///
/// # Panics
///
/// 4 bytes of data are required in view. Panics when insufficient.
pub fn into_byte_view[V: @byte.ByteMutView](self: Int, view: V, endian: @representation.Endianness) -> Unit {
if view.length() < 4 {
let len = view.length()
abort("insufficient length: \(len) provided, 4 required")
}
for bs = 0; bs < 4; bs = bs + 1 {
let bd = endian.convert_index(bs, 4)
view[bd] = Byte::from_int(self.lsr(bs * 8).land(0xFF))
}
}

/// Write bytes representation of an `Int` by little-endian into target view of `Byte`. A shorthand
/// of `into_byte_view(V, @representation.Endianness::LittleEndian)`.
///
/// # Panics
///
/// 4 bytes of data are required in view. Panics when insufficient.
pub fn into_byte_view_le[V: @byte.ByteMutView](self: Int, view: V) -> Unit {
self.into_byte_view(view, @representation.Endianness::LittleEndian)
}

/// Write bytes representation of an `Int` by little-endian into target view of `Byte`. A shorthand
/// of `into_byte_view(V, @representation.Endianness::LittleEndian)`.
///
/// # Panics
///
/// 4 bytes of data are required in view. Panics when insufficient.
pub fn into_byte_view_be[V: @byte.ByteMutView](self: Int, view: V) -> Unit {
self.into_byte_view(view, @representation.Endianness::BigEndian)
}

/// Extract an `Int` from the source view of `Byte` by specified endianness.
///
/// # Example
///
/// ```
/// let bytes = Bytes::[0xAA, 0x01, 0x02, 0x03, 0x04, 0xDD]
/// @assertion.assert_eq(
/// Int::from_byte_view(bytes[1..4], @representation.Endianness::LittleEndian),
/// 0x04030201
/// )?
/// ```
///
/// Note: `from_byte_view_le()` and `from_byte_view_be()` can be used as a shorthand
/// to avoid writing `Endianness` enum variant explicitly.
///
/// # Panics
///
/// 4 bytes of data are required in view. Panics when insufficient.
pub fn Int::from_byte_view[V: @byte.ByteView](view: V, endian: @representation.Endianness) -> Int {
if view.length() < 4 {
let len = view.length()
abort("insufficient length: \(len) provided, 4 required")
}
let mut r = 0
for bd = 0; bd < 4; bd = bd + 1 {
let bs = endian.convert_index(bd, 4)
r = r.lor(view[bs].to_int().land(0xff).lsl(8 * bd))
}
r
}

/// Extract an `Int` from the source view of `Byte` by little-endian. A shorthand
/// of `from_byte_view(V, @representation.Endianness::LittleEndian)`.
///
/// # Panics
///
/// 4 bytes of data are required in view. Panics when insufficient.
pub fn Int::from_byte_view_le[V: @byte.ByteView](view: V) -> Int {
Int::from_byte_view(view, @representation.Endianness::LittleEndian)
}

pub fn hash(self : Int) -> Int {
self
/// Extract an `Int` from the source view of `Byte` by big-endian. A shorthand
/// of `from_byte_view(V, @representation.Endianness::BigEndian)`.
///
/// # Panics
///
/// 4 bytes of data are required in view. Panics when insufficient.
pub fn Int::from_byte_view_be[V: @byte.ByteView](view: V) -> Int {
Int::from_byte_view(view, @representation.Endianness::BigEndian)
}
89 changes: 88 additions & 1 deletion int/int_test.mbt
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,91 @@ test "int.num" {
let x = -5
let y = 7
test_num(x, y, x + y, x * y, x - y, x / y, -1)?
}
}

test "int.into_byte_view(series)" {
let test_bytes_eq = fn (l: Bytes, r: Bytes) -> Result[Unit, String] {
if l.length() == r.length() {
for i = 0; i < l.length(); i = i + 1 {
if l[i] != r[i] {
let (lv, rv) = (l[i], r[i])
return Err("byte mismatch at \(i): \(lv) != \(rv)")
}
}
Ok(())
} else {
let (ll, rl) = (l.length(), r.length())
Err("length mismatch: \(ll) != \(rl)")
}
}

let cases = [
(
0x07_06_05_04,
0,
Bytes::[0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc],
Bytes::[0x04, 0x05, 0x06, 0x07, 0xcc, 0xcc],
Bytes::[0x07, 0x06, 0x05, 0x04, 0xcc, 0xcc],
),
(
0xff_0b_0c_0d,
1,
Bytes::[0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa],
Bytes::[0xaa, 0x0d, 0x0c, 0x0b, 0xff, 0xaa],
Bytes::[0xaa, 0xff, 0x0b, 0x0c, 0x0d, 0xaa],
),
(
0x44_33_22_11,
2,
Bytes::[0xdf, 0xfd, 0xdf, 0xdf, 0xdf, 0xdf],
Bytes::[0xdf, 0xfd, 0x11, 0x22, 0x33, 0x44],
Bytes::[0xdf, 0xfd, 0x44, 0x33, 0x22, 0x11]
),
]

for icase = 0; icase < cases.length(); icase = icase + 1 {
let (input, pos, original, le_expected, be_expected) = cases[icase];

let buffer = original.copy()
let view = buffer[pos..(pos + 4)]
input.into_byte_view(view, @representation.Endianness::LittleEndian)
test_bytes_eq(buffer, le_expected)?

let buffer = original.copy()
let view = buffer[pos..(pos + 4)]
input.into_byte_view(view, @representation.Endianness::BigEndian)
test_bytes_eq(buffer, be_expected)?

let buffer = original.copy()
let view = buffer[pos..(pos + 4)]
input.into_byte_view(view, @representation.Endianness::NetworkEndian)
test_bytes_eq(buffer, be_expected)?

let buffer = original.copy()
let view = buffer[pos..(pos + 4)]
input.into_byte_view_le(view)
test_bytes_eq(buffer, le_expected)?

let buffer = original.copy()
let view = buffer[pos..(pos + 4)]
input.into_byte_view_be(view)
test_bytes_eq(buffer, be_expected)?
}
}

test "int.from_byte_view(series)" {
let input = Bytes::[0x7c, 0x47, 0x18, 0x49, 0xff, 0x6c]
let cases = [
(0, 0x49_18_47_7c, 0x7c_47_18_49), (1, 0xff_49_18_47, 0x47_18_49_ff), (2, 0x6c_ff_49_18, 0x18_49_ff_6c),
]
for icase = 0; icase < cases.length(); icase = icase + 1 {
let (pos, le_expected, be_expected) = cases[icase];
let view = input[pos..(pos + 4)]

@assertion.assert_eq(Int::from_byte_view(view, @representation.Endianness::LittleEndian), le_expected)?
@assertion.assert_eq(Int::from_byte_view_le(view), le_expected)?
@assertion.assert_eq(Int::from_byte_view(view, @representation.Endianness::BigEndian), be_expected)?
@assertion.assert_eq(Int::from_byte_view(view, @representation.Endianness::NetworkEndian), be_expected)?
@assertion.assert_eq(Int::from_byte_view_be(view), be_expected)?
}
}
15 changes: 12 additions & 3 deletions int/moon.pkg.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
{
"import": ["moonbitlang/core/builtin", "moonbitlang/core/coverage"],
"test_import": ["moonbitlang/core/num", "moonbitlang/core/assertion"]
}
"import": [
"moonbitlang/core/builtin",
"moonbitlang/core/coverage",
"moonbitlang/core/byte",
"moonbitlang/core/representation"
],
"test_import": [
"moonbitlang/core/bytes",
"moonbitlang/core/num",
"moonbitlang/core/assertion"
]
}
Loading

0 comments on commit 620370e

Please sign in to comment.