aboutsummaryrefslogtreecommitdiff
path: root/core/math/big/helpers.odin
diff options
context:
space:
mode:
authorJeroen van Rijn <Kelimion@users.noreply.github.com>2021-08-11 15:48:20 +0200
committerJeroen van Rijn <Kelimion@users.noreply.github.com>2021-08-11 20:59:54 +0200
commit12f9b6db63fbbc0fe57ce93145d8c55b8f1f0db8 (patch)
tree59143b21569ba52138fabf9ec3d91cdc2b770fba /core/math/big/helpers.odin
parent851780b8f449847953c49e59d7ce401f2d53c779 (diff)
big: Add `int_to_bytes_{big, little}` + Python compatible variants.
Diffstat (limited to 'core/math/big/helpers.odin')
-rw-r--r--core/math/big/helpers.odin144
1 files changed, 143 insertions, 1 deletions
diff --git a/core/math/big/helpers.odin b/core/math/big/helpers.odin
index 14c88be5f..058836df9 100644
--- a/core/math/big/helpers.odin
+++ b/core/math/big/helpers.odin
@@ -45,7 +45,7 @@ int_set_from_integer :: proc(dest: ^Int, src: $T, minimize := false, allocator :
return #force_inline internal_int_set_from_integer(dest, src, minimize);
}
-set :: proc { int_set_from_integer, int_copy };
+set :: proc { int_set_from_integer, int_copy, int_atoi, };
/*
Copy one `Int` to another.
@@ -464,6 +464,148 @@ clamp :: proc(a: ^Int, allocator := context.allocator) -> (err: Error) {
return nil;
}
+
+/*
+ Size binary representation
+*/
+int_to_bytes_size :: proc(a: ^Int, signed := false, allocator := context.allocator) -> (size_in_bytes: int, err: Error) {
+ assert_if_nil(a);
+ if err = #force_inline internal_clear_if_uninitialized(a, allocator); err != nil { return {}, err; }
+
+ size_in_bits := internal_count_bits(a);
+
+ size_in_bytes = (size_in_bits / 8);
+ size_in_bytes += 0 if size_in_bits % 8 == 0 else 1;
+ size_in_bytes += 1 if signed else 0;
+ return;
+}
+
+/*
+ Size binary representation, Python `._to_bytes(num_bytes, "endianness", signed=Bool)` compatible.
+*/
+int_to_bytes_size_python :: proc(a: ^Int, signed := false, allocator := context.allocator) -> (size_in_bytes: int, err: Error) {
+ assert_if_nil(a);
+ size_in_bytes, err = int_to_bytes_size(a, signed, allocator);
+
+ /*
+ Python uses a complement representation of negative numbers and doesn't add a prefix byte.
+ */
+ if signed {
+ size_in_bytes -= 1;
+ }
+ return;
+}
+
+/*
+ Return Little Endian binary representation of `a`, either signed or unsigned.
+ If `a` is negative and we ask for the default unsigned representation, we return abs(a).
+*/
+int_to_bytes_little :: proc(a: ^Int, buf: []u8, signed := false, allocator := context.allocator) -> (err: Error) {
+ assert_if_nil(a);
+ size_in_bytes: int;
+
+ if size_in_bytes, err = int_to_bytes_size(a, signed, allocator); err != nil { return err; }
+ if size_in_bytes > len(buf) { return .Buffer_Overflow; }
+
+ size_in_bits := internal_count_bits(a);
+ i := 0;
+ if signed {
+ i += 1;
+ buf[0] = 1 if a.sign == .Negative else 0;
+ }
+ for offset := 0; offset < size_in_bits; offset += 8 {
+ bits, _ := internal_int_bitfield_extract(a, offset, 8);
+ buf[i] = u8(bits & 255); i += 1;
+ }
+ return;
+}
+
+/*
+ Return Big Endian binary representation of `a`, either signed or unsigned.
+ If `a` is negative and we ask for the default unsigned representation, we return abs(a).
+*/
+int_to_bytes_big :: proc(a: ^Int, buf: []u8, signed := false, allocator := context.allocator) -> (err: Error) {
+ assert_if_nil(a);
+ size_in_bytes: int;
+
+ if size_in_bytes, err = int_to_bytes_size(a, signed, allocator); err != nil { return err; }
+ l := len(buf);
+ if size_in_bytes > l { return .Buffer_Overflow; }
+
+ size_in_bits := internal_count_bits(a);
+ i := l - 1;
+
+ if signed {
+ buf[0] = 1 if a.sign == .Negative else 0;
+ }
+ for offset := 0; offset < size_in_bits; offset += 8 {
+ bits, _ := internal_int_bitfield_extract(a, offset, 8);
+ buf[i] = u8(bits & 255); i -= 1;
+ }
+ return;
+}
+
+/*
+ Return Python 3.x compatible Little Endian binary representation of `a`, either signed or unsigned.
+ If `a` is negative when asking for an unsigned number, we return an error like Python does.
+*/
+int_to_bytes_little_python :: proc(a: ^Int, buf: []u8, signed := false, allocator := context.allocator) -> (err: Error) {
+ assert_if_nil(a);
+ size_in_bytes: int;
+
+ if a.sign == .Zero_or_Positive {
+ return int_to_bytes_little(a, buf, signed, allocator);
+ }
+ if a.sign == .Negative && !signed { return .Invalid_Argument; }
+
+ l := len(buf);
+ if size_in_bytes, err = int_to_bytes_size_python(a, signed, allocator); err != nil { return err; }
+ if size_in_bytes > l { return .Buffer_Overflow; }
+
+ t := &Int{};
+ defer destroy(t);
+ if err = complement(t, a, allocator); err != nil { return err; }
+
+ size_in_bits := internal_count_bits(t);
+ i := 0;
+ 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;
+ }
+ return;
+}
+
+/*
+ Return Python 3.x compatible Big Endian binary representation of `a`, either signed or unsigned.
+ If `a` is negative when asking for an unsigned number, we return an error like Python does.
+*/
+int_to_bytes_big_python :: proc(a: ^Int, buf: []u8, signed := false, allocator := context.allocator) -> (err: Error) {
+ assert_if_nil(a);
+ size_in_bytes: int;
+
+ if a.sign == .Zero_or_Positive {
+ return int_to_bytes_big(a, buf, signed, allocator);
+ }
+ if a.sign == .Negative && !signed { return .Invalid_Argument; }
+
+ l := len(buf);
+ if size_in_bytes, err = int_to_bytes_size_python(a, signed, allocator); err != nil { return err; }
+ if size_in_bytes > l { return .Buffer_Overflow; }
+
+ t := &Int{};
+ defer destroy(t);
+ if err = complement(t, a, allocator); err != nil { return err; }
+
+ size_in_bits := internal_count_bits(t);
+ i := l - 1;
+
+ for offset := 0; offset < size_in_bits; offset += 8 {
+ bits, _ := internal_int_bitfield_extract(a, offset, 8);
+ buf[i] = 255 - u8(bits & 255); i -= 1;
+ }
+ return;
+}
+
/*
Initialize constants.
*/