mirror of
https://github.com/odin-lang/Odin.git
synced 2026-06-08 03:24:19 +00:00
bigint: Fast paths for radix code.
This commit is contained in:
@@ -46,37 +46,35 @@ demo :: proc() {
|
||||
as, bs, cs: string;
|
||||
err: Error;
|
||||
|
||||
a, err = init(512);
|
||||
a, err = init(4);
|
||||
defer destroy(a);
|
||||
as, err = itoa(a, 10);
|
||||
as, err = itoa(a);
|
||||
fmt.printf("a: %v, err: %v\n\n", as, err);
|
||||
delete(as);
|
||||
|
||||
b, err = init(42);
|
||||
b, err = init(4);
|
||||
defer destroy(b);
|
||||
bs, err = itoa(b, 10);
|
||||
fmt.printf("b: %v, err: %v\n\n", bs, err);
|
||||
bs, err = itoa(b);
|
||||
fmt.printf("b: %s, err: %v\n\n", bs, err);
|
||||
delete(bs);
|
||||
|
||||
c, err = init();
|
||||
defer destroy(c);
|
||||
cs, err = itoa(c, 10);
|
||||
fmt.printf("c: %v\n", cs);
|
||||
cs, err = itoa(c);
|
||||
fmt.printf("b: %s, err: %v\n\n", cs, err);
|
||||
delete(cs);
|
||||
|
||||
fmt.println("=== Add ===");
|
||||
err = sub(c, a, b);
|
||||
|
||||
fmt.printf("Error: %v\n", err);
|
||||
as, err = itoa(a, 10);
|
||||
bs, err = itoa(b, 10);
|
||||
cs, err = itoa(c, 10);
|
||||
as, err = itoa(a);
|
||||
bs, err = itoa(b);
|
||||
cs, err = itoa(c);
|
||||
fmt.printf("a: %v, bits: %v\n", as, count_bits(a));
|
||||
fmt.printf("b: %v, bits: %v\n", bs, count_bits(b));
|
||||
fmt.printf("c: %v, bits: %v\n", cs, count_bits(c));
|
||||
delete(as); delete(bs); delete(cs);
|
||||
|
||||
fmt.println("radix_size:", radix_size(a, 10));
|
||||
}
|
||||
|
||||
main :: proc() {
|
||||
|
||||
@@ -11,7 +11,7 @@ package bigint
|
||||
|
||||
import "core:fmt"
|
||||
|
||||
log_n_int :: proc(a: ^Int, base: int) -> (log: int, err: Error) {
|
||||
log_n_int :: proc(a: ^Int, base: DIGIT) -> (log: int, err: Error) {
|
||||
assert_initialized(a);
|
||||
if is_neg(a) || is_zero(a) || base < 2 || DIGIT(base) > _DIGIT_MAX {
|
||||
return -1, .Invalid_Input;
|
||||
@@ -20,7 +20,7 @@ log_n_int :: proc(a: ^Int, base: int) -> (log: int, err: Error) {
|
||||
/*
|
||||
Fast path for bases that are a power of two.
|
||||
*/
|
||||
if is_power_of_two(base) {
|
||||
if is_power_of_two(int(base)) {
|
||||
return _log_power_of_two(a, base), .OK;
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ log_n :: proc{log_n_int, log_n_digit};
|
||||
Returns the log2 of an `Int`, provided `base` is a power of two.
|
||||
Don't call it if it isn't.
|
||||
*/
|
||||
_log_power_of_two :: proc(a: ^Int, base: int) -> (log: int) {
|
||||
_log_power_of_two :: proc(a: ^Int, base: DIGIT) -> (log: int) {
|
||||
base := base;
|
||||
y: int;
|
||||
for y = 0; base & 1 == 0; {
|
||||
|
||||
@@ -15,23 +15,60 @@ import "core:mem"
|
||||
import "core:intrinsics"
|
||||
import "core:fmt"
|
||||
import "core:strings"
|
||||
import "core:slice"
|
||||
|
||||
itoa :: proc(a: ^Int, radix: int, allocator := context.allocator) -> (res: string, err: Error) {
|
||||
/*
|
||||
This version of `itoa` allocates one behalf of the caller. The caller must free the string.
|
||||
*/
|
||||
itoa_string :: proc(a: ^Int, radix := i8(-1), zero_terminate := false, allocator := context.allocator) -> (res: string, err: Error) {
|
||||
assert_initialized(a);
|
||||
/*
|
||||
Radix defaults to 10.
|
||||
*/
|
||||
radix := radix if radix > 0 else 10;
|
||||
|
||||
if radix < 2 || radix > 64 {
|
||||
return strings.clone("", allocator), .Invalid_Input;
|
||||
/*
|
||||
TODO: If we want to write a prefix for some of the radixes, we can oversize the buffer.
|
||||
Then after the digits are written and the string is reversed
|
||||
*/
|
||||
|
||||
/*
|
||||
Calculate the size of the buffer we need.
|
||||
*/
|
||||
size: int;
|
||||
size, err = radix_size(a, radix);
|
||||
if zero_terminate {
|
||||
size += 1;
|
||||
}
|
||||
|
||||
/*
|
||||
Fast path for radixes that are a power of two.
|
||||
Exit if calculating the size returned an error.
|
||||
*/
|
||||
if is_power_of_two(radix) {
|
||||
|
||||
if err != .OK {
|
||||
if zero_terminate {
|
||||
return string(cstring("")), err;
|
||||
}
|
||||
return "", err;
|
||||
}
|
||||
|
||||
/*
|
||||
Allocate the buffer we need.
|
||||
*/
|
||||
buffer := make([]u8, size);
|
||||
|
||||
/*
|
||||
Write the digits out into the buffer.
|
||||
*/
|
||||
written: int;
|
||||
written, err = itoa_raw(a, radix, buffer, zero_terminate);
|
||||
|
||||
/*
|
||||
For now, delete the buffer and fall back to the below on failure.
|
||||
*/
|
||||
if err == .OK {
|
||||
return string(buffer[:written]), .OK;
|
||||
}
|
||||
delete(buffer);
|
||||
|
||||
fallback :: proc(a: ^Int, print_raw := false) -> string {
|
||||
if print_raw {
|
||||
@@ -48,12 +85,91 @@ itoa :: proc(a: ^Int, radix: int, allocator := context.allocator) -> (res: strin
|
||||
return strings.clone(fallback(a), allocator), .Unimplemented;
|
||||
}
|
||||
|
||||
int_to_string :: itoa;
|
||||
/*
|
||||
This version of `itoa` allocates one behalf of the caller. The caller must free the string.
|
||||
*/
|
||||
itoa_cstring :: proc(a: ^Int, radix := i8(-1), allocator := context.allocator) -> (res: cstring, err: Error) {
|
||||
assert_initialized(a);
|
||||
/*
|
||||
Radix defaults to 10.
|
||||
*/
|
||||
radix := radix if radix > 0 else 10;
|
||||
|
||||
s: string;
|
||||
s, err = itoa_string(a, radix, true, allocator);
|
||||
return cstring(raw_data(s)), err;
|
||||
}
|
||||
|
||||
/*
|
||||
A low-level `itoa` using a caller-provided buffer. `itoa_string` and `itoa_cstring` use this.
|
||||
You can use also use it if you want to pre-allocate a buffer and optionally reuse it.
|
||||
|
||||
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.
|
||||
*/
|
||||
itoa_raw :: proc(a: ^Int, radix: i8, buffer: []u8, zero_terminate := false) -> (written: int, err: Error) {
|
||||
assert_initialized(a);
|
||||
/*
|
||||
Radix defaults to 10.
|
||||
*/
|
||||
radix := radix if radix > 0 else 10;
|
||||
|
||||
/*
|
||||
Early exit if we were given an empty buffer.
|
||||
*/
|
||||
available := len(buffer);
|
||||
if available == 0 {
|
||||
return 0, .Buffer_Overflow;
|
||||
}
|
||||
/*
|
||||
Early exit if `Int` == 0 or the entire `Int` fits in a single radix digit.
|
||||
*/
|
||||
if is_zero(a) || (a.used == 1 && a.digit[0] < DIGIT(radix)) {
|
||||
needed := 2 if is_neg(a) else 1;
|
||||
needed += 1 if zero_terminate else 0;
|
||||
if available < needed {
|
||||
return 0, .Buffer_Overflow;
|
||||
}
|
||||
|
||||
if is_neg(a) {
|
||||
buffer[written] = '-';
|
||||
written += 1;
|
||||
}
|
||||
|
||||
buffer[written] = RADIX_TABLE[a.digit[0]];
|
||||
written += 1;
|
||||
|
||||
if zero_terminate {
|
||||
buffer[written] = 0;
|
||||
written += 1;
|
||||
}
|
||||
|
||||
return written, .OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Fast path for radixes that are a power of two.
|
||||
*/
|
||||
if is_power_of_two(int(radix)) {
|
||||
|
||||
|
||||
|
||||
return len(buffer), .OK;
|
||||
}
|
||||
|
||||
return -1, .Unimplemented;
|
||||
}
|
||||
|
||||
itoa :: proc{itoa_string, itoa_raw};
|
||||
int_to_string :: itoa;
|
||||
int_to_cstring :: itoa_cstring;
|
||||
|
||||
/*
|
||||
We size for `string`, not `cstring`.
|
||||
*/
|
||||
radix_size :: proc(a: ^Int, radix: int) -> (size: int, err: Error) {
|
||||
radix_size :: proc(a: ^Int, radix: i8) -> (size: int, err: Error) {
|
||||
t := a;
|
||||
|
||||
if radix < 2 || radix > 64 {
|
||||
@@ -67,7 +183,7 @@ radix_size :: proc(a: ^Int, radix: int) -> (size: int, err: Error) {
|
||||
t.sign = .Zero_or_Positive;
|
||||
log: int;
|
||||
|
||||
log, err = log_n(t, radix);
|
||||
log, err = log_n(t, DIGIT(radix));
|
||||
if err != .OK {
|
||||
return log, err;
|
||||
}
|
||||
@@ -80,4 +196,19 @@ radix_size :: proc(a: ^Int, radix: int) -> (size: int, err: Error) {
|
||||
} else {
|
||||
return log + 1, .OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Characters used in radix conversions.
|
||||
*/
|
||||
RADIX_TABLE := "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/";
|
||||
RADIX_TABLE_REVERSE := [80]u8{
|
||||
0x3e, 0xff, 0xff, 0xff, 0x3f, 0x00, 0x01, 0x02, 0x03, 0x04, /* +,-./01234 */
|
||||
0x05, 0x06, 0x07, 0x08, 0x09, 0xff, 0xff, 0xff, 0xff, 0xff, /* 56789:;<=> */
|
||||
0xff, 0xff, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, /* ?@ABCDEFGH */
|
||||
0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, /* IJKLMNOPQR */
|
||||
0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0xff, 0xff, /* STUVWXYZ[\ */
|
||||
0xff, 0xff, 0xff, 0xff, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, /* ]^_`abcdef */
|
||||
0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, /* ghijklmnop */
|
||||
0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, /* qrstuvwxyz */
|
||||
};
|
||||
Reference in New Issue
Block a user