mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-06 15:48:51 +00:00
Add internal_int_(pack, unpack).
This commit is contained in:
@@ -186,6 +186,10 @@ Error_String :: #partial [Error]string{
|
|||||||
.Division_by_Zero = "Division by zero",
|
.Division_by_Zero = "Division by zero",
|
||||||
.Math_Domain_Error = "Math domain error",
|
.Math_Domain_Error = "Math domain error",
|
||||||
|
|
||||||
|
.Cannot_Open_File = "Cannot_Open_File",
|
||||||
|
.Cannot_Read_File = "Cannot_Read_File",
|
||||||
|
.Cannot_Write_File = "Cannot_Write_File",
|
||||||
|
|
||||||
.Unimplemented = "Unimplemented",
|
.Unimplemented = "Unimplemented",
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -231,7 +235,8 @@ when MATH_BIG_FORCE_64_BIT || (!MATH_BIG_FORCE_32_BIT && size_of(rawptr) == 8) {
|
|||||||
_DIGIT_TYPE_BITS :: 8 * size_of(DIGIT);
|
_DIGIT_TYPE_BITS :: 8 * size_of(DIGIT);
|
||||||
_WORD_TYPE_BITS :: 8 * size_of(_WORD);
|
_WORD_TYPE_BITS :: 8 * size_of(_WORD);
|
||||||
|
|
||||||
_DIGIT_BITS :: _DIGIT_TYPE_BITS - 4;
|
_DIGIT_NAILS :: 4;
|
||||||
|
_DIGIT_BITS :: _DIGIT_TYPE_BITS - _DIGIT_NAILS;
|
||||||
_WORD_BITS :: 2 * _DIGIT_BITS;
|
_WORD_BITS :: 2 * _DIGIT_BITS;
|
||||||
|
|
||||||
_MASK :: (DIGIT(1) << DIGIT(_DIGIT_BITS)) - DIGIT(1);
|
_MASK :: (DIGIT(1) << DIGIT(_DIGIT_BITS)) - DIGIT(1);
|
||||||
|
|||||||
+17
-11
@@ -86,13 +86,13 @@ print :: proc(name: string, a: ^Int, base := i8(10), print_name := true, newline
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
printf :: fmt.printf;
|
// printf :: fmt.printf;
|
||||||
|
|
||||||
demo :: proc() {
|
demo :: proc() {
|
||||||
a, b, c, d, e, f, res := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{};
|
a, b, c, d, e, f, res := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{};
|
||||||
defer destroy(a, b, c, d, e, f, res);
|
defer destroy(a, b, c, d, e, f, res);
|
||||||
|
|
||||||
bits := 64;
|
bits := 111;
|
||||||
trials := -1;
|
trials := -1;
|
||||||
|
|
||||||
flags := Primality_Flags{};
|
flags := Primality_Flags{};
|
||||||
@@ -108,17 +108,23 @@ demo :: proc() {
|
|||||||
fmt.printf("err: %v\n", err);
|
fmt.printf("err: %v\n", err);
|
||||||
fmt.printf("RANDOM_PRIME_ITERATIONS_USED: %v\n", RANDOM_PRIME_ITERATIONS_USED);
|
fmt.printf("RANDOM_PRIME_ITERATIONS_USED: %v\n", RANDOM_PRIME_ITERATIONS_USED);
|
||||||
|
|
||||||
// err = internal_int_write_to_ascii_file(a, "a.txt");
|
nails := 0;
|
||||||
// if err != nil {
|
|
||||||
// fmt.printf("internal_int_write_to_ascii_file returned %v\n", err);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// err = internal_int_read_from_ascii_file(b, "a.txt");
|
count := internal_int_pack_count(a, u8, nails);
|
||||||
// if err != nil {
|
buf := make([]u8, count);
|
||||||
// fmt.printf("internal_int_read_from_ascii_file returned %v\n", err);
|
defer delete(buf);
|
||||||
// }
|
|
||||||
|
|
||||||
// print("b: ", b);
|
written: int;
|
||||||
|
order := Order.LSB_First;
|
||||||
|
|
||||||
|
fmt.printf("\na.digit: %v\n", a.digit[:a.used]);
|
||||||
|
written, err = internal_int_pack(a, buf, nails, order);
|
||||||
|
fmt.printf("\nPacked into buf: %v | err: %v | written: %v\n", buf, err, written);
|
||||||
|
|
||||||
|
err = internal_int_unpack(b, buf, nails, order);
|
||||||
|
print("\nUnpacked into b: ", b);
|
||||||
|
fmt.printf("err: %v\n", err);
|
||||||
|
fmt.printf("b.digit: %v\n", b.digit[:b.used]);
|
||||||
}
|
}
|
||||||
|
|
||||||
main :: proc() {
|
main :: proc() {
|
||||||
|
|||||||
@@ -495,7 +495,7 @@ int_to_bytes_little :: proc(a: ^Int, buf: []u8, signed := false, allocator := co
|
|||||||
if signed {
|
if signed {
|
||||||
buf[l - 1] = 1 if a.sign == .Negative else 0;
|
buf[l - 1] = 1 if a.sign == .Negative else 0;
|
||||||
}
|
}
|
||||||
for offset := 0; offset < size_in_bits; offset += 8 {
|
#no_bounds_check for offset := 0; offset < size_in_bits; offset += 8 {
|
||||||
bits, _ := internal_int_bitfield_extract(a, offset, 8);
|
bits, _ := internal_int_bitfield_extract(a, offset, 8);
|
||||||
buf[i] = u8(bits & 255); i += 1;
|
buf[i] = u8(bits & 255); i += 1;
|
||||||
}
|
}
|
||||||
@@ -519,7 +519,7 @@ int_to_bytes_big :: proc(a: ^Int, buf: []u8, signed := false, allocator := conte
|
|||||||
if signed {
|
if signed {
|
||||||
buf[0] = 1 if a.sign == .Negative else 0;
|
buf[0] = 1 if a.sign == .Negative else 0;
|
||||||
}
|
}
|
||||||
for offset := 0; offset < size_in_bits; offset += 8 {
|
#no_bounds_check for offset := 0; offset < size_in_bits; offset += 8 {
|
||||||
bits, _ := internal_int_bitfield_extract(a, offset, 8);
|
bits, _ := internal_int_bitfield_extract(a, offset, 8);
|
||||||
buf[i] = u8(bits & 255); i -= 1;
|
buf[i] = u8(bits & 255); i -= 1;
|
||||||
}
|
}
|
||||||
@@ -546,7 +546,7 @@ int_to_bytes_little_python :: proc(a: ^Int, buf: []u8, signed := false, allocato
|
|||||||
|
|
||||||
size_in_bits := internal_count_bits(t);
|
size_in_bits := internal_count_bits(t);
|
||||||
i := 0;
|
i := 0;
|
||||||
for offset := 0; offset < size_in_bits; offset += 8 {
|
#no_bounds_check for offset := 0; offset < size_in_bits; offset += 8 {
|
||||||
bits, _ := internal_int_bitfield_extract(t, offset, 8);
|
bits, _ := internal_int_bitfield_extract(t, offset, 8);
|
||||||
buf[i] = 255 - u8(bits & 255); i += 1;
|
buf[i] = 255 - u8(bits & 255); i += 1;
|
||||||
}
|
}
|
||||||
@@ -554,7 +554,7 @@ int_to_bytes_little_python :: proc(a: ^Int, buf: []u8, signed := false, allocato
|
|||||||
} else {
|
} else {
|
||||||
size_in_bits := internal_count_bits(a);
|
size_in_bits := internal_count_bits(a);
|
||||||
i := 0;
|
i := 0;
|
||||||
for offset := 0; offset < size_in_bits; offset += 8 {
|
#no_bounds_check for offset := 0; offset < size_in_bits; offset += 8 {
|
||||||
bits, _ := internal_int_bitfield_extract(a, offset, 8);
|
bits, _ := internal_int_bitfield_extract(a, offset, 8);
|
||||||
buf[i] = u8(bits & 255); i += 1;
|
buf[i] = u8(bits & 255); i += 1;
|
||||||
}
|
}
|
||||||
@@ -583,7 +583,7 @@ int_to_bytes_big_python :: proc(a: ^Int, buf: []u8, signed := false, allocator :
|
|||||||
|
|
||||||
size_in_bits := internal_count_bits(t);
|
size_in_bits := internal_count_bits(t);
|
||||||
i := l - 1;
|
i := l - 1;
|
||||||
for offset := 0; offset < size_in_bits; offset += 8 {
|
#no_bounds_check for offset := 0; offset < size_in_bits; offset += 8 {
|
||||||
bits, _ := internal_int_bitfield_extract(t, offset, 8);
|
bits, _ := internal_int_bitfield_extract(t, offset, 8);
|
||||||
buf[i] = 255 - u8(bits & 255); i -= 1;
|
buf[i] = 255 - u8(bits & 255); i -= 1;
|
||||||
}
|
}
|
||||||
@@ -620,7 +620,7 @@ int_from_bytes_big :: proc(a: ^Int, buf: []u8, signed := false, allocator := con
|
|||||||
buf = buf[1:];
|
buf = buf[1:];
|
||||||
}
|
}
|
||||||
|
|
||||||
for v in buf {
|
#no_bounds_check for v in buf {
|
||||||
internal_shl(a, a, 8) or_return;
|
internal_shl(a, a, 8) or_return;
|
||||||
a.digit[0] |= DIGIT(v);
|
a.digit[0] |= DIGIT(v);
|
||||||
}
|
}
|
||||||
@@ -657,7 +657,7 @@ int_from_bytes_big_python :: proc(a: ^Int, buf: []u8, signed := false, allocator
|
|||||||
buf = buf[1:];
|
buf = buf[1:];
|
||||||
}
|
}
|
||||||
|
|
||||||
for v in buf {
|
#no_bounds_check for v in buf {
|
||||||
internal_shl(a, a, 8) or_return;
|
internal_shl(a, a, 8) or_return;
|
||||||
if signed && sign == .Negative {
|
if signed && sign == .Negative {
|
||||||
a.digit[0] |= DIGIT(255 - v);
|
a.digit[0] |= DIGIT(255 - v);
|
||||||
|
|||||||
@@ -431,6 +431,117 @@ internal_int_write_to_ascii_file :: proc(a: ^Int, filename: string, radix := i8(
|
|||||||
return nil if ok else .Cannot_Write_File;
|
return nil if ok else .Cannot_Write_File;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Calculate the size needed for `internal_int_pack`.
|
||||||
|
|
||||||
|
See https://gmplib.org/manual/Integer-Import-and-Export.html
|
||||||
|
*/
|
||||||
|
internal_int_pack_count :: proc(a: ^Int, $T: typeid, nails := 0) -> (size_needed: int) {
|
||||||
|
assert(nails >= 0 && nails < (size_of(T) * 8));
|
||||||
|
|
||||||
|
bits := internal_count_bits(a);
|
||||||
|
size := size_of(T);
|
||||||
|
|
||||||
|
size_needed = bits / ((size * 8) - nails);
|
||||||
|
size_needed += 1 if (bits % ((size * 8) - nails)) != 0 else 0;
|
||||||
|
|
||||||
|
return size_needed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Based on gmp's mpz_export.
|
||||||
|
See https://gmplib.org/manual/Integer-Import-and-Export.html
|
||||||
|
|
||||||
|
`buf` is a pre-allocated slice of type `T` "words", which must be an unsigned integer of some description.
|
||||||
|
Use `internal_int_pack_count(a, T, nails)` to calculate the necessary size.
|
||||||
|
The library internally uses `DIGIT` as the type, which is u64 or u32 depending on the platform.
|
||||||
|
You are of course welcome to export to []u8, []u32be, and so forth.
|
||||||
|
After this you can use `mem.slice_data_cast` to interpret the buffer as bytes if you so choose.
|
||||||
|
|
||||||
|
`nails` are the number of top bits the output "word" reserves.
|
||||||
|
To mimic the internals of this library, this would be 4.
|
||||||
|
|
||||||
|
To use the minimum amount of output bytes, set `nails` to 0 and pass a `[]u8`.
|
||||||
|
IMPORTANT: `pack` serializes the magnitude of an Int, that is, the output is unsigned.
|
||||||
|
|
||||||
|
Assumes `a` not to be `nil` and to have been initialized.
|
||||||
|
*/
|
||||||
|
internal_int_pack :: proc(a: ^Int, buf: []$T, nails := 0, order := Order.LSB_First) -> (written: int, err: Error)
|
||||||
|
where intrinsics.type_is_integer(T) && intrinsics.type_is_unsigned(T) && size_of(T) <= 16 {
|
||||||
|
|
||||||
|
assert(nails >= 0 && nails < (size_of(T) * 8));
|
||||||
|
|
||||||
|
type_size := size_of(T);
|
||||||
|
type_bits := (type_size * 8) - nails;
|
||||||
|
|
||||||
|
word_count := internal_int_pack_count(a, T, nails);
|
||||||
|
bit_count := internal_count_bits(a);
|
||||||
|
|
||||||
|
if len(buf) < word_count {
|
||||||
|
return 0, .Buffer_Overflow;
|
||||||
|
}
|
||||||
|
|
||||||
|
bit_offset := 0;
|
||||||
|
word_offset := 0;
|
||||||
|
|
||||||
|
#no_bounds_check for i := 0; i < word_count; i += 1 {
|
||||||
|
bit_offset = i * type_bits;
|
||||||
|
if order == .MSB_First {
|
||||||
|
word_offset = word_count - i - 1;
|
||||||
|
} else {
|
||||||
|
word_offset = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
bits_to_get := min(type_bits, bit_count - bit_offset);
|
||||||
|
W := internal_int_bitfield_extract(a, bit_offset, bits_to_get) or_return;
|
||||||
|
buf[word_offset] = T(W);
|
||||||
|
}
|
||||||
|
|
||||||
|
return word_count, nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
internal_int_unpack :: proc(a: ^Int, buf: []$T, nails := 0, order := Order.LSB_First, allocator := context.allocator) -> (err: Error)
|
||||||
|
where intrinsics.type_is_integer(T) && intrinsics.type_is_unsigned(T) && size_of(T) <= 16 {
|
||||||
|
assert(nails >= 0 && nails < (size_of(T) * 8));
|
||||||
|
context.allocator = allocator;
|
||||||
|
|
||||||
|
type_size := size_of(T);
|
||||||
|
type_bits := (type_size * 8) - nails;
|
||||||
|
type_mask := T(1 << uint(type_bits)) - 1;
|
||||||
|
|
||||||
|
if len(buf) == 0 {
|
||||||
|
return .Invalid_Argument;
|
||||||
|
}
|
||||||
|
|
||||||
|
bit_count := type_bits * len(buf);
|
||||||
|
digit_count := (bit_count / _DIGIT_BITS) + min(1, bit_count % _DIGIT_BITS);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Pre-size output Int.
|
||||||
|
*/
|
||||||
|
internal_grow(a, digit_count) or_return;
|
||||||
|
|
||||||
|
t := &Int{};
|
||||||
|
defer internal_destroy(t);
|
||||||
|
|
||||||
|
if order == .LSB_First {
|
||||||
|
for W, i in buf {
|
||||||
|
internal_set(t, W & type_mask) or_return;
|
||||||
|
internal_shl(t, t, type_bits * i) or_return;
|
||||||
|
internal_add(a, a, t) or_return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for W in buf {
|
||||||
|
internal_set(t, W & type_mask) or_return;
|
||||||
|
internal_shl(a, a, type_bits) or_return;
|
||||||
|
internal_add(a, a, t) or_return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return internal_clamp(a);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Overestimate the size needed for the bigint to string conversion by a very small amount.
|
Overestimate the size needed for the bigint to string conversion by a very small amount.
|
||||||
|
|||||||
Reference in New Issue
Block a user