Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix BigInt#to_s emitting null bytes for certain values #11063

Merged
merged 1 commit into from
Aug 11, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions spec/std/big/big_decimal_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
26 changes: 21 additions & 5 deletions spec/std/big/big_int_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
39 changes: 37 additions & 2 deletions src/big/big_int.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down