From 620370eb50df817f0582d66395c2d247fad9a019 Mon Sep 17 00:00:00 2001 From: SHA Miao Date: Mon, 29 Apr 2024 21:12:51 +0800 Subject: [PATCH] add: binary representations of `Int`/`Int64`/`Double` --- double/double.mbt | 64 +++++++++++++++++++++ double/double_test.mbt | 89 +++++++++++++++++++++++++++++ double/moon.pkg.json | 9 ++- int/int.mbt | 101 ++++++++++++++++++++++++++++++++- int/int_test.mbt | 89 ++++++++++++++++++++++++++++- int/moon.pkg.json | 15 ++++- int64/int64.mbt | 103 +++++++++++++++++++++++++++++++++- int64/int64_test.mbt | 90 ++++++++++++++++++++++++++++- int64/moon.pkg.json | 9 ++- representation/endianness.mbt | 47 ++++++++++++++++ representation/moon.pkg.json | 10 ++++ 11 files changed, 614 insertions(+), 12 deletions(-) create mode 100644 representation/endianness.mbt create mode 100644 representation/moon.pkg.json diff --git a/double/double.mbt b/double/double.mbt index 5252bcd7..b5569f0b 100644 --- a/double/double.mbt +++ b/double/double.mbt @@ -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() +} diff --git a/double/double_test.mbt b/double/double_test.mbt index 4b9cd36f..31ba2463 100644 --- a/double/double_test.mbt +++ b/double/double_test.mbt @@ -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)? + } +} diff --git a/double/moon.pkg.json b/double/moon.pkg.json index 7eacc139..c6685da2 100644 --- a/double/moon.pkg.json +++ b/double/moon.pkg.json @@ -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" + ] } diff --git a/int/int.mbt b/int/int.mbt index bd009b58..33263b2d 100644 --- a/int/int.mbt +++ b/int/int.mbt @@ -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) } diff --git a/int/int_test.mbt b/int/int_test.mbt index a335d4b6..c75f083c 100644 --- a/int/int_test.mbt +++ b/int/int_test.mbt @@ -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)? -} \ No newline at end of file +} + +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)? + } +} diff --git a/int/moon.pkg.json b/int/moon.pkg.json index 36812239..2687e7bb 100644 --- a/int/moon.pkg.json +++ b/int/moon.pkg.json @@ -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" + ] +} \ No newline at end of file diff --git a/int64/int64.mbt b/int64/int64.mbt index 21241080..d5a89a32 100644 --- a/int64/int64.mbt +++ b/int64/int64.mbt @@ -48,10 +48,109 @@ pub fn Int64::min_value() -> Int64 { min_val } - - pub fn hash(self : Int64) -> Int { let lo = self.to_int() let hi = self.lsr(32).to_int() lo.lxor(hi) } + +/// Write bytes representation of an `Int64` by specified endianness into target view of `Byte`. +/// +/// # Example +/// +/// ``` +/// let bytes = Bytes::[0x4D, 0x00, 0x7A, 0x00, 0x7A, 0x00, 0x7A, 0x00, 0x7A, 0x00, 0x42, 0x00] +/// 0x8A008BED51546708.into_view(@representation.Endianness::LittleEndian, bytes[2..6]) +/// @assertion.assert_eq(bytes.to_string(), "M\u{6708}\u{5154}\u{8BED}\u{8A00}B")? +/// // "M\u{6708}\u{5154}\u{8BED}\u{8A00}B" +/// // => "M月兔语言B" +/// // => hex:4D,00,08,67,54,51,ED,8B,00,8A,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 +/// +/// 8 bytes of data are required in view. Panics when insufficient. +pub fn into_byte_view[V: @byte.ByteMutView](self: Int64, view: V, endian: @representation.Endianness) -> Unit { + if view.length() < 8 { + let len = view.length() + abort("insufficient length: \(len) provided, 8 required") + } + for bs = 0; bs < 8; bs = bs + 1 { + let bd = endian.convert_index(bs, 8) + view[bd] = Byte::from_int(self.lsr(bs * 8).land(0xFFL).to_int()) + } +} + +/// Write bytes representation of an `Int64` 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: Int64, view: V) -> Unit { + self.into_byte_view(view, @representation.Endianness::LittleEndian) +} + +/// Write bytes representation of an `Int64` 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: Int64, view: V) -> Unit { + self.into_byte_view(view, @representation.Endianness::BigEndian) +} + +/// Extract an `Int64` from the source view of `Byte` by specified endianness. +/// +/// # Example +/// +/// ``` +/// let bytes = Bytes::[0xAA, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0xDD] +/// @assertion.assert_eq( +/// Int64::from_byte_view(bytes[1..8], @representation.Endianness::LittleEndian), +/// 0x0807060504030201L +/// )? +/// ``` +/// +/// 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 Int64::from_byte_view[V: @byte.ByteView](view: V, endian: @representation.Endianness) -> Int64 { + if view.length() < 8 { + let len = view.length() + abort("insufficient length: \(len) provided, 4 required") + } + let mut r = 0L + for bd = 0; bd < 8; bd = bd + 1 { + let bs = endian.convert_index(bd, 8) + r = r.lor(view[bs].to_int().land(0xff).to_int64().lsl(8 * bd)) + } + r +} + +/// Extract an `Int64` 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 Int64::from_byte_view_le[V: @byte.ByteView](view: V) -> Int64 { + Int64::from_byte_view(view, @representation.Endianness::LittleEndian) +} + +/// Extract an `Int64` 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 Int64::from_byte_view_be[V: @byte.ByteView](view: V) -> Int64 { + Int64::from_byte_view(view, @representation.Endianness::BigEndian) +} diff --git a/int64/int64_test.mbt b/int64/int64_test.mbt index 2f909f91..f8873767 100644 --- a/int64/int64_test.mbt +++ b/int64/int64_test.mbt @@ -34,4 +34,92 @@ test "int64.num" { let x = -500L let y = 792L test_num(x, y, x + y, x * y, x - y, x / y, -1L)? -} \ No newline at end of file +} + +test "int64.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 = [ + ( + 0xff_01_02_03_04_05_06_07_L, + 0, + Bytes::[0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc], + Bytes::[0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0xff, 0xcc, 0xcc], + Bytes::[0xff, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0xcc, 0xcc], + ), + ( + 0x01_02_03_04_05_06_07_ff_L, + 1, + Bytes::[0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa], + Bytes::[0xaa, 0xff, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0xaa], + Bytes::[0xaa, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0xff, 0xaa], + ), + ( + 0xd3_9c_33_c7_f8_17_5d_9c_L, + 2, + Bytes::[0xdf, 0xfd, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf], + Bytes::[0xdf, 0xfd, 0x9c, 0x5d, 0x17, 0xf8, 0xc7, 0x33, 0x9c, 0xd3], + Bytes::[0xdf, 0xfd, 0xd3, 0x9c, 0x33, 0xc7, 0xf8, 0x17, 0x5d, 0x9c], + ), + ] + + 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 "int64.from_byte_view(series)" { + let input = Bytes::[0x83, 0xe7, 0x4c, 0xcb, 0x8e, 0x35, 0x1c, 0xc5, 0xa4, 0x4e] + let cases = [ + (0, 0xc5_1c_35_8e_cb_4c_e7_83_L, 0x83_e7_4c_cb_8e_35_1c_c5_L), + (1, 0xa4_c5_1c_35_8e_cb_4c_e7_L, 0xe7_4c_cb_8e_35_1c_c5_a4_L), + (2, 0x4e_a4_c5_1c_35_8e_cb_4c_L, 0x4c_cb_8e_35_1c_c5_a4_4e_L), + ] + 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(Int64::from_byte_view(view, @representation.Endianness::LittleEndian), le_expected)? + @assertion.assert_eq(Int64::from_byte_view_le(view), le_expected)? + @assertion.assert_eq(Int64::from_byte_view(view, @representation.Endianness::BigEndian), be_expected)? + @assertion.assert_eq(Int64::from_byte_view(view, @representation.Endianness::NetworkEndian), be_expected)? + @assertion.assert_eq(Int64::from_byte_view_be(view), be_expected)? + } +} diff --git a/int64/moon.pkg.json b/int64/moon.pkg.json index 3f548e9c..89b71050 100644 --- a/int64/moon.pkg.json +++ b/int64/moon.pkg.json @@ -3,6 +3,13 @@ "moonbitlang/core/builtin", "moonbitlang/core/num", "moonbitlang/core/assertion", - "moonbitlang/core/coverage" + "moonbitlang/core/coverage", + "moonbitlang/core/byte", + "moonbitlang/core/representation" + ], + "test_import": [ + "moonbitlang/core/bytes", + "moonbitlang/core/num", + "moonbitlang/core/assertion" ] } diff --git a/representation/endianness.mbt b/representation/endianness.mbt new file mode 100644 index 00000000..ed8a6382 --- /dev/null +++ b/representation/endianness.mbt @@ -0,0 +1,47 @@ +// Copyright 2024 International Digital Economy Academy +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/// Byte order, aka "endianness". +/// +/// # Note +/// +/// - Little-endian is the de-facto standard practice for current computer architectures, and the standardized byte order of WASM. +/// +/// # Warn +/// +/// Assumes system native endian as little-endian. +/// +/// A variant of `NativeEndian` may be introduced in the future. +pub enum Endianness { + // Little-endian. + LittleEndian + // Big-endian. + BigEndian + // Same as `BigEndian`. The network byte order defined by [IETF RFC 1700](https://datatracker.ietf.org/doc/html/rfc1700). + NetworkEndian + + // non-exhaustive +} derive(Debug, Show, Eq) + +/// Convert byte index from specified endianness to native endianness (or vice-versa). +pub fn convert_index(self: Endianness, src_idx: Int, bytes_count: Int) -> Int { + // TODO: this assumes native endianness is LE, this may not fit to targets other than WASM. + if self == Endianness::LittleEndian { + src_idx + } else if self == Endianness::BigEndian || self == Endianness::NetworkEndian { + bytes_count - 1 - src_idx + } else { + abort("invalid endianness") + } +} diff --git a/representation/moon.pkg.json b/representation/moon.pkg.json new file mode 100644 index 00000000..a158d817 --- /dev/null +++ b/representation/moon.pkg.json @@ -0,0 +1,10 @@ +{ + "import": [ + "moonbitlang/core/builtin" + ], + "test_import": [ + "moonbitlang/core/assertion", + "moonbitlang/core/coverage" + ] + } + \ No newline at end of file