Browse Source

bigint: `itoa` now writes backwards directly, no need to reverse after.

Jeroen van Rijn 4 years ago
parent
commit
d7ae611f76
2 changed files with 40 additions and 27 deletions
  1. 8 2
      core/math/bigint/example.odin
  2. 32 25
      core/math/bigint/radix.odin

+ 8 - 2
core/math/bigint/example.odin

@@ -58,12 +58,18 @@ demo :: proc() {
 	fmt.printf("b: %s, err: %v\n\n", bs, err);
 	fmt.printf("b: %s, err: %v\n\n", bs, err);
 	delete(bs);
 	delete(bs);
 
 
-	c, err = init();
+	c, err = init(-4);
 	defer destroy(c);
 	defer destroy(c);
 	cs, err = itoa(c);
 	cs, err = itoa(c);
-	fmt.printf("b: %s, err: %v\n\n", cs, err);
+	fmt.printf("c: %s, err: %v\n\n", cs, err);
 	delete(cs);
 	delete(cs);
 
 
+	cstr: cstring;
+	defer delete(cstr);
+
+	cstr, err = itoa_cstring(a);
+	fmt.printf("cstring: %v, err: %v\n\n", cstr, err);
+
 	fmt.println("=== Add ===");
 	fmt.println("=== Add ===");
 	err = sub(c, a, b);
 	err = sub(c, a, b);
 
 

+ 32 - 25
core/math/bigint/radix.odin

@@ -103,13 +103,19 @@ itoa_cstring :: proc(a: ^Int, radix := i8(-1), allocator := context.allocator) -
 
 
 	Use `radix_size` or `radix_size_estimate` to determine a buffer size big enough.
 	Use `radix_size` or `radix_size_estimate` to determine a buffer size big enough.
 
 
-	`written` includes the sign if negative, and the zero terminator if asked for.
-	If you want a 2s complement negative number, adjust it before calling.
-
 	You can pass the output of `radix_size` to `size` if you've previously called it to size
 	You can pass the output of `radix_size` to `size` if you've previously called it to size
 	the output buffer. If you haven't, this routine will call it. This way it knows if the buffer
 	the output buffer. If you haven't, this routine will call it. This way it knows if the buffer
-	is the appropriate size, and it can avoid writing backwards and then reversing.
+	is the appropriate size, and we can write directly in place without a reverse step at the end.
+
+					=== === === IMPORTANT === === ===
 
 
+	If you determined the buffer size using `radix_size_estimate`, or have a buffer
+	that you reuse that you know is large enough, don't pass this size unless you know what you are doing,
+	because we will always write backwards starting at last byte of the buffer.
+
+	Keep in mind that if you set `size` yourself and it's smaller than the buffer,
+	it'll result in buffer overflows, as we use it to avoid reversing at the end
+	and having to perform a buffer overflow check each character.
 */
 */
 itoa_raw :: proc(a: ^Int, radix: i8, buffer: []u8, size := int(-1), zero_terminate := false) -> (written: int, err: Error) {
 itoa_raw :: proc(a: ^Int, radix: i8, buffer: []u8, size := int(-1), zero_terminate := false) -> (written: int, err: Error) {
 	assert_initialized(a); size := size;
 	assert_initialized(a); size := size;
@@ -145,17 +151,20 @@ itoa_raw :: proc(a: ^Int, radix: i8, buffer: []u8, size := int(-1), zero_termina
 			return 0, .Buffer_Overflow;
 			return 0, .Buffer_Overflow;
 		}
 		}
 
 
-		if is_neg(a) {
-			buffer[written] = '-';
-			written += 1;
+		if zero_terminate {
+			available -= 1;
+			buffer[available] = 0;
+			written   += 1;
 		}
 		}
 
 
-		buffer[written] = RADIX_TABLE[a.digit[0]];
-		written += 1;
+		available -= 1;
+		buffer[available] = RADIX_TABLE[a.digit[0]];
+		written   += 1;
 
 
-		if zero_terminate {
-			buffer[written] = 0;
-			written += 1;
+		if is_neg(a) {
+			available -= 1;
+			buffer[available] = '-';
+			written   += 1;
 		}
 		}
 
 
 		return written, .OK;
 		return written, .OK;
@@ -165,6 +174,15 @@ itoa_raw :: proc(a: ^Int, radix: i8, buffer: []u8, size := int(-1), zero_termina
 		Fast path for when `Int` fits within a `_WORD`.
 		Fast path for when `Int` fits within a `_WORD`.
 	*/
 	*/
 	if a.used == 1 || a.used == 2 {
 	if a.used == 1 || a.used == 2 {
+		if zero_terminate {
+			if available == 0 {
+				return written, .Buffer_Overflow;
+			}
+			available -= 1;
+			buffer[available] = 0;
+			written   += 1;
+		}
+
 		val := _WORD(a.digit[1]) << _DIGIT_BITS + _WORD(a.digit[0]);
 		val := _WORD(a.digit[1]) << _DIGIT_BITS + _WORD(a.digit[0]);
 		for val > 0 {
 		for val > 0 {
 			if available == 0 {
 			if available == 0 {
@@ -172,8 +190,8 @@ itoa_raw :: proc(a: ^Int, radix: i8, buffer: []u8, size := int(-1), zero_termina
 			}
 			}
 
 
 			q := val / _WORD(radix);
 			q := val / _WORD(radix);
-			buffer[written] = RADIX_TABLE[val - (q * _WORD(radix))];
 			available -= 1;
 			available -= 1;
+			buffer[available] = RADIX_TABLE[val - (q * _WORD(radix))];
 			written   += 1;
 			written   += 1;
 
 
 			val = q;
 			val = q;
@@ -182,19 +200,8 @@ itoa_raw :: proc(a: ^Int, radix: i8, buffer: []u8, size := int(-1), zero_termina
 			if available == 0 {
 			if available == 0 {
 				return written, .Buffer_Overflow;
 				return written, .Buffer_Overflow;
 			}
 			}
-			buffer[written] = '-';
 			available -= 1;
 			available -= 1;
-			written   += 1;
-		}
-		/*
-			Reverse output.
-		*/
-		slice.reverse(buffer[:written]);
-		if zero_terminate {
-			if available == 0 {
-				return written, .Buffer_Overflow;
-			}
-			buffer[written] = 0;
+			buffer[available] = '-';
 			written   += 1;
 			written   += 1;
 		}
 		}
 		return written, .OK;
 		return written, .OK;