Skip to content

Commit 0f8e514

Browse files
authored
Add in support for hex literals using underscores (#354)
* Add in support for hex literals using underscores * check for invalid underscores * add more test cases, unify into one test
1 parent 8e227a7 commit 0f8e514

File tree

3 files changed

+173
-26
lines changed

3 files changed

+173
-26
lines changed

source/mir/bignum/fixed.d

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -197,10 +197,10 @@ struct UInt(size_t size)
197197
}
198198

199199
///
200-
static UInt!size fromHexString(scope const(char)[] str)
200+
static UInt!size fromHexString(bool allowUnderscores = false)(scope const(char)[] str)
201201
{
202202
typeof(return) ret;
203-
if (ret.fromHexStringImpl(str))
203+
if (ret.fromHexStringImpl!(char, allowUnderscores)(str))
204204
return ret;
205205
version(D_Exceptions)
206206
{
@@ -216,11 +216,11 @@ struct UInt(size_t size)
216216

217217
/++
218218
+/
219-
bool fromHexStringImpl(C)(scope const(C)[] str)
219+
bool fromHexStringImpl(C, bool allowUnderscores = false)(scope const(C)[] str)
220220
@safe pure @nogc nothrow
221221
if (isSomeChar!C)
222222
{
223-
return view.fromHexStringImpl(str);
223+
return view.fromHexStringImpl!(C, allowUnderscores)(str);
224224
}
225225

226226
/++

source/mir/bignum/integer.d

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -366,11 +366,11 @@ struct BigInt(size_t maxSize64)
366366

367367
/++
368368
+/
369-
static BigInt fromHexString()(scope const(char)[] str)
369+
static BigInt fromHexString(bool allowUnderscores = false)(scope const(char)[] str)
370370
@trusted pure
371371
{
372372
BigInt ret;
373-
if (ret.fromHexStringImpl(str))
373+
if (ret.fromHexStringImpl!(char, allowUnderscores)(str))
374374
return ret;
375375
version(D_Exceptions)
376376
throw hexStringException;
@@ -380,12 +380,12 @@ struct BigInt(size_t maxSize64)
380380

381381
/++
382382
+/
383-
bool fromHexStringImpl(C)(scope const(C)[] str)
383+
bool fromHexStringImpl(C, bool allowUnderscores = false)(scope const(C)[] str)
384384
@safe pure @nogc nothrow
385385
if (isSomeChar!C)
386386
{
387387
auto work = BigIntView!size_t(data);
388-
auto ret = work.fromHexStringImpl(str);
388+
auto ret = work.fromHexStringImpl!(C, allowUnderscores)(str);
389389
if (ret)
390390
{
391391
length = cast(uint)work.unsigned.coefficients.length;

source/mir/bignum/low_level_view.d

Lines changed: 165 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -565,14 +565,15 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
565565

566566
/++
567567
+/
568-
static BigUIntView fromHexString(C)(scope const(C)[] str)
568+
static BigUIntView fromHexString(C, bool allowUnderscores = false)(scope const(C)[] str)
569569
@trusted pure
570570
if (isSomeChar!C)
571571
{
572572
auto length = str.length / (W.sizeof * 2) + (str.length % (W.sizeof * 2) != 0);
573573
auto data = new Unqual!W[length];
574-
if (BigUIntView!(Unqual!W, endian)(data).fromHexStringImpl(str))
575-
return BigUIntView(cast(W[])data);
574+
auto view = BigUIntView!(Unqual!W, endian)(data);
575+
if (view.fromHexStringImpl!(C, allowUnderscores)(str))
576+
return BigUIntView(cast(W[])view.coefficients);
576577
version(D_Exceptions)
577578
throw hexStringException;
578579
else
@@ -582,18 +583,26 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
582583
static if (isMutable!W)
583584
/++
584585
+/
585-
bool fromHexStringImpl(C)(scope const(C)[] str)
586+
bool fromHexStringImpl(C, bool allowUnderscores = false)(scope const(C)[] str)
586587
@safe pure @nogc nothrow scope
587588
if (isSomeChar!C)
588589
{
589590
pragma(inline, false);
590591
import mir.utility: _expect;
591-
if (_expect(str.length == 0 || str.length > coefficients.length * W.sizeof * 2, false))
592-
return false;
593-
coefficients = coefficients[0 .. str.length / (W.sizeof * 2) + (str.length % (W.sizeof * 2) != 0)];
594-
auto rdata = leastSignificantFirst;
592+
static if (allowUnderscores) {
593+
if (_expect(str.length == 0, false)) // can't tell how big the coeff array needs to be, rely on a runtime check
594+
return false;
595+
} else {
596+
if (_expect(str.length == 0 || str.length > coefficients.length * W.sizeof * 2, false))
597+
return false;
598+
}
599+
600+
leastSignificant = 0;
601+
auto work = topLeastSignificantPart(1);
595602
W current;
596-
size_t i;
603+
size_t i, j;
604+
static if (allowUnderscores) bool recentUnderscore;
605+
597606
do
598607
{
599608
ubyte c;
@@ -621,28 +630,98 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
621630
case 'e': c = 0xE; break;
622631
case 'F':
623632
case 'f': c = 0xF; break;
633+
static if (allowUnderscores)
634+
{
635+
case '_':
636+
if (recentUnderscore) return false;
637+
recentUnderscore = true;
638+
continue;
639+
}
624640
default: return false;
625641
}
642+
++j;
643+
static if (allowUnderscores) recentUnderscore = false;
644+
// how far do we need to shift to get to the top 4 bits
626645
enum s = W.sizeof * 8 - 4;
646+
// shift number to the top most 4 bits
627647
W cc = cast(W)(W(c) << s);
648+
// shift unsigned right 4 bits
628649
current >>>= 4;
650+
// add number to top most 4 bits of current var
629651
current |= cc;
630-
if (i % (W.sizeof * 2) == 0)
652+
if (j % (W.sizeof * 2) == 0) // is this packed var full?
631653
{
632-
rdata.front = current;
633-
rdata.popFront;
654+
work.mostSignificant = current;
634655
current = 0;
656+
if (_expect(work.coefficients.length < coefficients.length, true))
657+
{
658+
work = topLeastSignificantPart(work.coefficients.length + 1);
659+
}
660+
else if (i < str.length) // if we've run out of coefficients before reaching the end of the string, error
661+
{
662+
return false;
663+
}
635664
}
636665
}
637666
while(i < str.length);
667+
668+
static if (allowUnderscores)
669+
{
670+
// check for a underscore at the beginning or the end
671+
if (recentUnderscore || str[$ - 1] == '_') return false;
672+
}
673+
638674
if (current)
639675
{
640-
current >>>= 4 * (W.sizeof * 2 - i % (W.sizeof * 2));
641-
rdata.front = current;
676+
current >>>= 4 * (W.sizeof * 2 - j % (W.sizeof * 2));
677+
work.mostSignificant = current;
642678
}
679+
680+
coefficients = coefficients[0 .. (j / (W.sizeof * 2) + (j % (W.sizeof * 2) != 0))];
681+
643682
return true;
644683
}
645684

685+
static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
686+
///
687+
version(mir_bignum_test)
688+
@safe pure
689+
unittest
690+
{
691+
auto view = BigUIntView!size_t.fromHexString!(char, true)("abcd_efab_cdef");
692+
assert(cast(ulong)view == 0xabcd_efab_cdef);
693+
}
694+
695+
static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
696+
///
697+
version(mir_bignum_test)
698+
@safe pure
699+
unittest
700+
{
701+
// Check that invalid underscores in hex literals throw an error.
702+
void expectThrow(const(char)[] input) {
703+
bool caught = false;
704+
try {
705+
auto view = BigUIntView!size_t.fromHexString!(char, true)(input);
706+
} catch (Exception e) {
707+
caught = true;
708+
}
709+
710+
assert(caught);
711+
}
712+
713+
expectThrow("abcd_efab_cef_");
714+
expectThrow("abcd__efab__cef");
715+
expectThrow("_abcd_efab_cdef");
716+
expectThrow("_abcd_efab_cdef_");
717+
expectThrow("_abcd_efab_cdef__");
718+
expectThrow("__abcd_efab_cdef");
719+
expectThrow("__abcd_efab_cdef_");
720+
expectThrow("__abcd_efab_cdef__");
721+
expectThrow("__abcd__efab_cdef__");
722+
expectThrow("__abcd__efab__cdef__");
723+
}
724+
646725
static if (isMutable!W && W.sizeof >= 4)
647726
/++
648727
Returns: false in case of overflow or incorrect string.
@@ -1378,13 +1457,13 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
13781457

13791458
/++
13801459
+/
1381-
static BigIntView fromHexString(C)(scope const(C)[] str)
1460+
static BigIntView fromHexString(C, bool allowUnderscores = false)(scope const(C)[] str)
13821461
@trusted pure
13831462
if (isSomeChar!C)
13841463
{
13851464
auto length = str.length / (W.sizeof * 2) + (str.length % (W.sizeof * 2) != 0);
13861465
auto ret = BigIntView!(Unqual!W, endian)(new Unqual!W[length]);
1387-
if (ret.fromHexStringImpl(str))
1466+
if (ret.fromHexStringImpl!(C, allowUnderscores)(str))
13881467
return cast(BigIntView) ret;
13891468
version(D_Exceptions)
13901469
throw hexStringException;
@@ -1395,7 +1474,7 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
13951474
static if (isMutable!W)
13961475
/++
13971476
+/
1398-
bool fromHexStringImpl(C)(scope const(C)[] str)
1477+
bool fromHexStringImpl(C, bool allowUnderscores = false)(scope const(C)[] str)
13991478
@safe pure @nogc nothrow
14001479
if (isSomeChar!C)
14011480
{
@@ -1420,7 +1499,7 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
14201499
str = str[1 .. $];
14211500
}
14221501

1423-
return unsigned.fromHexStringImpl(str);
1502+
return unsigned.fromHexStringImpl!(C, allowUnderscores)(str);
14241503
}
14251504

14261505
///
@@ -1443,6 +1522,15 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
14431522
assert(a == -0xa.fbbfae3cd0bp+124);
14441523
}
14451524

1525+
static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
1526+
///
1527+
version(mir_bignum_test)
1528+
@safe pure
1529+
unittest
1530+
{
1531+
auto a = cast(double) BigIntView!size_t.fromHexString!(char, true)("-afbb_fae3_cd0a_ff27_14a1_de70_22b0_029d");
1532+
assert(a == -0xa.fbbfae3cd0bp+124);
1533+
}
14461534

14471535
///
14481536
T opCast(T, bool nonZero = false)() scope const
@@ -1465,6 +1553,17 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
14651553
assert(cast(int) view == -0x22b0021d);
14661554
}
14671555

1556+
static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
1557+
///
1558+
version(mir_bignum_test)
1559+
@safe pure
1560+
unittest
1561+
{
1562+
auto view = BigIntView!size_t.fromHexString!(char, true)("-afbb_fae3_cd0a_ff27_14a1_de70_22b0_021d");
1563+
assert(cast(long) view == -0x14a1de7022b0021d);
1564+
assert(cast(int) view == -0x22b0021d);
1565+
}
1566+
14681567
static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
14691568
version(mir_bignum_test)
14701569
@safe pure
@@ -1474,6 +1573,16 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
14741573
assert(cast(long) view == -0x14a1de7022b0021d);
14751574
assert(cast(int) view == -0x22b0021d);
14761575
}
1576+
1577+
static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
1578+
version(mir_bignum_test)
1579+
@safe pure
1580+
unittest
1581+
{
1582+
auto view = BigIntView!ushort.fromHexString!(char, true)("-afbb_fae3_cd0a_ff27_14a1_de70_22b0_021d");
1583+
assert(cast(long) view == -0x14a1de7022b0021d);
1584+
assert(cast(int) view == -0x22b0021d);
1585+
}
14771586

14781587
static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
14791588
version(mir_bignum_test)
@@ -1485,6 +1594,16 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
14851594
assert(cast(int) view == -0x22b0021d);
14861595
}
14871596

1597+
static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
1598+
version(mir_bignum_test)
1599+
@safe pure
1600+
unittest
1601+
{
1602+
auto view = BigIntView!ubyte.fromHexString!(char, true)("-afbb_fae3_cd0a_ff27_14a1_de70_22b0_021d");
1603+
assert(cast(long) view == -0x14a1de7022b0021d);
1604+
assert(cast(int) view == -0x22b0021d);
1605+
}
1606+
14881607
/++
14891608
+/
14901609
T opCast(T : Fp!coefficientSize, size_t internalRoundLastBits = 0, bool wordNormalized = false, bool nonZero = false, size_t coefficientSize)() scope const
@@ -1510,6 +1629,20 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
15101629
assert(fp.coefficient == UInt!128.fromHexString("afbbfae3cd0aff2714a1de7022b0029d"));
15111630
}
15121631

1632+
static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
1633+
version(mir_bignum_test)
1634+
@safe pure
1635+
unittest
1636+
{
1637+
import mir.bignum.fixed: UInt;
1638+
import mir.bignum.fp: Fp;
1639+
1640+
auto fp = cast(Fp!128) BigIntView!size_t.fromHexString!(char, true)("-afbb_fae3_cd0a_ff27_14a1_de70_22b0_029d");
1641+
assert(fp.sign);
1642+
assert(fp.exponent == 0);
1643+
assert(fp.coefficient == UInt!128.fromHexString("afbbfae3cd0aff2714a1de7022b0029d"));
1644+
}
1645+
15131646
static if (endian == TargetEndian)
15141647
///
15151648
BigIntView!V opCast(T : BigIntView!V, V)() scope return
@@ -1821,6 +1954,20 @@ unittest
18211954

18221955
}
18231956

1957+
///
1958+
version(mir_bignum_test)
1959+
unittest
1960+
{
1961+
import mir.bignum.fixed: UInt;
1962+
import mir.bignum.low_level_view: BigUIntView;
1963+
auto bigView2 = BigUIntView!size_t.fromHexString("55a325ad18b2a77120d870d987d5237473790532acab45da44bc07c92c92babf0b5e2e2c7771cd472ae5d7acdb159a56fbf74f851a058ae341f69d1eb750d7e3");
1964+
auto bigView = BigUIntView!size_t.fromHexString!(char, true)("55a3_25ad_18b2_a771_20d8_70d9_87d5_2374_7379_0532_acab_45da_44bc_07c9_2c92_babf_0b5e_2e2c_7771_cd47_2ae5_d7ac_db15_9a56_fbf7_4f85_1a05_8ae3_41f6_9d1e_b750_d7e3");
1965+
auto fixed = UInt!256.fromHexString!(true)("55e5_6695_76d3_1726_f4a9_b58a_9015_9de5_923a_dc6c_762e_bd3c_4ba5_18d4_9522_9072");
1966+
auto overflow = bigView *= fixed;
1967+
assert(overflow == UInt!256.fromHexString("1cbbe8c42dc21f936e4ce5b2f52ac404439857f174084012fcd1b71fdec2a398"));
1968+
assert(bigView == BigUIntView!size_t.fromHexString("c73fd2b26f2514c103c324943b6c90a05d2732118d5f0099c36a69a8051bb0573adc825b5c9295896c70280faa4c4d369df8e92f82bfffafe078b52ae695d316"));
1969+
}
1970+
18241971
/++
18251972
An utility type to wrap a local buffer to accumulate unsigned numbers.
18261973
+/

0 commit comments

Comments
 (0)