xref: /llvm-project/flang/runtime/edit-input.cpp (revision 7463b46a34efedbc3a72d05721886284e80b5cce)
1651f58bfSDiana Picus //===-- runtime/edit-input.cpp --------------------------------------------===//
23b635714Speter klausler //
33b635714Speter klausler // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
43b635714Speter klausler // See https://llvm.org/LICENSE.txt for license information.
53b635714Speter klausler // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
63b635714Speter klausler //
73b635714Speter klausler //===----------------------------------------------------------------------===//
83b635714Speter klausler 
93b635714Speter klausler #include "edit-input.h"
10b8452dbaSpeter klausler #include "namelist.h"
11bafbae23SPeter Klausler #include "utf.h"
1271e0261fSSlava Zakharin #include "flang/Common/optional.h"
133b635714Speter klausler #include "flang/Common/real.h"
143b635714Speter klausler #include "flang/Common/uint128.h"
153b337242SSlava Zakharin #include "flang/Runtime/freestanding-tools.h"
16231fae90SIsuru Fernando #include <algorithm>
17de026aebSPeter Klausler #include <cfenv>
183b635714Speter klausler 
193b635714Speter klausler namespace Fortran::runtime::io {
208ebf7411SSlava Zakharin RT_OFFLOAD_API_GROUP_BEGIN
213b635714Speter klausler 
2237ea42b2SPeter Klausler // Checks that a list-directed input value has been entirely consumed and
2337ea42b2SPeter Klausler // doesn't contain unparsed characters before the next value separator.
248ebf7411SSlava Zakharin static inline RT_API_ATTRS bool IsCharValueSeparator(
258ebf7411SSlava Zakharin     const DataEdit &edit, char32_t ch) {
2637ea42b2SPeter Klausler   char32_t comma{
2737ea42b2SPeter Klausler       edit.modes.editingFlags & decimalComma ? char32_t{';'} : char32_t{','}};
28120ad250SPeter Klausler   return ch == ' ' || ch == '\t' || ch == comma || ch == '/' ||
29120ad250SPeter Klausler       (edit.IsNamelist() && (ch == '&' || ch == '$'));
3037ea42b2SPeter Klausler }
3137ea42b2SPeter Klausler 
328ebf7411SSlava Zakharin static RT_API_ATTRS bool CheckCompleteListDirectedField(
3337ea42b2SPeter Klausler     IoStatementState &io, const DataEdit &edit) {
3437ea42b2SPeter Klausler   if (edit.IsListDirected()) {
3537ea42b2SPeter Klausler     std::size_t byteCount;
3637ea42b2SPeter Klausler     if (auto ch{io.GetCurrentChar(byteCount)}) {
3737ea42b2SPeter Klausler       if (IsCharValueSeparator(edit, *ch)) {
3837ea42b2SPeter Klausler         return true;
3937ea42b2SPeter Klausler       } else {
4037ea42b2SPeter Klausler         const auto &connection{io.GetConnectionState()};
4137ea42b2SPeter Klausler         io.GetIoErrorHandler().SignalError(IostatBadListDirectedInputSeparator,
4237ea42b2SPeter Klausler             "invalid character (0x%x) after list-directed input value, "
4337ea42b2SPeter Klausler             "at column %d in record %d",
4437ea42b2SPeter Klausler             static_cast<unsigned>(*ch),
4537ea42b2SPeter Klausler             static_cast<int>(connection.positionInRecord + 1),
4637ea42b2SPeter Klausler             static_cast<int>(connection.currentRecordNumber));
4737ea42b2SPeter Klausler         return false;
4837ea42b2SPeter Klausler       }
4937ea42b2SPeter Klausler     } else {
5037ea42b2SPeter Klausler       return true; // end of record: ok
5137ea42b2SPeter Klausler     }
5237ea42b2SPeter Klausler   } else {
5337ea42b2SPeter Klausler     return true;
5437ea42b2SPeter Klausler   }
5537ea42b2SPeter Klausler }
5637ea42b2SPeter Klausler 
577aad8731SPeter Klausler static inline RT_API_ATTRS char32_t GetSeparatorChar(const DataEdit &edit) {
587aad8731SPeter Klausler   return edit.modes.editingFlags & decimalComma ? char32_t{';'} : char32_t{','};
597aad8731SPeter Klausler }
607aad8731SPeter Klausler 
6153f775bbSPeter Klausler template <int LOG2_BASE>
628ebf7411SSlava Zakharin static RT_API_ATTRS bool EditBOZInput(
6353f775bbSPeter Klausler     IoStatementState &io, const DataEdit &edit, void *n, std::size_t bytes) {
64bad52055SPeter Klausler   // Skip leading white space & zeroes
6571e0261fSSlava Zakharin   Fortran::common::optional<int> remaining{io.CueUpInput(edit)};
66bad52055SPeter Klausler   auto start{io.GetConnectionState().positionInRecord};
6771e0261fSSlava Zakharin   Fortran::common::optional<char32_t> next{io.NextInField(remaining, edit)};
68850097d6SPeter Klausler   if (next.value_or('?') == '0') {
6953f775bbSPeter Klausler     do {
70bad52055SPeter Klausler       start = io.GetConnectionState().positionInRecord;
7153f775bbSPeter Klausler       next = io.NextInField(remaining, edit);
7253f775bbSPeter Klausler     } while (next && *next == '0');
7353f775bbSPeter Klausler   }
7453f775bbSPeter Klausler   // Count significant digits after any leading white space & zeroes
7553f775bbSPeter Klausler   int digits{0};
764f9cb79aSPeter Klausler   int significantBits{0};
777aad8731SPeter Klausler   const char32_t comma{GetSeparatorChar(edit)};
78991696c2SPeter Klausler   for (; next; next = io.NextInField(remaining, edit)) {
793b635714Speter klausler     char32_t ch{*next};
808305a92aSpeter klausler     if (ch == ' ' || ch == '\t') {
814f9cb79aSPeter Klausler       if (edit.modes.editingFlags & blankZero) {
824f9cb79aSPeter Klausler         ch = '0'; // BZ mode - treat blank as if it were zero
834f9cb79aSPeter Klausler       } else {
843b635714Speter klausler         continue;
853b635714Speter klausler       }
864f9cb79aSPeter Klausler     }
873b635714Speter klausler     if (ch >= '0' && ch <= '1') {
8853f775bbSPeter Klausler     } else if (LOG2_BASE >= 3 && ch >= '2' && ch <= '7') {
8953f775bbSPeter Klausler     } else if (LOG2_BASE >= 4 && ch >= '8' && ch <= '9') {
9053f775bbSPeter Klausler     } else if (LOG2_BASE >= 4 && ch >= 'A' && ch <= 'F') {
9153f775bbSPeter Klausler     } else if (LOG2_BASE >= 4 && ch >= 'a' && ch <= 'f') {
927aad8731SPeter Klausler     } else if (ch == comma) {
932e08e821SPeter Klausler       break; // end non-list-directed field early
943b635714Speter klausler     } else {
953b635714Speter klausler       io.GetIoErrorHandler().SignalError(
963b635714Speter klausler           "Bad character '%lc' in B/O/Z input field", ch);
973b635714Speter klausler       return false;
983b635714Speter klausler     }
994f9cb79aSPeter Klausler     if (digits++ == 0) {
1004f9cb79aSPeter Klausler       if (ch >= '0' && ch <= '1') {
1014f9cb79aSPeter Klausler         significantBits = 1;
1024f9cb79aSPeter Klausler       } else if (ch >= '2' && ch <= '3') {
1034f9cb79aSPeter Klausler         significantBits = 2;
1044f9cb79aSPeter Klausler       } else if (ch >= '4' && ch <= '7') {
1054f9cb79aSPeter Klausler         significantBits = 3;
1064f9cb79aSPeter Klausler       } else {
1074f9cb79aSPeter Klausler         significantBits = 4;
1083b635714Speter klausler       }
1094f9cb79aSPeter Klausler     } else {
1104f9cb79aSPeter Klausler       significantBits += LOG2_BASE;
1114f9cb79aSPeter Klausler     }
1124f9cb79aSPeter Klausler   }
1134f9cb79aSPeter Klausler   auto significantBytes{static_cast<std::size_t>(significantBits + 7) / 8};
11453f775bbSPeter Klausler   if (significantBytes > bytes) {
115cdd54cbdSPeter Klausler     io.GetIoErrorHandler().SignalError(IostatBOZInputOverflow,
11653f775bbSPeter Klausler         "B/O/Z input of %d digits overflows %zd-byte variable", digits, bytes);
11753f775bbSPeter Klausler     return false;
11853f775bbSPeter Klausler   }
11953f775bbSPeter Klausler   // Reset to start of significant digits
120bad52055SPeter Klausler   io.HandleAbsolutePosition(start);
12153f775bbSPeter Klausler   remaining.reset();
12253f775bbSPeter Klausler   // Make a second pass now that the digit count is known
12353f775bbSPeter Klausler   std::memset(n, 0, bytes);
12453f775bbSPeter Klausler   int increment{isHostLittleEndian ? -1 : 1};
12553f775bbSPeter Klausler   auto *data{reinterpret_cast<unsigned char *>(n) +
12659531cf0SKelvin Li       (isHostLittleEndian ? significantBytes - 1 : bytes - significantBytes)};
127fc97d2e6SPeter Klausler   int bitsAfterFirstDigit{(digits - 1) * LOG2_BASE};
128fc97d2e6SPeter Klausler   int shift{bitsAfterFirstDigit & 7};
129fc97d2e6SPeter Klausler   if (shift + (significantBits - bitsAfterFirstDigit) > 8) {
130fc97d2e6SPeter Klausler     shift = shift - 8; // misaligned octal
131fc97d2e6SPeter Klausler   }
13253f775bbSPeter Klausler   while (digits > 0) {
133*7463b46aSPeter Klausler     char32_t ch{io.NextInField(remaining, edit).value_or(' ')};
13453f775bbSPeter Klausler     int digit{0};
1354f9cb79aSPeter Klausler     if (ch == ' ' || ch == '\t') {
1364f9cb79aSPeter Klausler       if (edit.modes.editingFlags & blankZero) {
1374f9cb79aSPeter Klausler         ch = '0'; // BZ mode - treat blank as if it were zero
1384f9cb79aSPeter Klausler       } else {
1394f9cb79aSPeter Klausler         continue;
1404f9cb79aSPeter Klausler       }
1414f9cb79aSPeter Klausler     }
1424f9cb79aSPeter Klausler     --digits;
14353f775bbSPeter Klausler     if (ch >= '0' && ch <= '9') {
14453f775bbSPeter Klausler       digit = ch - '0';
14553f775bbSPeter Klausler     } else if (ch >= 'A' && ch <= 'F') {
14653f775bbSPeter Klausler       digit = ch + 10 - 'A';
14753f775bbSPeter Klausler     } else if (ch >= 'a' && ch <= 'f') {
14853f775bbSPeter Klausler       digit = ch + 10 - 'a';
14953f775bbSPeter Klausler     } else {
15053f775bbSPeter Klausler       continue;
15153f775bbSPeter Klausler     }
15253f775bbSPeter Klausler     if (shift < 0) {
1534f9cb79aSPeter Klausler       if (shift + LOG2_BASE > 0) { // misaligned octal
1544f9cb79aSPeter Klausler         *data |= digit >> -shift;
15553f775bbSPeter Klausler       }
1564f9cb79aSPeter Klausler       shift += 8;
15753f775bbSPeter Klausler       data += increment;
15853f775bbSPeter Klausler     }
15953f775bbSPeter Klausler     *data |= digit << shift;
16053f775bbSPeter Klausler     shift -= LOG2_BASE;
16153f775bbSPeter Klausler   }
16237ea42b2SPeter Klausler   return CheckCompleteListDirectedField(io, edit);
1633b635714Speter klausler }
1643b635714Speter klausler 
1658ebf7411SSlava Zakharin static inline RT_API_ATTRS char32_t GetRadixPointChar(const DataEdit &edit) {
166896a543eSPeter Klausler   return edit.modes.editingFlags & decimalComma ? char32_t{','} : char32_t{'.'};
167896a543eSPeter Klausler }
168896a543eSPeter Klausler 
16937ea42b2SPeter Klausler // Prepares input from a field, and returns the sign, if any, else '\0'.
1708ebf7411SSlava Zakharin static RT_API_ATTRS char ScanNumericPrefix(IoStatementState &io,
1718ebf7411SSlava Zakharin     const DataEdit &edit, Fortran::common::optional<char32_t> &next,
17271e0261fSSlava Zakharin     Fortran::common::optional<int> &remaining) {
173bad52055SPeter Klausler   remaining = io.CueUpInput(edit);
174bad52055SPeter Klausler   next = io.NextInField(remaining, edit);
17537ea42b2SPeter Klausler   char sign{'\0'};
1763b635714Speter klausler   if (next) {
17737ea42b2SPeter Klausler     if (*next == '-' || *next == '+') {
17837ea42b2SPeter Klausler       sign = *next;
17937ea42b2SPeter Klausler       if (!edit.IsListDirected()) {
1807c5630feSpeter klausler         io.SkipSpaces(remaining);
18137ea42b2SPeter Klausler       }
182991696c2SPeter Klausler       next = io.NextInField(remaining, edit);
1833b635714Speter klausler     }
1843b635714Speter klausler   }
18537ea42b2SPeter Klausler   return sign;
1863b635714Speter klausler }
1873b635714Speter klausler 
188fc97d2e6SPeter Klausler RT_API_ATTRS bool EditIntegerInput(IoStatementState &io, const DataEdit &edit,
189fc97d2e6SPeter Klausler     void *n, int kind, bool isSigned) {
1903b635714Speter klausler   RUNTIME_CHECK(io.GetIoErrorHandler(), kind >= 1 && !(kind & (kind - 1)));
1913b635714Speter klausler   switch (edit.descriptor) {
1923b635714Speter klausler   case DataEdit::ListDirected:
193514b759aSPeter Klausler     if (IsNamelistNameOrSlash(io)) {
194b8452dbaSpeter klausler       return false;
195b8452dbaSpeter klausler     }
196b8452dbaSpeter klausler     break;
1973b635714Speter klausler   case 'G':
1981f879005STim Keith   case 'I':
1991f879005STim Keith     break;
2001f879005STim Keith   case 'B':
20153f775bbSPeter Klausler     return EditBOZInput<1>(io, edit, n, kind);
2021f879005STim Keith   case 'O':
20353f775bbSPeter Klausler     return EditBOZInput<3>(io, edit, n, kind);
2041f879005STim Keith   case 'Z':
20553f775bbSPeter Klausler     return EditBOZInput<4>(io, edit, n, kind);
206b83242e2Speter klausler   case 'A': // legacy extension
207bafbae23SPeter Klausler     return EditCharacterInput(io, edit, reinterpret_cast<char *>(n), kind);
2083b635714Speter klausler   default:
2093b635714Speter klausler     io.GetIoErrorHandler().SignalError(IostatErrorInFormat,
2103b635714Speter klausler         "Data edit descriptor '%c' may not be used with an INTEGER data item",
2113b635714Speter klausler         edit.descriptor);
2123b635714Speter klausler     return false;
2133b635714Speter klausler   }
21471e0261fSSlava Zakharin   Fortran::common::optional<int> remaining;
21571e0261fSSlava Zakharin   Fortran::common::optional<char32_t> next;
21637ea42b2SPeter Klausler   char sign{ScanNumericPrefix(io, edit, next, remaining)};
217fc97d2e6SPeter Klausler   if (sign == '-' && !isSigned) {
218fc97d2e6SPeter Klausler     io.GetIoErrorHandler().SignalError("Negative sign in UNSIGNED input field");
219fc97d2e6SPeter Klausler     return false;
220fc97d2e6SPeter Klausler   }
2210f5c60f1SPeter Klausler   common::UnsignedInt128 value{0};
22237ea42b2SPeter Klausler   bool any{!!sign};
223cdd54cbdSPeter Klausler   bool overflow{false};
2247aad8731SPeter Klausler   const char32_t comma{GetSeparatorChar(edit)};
225fc97d2e6SPeter Klausler   static constexpr auto maxu128{~common::UnsignedInt128{0}};
226991696c2SPeter Klausler   for (; next; next = io.NextInField(remaining, edit)) {
2273b635714Speter klausler     char32_t ch{*next};
2288305a92aSpeter klausler     if (ch == ' ' || ch == '\t') {
2293b635714Speter klausler       if (edit.modes.editingFlags & blankZero) {
2303b635714Speter klausler         ch = '0'; // BZ mode - treat blank as if it were zero
2313b635714Speter klausler       } else {
2323b635714Speter klausler         continue;
2333b635714Speter klausler       }
2343b635714Speter klausler     }
2353b635714Speter klausler     int digit{0};
2363b635714Speter klausler     if (ch >= '0' && ch <= '9') {
2373b635714Speter klausler       digit = ch - '0';
2387aad8731SPeter Klausler     } else if (ch == comma) {
2392e08e821SPeter Klausler       break; // end non-list-directed field early
2403b635714Speter klausler     } else {
2417aad8731SPeter Klausler       if (edit.modes.inNamelist && ch == GetRadixPointChar(edit)) {
2427aad8731SPeter Klausler         // Ignore any fractional part that might appear in NAMELIST integer
2437aad8731SPeter Klausler         // input, like a few other Fortran compilers do.
2447aad8731SPeter Klausler         // TODO: also process exponents?  Some compilers do, but they obviously
2457aad8731SPeter Klausler         // can't just be ignored.
2467aad8731SPeter Klausler         while ((next = io.NextInField(remaining, edit))) {
2477aad8731SPeter Klausler           if (*next < '0' || *next > '9') {
2487aad8731SPeter Klausler             break;
2497aad8731SPeter Klausler           }
2507aad8731SPeter Klausler         }
2517aad8731SPeter Klausler         if (!next || *next == comma) {
2527aad8731SPeter Klausler           break;
2537aad8731SPeter Klausler         }
2547aad8731SPeter Klausler       }
2553b635714Speter klausler       io.GetIoErrorHandler().SignalError(
2563b635714Speter klausler           "Bad character '%lc' in INTEGER input field", ch);
2573b635714Speter klausler       return false;
2583b635714Speter klausler     }
259cdd54cbdSPeter Klausler     static constexpr auto maxu128OverTen{maxu128 / 10};
260cdd54cbdSPeter Klausler     static constexpr int maxLastDigit{
261cdd54cbdSPeter Klausler         static_cast<int>(maxu128 - (maxu128OverTen * 10))};
262cdd54cbdSPeter Klausler     overflow |= value >= maxu128OverTen &&
263cdd54cbdSPeter Klausler         (value > maxu128OverTen || digit > maxLastDigit);
2643b635714Speter klausler     value *= 10;
2653b635714Speter klausler     value += digit;
266ac4202feSPeter Klausler     any = true;
2673b635714Speter klausler   }
268514b759aSPeter Klausler   if (!any && !remaining) {
269514b759aSPeter Klausler     io.GetIoErrorHandler().SignalError(
270514b759aSPeter Klausler         "Integer value absent from NAMELIST or list-directed input");
271514b759aSPeter Klausler     return false;
272514b759aSPeter Klausler   }
273fc97d2e6SPeter Klausler   if (isSigned) {
274cdd54cbdSPeter Klausler     auto maxForKind{common::UnsignedInt128{1} << ((8 * kind) - 1)};
27537ea42b2SPeter Klausler     overflow |= value >= maxForKind && (value > maxForKind || sign != '-');
276fc97d2e6SPeter Klausler   } else {
277fc97d2e6SPeter Klausler     auto maxForKind{maxu128 >> (((16 - kind) * 8) + (isSigned ? 1 : 0))};
278fc97d2e6SPeter Klausler     overflow |= value >= maxForKind;
279fc97d2e6SPeter Klausler   }
280cdd54cbdSPeter Klausler   if (overflow) {
281cdd54cbdSPeter Klausler     io.GetIoErrorHandler().SignalError(IostatIntegerInputOverflow,
282cdd54cbdSPeter Klausler         "Decimal input overflows INTEGER(%d) variable", kind);
283cdd54cbdSPeter Klausler     return false;
284cdd54cbdSPeter Klausler   }
28537ea42b2SPeter Klausler   if (sign == '-') {
2863b635714Speter klausler     value = -value;
2873b635714Speter klausler   }
2889fd03cb6SPeter Klausler   if (any || !io.GetIoErrorHandler().InError()) {
289fdccfa33Smadanial0     // The value is stored in the lower order bits on big endian platform.
290fdccfa33Smadanial0     // When memcpy, shift the value to the higher order bit.
291fdccfa33Smadanial0     auto shft{static_cast<int>(sizeof(value.low())) - kind};
292fdccfa33Smadanial0     // For kind==8 (i.e. shft==0), the value is stored in low_ in big endian.
293fdccfa33Smadanial0     if (!isHostLittleEndian && shft >= 0) {
294fdccfa33Smadanial0       auto l{value.low() << (8 * shft)};
295fdccfa33Smadanial0       std::memcpy(n, &l, kind);
296fdccfa33Smadanial0     } else {
2970f5c60f1SPeter Klausler       std::memcpy(n, &value, kind); // a blank field means zero
298ac4202feSPeter Klausler     }
2999fd03cb6SPeter Klausler     return true;
3009fd03cb6SPeter Klausler   } else {
3019fd03cb6SPeter Klausler     return false;
302fdccfa33Smadanial0   }
3033b635714Speter klausler }
3043b635714Speter klausler 
3057c5630feSpeter klausler // Parses a REAL input number from the input source as a normalized
3067c5630feSpeter klausler // fraction into a supplied buffer -- there's an optional '-', a
307ea7e50cdSPeter Klausler // decimal point when the input is not hexadecimal, and at least one
308ea7e50cdSPeter Klausler // digit.  Replaces blanks with zeroes where appropriate.
309ea7e50cdSPeter Klausler struct ScannedRealInput {
310ea7e50cdSPeter Klausler   // Number of characters that (should) have been written to the
311ea7e50cdSPeter Klausler   // buffer -- this can be larger than the buffer size, which
312ea7e50cdSPeter Klausler   // indicates buffer overflow.  Zero indicates an error.
313ea7e50cdSPeter Klausler   int got{0};
314ea7e50cdSPeter Klausler   int exponent{0}; // adjusted as necessary; binary if isHexadecimal
315ea7e50cdSPeter Klausler   bool isHexadecimal{false}; // 0X...
316ea7e50cdSPeter Klausler };
3178ebf7411SSlava Zakharin static RT_API_ATTRS ScannedRealInput ScanRealInput(
318ea7e50cdSPeter Klausler     char *buffer, int bufferSize, IoStatementState &io, const DataEdit &edit) {
31971e0261fSSlava Zakharin   Fortran::common::optional<int> remaining;
32071e0261fSSlava Zakharin   Fortran::common::optional<char32_t> next;
3213b635714Speter klausler   int got{0};
32271e0261fSSlava Zakharin   Fortran::common::optional<int> radixPointOffset;
3238ebf7411SSlava Zakharin   // The following lambda definition violates the conding style,
3248ebf7411SSlava Zakharin   // but cuda-11.8 nvcc hits an internal error with the brace initialization.
3258ebf7411SSlava Zakharin   auto Put = [&](char ch) -> void {
3263b635714Speter klausler     if (got < bufferSize) {
3277c5630feSpeter klausler       buffer[got] = ch;
3283b635714Speter klausler     }
3297c5630feSpeter klausler     ++got;
3308ebf7411SSlava Zakharin   };
33137ea42b2SPeter Klausler   char sign{ScanNumericPrefix(io, edit, next, remaining)};
33237ea42b2SPeter Klausler   if (sign == '-') {
3337c5630feSpeter klausler     Put('-');
3343b635714Speter klausler   }
335e6873bfbSPeter Klausler   bool bzMode{(edit.modes.editingFlags & blankZero) != 0};
336ea7e50cdSPeter Klausler   int exponent{0};
3372e08e821SPeter Klausler   if (!next || (!bzMode && *next == ' ') ||
3382e08e821SPeter Klausler       (!(edit.modes.editingFlags & decimalComma) && *next == ',')) {
33937ea42b2SPeter Klausler     if (!edit.IsListDirected() && !io.GetConnectionState().IsAtEOF()) {
34037ea42b2SPeter Klausler       // An empty/blank field means zero when not list-directed.
34137ea42b2SPeter Klausler       // A fixed-width field containing only a sign is also zero;
34237ea42b2SPeter Klausler       // this behavior isn't standard-conforming in F'2023 but it is
34337ea42b2SPeter Klausler       // required to pass FCVS.
3447c5630feSpeter klausler       Put('0');
3450f5c60f1SPeter Klausler     }
346ea7e50cdSPeter Klausler     return {got, exponent, false};
3473b635714Speter klausler   }
348ea7e50cdSPeter Klausler   char32_t radixPointChar{GetRadixPointChar(edit)};
3497c5630feSpeter klausler   char32_t first{*next >= 'a' && *next <= 'z' ? *next + 'A' - 'a' : *next};
350ea7e50cdSPeter Klausler   bool isHexadecimal{false};
3517c5630feSpeter klausler   if (first == 'N' || first == 'I') {
3523b635714Speter klausler     // NaN or infinity - convert to upper case
3537c5630feSpeter klausler     // Subtle: a blank field of digits could be followed by 'E' or 'D',
3543b635714Speter klausler     for (; next &&
3553b635714Speter klausler          ((*next >= 'a' && *next <= 'z') || (*next >= 'A' && *next <= 'Z'));
356991696c2SPeter Klausler          next = io.NextInField(remaining, edit)) {
3573b635714Speter klausler       if (*next >= 'a' && *next <= 'z') {
3587c5630feSpeter klausler         Put(*next - 'a' + 'A');
3593b635714Speter klausler       } else {
3607c5630feSpeter klausler         Put(*next);
3613b635714Speter klausler       }
3623b635714Speter klausler     }
3633b635714Speter klausler     if (next && *next == '(') { // NaN(...)
364f1dbf8e4SPeter Klausler       Put('(');
365f1dbf8e4SPeter Klausler       int depth{1};
3662f31b4b1SPeter Klausler       while (true) {
367991696c2SPeter Klausler         next = io.NextInField(remaining, edit);
3682f31b4b1SPeter Klausler         if (depth == 0) {
369f1dbf8e4SPeter Klausler           break;
3702f31b4b1SPeter Klausler         } else if (!next) {
371ea7e50cdSPeter Klausler           return {}; // error
372f1dbf8e4SPeter Klausler         } else if (*next == '(') {
373f1dbf8e4SPeter Klausler           ++depth;
374f1dbf8e4SPeter Klausler         } else if (*next == ')') {
375f1dbf8e4SPeter Klausler           --depth;
3763b635714Speter klausler         }
377f1dbf8e4SPeter Klausler         Put(*next);
3782f31b4b1SPeter Klausler       }
3793b635714Speter klausler     }
380ea7e50cdSPeter Klausler   } else if (first == radixPointChar || (first >= '0' && first <= '9') ||
381e6873bfbSPeter Klausler       (bzMode && (first == ' ' || first == '\t')) || first == 'E' ||
382e6873bfbSPeter Klausler       first == 'D' || first == 'Q') {
383ea7e50cdSPeter Klausler     if (first == '0') {
384ea7e50cdSPeter Klausler       next = io.NextInField(remaining, edit);
385ea7e50cdSPeter Klausler       if (next && (*next == 'x' || *next == 'X')) { // 0X...
386ea7e50cdSPeter Klausler         isHexadecimal = true;
387ea7e50cdSPeter Klausler         next = io.NextInField(remaining, edit);
388ea7e50cdSPeter Klausler       } else {
389ea7e50cdSPeter Klausler         Put('0');
390ea7e50cdSPeter Klausler       }
391ea7e50cdSPeter Klausler     }
392ea7e50cdSPeter Klausler     // input field is normalized to a fraction
393ea7e50cdSPeter Klausler     if (!isHexadecimal) {
394ea7e50cdSPeter Klausler       Put('.');
395ea7e50cdSPeter Klausler     }
3967c5630feSpeter klausler     auto start{got};
397991696c2SPeter Klausler     for (; next; next = io.NextInField(remaining, edit)) {
3983b635714Speter klausler       char32_t ch{*next};
3998305a92aSpeter klausler       if (ch == ' ' || ch == '\t') {
400ea7e50cdSPeter Klausler         if (isHexadecimal) {
401ea7e50cdSPeter Klausler           return {}; // error
402ea7e50cdSPeter Klausler         } else if (bzMode) {
4033b635714Speter klausler           ch = '0'; // BZ mode - treat blank as if it were zero
4043b635714Speter klausler         } else {
405ea7e50cdSPeter Klausler           continue; // ignore blank in fixed field
4063b635714Speter klausler         }
4073b635714Speter klausler       }
408ea7e50cdSPeter Klausler       if (ch == '0' && got == start && !radixPointOffset) {
409ea7e50cdSPeter Klausler         // omit leading zeroes before the radix point
4103b635714Speter klausler       } else if (ch >= '0' && ch <= '9') {
4117c5630feSpeter klausler         Put(ch);
412ea7e50cdSPeter Klausler       } else if (ch == radixPointChar && !radixPointOffset) {
413ea7e50cdSPeter Klausler         // The radix point character is *not* copied to the buffer.
414ea7e50cdSPeter Klausler         radixPointOffset = got - start; // # of digits before the radix point
415ea7e50cdSPeter Klausler       } else if (isHexadecimal && ch >= 'A' && ch <= 'F') {
416ea7e50cdSPeter Klausler         Put(ch);
417ea7e50cdSPeter Klausler       } else if (isHexadecimal && ch >= 'a' && ch <= 'f') {
418ea7e50cdSPeter Klausler         Put(ch - 'a' + 'A'); // normalize to capitals
4193b635714Speter klausler       } else {
4203b635714Speter klausler         break;
4213b635714Speter klausler       }
4223b635714Speter klausler     }
4237c5630feSpeter klausler     if (got == start) {
424ea7e50cdSPeter Klausler       // Nothing but zeroes and maybe a radix point.  F'2018 requires
425839f0abdSPeter Klausler       // at least one digit, but F'77 did not, and a bare "." shows up in
426839f0abdSPeter Klausler       // the FCVS suite.
4277c5630feSpeter klausler       Put('0'); // emit at least one digit
4283b635714Speter klausler     }
42937ea42b2SPeter Klausler     // In list-directed input, a bad exponent is not consumed.
43037ea42b2SPeter Klausler     auto nextBeforeExponent{next};
43137ea42b2SPeter Klausler     auto startExponent{io.GetConnectionState().positionInRecord};
43237ea42b2SPeter Klausler     bool hasGoodExponent{false};
433ea7e50cdSPeter Klausler     if (next) {
434ea7e50cdSPeter Klausler       if (isHexadecimal) {
435ea7e50cdSPeter Klausler         if (*next == 'p' || *next == 'P') {
436ea7e50cdSPeter Klausler           next = io.NextInField(remaining, edit);
437ea7e50cdSPeter Klausler         } else {
438ea7e50cdSPeter Klausler           // The binary exponent is not optional in the standard.
439ea7e50cdSPeter Klausler           return {}; // error
440ea7e50cdSPeter Klausler         }
441ea7e50cdSPeter Klausler       } else if (*next == 'e' || *next == 'E' || *next == 'd' || *next == 'D' ||
442ea7e50cdSPeter Klausler           *next == 'q' || *next == 'Q') {
443b2cf572bSpeter klausler         // Optional exponent letter.  Blanks are allowed between the
444b2cf572bSpeter klausler         // optional exponent letter and the exponent value.
4453b635714Speter klausler         io.SkipSpaces(remaining);
446991696c2SPeter Klausler         next = io.NextInField(remaining, edit);
4473b635714Speter klausler       }
448ea7e50cdSPeter Klausler     }
4493b635714Speter klausler     if (next &&
450b2cf572bSpeter klausler         (*next == '-' || *next == '+' || (*next >= '0' && *next <= '9') ||
451f1dbf8e4SPeter Klausler             *next == ' ' || *next == '\t')) {
4523b635714Speter klausler       bool negExpo{*next == '-'};
4533b635714Speter klausler       if (negExpo || *next == '+') {
454991696c2SPeter Klausler         next = io.NextInField(remaining, edit);
4553b635714Speter klausler       }
456ea7e50cdSPeter Klausler       for (; next; next = io.NextInField(remaining, edit)) {
457b2cf572bSpeter klausler         if (*next >= '0' && *next <= '9') {
45837ea42b2SPeter Klausler           hasGoodExponent = true;
4594c42e67bSPeter Klausler           if (exponent < 10000) {
4603b635714Speter klausler             exponent = 10 * exponent + *next - '0';
4614c42e67bSPeter Klausler           }
462f1dbf8e4SPeter Klausler         } else if (*next == ' ' || *next == '\t') {
463ea7e50cdSPeter Klausler           if (isHexadecimal) {
464ea7e50cdSPeter Klausler             break;
465ea7e50cdSPeter Klausler           } else if (bzMode) {
46637ea42b2SPeter Klausler             hasGoodExponent = true;
467b2cf572bSpeter klausler             exponent = 10 * exponent;
468f1dbf8e4SPeter Klausler           }
469b2cf572bSpeter klausler         } else {
470b2cf572bSpeter klausler           break;
471b2cf572bSpeter klausler         }
4723b635714Speter klausler       }
4733b635714Speter klausler       if (negExpo) {
4743b635714Speter klausler         exponent = -exponent;
4753b635714Speter klausler       }
4763b635714Speter klausler     }
47737ea42b2SPeter Klausler     if (!hasGoodExponent) {
478ea7e50cdSPeter Klausler       if (isHexadecimal) {
479ea7e50cdSPeter Klausler         return {}; // error
480ea7e50cdSPeter Klausler       }
48137ea42b2SPeter Klausler       // There isn't a good exponent; do not consume it.
48237ea42b2SPeter Klausler       next = nextBeforeExponent;
48337ea42b2SPeter Klausler       io.HandleAbsolutePosition(startExponent);
484ea7e50cdSPeter Klausler       // The default exponent is -kP, but the scale factor doesn't affect
485ea7e50cdSPeter Klausler       // an explicit exponent.
486ea7e50cdSPeter Klausler       exponent = -edit.modes.scale;
48737ea42b2SPeter Klausler     }
488ea7e50cdSPeter Klausler     // Adjust exponent by number of digits before the radix point.
489ea7e50cdSPeter Klausler     if (isHexadecimal) {
490ea7e50cdSPeter Klausler       // Exponents for hexadecimal input are binary.
491ea7e50cdSPeter Klausler       exponent += radixPointOffset.value_or(got - start) * 4;
492ea7e50cdSPeter Klausler     } else if (radixPointOffset) {
493ea7e50cdSPeter Klausler       exponent += *radixPointOffset;
4943b635714Speter klausler     } else {
495ea7e50cdSPeter Klausler       // When no redix point (or comma) appears in the value, the 'd'
4963b635714Speter klausler       // part of the edit descriptor must be interpreted as the number of
4973b635714Speter klausler       // digits in the value to be interpreted as being to the *right* of
498ea7e50cdSPeter Klausler       // the assumed radix point (13.7.2.3.2)
4993b635714Speter klausler       exponent += got - start - edit.digits.value_or(0);
5003b635714Speter klausler     }
5013b635714Speter klausler   }
5026a1c3efaSpeter klausler   // Consume the trailing ')' of a list-directed or NAMELIST complex
5036a1c3efaSpeter klausler   // input value.
5046a1c3efaSpeter klausler   if (edit.descriptor == DataEdit::ListDirectedImaginaryPart) {
5056a1c3efaSpeter klausler     if (next && (*next == ' ' || *next == '\t')) {
50637ea42b2SPeter Klausler       io.SkipSpaces(remaining);
507991696c2SPeter Klausler       next = io.NextInField(remaining, edit);
5086a1c3efaSpeter klausler     }
5096a1c3efaSpeter klausler     if (!next) { // NextInField fails on separators like ')'
510bafbae23SPeter Klausler       std::size_t byteCount{0};
511bafbae23SPeter Klausler       next = io.GetCurrentChar(byteCount);
5126a1c3efaSpeter klausler       if (next && *next == ')') {
513bafbae23SPeter Klausler         io.HandleRelativePosition(byteCount);
5146a1c3efaSpeter klausler       }
5156a1c3efaSpeter klausler     }
5166a1c3efaSpeter klausler   } else if (remaining) {
5178305a92aSpeter klausler     while (next && (*next == ' ' || *next == '\t')) {
518991696c2SPeter Klausler       next = io.NextInField(remaining, edit);
5193b635714Speter klausler     }
5202e08e821SPeter Klausler     if (next && (*next != ',' || (edit.modes.editingFlags & decimalComma))) {
521ea7e50cdSPeter Klausler       return {}; // error: unused nonblank character in fixed-width field
5223b635714Speter klausler     }
5233b635714Speter klausler   }
524ea7e50cdSPeter Klausler   return {got, exponent, isHexadecimal};
5253b635714Speter klausler }
5263b635714Speter klausler 
5278ebf7411SSlava Zakharin static RT_API_ATTRS void RaiseFPExceptions(
5288ebf7411SSlava Zakharin     decimal::ConversionResultFlags flags) {
529de026aebSPeter Klausler #undef RAISE
5308ebf7411SSlava Zakharin #if defined(RT_DEVICE_COMPILATION)
5318ebf7411SSlava Zakharin   Terminator terminator(__FILE__, __LINE__);
5328ebf7411SSlava Zakharin #define RAISE(e) \
5338ebf7411SSlava Zakharin   terminator.Crash( \
5348ebf7411SSlava Zakharin       "not implemented yet: raising FP exception in device code: %s", #e);
5358ebf7411SSlava Zakharin #else // !defined(RT_DEVICE_COMPILATION)
536de026aebSPeter Klausler #ifdef feraisexcept // a macro in some environments; omit std::
537de026aebSPeter Klausler #define RAISE feraiseexcept
538de026aebSPeter Klausler #else
539de026aebSPeter Klausler #define RAISE std::feraiseexcept
540de026aebSPeter Klausler #endif
5418ebf7411SSlava Zakharin #endif // !defined(RT_DEVICE_COMPILATION)
542b6686e76Sserge-sans-paille 
543b6686e76Sserge-sans-paille // Some environment (e.g. emscripten, musl) don't define FE_OVERFLOW as allowed
544b6686e76Sserge-sans-paille // by c99 (but not c++11) :-/
545b6686e76Sserge-sans-paille #if defined(FE_OVERFLOW) || defined(RT_DEVICE_COMPILATION)
546de026aebSPeter Klausler   if (flags & decimal::ConversionResultFlags::Overflow) {
547de026aebSPeter Klausler     RAISE(FE_OVERFLOW);
548de026aebSPeter Klausler   }
549b6686e76Sserge-sans-paille #endif
550b6686e76Sserge-sans-paille #if defined(FE_UNDERFLOW) || defined(RT_DEVICE_COMPILATION)
551befdfae1SPeter Klausler   if (flags & decimal::ConversionResultFlags::Underflow) {
552befdfae1SPeter Klausler     RAISE(FE_UNDERFLOW);
553befdfae1SPeter Klausler   }
554b6686e76Sserge-sans-paille #endif
555b6686e76Sserge-sans-paille #if defined(FE_INEXACT) || defined(RT_DEVICE_COMPILATION)
556de026aebSPeter Klausler   if (flags & decimal::ConversionResultFlags::Inexact) {
557de026aebSPeter Klausler     RAISE(FE_INEXACT);
558de026aebSPeter Klausler   }
559b6686e76Sserge-sans-paille #endif
560b6686e76Sserge-sans-paille #if defined(FE_INVALID) || defined(RT_DEVICE_COMPILATION)
561de026aebSPeter Klausler   if (flags & decimal::ConversionResultFlags::Invalid) {
562de026aebSPeter Klausler     RAISE(FE_INVALID);
563de026aebSPeter Klausler   }
564b6686e76Sserge-sans-paille #endif
565de026aebSPeter Klausler #undef RAISE
566de026aebSPeter Klausler }
567de026aebSPeter Klausler 
568da25f968SPeter Klausler // If no special modes are in effect and the form of the input value
569da25f968SPeter Klausler // that's present in the input stream is acceptable to the decimal->binary
570da25f968SPeter Klausler // converter without modification, this fast path for real input
571da25f968SPeter Klausler // saves time by avoiding memory copies and reformatting of the exponent.
572da25f968SPeter Klausler template <int PRECISION>
5738ebf7411SSlava Zakharin static RT_API_ATTRS bool TryFastPathRealDecimalInput(
574da25f968SPeter Klausler     IoStatementState &io, const DataEdit &edit, void *n) {
575da25f968SPeter Klausler   if (edit.modes.editingFlags & (blankZero | decimalComma)) {
576da25f968SPeter Klausler     return false;
577da25f968SPeter Klausler   }
578da25f968SPeter Klausler   if (edit.modes.scale != 0) {
579da25f968SPeter Klausler     return false;
580da25f968SPeter Klausler   }
581bad52055SPeter Klausler   const ConnectionState &connection{io.GetConnectionState()};
582bad52055SPeter Klausler   if (connection.internalIoCharKind > 1) {
583bad52055SPeter Klausler     return false; // reading non-default character
584bad52055SPeter Klausler   }
585da25f968SPeter Klausler   const char *str{nullptr};
586da25f968SPeter Klausler   std::size_t got{io.GetNextInputBytes(str)};
587bad52055SPeter Klausler   if (got == 0 || str == nullptr || !connection.recordLength.has_value()) {
588da25f968SPeter Klausler     return false; // could not access reliably-terminated input stream
589da25f968SPeter Klausler   }
590da25f968SPeter Klausler   const char *p{str};
591da25f968SPeter Klausler   std::int64_t maxConsume{
592da25f968SPeter Klausler       std::min<std::int64_t>(got, edit.width.value_or(got))};
593da25f968SPeter Klausler   const char *limit{str + maxConsume};
594da25f968SPeter Klausler   decimal::ConversionToBinaryResult<PRECISION> converted{
595da25f968SPeter Klausler       decimal::ConvertToBinary<PRECISION>(p, edit.modes.round, limit)};
5964c42e67bSPeter Klausler   if (converted.flags & (decimal::Invalid | decimal::Overflow)) {
597da25f968SPeter Klausler     return false;
598da25f968SPeter Klausler   }
599f1dbf8e4SPeter Klausler   if (edit.digits.value_or(0) != 0) {
600f1dbf8e4SPeter Klausler     // Edit descriptor is Fw.d (or other) with d != 0, which
601f1dbf8e4SPeter Klausler     // implies scaling
602f1dbf8e4SPeter Klausler     const char *q{str};
603f1dbf8e4SPeter Klausler     for (; q < limit; ++q) {
604f1dbf8e4SPeter Klausler       if (*q == '.' || *q == 'n' || *q == 'N') {
605f1dbf8e4SPeter Klausler         break;
606f1dbf8e4SPeter Klausler       }
607f1dbf8e4SPeter Klausler     }
608f1dbf8e4SPeter Klausler     if (q == limit) {
609f1dbf8e4SPeter Klausler       // No explicit decimal point, and not NaN/Inf.
610da25f968SPeter Klausler       return false;
611da25f968SPeter Klausler     }
612f1dbf8e4SPeter Klausler   }
61337ea42b2SPeter Klausler   if (edit.descriptor == DataEdit::ListDirectedImaginaryPart) {
61437ea42b2SPeter Klausler     // Need to consume a trailing ')', possibly with leading spaces
615da25f968SPeter Klausler     for (; p < limit && (*p == ' ' || *p == '\t'); ++p) {
616da25f968SPeter Klausler     }
61737ea42b2SPeter Klausler     if (p < limit && *p == ')') {
61837ea42b2SPeter Klausler       ++p;
61937ea42b2SPeter Klausler     } else {
620da25f968SPeter Klausler       return false;
621da25f968SPeter Klausler     }
62237ea42b2SPeter Klausler   } else if (edit.IsListDirected()) {
62337ea42b2SPeter Klausler     if (p < limit && !IsCharValueSeparator(edit, *p)) {
62437ea42b2SPeter Klausler       return false;
625da25f968SPeter Klausler     }
62637ea42b2SPeter Klausler   } else {
62737ea42b2SPeter Klausler     for (; p < limit && (*p == ' ' || *p == '\t'); ++p) {
628da25f968SPeter Klausler     }
629d02b318aSPeter Klausler     if (edit.width && p < str + *edit.width) {
630d02b318aSPeter Klausler       return false; // unconverted characters remain in fixed width field
631da25f968SPeter Klausler     }
63237ea42b2SPeter Klausler   }
633da25f968SPeter Klausler   // Success on the fast path!
634da25f968SPeter Klausler   *reinterpret_cast<decimal::BinaryFloatingPointNumber<PRECISION> *>(n) =
635da25f968SPeter Klausler       converted.binary;
636da25f968SPeter Klausler   io.HandleRelativePosition(p - str);
637de026aebSPeter Klausler   // Set FP exception flags
638de026aebSPeter Klausler   if (converted.flags != decimal::ConversionResultFlags::Exact) {
639de026aebSPeter Klausler     RaiseFPExceptions(converted.flags);
640de026aebSPeter Klausler   }
641da25f968SPeter Klausler   return true;
642da25f968SPeter Klausler }
643da25f968SPeter Klausler 
644ea7e50cdSPeter Klausler template <int binaryPrecision>
6458ebf7411SSlava Zakharin RT_API_ATTRS decimal::ConversionToBinaryResult<binaryPrecision>
6468ebf7411SSlava Zakharin ConvertHexadecimal(
647ea7e50cdSPeter Klausler     const char *&p, enum decimal::FortranRounding rounding, int expo) {
648ea7e50cdSPeter Klausler   using RealType = decimal::BinaryFloatingPointNumber<binaryPrecision>;
649ea7e50cdSPeter Klausler   using RawType = typename RealType::RawType;
650ea7e50cdSPeter Klausler   bool isNegative{*p == '-'};
651ea7e50cdSPeter Klausler   constexpr RawType one{1};
652ea7e50cdSPeter Klausler   RawType signBit{0};
653ea7e50cdSPeter Klausler   if (isNegative) {
654ea7e50cdSPeter Klausler     ++p;
655ea7e50cdSPeter Klausler     signBit = one << (RealType::bits - 1);
656ea7e50cdSPeter Klausler   }
657ea7e50cdSPeter Klausler   RawType fraction{0};
658ea7e50cdSPeter Klausler   // Adjust the incoming binary P+/- exponent to shift the radix point
659ea7e50cdSPeter Klausler   // to below the LSB and add in the bias.
660ea7e50cdSPeter Klausler   expo += binaryPrecision - 1 + RealType::exponentBias;
661ea7e50cdSPeter Klausler   // Input the fraction.
662ea7e50cdSPeter Klausler   int roundingBit{0};
663ea7e50cdSPeter Klausler   int guardBit{0};
664ea7e50cdSPeter Klausler   for (; *p; ++p) {
665ea7e50cdSPeter Klausler     fraction <<= 4;
666ea7e50cdSPeter Klausler     expo -= 4;
667ea7e50cdSPeter Klausler     if (*p >= '0' && *p <= '9') {
668ea7e50cdSPeter Klausler       fraction |= *p - '0';
669ea7e50cdSPeter Klausler     } else if (*p >= 'A' && *p <= 'F') {
670ea7e50cdSPeter Klausler       fraction |= *p - 'A' + 10; // data were normalized to capitals
671ea7e50cdSPeter Klausler     } else {
672ea7e50cdSPeter Klausler       break;
673ea7e50cdSPeter Klausler     }
6747eb5d4fcSPeter Klausler     if (fraction >> binaryPrecision) {
675ea7e50cdSPeter Klausler       while (fraction >> binaryPrecision) {
676ea7e50cdSPeter Klausler         guardBit |= roundingBit;
677ea7e50cdSPeter Klausler         roundingBit = (int)fraction & 1;
678ea7e50cdSPeter Klausler         fraction >>= 1;
679ea7e50cdSPeter Klausler         ++expo;
680ea7e50cdSPeter Klausler       }
6817eb5d4fcSPeter Klausler       // Consume excess digits
6827eb5d4fcSPeter Klausler       while (*++p) {
6837eb5d4fcSPeter Klausler         if (*p == '0') {
6847eb5d4fcSPeter Klausler         } else if ((*p >= '1' && *p <= '9') || (*p >= 'A' && *p <= 'F')) {
6857eb5d4fcSPeter Klausler           guardBit = 1;
6867eb5d4fcSPeter Klausler         } else {
6877eb5d4fcSPeter Klausler           break;
6887eb5d4fcSPeter Klausler         }
6897eb5d4fcSPeter Klausler       }
6907eb5d4fcSPeter Klausler       break;
6917eb5d4fcSPeter Klausler     }
692ea7e50cdSPeter Klausler   }
693ea7e50cdSPeter Klausler   if (fraction) {
694ea7e50cdSPeter Klausler     // Boost biased expo if too small
695ea7e50cdSPeter Klausler     while (expo < 1) {
696ea7e50cdSPeter Klausler       guardBit |= roundingBit;
697ea7e50cdSPeter Klausler       roundingBit = (int)fraction & 1;
698ea7e50cdSPeter Klausler       fraction >>= 1;
699ea7e50cdSPeter Klausler       ++expo;
700ea7e50cdSPeter Klausler     }
701ea7e50cdSPeter Klausler     // Normalize
702ea7e50cdSPeter Klausler     while (expo > 1 && !(fraction >> (binaryPrecision - 1))) {
703ea7e50cdSPeter Klausler       fraction <<= 1;
704ea7e50cdSPeter Klausler       --expo;
7057eb5d4fcSPeter Klausler       guardBit = roundingBit = 0;
7067eb5d4fcSPeter Klausler     }
707ea7e50cdSPeter Klausler   }
708add189cdSPeter Klausler   // Rounding
709add189cdSPeter Klausler   bool increase{false};
710add189cdSPeter Klausler   switch (rounding) {
711add189cdSPeter Klausler   case decimal::RoundNearest: // RN & RP
712add189cdSPeter Klausler     increase = roundingBit && (guardBit | ((int)fraction & 1));
713add189cdSPeter Klausler     break;
714add189cdSPeter Klausler   case decimal::RoundUp: // RU
715add189cdSPeter Klausler     increase = !isNegative && (roundingBit | guardBit);
716add189cdSPeter Klausler     break;
717add189cdSPeter Klausler   case decimal::RoundDown: // RD
718add189cdSPeter Klausler     increase = isNegative && (roundingBit | guardBit);
719add189cdSPeter Klausler     break;
720add189cdSPeter Klausler   case decimal::RoundToZero: // RZ
721add189cdSPeter Klausler     break;
722add189cdSPeter Klausler   case decimal::RoundCompatible: // RC
723add189cdSPeter Klausler     increase = roundingBit != 0;
724add189cdSPeter Klausler     break;
725add189cdSPeter Klausler   }
726add189cdSPeter Klausler   if (increase) {
727add189cdSPeter Klausler     ++fraction;
728add189cdSPeter Klausler     if (fraction >> binaryPrecision) {
729add189cdSPeter Klausler       fraction >>= 1;
730add189cdSPeter Klausler       ++expo;
731add189cdSPeter Klausler     }
732add189cdSPeter Klausler   }
733ea7e50cdSPeter Klausler   // Package & return result
734ea7e50cdSPeter Klausler   constexpr RawType significandMask{(one << RealType::significandBits) - 1};
735befdfae1SPeter Klausler   int flags{(roundingBit | guardBit) ? decimal::Inexact : decimal::Exact};
736ea7e50cdSPeter Klausler   if (!fraction) {
737ea7e50cdSPeter Klausler     expo = 0;
738ea7e50cdSPeter Klausler   } else if (expo == 1 && !(fraction >> (binaryPrecision - 1))) {
739ea7e50cdSPeter Klausler     expo = 0; // subnormal
740befdfae1SPeter Klausler     flags |= decimal::Underflow;
741ea7e50cdSPeter Klausler   } else if (expo >= RealType::maxExponent) {
742add189cdSPeter Klausler     if (rounding == decimal::RoundToZero ||
743add189cdSPeter Klausler         (rounding == decimal::RoundDown && !isNegative) ||
744add189cdSPeter Klausler         (rounding == decimal::RoundUp && isNegative)) {
745add189cdSPeter Klausler       expo = RealType::maxExponent - 1; // +/-HUGE()
746add189cdSPeter Klausler       fraction = significandMask;
747add189cdSPeter Klausler     } else {
748ea7e50cdSPeter Klausler       expo = RealType::maxExponent; // +/-Inf
749ea7e50cdSPeter Klausler       fraction = 0;
7501346037fSPeter Klausler       flags |= decimal::Overflow;
751add189cdSPeter Klausler     }
752ea7e50cdSPeter Klausler   } else {
753ea7e50cdSPeter Klausler     fraction &= significandMask; // remove explicit normalization unless x87
754ea7e50cdSPeter Klausler   }
755ea7e50cdSPeter Klausler   return decimal::ConversionToBinaryResult<binaryPrecision>{
756ea7e50cdSPeter Klausler       RealType{static_cast<RawType>(signBit |
757ea7e50cdSPeter Klausler           static_cast<RawType>(expo) << RealType::significandBits | fraction)},
7581346037fSPeter Klausler       static_cast<decimal::ConversionResultFlags>(flags)};
759ea7e50cdSPeter Klausler }
760ea7e50cdSPeter Klausler 
761d56fdc8eSpeter klausler template <int KIND>
7628ebf7411SSlava Zakharin RT_API_ATTRS bool EditCommonRealInput(
7638ebf7411SSlava Zakharin     IoStatementState &io, const DataEdit &edit, void *n) {
764d56fdc8eSpeter klausler   constexpr int binaryPrecision{common::PrecisionOfRealKind(KIND)};
765ea7e50cdSPeter Klausler   if (TryFastPathRealDecimalInput<binaryPrecision>(io, edit, n)) {
76637ea42b2SPeter Klausler     return CheckCompleteListDirectedField(io, edit);
767da25f968SPeter Klausler   }
768da25f968SPeter Klausler   // Fast path wasn't available or didn't work; go the more general route
7693b635714Speter klausler   static constexpr int maxDigits{
7703b635714Speter klausler       common::MaxDecimalConversionDigits(binaryPrecision)};
7713b635714Speter klausler   static constexpr int bufferSize{maxDigits + 18};
7723b635714Speter klausler   char buffer[bufferSize];
773ea7e50cdSPeter Klausler   auto scanned{ScanRealInput(buffer, maxDigits + 2, io, edit)};
774ea7e50cdSPeter Klausler   int got{scanned.got};
7753b635714Speter klausler   if (got >= maxDigits + 2) {
7763bc2ae95Speter klausler     io.GetIoErrorHandler().Crash("EditCommonRealInput: buffer was too small");
7773b635714Speter klausler     return false;
7783b635714Speter klausler   }
7793b635714Speter klausler   if (got == 0) {
78037ea42b2SPeter Klausler     const auto &connection{io.GetConnectionState()};
78137ea42b2SPeter Klausler     io.GetIoErrorHandler().SignalError(IostatBadRealInput,
78237ea42b2SPeter Klausler         "Bad real input data at column %d of record %d",
78337ea42b2SPeter Klausler         static_cast<int>(connection.positionInRecord + 1),
78437ea42b2SPeter Klausler         static_cast<int>(connection.currentRecordNumber));
7853b635714Speter klausler     return false;
7863b635714Speter klausler   }
787ea7e50cdSPeter Klausler   decimal::ConversionToBinaryResult<binaryPrecision> converted;
788ea7e50cdSPeter Klausler   const char *p{buffer};
789ea7e50cdSPeter Klausler   if (scanned.isHexadecimal) {
790ea7e50cdSPeter Klausler     buffer[got] = '\0';
791ea7e50cdSPeter Klausler     converted = ConvertHexadecimal<binaryPrecision>(
792ea7e50cdSPeter Klausler         p, edit.modes.round, scanned.exponent);
793ea7e50cdSPeter Klausler   } else {
7943b635714Speter klausler     bool hadExtra{got > maxDigits};
795ea7e50cdSPeter Klausler     int exponent{scanned.exponent};
7963b635714Speter klausler     if (exponent != 0) {
797da25f968SPeter Klausler       buffer[got++] = 'e';
798da25f968SPeter Klausler       if (exponent < 0) {
799da25f968SPeter Klausler         buffer[got++] = '-';
800da25f968SPeter Klausler         exponent = -exponent;
801da25f968SPeter Klausler       }
802da25f968SPeter Klausler       if (exponent > 9999) {
803da25f968SPeter Klausler         exponent = 9999; // will convert to +/-Inf
804da25f968SPeter Klausler       }
805da25f968SPeter Klausler       if (exponent > 999) {
806da25f968SPeter Klausler         int dig{exponent / 1000};
807da25f968SPeter Klausler         buffer[got++] = '0' + dig;
808da25f968SPeter Klausler         int rest{exponent - 1000 * dig};
809da25f968SPeter Klausler         dig = rest / 100;
810da25f968SPeter Klausler         buffer[got++] = '0' + dig;
811da25f968SPeter Klausler         rest -= 100 * dig;
812da25f968SPeter Klausler         dig = rest / 10;
813da25f968SPeter Klausler         buffer[got++] = '0' + dig;
814da25f968SPeter Klausler         buffer[got++] = '0' + (rest - 10 * dig);
815da25f968SPeter Klausler       } else if (exponent > 99) {
816da25f968SPeter Klausler         int dig{exponent / 100};
817da25f968SPeter Klausler         buffer[got++] = '0' + dig;
818da25f968SPeter Klausler         int rest{exponent - 100 * dig};
819da25f968SPeter Klausler         dig = rest / 10;
820da25f968SPeter Klausler         buffer[got++] = '0' + dig;
821da25f968SPeter Klausler         buffer[got++] = '0' + (rest - 10 * dig);
822da25f968SPeter Klausler       } else if (exponent > 9) {
823da25f968SPeter Klausler         int dig{exponent / 10};
824da25f968SPeter Klausler         buffer[got++] = '0' + dig;
825da25f968SPeter Klausler         buffer[got++] = '0' + (exponent - 10 * dig);
826da25f968SPeter Klausler       } else {
827da25f968SPeter Klausler         buffer[got++] = '0' + exponent;
828da25f968SPeter Klausler       }
8293b635714Speter klausler     }
8303b635714Speter klausler     buffer[got] = '\0';
831ea7e50cdSPeter Klausler     converted = decimal::ConvertToBinary<binaryPrecision>(p, edit.modes.round);
8323b635714Speter klausler     if (hadExtra) {
8333b635714Speter klausler       converted.flags = static_cast<enum decimal::ConversionResultFlags>(
8343b635714Speter klausler           converted.flags | decimal::Inexact);
8353b635714Speter klausler     }
836ea7e50cdSPeter Klausler   }
837f1dbf8e4SPeter Klausler   if (*p) { // unprocessed junk after value
83837ea42b2SPeter Klausler     const auto &connection{io.GetConnectionState()};
83937ea42b2SPeter Klausler     io.GetIoErrorHandler().SignalError(IostatBadRealInput,
84037ea42b2SPeter Klausler         "Trailing characters after real input data at column %d of record %d",
84137ea42b2SPeter Klausler         static_cast<int>(connection.positionInRecord + 1),
84237ea42b2SPeter Klausler         static_cast<int>(connection.currentRecordNumber));
843f1dbf8e4SPeter Klausler     return false;
844f1dbf8e4SPeter Klausler   }
8453b635714Speter klausler   *reinterpret_cast<decimal::BinaryFloatingPointNumber<binaryPrecision> *>(n) =
8463b635714Speter klausler       converted.binary;
847de026aebSPeter Klausler   // Set FP exception flags
848de026aebSPeter Klausler   if (converted.flags != decimal::ConversionResultFlags::Exact) {
8499c54d762SPeter Klausler     if (converted.flags & decimal::ConversionResultFlags::Overflow) {
8509c54d762SPeter Klausler       io.GetIoErrorHandler().SignalError(IostatRealInputOverflow);
8519c54d762SPeter Klausler       return false;
8529c54d762SPeter Klausler     }
853de026aebSPeter Klausler     RaiseFPExceptions(converted.flags);
854de026aebSPeter Klausler   }
85537ea42b2SPeter Klausler   return CheckCompleteListDirectedField(io, edit);
8563b635714Speter klausler }
8573b635714Speter klausler 
858d56fdc8eSpeter klausler template <int KIND>
8598ebf7411SSlava Zakharin RT_API_ATTRS bool EditRealInput(
8608ebf7411SSlava Zakharin     IoStatementState &io, const DataEdit &edit, void *n) {
8613b635714Speter klausler   switch (edit.descriptor) {
8623b635714Speter klausler   case DataEdit::ListDirected:
863514b759aSPeter Klausler     if (IsNamelistNameOrSlash(io)) {
864b8452dbaSpeter klausler       return false;
865b8452dbaSpeter klausler     }
866b8452dbaSpeter klausler     return EditCommonRealInput<KIND>(io, edit, n);
8673bc2ae95Speter klausler   case DataEdit::ListDirectedRealPart:
8683bc2ae95Speter klausler   case DataEdit::ListDirectedImaginaryPart:
8693b635714Speter klausler   case 'F':
8703b635714Speter klausler   case 'E': // incl. EN, ES, & EX
8713b635714Speter klausler   case 'D':
8721f879005STim Keith   case 'G':
873d56fdc8eSpeter klausler     return EditCommonRealInput<KIND>(io, edit, n);
8743b635714Speter klausler   case 'B':
87553f775bbSPeter Klausler     return EditBOZInput<1>(io, edit, n,
87653f775bbSPeter Klausler         common::BitsForBinaryPrecision(common::PrecisionOfRealKind(KIND)) >> 3);
8773b635714Speter klausler   case 'O':
87853f775bbSPeter Klausler     return EditBOZInput<3>(io, edit, n,
87953f775bbSPeter Klausler         common::BitsForBinaryPrecision(common::PrecisionOfRealKind(KIND)) >> 3);
8803b635714Speter klausler   case 'Z':
88153f775bbSPeter Klausler     return EditBOZInput<4>(io, edit, n,
88253f775bbSPeter Klausler         common::BitsForBinaryPrecision(common::PrecisionOfRealKind(KIND)) >> 3);
883b83242e2Speter klausler   case 'A': // legacy extension
884bafbae23SPeter Klausler     return EditCharacterInput(io, edit, reinterpret_cast<char *>(n), KIND);
8853b635714Speter klausler   default:
8863b635714Speter klausler     io.GetIoErrorHandler().SignalError(IostatErrorInFormat,
8873b635714Speter klausler         "Data edit descriptor '%c' may not be used for REAL input",
8883b635714Speter klausler         edit.descriptor);
8893b635714Speter klausler     return false;
8903b635714Speter klausler   }
8913b635714Speter klausler }
8923b635714Speter klausler 
8933b635714Speter klausler // 13.7.3 in Fortran 2018
8948ebf7411SSlava Zakharin RT_API_ATTRS bool EditLogicalInput(
8958ebf7411SSlava Zakharin     IoStatementState &io, const DataEdit &edit, bool &x) {
8963b635714Speter klausler   switch (edit.descriptor) {
8973b635714Speter klausler   case DataEdit::ListDirected:
898514b759aSPeter Klausler     if (IsNamelistNameOrSlash(io)) {
899b8452dbaSpeter klausler       return false;
900b8452dbaSpeter klausler     }
901b8452dbaSpeter klausler     break;
9023b635714Speter klausler   case 'L':
9031f879005STim Keith   case 'G':
9041f879005STim Keith     break;
9053b635714Speter klausler   default:
9063b635714Speter klausler     io.GetIoErrorHandler().SignalError(IostatErrorInFormat,
9073b635714Speter klausler         "Data edit descriptor '%c' may not be used for LOGICAL input",
9083b635714Speter klausler         edit.descriptor);
9093b635714Speter klausler     return false;
9103b635714Speter klausler   }
91171e0261fSSlava Zakharin   Fortran::common::optional<int> remaining{io.CueUpInput(edit)};
91271e0261fSSlava Zakharin   Fortran::common::optional<char32_t> next{io.NextInField(remaining, edit)};
9133b635714Speter klausler   if (next && *next == '.') { // skip optional period
914991696c2SPeter Klausler     next = io.NextInField(remaining, edit);
9153b635714Speter klausler   }
9163b635714Speter klausler   if (!next) {
9173b635714Speter klausler     io.GetIoErrorHandler().SignalError("Empty LOGICAL input field");
9183b635714Speter klausler     return false;
9193b635714Speter klausler   }
9203b635714Speter klausler   switch (*next) {
9213b635714Speter klausler   case 'T':
9221f879005STim Keith   case 't':
9231f879005STim Keith     x = true;
9241f879005STim Keith     break;
9253b635714Speter klausler   case 'F':
9261f879005STim Keith   case 'f':
9271f879005STim Keith     x = false;
9281f879005STim Keith     break;
9293b635714Speter klausler   default:
9303b635714Speter klausler     io.GetIoErrorHandler().SignalError(
9313b635714Speter klausler         "Bad character '%lc' in LOGICAL input field", *next);
9323b635714Speter klausler     return false;
9333b635714Speter klausler   }
93437ea42b2SPeter Klausler   if (remaining) { // ignore the rest of a fixed-width field
9353b635714Speter klausler     io.HandleRelativePosition(*remaining);
9368dbc86adSpeter klausler   } else if (edit.descriptor == DataEdit::ListDirected) {
937991696c2SPeter Klausler     while (io.NextInField(remaining, edit)) { // discard rest of field
9388dbc86adSpeter klausler     }
9393b635714Speter klausler   }
94037ea42b2SPeter Klausler   return CheckCompleteListDirectedField(io, edit);
9413b635714Speter klausler }
9423b635714Speter klausler 
9433b635714Speter klausler // See 13.10.3.1 paragraphs 7-9 in Fortran 2018
944bafbae23SPeter Klausler template <typename CHAR>
9458ebf7411SSlava Zakharin static RT_API_ATTRS bool EditDelimitedCharacterInput(
946bafbae23SPeter Klausler     IoStatementState &io, CHAR *x, std::size_t length, char32_t delimiter) {
9476a1c3efaSpeter klausler   bool result{true};
9483b635714Speter klausler   while (true) {
949bafbae23SPeter Klausler     std::size_t byteCount{0};
950bafbae23SPeter Klausler     auto ch{io.GetCurrentChar(byteCount)};
9516a1c3efaSpeter klausler     if (!ch) {
9526a1c3efaSpeter klausler       if (io.AdvanceRecord()) {
9536a1c3efaSpeter klausler         continue;
9546a1c3efaSpeter klausler       } else {
9556a1c3efaSpeter klausler         result = false; // EOF in character value
9566a1c3efaSpeter klausler         break;
9576a1c3efaSpeter klausler       }
9586a1c3efaSpeter klausler     }
959bafbae23SPeter Klausler     io.HandleRelativePosition(byteCount);
9603b635714Speter klausler     if (*ch == delimiter) {
961bafbae23SPeter Klausler       auto next{io.GetCurrentChar(byteCount)};
9624d42e16eSpeter klausler       if (next && *next == delimiter) {
9636a1c3efaSpeter klausler         // Repeated delimiter: use as character value
964bafbae23SPeter Klausler         io.HandleRelativePosition(byteCount);
9654d42e16eSpeter klausler       } else {
9664d42e16eSpeter klausler         break; // closing delimiter
9673b635714Speter klausler       }
9683b635714Speter klausler     }
9693b635714Speter klausler     if (length > 0) {
9703b635714Speter klausler       *x++ = *ch;
9713b635714Speter klausler       --length;
9723b635714Speter klausler     }
9736a1c3efaSpeter klausler   }
9748ebf7411SSlava Zakharin   Fortran::runtime::fill_n(x, length, ' ');
9756a1c3efaSpeter klausler   return result;
9763b635714Speter klausler }
9773b635714Speter klausler 
978bafbae23SPeter Klausler template <typename CHAR>
9798ebf7411SSlava Zakharin static RT_API_ATTRS bool EditListDirectedCharacterInput(
980bafbae23SPeter Klausler     IoStatementState &io, CHAR *x, std::size_t length, const DataEdit &edit) {
981bafbae23SPeter Klausler   std::size_t byteCount{0};
982bafbae23SPeter Klausler   auto ch{io.GetCurrentChar(byteCount)};
9833b635714Speter klausler   if (ch && (*ch == '\'' || *ch == '"')) {
984bafbae23SPeter Klausler     io.HandleRelativePosition(byteCount);
9853b635714Speter klausler     return EditDelimitedCharacterInput(io, x, length, *ch);
9863b635714Speter klausler   }
987514b759aSPeter Klausler   if (IsNamelistNameOrSlash(io) || io.GetConnectionState().IsAtEOF()) {
988b8452dbaSpeter klausler     return false;
989b8452dbaSpeter klausler   }
9903b635714Speter klausler   // Undelimited list-directed character input: stop at a value separator
9919b64811eSPeter Klausler   // or the end of the current record.
9929b64811eSPeter Klausler   while (auto ch{io.GetCurrentChar(byteCount)}) {
993cb193931SPeter Klausler     bool isSep{false};
9949b64811eSPeter Klausler     switch (*ch) {
9953b635714Speter klausler     case ' ':
9968305a92aSpeter klausler     case '\t':
9973b635714Speter klausler     case '/':
998cb193931SPeter Klausler       isSep = true;
999cb193931SPeter Klausler       break;
1000120ad250SPeter Klausler     case '&':
1001120ad250SPeter Klausler     case '$':
1002120ad250SPeter Klausler       isSep = edit.IsNamelist();
1003120ad250SPeter Klausler       break;
1004cb193931SPeter Klausler     case ',':
1005cb193931SPeter Klausler       isSep = !(edit.modes.editingFlags & decimalComma);
1006cb193931SPeter Klausler       break;
1007cb193931SPeter Klausler     case ';':
1008cb193931SPeter Klausler       isSep = !!(edit.modes.editingFlags & decimalComma);
10093b635714Speter klausler       break;
10101f879005STim Keith     default:
1011cb193931SPeter Klausler       break;
1012cb193931SPeter Klausler     }
1013cb193931SPeter Klausler     if (isSep) {
10149b64811eSPeter Klausler       break;
10153b635714Speter klausler     }
10169b64811eSPeter Klausler     if (length > 0) {
10179b64811eSPeter Klausler       *x++ = *ch;
10189b64811eSPeter Klausler       --length;
10199b64811eSPeter Klausler     } else if (edit.IsNamelist()) {
10209b64811eSPeter Klausler       // GNU compatibility
10219b64811eSPeter Klausler       break;
10229b64811eSPeter Klausler     }
10239b64811eSPeter Klausler     io.HandleRelativePosition(byteCount);
10249b64811eSPeter Klausler     io.GotChar(byteCount);
10253b635714Speter klausler   }
10268ebf7411SSlava Zakharin   Fortran::runtime::fill_n(x, length, ' ');
10273b635714Speter klausler   return true;
10283b635714Speter klausler }
10293b635714Speter klausler 
1030bafbae23SPeter Klausler template <typename CHAR>
10318ebf7411SSlava Zakharin RT_API_ATTRS bool EditCharacterInput(IoStatementState &io, const DataEdit &edit,
10328ebf7411SSlava Zakharin     CHAR *x, std::size_t lengthChars) {
10333b635714Speter klausler   switch (edit.descriptor) {
10343b635714Speter klausler   case DataEdit::ListDirected:
1035353d56d2SPeter Klausler     return EditListDirectedCharacterInput(io, x, lengthChars, edit);
10363b635714Speter klausler   case 'A':
10371f879005STim Keith   case 'G':
10381f879005STim Keith     break;
103953f775bbSPeter Klausler   case 'B':
1040353d56d2SPeter Klausler     return EditBOZInput<1>(io, edit, x, lengthChars * sizeof *x);
104153f775bbSPeter Klausler   case 'O':
1042353d56d2SPeter Klausler     return EditBOZInput<3>(io, edit, x, lengthChars * sizeof *x);
104353f775bbSPeter Klausler   case 'Z':
1044353d56d2SPeter Klausler     return EditBOZInput<4>(io, edit, x, lengthChars * sizeof *x);
10453b635714Speter klausler   default:
10463b635714Speter klausler     io.GetIoErrorHandler().SignalError(IostatErrorInFormat,
10473b635714Speter klausler         "Data edit descriptor '%c' may not be used with a CHARACTER data item",
10483b635714Speter klausler         edit.descriptor);
10493b635714Speter klausler     return false;
10503b635714Speter klausler   }
1051bafbae23SPeter Klausler   const ConnectionState &connection{io.GetConnectionState()};
1052353d56d2SPeter Klausler   std::size_t remainingChars{lengthChars};
1053353d56d2SPeter Klausler   // Skip leading characters.
1054353d56d2SPeter Klausler   // Their bytes don't count towards INQUIRE(IOLENGTH=).
1055353d56d2SPeter Klausler   std::size_t skipChars{0};
10563b635714Speter klausler   if (edit.width && *edit.width > 0) {
1057353d56d2SPeter Klausler     remainingChars = *edit.width;
1058353d56d2SPeter Klausler     if (remainingChars > lengthChars) {
1059353d56d2SPeter Klausler       skipChars = remainingChars - lengthChars;
1060353d56d2SPeter Klausler     }
10613b635714Speter klausler   }
10623b635714Speter klausler   // When the field is wider than the variable, we drop the leading
1063cea8b8a7SPeter Klausler   // characters.  When the variable is wider than the field, there can be
10647cf198f7SPeter Klausler   // trailing padding or an EOR condition.
1065ef7f6f7cSPeter Klausler   const char *input{nullptr};
1066353d56d2SPeter Klausler   std::size_t readyBytes{0};
1067ef7f6f7cSPeter Klausler   // Transfer payload bytes; these do count.
1068353d56d2SPeter Klausler   while (remainingChars > 0) {
1069353d56d2SPeter Klausler     if (readyBytes == 0) {
1070353d56d2SPeter Klausler       readyBytes = io.GetNextInputBytes(input);
1071353d56d2SPeter Klausler       if (readyBytes == 0 ||
1072353d56d2SPeter Klausler           (readyBytes < remainingChars && edit.modes.nonAdvancing)) {
1073353d56d2SPeter Klausler         if (io.CheckForEndOfRecord(readyBytes)) {
1074353d56d2SPeter Klausler           if (readyBytes == 0) {
10757cf198f7SPeter Klausler             // PAD='YES' and no more data
10768ebf7411SSlava Zakharin             Fortran::runtime::fill_n(x, lengthChars, ' ');
1077cea8b8a7SPeter Klausler             return !io.GetIoErrorHandler().InError();
10787cf198f7SPeter Klausler           } else {
10797cf198f7SPeter Klausler             // Do partial read(s) then pad on last iteration
10807cf198f7SPeter Klausler           }
10817cf198f7SPeter Klausler         } else {
10827cf198f7SPeter Klausler           return !io.GetIoErrorHandler().InError();
10837cf198f7SPeter Klausler         }
1084ef7f6f7cSPeter Klausler       }
1085ef7f6f7cSPeter Klausler     }
1086353d56d2SPeter Klausler     std::size_t chunkBytes;
1087353d56d2SPeter Klausler     std::size_t chunkChars{1};
1088353d56d2SPeter Klausler     bool skipping{skipChars > 0};
1089bafbae23SPeter Klausler     if (connection.isUTF8) {
1090353d56d2SPeter Klausler       chunkBytes = MeasureUTF8Bytes(*input);
1091bafbae23SPeter Klausler       if (skipping) {
1092353d56d2SPeter Klausler         --skipChars;
1093bafbae23SPeter Klausler       } else if (auto ucs{DecodeUTF8(input)}) {
1094c6dfb62dSPeter Klausler         if ((sizeof *x == 1 && *ucs > 0xff) ||
1095c6dfb62dSPeter Klausler             (sizeof *x == 2 && *ucs > 0xffff)) {
1096c6dfb62dSPeter Klausler           *x++ = '?';
1097c6dfb62dSPeter Klausler         } else {
1098bafbae23SPeter Klausler           *x++ = *ucs;
1099c6dfb62dSPeter Klausler         }
1100353d56d2SPeter Klausler         --lengthChars;
1101353d56d2SPeter Klausler       } else if (chunkBytes == 0) {
1102bafbae23SPeter Klausler         // error recovery: skip bad encoding
1103353d56d2SPeter Klausler         chunkBytes = 1;
1104bafbae23SPeter Klausler       }
1105bad52055SPeter Klausler     } else if (connection.internalIoCharKind > 1) {
1106bad52055SPeter Klausler       // Reading from non-default character internal unit
1107353d56d2SPeter Klausler       chunkBytes = connection.internalIoCharKind;
1108bad52055SPeter Klausler       if (skipping) {
1109353d56d2SPeter Klausler         --skipChars;
1110bad52055SPeter Klausler       } else {
1111bad52055SPeter Klausler         char32_t buffer{0};
1112353d56d2SPeter Klausler         std::memcpy(&buffer, input, chunkBytes);
1113c6dfb62dSPeter Klausler         if ((sizeof *x == 1 && buffer > 0xff) ||
1114c6dfb62dSPeter Klausler             (sizeof *x == 2 && buffer > 0xffff)) {
1115c6dfb62dSPeter Klausler           *x++ = '?';
1116c6dfb62dSPeter Klausler         } else {
1117bad52055SPeter Klausler           *x++ = buffer;
1118c6dfb62dSPeter Klausler         }
1119353d56d2SPeter Klausler         --lengthChars;
1120bad52055SPeter Klausler       }
1121ede42131SPeter Klausler     } else if constexpr (sizeof *x > 1) {
1122ede42131SPeter Klausler       // Read single byte with expansion into multi-byte CHARACTER
1123353d56d2SPeter Klausler       chunkBytes = 1;
1124ede42131SPeter Klausler       if (skipping) {
1125353d56d2SPeter Klausler         --skipChars;
1126bafbae23SPeter Klausler       } else {
1127ede42131SPeter Klausler         *x++ = static_cast<unsigned char>(*input);
1128353d56d2SPeter Klausler         --lengthChars;
1129ede42131SPeter Klausler       }
1130ede42131SPeter Klausler     } else { // single bytes -> default CHARACTER
1131bafbae23SPeter Klausler       if (skipping) {
1132353d56d2SPeter Klausler         chunkBytes = std::min<std::size_t>(skipChars, readyBytes);
1133353d56d2SPeter Klausler         chunkChars = chunkBytes;
1134353d56d2SPeter Klausler         skipChars -= chunkChars;
1135bafbae23SPeter Klausler       } else {
1136353d56d2SPeter Klausler         chunkBytes = std::min<std::size_t>(remainingChars, readyBytes);
1137353d56d2SPeter Klausler         chunkBytes = std::min<std::size_t>(lengthChars, chunkBytes);
1138353d56d2SPeter Klausler         chunkChars = chunkBytes;
1139353d56d2SPeter Klausler         std::memcpy(x, input, chunkBytes);
1140353d56d2SPeter Klausler         x += chunkBytes;
1141353d56d2SPeter Klausler         lengthChars -= chunkChars;
1142ef7f6f7cSPeter Klausler       }
1143bafbae23SPeter Klausler     }
1144353d56d2SPeter Klausler     input += chunkBytes;
1145353d56d2SPeter Klausler     remainingChars -= chunkChars;
1146bafbae23SPeter Klausler     if (!skipping) {
1147353d56d2SPeter Klausler       io.GotChar(chunkBytes);
1148bafbae23SPeter Klausler     }
1149353d56d2SPeter Klausler     io.HandleRelativePosition(chunkBytes);
1150353d56d2SPeter Klausler     readyBytes -= chunkBytes;
1151bafbae23SPeter Klausler   }
1152ef7f6f7cSPeter Klausler   // Pad the remainder of the input variable, if any.
11538ebf7411SSlava Zakharin   Fortran::runtime::fill_n(x, lengthChars, ' ');
115437ea42b2SPeter Klausler   return CheckCompleteListDirectedField(io, edit);
11553b635714Speter klausler }
11563b635714Speter klausler 
11578ebf7411SSlava Zakharin template RT_API_ATTRS bool EditRealInput<2>(
11588ebf7411SSlava Zakharin     IoStatementState &, const DataEdit &, void *);
11598ebf7411SSlava Zakharin template RT_API_ATTRS bool EditRealInput<3>(
11608ebf7411SSlava Zakharin     IoStatementState &, const DataEdit &, void *);
11618ebf7411SSlava Zakharin template RT_API_ATTRS bool EditRealInput<4>(
11628ebf7411SSlava Zakharin     IoStatementState &, const DataEdit &, void *);
11638ebf7411SSlava Zakharin template RT_API_ATTRS bool EditRealInput<8>(
11648ebf7411SSlava Zakharin     IoStatementState &, const DataEdit &, void *);
11658ebf7411SSlava Zakharin template RT_API_ATTRS bool EditRealInput<10>(
11668ebf7411SSlava Zakharin     IoStatementState &, const DataEdit &, void *);
1167d56fdc8eSpeter klausler // TODO: double/double
11688ebf7411SSlava Zakharin template RT_API_ATTRS bool EditRealInput<16>(
11698ebf7411SSlava Zakharin     IoStatementState &, const DataEdit &, void *);
1170bafbae23SPeter Klausler 
11718ebf7411SSlava Zakharin template RT_API_ATTRS bool EditCharacterInput(
1172bafbae23SPeter Klausler     IoStatementState &, const DataEdit &, char *, std::size_t);
11738ebf7411SSlava Zakharin template RT_API_ATTRS bool EditCharacterInput(
1174bafbae23SPeter Klausler     IoStatementState &, const DataEdit &, char16_t *, std::size_t);
11758ebf7411SSlava Zakharin template RT_API_ATTRS bool EditCharacterInput(
1176bafbae23SPeter Klausler     IoStatementState &, const DataEdit &, char32_t *, std::size_t);
1177bafbae23SPeter Klausler 
11788ebf7411SSlava Zakharin RT_OFFLOAD_API_GROUP_END
11791f879005STim Keith } // namespace Fortran::runtime::io
1180