add more Num.bytesTo* functions

This commit is contained in:
Brendan Hansknecht 2023-03-12 01:52:56 -08:00
parent 785da377c8
commit e6964536b2
No known key found for this signature in database
GPG Key ID: 0EA784685083E75B
12 changed files with 308 additions and 60 deletions

View File

@ -67,6 +67,8 @@ const NUMBERS = INTEGERS ++ FLOATS;
comptime {
exportNumFn(num.bytesToU16C, "bytes_to_u16");
exportNumFn(num.bytesToU32C, "bytes_to_u32");
exportNumFn(num.bytesToU64C, "bytes_to_u64");
exportNumFn(num.bytesToU128C, "bytes_to_u128");
inline for (INTEGERS) |T, i| {
num.exportPow(T, ROC_BUILTINS ++ "." ++ NUM ++ ".pow_int.");

View File

@ -236,6 +236,24 @@ fn bytesToU32(arg: RocList, position: usize) u32 {
return @bitCast(u32, [_]u8{ bytes[position], bytes[position + 1], bytes[position + 2], bytes[position + 3] });
}
pub fn bytesToU64C(arg: RocList, position: usize) callconv(.C) u64 {
return @call(.{ .modifier = always_inline }, bytesToU64, .{ arg, position });
}
fn bytesToU64(arg: RocList, position: usize) u64 {
const bytes = @ptrCast([*]const u8, arg.bytes);
return @bitCast(u64, [_]u8{ bytes[position], bytes[position + 1], bytes[position + 2], bytes[position + 3], bytes[position + 4], bytes[position + 5], bytes[position + 6], bytes[position + 7] });
}
pub fn bytesToU128C(arg: RocList, position: usize) callconv(.C) u128 {
return @call(.{ .modifier = always_inline }, bytesToU128, .{ arg, position });
}
fn bytesToU128(arg: RocList, position: usize) u128 {
const bytes = @ptrCast([*]const u8, arg.bytes);
return @bitCast(u128, [_]u8{ bytes[position], bytes[position + 1], bytes[position + 2], bytes[position + 3], bytes[position + 4], bytes[position + 5], bytes[position + 6], bytes[position + 7], bytes[position + 8], bytes[position + 9], bytes[position + 10], bytes[position + 11], bytes[position + 12], bytes[position + 13], bytes[position + 14], bytes[position + 15] });
}
fn addWithOverflow(comptime T: type, self: T, other: T) WithOverflow(T) {
switch (@typeInfo(T)) {
.Int => {

View File

@ -89,6 +89,8 @@ interface Num
intCast,
bytesToU16,
bytesToU32,
bytesToU64,
bytesToU128,
divCeil,
divCeilChecked,
divTrunc,
@ -508,6 +510,8 @@ intCast : Int a -> Int b
bytesToU16Lowlevel : List U8, Nat -> U16
bytesToU32Lowlevel : List U8, Nat -> U32
bytesToU64Lowlevel : List U8, Nat -> U64
bytesToU128Lowlevel : List U8, Nat -> U128
bytesToU16 : List U8, Nat -> Result U16 [OutOfBounds]
bytesToU16 = \bytes, index ->
@ -529,6 +533,26 @@ bytesToU32 = \bytes, index ->
else
Err OutOfBounds
bytesToU64 : List U8, Nat -> Result U64 [OutOfBounds]
bytesToU64 = \bytes, index ->
# we need at least 7 more bytes
offset = 7
if index + offset < List.len bytes then
Ok (bytesToU64Lowlevel bytes index)
else
Err OutOfBounds
bytesToU128 : List U8, Nat -> Result U128 [OutOfBounds]
bytesToU128 = \bytes, index ->
# we need at least 15 more bytes
offset = 15
if index + offset < List.len bytes then
Ok (bytesToU128Lowlevel bytes index)
else
Err OutOfBounds
compare : Num a, Num a -> [LT, EQ, GT]
## Returns `Bool.true` if the first number is less than the second.

View File

@ -297,6 +297,8 @@ pub const NUM_COUNT_ONE_BITS: IntrinsicName = int_intrinsic!("roc_builtins.num.c
pub const NUM_BYTES_TO_U16: &str = "roc_builtins.num.bytes_to_u16";
pub const NUM_BYTES_TO_U32: &str = "roc_builtins.num.bytes_to_u32";
pub const NUM_BYTES_TO_U64: &str = "roc_builtins.num.bytes_to_u64";
pub const NUM_BYTES_TO_U128: &str = "roc_builtins.num.bytes_to_u128";
pub const STR_INIT: &str = "roc_builtins.str.init";
pub const STR_COUNT_SEGMENTS: &str = "roc_builtins.str.count_segments";

View File

@ -187,6 +187,8 @@ map_symbol_to_lowlevel_and_arity! {
NumAsin; NUM_ASIN; 1,
NumBytesToU16; NUM_BYTES_TO_U16_LOWLEVEL; 2,
NumBytesToU32; NUM_BYTES_TO_U32_LOWLEVEL; 2,
NumBytesToU64; NUM_BYTES_TO_U64_LOWLEVEL; 2,
NumBytesToU128; NUM_BYTES_TO_U128_LOWLEVEL; 2,
NumBitwiseAnd; NUM_BITWISE_AND; 2,
NumBitwiseXor; NUM_BITWISE_XOR; 2,
NumBitwiseOr; NUM_BITWISE_OR; 2,

View File

@ -929,6 +929,28 @@ pub(crate) fn run_low_level<'a, 'ctx, 'env>(
bitcode::NUM_BYTES_TO_U32,
)
}
NumBytesToU64 => {
arguments!(list, position);
call_list_bitcode_fn(
env,
&[list.into_struct_value()],
&[position],
BitcodeReturns::Basic,
bitcode::NUM_BYTES_TO_U64,
)
}
NumBytesToU128 => {
arguments!(list, position);
call_list_bitcode_fn(
env,
&[list.into_struct_value()],
&[position],
BitcodeReturns::Basic,
bitcode::NUM_BYTES_TO_U128,
)
}
NumCompare => {
arguments_with_layouts!((lhs_arg, lhs_layout), (rhs_arg, rhs_layout));

View File

@ -1611,6 +1611,8 @@ impl<'a> LowLevelCall<'a> {
},
NumBytesToU16 => self.load_args_and_call_zig(backend, bitcode::NUM_BYTES_TO_U16),
NumBytesToU32 => self.load_args_and_call_zig(backend, bitcode::NUM_BYTES_TO_U32),
NumBytesToU64 => self.load_args_and_call_zig(backend, bitcode::NUM_BYTES_TO_U64),
NumBytesToU128 => self.load_args_and_call_zig(backend, bitcode::NUM_BYTES_TO_U128),
NumBitwiseAnd => {
self.load_args(backend);
match CodeGenNumType::from(self.ret_layout) {

View File

@ -90,6 +90,8 @@ pub enum LowLevel {
NumAsin,
NumBytesToU16,
NumBytesToU32,
NumBytesToU64,
NumBytesToU128,
NumBitwiseAnd,
NumBitwiseXor,
NumBitwiseOr,
@ -308,6 +310,8 @@ map_symbol_to_lowlevel! {
NumAsin <= NUM_ASIN,
NumBytesToU16 <= NUM_BYTES_TO_U16_LOWLEVEL,
NumBytesToU32 <= NUM_BYTES_TO_U32_LOWLEVEL,
NumBytesToU64 <= NUM_BYTES_TO_U64_LOWLEVEL,
NumBytesToU128 <= NUM_BYTES_TO_U128_LOWLEVEL,
NumBitwiseAnd <= NUM_BITWISE_AND,
NumBitwiseXor <= NUM_BITWISE_XOR,
NumBitwiseOr <= NUM_BITWISE_OR,

View File

@ -1189,66 +1189,70 @@ define_builtins! {
88 NUM_DEC: "Dec" exposed_type=true // the Num.Dectype alias
89 NUM_BYTES_TO_U16: "bytesToU16"
90 NUM_BYTES_TO_U32: "bytesToU32"
91 NUM_CAST_TO_NAT: "#castToNat"
92 NUM_DIV_CEIL: "divCeil"
93 NUM_DIV_CEIL_CHECKED: "divCeilChecked"
94 NUM_TO_STR: "toStr"
95 NUM_MIN_I8: "minI8"
96 NUM_MAX_I8: "maxI8"
97 NUM_MIN_U8: "minU8"
98 NUM_MAX_U8: "maxU8"
99 NUM_MIN_I16: "minI16"
100 NUM_MAX_I16: "maxI16"
101 NUM_MIN_U16: "minU16"
102 NUM_MAX_U16: "maxU16"
103 NUM_MIN_I32: "minI32"
104 NUM_MAX_I32: "maxI32"
105 NUM_MIN_U32: "minU32"
106 NUM_MAX_U32: "maxU32"
107 NUM_MIN_I64: "minI64"
108 NUM_MAX_I64: "maxI64"
109 NUM_MIN_U64: "minU64"
110 NUM_MAX_U64: "maxU64"
111 NUM_MIN_I128: "minI128"
112 NUM_MAX_I128: "maxI128"
113 NUM_MIN_U128: "minU128"
114 NUM_MAX_U128: "maxU128"
115 NUM_TO_I8: "toI8"
116 NUM_TO_I8_CHECKED: "toI8Checked"
117 NUM_TO_I16: "toI16"
118 NUM_TO_I16_CHECKED: "toI16Checked"
119 NUM_TO_I32: "toI32"
120 NUM_TO_I32_CHECKED: "toI32Checked"
121 NUM_TO_I64: "toI64"
122 NUM_TO_I64_CHECKED: "toI64Checked"
123 NUM_TO_I128: "toI128"
124 NUM_TO_I128_CHECKED: "toI128Checked"
125 NUM_TO_U8: "toU8"
126 NUM_TO_U8_CHECKED: "toU8Checked"
127 NUM_TO_U16: "toU16"
128 NUM_TO_U16_CHECKED: "toU16Checked"
129 NUM_TO_U32: "toU32"
130 NUM_TO_U32_CHECKED: "toU32Checked"
131 NUM_TO_U64: "toU64"
132 NUM_TO_U64_CHECKED: "toU64Checked"
133 NUM_TO_U128: "toU128"
134 NUM_TO_U128_CHECKED: "toU128Checked"
135 NUM_TO_NAT: "toNat"
136 NUM_TO_NAT_CHECKED: "toNatChecked"
137 NUM_TO_F32: "toF32"
138 NUM_TO_F32_CHECKED: "toF32Checked"
139 NUM_TO_F64: "toF64"
140 NUM_TO_F64_CHECKED: "toF64Checked"
141 NUM_MAX_F64: "maxF64"
142 NUM_MIN_F64: "minF64"
143 NUM_ADD_CHECKED_LOWLEVEL: "addCheckedLowlevel"
144 NUM_SUB_CHECKED_LOWLEVEL: "subCheckedLowlevel"
145 NUM_MUL_CHECKED_LOWLEVEL: "mulCheckedLowlevel"
146 NUM_BYTES_TO_U16_LOWLEVEL: "bytesToU16Lowlevel"
147 NUM_BYTES_TO_U32_LOWLEVEL: "bytesToU32Lowlevel"
148 NUM_COUNT_LEADING_ZERO_BITS: "countLeadingZeroBits"
149 NUM_COUNT_TRAILING_ZERO_BITS: "countTrailingZeroBits"
150 NUM_COUNT_ONE_BITS: "countOneBits"
91 NUM_BYTES_TO_U64: "bytesToU64"
92 NUM_BYTES_TO_U128: "bytesToU128"
93 NUM_CAST_TO_NAT: "#castToNat"
94 NUM_DIV_CEIL: "divCeil"
95 NUM_DIV_CEIL_CHECKED: "divCeilChecked"
96 NUM_TO_STR: "toStr"
97 NUM_MIN_I8: "minI8"
98 NUM_MAX_I8: "maxI8"
99 NUM_MIN_U8: "minU8"
100 NUM_MAX_U8: "maxU8"
101 NUM_MIN_I16: "minI16"
102 NUM_MAX_I16: "maxI16"
103 NUM_MIN_U16: "minU16"
104 NUM_MAX_U16: "maxU16"
105 NUM_MIN_I32: "minI32"
106 NUM_MAX_I32: "maxI32"
107 NUM_MIN_U32: "minU32"
108 NUM_MAX_U32: "maxU32"
109 NUM_MIN_I64: "minI64"
110 NUM_MAX_I64: "maxI64"
111 NUM_MIN_U64: "minU64"
112 NUM_MAX_U64: "maxU64"
113 NUM_MIN_I128: "minI128"
114 NUM_MAX_I128: "maxI128"
115 NUM_MIN_U128: "minU128"
116 NUM_MAX_U128: "maxU128"
117 NUM_TO_I8: "toI8"
118 NUM_TO_I8_CHECKED: "toI8Checked"
119 NUM_TO_I16: "toI16"
120 NUM_TO_I16_CHECKED: "toI16Checked"
121 NUM_TO_I32: "toI32"
122 NUM_TO_I32_CHECKED: "toI32Checked"
123 NUM_TO_I64: "toI64"
124 NUM_TO_I64_CHECKED: "toI64Checked"
125 NUM_TO_I128: "toI128"
126 NUM_TO_I128_CHECKED: "toI128Checked"
127 NUM_TO_U8: "toU8"
128 NUM_TO_U8_CHECKED: "toU8Checked"
129 NUM_TO_U16: "toU16"
130 NUM_TO_U16_CHECKED: "toU16Checked"
131 NUM_TO_U32: "toU32"
132 NUM_TO_U32_CHECKED: "toU32Checked"
133 NUM_TO_U64: "toU64"
134 NUM_TO_U64_CHECKED: "toU64Checked"
135 NUM_TO_U128: "toU128"
136 NUM_TO_U128_CHECKED: "toU128Checked"
137 NUM_TO_NAT: "toNat"
138 NUM_TO_NAT_CHECKED: "toNatChecked"
139 NUM_TO_F32: "toF32"
140 NUM_TO_F32_CHECKED: "toF32Checked"
141 NUM_TO_F64: "toF64"
142 NUM_TO_F64_CHECKED: "toF64Checked"
143 NUM_MAX_F64: "maxF64"
144 NUM_MIN_F64: "minF64"
145 NUM_ADD_CHECKED_LOWLEVEL: "addCheckedLowlevel"
146 NUM_SUB_CHECKED_LOWLEVEL: "subCheckedLowlevel"
147 NUM_MUL_CHECKED_LOWLEVEL: "mulCheckedLowlevel"
148 NUM_BYTES_TO_U16_LOWLEVEL: "bytesToU16Lowlevel"
149 NUM_BYTES_TO_U32_LOWLEVEL: "bytesToU32Lowlevel"
150 NUM_BYTES_TO_U64_LOWLEVEL: "bytesToU64Lowlevel"
151 NUM_BYTES_TO_U128_LOWLEVEL: "bytesToU128Lowlevel"
152 NUM_COUNT_LEADING_ZERO_BITS: "countLeadingZeroBits"
153 NUM_COUNT_TRAILING_ZERO_BITS: "countTrailingZeroBits"
154 NUM_COUNT_ONE_BITS: "countOneBits"
}
4 BOOL: "Bool" => {
0 BOOL_BOOL: "Bool" exposed_type=true // the Bool.Bool type alias

View File

@ -1051,6 +1051,8 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] {
| NumCountOneBits => arena.alloc_slice_copy(&[irrelevant]),
NumBytesToU16 => arena.alloc_slice_copy(&[borrowed, irrelevant]),
NumBytesToU32 => arena.alloc_slice_copy(&[borrowed, irrelevant]),
NumBytesToU64 => arena.alloc_slice_copy(&[borrowed, irrelevant]),
NumBytesToU128 => arena.alloc_slice_copy(&[borrowed, irrelevant]),
StrStartsWith | StrEndsWith => arena.alloc_slice_copy(&[borrowed, borrowed]),
StrStartsWithScalar => arena.alloc_slice_copy(&[borrowed, irrelevant]),
StrFromUtf8Range => arena.alloc_slice_copy(&[owned, irrelevant, irrelevant]),

View File

@ -120,6 +120,8 @@ enum FirstOrder {
NumShiftRightBy,
NumBytesToU16,
NumBytesToU32,
NumBytesToU64,
NumBytesToU128,
NumShiftRightZfBy,
NumIntCast,
NumFloatCast,

View File

@ -2806,6 +2806,74 @@ fn bytes_to_u32_subtly_out_of_bounds() {
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn bytes_to_u64_clearly_out_of_bounds() {
assert_evals_to!(
indoc!(
r#"
bytes = Str.toUtf8 "hello"
when Num.bytesToU64 bytes 234 is
Ok v -> v
Err OutOfBounds -> 1
"#
),
1,
u64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn bytes_to_u64_subtly_out_of_bounds() {
assert_evals_to!(
indoc!(
r#"
bytes = Str.toUtf8 "hello world"
when Num.bytesToU64 bytes 4 is
Ok v -> v
Err OutOfBounds -> 1
"#
),
1,
u64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn bytes_to_u128_clearly_out_of_bounds() {
assert_evals_to!(
indoc!(
r#"
bytes = Str.toUtf8 "hello"
when Num.bytesToU128 bytes 234 is
Ok v -> v
Err OutOfBounds -> 1
"#
),
1,
u128
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn bytes_to_u128_subtly_out_of_bounds() {
assert_evals_to!(
indoc!(
r#"
bytes = Str.toUtf8 "hello world!!!!!!"
when Num.bytesToU128 bytes 2 is
Ok v -> v
Err OutOfBounds -> 1
"#
),
1,
u128
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn bytes_to_u16_max_u8s() {
@ -2902,6 +2970,102 @@ fn bytes_to_u32_random_u8s() {
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn bytes_to_u64_min_u8s() {
assert_evals_to!(
indoc!(
r#"
when Num.bytesToU64 [0, 0, 0, 0, 0, 0, 0, 0] 0 is
Ok v -> v
Err OutOfBounds -> 1
"#
),
0,
u64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn bytes_to_u64_max_u8s() {
assert_evals_to!(
indoc!(
r#"
when Num.bytesToU64 [255, 255, 255, 255, 255, 255, 255, 255] 0 is
Ok v -> v
Err OutOfBounds -> 1
"#
),
18_446_744_073_709_551_615,
u64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn bytes_to_u64_random_u8s() {
assert_evals_to!(
indoc!(
r#"
when Num.bytesToU64 [252, 124, 128, 121, 1, 32, 177, 211] 0 is
Ok v -> v
Err OutOfBounds -> 1
"#
),
15_254_008_603_586_100_476,
u64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn bytes_to_u128_min_u8s() {
assert_evals_to!(
indoc!(
r#"
when Num.bytesToU128 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] 0 is
Ok v -> v
Err OutOfBounds -> 1
"#
),
0,
u128
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn bytes_to_u128_max_u8s() {
assert_evals_to!(
indoc!(
r#"
when Num.bytesToU128 [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255] 0 is
Ok v -> v
Err OutOfBounds -> 1
"#
),
340_282_366_920_938_463_463_374_607_431_768_211_455,
u128
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn bytes_to_u128_random_u8s() {
assert_evals_to!(
indoc!(
r#"
when Num.bytesToU128 [252, 124, 128, 121, 1, 32, 177, 211, 3, 57, 203, 122, 95, 164, 23, 145] 0 is
Ok v -> v
Err OutOfBounds -> 1
"#
),
192_860_816_096_412_392_720_639_456_393_488_792_828,
u128
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn when_on_i32() {