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