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/optional.h"
13 #include "flang/Common/real.h"
14 #include "flang/Common/uint128.h"
15 #include "flang/Runtime/freestanding-tools.h"
19 namespace Fortran::runtime::io
{
20 RT_OFFLOAD_API_GROUP_BEGIN
22 // Checks that a list-directed input value has been entirely consumed and
23 // doesn't contain unparsed characters before the next value separator.
24 static inline RT_API_ATTRS
bool IsCharValueSeparator(
25 const DataEdit
&edit
, char32_t ch
) {
27 edit
.modes
.editingFlags
& decimalComma
? char32_t
{';'} : char32_t
{','}};
28 return ch
== ' ' || ch
== '\t' || ch
== comma
|| ch
== '/' ||
29 (edit
.IsNamelist() && (ch
== '&' || ch
== '$'));
32 static RT_API_ATTRS
bool CheckCompleteListDirectedField(
33 IoStatementState
&io
, const DataEdit
&edit
) {
34 if (edit
.IsListDirected()) {
35 std::size_t byteCount
;
36 if (auto ch
{io
.GetCurrentChar(byteCount
)}) {
37 if (IsCharValueSeparator(edit
, *ch
)) {
40 const auto &connection
{io
.GetConnectionState()};
41 io
.GetIoErrorHandler().SignalError(IostatBadListDirectedInputSeparator
,
42 "invalid character (0x%x) after list-directed input value, "
43 "at column %d in record %d",
44 static_cast<unsigned>(*ch
),
45 static_cast<int>(connection
.positionInRecord
+ 1),
46 static_cast<int>(connection
.currentRecordNumber
));
50 return true; // end of record: ok
57 static inline RT_API_ATTRS char32_t
GetSeparatorChar(const DataEdit
&edit
) {
58 return edit
.modes
.editingFlags
& decimalComma
? char32_t
{';'} : char32_t
{','};
61 template <int LOG2_BASE
>
62 static RT_API_ATTRS
bool EditBOZInput(
63 IoStatementState
&io
, const DataEdit
&edit
, void *n
, std::size_t bytes
) {
64 // Skip leading white space & zeroes
65 Fortran::common::optional
<int> remaining
{io
.CueUpInput(edit
)};
66 auto start
{io
.GetConnectionState().positionInRecord
};
67 Fortran::common::optional
<char32_t
> next
{io
.NextInField(remaining
, edit
)};
68 if (next
.value_or('?') == '0') {
70 start
= io
.GetConnectionState().positionInRecord
;
71 next
= io
.NextInField(remaining
, edit
);
72 } while (next
&& *next
== '0');
74 // Count significant digits after any leading white space & zeroes
76 int significantBits
{0};
77 const char32_t comma
{GetSeparatorChar(edit
)};
78 for (; next
; next
= io
.NextInField(remaining
, edit
)) {
80 if (ch
== ' ' || ch
== '\t') {
81 if (edit
.modes
.editingFlags
& blankZero
) {
82 ch
= '0'; // BZ mode - treat blank as if it were zero
87 if (ch
>= '0' && ch
<= '1') {
88 } else if (LOG2_BASE
>= 3 && ch
>= '2' && ch
<= '7') {
89 } else if (LOG2_BASE
>= 4 && ch
>= '8' && ch
<= '9') {
90 } else if (LOG2_BASE
>= 4 && ch
>= 'A' && ch
<= 'F') {
91 } else if (LOG2_BASE
>= 4 && ch
>= 'a' && ch
<= 'f') {
92 } else if (ch
== comma
) {
93 break; // end non-list-directed field early
95 io
.GetIoErrorHandler().SignalError(
96 "Bad character '%lc' in B/O/Z input field", ch
);
101 if (ch
>= '0' && ch
<= '1') {
103 } else if (ch
>= '2' && ch
<= '3') {
105 } else if (ch
>= '4' && ch
<= '7') {
111 significantBits
+= LOG2_BASE
;
114 auto significantBytes
{static_cast<std::size_t>(significantBits
+ 7) / 8};
115 if (significantBytes
> bytes
) {
116 io
.GetIoErrorHandler().SignalError(IostatBOZInputOverflow
,
117 "B/O/Z input of %d digits overflows %zd-byte variable", digits
, bytes
);
120 // Reset to start of significant digits
121 io
.HandleAbsolutePosition(start
);
123 // Make a second pass now that the digit count is known
124 std::memset(n
, 0, bytes
);
125 int increment
{isHostLittleEndian
? -1 : 1};
126 auto *data
{reinterpret_cast<unsigned char *>(n
) +
127 (isHostLittleEndian
? significantBytes
- 1 : bytes
- significantBytes
)};
128 int shift
{((digits
- 1) * LOG2_BASE
) & 7};
130 char32_t ch
{*io
.NextInField(remaining
, edit
)};
132 if (ch
== ' ' || ch
== '\t') {
133 if (edit
.modes
.editingFlags
& blankZero
) {
134 ch
= '0'; // BZ mode - treat blank as if it were zero
140 if (ch
>= '0' && ch
<= '9') {
142 } else if (ch
>= 'A' && ch
<= 'F') {
143 digit
= ch
+ 10 - 'A';
144 } else if (ch
>= 'a' && ch
<= 'f') {
145 digit
= ch
+ 10 - 'a';
150 if (shift
+ LOG2_BASE
> 0) { // misaligned octal
151 *data
|= digit
>> -shift
;
156 *data
|= digit
<< shift
;
159 return CheckCompleteListDirectedField(io
, edit
);
162 static inline RT_API_ATTRS char32_t
GetRadixPointChar(const DataEdit
&edit
) {
163 return edit
.modes
.editingFlags
& decimalComma
? char32_t
{','} : char32_t
{'.'};
166 // Prepares input from a field, and returns the sign, if any, else '\0'.
167 static RT_API_ATTRS
char ScanNumericPrefix(IoStatementState
&io
,
168 const DataEdit
&edit
, Fortran::common::optional
<char32_t
> &next
,
169 Fortran::common::optional
<int> &remaining
) {
170 remaining
= io
.CueUpInput(edit
);
171 next
= io
.NextInField(remaining
, edit
);
174 if (*next
== '-' || *next
== '+') {
176 if (!edit
.IsListDirected()) {
177 io
.SkipSpaces(remaining
);
179 next
= io
.NextInField(remaining
, edit
);
185 RT_API_ATTRS
bool EditIntegerInput(
186 IoStatementState
&io
, const DataEdit
&edit
, void *n
, int kind
) {
187 RUNTIME_CHECK(io
.GetIoErrorHandler(), kind
>= 1 && !(kind
& (kind
- 1)));
188 switch (edit
.descriptor
) {
189 case DataEdit::ListDirected
:
190 if (IsNamelistNameOrSlash(io
)) {
198 return EditBOZInput
<1>(io
, edit
, n
, kind
);
200 return EditBOZInput
<3>(io
, edit
, n
, kind
);
202 return EditBOZInput
<4>(io
, edit
, n
, kind
);
203 case 'A': // legacy extension
204 return EditCharacterInput(io
, edit
, reinterpret_cast<char *>(n
), kind
);
206 io
.GetIoErrorHandler().SignalError(IostatErrorInFormat
,
207 "Data edit descriptor '%c' may not be used with an INTEGER data item",
211 Fortran::common::optional
<int> remaining
;
212 Fortran::common::optional
<char32_t
> next
;
213 char sign
{ScanNumericPrefix(io
, edit
, next
, remaining
)};
214 common::UnsignedInt128 value
{0};
216 bool overflow
{false};
217 const char32_t comma
{GetSeparatorChar(edit
)};
218 for (; next
; next
= io
.NextInField(remaining
, edit
)) {
220 if (ch
== ' ' || ch
== '\t') {
221 if (edit
.modes
.editingFlags
& blankZero
) {
222 ch
= '0'; // BZ mode - treat blank as if it were zero
228 if (ch
>= '0' && ch
<= '9') {
230 } else if (ch
== comma
) {
231 break; // end non-list-directed field early
233 if (edit
.modes
.inNamelist
&& ch
== GetRadixPointChar(edit
)) {
234 // Ignore any fractional part that might appear in NAMELIST integer
235 // input, like a few other Fortran compilers do.
236 // TODO: also process exponents? Some compilers do, but they obviously
237 // can't just be ignored.
238 while ((next
= io
.NextInField(remaining
, edit
))) {
239 if (*next
< '0' || *next
> '9') {
243 if (!next
|| *next
== comma
) {
247 io
.GetIoErrorHandler().SignalError(
248 "Bad character '%lc' in INTEGER input field", ch
);
251 static constexpr auto maxu128
{~common::UnsignedInt128
{0}};
252 static constexpr auto maxu128OverTen
{maxu128
/ 10};
253 static constexpr int maxLastDigit
{
254 static_cast<int>(maxu128
- (maxu128OverTen
* 10))};
255 overflow
|= value
>= maxu128OverTen
&&
256 (value
> maxu128OverTen
|| digit
> maxLastDigit
);
261 if (!any
&& !remaining
) {
262 io
.GetIoErrorHandler().SignalError(
263 "Integer value absent from NAMELIST or list-directed input");
266 auto maxForKind
{common::UnsignedInt128
{1} << ((8 * kind
) - 1)};
267 overflow
|= value
>= maxForKind
&& (value
> maxForKind
|| sign
!= '-');
269 io
.GetIoErrorHandler().SignalError(IostatIntegerInputOverflow
,
270 "Decimal input overflows INTEGER(%d) variable", kind
);
276 if (any
|| !io
.GetIoErrorHandler().InError()) {
277 // The value is stored in the lower order bits on big endian platform.
278 // When memcpy, shift the value to the higher order bit.
279 auto shft
{static_cast<int>(sizeof(value
.low())) - kind
};
280 // For kind==8 (i.e. shft==0), the value is stored in low_ in big endian.
281 if (!isHostLittleEndian
&& shft
>= 0) {
282 auto l
{value
.low() << (8 * shft
)};
283 std::memcpy(n
, &l
, kind
);
285 std::memcpy(n
, &value
, kind
); // a blank field means zero
293 // Parses a REAL input number from the input source as a normalized
294 // fraction into a supplied buffer -- there's an optional '-', a
295 // decimal point when the input is not hexadecimal, and at least one
296 // digit. Replaces blanks with zeroes where appropriate.
297 struct ScannedRealInput
{
298 // Number of characters that (should) have been written to the
299 // buffer -- this can be larger than the buffer size, which
300 // indicates buffer overflow. Zero indicates an error.
302 int exponent
{0}; // adjusted as necessary; binary if isHexadecimal
303 bool isHexadecimal
{false}; // 0X...
305 static RT_API_ATTRS ScannedRealInput
ScanRealInput(
306 char *buffer
, int bufferSize
, IoStatementState
&io
, const DataEdit
&edit
) {
307 Fortran::common::optional
<int> remaining
;
308 Fortran::common::optional
<char32_t
> next
;
310 Fortran::common::optional
<int> radixPointOffset
;
311 // The following lambda definition violates the conding style,
312 // but cuda-11.8 nvcc hits an internal error with the brace initialization.
313 auto Put
= [&](char ch
) -> void {
314 if (got
< bufferSize
) {
319 char sign
{ScanNumericPrefix(io
, edit
, next
, remaining
)};
323 bool bzMode
{(edit
.modes
.editingFlags
& blankZero
) != 0};
325 if (!next
|| (!bzMode
&& *next
== ' ') ||
326 (!(edit
.modes
.editingFlags
& decimalComma
) && *next
== ',')) {
327 if (!edit
.IsListDirected() && !io
.GetConnectionState().IsAtEOF()) {
328 // An empty/blank field means zero when not list-directed.
329 // A fixed-width field containing only a sign is also zero;
330 // this behavior isn't standard-conforming in F'2023 but it is
331 // required to pass FCVS.
334 return {got
, exponent
, false};
336 char32_t radixPointChar
{GetRadixPointChar(edit
)};
337 char32_t first
{*next
>= 'a' && *next
<= 'z' ? *next
+ 'A' - 'a' : *next
};
338 bool isHexadecimal
{false};
339 if (first
== 'N' || first
== 'I') {
340 // NaN or infinity - convert to upper case
341 // Subtle: a blank field of digits could be followed by 'E' or 'D',
343 ((*next
>= 'a' && *next
<= 'z') || (*next
>= 'A' && *next
<= 'Z'));
344 next
= io
.NextInField(remaining
, edit
)) {
345 if (*next
>= 'a' && *next
<= 'z') {
346 Put(*next
- 'a' + 'A');
351 if (next
&& *next
== '(') { // NaN(...)
355 next
= io
.NextInField(remaining
, edit
);
360 } else if (*next
== '(') {
362 } else if (*next
== ')') {
368 } else if (first
== radixPointChar
|| (first
>= '0' && first
<= '9') ||
369 (bzMode
&& (first
== ' ' || first
== '\t')) || first
== 'E' ||
370 first
== 'D' || first
== 'Q') {
372 next
= io
.NextInField(remaining
, edit
);
373 if (next
&& (*next
== 'x' || *next
== 'X')) { // 0X...
374 isHexadecimal
= true;
375 next
= io
.NextInField(remaining
, edit
);
380 // input field is normalized to a fraction
381 if (!isHexadecimal
) {
385 for (; next
; next
= io
.NextInField(remaining
, edit
)) {
387 if (ch
== ' ' || ch
== '\t') {
391 ch
= '0'; // BZ mode - treat blank as if it were zero
393 continue; // ignore blank in fixed field
396 if (ch
== '0' && got
== start
&& !radixPointOffset
) {
397 // omit leading zeroes before the radix point
398 } else if (ch
>= '0' && ch
<= '9') {
400 } else if (ch
== radixPointChar
&& !radixPointOffset
) {
401 // The radix point character is *not* copied to the buffer.
402 radixPointOffset
= got
- start
; // # of digits before the radix point
403 } else if (isHexadecimal
&& ch
>= 'A' && ch
<= 'F') {
405 } else if (isHexadecimal
&& ch
>= 'a' && ch
<= 'f') {
406 Put(ch
- 'a' + 'A'); // normalize to capitals
412 // Nothing but zeroes and maybe a radix point. F'2018 requires
413 // at least one digit, but F'77 did not, and a bare "." shows up in
415 Put('0'); // emit at least one digit
417 // In list-directed input, a bad exponent is not consumed.
418 auto nextBeforeExponent
{next
};
419 auto startExponent
{io
.GetConnectionState().positionInRecord
};
420 bool hasGoodExponent
{false};
423 if (*next
== 'p' || *next
== 'P') {
424 next
= io
.NextInField(remaining
, edit
);
426 // The binary exponent is not optional in the standard.
429 } else if (*next
== 'e' || *next
== 'E' || *next
== 'd' || *next
== 'D' ||
430 *next
== 'q' || *next
== 'Q') {
431 // Optional exponent letter. Blanks are allowed between the
432 // optional exponent letter and the exponent value.
433 io
.SkipSpaces(remaining
);
434 next
= io
.NextInField(remaining
, edit
);
438 (*next
== '-' || *next
== '+' || (*next
>= '0' && *next
<= '9') ||
439 *next
== ' ' || *next
== '\t')) {
440 bool negExpo
{*next
== '-'};
441 if (negExpo
|| *next
== '+') {
442 next
= io
.NextInField(remaining
, edit
);
444 for (; next
; next
= io
.NextInField(remaining
, edit
)) {
445 if (*next
>= '0' && *next
<= '9') {
446 hasGoodExponent
= true;
447 if (exponent
< 10000) {
448 exponent
= 10 * exponent
+ *next
- '0';
450 } else if (*next
== ' ' || *next
== '\t') {
454 hasGoodExponent
= true;
455 exponent
= 10 * exponent
;
462 exponent
= -exponent
;
465 if (!hasGoodExponent
) {
469 // There isn't a good exponent; do not consume it.
470 next
= nextBeforeExponent
;
471 io
.HandleAbsolutePosition(startExponent
);
472 // The default exponent is -kP, but the scale factor doesn't affect
473 // an explicit exponent.
474 exponent
= -edit
.modes
.scale
;
476 // Adjust exponent by number of digits before the radix point.
478 // Exponents for hexadecimal input are binary.
479 exponent
+= radixPointOffset
.value_or(got
- start
) * 4;
480 } else if (radixPointOffset
) {
481 exponent
+= *radixPointOffset
;
483 // When no redix point (or comma) appears in the value, the 'd'
484 // part of the edit descriptor must be interpreted as the number of
485 // digits in the value to be interpreted as being to the *right* of
486 // the assumed radix point (13.7.2.3.2)
487 exponent
+= got
- start
- edit
.digits
.value_or(0);
490 // Consume the trailing ')' of a list-directed or NAMELIST complex
492 if (edit
.descriptor
== DataEdit::ListDirectedImaginaryPart
) {
493 if (next
&& (*next
== ' ' || *next
== '\t')) {
494 io
.SkipSpaces(remaining
);
495 next
= io
.NextInField(remaining
, edit
);
497 if (!next
) { // NextInField fails on separators like ')'
498 std::size_t byteCount
{0};
499 next
= io
.GetCurrentChar(byteCount
);
500 if (next
&& *next
== ')') {
501 io
.HandleRelativePosition(byteCount
);
504 } else if (remaining
) {
505 while (next
&& (*next
== ' ' || *next
== '\t')) {
506 next
= io
.NextInField(remaining
, edit
);
508 if (next
&& (*next
!= ',' || (edit
.modes
.editingFlags
& decimalComma
))) {
509 return {}; // error: unused nonblank character in fixed-width field
512 return {got
, exponent
, isHexadecimal
};
515 static RT_API_ATTRS
void RaiseFPExceptions(
516 decimal::ConversionResultFlags flags
) {
518 #if defined(RT_DEVICE_COMPILATION)
519 Terminator
terminator(__FILE__
, __LINE__
);
522 "not implemented yet: raising FP exception in device code: %s", #e);
523 #else // !defined(RT_DEVICE_COMPILATION)
524 #ifdef feraisexcept // a macro in some environments; omit std::
525 #define RAISE feraiseexcept
527 #define RAISE std::feraiseexcept
529 #endif // !defined(RT_DEVICE_COMPILATION)
531 // Some environment (e.g. emscripten, musl) don't define FE_OVERFLOW as allowed
532 // by c99 (but not c++11) :-/
533 #if defined(FE_OVERFLOW) || defined(RT_DEVICE_COMPILATION)
534 if (flags
& decimal::ConversionResultFlags::Overflow
) {
538 #if defined(FE_UNDERFLOW) || defined(RT_DEVICE_COMPILATION)
539 if (flags
& decimal::ConversionResultFlags::Underflow
) {
543 #if defined(FE_INEXACT) || defined(RT_DEVICE_COMPILATION)
544 if (flags
& decimal::ConversionResultFlags::Inexact
) {
548 #if defined(FE_INVALID) || defined(RT_DEVICE_COMPILATION)
549 if (flags
& decimal::ConversionResultFlags::Invalid
) {
556 // If no special modes are in effect and the form of the input value
557 // that's present in the input stream is acceptable to the decimal->binary
558 // converter without modification, this fast path for real input
559 // saves time by avoiding memory copies and reformatting of the exponent.
560 template <int PRECISION
>
561 static RT_API_ATTRS
bool TryFastPathRealDecimalInput(
562 IoStatementState
&io
, const DataEdit
&edit
, void *n
) {
563 if (edit
.modes
.editingFlags
& (blankZero
| decimalComma
)) {
566 if (edit
.modes
.scale
!= 0) {
569 const ConnectionState
&connection
{io
.GetConnectionState()};
570 if (connection
.internalIoCharKind
> 1) {
571 return false; // reading non-default character
573 const char *str
{nullptr};
574 std::size_t got
{io
.GetNextInputBytes(str
)};
575 if (got
== 0 || str
== nullptr || !connection
.recordLength
.has_value()) {
576 return false; // could not access reliably-terminated input stream
579 std::int64_t maxConsume
{
580 std::min
<std::int64_t>(got
, edit
.width
.value_or(got
))};
581 const char *limit
{str
+ maxConsume
};
582 decimal::ConversionToBinaryResult
<PRECISION
> converted
{
583 decimal::ConvertToBinary
<PRECISION
>(p
, edit
.modes
.round
, limit
)};
584 if (converted
.flags
& (decimal::Invalid
| decimal::Overflow
)) {
587 if (edit
.digits
.value_or(0) != 0) {
588 // Edit descriptor is Fw.d (or other) with d != 0, which
591 for (; q
< limit
; ++q
) {
592 if (*q
== '.' || *q
== 'n' || *q
== 'N') {
597 // No explicit decimal point, and not NaN/Inf.
601 if (edit
.descriptor
== DataEdit::ListDirectedImaginaryPart
) {
602 // Need to consume a trailing ')', possibly with leading spaces
603 for (; p
< limit
&& (*p
== ' ' || *p
== '\t'); ++p
) {
605 if (p
< limit
&& *p
== ')') {
610 } else if (edit
.IsListDirected()) {
611 if (p
< limit
&& !IsCharValueSeparator(edit
, *p
)) {
615 for (; p
< limit
&& (*p
== ' ' || *p
== '\t'); ++p
) {
617 if (edit
.width
&& p
< str
+ *edit
.width
) {
618 return false; // unconverted characters remain in fixed width field
621 // Success on the fast path!
622 *reinterpret_cast<decimal::BinaryFloatingPointNumber
<PRECISION
> *>(n
) =
624 io
.HandleRelativePosition(p
- str
);
625 // Set FP exception flags
626 if (converted
.flags
!= decimal::ConversionResultFlags::Exact
) {
627 RaiseFPExceptions(converted
.flags
);
632 template <int binaryPrecision
>
633 RT_API_ATTRS
decimal::ConversionToBinaryResult
<binaryPrecision
>
635 const char *&p
, enum decimal::FortranRounding rounding
, int expo
) {
636 using RealType
= decimal::BinaryFloatingPointNumber
<binaryPrecision
>;
637 using RawType
= typename
RealType::RawType
;
638 bool isNegative
{*p
== '-'};
639 constexpr RawType one
{1};
643 signBit
= one
<< (RealType::bits
- 1);
646 // Adjust the incoming binary P+/- exponent to shift the radix point
647 // to below the LSB and add in the bias.
648 expo
+= binaryPrecision
- 1 + RealType::exponentBias
;
649 // Input the fraction.
655 if (*p
>= '0' && *p
<= '9') {
656 fraction
|= *p
- '0';
657 } else if (*p
>= 'A' && *p
<= 'F') {
658 fraction
|= *p
- 'A' + 10; // data were normalized to capitals
662 if (fraction
>> binaryPrecision
) {
663 while (fraction
>> binaryPrecision
) {
664 guardBit
|= roundingBit
;
665 roundingBit
= (int)fraction
& 1;
669 // Consume excess digits
672 } else if ((*p
>= '1' && *p
<= '9') || (*p
>= 'A' && *p
<= 'F')) {
682 // Boost biased expo if too small
684 guardBit
|= roundingBit
;
685 roundingBit
= (int)fraction
& 1;
690 while (expo
> 1 && !(fraction
>> (binaryPrecision
- 1))) {
693 guardBit
= roundingBit
= 0;
697 bool increase
{false};
699 case decimal::RoundNearest
: // RN & RP
700 increase
= roundingBit
&& (guardBit
| ((int)fraction
& 1));
702 case decimal::RoundUp
: // RU
703 increase
= !isNegative
&& (roundingBit
| guardBit
);
705 case decimal::RoundDown
: // RD
706 increase
= isNegative
&& (roundingBit
| guardBit
);
708 case decimal::RoundToZero
: // RZ
710 case decimal::RoundCompatible
: // RC
711 increase
= roundingBit
!= 0;
716 if (fraction
>> binaryPrecision
) {
721 // Package & return result
722 constexpr RawType significandMask
{(one
<< RealType::significandBits
) - 1};
723 int flags
{(roundingBit
| guardBit
) ? decimal::Inexact
: decimal::Exact
};
726 } else if (expo
== 1 && !(fraction
>> (binaryPrecision
- 1))) {
727 expo
= 0; // subnormal
728 flags
|= decimal::Underflow
;
729 } else if (expo
>= RealType::maxExponent
) {
730 if (rounding
== decimal::RoundToZero
||
731 (rounding
== decimal::RoundDown
&& !isNegative
) ||
732 (rounding
== decimal::RoundUp
&& isNegative
)) {
733 expo
= RealType::maxExponent
- 1; // +/-HUGE()
734 fraction
= significandMask
;
736 expo
= RealType::maxExponent
; // +/-Inf
738 flags
|= decimal::Overflow
;
741 fraction
&= significandMask
; // remove explicit normalization unless x87
743 return decimal::ConversionToBinaryResult
<binaryPrecision
>{
744 RealType
{static_cast<RawType
>(signBit
|
745 static_cast<RawType
>(expo
) << RealType::significandBits
| fraction
)},
746 static_cast<decimal::ConversionResultFlags
>(flags
)};
750 RT_API_ATTRS
bool EditCommonRealInput(
751 IoStatementState
&io
, const DataEdit
&edit
, void *n
) {
752 constexpr int binaryPrecision
{common::PrecisionOfRealKind(KIND
)};
753 if (TryFastPathRealDecimalInput
<binaryPrecision
>(io
, edit
, n
)) {
754 return CheckCompleteListDirectedField(io
, edit
);
756 // Fast path wasn't available or didn't work; go the more general route
757 static constexpr int maxDigits
{
758 common::MaxDecimalConversionDigits(binaryPrecision
)};
759 static constexpr int bufferSize
{maxDigits
+ 18};
760 char buffer
[bufferSize
];
761 auto scanned
{ScanRealInput(buffer
, maxDigits
+ 2, io
, edit
)};
762 int got
{scanned
.got
};
763 if (got
>= maxDigits
+ 2) {
764 io
.GetIoErrorHandler().Crash("EditCommonRealInput: buffer was too small");
768 const auto &connection
{io
.GetConnectionState()};
769 io
.GetIoErrorHandler().SignalError(IostatBadRealInput
,
770 "Bad real input data at column %d of record %d",
771 static_cast<int>(connection
.positionInRecord
+ 1),
772 static_cast<int>(connection
.currentRecordNumber
));
775 decimal::ConversionToBinaryResult
<binaryPrecision
> converted
;
776 const char *p
{buffer
};
777 if (scanned
.isHexadecimal
) {
779 converted
= ConvertHexadecimal
<binaryPrecision
>(
780 p
, edit
.modes
.round
, scanned
.exponent
);
782 bool hadExtra
{got
> maxDigits
};
783 int exponent
{scanned
.exponent
};
788 exponent
= -exponent
;
790 if (exponent
> 9999) {
791 exponent
= 9999; // will convert to +/-Inf
793 if (exponent
> 999) {
794 int dig
{exponent
/ 1000};
795 buffer
[got
++] = '0' + dig
;
796 int rest
{exponent
- 1000 * dig
};
798 buffer
[got
++] = '0' + dig
;
801 buffer
[got
++] = '0' + dig
;
802 buffer
[got
++] = '0' + (rest
- 10 * dig
);
803 } else if (exponent
> 99) {
804 int dig
{exponent
/ 100};
805 buffer
[got
++] = '0' + dig
;
806 int rest
{exponent
- 100 * dig
};
808 buffer
[got
++] = '0' + dig
;
809 buffer
[got
++] = '0' + (rest
- 10 * dig
);
810 } else if (exponent
> 9) {
811 int dig
{exponent
/ 10};
812 buffer
[got
++] = '0' + dig
;
813 buffer
[got
++] = '0' + (exponent
- 10 * dig
);
815 buffer
[got
++] = '0' + exponent
;
819 converted
= decimal::ConvertToBinary
<binaryPrecision
>(p
, edit
.modes
.round
);
821 converted
.flags
= static_cast<enum decimal::ConversionResultFlags
>(
822 converted
.flags
| decimal::Inexact
);
825 if (*p
) { // unprocessed junk after value
826 const auto &connection
{io
.GetConnectionState()};
827 io
.GetIoErrorHandler().SignalError(IostatBadRealInput
,
828 "Trailing characters after real input data at column %d of record %d",
829 static_cast<int>(connection
.positionInRecord
+ 1),
830 static_cast<int>(connection
.currentRecordNumber
));
833 *reinterpret_cast<decimal::BinaryFloatingPointNumber
<binaryPrecision
> *>(n
) =
835 // Set FP exception flags
836 if (converted
.flags
!= decimal::ConversionResultFlags::Exact
) {
837 if (converted
.flags
& decimal::ConversionResultFlags::Overflow
) {
838 io
.GetIoErrorHandler().SignalError(IostatRealInputOverflow
);
841 RaiseFPExceptions(converted
.flags
);
843 return CheckCompleteListDirectedField(io
, edit
);
847 RT_API_ATTRS
bool EditRealInput(
848 IoStatementState
&io
, const DataEdit
&edit
, void *n
) {
849 switch (edit
.descriptor
) {
850 case DataEdit::ListDirected
:
851 if (IsNamelistNameOrSlash(io
)) {
854 return EditCommonRealInput
<KIND
>(io
, edit
, n
);
855 case DataEdit::ListDirectedRealPart
:
856 case DataEdit::ListDirectedImaginaryPart
:
858 case 'E': // incl. EN, ES, & EX
861 return EditCommonRealInput
<KIND
>(io
, edit
, n
);
863 return EditBOZInput
<1>(io
, edit
, n
,
864 common::BitsForBinaryPrecision(common::PrecisionOfRealKind(KIND
)) >> 3);
866 return EditBOZInput
<3>(io
, edit
, n
,
867 common::BitsForBinaryPrecision(common::PrecisionOfRealKind(KIND
)) >> 3);
869 return EditBOZInput
<4>(io
, edit
, n
,
870 common::BitsForBinaryPrecision(common::PrecisionOfRealKind(KIND
)) >> 3);
871 case 'A': // legacy extension
872 return EditCharacterInput(io
, edit
, reinterpret_cast<char *>(n
), KIND
);
874 io
.GetIoErrorHandler().SignalError(IostatErrorInFormat
,
875 "Data edit descriptor '%c' may not be used for REAL input",
881 // 13.7.3 in Fortran 2018
882 RT_API_ATTRS
bool EditLogicalInput(
883 IoStatementState
&io
, const DataEdit
&edit
, bool &x
) {
884 switch (edit
.descriptor
) {
885 case DataEdit::ListDirected
:
886 if (IsNamelistNameOrSlash(io
)) {
894 io
.GetIoErrorHandler().SignalError(IostatErrorInFormat
,
895 "Data edit descriptor '%c' may not be used for LOGICAL input",
899 Fortran::common::optional
<int> remaining
{io
.CueUpInput(edit
)};
900 Fortran::common::optional
<char32_t
> next
{io
.NextInField(remaining
, edit
)};
901 if (next
&& *next
== '.') { // skip optional period
902 next
= io
.NextInField(remaining
, edit
);
905 io
.GetIoErrorHandler().SignalError("Empty LOGICAL input field");
918 io
.GetIoErrorHandler().SignalError(
919 "Bad character '%lc' in LOGICAL input field", *next
);
922 if (remaining
) { // ignore the rest of a fixed-width field
923 io
.HandleRelativePosition(*remaining
);
924 } else if (edit
.descriptor
== DataEdit::ListDirected
) {
925 while (io
.NextInField(remaining
, edit
)) { // discard rest of field
928 return CheckCompleteListDirectedField(io
, edit
);
931 // See 13.10.3.1 paragraphs 7-9 in Fortran 2018
932 template <typename CHAR
>
933 static RT_API_ATTRS
bool EditDelimitedCharacterInput(
934 IoStatementState
&io
, CHAR
*x
, std::size_t length
, char32_t delimiter
) {
937 std::size_t byteCount
{0};
938 auto ch
{io
.GetCurrentChar(byteCount
)};
940 if (io
.AdvanceRecord()) {
943 result
= false; // EOF in character value
947 io
.HandleRelativePosition(byteCount
);
948 if (*ch
== delimiter
) {
949 auto next
{io
.GetCurrentChar(byteCount
)};
950 if (next
&& *next
== delimiter
) {
951 // Repeated delimiter: use as character value
952 io
.HandleRelativePosition(byteCount
);
954 break; // closing delimiter
962 Fortran::runtime::fill_n(x
, length
, ' ');
966 template <typename CHAR
>
967 static RT_API_ATTRS
bool EditListDirectedCharacterInput(
968 IoStatementState
&io
, CHAR
*x
, std::size_t length
, const DataEdit
&edit
) {
969 std::size_t byteCount
{0};
970 auto ch
{io
.GetCurrentChar(byteCount
)};
971 if (ch
&& (*ch
== '\'' || *ch
== '"')) {
972 io
.HandleRelativePosition(byteCount
);
973 return EditDelimitedCharacterInput(io
, x
, length
, *ch
);
975 if (IsNamelistNameOrSlash(io
) || io
.GetConnectionState().IsAtEOF()) {
978 // Undelimited list-directed character input: stop at a value separator
979 // or the end of the current record. Subtlety: the "remaining" count
980 // here is a dummy that's used to avoid the interpretation of separators
982 Fortran::common::optional
<int> remaining
{length
> 0 ? maxUTF8Bytes
: 0};
983 while (Fortran::common::optional
<char32_t
> next
{
984 io
.NextInField(remaining
, edit
)}) {
994 isSep
= edit
.IsNamelist();
997 isSep
= !(edit
.modes
.editingFlags
& decimalComma
);
1000 isSep
= !!(edit
.modes
.editingFlags
& decimalComma
);
1009 remaining
= --length
> 0 ? maxUTF8Bytes
: 0;
1012 Fortran::runtime::fill_n(x
, length
, ' ');
1016 template <typename CHAR
>
1017 RT_API_ATTRS
bool EditCharacterInput(IoStatementState
&io
, const DataEdit
&edit
,
1018 CHAR
*x
, std::size_t lengthChars
) {
1019 switch (edit
.descriptor
) {
1020 case DataEdit::ListDirected
:
1021 return EditListDirectedCharacterInput(io
, x
, lengthChars
, edit
);
1026 return EditBOZInput
<1>(io
, edit
, x
, lengthChars
* sizeof *x
);
1028 return EditBOZInput
<3>(io
, edit
, x
, lengthChars
* sizeof *x
);
1030 return EditBOZInput
<4>(io
, edit
, x
, lengthChars
* sizeof *x
);
1032 io
.GetIoErrorHandler().SignalError(IostatErrorInFormat
,
1033 "Data edit descriptor '%c' may not be used with a CHARACTER data item",
1037 const ConnectionState
&connection
{io
.GetConnectionState()};
1038 std::size_t remainingChars
{lengthChars
};
1039 // Skip leading characters.
1040 // Their bytes don't count towards INQUIRE(IOLENGTH=).
1041 std::size_t skipChars
{0};
1042 if (edit
.width
&& *edit
.width
> 0) {
1043 remainingChars
= *edit
.width
;
1044 if (remainingChars
> lengthChars
) {
1045 skipChars
= remainingChars
- lengthChars
;
1048 // When the field is wider than the variable, we drop the leading
1049 // characters. When the variable is wider than the field, there can be
1050 // trailing padding or an EOR condition.
1051 const char *input
{nullptr};
1052 std::size_t readyBytes
{0};
1053 // Transfer payload bytes; these do count.
1054 while (remainingChars
> 0) {
1055 if (readyBytes
== 0) {
1056 readyBytes
= io
.GetNextInputBytes(input
);
1057 if (readyBytes
== 0 ||
1058 (readyBytes
< remainingChars
&& edit
.modes
.nonAdvancing
)) {
1059 if (io
.CheckForEndOfRecord(readyBytes
)) {
1060 if (readyBytes
== 0) {
1061 // PAD='YES' and no more data
1062 Fortran::runtime::fill_n(x
, lengthChars
, ' ');
1063 return !io
.GetIoErrorHandler().InError();
1065 // Do partial read(s) then pad on last iteration
1068 return !io
.GetIoErrorHandler().InError();
1072 std::size_t chunkBytes
;
1073 std::size_t chunkChars
{1};
1074 bool skipping
{skipChars
> 0};
1075 if (connection
.isUTF8
) {
1076 chunkBytes
= MeasureUTF8Bytes(*input
);
1079 } else if (auto ucs
{DecodeUTF8(input
)}) {
1080 if ((sizeof *x
== 1 && *ucs
> 0xff) ||
1081 (sizeof *x
== 2 && *ucs
> 0xffff)) {
1087 } else if (chunkBytes
== 0) {
1088 // error recovery: skip bad encoding
1091 } else if (connection
.internalIoCharKind
> 1) {
1092 // Reading from non-default character internal unit
1093 chunkBytes
= connection
.internalIoCharKind
;
1098 std::memcpy(&buffer
, input
, chunkBytes
);
1099 if ((sizeof *x
== 1 && buffer
> 0xff) ||
1100 (sizeof *x
== 2 && buffer
> 0xffff)) {
1107 } else if constexpr (sizeof *x
> 1) {
1108 // Read single byte with expansion into multi-byte CHARACTER
1113 *x
++ = static_cast<unsigned char>(*input
);
1116 } else { // single bytes -> default CHARACTER
1118 chunkBytes
= std::min
<std::size_t>(skipChars
, readyBytes
);
1119 chunkChars
= chunkBytes
;
1120 skipChars
-= chunkChars
;
1122 chunkBytes
= std::min
<std::size_t>(remainingChars
, readyBytes
);
1123 chunkBytes
= std::min
<std::size_t>(lengthChars
, chunkBytes
);
1124 chunkChars
= chunkBytes
;
1125 std::memcpy(x
, input
, chunkBytes
);
1127 lengthChars
-= chunkChars
;
1130 input
+= chunkBytes
;
1131 remainingChars
-= chunkChars
;
1133 io
.GotChar(chunkBytes
);
1135 io
.HandleRelativePosition(chunkBytes
);
1136 readyBytes
-= chunkBytes
;
1138 // Pad the remainder of the input variable, if any.
1139 Fortran::runtime::fill_n(x
, lengthChars
, ' ');
1140 return CheckCompleteListDirectedField(io
, edit
);
1143 template RT_API_ATTRS
bool EditRealInput
<2>(
1144 IoStatementState
&, const DataEdit
&, void *);
1145 template RT_API_ATTRS
bool EditRealInput
<3>(
1146 IoStatementState
&, const DataEdit
&, void *);
1147 template RT_API_ATTRS
bool EditRealInput
<4>(
1148 IoStatementState
&, const DataEdit
&, void *);
1149 template RT_API_ATTRS
bool EditRealInput
<8>(
1150 IoStatementState
&, const DataEdit
&, void *);
1151 template RT_API_ATTRS
bool EditRealInput
<10>(
1152 IoStatementState
&, const DataEdit
&, void *);
1153 // TODO: double/double
1154 template RT_API_ATTRS
bool EditRealInput
<16>(
1155 IoStatementState
&, const DataEdit
&, void *);
1157 template RT_API_ATTRS
bool EditCharacterInput(
1158 IoStatementState
&, const DataEdit
&, char *, std::size_t);
1159 template RT_API_ATTRS
bool EditCharacterInput(
1160 IoStatementState
&, const DataEdit
&, char16_t
*, std::size_t);
1161 template RT_API_ATTRS
bool EditCharacterInput(
1162 IoStatementState
&, const DataEdit
&, char32_t
*, std::size_t);
1164 RT_OFFLOAD_API_GROUP_END
1165 } // namespace Fortran::runtime::io