1 //===-- runtime/edit-input.cpp --------------------------------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "edit-input.h"
12 #include "flang/Common/real.h"
13 #include "flang/Common/uint128.h"
17 namespace Fortran::runtime::io
{
19 // Checks that a list-directed input value has been entirely consumed and
20 // doesn't contain unparsed characters before the next value separator.
21 static inline bool IsCharValueSeparator(const DataEdit
&edit
, char32_t ch
) {
23 edit
.modes
.editingFlags
& decimalComma
? char32_t
{';'} : char32_t
{','}};
24 return ch
== ' ' || ch
== '\t' || ch
== '/' || ch
== comma
;
27 static bool CheckCompleteListDirectedField(
28 IoStatementState
&io
, const DataEdit
&edit
) {
29 if (edit
.IsListDirected()) {
30 std::size_t byteCount
;
31 if (auto ch
{io
.GetCurrentChar(byteCount
)}) {
32 if (IsCharValueSeparator(edit
, *ch
)) {
35 const auto &connection
{io
.GetConnectionState()};
36 io
.GetIoErrorHandler().SignalError(IostatBadListDirectedInputSeparator
,
37 "invalid character (0x%x) after list-directed input value, "
38 "at column %d in record %d",
39 static_cast<unsigned>(*ch
),
40 static_cast<int>(connection
.positionInRecord
+ 1),
41 static_cast<int>(connection
.currentRecordNumber
));
45 return true; // end of record: ok
52 template <int LOG2_BASE
>
53 static bool EditBOZInput(
54 IoStatementState
&io
, const DataEdit
&edit
, void *n
, std::size_t bytes
) {
55 // Skip leading white space & zeroes
56 std::optional
<int> remaining
{io
.CueUpInput(edit
)};
57 auto start
{io
.GetConnectionState().positionInRecord
};
58 std::optional
<char32_t
> next
{io
.NextInField(remaining
, edit
)};
59 if (next
.value_or('?') == '0') {
61 start
= io
.GetConnectionState().positionInRecord
;
62 next
= io
.NextInField(remaining
, edit
);
63 } while (next
&& *next
== '0');
65 // Count significant digits after any leading white space & zeroes
67 for (; next
; next
= io
.NextInField(remaining
, edit
)) {
69 if (ch
== ' ' || ch
== '\t') {
72 if (ch
>= '0' && ch
<= '1') {
73 } else if (LOG2_BASE
>= 3 && ch
>= '2' && ch
<= '7') {
74 } else if (LOG2_BASE
>= 4 && ch
>= '8' && ch
<= '9') {
75 } else if (LOG2_BASE
>= 4 && ch
>= 'A' && ch
<= 'F') {
76 } else if (LOG2_BASE
>= 4 && ch
>= 'a' && ch
<= 'f') {
78 io
.GetIoErrorHandler().SignalError(
79 "Bad character '%lc' in B/O/Z input field", ch
);
84 auto significantBytes
{static_cast<std::size_t>(digits
* LOG2_BASE
+ 7) / 8};
85 if (significantBytes
> bytes
) {
86 io
.GetIoErrorHandler().SignalError(IostatBOZInputOverflow
,
87 "B/O/Z input of %d digits overflows %zd-byte variable", digits
, bytes
);
90 // Reset to start of significant digits
91 io
.HandleAbsolutePosition(start
);
93 // Make a second pass now that the digit count is known
94 std::memset(n
, 0, bytes
);
95 int increment
{isHostLittleEndian
? -1 : 1};
96 auto *data
{reinterpret_cast<unsigned char *>(n
) +
97 (isHostLittleEndian
? significantBytes
- 1 : 0)};
98 int shift
{((digits
- 1) * LOG2_BASE
) & 7};
99 if (shift
+ LOG2_BASE
> 8) {
100 shift
-= 8; // misaligned octal
103 char32_t ch
{*io
.NextInField(remaining
, edit
)};
105 if (ch
>= '0' && ch
<= '9') {
107 } else if (ch
>= 'A' && ch
<= 'F') {
108 digit
= ch
+ 10 - 'A';
109 } else if (ch
>= 'a' && ch
<= 'f') {
110 digit
= ch
+ 10 - 'a';
117 if (shift
+ LOG2_BASE
> 8) { // misaligned octal
118 *data
|= digit
>> (8 - shift
);
122 *data
|= digit
<< shift
;
125 return CheckCompleteListDirectedField(io
, edit
);
128 static inline char32_t
GetRadixPointChar(const DataEdit
&edit
) {
129 return edit
.modes
.editingFlags
& decimalComma
? char32_t
{','} : char32_t
{'.'};
132 // Prepares input from a field, and returns the sign, if any, else '\0'.
133 static char ScanNumericPrefix(IoStatementState
&io
, const DataEdit
&edit
,
134 std::optional
<char32_t
> &next
, std::optional
<int> &remaining
) {
135 remaining
= io
.CueUpInput(edit
);
136 next
= io
.NextInField(remaining
, edit
);
139 if (*next
== '-' || *next
== '+') {
141 if (!edit
.IsListDirected()) {
142 io
.SkipSpaces(remaining
);
144 next
= io
.NextInField(remaining
, edit
);
150 bool EditIntegerInput(
151 IoStatementState
&io
, const DataEdit
&edit
, void *n
, int kind
) {
152 RUNTIME_CHECK(io
.GetIoErrorHandler(), kind
>= 1 && !(kind
& (kind
- 1)));
153 switch (edit
.descriptor
) {
154 case DataEdit::ListDirected
:
155 if (IsNamelistNameOrSlash(io
)) {
163 return EditBOZInput
<1>(io
, edit
, n
, kind
);
165 return EditBOZInput
<3>(io
, edit
, n
, kind
);
167 return EditBOZInput
<4>(io
, edit
, n
, kind
);
168 case 'A': // legacy extension
169 return EditCharacterInput(io
, edit
, reinterpret_cast<char *>(n
), kind
);
171 io
.GetIoErrorHandler().SignalError(IostatErrorInFormat
,
172 "Data edit descriptor '%c' may not be used with an INTEGER data item",
176 std::optional
<int> remaining
;
177 std::optional
<char32_t
> next
;
178 char sign
{ScanNumericPrefix(io
, edit
, next
, remaining
)};
179 common::UnsignedInt128 value
{0};
181 bool overflow
{false};
182 for (; next
; next
= io
.NextInField(remaining
, edit
)) {
184 if (ch
== ' ' || ch
== '\t') {
185 if (edit
.modes
.editingFlags
& blankZero
) {
186 ch
= '0'; // BZ mode - treat blank as if it were zero
192 if (ch
>= '0' && ch
<= '9') {
195 io
.GetIoErrorHandler().SignalError(
196 "Bad character '%lc' in INTEGER input field", ch
);
199 static constexpr auto maxu128
{~common::UnsignedInt128
{0}};
200 static constexpr auto maxu128OverTen
{maxu128
/ 10};
201 static constexpr int maxLastDigit
{
202 static_cast<int>(maxu128
- (maxu128OverTen
* 10))};
203 overflow
|= value
>= maxu128OverTen
&&
204 (value
> maxu128OverTen
|| digit
> maxLastDigit
);
209 if (!any
&& !remaining
) {
210 io
.GetIoErrorHandler().SignalError(
211 "Integer value absent from NAMELIST or list-directed input");
214 auto maxForKind
{common::UnsignedInt128
{1} << ((8 * kind
) - 1)};
215 overflow
|= value
>= maxForKind
&& (value
> maxForKind
|| sign
!= '-');
217 io
.GetIoErrorHandler().SignalError(IostatIntegerInputOverflow
,
218 "Decimal input overflows INTEGER(%d) variable", kind
);
224 if (any
|| !io
.GetConnectionState().IsAtEOF()) {
225 std::memcpy(n
, &value
, kind
); // a blank field means zero
230 // Parses a REAL input number from the input source as a normalized
231 // fraction into a supplied buffer -- there's an optional '-', a
232 // decimal point when the input is not hexadecimal, and at least one
233 // digit. Replaces blanks with zeroes where appropriate.
234 struct ScannedRealInput
{
235 // Number of characters that (should) have been written to the
236 // buffer -- this can be larger than the buffer size, which
237 // indicates buffer overflow. Zero indicates an error.
239 int exponent
{0}; // adjusted as necessary; binary if isHexadecimal
240 bool isHexadecimal
{false}; // 0X...
242 static ScannedRealInput
ScanRealInput(
243 char *buffer
, int bufferSize
, IoStatementState
&io
, const DataEdit
&edit
) {
244 std::optional
<int> remaining
;
245 std::optional
<char32_t
> next
;
247 std::optional
<int> radixPointOffset
;
248 auto Put
{[&](char ch
) -> void {
249 if (got
< bufferSize
) {
254 char sign
{ScanNumericPrefix(io
, edit
, next
, remaining
)};
258 bool bzMode
{(edit
.modes
.editingFlags
& blankZero
) != 0};
260 if (!next
|| (!bzMode
&& *next
== ' ')) {
261 if (!edit
.IsListDirected() && !io
.GetConnectionState().IsAtEOF()) {
262 // An empty/blank field means zero when not list-directed.
263 // A fixed-width field containing only a sign is also zero;
264 // this behavior isn't standard-conforming in F'2023 but it is
265 // required to pass FCVS.
268 return {got
, exponent
, false};
270 char32_t radixPointChar
{GetRadixPointChar(edit
)};
271 char32_t first
{*next
>= 'a' && *next
<= 'z' ? *next
+ 'A' - 'a' : *next
};
272 bool isHexadecimal
{false};
273 if (first
== 'N' || first
== 'I') {
274 // NaN or infinity - convert to upper case
275 // Subtle: a blank field of digits could be followed by 'E' or 'D',
277 ((*next
>= 'a' && *next
<= 'z') || (*next
>= 'A' && *next
<= 'Z'));
278 next
= io
.NextInField(remaining
, edit
)) {
279 if (*next
>= 'a' && *next
<= 'z') {
280 Put(*next
- 'a' + 'A');
285 if (next
&& *next
== '(') { // NaN(...)
289 next
= io
.NextInField(remaining
, edit
);
294 } else if (*next
== '(') {
296 } else if (*next
== ')') {
302 } else if (first
== radixPointChar
|| (first
>= '0' && first
<= '9') ||
303 (bzMode
&& (first
== ' ' || first
== '\t')) || first
== 'E' ||
304 first
== 'D' || first
== 'Q') {
306 next
= io
.NextInField(remaining
, edit
);
307 if (next
&& (*next
== 'x' || *next
== 'X')) { // 0X...
308 isHexadecimal
= true;
309 next
= io
.NextInField(remaining
, edit
);
314 // input field is normalized to a fraction
315 if (!isHexadecimal
) {
319 for (; next
; next
= io
.NextInField(remaining
, edit
)) {
321 if (ch
== ' ' || ch
== '\t') {
325 ch
= '0'; // BZ mode - treat blank as if it were zero
327 continue; // ignore blank in fixed field
330 if (ch
== '0' && got
== start
&& !radixPointOffset
) {
331 // omit leading zeroes before the radix point
332 } else if (ch
>= '0' && ch
<= '9') {
334 } else if (ch
== radixPointChar
&& !radixPointOffset
) {
335 // The radix point character is *not* copied to the buffer.
336 radixPointOffset
= got
- start
; // # of digits before the radix point
337 } else if (isHexadecimal
&& ch
>= 'A' && ch
<= 'F') {
339 } else if (isHexadecimal
&& ch
>= 'a' && ch
<= 'f') {
340 Put(ch
- 'a' + 'A'); // normalize to capitals
346 // Nothing but zeroes and maybe a radix point. F'2018 requires
347 // at least one digit, but F'77 did not, and a bare "." shows up in
349 Put('0'); // emit at least one digit
351 // In list-directed input, a bad exponent is not consumed.
352 auto nextBeforeExponent
{next
};
353 auto startExponent
{io
.GetConnectionState().positionInRecord
};
354 bool hasGoodExponent
{false};
357 if (*next
== 'p' || *next
== 'P') {
358 next
= io
.NextInField(remaining
, edit
);
360 // The binary exponent is not optional in the standard.
363 } else if (*next
== 'e' || *next
== 'E' || *next
== 'd' || *next
== 'D' ||
364 *next
== 'q' || *next
== 'Q') {
365 // Optional exponent letter. Blanks are allowed between the
366 // optional exponent letter and the exponent value.
367 io
.SkipSpaces(remaining
);
368 next
= io
.NextInField(remaining
, edit
);
372 (*next
== '-' || *next
== '+' || (*next
>= '0' && *next
<= '9') ||
373 *next
== ' ' || *next
== '\t')) {
374 bool negExpo
{*next
== '-'};
375 if (negExpo
|| *next
== '+') {
376 next
= io
.NextInField(remaining
, edit
);
378 for (; next
; next
= io
.NextInField(remaining
, edit
)) {
379 if (*next
>= '0' && *next
<= '9') {
380 hasGoodExponent
= true;
381 if (exponent
< 10000) {
382 exponent
= 10 * exponent
+ *next
- '0';
384 } else if (*next
== ' ' || *next
== '\t') {
388 hasGoodExponent
= true;
389 exponent
= 10 * exponent
;
396 exponent
= -exponent
;
399 if (!hasGoodExponent
) {
403 // There isn't a good exponent; do not consume it.
404 next
= nextBeforeExponent
;
405 io
.HandleAbsolutePosition(startExponent
);
406 // The default exponent is -kP, but the scale factor doesn't affect
407 // an explicit exponent.
408 exponent
= -edit
.modes
.scale
;
410 // Adjust exponent by number of digits before the radix point.
412 // Exponents for hexadecimal input are binary.
413 exponent
+= radixPointOffset
.value_or(got
- start
) * 4;
414 } else if (radixPointOffset
) {
415 exponent
+= *radixPointOffset
;
417 // When no redix point (or comma) appears in the value, the 'd'
418 // part of the edit descriptor must be interpreted as the number of
419 // digits in the value to be interpreted as being to the *right* of
420 // the assumed radix point (13.7.2.3.2)
421 exponent
+= got
- start
- edit
.digits
.value_or(0);
424 // Consume the trailing ')' of a list-directed or NAMELIST complex
426 if (edit
.descriptor
== DataEdit::ListDirectedImaginaryPart
) {
427 if (next
&& (*next
== ' ' || *next
== '\t')) {
428 io
.SkipSpaces(remaining
);
429 next
= io
.NextInField(remaining
, edit
);
431 if (!next
) { // NextInField fails on separators like ')'
432 std::size_t byteCount
{0};
433 next
= io
.GetCurrentChar(byteCount
);
434 if (next
&& *next
== ')') {
435 io
.HandleRelativePosition(byteCount
);
438 } else if (remaining
) {
439 while (next
&& (*next
== ' ' || *next
== '\t')) {
440 next
= io
.NextInField(remaining
, edit
);
443 return {}; // error: unused nonblank character in fixed-width field
446 return {got
, exponent
, isHexadecimal
};
449 static void RaiseFPExceptions(decimal::ConversionResultFlags flags
) {
451 #ifdef feraisexcept // a macro in some environments; omit std::
452 #define RAISE feraiseexcept
454 #define RAISE std::feraiseexcept
456 if (flags
& decimal::ConversionResultFlags::Overflow
) {
459 if (flags
& decimal::ConversionResultFlags::Inexact
) {
462 if (flags
& decimal::ConversionResultFlags::Invalid
) {
468 // If no special modes are in effect and the form of the input value
469 // that's present in the input stream is acceptable to the decimal->binary
470 // converter without modification, this fast path for real input
471 // saves time by avoiding memory copies and reformatting of the exponent.
472 template <int PRECISION
>
473 static bool TryFastPathRealDecimalInput(
474 IoStatementState
&io
, const DataEdit
&edit
, void *n
) {
475 if (edit
.modes
.editingFlags
& (blankZero
| decimalComma
)) {
478 if (edit
.modes
.scale
!= 0) {
481 const ConnectionState
&connection
{io
.GetConnectionState()};
482 if (connection
.internalIoCharKind
> 1) {
483 return false; // reading non-default character
485 const char *str
{nullptr};
486 std::size_t got
{io
.GetNextInputBytes(str
)};
487 if (got
== 0 || str
== nullptr || !connection
.recordLength
.has_value()) {
488 return false; // could not access reliably-terminated input stream
491 std::int64_t maxConsume
{
492 std::min
<std::int64_t>(got
, edit
.width
.value_or(got
))};
493 const char *limit
{str
+ maxConsume
};
494 decimal::ConversionToBinaryResult
<PRECISION
> converted
{
495 decimal::ConvertToBinary
<PRECISION
>(p
, edit
.modes
.round
, limit
)};
496 if (converted
.flags
& (decimal::Invalid
| decimal::Overflow
)) {
499 if (edit
.digits
.value_or(0) != 0) {
500 // Edit descriptor is Fw.d (or other) with d != 0, which
503 for (; q
< limit
; ++q
) {
504 if (*q
== '.' || *q
== 'n' || *q
== 'N') {
509 // No explicit decimal point, and not NaN/Inf.
513 if (edit
.descriptor
== DataEdit::ListDirectedImaginaryPart
) {
514 // Need to consume a trailing ')', possibly with leading spaces
515 for (; p
< limit
&& (*p
== ' ' || *p
== '\t'); ++p
) {
517 if (p
< limit
&& *p
== ')') {
522 } else if (edit
.IsListDirected()) {
523 if (p
< limit
&& !IsCharValueSeparator(edit
, *p
)) {
527 for (; p
< limit
&& (*p
== ' ' || *p
== '\t'); ++p
) {
529 if (edit
.width
&& p
< str
+ *edit
.width
) {
530 return false; // unconverted characters remain in fixed width field
533 // Success on the fast path!
534 *reinterpret_cast<decimal::BinaryFloatingPointNumber
<PRECISION
> *>(n
) =
536 io
.HandleRelativePosition(p
- str
);
537 // Set FP exception flags
538 if (converted
.flags
!= decimal::ConversionResultFlags::Exact
) {
539 RaiseFPExceptions(converted
.flags
);
544 template <int binaryPrecision
>
545 decimal::ConversionToBinaryResult
<binaryPrecision
> ConvertHexadecimal(
546 const char *&p
, enum decimal::FortranRounding rounding
, int expo
) {
547 using RealType
= decimal::BinaryFloatingPointNumber
<binaryPrecision
>;
548 using RawType
= typename
RealType::RawType
;
549 bool isNegative
{*p
== '-'};
550 constexpr RawType one
{1};
554 signBit
= one
<< (RealType::bits
- 1);
557 // Adjust the incoming binary P+/- exponent to shift the radix point
558 // to below the LSB and add in the bias.
559 expo
+= binaryPrecision
- 1 + RealType::exponentBias
;
560 // Input the fraction.
566 if (*p
>= '0' && *p
<= '9') {
567 fraction
|= *p
- '0';
568 } else if (*p
>= 'A' && *p
<= 'F') {
569 fraction
|= *p
- 'A' + 10; // data were normalized to capitals
573 while (fraction
>> binaryPrecision
) {
574 guardBit
|= roundingBit
;
575 roundingBit
= (int)fraction
& 1;
581 // Boost biased expo if too small
583 guardBit
|= roundingBit
;
584 roundingBit
= (int)fraction
& 1;
589 while (expo
> 1 && !(fraction
>> (binaryPrecision
- 1))) {
594 bool increase
{false};
596 case decimal::RoundNearest
: // RN & RP
597 increase
= roundingBit
&& (guardBit
| ((int)fraction
& 1));
599 case decimal::RoundUp
: // RU
600 increase
= !isNegative
&& (roundingBit
| guardBit
);
602 case decimal::RoundDown
: // RD
603 increase
= isNegative
&& (roundingBit
| guardBit
);
605 case decimal::RoundToZero
: // RZ
607 case decimal::RoundCompatible
: // RC
608 increase
= roundingBit
!= 0;
613 if (fraction
>> binaryPrecision
) {
619 // Package & return result
620 constexpr RawType significandMask
{(one
<< RealType::significandBits
) - 1};
623 } else if (expo
== 1 && !(fraction
>> (binaryPrecision
- 1))) {
624 expo
= 0; // subnormal
625 } else if (expo
>= RealType::maxExponent
) {
626 expo
= RealType::maxExponent
; // +/-Inf
629 fraction
&= significandMask
; // remove explicit normalization unless x87
631 return decimal::ConversionToBinaryResult
<binaryPrecision
>{
632 RealType
{static_cast<RawType
>(signBit
|
633 static_cast<RawType
>(expo
) << RealType::significandBits
| fraction
)},
634 (roundingBit
| guardBit
) ? decimal::Inexact
: decimal::Exact
};
638 bool EditCommonRealInput(IoStatementState
&io
, const DataEdit
&edit
, void *n
) {
639 constexpr int binaryPrecision
{common::PrecisionOfRealKind(KIND
)};
640 if (TryFastPathRealDecimalInput
<binaryPrecision
>(io
, edit
, n
)) {
641 return CheckCompleteListDirectedField(io
, edit
);
643 // Fast path wasn't available or didn't work; go the more general route
644 static constexpr int maxDigits
{
645 common::MaxDecimalConversionDigits(binaryPrecision
)};
646 static constexpr int bufferSize
{maxDigits
+ 18};
647 char buffer
[bufferSize
];
648 auto scanned
{ScanRealInput(buffer
, maxDigits
+ 2, io
, edit
)};
649 int got
{scanned
.got
};
650 if (got
>= maxDigits
+ 2) {
651 io
.GetIoErrorHandler().Crash("EditCommonRealInput: buffer was too small");
655 const auto &connection
{io
.GetConnectionState()};
656 io
.GetIoErrorHandler().SignalError(IostatBadRealInput
,
657 "Bad real input data at column %d of record %d",
658 static_cast<int>(connection
.positionInRecord
+ 1),
659 static_cast<int>(connection
.currentRecordNumber
));
662 decimal::ConversionToBinaryResult
<binaryPrecision
> converted
;
663 const char *p
{buffer
};
664 if (scanned
.isHexadecimal
) {
666 converted
= ConvertHexadecimal
<binaryPrecision
>(
667 p
, edit
.modes
.round
, scanned
.exponent
);
669 bool hadExtra
{got
> maxDigits
};
670 int exponent
{scanned
.exponent
};
675 exponent
= -exponent
;
677 if (exponent
> 9999) {
678 exponent
= 9999; // will convert to +/-Inf
680 if (exponent
> 999) {
681 int dig
{exponent
/ 1000};
682 buffer
[got
++] = '0' + dig
;
683 int rest
{exponent
- 1000 * dig
};
685 buffer
[got
++] = '0' + dig
;
688 buffer
[got
++] = '0' + dig
;
689 buffer
[got
++] = '0' + (rest
- 10 * dig
);
690 } else if (exponent
> 99) {
691 int dig
{exponent
/ 100};
692 buffer
[got
++] = '0' + dig
;
693 int rest
{exponent
- 100 * dig
};
695 buffer
[got
++] = '0' + dig
;
696 buffer
[got
++] = '0' + (rest
- 10 * dig
);
697 } else if (exponent
> 9) {
698 int dig
{exponent
/ 10};
699 buffer
[got
++] = '0' + dig
;
700 buffer
[got
++] = '0' + (exponent
- 10 * dig
);
702 buffer
[got
++] = '0' + exponent
;
706 converted
= decimal::ConvertToBinary
<binaryPrecision
>(p
, edit
.modes
.round
);
708 converted
.flags
= static_cast<enum decimal::ConversionResultFlags
>(
709 converted
.flags
| decimal::Inexact
);
712 if (*p
) { // unprocessed junk after value
713 const auto &connection
{io
.GetConnectionState()};
714 io
.GetIoErrorHandler().SignalError(IostatBadRealInput
,
715 "Trailing characters after real input data at column %d of record %d",
716 static_cast<int>(connection
.positionInRecord
+ 1),
717 static_cast<int>(connection
.currentRecordNumber
));
720 *reinterpret_cast<decimal::BinaryFloatingPointNumber
<binaryPrecision
> *>(n
) =
722 // Set FP exception flags
723 if (converted
.flags
!= decimal::ConversionResultFlags::Exact
) {
724 if (converted
.flags
& decimal::ConversionResultFlags::Overflow
) {
725 io
.GetIoErrorHandler().SignalError(IostatRealInputOverflow
);
728 RaiseFPExceptions(converted
.flags
);
730 return CheckCompleteListDirectedField(io
, edit
);
734 bool EditRealInput(IoStatementState
&io
, const DataEdit
&edit
, void *n
) {
735 switch (edit
.descriptor
) {
736 case DataEdit::ListDirected
:
737 if (IsNamelistNameOrSlash(io
)) {
740 return EditCommonRealInput
<KIND
>(io
, edit
, n
);
741 case DataEdit::ListDirectedRealPart
:
742 case DataEdit::ListDirectedImaginaryPart
:
744 case 'E': // incl. EN, ES, & EX
747 return EditCommonRealInput
<KIND
>(io
, edit
, n
);
749 return EditBOZInput
<1>(io
, edit
, n
,
750 common::BitsForBinaryPrecision(common::PrecisionOfRealKind(KIND
)) >> 3);
752 return EditBOZInput
<3>(io
, edit
, n
,
753 common::BitsForBinaryPrecision(common::PrecisionOfRealKind(KIND
)) >> 3);
755 return EditBOZInput
<4>(io
, edit
, n
,
756 common::BitsForBinaryPrecision(common::PrecisionOfRealKind(KIND
)) >> 3);
757 case 'A': // legacy extension
758 return EditCharacterInput(io
, edit
, reinterpret_cast<char *>(n
), KIND
);
760 io
.GetIoErrorHandler().SignalError(IostatErrorInFormat
,
761 "Data edit descriptor '%c' may not be used for REAL input",
767 // 13.7.3 in Fortran 2018
768 bool EditLogicalInput(IoStatementState
&io
, const DataEdit
&edit
, bool &x
) {
769 switch (edit
.descriptor
) {
770 case DataEdit::ListDirected
:
771 if (IsNamelistNameOrSlash(io
)) {
779 io
.GetIoErrorHandler().SignalError(IostatErrorInFormat
,
780 "Data edit descriptor '%c' may not be used for LOGICAL input",
784 std::optional
<int> remaining
{io
.CueUpInput(edit
)};
785 std::optional
<char32_t
> next
{io
.NextInField(remaining
, edit
)};
786 if (next
&& *next
== '.') { // skip optional period
787 next
= io
.NextInField(remaining
, edit
);
790 io
.GetIoErrorHandler().SignalError("Empty LOGICAL input field");
803 io
.GetIoErrorHandler().SignalError(
804 "Bad character '%lc' in LOGICAL input field", *next
);
807 if (remaining
) { // ignore the rest of a fixed-width field
808 io
.HandleRelativePosition(*remaining
);
809 } else if (edit
.descriptor
== DataEdit::ListDirected
) {
810 while (io
.NextInField(remaining
, edit
)) { // discard rest of field
813 return CheckCompleteListDirectedField(io
, edit
);
816 // See 13.10.3.1 paragraphs 7-9 in Fortran 2018
817 template <typename CHAR
>
818 static bool EditDelimitedCharacterInput(
819 IoStatementState
&io
, CHAR
*x
, std::size_t length
, char32_t delimiter
) {
822 std::size_t byteCount
{0};
823 auto ch
{io
.GetCurrentChar(byteCount
)};
825 if (io
.AdvanceRecord()) {
828 result
= false; // EOF in character value
832 io
.HandleRelativePosition(byteCount
);
833 if (*ch
== delimiter
) {
834 auto next
{io
.GetCurrentChar(byteCount
)};
835 if (next
&& *next
== delimiter
) {
836 // Repeated delimiter: use as character value
837 io
.HandleRelativePosition(byteCount
);
839 break; // closing delimiter
847 std::fill_n(x
, length
, ' ');
851 template <typename CHAR
>
852 static bool EditListDirectedCharacterInput(
853 IoStatementState
&io
, CHAR
*x
, std::size_t length
, const DataEdit
&edit
) {
854 std::size_t byteCount
{0};
855 auto ch
{io
.GetCurrentChar(byteCount
)};
856 if (ch
&& (*ch
== '\'' || *ch
== '"')) {
857 io
.HandleRelativePosition(byteCount
);
858 return EditDelimitedCharacterInput(io
, x
, length
, *ch
);
860 if (IsNamelistNameOrSlash(io
) || io
.GetConnectionState().IsAtEOF()) {
863 // Undelimited list-directed character input: stop at a value separator
864 // or the end of the current record. Subtlety: the "remaining" count
865 // here is a dummy that's used to avoid the interpretation of separators
867 std::optional
<int> remaining
{length
> 0 ? maxUTF8Bytes
: 0};
868 while (std::optional
<char32_t
> next
{io
.NextInField(remaining
, edit
)}) {
877 isSep
= !(edit
.modes
.editingFlags
& decimalComma
);
880 isSep
= !!(edit
.modes
.editingFlags
& decimalComma
);
889 remaining
= --length
> 0 ? maxUTF8Bytes
: 0;
892 std::fill_n(x
, length
, ' ');
896 template <typename CHAR
>
897 bool EditCharacterInput(
898 IoStatementState
&io
, const DataEdit
&edit
, CHAR
*x
, std::size_t length
) {
899 switch (edit
.descriptor
) {
900 case DataEdit::ListDirected
:
901 return EditListDirectedCharacterInput(io
, x
, length
, edit
);
906 return EditBOZInput
<1>(io
, edit
, x
, length
* sizeof *x
);
908 return EditBOZInput
<3>(io
, edit
, x
, length
* sizeof *x
);
910 return EditBOZInput
<4>(io
, edit
, x
, length
* sizeof *x
);
912 io
.GetIoErrorHandler().SignalError(IostatErrorInFormat
,
913 "Data edit descriptor '%c' may not be used with a CHARACTER data item",
917 const ConnectionState
&connection
{io
.GetConnectionState()};
918 std::size_t remaining
{length
};
919 if (edit
.width
&& *edit
.width
> 0) {
920 remaining
= *edit
.width
;
922 // When the field is wider than the variable, we drop the leading
923 // characters. When the variable is wider than the field, there can be
924 // trailing padding or an EOR condition.
925 const char *input
{nullptr};
926 std::size_t ready
{0};
927 // Skip leading bytes.
928 // These bytes don't count towards INQUIRE(IOLENGTH=).
929 std::size_t skip
{remaining
> length
? remaining
- length
: 0};
930 // Transfer payload bytes; these do count.
931 while (remaining
> 0) {
933 ready
= io
.GetNextInputBytes(input
);
934 if (ready
== 0 || (ready
< remaining
&& edit
.modes
.nonAdvancing
)) {
935 if (io
.CheckForEndOfRecord(ready
)) {
937 // PAD='YES' and no more data
938 std::fill_n(x
, length
, ' ');
939 return !io
.GetIoErrorHandler().InError();
941 // Do partial read(s) then pad on last iteration
944 return !io
.GetIoErrorHandler().InError();
949 bool skipping
{skip
> 0};
950 if (connection
.isUTF8
) {
951 chunk
= MeasureUTF8Bytes(*input
);
954 } else if (auto ucs
{DecodeUTF8(input
)}) {
957 } else if (chunk
== 0) {
958 // error recovery: skip bad encoding
962 } else if (connection
.internalIoCharKind
> 1) {
963 // Reading from non-default character internal unit
964 chunk
= connection
.internalIoCharKind
;
969 std::memcpy(&buffer
, input
, chunk
);
974 } else if constexpr (sizeof *x
> 1) {
975 // Read single byte with expansion into multi-byte CHARACTER
980 *x
++ = static_cast<unsigned char>(*input
);
984 } else { // single bytes -> default CHARACTER
986 chunk
= std::min
<std::size_t>(skip
, ready
);
989 chunk
= std::min
<std::size_t>(remaining
, ready
);
990 std::memcpy(x
, input
, chunk
);
1000 io
.HandleRelativePosition(chunk
);
1003 // Pad the remainder of the input variable, if any.
1004 std::fill_n(x
, length
, ' ');
1005 return CheckCompleteListDirectedField(io
, edit
);
1008 template bool EditRealInput
<2>(IoStatementState
&, const DataEdit
&, void *);
1009 template bool EditRealInput
<3>(IoStatementState
&, const DataEdit
&, void *);
1010 template bool EditRealInput
<4>(IoStatementState
&, const DataEdit
&, void *);
1011 template bool EditRealInput
<8>(IoStatementState
&, const DataEdit
&, void *);
1012 template bool EditRealInput
<10>(IoStatementState
&, const DataEdit
&, void *);
1013 // TODO: double/double
1014 template bool EditRealInput
<16>(IoStatementState
&, const DataEdit
&, void *);
1016 template bool EditCharacterInput(
1017 IoStatementState
&, const DataEdit
&, char *, std::size_t);
1018 template bool EditCharacterInput(
1019 IoStatementState
&, const DataEdit
&, char16_t
*, std::size_t);
1020 template bool EditCharacterInput(
1021 IoStatementState
&, const DataEdit
&, char32_t
*, std::size_t);
1023 } // namespace Fortran::runtime::io