diff options
| author | Jeroen van Rijn <Kelimion@users.noreply.github.com> | 2021-09-05 15:50:23 +0200 |
|---|---|---|
| committer | Jeroen van Rijn <Kelimion@users.noreply.github.com> | 2021-09-05 15:50:23 +0200 |
| commit | 3faac14d62c2fb05e7ee9e207f29033897d4ae77 (patch) | |
| tree | cdaf82f5d57e6d6bdaa997f21c37a923d88910ec | |
| parent | b2fa4ec675afb6be7bee53fdd390c54a8511f161 (diff) | |
big: Add ASCII file import/export.
| -rw-r--r-- | core/math/big/common.odin | 4 | ||||
| -rw-r--r-- | core/math/big/example.odin | 14 | ||||
| -rw-r--r-- | core/math/big/radix.odin | 79 |
3 files changed, 84 insertions, 13 deletions
diff --git a/core/math/big/common.odin b/core/math/big/common.odin index fb215cd69..11810d144 100644 --- a/core/math/big/common.odin +++ b/core/math/big/common.odin @@ -166,6 +166,10 @@ Error :: enum int { Division_by_Zero = 8, Math_Domain_Error = 9, + Cannot_Open_File = 50, + Cannot_Read_File = 51, + Cannot_Write_File = 52, + Unimplemented = 127, }; diff --git a/core/math/big/example.odin b/core/math/big/example.odin index 6b697d472..dfc49bc15 100644 --- a/core/math/big/example.odin +++ b/core/math/big/example.odin @@ -86,7 +86,7 @@ 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{}; @@ -107,6 +107,18 @@ demo :: proc() { print("a(10): ", a, 10, true, true, true); 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); + // } + + // 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); + // } + + // print("b: ", b); } main :: proc() { diff --git a/core/math/big/radix.odin b/core/math/big/radix.odin index 76854e244..fb1af250b 100644 --- a/core/math/big/radix.odin +++ b/core/math/big/radix.odin @@ -16,20 +16,18 @@ package math_big import "core:intrinsics" import "core:mem" +import "core:os" /* - This version of `itoa` allocates one behalf of the caller. The caller must free the string. + This version of `itoa` allocates on behalf of the caller. The caller must free the string. + The radix defaults to 10. */ -int_itoa_string :: proc(a: ^Int, radix := i8(-1), zero_terminate := false, allocator := context.allocator) -> (res: string, err: Error) { +int_itoa_string :: proc(a: ^Int, radix := i8(10), zero_terminate := false, allocator := context.allocator) -> (res: string, err: Error) { assert_if_nil(a); context.allocator = allocator; a := a; radix := radix; clear_if_uninitialized(a) or_return; - /* - Radix defaults to 10. - */ - radix = radix if radix > 0 else 10; /* TODO: If we want to write a prefix for some of the radixes, we can oversize the buffer. @@ -57,18 +55,15 @@ int_itoa_string :: proc(a: ^Int, radix := i8(-1), zero_terminate := false, alloc } /* - This version of `itoa` allocates one behalf of the caller. The caller must free the string. + This version of `itoa` allocates on behalf of the caller. The caller must free the string. + The radix defaults to 10. */ -int_itoa_cstring :: proc(a: ^Int, radix := i8(-1), allocator := context.allocator) -> (res: cstring, err: Error) { +int_itoa_cstring :: proc(a: ^Int, radix := i8(10), allocator := context.allocator) -> (res: cstring, err: Error) { assert_if_nil(a); context.allocator = allocator; a := a; radix := radix; clear_if_uninitialized(a) or_return; - /* - Radix defaults to 10. - */ - radix = radix if radix > 0 else 10; s: string; s, err = int_itoa_string(a, radix, true); @@ -378,6 +373,66 @@ radix_size :: proc(a: ^Int, radix: i8, zero_terminate := false, allocator := con } /* + We might add functions to read and write byte-encoded Ints from/to files, using `int_to_bytes_*` functions. + + LibTomMath allows exporting/importing to/from a file in ASCII, but it doesn't support a much more compact representation in binary, even though it has several pack functions int_to_bytes_* (which I expanded upon and wrote Python interoperable versions of as well), and (un)pack, which is GMP compatible. + Someone could implement their own read/write binary int procedures, of course. + + Could be worthwhile to add a canonical binary file representation with an optional small header that says it's an Odin big.Int, big.Rat or Big.Float, byte count for each component that follows, flag for big/little endian and a flag that says a checksum exists at the end of the file. + For big.Rat and big.Float the header couldn't be optional, because we'd have no way to distinguish where the components end. +*/ + +/* + Read an Int from an ASCII file. +*/ +internal_int_read_from_ascii_file :: proc(a: ^Int, filename: string, radix := i8(10), allocator := context.allocator) -> (err: Error) { + context.allocator = allocator; + + /* + We can either read the entire file at once, or read a bunch at a time and keep multiplying by the radix. + For now, we'll read the entire file. Eventually we'll replace this with a copy that duplicates the logic + of `atoi` so we don't need to read the entire file. + */ + + res, ok := os.read_entire_file(filename, allocator); + defer delete(res, allocator); + + if !ok { + return .Cannot_Read_File; + } + + as := string(res); + return atoi(a, as, radix); +} + +/* + Write an Int to an ASCII file. +*/ +internal_int_write_to_ascii_file :: proc(a: ^Int, filename: string, radix := i8(10), allocator := context.allocator) -> (err: Error) { + context.allocator = allocator; + + /* + For now we'll convert the Int using itoa and writing the result in one go. + If we want to preserve memory we could duplicate the itoa logic and write backwards. + */ + + as := itoa(a, radix) or_return; + defer delete(as); + + l := len(as); + assert(l > 0); + + data := transmute([]u8)mem.Raw_Slice{ + data = raw_data(as), + len = l, + }; + + ok := os.write_entire_file(name=filename, data=data, truncate=true); + return nil if ok else .Cannot_Write_File; +} + + +/* Overestimate the size needed for the bigint to string conversion by a very small amount. The error is about 10^-8; it will overestimate the result by at most 11 elements for a number of the size 2^(2^31)-1 which is currently the largest possible in this library. |