mirror of
https://github.com/Ed94/Odin.git
synced 2026-06-18 11:52:22 -07: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",
|
||||
.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",
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
_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;
|
||||
|
||||
_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() {
|
||||
a, b, c, d, e, f, res := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{};
|
||||
defer destroy(a, b, c, d, e, f, res);
|
||||
|
||||
bits := 64;
|
||||
bits := 111;
|
||||
trials := -1;
|
||||
|
||||
flags := Primality_Flags{};
|
||||
@@ -108,17 +108,23 @@ demo :: proc() {
|
||||
fmt.printf("err: %v\n", err);
|
||||
fmt.printf("RANDOM_PRIME_ITERATIONS_USED: %v\n", RANDOM_PRIME_ITERATIONS_USED);
|
||||
|
||||
// err = internal_int_write_to_ascii_file(a, "a.txt");
|
||||
// if err != nil {
|
||||
// fmt.printf("internal_int_write_to_ascii_file returned %v\n", err);
|
||||
// }
|
||||
nails := 0;
|
||||
|
||||
// err = internal_int_read_from_ascii_file(b, "a.txt");
|
||||
// if err != nil {
|
||||
// fmt.printf("internal_int_read_from_ascii_file returned %v\n", err);
|
||||
// }
|
||||
count := internal_int_pack_count(a, u8, nails);
|
||||
buf := make([]u8, count);
|
||||
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() {
|
||||
|
||||
@@ -495,7 +495,7 @@ int_to_bytes_little :: proc(a: ^Int, buf: []u8, signed := false, allocator := co
|
||||
if signed {
|
||||
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);
|
||||
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 {
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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 {
|
||||
size_in_bits := internal_count_bits(a);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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:];
|
||||
}
|
||||
|
||||
for v in buf {
|
||||
#no_bounds_check for v in buf {
|
||||
internal_shl(a, a, 8) or_return;
|
||||
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:];
|
||||
}
|
||||
|
||||
for v in buf {
|
||||
#no_bounds_check for v in buf {
|
||||
internal_shl(a, a, 8) or_return;
|
||||
if signed && sign == .Negative {
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
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.
|
||||
|
||||
Reference in New Issue
Block a user