From 274e7d5974cc80eb0519f2a3bb096ea0344a8741 Mon Sep 17 00:00:00 2001 From: Quinton Miller Date: Thu, 5 Aug 2021 05:26:01 +0800 Subject: [PATCH] Fix `BigInt#to_s` emitting null bytes for certain values --- spec/std/big/big_decimal_spec.cr | 1 + spec/std/big/big_int_spec.cr | 26 +++++++++++++++++---- src/big/big_int.cr | 39 ++++++++++++++++++++++++++++++-- 3 files changed, 59 insertions(+), 7 deletions(-) diff --git a/spec/std/big/big_decimal_spec.cr b/spec/std/big/big_decimal_spec.cr index 4b211ef80940..05957fa6ece8 100644 --- a/spec/std/big/big_decimal_spec.cr +++ b/spec/std/big/big_decimal_spec.cr @@ -318,6 +318,7 @@ describe BigDecimal do BigDecimal.new(0).to_s.should eq "0" BigDecimal.new(1).to_s.should eq "1" BigDecimal.new(-1).to_s.should eq "-1" + BigDecimal.new("8.5").to_s.should eq "8.5" BigDecimal.new("-0.35").to_s.should eq "-0.35" BigDecimal.new("-.35").to_s.should eq "-0.35" BigDecimal.new("0.01").to_s.should eq "0.01" diff --git a/spec/std/big/big_int_spec.cr b/spec/std/big/big_int_spec.cr index cb72cdc12576..92a483c69962 100644 --- a/spec/std/big/big_int_spec.cr +++ b/spec/std/big/big_int_spec.cr @@ -392,11 +392,27 @@ describe "BigInt" do it_converts_to_s 2.to_big_i, "00002", precision: 5 it_converts_to_s 2.to_big_i, "#{"0" * 199}2", precision: 200 - it_converts_to_s -1.to_big_i, "-1", precision: 0 - it_converts_to_s -1.to_big_i, "-1", precision: 1 - it_converts_to_s -1.to_big_i, "-01", precision: 2 - it_converts_to_s -1.to_big_i, "-00001", precision: 5 - it_converts_to_s -1.to_big_i, "-#{"0" * 199}1", precision: 200 + it_converts_to_s (-1).to_big_i, "-1", precision: 0 + it_converts_to_s (-1).to_big_i, "-1", precision: 1 + it_converts_to_s (-1).to_big_i, "-01", precision: 2 + it_converts_to_s (-1).to_big_i, "-00001", precision: 5 + it_converts_to_s (-1).to_big_i, "-#{"0" * 199}1", precision: 200 + + it_converts_to_s 85.to_big_i, "85", precision: 0 + it_converts_to_s 85.to_big_i, "85", precision: 1 + it_converts_to_s 85.to_big_i, "85", precision: 2 + it_converts_to_s 85.to_big_i, "085", precision: 3 + it_converts_to_s 85.to_big_i, "0085", precision: 4 + it_converts_to_s 85.to_big_i, "00085", precision: 5 + it_converts_to_s 85.to_big_i, "#{"0" * 198}85", precision: 200 + + it_converts_to_s (-85).to_big_i, "-85", precision: 0 + it_converts_to_s (-85).to_big_i, "-85", precision: 1 + it_converts_to_s (-85).to_big_i, "-85", precision: 2 + it_converts_to_s (-85).to_big_i, "-085", precision: 3 + it_converts_to_s (-85).to_big_i, "-0085", precision: 4 + it_converts_to_s (-85).to_big_i, "-00085", precision: 5 + it_converts_to_s (-85).to_big_i, "-#{"0" * 198}85", precision: 200 it_converts_to_s 123.to_big_i, "123", precision: 0 it_converts_to_s 123.to_big_i, "123", precision: 1 diff --git a/src/big/big_int.cr b/src/big/big_int.cr index 11d76df492e2..a93663f9def0 100644 --- a/src/big/big_int.cr +++ b/src/big/big_int.cr @@ -429,7 +429,25 @@ struct BigInt < Int if precision <= count len = count + (negative ? 1 : 0) String.new(len + 1) do |buffer| # null terminator required by GMP + buffer[len - 1] = 0 LibGMP.get_str(buffer, upcase ? -base : base, self) + + # `sizeinbase` may be 1 greater than the exact value + if buffer[len - 1] == 0 + if precision == count + # In this case the exact `count` is `precision - 1`, i.e. one zero + # should be inserted at the beginning of the number + # e.g. precision = 3, count = 3, exact count = 2 + # "85\0\0" -> "085\0" for positive + # "-85\0\0" -> "-085\0" for negative + start = buffer + (negative ? 1 : 0) + start.move_to(start + 1, count - 1) + start.value = '0'.ord.to_u8 + else + len -= 1 + end + end + base62_swapcase(Slice.new(buffer, len)) if base == 62 {len, len} end @@ -439,14 +457,28 @@ struct BigInt < Int # e.g. precision = 13, count = 8 # "_____12345678\0" for positive # "_____-12345678\0" for negative - LibGMP.get_str(buffer + precision - count, upcase ? -base : base, self) + buffer[len - 1] = 0 + start = buffer + precision - count + LibGMP.get_str(start, upcase ? -base : base, self) + + # `sizeinbase` may be 1 greater than the exact value + if buffer[len - 1] == 0 + # e.g. precision = 7, count = 3, exact count = 2 + # "____85\0\0" -> "____885\0" for positive + # "____-85\0\0" -> "____-885\0" for negative + # `start` will be zero-filled later + count -= 1 + start += 1 if negative + start.move_to(start + 1, count) + end + base62_swapcase(Slice.new(buffer + len - count, count)) if base == 62 if negative buffer.value = '-'.ord.to_u8 buffer += 1 end - Intrinsics.memset(buffer, '0'.ord.to_u8, precision - count, false) + Slice.new(buffer, precision - count).fill('0'.ord.to_u8) {len, len} end @@ -471,6 +503,9 @@ struct BigInt < Int ptr = LibGMP.get_str(nil, upcase ? -base : base, self) negative = self < 0 + # `sizeinbase` may be 1 greater than the exact value + count -= 1 if ptr[count + (negative ? 0 : -1)] == 0 + if precision <= count buffer = Slice.new(ptr, count + (negative ? 1 : 0)) else