浏览代码

Add `internal_int_(pack, unpack)`.

Jeroen van Rijn 3 年之前
父节点
当前提交
de5d897b5c
共有 4 个文件被更改,包括 141 次插入19 次删除
  1. 6 1
      core/math/big/common.odin
  2. 17 11
      core/math/big/example.odin
  3. 7 7
      core/math/big/helpers.odin
  4. 111 0
      core/math/big/radix.odin

+ 6 - 1
core/math/big/common.odin

@@ -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
core/math/big/example.odin

@@ -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
core/math/big/helpers.odin

@@ -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 - 0
core/math/big/radix.odin

@@ -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.