16
16
17
17
namespace Fortran ::runtime::io {
18
18
19
- static bool EditBOZInput (IoStatementState &io, const DataEdit &edit, void *n,
20
- int base, int totalBitSize) {
19
+ template <int LOG2_BASE>
20
+ static bool EditBOZInput (
21
+ IoStatementState &io, const DataEdit &edit, void *n, std::size_t bytes) {
21
22
std::optional<int > remaining;
22
23
std::optional<char32_t > next{io.PrepareInput (edit, remaining)};
23
- common::UnsignedInt128 value{0 };
24
+ if (*next == ' 0' ) {
25
+ do {
26
+ next = io.NextInField (remaining, edit);
27
+ } while (next && *next == ' 0' );
28
+ }
29
+ // Count significant digits after any leading white space & zeroes
30
+ int digits{0 };
24
31
for (; next; next = io.NextInField (remaining, edit)) {
25
32
char32_t ch{*next};
26
33
if (ch == ' ' || ch == ' \t ' ) {
27
34
continue ;
28
35
}
29
- int digit{0 };
30
36
if (ch >= ' 0' && ch <= ' 1' ) {
31
- digit = ch - ' 0' ;
32
- } else if (base >= 8 && ch >= ' 2' && ch <= ' 7' ) {
33
- digit = ch - ' 0' ;
34
- } else if (base >= 10 && ch >= ' 8' && ch <= ' 9' ) {
35
- digit = ch - ' 0' ;
36
- } else if (base == 16 && ch >= ' A' && ch <= ' Z' ) {
37
- digit = ch + 10 - ' A' ;
38
- } else if (base == 16 && ch >= ' a' && ch <= ' z' ) {
39
- digit = ch + 10 - ' a' ;
37
+ } else if (LOG2_BASE >= 3 && ch >= ' 2' && ch <= ' 7' ) {
38
+ } else if (LOG2_BASE >= 4 && ch >= ' 8' && ch <= ' 9' ) {
39
+ } else if (LOG2_BASE >= 4 && ch >= ' A' && ch <= ' F' ) {
40
+ } else if (LOG2_BASE >= 4 && ch >= ' a' && ch <= ' f' ) {
40
41
} else {
41
42
io.GetIoErrorHandler ().SignalError (
42
43
" Bad character '%lc' in B/O/Z input field" , ch);
43
44
return false ;
44
45
}
45
- value *= base;
46
- value += digit;
46
+ ++digits;
47
+ }
48
+ auto significantBytes{static_cast <std::size_t >(digits * LOG2_BASE + 7 ) / 8 };
49
+ if (significantBytes > bytes) {
50
+ io.GetIoErrorHandler ().SignalError (
51
+ " B/O/Z input of %d digits overflows %zd-byte variable" , digits, bytes);
52
+ return false ;
53
+ }
54
+ // Reset to start of significant digits
55
+ io.HandleRelativePosition (-digits);
56
+ remaining.reset ();
57
+ // Make a second pass now that the digit count is known
58
+ std::memset (n, 0 , bytes);
59
+ int increment{isHostLittleEndian ? -1 : 1 };
60
+ auto *data{reinterpret_cast <unsigned char *>(n) +
61
+ (isHostLittleEndian ? significantBytes - 1 : 0 )};
62
+ int shift{((digits - 1 ) * LOG2_BASE) & 7 };
63
+ if (shift + LOG2_BASE > 8 ) {
64
+ shift -= 8 ; // misaligned octal
65
+ }
66
+ while (digits > 0 ) {
67
+ char32_t ch{*io.NextInField (remaining, edit)};
68
+ int digit{0 };
69
+ if (ch >= ' 0' && ch <= ' 9' ) {
70
+ digit = ch - ' 0' ;
71
+ } else if (ch >= ' A' && ch <= ' F' ) {
72
+ digit = ch + 10 - ' A' ;
73
+ } else if (ch >= ' a' && ch <= ' f' ) {
74
+ digit = ch + 10 - ' a' ;
75
+ } else {
76
+ continue ;
77
+ }
78
+ --digits;
79
+ if (shift < 0 ) {
80
+ shift += 8 ;
81
+ if (shift + LOG2_BASE > 8 ) { // misaligned octal
82
+ *data |= digit >> (8 - shift);
83
+ }
84
+ data += increment;
85
+ }
86
+ *data |= digit << shift;
87
+ shift -= LOG2_BASE;
47
88
}
48
- // TODO: check for overflow
49
- std::memcpy (n, &value, totalBitSize >> 3 );
50
89
return true ;
51
90
}
52
91
@@ -83,11 +122,11 @@ bool EditIntegerInput(
83
122
case ' I' :
84
123
break ;
85
124
case ' B' :
86
- return EditBOZInput (io, edit, n, 2 , kind << 3 );
125
+ return EditBOZInput< 1 > (io, edit, n, kind);
87
126
case ' O' :
88
- return EditBOZInput (io, edit, n, 8 , kind << 3 );
127
+ return EditBOZInput< 3 > (io, edit, n, kind);
89
128
case ' Z' :
90
- return EditBOZInput (io, edit, n, 16 , kind << 3 );
129
+ return EditBOZInput< 4 > (io, edit, n, kind);
91
130
case ' A' : // legacy extension
92
131
return EditCharacterInput (io, edit, reinterpret_cast <char *>(n), kind);
93
132
default :
@@ -457,7 +496,6 @@ bool EditCommonRealInput(IoStatementState &io, const DataEdit &edit, void *n) {
457
496
458
497
template <int KIND>
459
498
bool EditRealInput (IoStatementState &io, const DataEdit &edit, void *n) {
460
- constexpr int binaryPrecision{common::PrecisionOfRealKind (KIND)};
461
499
switch (edit.descriptor ) {
462
500
case DataEdit::ListDirected:
463
501
if (IsNamelistName (io)) {
@@ -472,14 +510,14 @@ bool EditRealInput(IoStatementState &io, const DataEdit &edit, void *n) {
472
510
case ' G' :
473
511
return EditCommonRealInput<KIND>(io, edit, n);
474
512
case ' B' :
475
- return EditBOZInput (
476
- io, edit, n, 2 , common::BitsForBinaryPrecision (binaryPrecision) );
513
+ return EditBOZInput< 1 >(io, edit, n,
514
+ common::BitsForBinaryPrecision (common::PrecisionOfRealKind (KIND)) >> 3 );
477
515
case ' O' :
478
- return EditBOZInput (
479
- io, edit, n, 8 , common::BitsForBinaryPrecision (binaryPrecision) );
516
+ return EditBOZInput< 3 >(io, edit, n,
517
+ common::BitsForBinaryPrecision (common::PrecisionOfRealKind (KIND)) >> 3 );
480
518
case ' Z' :
481
- return EditBOZInput (
482
- io, edit, n, 16 , common::BitsForBinaryPrecision (binaryPrecision) );
519
+ return EditBOZInput< 4 >(io, edit, n,
520
+ common::BitsForBinaryPrecision (common::PrecisionOfRealKind (KIND)) >> 3 );
483
521
case ' A' : // legacy extension
484
522
return EditCharacterInput (io, edit, reinterpret_cast <char *>(n), KIND);
485
523
default :
@@ -590,7 +628,7 @@ static bool EditListDirectedCharacterInput(
590
628
// or the end of the current record. Subtlety: the "remaining" count
591
629
// here is a dummy that's used to avoid the interpretation of separators
592
630
// in NextInField.
593
- std::optional<int > remaining{maxUTF8Bytes};
631
+ std::optional<int > remaining{length > 0 ? maxUTF8Bytes : 0 };
594
632
while (std::optional<char32_t > next{io.NextInField (remaining, edit)}) {
595
633
switch (*next) {
596
634
case ' ' :
@@ -602,8 +640,7 @@ static bool EditListDirectedCharacterInput(
602
640
break ;
603
641
default :
604
642
*x++ = *next;
605
- --length;
606
- remaining = maxUTF8Bytes;
643
+ remaining = --length > 0 ? maxUTF8Bytes : 0 ;
607
644
}
608
645
}
609
646
std::fill_n (x, length, ' ' );
@@ -619,6 +656,12 @@ bool EditCharacterInput(
619
656
case ' A' :
620
657
case ' G' :
621
658
break ;
659
+ case ' B' :
660
+ return EditBOZInput<1 >(io, edit, x, length * sizeof *x);
661
+ case ' O' :
662
+ return EditBOZInput<3 >(io, edit, x, length * sizeof *x);
663
+ case ' Z' :
664
+ return EditBOZInput<4 >(io, edit, x, length * sizeof *x);
622
665
default :
623
666
io.GetIoErrorHandler ().SignalError (IostatErrorInFormat,
624
667
" Data edit descriptor '%c' may not be used with a CHARACTER data item" ,
0 commit comments