@@ -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/+ +
18251972An utility type to wrap a local buffer to accumulate unsigned numbers.
18261973+/
0 commit comments