Skip to content

Commit 3005344

Browse files
authored
Fix dump/load bigdecimal with few or large precs (#362)
Dumped string should include Prec instead of MaxPrec. Don't trust MaxPrec of the dumped string.
1 parent a35090b commit 3005344

2 files changed

Lines changed: 28 additions & 10 deletions

File tree

ext/bigdecimal/bigdecimal.c

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -844,7 +844,7 @@ BigDecimal_dump(int argc, VALUE *argv, VALUE self)
844844
GUARD_OBJ(v, GetBDValueMust(self));
845845
dump = rb_str_new(0, VpNumOfChars(v.real, "E")+50);
846846
psz = RSTRING_PTR(dump);
847-
snprintf(psz, RSTRING_LEN(dump), "%"PRIuSIZE":", VpMaxPrec(v.real)*VpBaseFig());
847+
snprintf(psz, RSTRING_LEN(dump), "%"PRIuSIZE":", VpPrec(v.real)*VpBaseFig());
848848
len = strlen(psz);
849849
VpToString(v.real, psz+len, RSTRING_LEN(dump)-len, 0, 0);
850850
rb_str_resize(dump, strlen(psz));
@@ -861,22 +861,15 @@ BigDecimal_load(VALUE self, VALUE str)
861861
BDVALUE v;
862862
unsigned char *pch;
863863
unsigned char ch;
864-
unsigned long m=0;
865864

866865
pch = (unsigned char *)StringValueCStr(str);
867-
/* First get max prec */
866+
/* First skip max prec. Don't trust the value. */
868867
while((*pch) != (unsigned char)'\0' && (ch = *pch++) != (unsigned char)':') {
869868
if(!ISDIGIT(ch)) {
870869
rb_raise(rb_eTypeError, "load failed: invalid character in the marshaled string");
871870
}
872-
m = m*10 + (unsigned long)(ch-'0');
873-
}
874-
if (m > VpBaseFig()) m -= VpBaseFig();
875-
GUARD_OBJ(v, bdvalue_nonnullable(CreateFromString(m, (char *)pch, self, true, true)));
876-
m /= VpBaseFig();
877-
if (m && v.real->MaxPrec > m) {
878-
v.real->MaxPrec = m+1;
879871
}
872+
GUARD_OBJ(v, bdvalue_nonnullable(CreateFromString(0, (char *)pch, self, true, true)));
880873
return CheckGetValue(v);
881874
}
882875

test/bigdecimal/test_bigdecimal.rb

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -760,6 +760,31 @@ def test_marshal
760760
assert_raise(TypeError) { Marshal.load(s) }
761761
end
762762

763+
def test_dump_extra_high_maxprec
764+
m = BigDecimal(2 ** 1000)
765+
n = BigDecimal(2) ** 1000
766+
# Even if two bigdecimals have different MaxPrec,
767+
# _dump should return same string if they represent the same value.
768+
assert_equal(m._dump, n._dump)
769+
end
770+
771+
def test_load_invalid_precision
772+
$VERBOSE, verbose = nil, $VERBOSE
773+
dumped = BigDecimal('1' * 1000)._dump
774+
n = BigDecimal._load(dumped)
775+
digits_part = dumped.split(':').last
776+
too_few_precs = BigDecimal._load('100:' + digits_part)
777+
assert_equal(1000, too_few_precs.precision)
778+
assert_equal(n, too_few_precs)
779+
assert_equal(n.precs, too_few_precs.precs)
780+
too_large_precs = BigDecimal._load('999999999999:' + digits_part)
781+
assert_equal(1000, too_large_precs.precision)
782+
assert_equal(n, too_large_precs)
783+
assert_equal(n.precs, too_large_precs.precs)
784+
ensure
785+
$VERBOSE = verbose
786+
end
787+
763788
def test_finite_infinite_nan
764789
BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, false)
765790
BigDecimal.mode(BigDecimal::EXCEPTION_ZERODIVIDE, false)

0 commit comments

Comments
 (0)