From ee24f2dd37540a65fa68b86d90d3bd49a3fdb655 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Wed, 11 Aug 2021 18:48:31 +0200 Subject: [PATCH] big: Improve `int_to_bytes_*`. --- core/math/big/example.odin | 160 ++++++++++++++++++++++++------------- core/math/big/helpers.odin | 106 ++++++++++++++---------- 2 files changed, 170 insertions(+), 96 deletions(-) diff --git a/core/math/big/example.odin b/core/math/big/example.odin index 552ba69f6..e8793ba23 100644 --- a/core/math/big/example.odin +++ b/core/math/big/example.odin @@ -73,67 +73,115 @@ print :: proc(name: string, a: ^Int, base := i8(10), print_name := true, newline } } +int_to_byte :: proc(v: ^Int) { + err: Error; + size: int; + print("v: ", v); + fmt.println(); + + if size, err = int_to_bytes_size(v); err != nil { + fmt.printf("int_to_bytes_size returned: %v\n", err); + return; + } + b1 := make([]u8, size, context.temp_allocator); + err = int_to_bytes_big(v, b1); + fmt.printf("big: %v | err: %v\n", b1, err); + + + if size, err = int_to_bytes_size(v); err != nil { + fmt.printf("int_to_bytes_size returned: %v\n", err); + return; + } + b2 := make([]u8, size, context.temp_allocator); + err = int_to_bytes_big_python(v, b2); + fmt.printf("big python: %v | err: %v\n", b2, err); + + + if size, err = int_to_bytes_size(v, true); err != nil { + fmt.printf("int_to_bytes_size returned: %v\n", err); + return; + } + b3 := make([]u8, size, context.temp_allocator); + err = int_to_bytes_big(v, b3, true); + fmt.printf("big signed: %v | err: %v\n", b3, err); + + t := &Int{}; + int_from_bytes_big(t, b3, true); + defer destroy(t); + print("t: ", t); + + if size, err = int_to_bytes_size(v, true); err != nil { + fmt.printf("int_to_bytes_size returned: %v\n", err); + return; + } + b4 := make([]u8, size, context.temp_allocator); + err = int_to_bytes_big_python(v, b4, true); + fmt.printf("big signed python: %v | err: %v\n", b4, err); +} + +int_to_byte_little :: proc(v: ^Int) { + err: Error; + size: int; + print("v: ", v); + fmt.println(); + + if size, err = int_to_bytes_size(v); err != nil { + fmt.printf("int_to_bytes_size returned: %v\n", err); + return; + } + b1 := make([]u8, size, context.temp_allocator); + err = int_to_bytes_little(v, b1); + fmt.printf("little: %v | err: %v\n", b1, err); + + + if size, err = int_to_bytes_size(v); err != nil { + fmt.printf("int_to_bytes_size returned: %v\n", err); + return; + } + b2 := make([]u8, size, context.temp_allocator); + err = int_to_bytes_little_python(v, b2); + fmt.printf("little python: %v | err: %v\n", b2, err); + + + if size, err = int_to_bytes_size(v, true); err != nil { + fmt.printf("int_to_bytes_size returned: %v\n", err); + return; + } + b3 := make([]u8, size, context.temp_allocator); + err = int_to_bytes_little(v, b3, true); + fmt.printf("little signed: %v | err: %v\n", b3, err); + + // t := &Int{}; + // int_from_bytes_little(t, b3, true); + // defer destroy(t); + // print("t: ", t); + + if size, err = int_to_bytes_size(v, true); err != nil { + fmt.printf("int_to_bytes_size returned: %v\n", err); + return; + } + b4 := make([]u8, size, context.temp_allocator); + err = int_to_bytes_little_python(v, b4, true); + fmt.printf("little signed python: %v | err: %v\n", b4, err); +} + demo :: proc() { a, b, c, d, e, f := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{}; defer destroy(a, b, c, d, e, f); - err: Error; + set(a, 64336); + fmt.println("--- --- --- ---"); + int_to_byte(a); + fmt.println("--- --- --- ---"); + int_to_byte_little(a); + fmt.println("--- --- --- ---"); - foo := "2291194942392555914538479778530519876003906024854260006581638127590953"; - if err = atoi(a, foo, 10); err != nil { return; } - // print("a: ", a, 10, true, true, true); - - byte_length, _ := int_to_bytes_size(a); - - fmt.printf("byte_length(a): %v\n", byte_length); - - buf := make([]u8, byte_length); - defer delete(buf); - - err = int_to_bytes_big(a, buf); - - python_big := []u8{ - 84, 252, 50, 97, 27, 81, 11, 101, 58, 96, 138, 175, 65, 202, 109, - 142, 106, 146, 117, 32, 200, 113, 36, 214, 188, 157, 242, 158, 41, - }; - - if mem.compare(python_big, buf) == 0 { - fmt.printf("int_to_bytes_big: pass\n"); - } else { - fmt.printf("int_to_bytes_big: fail | %v\n", buf); - } - python_little := []u8{ - 41, 158, 242, 157, 188, 214, 36, 113, 200, 32, 117, 146, 106, 142, 109, - 202, 65, 175, 138, 96, 58, 101, 11, 81, 27, 97, 50, 252, 84, - }; - - err = int_to_bytes_little(a, buf); - if mem.compare(python_little, buf) == 0 { - fmt.printf("int_to_bytes_little: pass\n"); - } else { - fmt.printf("int_to_bytes_little: fail | %v\n", buf); - } - - _ = neg(b, a); - - python_little_neg := []u8{ - 215, 97, 13, 98, 67, 41, 219, 142, 55, 223, 138, 109, 149, 113, 146, - 53, 190, 80, 117, 159, 197, 154, 244, 174, 228, 158, 205, 3, 171, - }; - - byte_length, _ = int_to_bytes_size_python(b, true); - - fmt.printf("byte_length(a): %v\n", byte_length); - - buf2 := make([]u8, byte_length); - defer delete(buf2); - - err = int_to_bytes_little_python(b, buf, true); - if mem.compare(python_little_neg, buf) == 0 { - fmt.printf("int_to_bytes_little: pass\n"); - } else { - fmt.printf("int_to_bytes_little: %v | %v\n", err, buf); - } + set(b, -64336); + fmt.println("--- --- --- ---"); + int_to_byte(b); + fmt.println("--- --- --- ---"); + int_to_byte_little(b); + fmt.println("--- --- --- ---"); } main :: proc() { diff --git a/core/math/big/helpers.odin b/core/math/big/helpers.odin index 058836df9..cdd1e3bcb 100644 --- a/core/math/big/helpers.odin +++ b/core/math/big/helpers.odin @@ -480,22 +480,6 @@ int_to_bytes_size :: proc(a: ^Int, signed := false, allocator := context.allocat 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). @@ -505,13 +489,13 @@ int_to_bytes_little :: proc(a: ^Int, buf: []u8, signed := false, allocator := co 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; } + l := len(buf); + if size_in_bytes > l { 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; + buf[l - 1] = 1 if a.sign == .Negative else 0; } for offset := 0; offset < size_in_bits; offset += 8 { bits, _ := internal_int_bitfield_extract(a, offset, 8); @@ -553,24 +537,31 @@ int_to_bytes_little_python :: proc(a: ^Int, buf: []u8, signed := false, allocato 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; } + if !signed && a.sign == .Negative { 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, err = int_to_bytes_size(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; } + if a.sign == .Negative { + t := &Int{}; + defer destroy(t); + if err = internal_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; + 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; + } + buf[l-1] = 255; + } else { + size_in_bits := internal_count_bits(a); + i := 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; } @@ -583,29 +574,64 @@ int_to_bytes_big_python :: proc(a: ^Int, buf: []u8, signed := false, allocator : 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; } + if !signed && a.sign == .Negative { return .Invalid_Argument; } + if a.sign == .Zero_or_Positive { return int_to_bytes_big(a, buf, signed, allocator); } l := len(buf); - if size_in_bytes, err = int_to_bytes_size_python(a, signed, allocator); err != nil { return err; } + if size_in_bytes, err = int_to_bytes_size(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; } + + if err = internal_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); + bits, _ := internal_int_bitfield_extract(t, offset, 8); buf[i] = 255 - u8(bits & 255); i -= 1; } + buf[0] = 255; + return; } +/* + Read `Int` from a Big Endian binary representation. + Sign is detected from the first byte if `signed` is true. +*/ +int_from_bytes_big :: proc(a: ^Int, buf: []u8, signed := false, allocator := context.allocator) -> (err: Error) { + assert_if_nil(a); + buf := buf; + l := len(buf); + if l == 0 { return .Invalid_Argument; } + + sign: Sign; + size_in_bits := l * 8; + if signed { + /* + First byte denotes the sign. + */ + size_in_bits -= 8; + } + size_in_digits := size_in_bits / _DIGIT_BITS; + size_in_digits += 0 if size_in_bits % 8 == 0 else 1; + if err = internal_grow(a, size_in_digits); err != nil { return err; } + + if signed { + sign = .Zero_or_Positive if buf[0] == 0 else .Negative; + buf = buf[1:]; + } + + for v in buf { + if err = internal_shl(a, a, 8); err != nil { return err; } + a.digit[0] |= DIGIT(v); + } + a.sign = sign; + return internal_clamp(a); +} + /* Initialize constants. */