#include "edit-input.h"
#include "namelist.h"
#include "utf.h"
#include "flang/Common/optional.h"
#include "flang/Common/real.h"
#include "flang/Common/uint128.h"
#include "flang/Runtime/freestanding-tools.h"
#include <algorithm>
#include <cfenv>
namespace Fortran::runtime::io {
RT_OFFLOAD_API_GROUP_BEGIN
static inline RT_API_ATTRS bool IsCharValueSeparator(
const DataEdit &edit, char32_t ch) {
char32_t comma{
edit.modes.editingFlags & decimalComma ? char32_t{';'} : char32_t{','}};
return ch == ' ' || ch == '\t' || ch == comma || ch == '/' ||
(edit.IsNamelist() && (ch == '&' || ch == '$'));
}
static RT_API_ATTRS bool CheckCompleteListDirectedField(
IoStatementState &io, const DataEdit &edit) {
if (edit.IsListDirected()) {
std::size_t byteCount;
if (auto ch{io.GetCurrentChar(byteCount)}) {
if (IsCharValueSeparator(edit, *ch)) {
return true;
} else {
const auto &connection{io.GetConnectionState()};
io.GetIoErrorHandler().SignalError(IostatBadListDirectedInputSeparator,
"invalid character (0x%x) after list-directed input value, "
"at column %d in record %d",
static_cast<unsigned>(*ch),
static_cast<int>(connection.positionInRecord + 1),
static_cast<int>(connection.currentRecordNumber));
return false;
}
} else {
return true;
}
} else {
return true;
}
}
template <int LOG2_BASE>
static RT_API_ATTRS bool EditBOZInput(
IoStatementState &io, const DataEdit &edit, void *n, std::size_t bytes) {
Fortran::common::optional<int> remaining{io.CueUpInput(edit)};
auto start{io.GetConnectionState().positionInRecord};
Fortran::common::optional<char32_t> next{io.NextInField(remaining, edit)};
if (next.value_or('?') == '0') {
do {
start = io.GetConnectionState().positionInRecord;
next = io.NextInField(remaining, edit);
} while (next && *next == '0');
}
int digits{0};
int significantBits{0};
for (; next; next = io.NextInField(remaining, edit)) {
char32_t ch{*next};
if (ch == ' ' || ch == '\t') {
if (edit.modes.editingFlags & blankZero) {
ch = '0';
} else {
continue;
}
}
if (ch >= '0' && ch <= '1') {
} else if (LOG2_BASE >= 3 && ch >= '2' && ch <= '7') {
} else if (LOG2_BASE >= 4 && ch >= '8' && ch <= '9') {
} else if (LOG2_BASE >= 4 && ch >= 'A' && ch <= 'F') {
} else if (LOG2_BASE >= 4 && ch >= 'a' && ch <= 'f') {
} else if (ch == ',') {
break;
} else {
io.GetIoErrorHandler().SignalError(
"Bad character '%lc' in B/O/Z input field", ch);
return false;
}
if (digits++ == 0) {
significantBits = 4;
if (ch >= '0' && ch <= '1') {
significantBits = 1;
} else if (ch >= '2' && ch <= '3') {
significantBits = 2;
} else if (ch >= '4' && ch <= '7') {
significantBits = 3;
} else {
significantBits = 4;
}
} else {
significantBits += LOG2_BASE;
}
}
auto significantBytes{static_cast<std::size_t>(significantBits + 7) / 8};
if (significantBytes > bytes) {
io.GetIoErrorHandler().SignalError(IostatBOZInputOverflow,
"B/O/Z input of %d digits overflows %zd-byte variable", digits, bytes);
return false;
}
io.HandleAbsolutePosition(start);
remaining.reset();
std::memset(n, 0, bytes);
int increment{isHostLittleEndian ? -1 : 1};
auto *data{reinterpret_cast<unsigned char *>(n) +
(isHostLittleEndian ? significantBytes - 1 : 0)};
int shift{((digits - 1) * LOG2_BASE) & 7};
while (digits > 0) {
char32_t ch{*io.NextInField(remaining, edit)};
int digit{0};
if (ch == ' ' || ch == '\t') {
if (edit.modes.editingFlags & blankZero) {
ch = '0';
} else {
continue;
}
}
--digits;
if (ch >= '0' && ch <= '9') {
digit = ch - '0';
} else if (ch >= 'A' && ch <= 'F') {
digit = ch + 10 - 'A';
} else if (ch >= 'a' && ch <= 'f') {
digit = ch + 10 - 'a';
} else {
continue;
}
if (shift < 0) {
if (shift + LOG2_BASE > 0) {
*data |= digit >> -shift;
}
shift += 8;
data += increment;
}
*data |= digit << shift;
shift -= LOG2_BASE;
}
return CheckCompleteListDirectedField(io, edit);
}
static inline RT_API_ATTRS char32_t GetRadixPointChar(const DataEdit &edit) {
return edit.modes.editingFlags & decimalComma ? char32_t{','} : char32_t{'.'};
}
static RT_API_ATTRS char ScanNumericPrefix(IoStatementState &io,
const DataEdit &edit, Fortran::common::optional<char32_t> &next,
Fortran::common::optional<int> &remaining) {
remaining = io.CueUpInput(edit);
next = io.NextInField(remaining, edit);
char sign{'\0'};
if (next) {
if (*next == '-' || *next == '+') {
sign = *next;
if (!edit.IsListDirected()) {
io.SkipSpaces(remaining);
}
next = io.NextInField(remaining, edit);
}
}
return sign;
}
RT_API_ATTRS bool EditIntegerInput(
IoStatementState &io, const DataEdit &edit, void *n, int kind) {
RUNTIME_CHECK(io.GetIoErrorHandler(), kind >= 1 && !(kind & (kind - 1)));
switch (edit.descriptor) {
case DataEdit::ListDirected:
if (IsNamelistNameOrSlash(io)) {
return false;
}
break;
case 'G':
case 'I':
break;
case 'B':
return EditBOZInput<1>(io, edit, n, kind);
case 'O':
return EditBOZInput<3>(io, edit, n, kind);
case 'Z':
return EditBOZInput<4>(io, edit, n, kind);
case 'A':
return EditCharacterInput(io, edit, reinterpret_cast<char *>(n), kind);
default:
io.GetIoErrorHandler().SignalError(IostatErrorInFormat,
"Data edit descriptor '%c' may not be used with an INTEGER data item",
edit.descriptor);
return false;
}
Fortran::common::optional<int> remaining;
Fortran::common::optional<char32_t> next;
char sign{ScanNumericPrefix(io, edit, next, remaining)};
common::UnsignedInt128 value{0};
bool any{!!sign};
bool overflow{false};
for (; next; next = io.NextInField(remaining, edit)) {
char32_t ch{*next};
if (ch == ' ' || ch == '\t') {
if (edit.modes.editingFlags & blankZero) {
ch = '0';
} else {
continue;
}
}
int digit{0};
if (ch >= '0' && ch <= '9') {
digit = ch - '0';
} else if (ch == ',') {
break;
} else {
io.GetIoErrorHandler().SignalError(
"Bad character '%lc' in INTEGER input field", ch);
return false;
}
static constexpr auto maxu128{~common::UnsignedInt128{0}};
static constexpr auto maxu128OverTen{maxu128 / 10};
static constexpr int maxLastDigit{
static_cast<int>(maxu128 - (maxu128OverTen * 10))};
overflow |= value >= maxu128OverTen &&
(value > maxu128OverTen || digit > maxLastDigit);
value *= 10;
value += digit;
any = true;
}
if (!any && !remaining) {
io.GetIoErrorHandler().SignalError(
"Integer value absent from NAMELIST or list-directed input");
return false;
}
auto maxForKind{common::UnsignedInt128{1} << ((8 * kind) - 1)};
overflow |= value >= maxForKind && (value > maxForKind || sign != '-');
if (overflow) {
io.GetIoErrorHandler().SignalError(IostatIntegerInputOverflow,
"Decimal input overflows INTEGER(%d) variable", kind);
return false;
}
if (sign == '-') {
value = -value;
}
if (any || !io.GetIoErrorHandler().InError()) {
auto shft{static_cast<int>(sizeof(value.low())) - kind};
if (!isHostLittleEndian && shft >= 0) {
auto l{value.low() << (8 * shft)};
std::memcpy(n, &l, kind);
} else {
std::memcpy(n, &value, kind);
}
return true;
} else {
return false;
}
}
struct ScannedRealInput {
int got{0};
int exponent{0};
bool isHexadecimal{false};
};
static RT_API_ATTRS ScannedRealInput ScanRealInput(
char *buffer, int bufferSize, IoStatementState &io, const DataEdit &edit) {
Fortran::common::optional<int> remaining;
Fortran::common::optional<char32_t> next;
int got{0};
Fortran::common::optional<int> radixPointOffset;
auto Put = [&](char ch) -> void {
if (got < bufferSize) {
buffer[got] = ch;
}
++got;
};
char sign{ScanNumericPrefix(io, edit, next, remaining)};
if (sign == '-') {
Put('-');
}
bool bzMode{(edit.modes.editingFlags & blankZero) != 0};
int exponent{0};
if (!next || (!bzMode && *next == ' ') ||
(!(edit.modes.editingFlags & decimalComma) && *next == ',')) {
if (!edit.IsListDirected() && !io.GetConnectionState().IsAtEOF()) {
Put('0');
}
return {got, exponent, false};
}
char32_t radixPointChar{GetRadixPointChar(edit)};
char32_t first{*next >= 'a' && *next <= 'z' ? *next + 'A' - 'a' : *next};
bool isHexadecimal{false};
if (first == 'N' || first == 'I') {
for (; next &&
((*next >= 'a' && *next <= 'z') || (*next >= 'A' && *next <= 'Z'));
next = io.NextInField(remaining, edit)) {
if (*next >= 'a' && *next <= 'z') {
Put(*next - 'a' + 'A');
} else {
Put(*next);
}
}
if (next && *next == '(') {
Put('(');
int depth{1};
while (true) {
next = io.NextInField(remaining, edit);
if (depth == 0) {
break;
} else if (!next) {
return {};
} else if (*next == '(') {
++depth;
} else if (*next == ')') {
--depth;
}
Put(*next);
}
}
} else if (first == radixPointChar || (first >= '0' && first <= '9') ||
(bzMode && (first == ' ' || first == '\t')) || first == 'E' ||
first == 'D' || first == 'Q') {
if (first == '0') {
next = io.NextInField(remaining, edit);
if (next && (*next == 'x' || *next == 'X')) {
isHexadecimal = true;
next = io.NextInField(remaining, edit);
} else {
Put('0');
}
}
if (!isHexadecimal) {
Put('.');
}
auto start{got};
for (; next; next = io.NextInField(remaining, edit)) {
char32_t ch{*next};
if (ch == ' ' || ch == '\t') {
if (isHexadecimal) {
return {};
} else if (bzMode) {
ch = '0';
} else {
continue;
}
}
if (ch == '0' && got == start && !radixPointOffset) {
} else if (ch >= '0' && ch <= '9') {
Put(ch);
} else if (ch == radixPointChar && !radixPointOffset) {
radixPointOffset = got - start;
} else if (isHexadecimal && ch >= 'A' && ch <= 'F') {
Put(ch);
} else if (isHexadecimal && ch >= 'a' && ch <= 'f') {
Put(ch - 'a' + 'A');
} else {
break;
}
}
if (got == start) {
Put('0');
}
auto nextBeforeExponent{next};
auto startExponent{io.GetConnectionState().positionInRecord};
bool hasGoodExponent{false};
if (next) {
if (isHexadecimal) {
if (*next == 'p' || *next == 'P') {
next = io.NextInField(remaining, edit);
} else {
return {};
}
} else if (*next == 'e' || *next == 'E' || *next == 'd' || *next == 'D' ||
*next == 'q' || *next == 'Q') {
io.SkipSpaces(remaining);
next = io.NextInField(remaining, edit);
}
}
if (next &&
(*next == '-' || *next == '+' || (*next >= '0' && *next <= '9') ||
*next == ' ' || *next == '\t')) {
bool negExpo{*next == '-'};
if (negExpo || *next == '+') {
next = io.NextInField(remaining, edit);
}
for (; next; next = io.NextInField(remaining, edit)) {
if (*next >= '0' && *next <= '9') {
hasGoodExponent = true;
if (exponent < 10000) {
exponent = 10 * exponent + *next - '0';
}
} else if (*next == ' ' || *next == '\t') {
if (isHexadecimal) {
break;
} else if (bzMode) {
hasGoodExponent = true;
exponent = 10 * exponent;
}
} else {
break;
}
}
if (negExpo) {
exponent = -exponent;
}
}
if (!hasGoodExponent) {
if (isHexadecimal) {
return {};
}
next = nextBeforeExponent;
io.HandleAbsolutePosition(startExponent);
exponent = -edit.modes.scale;
}
if (isHexadecimal) {
exponent += radixPointOffset.value_or(got - start) * 4;
} else if (radixPointOffset) {
exponent += *radixPointOffset;
} else {
exponent += got - start - edit.digits.value_or(0);
}
}
if (edit.descriptor == DataEdit::ListDirectedImaginaryPart) {
if (next && (*next == ' ' || *next == '\t')) {
io.SkipSpaces(remaining);
next = io.NextInField(remaining, edit);
}
if (!next) {
std::size_t byteCount{0};
next = io.GetCurrentChar(byteCount);
if (next && *next == ')') {
io.HandleRelativePosition(byteCount);
}
}
} else if (remaining) {
while (next && (*next == ' ' || *next == '\t')) {
next = io.NextInField(remaining, edit);
}
if (next && (*next != ',' || (edit.modes.editingFlags & decimalComma))) {
return {};
}
}
return {got, exponent, isHexadecimal};
}
static RT_API_ATTRS void RaiseFPExceptions(
decimal::ConversionResultFlags flags) {
#undef RAISE
#if defined(RT_DEVICE_COMPILATION)
Terminator terminator(__FILE__, __LINE__);
#define RAISE(e) \
terminator.Crash( \
"not implemented yet: raising FP exception in device code: %s", #e);
#else
#ifdef feraisexcept
#define RAISE feraiseexcept
#else
#define RAISE std::feraiseexcept
#endif
#endif
if (flags & decimal::ConversionResultFlags::Overflow) {
RAISE(FE_OVERFLOW);
}
if (flags & decimal::ConversionResultFlags::Underflow) {
RAISE(FE_UNDERFLOW);
}
if (flags & decimal::ConversionResultFlags::Inexact) {
RAISE(FE_INEXACT);
}
if (flags & decimal::ConversionResultFlags::Invalid) {
RAISE(FE_INVALID);
}
#undef RAISE
}
template <int PRECISION>
static RT_API_ATTRS bool TryFastPathRealDecimalInput(
IoStatementState &io, const DataEdit &edit, void *n) {
if (edit.modes.editingFlags & (blankZero | decimalComma)) {
return false;
}
if (edit.modes.scale != 0) {
return false;
}
const ConnectionState &connection{io.GetConnectionState()};
if (connection.internalIoCharKind > 1) {
return false;
}
const char *str{nullptr};
std::size_t got{io.GetNextInputBytes(str)};
if (got == 0 || str == nullptr || !connection.recordLength.has_value()) {
return false;
}
const char *p{str};
std::int64_t maxConsume{
std::min<std::int64_t>(got, edit.width.value_or(got))};
const char *limit{str + maxConsume};
decimal::ConversionToBinaryResult<PRECISION> converted{
decimal::ConvertToBinary<PRECISION>(p, edit.modes.round, limit)};
if (converted.flags & (decimal::Invalid | decimal::Overflow)) {
return false;
}
if (edit.digits.value_or(0) != 0) {
const char *q{str};
for (; q < limit; ++q) {
if (*q == '.' || *q == 'n' || *q == 'N') {
break;
}
}
if (q == limit) {
return false;
}
}
if (edit.descriptor == DataEdit::ListDirectedImaginaryPart) {
for (; p < limit && (*p == ' ' || *p == '\t'); ++p) {
}
if (p < limit && *p == ')') {
++p;
} else {
return false;
}
} else if (edit.IsListDirected()) {
if (p < limit && !IsCharValueSeparator(edit, *p)) {
return false;
}
} else {
for (; p < limit && (*p == ' ' || *p == '\t'); ++p) {
}
if (edit.width && p < str + *edit.width) {
return false;
}
}
*reinterpret_cast<decimal::BinaryFloatingPointNumber<PRECISION> *>(n) =
converted.binary;
io.HandleRelativePosition(p - str);
if (converted.flags != decimal::ConversionResultFlags::Exact) {
RaiseFPExceptions(converted.flags);
}
return true;
}
template <int binaryPrecision>
RT_API_ATTRS decimal::ConversionToBinaryResult<binaryPrecision>
ConvertHexadecimal(
const char *&p, enum decimal::FortranRounding rounding, int expo) {
using RealType = decimal::BinaryFloatingPointNumber<binaryPrecision>;
using RawType = typename RealType::RawType;
bool isNegative{*p == '-'};
constexpr RawType one{1};
RawType signBit{0};
if (isNegative) {
++p;
signBit = one << (RealType::bits - 1);
}
RawType fraction{0};
expo += binaryPrecision - 1 + RealType::exponentBias;
int roundingBit{0};
int guardBit{0};
for (; *p; ++p) {
fraction <<= 4;
expo -= 4;
if (*p >= '0' && *p <= '9') {
fraction |= *p - '0';
} else if (*p >= 'A' && *p <= 'F') {
fraction |= *p - 'A' + 10;
} else {
break;
}
if (fraction >> binaryPrecision) {
while (fraction >> binaryPrecision) {
guardBit |= roundingBit;
roundingBit = (int)fraction & 1;
fraction >>= 1;
++expo;
}
while (*++p) {
if (*p == '0') {
} else if ((*p >= '1' && *p <= '9') || (*p >= 'A' && *p <= 'F')) {
guardBit = 1;
} else {
break;
}
}
break;
}
}
if (fraction) {
while (expo < 1) {
guardBit |= roundingBit;
roundingBit = (int)fraction & 1;
fraction >>= 1;
++expo;
}
while (expo > 1 && !(fraction >> (binaryPrecision - 1))) {
fraction <<= 1;
--expo;
guardBit = roundingBit = 0;
}
}
bool increase{false};
switch (rounding) {
case decimal::RoundNearest:
increase = roundingBit && (guardBit | ((int)fraction & 1));
break;
case decimal::RoundUp:
increase = !isNegative && (roundingBit | guardBit);
break;
case decimal::RoundDown:
increase = isNegative && (roundingBit | guardBit);
break;
case decimal::RoundToZero:
break;
case decimal::RoundCompatible:
increase = roundingBit != 0;
break;
}
if (increase) {
++fraction;
if (fraction >> binaryPrecision) {
fraction >>= 1;
++expo;
}
}
constexpr RawType significandMask{(one << RealType::significandBits) - 1};
int flags{(roundingBit | guardBit) ? decimal::Inexact : decimal::Exact};
if (!fraction) {
expo = 0;
} else if (expo == 1 && !(fraction >> (binaryPrecision - 1))) {
expo = 0;
flags |= decimal::Underflow;
} else if (expo >= RealType::maxExponent) {
if (rounding == decimal::RoundToZero ||
(rounding == decimal::RoundDown && !isNegative) ||
(rounding == decimal::RoundUp && isNegative)) {
expo = RealType::maxExponent - 1;
fraction = significandMask;
} else {
expo = RealType::maxExponent;
fraction = 0;
flags |= decimal::Overflow;
}
} else {
fraction &= significandMask;
}
return decimal::ConversionToBinaryResult<binaryPrecision>{
RealType{static_cast<RawType>(signBit |
static_cast<RawType>(expo) << RealType::significandBits | fraction)},
static_cast<decimal::ConversionResultFlags>(flags)};
}
template <int KIND>
RT_API_ATTRS bool EditCommonRealInput(
IoStatementState &io, const DataEdit &edit, void *n) {
constexpr int binaryPrecision{common::PrecisionOfRealKind(KIND)};
if (TryFastPathRealDecimalInput<binaryPrecision>(io, edit, n)) {
return CheckCompleteListDirectedField(io, edit);
}
static constexpr int maxDigits{
common::MaxDecimalConversionDigits(binaryPrecision)};
static constexpr int bufferSize{maxDigits + 18};
char buffer[bufferSize];
auto scanned{ScanRealInput(buffer, maxDigits + 2, io, edit)};
int got{scanned.got};
if (got >= maxDigits + 2) {
io.GetIoErrorHandler().Crash("EditCommonRealInput: buffer was too small");
return false;
}
if (got == 0) {
const auto &connection{io.GetConnectionState()};
io.GetIoErrorHandler().SignalError(IostatBadRealInput,
"Bad real input data at column %d of record %d",
static_cast<int>(connection.positionInRecord + 1),
static_cast<int>(connection.currentRecordNumber));
return false;
}
decimal::ConversionToBinaryResult<binaryPrecision> converted;
const char *p{buffer};
if (scanned.isHexadecimal) {
buffer[got] = '\0';
converted = ConvertHexadecimal<binaryPrecision>(
p, edit.modes.round, scanned.exponent);
} else {
bool hadExtra{got > maxDigits};
int exponent{scanned.exponent};
if (exponent != 0) {
buffer[got++] = 'e';
if (exponent < 0) {
buffer[got++] = '-';
exponent = -exponent;
}
if (exponent > 9999) {
exponent = 9999;
}
if (exponent > 999) {
int dig{exponent / 1000};
buffer[got++] = '0' + dig;
int rest{exponent - 1000 * dig};
dig = rest / 100;
buffer[got++] = '0' + dig;
rest -= 100 * dig;
dig = rest / 10;
buffer[got++] = '0' + dig;
buffer[got++] = '0' + (rest - 10 * dig);
} else if (exponent > 99) {
int dig{exponent / 100};
buffer[got++] = '0' + dig;
int rest{exponent - 100 * dig};
dig = rest / 10;
buffer[got++] = '0' + dig;
buffer[got++] = '0' + (rest - 10 * dig);
} else if (exponent > 9) {
int dig{exponent / 10};
buffer[got++] = '0' + dig;
buffer[got++] = '0' + (exponent - 10 * dig);
} else {
buffer[got++] = '0' + exponent;
}
}
buffer[got] = '\0';
converted = decimal::ConvertToBinary<binaryPrecision>(p, edit.modes.round);
if (hadExtra) {
converted.flags = static_cast<enum decimal::ConversionResultFlags>(
converted.flags | decimal::Inexact);
}
}
if (*p) {
const auto &connection{io.GetConnectionState()};
io.GetIoErrorHandler().SignalError(IostatBadRealInput,
"Trailing characters after real input data at column %d of record %d",
static_cast<int>(connection.positionInRecord + 1),
static_cast<int>(connection.currentRecordNumber));
return false;
}
*reinterpret_cast<decimal::BinaryFloatingPointNumber<binaryPrecision> *>(n) =
converted.binary;
if (converted.flags != decimal::ConversionResultFlags::Exact) {
if (converted.flags & decimal::ConversionResultFlags::Overflow) {
io.GetIoErrorHandler().SignalError(IostatRealInputOverflow);
return false;
}
RaiseFPExceptions(converted.flags);
}
return CheckCompleteListDirectedField(io, edit);
}
template <int KIND>
RT_API_ATTRS bool EditRealInput(
IoStatementState &io, const DataEdit &edit, void *n) {
switch (edit.descriptor) {
case DataEdit::ListDirected:
if (IsNamelistNameOrSlash(io)) {
return false;
}
return EditCommonRealInput<KIND>(io, edit, n);
case DataEdit::ListDirectedRealPart:
case DataEdit::ListDirectedImaginaryPart:
case 'F':
case 'E':
case 'D':
case 'G':
return EditCommonRealInput<KIND>(io, edit, n);
case 'B':
return EditBOZInput<1>(io, edit, n,
common::BitsForBinaryPrecision(common::PrecisionOfRealKind(KIND)) >> 3);
case 'O':
return EditBOZInput<3>(io, edit, n,
common::BitsForBinaryPrecision(common::PrecisionOfRealKind(KIND)) >> 3);
case 'Z':
return EditBOZInput<4>(io, edit, n,
common::BitsForBinaryPrecision(common::PrecisionOfRealKind(KIND)) >> 3);
case 'A':
return EditCharacterInput(io, edit, reinterpret_cast<char *>(n), KIND);
default:
io.GetIoErrorHandler().SignalError(IostatErrorInFormat,
"Data edit descriptor '%c' may not be used for REAL input",
edit.descriptor);
return false;
}
}
RT_API_ATTRS bool EditLogicalInput(
IoStatementState &io, const DataEdit &edit, bool &x) {
switch (edit.descriptor) {
case DataEdit::ListDirected:
if (IsNamelistNameOrSlash(io)) {
return false;
}
break;
case 'L':
case 'G':
break;
default:
io.GetIoErrorHandler().SignalError(IostatErrorInFormat,
"Data edit descriptor '%c' may not be used for LOGICAL input",
edit.descriptor);
return false;
}
Fortran::common::optional<int> remaining{io.CueUpInput(edit)};
Fortran::common::optional<char32_t> next{io.NextInField(remaining, edit)};
if (next && *next == '.') {
next = io.NextInField(remaining, edit);
}
if (!next) {
io.GetIoErrorHandler().SignalError("Empty LOGICAL input field");
return false;
}
switch (*next) {
case 'T':
case 't':
x = true;
break;
case 'F':
case 'f':
x = false;
break;
default:
io.GetIoErrorHandler().SignalError(
"Bad character '%lc' in LOGICAL input field", *next);
return false;
}
if (remaining) {
io.HandleRelativePosition(*remaining);
} else if (edit.descriptor == DataEdit::ListDirected) {
while (io.NextInField(remaining, edit)) {
}
}
return CheckCompleteListDirectedField(io, edit);
}
template <typename CHAR>
static RT_API_ATTRS bool EditDelimitedCharacterInput(
IoStatementState &io, CHAR *x, std::size_t length, char32_t delimiter) {
bool result{true};
while (true) {
std::size_t byteCount{0};
auto ch{io.GetCurrentChar(byteCount)};
if (!ch) {
if (io.AdvanceRecord()) {
continue;
} else {
result = false;
break;
}
}
io.HandleRelativePosition(byteCount);
if (*ch == delimiter) {
auto next{io.GetCurrentChar(byteCount)};
if (next && *next == delimiter) {
io.HandleRelativePosition(byteCount);
} else {
break;
}
}
if (length > 0) {
*x++ = *ch;
--length;
}
}
Fortran::runtime::fill_n(x, length, ' ');
return result;
}
template <typename CHAR>
static RT_API_ATTRS bool EditListDirectedCharacterInput(
IoStatementState &io, CHAR *x, std::size_t length, const DataEdit &edit) {
std::size_t byteCount{0};
auto ch{io.GetCurrentChar(byteCount)};
if (ch && (*ch == '\'' || *ch == '"')) {
io.HandleRelativePosition(byteCount);
return EditDelimitedCharacterInput(io, x, length, *ch);
}
if (IsNamelistNameOrSlash(io) || io.GetConnectionState().IsAtEOF()) {
return false;
}
Fortran::common::optional<int> remaining{length > 0 ? maxUTF8Bytes : 0};
while (Fortran::common::optional<char32_t> next{
io.NextInField(remaining, edit)}) {
bool isSep{false};
switch (*next) {
case ' ':
case '\t':
case '/':
isSep = true;
break;
case '&':
case '$':
isSep = edit.IsNamelist();
break;
case ',':
isSep = !(edit.modes.editingFlags & decimalComma);
break;
case ';':
isSep = !!(edit.modes.editingFlags & decimalComma);
break;
default:
break;
}
if (isSep) {
remaining = 0;
} else {
*x++ = *next;
remaining = --length > 0 ? maxUTF8Bytes : 0;
}
}
Fortran::runtime::fill_n(x, length, ' ');
return true;
}
template <typename CHAR>
RT_API_ATTRS bool EditCharacterInput(IoStatementState &io, const DataEdit &edit,
CHAR *x, std::size_t lengthChars) {
switch (edit.descriptor) {
case DataEdit::ListDirected:
return EditListDirectedCharacterInput(io, x, lengthChars, edit);
case 'A':
case 'G':
break;
case 'B':
return EditBOZInput<1>(io, edit, x, lengthChars * sizeof *x);
case 'O':
return EditBOZInput<3>(io, edit, x, lengthChars * sizeof *x);
case 'Z':
return EditBOZInput<4>(io, edit, x, lengthChars * sizeof *x);
default:
io.GetIoErrorHandler().SignalError(IostatErrorInFormat,
"Data edit descriptor '%c' may not be used with a CHARACTER data item",
edit.descriptor);
return false;
}
const ConnectionState &connection{io.GetConnectionState()};
std::size_t remainingChars{lengthChars};
std::size_t skipChars{0};
if (edit.width && *edit.width > 0) {
remainingChars = *edit.width;
if (remainingChars > lengthChars) {
skipChars = remainingChars - lengthChars;
}
}
const char *input{nullptr};
std::size_t readyBytes{0};
while (remainingChars > 0) {
if (readyBytes == 0) {
readyBytes = io.GetNextInputBytes(input);
if (readyBytes == 0 ||
(readyBytes < remainingChars && edit.modes.nonAdvancing)) {
if (io.CheckForEndOfRecord(readyBytes)) {
if (readyBytes == 0) {
Fortran::runtime::fill_n(x, lengthChars, ' ');
return !io.GetIoErrorHandler().InError();
} else {
}
} else {
return !io.GetIoErrorHandler().InError();
}
}
}
std::size_t chunkBytes;
std::size_t chunkChars{1};
bool skipping{skipChars > 0};
if (connection.isUTF8) {
chunkBytes = MeasureUTF8Bytes(*input);
if (skipping) {
--skipChars;
} else if (auto ucs{DecodeUTF8(input)}) {
if ((sizeof *x == 1 && *ucs > 0xff) ||
(sizeof *x == 2 && *ucs > 0xffff)) {
*x++ = '?';
} else {
*x++ = *ucs;
}
--lengthChars;
} else if (chunkBytes == 0) {
chunkBytes = 1;
}
} else if (connection.internalIoCharKind > 1) {
chunkBytes = connection.internalIoCharKind;
if (skipping) {
--skipChars;
} else {
char32_t buffer{0};
std::memcpy(&buffer, input, chunkBytes);
if ((sizeof *x == 1 && buffer > 0xff) ||
(sizeof *x == 2 && buffer > 0xffff)) {
*x++ = '?';
} else {
*x++ = buffer;
}
--lengthChars;
}
} else if constexpr (sizeof *x > 1) {
chunkBytes = 1;
if (skipping) {
--skipChars;
} else {
*x++ = static_cast<unsigned char>(*input);
--lengthChars;
}
} else {
if (skipping) {
chunkBytes = std::min<std::size_t>(skipChars, readyBytes);
chunkChars = chunkBytes;
skipChars -= chunkChars;
} else {
chunkBytes = std::min<std::size_t>(remainingChars, readyBytes);
chunkBytes = std::min<std::size_t>(lengthChars, chunkBytes);
chunkChars = chunkBytes;
std::memcpy(x, input, chunkBytes);
x += chunkBytes;
lengthChars -= chunkChars;
}
}
input += chunkBytes;
remainingChars -= chunkChars;
if (!skipping) {
io.GotChar(chunkBytes);
}
io.HandleRelativePosition(chunkBytes);
readyBytes -= chunkBytes;
}
Fortran::runtime::fill_n(x, lengthChars, ' ');
return CheckCompleteListDirectedField(io, edit);
}
template RT_API_ATTRS bool EditRealInput<2>(
IoStatementState &, const DataEdit &, void *);
template RT_API_ATTRS bool EditRealInput<3>(
IoStatementState &, const DataEdit &, void *);
template RT_API_ATTRS bool EditRealInput<4>(
IoStatementState &, const DataEdit &, void *);
template RT_API_ATTRS bool EditRealInput<8>(
IoStatementState &, const DataEdit &, void *);
template RT_API_ATTRS bool EditRealInput<10>(
IoStatementState &, const DataEdit &, void *);
template RT_API_ATTRS bool EditRealInput<16>(
IoStatementState &, const DataEdit &, void *);
template RT_API_ATTRS bool EditCharacterInput(
IoStatementState &, const DataEdit &, char *, std::size_t);
template RT_API_ATTRS bool EditCharacterInput(
IoStatementState &, const DataEdit &, char16_t *, std::size_t);
template RT_API_ATTRS bool EditCharacterInput(
IoStatementState &, const DataEdit &, char32_t *, std::size_t);
RT_OFFLOAD_API_GROUP_END
}