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