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"
123b635714Speter klausler #include "flang/Common/real.h"
133b635714Speter klausler #include "flang/Common/uint128.h"
14231fae90SIsuru Fernando #include <algorithm>
15de026aebSPeter Klausler #include <cfenv>
163b635714Speter klausler 
173b635714Speter klausler namespace Fortran::runtime::io {
183b635714Speter klausler 
1953f775bbSPeter Klausler template <int LOG2_BASE>
EditBOZInput(IoStatementState & io,const DataEdit & edit,void * n,std::size_t bytes)2053f775bbSPeter Klausler static bool EditBOZInput(
2153f775bbSPeter Klausler     IoStatementState &io, const DataEdit &edit, void *n, std::size_t bytes) {
223b635714Speter klausler   std::optional<int> remaining;
236a1c3efaSpeter klausler   std::optional<char32_t> next{io.PrepareInput(edit, remaining)};
24850097d6SPeter Klausler   if (next.value_or('?') == '0') {
2553f775bbSPeter Klausler     do {
2653f775bbSPeter Klausler       next = io.NextInField(remaining, edit);
2753f775bbSPeter Klausler     } while (next && *next == '0');
2853f775bbSPeter Klausler   }
2953f775bbSPeter Klausler   // Count significant digits after any leading white space & zeroes
3053f775bbSPeter Klausler   int digits{0};
31850097d6SPeter Klausler   int chars{0};
32991696c2SPeter Klausler   for (; next; next = io.NextInField(remaining, edit)) {
33850097d6SPeter Klausler     ++chars;
343b635714Speter klausler     char32_t ch{*next};
358305a92aSpeter klausler     if (ch == ' ' || ch == '\t') {
363b635714Speter klausler       continue;
373b635714Speter klausler     }
383b635714Speter klausler     if (ch >= '0' && ch <= '1') {
3953f775bbSPeter Klausler     } else if (LOG2_BASE >= 3 && ch >= '2' && ch <= '7') {
4053f775bbSPeter Klausler     } else if (LOG2_BASE >= 4 && ch >= '8' && ch <= '9') {
4153f775bbSPeter Klausler     } else if (LOG2_BASE >= 4 && ch >= 'A' && ch <= 'F') {
4253f775bbSPeter Klausler     } else if (LOG2_BASE >= 4 && ch >= 'a' && ch <= 'f') {
433b635714Speter klausler     } else {
443b635714Speter klausler       io.GetIoErrorHandler().SignalError(
453b635714Speter klausler           "Bad character '%lc' in B/O/Z input field", ch);
463b635714Speter klausler       return false;
473b635714Speter klausler     }
4853f775bbSPeter Klausler     ++digits;
493b635714Speter klausler   }
5053f775bbSPeter Klausler   auto significantBytes{static_cast<std::size_t>(digits * LOG2_BASE + 7) / 8};
5153f775bbSPeter Klausler   if (significantBytes > bytes) {
52cdd54cbdSPeter Klausler     io.GetIoErrorHandler().SignalError(IostatBOZInputOverflow,
5353f775bbSPeter Klausler         "B/O/Z input of %d digits overflows %zd-byte variable", digits, bytes);
5453f775bbSPeter Klausler     return false;
5553f775bbSPeter Klausler   }
5653f775bbSPeter Klausler   // Reset to start of significant digits
57850097d6SPeter Klausler   io.HandleRelativePosition(-chars);
5853f775bbSPeter Klausler   remaining.reset();
5953f775bbSPeter Klausler   // Make a second pass now that the digit count is known
6053f775bbSPeter Klausler   std::memset(n, 0, bytes);
6153f775bbSPeter Klausler   int increment{isHostLittleEndian ? -1 : 1};
6253f775bbSPeter Klausler   auto *data{reinterpret_cast<unsigned char *>(n) +
6353f775bbSPeter Klausler       (isHostLittleEndian ? significantBytes - 1 : 0)};
6453f775bbSPeter Klausler   int shift{((digits - 1) * LOG2_BASE) & 7};
6553f775bbSPeter Klausler   if (shift + LOG2_BASE > 8) {
6653f775bbSPeter Klausler     shift -= 8; // misaligned octal
6753f775bbSPeter Klausler   }
6853f775bbSPeter Klausler   while (digits > 0) {
6953f775bbSPeter Klausler     char32_t ch{*io.NextInField(remaining, edit)};
7053f775bbSPeter Klausler     int digit{0};
7153f775bbSPeter Klausler     if (ch >= '0' && ch <= '9') {
7253f775bbSPeter Klausler       digit = ch - '0';
7353f775bbSPeter Klausler     } else if (ch >= 'A' && ch <= 'F') {
7453f775bbSPeter Klausler       digit = ch + 10 - 'A';
7553f775bbSPeter Klausler     } else if (ch >= 'a' && ch <= 'f') {
7653f775bbSPeter Klausler       digit = ch + 10 - 'a';
7753f775bbSPeter Klausler     } else {
7853f775bbSPeter Klausler       continue;
7953f775bbSPeter Klausler     }
8053f775bbSPeter Klausler     --digits;
8153f775bbSPeter Klausler     if (shift < 0) {
8253f775bbSPeter Klausler       shift += 8;
8353f775bbSPeter Klausler       if (shift + LOG2_BASE > 8) { // misaligned octal
8453f775bbSPeter Klausler         *data |= digit >> (8 - shift);
8553f775bbSPeter Klausler       }
8653f775bbSPeter Klausler       data += increment;
8753f775bbSPeter Klausler     }
8853f775bbSPeter Klausler     *data |= digit << shift;
8953f775bbSPeter Klausler     shift -= LOG2_BASE;
9053f775bbSPeter Klausler   }
913b635714Speter klausler   return true;
923b635714Speter klausler }
933b635714Speter klausler 
GetDecimalPoint(const DataEdit & edit)94896a543eSPeter Klausler static inline char32_t GetDecimalPoint(const DataEdit &edit) {
95896a543eSPeter Klausler   return edit.modes.editingFlags & decimalComma ? char32_t{','} : char32_t{'.'};
96896a543eSPeter Klausler }
97896a543eSPeter Klausler 
987c5630feSpeter klausler // Prepares input from a field, and consumes the sign, if any.
997c5630feSpeter klausler // Returns true if there's a '-' sign.
ScanNumericPrefix(IoStatementState & io,const DataEdit & edit,std::optional<char32_t> & next,std::optional<int> & remaining)1003b635714Speter klausler static bool ScanNumericPrefix(IoStatementState &io, const DataEdit &edit,
1013b635714Speter klausler     std::optional<char32_t> &next, std::optional<int> &remaining) {
102cbbc6629SPeter Klausler   next = io.PrepareInput(edit, remaining);
1033b635714Speter klausler   bool negative{false};
1043b635714Speter klausler   if (next) {
1053b635714Speter klausler     negative = *next == '-';
1063b635714Speter klausler     if (negative || *next == '+') {
1077c5630feSpeter klausler       io.SkipSpaces(remaining);
108991696c2SPeter Klausler       next = io.NextInField(remaining, edit);
1093b635714Speter klausler     }
1103b635714Speter klausler   }
1113b635714Speter klausler   return negative;
1123b635714Speter klausler }
1133b635714Speter klausler 
EditIntegerInput(IoStatementState & io,const DataEdit & edit,void * n,int kind)1143b635714Speter klausler bool EditIntegerInput(
1153b635714Speter klausler     IoStatementState &io, const DataEdit &edit, void *n, int kind) {
1163b635714Speter klausler   RUNTIME_CHECK(io.GetIoErrorHandler(), kind >= 1 && !(kind & (kind - 1)));
1173b635714Speter klausler   switch (edit.descriptor) {
1183b635714Speter klausler   case DataEdit::ListDirected:
119b8452dbaSpeter klausler     if (IsNamelistName(io)) {
120b8452dbaSpeter klausler       return false;
121b8452dbaSpeter klausler     }
122b8452dbaSpeter klausler     break;
1233b635714Speter klausler   case 'G':
1241f879005STim Keith   case 'I':
1251f879005STim Keith     break;
1261f879005STim Keith   case 'B':
12753f775bbSPeter Klausler     return EditBOZInput<1>(io, edit, n, kind);
1281f879005STim Keith   case 'O':
12953f775bbSPeter Klausler     return EditBOZInput<3>(io, edit, n, kind);
1301f879005STim Keith   case 'Z':
13153f775bbSPeter Klausler     return EditBOZInput<4>(io, edit, n, kind);
132b83242e2Speter klausler   case 'A': // legacy extension
133bafbae23SPeter Klausler     return EditCharacterInput(io, edit, reinterpret_cast<char *>(n), kind);
1343b635714Speter klausler   default:
1353b635714Speter klausler     io.GetIoErrorHandler().SignalError(IostatErrorInFormat,
1363b635714Speter klausler         "Data edit descriptor '%c' may not be used with an INTEGER data item",
1373b635714Speter klausler         edit.descriptor);
1383b635714Speter klausler     return false;
1393b635714Speter klausler   }
1403b635714Speter klausler   std::optional<int> remaining;
1413b635714Speter klausler   std::optional<char32_t> next;
1423b635714Speter klausler   bool negate{ScanNumericPrefix(io, edit, next, remaining)};
1430f5c60f1SPeter Klausler   common::UnsignedInt128 value{0};
1440f5c60f1SPeter Klausler   bool any{negate};
145cdd54cbdSPeter Klausler   bool overflow{false};
146991696c2SPeter Klausler   for (; next; next = io.NextInField(remaining, edit)) {
1473b635714Speter klausler     char32_t ch{*next};
1488305a92aSpeter klausler     if (ch == ' ' || ch == '\t') {
1493b635714Speter klausler       if (edit.modes.editingFlags & blankZero) {
1503b635714Speter klausler         ch = '0'; // BZ mode - treat blank as if it were zero
1513b635714Speter klausler       } else {
1523b635714Speter klausler         continue;
1533b635714Speter klausler       }
1543b635714Speter klausler     }
1553b635714Speter klausler     int digit{0};
1563b635714Speter klausler     if (ch >= '0' && ch <= '9') {
1573b635714Speter klausler       digit = ch - '0';
1583b635714Speter klausler     } else {
1593b635714Speter klausler       io.GetIoErrorHandler().SignalError(
1603b635714Speter klausler           "Bad character '%lc' in INTEGER input field", ch);
1613b635714Speter klausler       return false;
1623b635714Speter klausler     }
163cdd54cbdSPeter Klausler     static constexpr auto maxu128{~common::UnsignedInt128{0}};
164cdd54cbdSPeter Klausler     static constexpr auto maxu128OverTen{maxu128 / 10};
165cdd54cbdSPeter Klausler     static constexpr int maxLastDigit{
166cdd54cbdSPeter Klausler         static_cast<int>(maxu128 - (maxu128OverTen * 10))};
167cdd54cbdSPeter Klausler     overflow |= value >= maxu128OverTen &&
168cdd54cbdSPeter Klausler         (value > maxu128OverTen || digit > maxLastDigit);
1693b635714Speter klausler     value *= 10;
1703b635714Speter klausler     value += digit;
171ac4202feSPeter Klausler     any = true;
1723b635714Speter klausler   }
173cdd54cbdSPeter Klausler   auto maxForKind{common::UnsignedInt128{1} << ((8 * kind) - 1)};
174cdd54cbdSPeter Klausler   overflow |= value >= maxForKind && (value > maxForKind || !negate);
175cdd54cbdSPeter Klausler   if (overflow) {
176cdd54cbdSPeter Klausler     io.GetIoErrorHandler().SignalError(IostatIntegerInputOverflow,
177cdd54cbdSPeter Klausler         "Decimal input overflows INTEGER(%d) variable", kind);
178cdd54cbdSPeter Klausler     return false;
179cdd54cbdSPeter Klausler   }
1803b635714Speter klausler   if (negate) {
1813b635714Speter klausler     value = -value;
1823b635714Speter klausler   }
1830f5c60f1SPeter Klausler   if (any || !io.GetConnectionState().IsAtEOF()) {
1840f5c60f1SPeter Klausler     std::memcpy(n, &value, kind); // a blank field means zero
185ac4202feSPeter Klausler   }
186ac4202feSPeter Klausler   return any;
1873b635714Speter klausler }
1883b635714Speter klausler 
1897c5630feSpeter klausler // Parses a REAL input number from the input source as a normalized
1907c5630feSpeter klausler // fraction into a supplied buffer -- there's an optional '-', a
1917c5630feSpeter klausler // decimal point, and at least one digit.  The adjusted exponent value
1927c5630feSpeter klausler // is returned in a reference argument.  The returned value is the number
1937c5630feSpeter klausler // of characters that (should) have been written to the buffer -- this can
1947c5630feSpeter klausler // be larger than the buffer size and can indicate overflow.  Replaces
1957c5630feSpeter klausler // blanks with zeroes if appropriate.
ScanRealInput(char * buffer,int bufferSize,IoStatementState & io,const DataEdit & edit,int & exponent)1963b635714Speter klausler static int ScanRealInput(char *buffer, int bufferSize, IoStatementState &io,
1973b635714Speter klausler     const DataEdit &edit, int &exponent) {
1983b635714Speter klausler   std::optional<int> remaining;
1993b635714Speter klausler   std::optional<char32_t> next;
2003b635714Speter klausler   int got{0};
2013b635714Speter klausler   std::optional<int> decimalPoint;
2027c5630feSpeter klausler   auto Put{[&](char ch) -> void {
2033b635714Speter klausler     if (got < bufferSize) {
2047c5630feSpeter klausler       buffer[got] = ch;
2053b635714Speter klausler     }
2067c5630feSpeter klausler     ++got;
2077c5630feSpeter klausler   }};
2087c5630feSpeter klausler   if (ScanNumericPrefix(io, edit, next, remaining)) {
2097c5630feSpeter klausler     Put('-');
2103b635714Speter klausler   }
211e6873bfbSPeter Klausler   bool bzMode{(edit.modes.editingFlags & blankZero) != 0};
212e6873bfbSPeter Klausler   if (!next || (!bzMode && *next == ' ')) { // empty/blank field means zero
213f63dafebSpeter klausler     remaining.reset();
2140f5c60f1SPeter Klausler     if (!io.GetConnectionState().IsAtEOF()) {
2157c5630feSpeter klausler       Put('0');
2160f5c60f1SPeter Klausler     }
2173b635714Speter klausler     return got;
2183b635714Speter klausler   }
219896a543eSPeter Klausler   char32_t decimal{GetDecimalPoint(edit)};
2207c5630feSpeter klausler   char32_t first{*next >= 'a' && *next <= 'z' ? *next + 'A' - 'a' : *next};
2217c5630feSpeter klausler   if (first == 'N' || first == 'I') {
2223b635714Speter klausler     // NaN or infinity - convert to upper case
2237c5630feSpeter klausler     // Subtle: a blank field of digits could be followed by 'E' or 'D',
2243b635714Speter klausler     for (; next &&
2253b635714Speter klausler          ((*next >= 'a' && *next <= 'z') || (*next >= 'A' && *next <= 'Z'));
226991696c2SPeter Klausler          next = io.NextInField(remaining, edit)) {
2273b635714Speter klausler       if (*next >= 'a' && *next <= 'z') {
2287c5630feSpeter klausler         Put(*next - 'a' + 'A');
2293b635714Speter klausler       } else {
2307c5630feSpeter klausler         Put(*next);
2313b635714Speter klausler       }
2323b635714Speter klausler     }
2333b635714Speter klausler     if (next && *next == '(') { // NaN(...)
234f1dbf8e4SPeter Klausler       Put('(');
235f1dbf8e4SPeter Klausler       int depth{1};
2362f31b4b1SPeter Klausler       while (true) {
237991696c2SPeter Klausler         next = io.NextInField(remaining, edit);
2382f31b4b1SPeter Klausler         if (depth == 0) {
239f1dbf8e4SPeter Klausler           break;
2402f31b4b1SPeter Klausler         } else if (!next) {
2412f31b4b1SPeter Klausler           return 0; // error
242f1dbf8e4SPeter Klausler         } else if (*next == '(') {
243f1dbf8e4SPeter Klausler           ++depth;
244f1dbf8e4SPeter Klausler         } else if (*next == ')') {
245f1dbf8e4SPeter Klausler           --depth;
2463b635714Speter klausler         }
247f1dbf8e4SPeter Klausler         Put(*next);
2482f31b4b1SPeter Klausler       }
2493b635714Speter klausler     }
2503b635714Speter klausler     exponent = 0;
2517c5630feSpeter klausler   } else if (first == decimal || (first >= '0' && first <= '9') ||
252e6873bfbSPeter Klausler       (bzMode && (first == ' ' || first == '\t')) || first == 'E' ||
253e6873bfbSPeter Klausler       first == 'D' || first == 'Q') {
2547c5630feSpeter klausler     Put('.'); // input field is normalized to a fraction
2557c5630feSpeter klausler     auto start{got};
256991696c2SPeter Klausler     for (; next; next = io.NextInField(remaining, edit)) {
2573b635714Speter klausler       char32_t ch{*next};
2588305a92aSpeter klausler       if (ch == ' ' || ch == '\t') {
259b2cf572bSpeter klausler         if (bzMode) {
2603b635714Speter klausler           ch = '0'; // BZ mode - treat blank as if it were zero
2613b635714Speter klausler         } else {
2623b635714Speter klausler           continue;
2633b635714Speter klausler         }
2643b635714Speter klausler       }
2659bb091a8Speter klausler       if (ch == '0' && got == start && !decimalPoint) {
2669bb091a8Speter klausler         // omit leading zeroes before the decimal
2673b635714Speter klausler       } else if (ch >= '0' && ch <= '9') {
2687c5630feSpeter klausler         Put(ch);
2693b635714Speter klausler       } else if (ch == decimal && !decimalPoint) {
2703b635714Speter klausler         // the decimal point is *not* copied to the buffer
2713b635714Speter klausler         decimalPoint = got - start; // # of digits before the decimal point
2723b635714Speter klausler       } else {
2733b635714Speter klausler         break;
2743b635714Speter klausler       }
2753b635714Speter klausler     }
2767c5630feSpeter klausler     if (got == start) {
277839f0abdSPeter Klausler       // Nothing but zeroes and maybe a decimal point.  F'2018 requires
278839f0abdSPeter Klausler       // at least one digit, but F'77 did not, and a bare "." shows up in
279839f0abdSPeter Klausler       // the FCVS suite.
2807c5630feSpeter klausler       Put('0'); // emit at least one digit
2813b635714Speter klausler     }
2823b635714Speter klausler     if (next &&
2833b635714Speter klausler         (*next == 'e' || *next == 'E' || *next == 'd' || *next == 'D' ||
2843b635714Speter klausler             *next == 'q' || *next == 'Q')) {
285b2cf572bSpeter klausler       // Optional exponent letter.  Blanks are allowed between the
286b2cf572bSpeter klausler       // optional exponent letter and the exponent value.
2873b635714Speter klausler       io.SkipSpaces(remaining);
288991696c2SPeter Klausler       next = io.NextInField(remaining, edit);
2893b635714Speter klausler     }
290b2cf572bSpeter klausler     // The default exponent is -kP, but the scale factor doesn't affect
291b2cf572bSpeter klausler     // an explicit exponent.
292b2cf572bSpeter klausler     exponent = -edit.modes.scale;
2933b635714Speter klausler     if (next &&
294b2cf572bSpeter klausler         (*next == '-' || *next == '+' || (*next >= '0' && *next <= '9') ||
295f1dbf8e4SPeter Klausler             *next == ' ' || *next == '\t')) {
2963b635714Speter klausler       bool negExpo{*next == '-'};
2973b635714Speter klausler       if (negExpo || *next == '+') {
298991696c2SPeter Klausler         next = io.NextInField(remaining, edit);
2993b635714Speter klausler       }
300991696c2SPeter Klausler       for (exponent = 0; next; next = io.NextInField(remaining, edit)) {
301b2cf572bSpeter klausler         if (*next >= '0' && *next <= '9') {
3024c42e67bSPeter Klausler           if (exponent < 10000) {
3033b635714Speter klausler             exponent = 10 * exponent + *next - '0';
3044c42e67bSPeter Klausler           }
305f1dbf8e4SPeter Klausler         } else if (*next == ' ' || *next == '\t') {
306f1dbf8e4SPeter Klausler           if (bzMode) {
307b2cf572bSpeter klausler             exponent = 10 * exponent;
308f1dbf8e4SPeter Klausler           }
309b2cf572bSpeter klausler         } else {
310b2cf572bSpeter klausler           break;
311b2cf572bSpeter klausler         }
3123b635714Speter klausler       }
3133b635714Speter klausler       if (negExpo) {
3143b635714Speter klausler         exponent = -exponent;
3153b635714Speter klausler       }
3163b635714Speter klausler     }
3173b635714Speter klausler     if (decimalPoint) {
3183b635714Speter klausler       exponent += *decimalPoint;
3193b635714Speter klausler     } else {
3203b635714Speter klausler       // When no decimal point (or comma) appears in the value, the 'd'
3213b635714Speter klausler       // part of the edit descriptor must be interpreted as the number of
3223b635714Speter klausler       // digits in the value to be interpreted as being to the *right* of
3233b635714Speter klausler       // the assumed decimal point (13.7.2.3.2)
3243b635714Speter klausler       exponent += got - start - edit.digits.value_or(0);
3253b635714Speter klausler     }
3263b635714Speter klausler   } else {
3273b635714Speter klausler     // TODO: hex FP input
3283b635714Speter klausler     exponent = 0;
3293b635714Speter klausler     return 0;
3303b635714Speter klausler   }
3316a1c3efaSpeter klausler   // Consume the trailing ')' of a list-directed or NAMELIST complex
3326a1c3efaSpeter klausler   // input value.
3336a1c3efaSpeter klausler   if (edit.descriptor == DataEdit::ListDirectedImaginaryPart) {
3346a1c3efaSpeter klausler     if (next && (*next == ' ' || *next == '\t')) {
335991696c2SPeter Klausler       next = io.NextInField(remaining, edit);
3366a1c3efaSpeter klausler     }
3376a1c3efaSpeter klausler     if (!next) { // NextInField fails on separators like ')'
338bafbae23SPeter Klausler       std::size_t byteCount{0};
339bafbae23SPeter Klausler       next = io.GetCurrentChar(byteCount);
3406a1c3efaSpeter klausler       if (next && *next == ')') {
341bafbae23SPeter Klausler         io.HandleRelativePosition(byteCount);
3426a1c3efaSpeter klausler       }
3436a1c3efaSpeter klausler     }
3446a1c3efaSpeter klausler   } else if (remaining) {
3458305a92aSpeter klausler     while (next && (*next == ' ' || *next == '\t')) {
346991696c2SPeter Klausler       next = io.NextInField(remaining, edit);
3473b635714Speter klausler     }
3483b635714Speter klausler     if (next) {
3493b635714Speter klausler       return 0; // error: unused nonblank character in fixed-width field
3503b635714Speter klausler     }
3513b635714Speter klausler   }
3523b635714Speter klausler   return got;
3533b635714Speter klausler }
3543b635714Speter klausler 
RaiseFPExceptions(decimal::ConversionResultFlags flags)355de026aebSPeter Klausler static void RaiseFPExceptions(decimal::ConversionResultFlags flags) {
356de026aebSPeter Klausler #undef RAISE
357de026aebSPeter Klausler #ifdef feraisexcept // a macro in some environments; omit std::
358de026aebSPeter Klausler #define RAISE feraiseexcept
359de026aebSPeter Klausler #else
360de026aebSPeter Klausler #define RAISE std::feraiseexcept
361de026aebSPeter Klausler #endif
362de026aebSPeter Klausler   if (flags & decimal::ConversionResultFlags::Overflow) {
363de026aebSPeter Klausler     RAISE(FE_OVERFLOW);
364de026aebSPeter Klausler   }
365de026aebSPeter Klausler   if (flags & decimal::ConversionResultFlags::Inexact) {
366de026aebSPeter Klausler     RAISE(FE_INEXACT);
367de026aebSPeter Klausler   }
368de026aebSPeter Klausler   if (flags & decimal::ConversionResultFlags::Invalid) {
369de026aebSPeter Klausler     RAISE(FE_INVALID);
370de026aebSPeter Klausler   }
371de026aebSPeter Klausler #undef RAISE
372de026aebSPeter Klausler }
373de026aebSPeter Klausler 
374da25f968SPeter Klausler // If no special modes are in effect and the form of the input value
375da25f968SPeter Klausler // that's present in the input stream is acceptable to the decimal->binary
376da25f968SPeter Klausler // converter without modification, this fast path for real input
377da25f968SPeter Klausler // saves time by avoiding memory copies and reformatting of the exponent.
378da25f968SPeter Klausler template <int PRECISION>
TryFastPathRealInput(IoStatementState & io,const DataEdit & edit,void * n)379da25f968SPeter Klausler static bool TryFastPathRealInput(
380da25f968SPeter Klausler     IoStatementState &io, const DataEdit &edit, void *n) {
381da25f968SPeter Klausler   if (edit.modes.editingFlags & (blankZero | decimalComma)) {
382da25f968SPeter Klausler     return false;
383da25f968SPeter Klausler   }
384da25f968SPeter Klausler   if (edit.modes.scale != 0) {
385da25f968SPeter Klausler     return false;
386da25f968SPeter Klausler   }
387da25f968SPeter Klausler   const char *str{nullptr};
388da25f968SPeter Klausler   std::size_t got{io.GetNextInputBytes(str)};
389da25f968SPeter Klausler   if (got == 0 || str == nullptr ||
390da25f968SPeter Klausler       !io.GetConnectionState().recordLength.has_value()) {
391da25f968SPeter Klausler     return false; // could not access reliably-terminated input stream
392da25f968SPeter Klausler   }
393da25f968SPeter Klausler   const char *p{str};
394da25f968SPeter Klausler   std::int64_t maxConsume{
395da25f968SPeter Klausler       std::min<std::int64_t>(got, edit.width.value_or(got))};
396da25f968SPeter Klausler   const char *limit{str + maxConsume};
397da25f968SPeter Klausler   decimal::ConversionToBinaryResult<PRECISION> converted{
398da25f968SPeter Klausler       decimal::ConvertToBinary<PRECISION>(p, edit.modes.round, limit)};
3994c42e67bSPeter Klausler   if (converted.flags & (decimal::Invalid | decimal::Overflow)) {
400da25f968SPeter Klausler     return false;
401da25f968SPeter Klausler   }
402f1dbf8e4SPeter Klausler   if (edit.digits.value_or(0) != 0) {
403f1dbf8e4SPeter Klausler     // Edit descriptor is Fw.d (or other) with d != 0, which
404f1dbf8e4SPeter Klausler     // implies scaling
405f1dbf8e4SPeter Klausler     const char *q{str};
406f1dbf8e4SPeter Klausler     for (; q < limit; ++q) {
407f1dbf8e4SPeter Klausler       if (*q == '.' || *q == 'n' || *q == 'N') {
408f1dbf8e4SPeter Klausler         break;
409f1dbf8e4SPeter Klausler       }
410f1dbf8e4SPeter Klausler     }
411f1dbf8e4SPeter Klausler     if (q == limit) {
412f1dbf8e4SPeter Klausler       // No explicit decimal point, and not NaN/Inf.
413da25f968SPeter Klausler       return false;
414da25f968SPeter Klausler     }
415f1dbf8e4SPeter Klausler   }
416da25f968SPeter Klausler   for (; p < limit && (*p == ' ' || *p == '\t'); ++p) {
417da25f968SPeter Klausler   }
418da25f968SPeter Klausler   if (edit.descriptor == DataEdit::ListDirectedImaginaryPart) {
419d02b318aSPeter Klausler     // Need to consume a trailing ')' and any white space after
420da25f968SPeter Klausler     if (p >= limit || *p != ')') {
421da25f968SPeter Klausler       return false;
422da25f968SPeter Klausler     }
423d02b318aSPeter Klausler     for (++p; p < limit && (*p == ' ' || *p == '\t'); ++p) {
424da25f968SPeter Klausler     }
425da25f968SPeter Klausler   }
426d02b318aSPeter Klausler   if (edit.width && p < str + *edit.width) {
427d02b318aSPeter Klausler     return false; // unconverted characters remain in fixed width field
428da25f968SPeter Klausler   }
429da25f968SPeter Klausler   // Success on the fast path!
430da25f968SPeter Klausler   *reinterpret_cast<decimal::BinaryFloatingPointNumber<PRECISION> *>(n) =
431da25f968SPeter Klausler       converted.binary;
432da25f968SPeter Klausler   io.HandleRelativePosition(p - str);
433de026aebSPeter Klausler   // Set FP exception flags
434de026aebSPeter Klausler   if (converted.flags != decimal::ConversionResultFlags::Exact) {
435de026aebSPeter Klausler     RaiseFPExceptions(converted.flags);
436de026aebSPeter Klausler   }
437da25f968SPeter Klausler   return true;
438da25f968SPeter Klausler }
439da25f968SPeter Klausler 
440d56fdc8eSpeter klausler template <int KIND>
EditCommonRealInput(IoStatementState & io,const DataEdit & edit,void * n)4413b635714Speter klausler bool EditCommonRealInput(IoStatementState &io, const DataEdit &edit, void *n) {
442d56fdc8eSpeter klausler   constexpr int binaryPrecision{common::PrecisionOfRealKind(KIND)};
443da25f968SPeter Klausler   if (TryFastPathRealInput<binaryPrecision>(io, edit, n)) {
444da25f968SPeter Klausler     return true;
445da25f968SPeter Klausler   }
446da25f968SPeter Klausler   // Fast path wasn't available or didn't work; go the more general route
4473b635714Speter klausler   static constexpr int maxDigits{
4483b635714Speter klausler       common::MaxDecimalConversionDigits(binaryPrecision)};
4493b635714Speter klausler   static constexpr int bufferSize{maxDigits + 18};
4503b635714Speter klausler   char buffer[bufferSize];
4513b635714Speter klausler   int exponent{0};
4523b635714Speter klausler   int got{ScanRealInput(buffer, maxDigits + 2, io, edit, exponent)};
4533b635714Speter klausler   if (got >= maxDigits + 2) {
4543bc2ae95Speter klausler     io.GetIoErrorHandler().Crash("EditCommonRealInput: buffer was too small");
4553b635714Speter klausler     return false;
4563b635714Speter klausler   }
4573b635714Speter klausler   if (got == 0) {
458e6873bfbSPeter Klausler     io.GetIoErrorHandler().SignalError(IostatBadRealInput);
4593b635714Speter klausler     return false;
4603b635714Speter klausler   }
4613b635714Speter klausler   bool hadExtra{got > maxDigits};
4623b635714Speter klausler   if (exponent != 0) {
463da25f968SPeter Klausler     buffer[got++] = 'e';
464da25f968SPeter Klausler     if (exponent < 0) {
465da25f968SPeter Klausler       buffer[got++] = '-';
466da25f968SPeter Klausler       exponent = -exponent;
467da25f968SPeter Klausler     }
468da25f968SPeter Klausler     if (exponent > 9999) {
469da25f968SPeter Klausler       exponent = 9999; // will convert to +/-Inf
470da25f968SPeter Klausler     }
471da25f968SPeter Klausler     if (exponent > 999) {
472da25f968SPeter Klausler       int dig{exponent / 1000};
473da25f968SPeter Klausler       buffer[got++] = '0' + dig;
474da25f968SPeter Klausler       int rest{exponent - 1000 * dig};
475da25f968SPeter Klausler       dig = rest / 100;
476da25f968SPeter Klausler       buffer[got++] = '0' + dig;
477da25f968SPeter Klausler       rest -= 100 * dig;
478da25f968SPeter Klausler       dig = rest / 10;
479da25f968SPeter Klausler       buffer[got++] = '0' + dig;
480da25f968SPeter Klausler       buffer[got++] = '0' + (rest - 10 * dig);
481da25f968SPeter Klausler     } else if (exponent > 99) {
482da25f968SPeter Klausler       int dig{exponent / 100};
483da25f968SPeter Klausler       buffer[got++] = '0' + dig;
484da25f968SPeter Klausler       int rest{exponent - 100 * dig};
485da25f968SPeter Klausler       dig = rest / 10;
486da25f968SPeter Klausler       buffer[got++] = '0' + dig;
487da25f968SPeter Klausler       buffer[got++] = '0' + (rest - 10 * dig);
488da25f968SPeter Klausler     } else if (exponent > 9) {
489da25f968SPeter Klausler       int dig{exponent / 10};
490da25f968SPeter Klausler       buffer[got++] = '0' + dig;
491da25f968SPeter Klausler       buffer[got++] = '0' + (exponent - 10 * dig);
492da25f968SPeter Klausler     } else {
493da25f968SPeter Klausler       buffer[got++] = '0' + exponent;
494da25f968SPeter Klausler     }
4953b635714Speter klausler   }
4963b635714Speter klausler   buffer[got] = '\0';
4973b635714Speter klausler   const char *p{buffer};
4983b635714Speter klausler   decimal::ConversionToBinaryResult<binaryPrecision> converted{
4993b635714Speter klausler       decimal::ConvertToBinary<binaryPrecision>(p, edit.modes.round)};
5003b635714Speter klausler   if (hadExtra) {
5013b635714Speter klausler     converted.flags = static_cast<enum decimal::ConversionResultFlags>(
5023b635714Speter klausler         converted.flags | decimal::Inexact);
5033b635714Speter klausler   }
504f1dbf8e4SPeter Klausler   if (*p) { // unprocessed junk after value
505f1dbf8e4SPeter Klausler     io.GetIoErrorHandler().SignalError(IostatBadRealInput);
506f1dbf8e4SPeter Klausler     return false;
507f1dbf8e4SPeter Klausler   }
5083b635714Speter klausler   *reinterpret_cast<decimal::BinaryFloatingPointNumber<binaryPrecision> *>(n) =
5093b635714Speter klausler       converted.binary;
510de026aebSPeter Klausler   // Set FP exception flags
511de026aebSPeter Klausler   if (converted.flags != decimal::ConversionResultFlags::Exact) {
5129c54d762SPeter Klausler     if (converted.flags & decimal::ConversionResultFlags::Overflow) {
5139c54d762SPeter Klausler       io.GetIoErrorHandler().SignalError(IostatRealInputOverflow);
5149c54d762SPeter Klausler       return false;
5159c54d762SPeter Klausler     }
516de026aebSPeter Klausler     RaiseFPExceptions(converted.flags);
517de026aebSPeter Klausler   }
5183b635714Speter klausler   return true;
5193b635714Speter klausler }
5203b635714Speter klausler 
521d56fdc8eSpeter klausler template <int KIND>
EditRealInput(IoStatementState & io,const DataEdit & edit,void * n)5223b635714Speter klausler bool EditRealInput(IoStatementState &io, const DataEdit &edit, void *n) {
5233b635714Speter klausler   switch (edit.descriptor) {
5243b635714Speter klausler   case DataEdit::ListDirected:
525b8452dbaSpeter klausler     if (IsNamelistName(io)) {
526b8452dbaSpeter klausler       return false;
527b8452dbaSpeter klausler     }
528b8452dbaSpeter klausler     return EditCommonRealInput<KIND>(io, edit, n);
5293bc2ae95Speter klausler   case DataEdit::ListDirectedRealPart:
5303bc2ae95Speter klausler   case DataEdit::ListDirectedImaginaryPart:
5313b635714Speter klausler   case 'F':
5323b635714Speter klausler   case 'E': // incl. EN, ES, & EX
5333b635714Speter klausler   case 'D':
5341f879005STim Keith   case 'G':
535d56fdc8eSpeter klausler     return EditCommonRealInput<KIND>(io, edit, n);
5363b635714Speter klausler   case 'B':
53753f775bbSPeter Klausler     return EditBOZInput<1>(io, edit, n,
53853f775bbSPeter Klausler         common::BitsForBinaryPrecision(common::PrecisionOfRealKind(KIND)) >> 3);
5393b635714Speter klausler   case 'O':
54053f775bbSPeter Klausler     return EditBOZInput<3>(io, edit, n,
54153f775bbSPeter Klausler         common::BitsForBinaryPrecision(common::PrecisionOfRealKind(KIND)) >> 3);
5423b635714Speter klausler   case 'Z':
54353f775bbSPeter Klausler     return EditBOZInput<4>(io, edit, n,
54453f775bbSPeter Klausler         common::BitsForBinaryPrecision(common::PrecisionOfRealKind(KIND)) >> 3);
545b83242e2Speter klausler   case 'A': // legacy extension
546bafbae23SPeter Klausler     return EditCharacterInput(io, edit, reinterpret_cast<char *>(n), KIND);
5473b635714Speter klausler   default:
5483b635714Speter klausler     io.GetIoErrorHandler().SignalError(IostatErrorInFormat,
5493b635714Speter klausler         "Data edit descriptor '%c' may not be used for REAL input",
5503b635714Speter klausler         edit.descriptor);
5513b635714Speter klausler     return false;
5523b635714Speter klausler   }
5533b635714Speter klausler }
5543b635714Speter klausler 
5553b635714Speter klausler // 13.7.3 in Fortran 2018
EditLogicalInput(IoStatementState & io,const DataEdit & edit,bool & x)5563b635714Speter klausler bool EditLogicalInput(IoStatementState &io, const DataEdit &edit, bool &x) {
5573b635714Speter klausler   switch (edit.descriptor) {
5583b635714Speter klausler   case DataEdit::ListDirected:
559b8452dbaSpeter klausler     if (IsNamelistName(io)) {
560b8452dbaSpeter klausler       return false;
561b8452dbaSpeter klausler     }
562b8452dbaSpeter klausler     break;
5633b635714Speter klausler   case 'L':
5641f879005STim Keith   case 'G':
5651f879005STim Keith     break;
5663b635714Speter klausler   default:
5673b635714Speter klausler     io.GetIoErrorHandler().SignalError(IostatErrorInFormat,
5683b635714Speter klausler         "Data edit descriptor '%c' may not be used for LOGICAL input",
5693b635714Speter klausler         edit.descriptor);
5703b635714Speter klausler     return false;
5713b635714Speter klausler   }
5723b635714Speter klausler   std::optional<int> remaining;
5736a1c3efaSpeter klausler   std::optional<char32_t> next{io.PrepareInput(edit, remaining)};
5743b635714Speter klausler   if (next && *next == '.') { // skip optional period
575991696c2SPeter Klausler     next = io.NextInField(remaining, edit);
5763b635714Speter klausler   }
5773b635714Speter klausler   if (!next) {
5783b635714Speter klausler     io.GetIoErrorHandler().SignalError("Empty LOGICAL input field");
5793b635714Speter klausler     return false;
5803b635714Speter klausler   }
5813b635714Speter klausler   switch (*next) {
5823b635714Speter klausler   case 'T':
5831f879005STim Keith   case 't':
5841f879005STim Keith     x = true;
5851f879005STim Keith     break;
5863b635714Speter klausler   case 'F':
5871f879005STim Keith   case 'f':
5881f879005STim Keith     x = false;
5891f879005STim Keith     break;
5903b635714Speter klausler   default:
5913b635714Speter klausler     io.GetIoErrorHandler().SignalError(
5923b635714Speter klausler         "Bad character '%lc' in LOGICAL input field", *next);
5933b635714Speter klausler     return false;
5943b635714Speter klausler   }
5953b635714Speter klausler   if (remaining) { // ignore the rest of the field
5963b635714Speter klausler     io.HandleRelativePosition(*remaining);
5978dbc86adSpeter klausler   } else if (edit.descriptor == DataEdit::ListDirected) {
598991696c2SPeter Klausler     while (io.NextInField(remaining, edit)) { // discard rest of field
5998dbc86adSpeter klausler     }
6003b635714Speter klausler   }
6013b635714Speter klausler   return true;
6023b635714Speter klausler }
6033b635714Speter klausler 
6043b635714Speter klausler // See 13.10.3.1 paragraphs 7-9 in Fortran 2018
605bafbae23SPeter Klausler template <typename CHAR>
EditDelimitedCharacterInput(IoStatementState & io,CHAR * x,std::size_t length,char32_t delimiter)6063b635714Speter klausler static bool EditDelimitedCharacterInput(
607bafbae23SPeter Klausler     IoStatementState &io, CHAR *x, std::size_t length, char32_t delimiter) {
6086a1c3efaSpeter klausler   bool result{true};
6093b635714Speter klausler   while (true) {
610bafbae23SPeter Klausler     std::size_t byteCount{0};
611bafbae23SPeter Klausler     auto ch{io.GetCurrentChar(byteCount)};
6126a1c3efaSpeter klausler     if (!ch) {
6136a1c3efaSpeter klausler       if (io.AdvanceRecord()) {
6146a1c3efaSpeter klausler         continue;
6156a1c3efaSpeter klausler       } else {
6166a1c3efaSpeter klausler         result = false; // EOF in character value
6176a1c3efaSpeter klausler         break;
6186a1c3efaSpeter klausler       }
6196a1c3efaSpeter klausler     }
620bafbae23SPeter Klausler     io.HandleRelativePosition(byteCount);
6213b635714Speter klausler     if (*ch == delimiter) {
622bafbae23SPeter Klausler       auto next{io.GetCurrentChar(byteCount)};
6234d42e16eSpeter klausler       if (next && *next == delimiter) {
6246a1c3efaSpeter klausler         // Repeated delimiter: use as character value
625bafbae23SPeter Klausler         io.HandleRelativePosition(byteCount);
6264d42e16eSpeter klausler       } else {
6274d42e16eSpeter klausler         break; // closing delimiter
6283b635714Speter klausler       }
6293b635714Speter klausler     }
6303b635714Speter klausler     if (length > 0) {
6313b635714Speter klausler       *x++ = *ch;
6323b635714Speter klausler       --length;
6333b635714Speter klausler     }
6346a1c3efaSpeter klausler   }
6353b635714Speter klausler   std::fill_n(x, length, ' ');
6366a1c3efaSpeter klausler   return result;
6373b635714Speter klausler }
6383b635714Speter klausler 
639bafbae23SPeter Klausler template <typename CHAR>
EditListDirectedCharacterInput(IoStatementState & io,CHAR * x,std::size_t length,const DataEdit & edit)640bafbae23SPeter Klausler static bool EditListDirectedCharacterInput(
641bafbae23SPeter Klausler     IoStatementState &io, CHAR *x, std::size_t length, const DataEdit &edit) {
642bafbae23SPeter Klausler   std::size_t byteCount{0};
643bafbae23SPeter Klausler   auto ch{io.GetCurrentChar(byteCount)};
6443b635714Speter klausler   if (ch && (*ch == '\'' || *ch == '"')) {
645bafbae23SPeter Klausler     io.HandleRelativePosition(byteCount);
6463b635714Speter klausler     return EditDelimitedCharacterInput(io, x, length, *ch);
6473b635714Speter klausler   }
6480f5c60f1SPeter Klausler   if (IsNamelistName(io) || io.GetConnectionState().IsAtEOF()) {
649b8452dbaSpeter klausler     return false;
650b8452dbaSpeter klausler   }
6513b635714Speter klausler   // Undelimited list-directed character input: stop at a value separator
652bafbae23SPeter Klausler   // or the end of the current record.  Subtlety: the "remaining" count
653bafbae23SPeter Klausler   // here is a dummy that's used to avoid the interpretation of separators
654bafbae23SPeter Klausler   // in NextInField.
65553f775bbSPeter Klausler   std::optional<int> remaining{length > 0 ? maxUTF8Bytes : 0};
656991696c2SPeter Klausler   while (std::optional<char32_t> next{io.NextInField(remaining, edit)}) {
657*cb193931SPeter Klausler     bool isSep{false};
6583b635714Speter klausler     switch (*next) {
6593b635714Speter klausler     case ' ':
6608305a92aSpeter klausler     case '\t':
6613b635714Speter klausler     case '/':
662*cb193931SPeter Klausler       isSep = true;
663*cb193931SPeter Klausler       break;
664*cb193931SPeter Klausler     case ',':
665*cb193931SPeter Klausler       isSep = !(edit.modes.editingFlags & decimalComma);
666*cb193931SPeter Klausler       break;
667*cb193931SPeter Klausler     case ';':
668*cb193931SPeter Klausler       isSep = !!(edit.modes.editingFlags & decimalComma);
6693b635714Speter klausler       break;
6701f879005STim Keith     default:
671*cb193931SPeter Klausler       break;
672*cb193931SPeter Klausler     }
673*cb193931SPeter Klausler     if (isSep) {
674*cb193931SPeter Klausler       remaining = 0;
675*cb193931SPeter Klausler     } else {
6761f879005STim Keith       *x++ = *next;
67753f775bbSPeter Klausler       remaining = --length > 0 ? maxUTF8Bytes : 0;
6783b635714Speter klausler     }
6793b635714Speter klausler   }
6803b635714Speter klausler   std::fill_n(x, length, ' ');
6813b635714Speter klausler   return true;
6823b635714Speter klausler }
6833b635714Speter klausler 
684bafbae23SPeter Klausler template <typename CHAR>
EditCharacterInput(IoStatementState & io,const DataEdit & edit,CHAR * x,std::size_t length)685bafbae23SPeter Klausler bool EditCharacterInput(
686bafbae23SPeter Klausler     IoStatementState &io, const DataEdit &edit, CHAR *x, std::size_t length) {
6873b635714Speter klausler   switch (edit.descriptor) {
6883b635714Speter klausler   case DataEdit::ListDirected:
689bafbae23SPeter Klausler     return EditListDirectedCharacterInput(io, x, length, edit);
6903b635714Speter klausler   case 'A':
6911f879005STim Keith   case 'G':
6921f879005STim Keith     break;
69353f775bbSPeter Klausler   case 'B':
69453f775bbSPeter Klausler     return EditBOZInput<1>(io, edit, x, length * sizeof *x);
69553f775bbSPeter Klausler   case 'O':
69653f775bbSPeter Klausler     return EditBOZInput<3>(io, edit, x, length * sizeof *x);
69753f775bbSPeter Klausler   case 'Z':
69853f775bbSPeter Klausler     return EditBOZInput<4>(io, edit, x, length * sizeof *x);
6993b635714Speter klausler   default:
7003b635714Speter klausler     io.GetIoErrorHandler().SignalError(IostatErrorInFormat,
7013b635714Speter klausler         "Data edit descriptor '%c' may not be used with a CHARACTER data item",
7023b635714Speter klausler         edit.descriptor);
7033b635714Speter klausler     return false;
7043b635714Speter klausler   }
705bafbae23SPeter Klausler   const ConnectionState &connection{io.GetConnectionState()};
706ef7f6f7cSPeter Klausler   std::size_t remaining{length};
7073b635714Speter klausler   if (edit.width && *edit.width > 0) {
7083b635714Speter klausler     remaining = *edit.width;
7093b635714Speter klausler   }
7103b635714Speter klausler   // When the field is wider than the variable, we drop the leading
711cea8b8a7SPeter Klausler   // characters.  When the variable is wider than the field, there can be
7123b635714Speter klausler   // trailing padding.
713ef7f6f7cSPeter Klausler   const char *input{nullptr};
714ef7f6f7cSPeter Klausler   std::size_t ready{0};
715bafbae23SPeter Klausler   // Skip leading bytes.
716ef7f6f7cSPeter Klausler   // These bytes don't count towards INQUIRE(IOLENGTH=).
717bafbae23SPeter Klausler   std::size_t skip{remaining > length ? remaining - length : 0};
718ef7f6f7cSPeter Klausler   // Transfer payload bytes; these do count.
719ef7f6f7cSPeter Klausler   while (remaining > 0) {
720ef7f6f7cSPeter Klausler     if (ready == 0) {
721ef7f6f7cSPeter Klausler       ready = io.GetNextInputBytes(input);
722ef7f6f7cSPeter Klausler       if (ready == 0) {
723cea8b8a7SPeter Klausler         if (io.CheckForEndOfRecord()) {
724cea8b8a7SPeter Klausler           std::fill_n(x, length, ' '); // PAD='YES'
725cea8b8a7SPeter Klausler         }
726cea8b8a7SPeter Klausler         return !io.GetIoErrorHandler().InError();
727ef7f6f7cSPeter Klausler       }
728ef7f6f7cSPeter Klausler     }
729bafbae23SPeter Klausler     std::size_t chunk;
730bafbae23SPeter Klausler     bool skipping{skip > 0};
731bafbae23SPeter Klausler     if (connection.isUTF8) {
732bafbae23SPeter Klausler       chunk = MeasureUTF8Bytes(*input);
733bafbae23SPeter Klausler       if (skipping) {
734bafbae23SPeter Klausler         --skip;
735bafbae23SPeter Klausler       } else if (auto ucs{DecodeUTF8(input)}) {
736bafbae23SPeter Klausler         *x++ = *ucs;
737bafbae23SPeter Klausler         --length;
738bafbae23SPeter Klausler       } else if (chunk == 0) {
739bafbae23SPeter Klausler         // error recovery: skip bad encoding
740bafbae23SPeter Klausler         chunk = 1;
741bafbae23SPeter Klausler       }
742bafbae23SPeter Klausler       --remaining;
743ede42131SPeter Klausler     } else if constexpr (sizeof *x > 1) {
744ede42131SPeter Klausler       // Read single byte with expansion into multi-byte CHARACTER
745ede42131SPeter Klausler       chunk = 1;
746ede42131SPeter Klausler       if (skipping) {
747ede42131SPeter Klausler         --skip;
748bafbae23SPeter Klausler       } else {
749ede42131SPeter Klausler         *x++ = static_cast<unsigned char>(*input);
750ede42131SPeter Klausler         --length;
751ede42131SPeter Klausler       }
752ede42131SPeter Klausler       --remaining;
753ede42131SPeter Klausler     } else { // single bytes -> default CHARACTER
754bafbae23SPeter Klausler       if (skipping) {
755bafbae23SPeter Klausler         chunk = std::min<std::size_t>(skip, ready);
756bafbae23SPeter Klausler         skip -= chunk;
757bafbae23SPeter Klausler       } else {
758bafbae23SPeter Klausler         chunk = std::min<std::size_t>(remaining, ready);
759ef7f6f7cSPeter Klausler         std::memcpy(x, input, chunk);
760ef7f6f7cSPeter Klausler         x += chunk;
761ef7f6f7cSPeter Klausler         length -= chunk;
762ef7f6f7cSPeter Klausler       }
763bafbae23SPeter Klausler       remaining -= chunk;
764bafbae23SPeter Klausler     }
765bafbae23SPeter Klausler     input += chunk;
766bafbae23SPeter Klausler     if (!skipping) {
767bafbae23SPeter Klausler       io.GotChar(chunk);
768bafbae23SPeter Klausler     }
769bafbae23SPeter Klausler     io.HandleRelativePosition(chunk);
770bafbae23SPeter Klausler     ready -= chunk;
771bafbae23SPeter Klausler   }
772ef7f6f7cSPeter Klausler   // Pad the remainder of the input variable, if any.
773bafbae23SPeter Klausler   std::fill_n(x, length, ' ');
7743b635714Speter klausler   return true;
7753b635714Speter klausler }
7763b635714Speter klausler 
777d56fdc8eSpeter klausler template bool EditRealInput<2>(IoStatementState &, const DataEdit &, void *);
778d56fdc8eSpeter klausler template bool EditRealInput<3>(IoStatementState &, const DataEdit &, void *);
779d56fdc8eSpeter klausler template bool EditRealInput<4>(IoStatementState &, const DataEdit &, void *);
7803b635714Speter klausler template bool EditRealInput<8>(IoStatementState &, const DataEdit &, void *);
781d56fdc8eSpeter klausler template bool EditRealInput<10>(IoStatementState &, const DataEdit &, void *);
782d56fdc8eSpeter klausler // TODO: double/double
783d56fdc8eSpeter klausler template bool EditRealInput<16>(IoStatementState &, const DataEdit &, void *);
784bafbae23SPeter Klausler 
785bafbae23SPeter Klausler template bool EditCharacterInput(
786bafbae23SPeter Klausler     IoStatementState &, const DataEdit &, char *, std::size_t);
787bafbae23SPeter Klausler template bool EditCharacterInput(
788bafbae23SPeter Klausler     IoStatementState &, const DataEdit &, char16_t *, std::size_t);
789bafbae23SPeter Klausler template bool EditCharacterInput(
790bafbae23SPeter Klausler     IoStatementState &, const DataEdit &, char32_t *, std::size_t);
791bafbae23SPeter Klausler 
7921f879005STim Keith } // namespace Fortran::runtime::io
793