Add internal_int_(pack, unpack).

This commit is contained in:
Jeroen van Rijn
2021-09-06 12:57:48 +02:00
parent 7db0c50e63
commit de5d897b5c
4 changed files with 141 additions and 19 deletions
+6 -1
View File
@@ -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
View File
@@ -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() {
+7 -7
View File
@@ -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);
+111
View File
@@ -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.