UNSUPPORT test on 64-bit AIX too
[llvm-project.git] / flang / runtime / edit-input.cpp
blobf2ee328c3b47876e66c2a64ee033b1f699ca5c2d
1 //===-- runtime/edit-input.cpp --------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
9 #include "edit-input.h"
10 #include "namelist.h"
11 #include "utf.h"
12 #include "flang/Common/real.h"
13 #include "flang/Common/uint128.h"
14 #include <algorithm>
15 #include <cfenv>
17 namespace Fortran::runtime::io {
19 template <int LOG2_BASE>
20 static bool EditBOZInput(
21 IoStatementState &io, const DataEdit &edit, void *n, std::size_t bytes) {
22 std::optional<int> remaining;
23 std::optional<char32_t> next{io.PrepareInput(edit, remaining)};
24 if (next.value_or('?') == '0') {
25 do {
26 next = io.NextInField(remaining, edit);
27 } while (next && *next == '0');
29 // Count significant digits after any leading white space & zeroes
30 int digits{0};
31 int chars{0};
32 for (; next; next = io.NextInField(remaining, edit)) {
33 ++chars;
34 char32_t ch{*next};
35 if (ch == ' ' || ch == '\t') {
36 continue;
38 if (ch >= '0' && ch <= '1') {
39 } else if (LOG2_BASE >= 3 && ch >= '2' && ch <= '7') {
40 } else if (LOG2_BASE >= 4 && ch >= '8' && ch <= '9') {
41 } else if (LOG2_BASE >= 4 && ch >= 'A' && ch <= 'F') {
42 } else if (LOG2_BASE >= 4 && ch >= 'a' && ch <= 'f') {
43 } else {
44 io.GetIoErrorHandler().SignalError(
45 "Bad character '%lc' in B/O/Z input field", ch);
46 return false;
48 ++digits;
50 auto significantBytes{static_cast<std::size_t>(digits * LOG2_BASE + 7) / 8};
51 if (significantBytes > bytes) {
52 io.GetIoErrorHandler().SignalError(IostatBOZInputOverflow,
53 "B/O/Z input of %d digits overflows %zd-byte variable", digits, bytes);
54 return false;
56 // Reset to start of significant digits
57 io.HandleRelativePosition(-chars);
58 remaining.reset();
59 // Make a second pass now that the digit count is known
60 std::memset(n, 0, bytes);
61 int increment{isHostLittleEndian ? -1 : 1};
62 auto *data{reinterpret_cast<unsigned char *>(n) +
63 (isHostLittleEndian ? significantBytes - 1 : 0)};
64 int shift{((digits - 1) * LOG2_BASE) & 7};
65 if (shift + LOG2_BASE > 8) {
66 shift -= 8; // misaligned octal
68 while (digits > 0) {
69 char32_t ch{*io.NextInField(remaining, edit)};
70 int digit{0};
71 if (ch >= '0' && ch <= '9') {
72 digit = ch - '0';
73 } else if (ch >= 'A' && ch <= 'F') {
74 digit = ch + 10 - 'A';
75 } else if (ch >= 'a' && ch <= 'f') {
76 digit = ch + 10 - 'a';
77 } else {
78 continue;
80 --digits;
81 if (shift < 0) {
82 shift += 8;
83 if (shift + LOG2_BASE > 8) { // misaligned octal
84 *data |= digit >> (8 - shift);
86 data += increment;
88 *data |= digit << shift;
89 shift -= LOG2_BASE;
91 return true;
94 static inline char32_t GetDecimalPoint(const DataEdit &edit) {
95 return edit.modes.editingFlags & decimalComma ? char32_t{','} : char32_t{'.'};
98 // Prepares input from a field, and consumes the sign, if any.
99 // Returns true if there's a '-' sign.
100 static bool ScanNumericPrefix(IoStatementState &io, const DataEdit &edit,
101 std::optional<char32_t> &next, std::optional<int> &remaining) {
102 next = io.PrepareInput(edit, remaining);
103 bool negative{false};
104 if (next) {
105 negative = *next == '-';
106 if (negative || *next == '+') {
107 io.SkipSpaces(remaining);
108 next = io.NextInField(remaining, edit);
111 return negative;
114 bool EditIntegerInput(
115 IoStatementState &io, const DataEdit &edit, void *n, int kind) {
116 RUNTIME_CHECK(io.GetIoErrorHandler(), kind >= 1 && !(kind & (kind - 1)));
117 switch (edit.descriptor) {
118 case DataEdit::ListDirected:
119 if (IsNamelistName(io)) {
120 return false;
122 break;
123 case 'G':
124 case 'I':
125 break;
126 case 'B':
127 return EditBOZInput<1>(io, edit, n, kind);
128 case 'O':
129 return EditBOZInput<3>(io, edit, n, kind);
130 case 'Z':
131 return EditBOZInput<4>(io, edit, n, kind);
132 case 'A': // legacy extension
133 return EditCharacterInput(io, edit, reinterpret_cast<char *>(n), kind);
134 default:
135 io.GetIoErrorHandler().SignalError(IostatErrorInFormat,
136 "Data edit descriptor '%c' may not be used with an INTEGER data item",
137 edit.descriptor);
138 return false;
140 std::optional<int> remaining;
141 std::optional<char32_t> next;
142 bool negate{ScanNumericPrefix(io, edit, next, remaining)};
143 common::UnsignedInt128 value{0};
144 bool any{negate};
145 bool overflow{false};
146 for (; next; next = io.NextInField(remaining, edit)) {
147 char32_t ch{*next};
148 if (ch == ' ' || ch == '\t') {
149 if (edit.modes.editingFlags & blankZero) {
150 ch = '0'; // BZ mode - treat blank as if it were zero
151 } else {
152 continue;
155 int digit{0};
156 if (ch >= '0' && ch <= '9') {
157 digit = ch - '0';
158 } else {
159 io.GetIoErrorHandler().SignalError(
160 "Bad character '%lc' in INTEGER input field", ch);
161 return false;
163 static constexpr auto maxu128{~common::UnsignedInt128{0}};
164 static constexpr auto maxu128OverTen{maxu128 / 10};
165 static constexpr int maxLastDigit{
166 static_cast<int>(maxu128 - (maxu128OverTen * 10))};
167 overflow |= value >= maxu128OverTen &&
168 (value > maxu128OverTen || digit > maxLastDigit);
169 value *= 10;
170 value += digit;
171 any = true;
173 auto maxForKind{common::UnsignedInt128{1} << ((8 * kind) - 1)};
174 overflow |= value >= maxForKind && (value > maxForKind || !negate);
175 if (overflow) {
176 io.GetIoErrorHandler().SignalError(IostatIntegerInputOverflow,
177 "Decimal input overflows INTEGER(%d) variable", kind);
178 return false;
180 if (negate) {
181 value = -value;
183 if (any || !io.GetConnectionState().IsAtEOF()) {
184 std::memcpy(n, &value, kind); // a blank field means zero
186 return any;
189 // Parses a REAL input number from the input source as a normalized
190 // fraction into a supplied buffer -- there's an optional '-', a
191 // decimal point, and at least one digit. The adjusted exponent value
192 // is returned in a reference argument. The returned value is the number
193 // of characters that (should) have been written to the buffer -- this can
194 // be larger than the buffer size and can indicate overflow. Replaces
195 // blanks with zeroes if appropriate.
196 static int ScanRealInput(char *buffer, int bufferSize, IoStatementState &io,
197 const DataEdit &edit, int &exponent) {
198 std::optional<int> remaining;
199 std::optional<char32_t> next;
200 int got{0};
201 std::optional<int> decimalPoint;
202 auto Put{[&](char ch) -> void {
203 if (got < bufferSize) {
204 buffer[got] = ch;
206 ++got;
208 if (ScanNumericPrefix(io, edit, next, remaining)) {
209 Put('-');
211 bool bzMode{(edit.modes.editingFlags & blankZero) != 0};
212 if (!next || (!bzMode && *next == ' ')) { // empty/blank field means zero
213 remaining.reset();
214 if (!io.GetConnectionState().IsAtEOF()) {
215 Put('0');
217 return got;
219 char32_t decimal{GetDecimalPoint(edit)};
220 char32_t first{*next >= 'a' && *next <= 'z' ? *next + 'A' - 'a' : *next};
221 if (first == 'N' || first == 'I') {
222 // NaN or infinity - convert to upper case
223 // Subtle: a blank field of digits could be followed by 'E' or 'D',
224 for (; next &&
225 ((*next >= 'a' && *next <= 'z') || (*next >= 'A' && *next <= 'Z'));
226 next = io.NextInField(remaining, edit)) {
227 if (*next >= 'a' && *next <= 'z') {
228 Put(*next - 'a' + 'A');
229 } else {
230 Put(*next);
233 if (next && *next == '(') { // NaN(...)
234 Put('(');
235 int depth{1};
236 while (true) {
237 next = io.NextInField(remaining, edit);
238 if (depth == 0) {
239 break;
240 } else if (!next) {
241 return 0; // error
242 } else if (*next == '(') {
243 ++depth;
244 } else if (*next == ')') {
245 --depth;
247 Put(*next);
250 exponent = 0;
251 } else if (first == decimal || (first >= '0' && first <= '9') ||
252 (bzMode && (first == ' ' || first == '\t')) || first == 'E' ||
253 first == 'D' || first == 'Q') {
254 Put('.'); // input field is normalized to a fraction
255 auto start{got};
256 for (; next; next = io.NextInField(remaining, edit)) {
257 char32_t ch{*next};
258 if (ch == ' ' || ch == '\t') {
259 if (bzMode) {
260 ch = '0'; // BZ mode - treat blank as if it were zero
261 } else {
262 continue;
265 if (ch == '0' && got == start && !decimalPoint) {
266 // omit leading zeroes before the decimal
267 } else if (ch >= '0' && ch <= '9') {
268 Put(ch);
269 } else if (ch == decimal && !decimalPoint) {
270 // the decimal point is *not* copied to the buffer
271 decimalPoint = got - start; // # of digits before the decimal point
272 } else {
273 break;
276 if (got == start) {
277 // Nothing but zeroes and maybe a decimal point. F'2018 requires
278 // at least one digit, but F'77 did not, and a bare "." shows up in
279 // the FCVS suite.
280 Put('0'); // emit at least one digit
282 if (next &&
283 (*next == 'e' || *next == 'E' || *next == 'd' || *next == 'D' ||
284 *next == 'q' || *next == 'Q')) {
285 // Optional exponent letter. Blanks are allowed between the
286 // optional exponent letter and the exponent value.
287 io.SkipSpaces(remaining);
288 next = io.NextInField(remaining, edit);
290 // The default exponent is -kP, but the scale factor doesn't affect
291 // an explicit exponent.
292 exponent = -edit.modes.scale;
293 if (next &&
294 (*next == '-' || *next == '+' || (*next >= '0' && *next <= '9') ||
295 *next == ' ' || *next == '\t')) {
296 bool negExpo{*next == '-'};
297 if (negExpo || *next == '+') {
298 next = io.NextInField(remaining, edit);
300 for (exponent = 0; next; next = io.NextInField(remaining, edit)) {
301 if (*next >= '0' && *next <= '9') {
302 if (exponent < 10000) {
303 exponent = 10 * exponent + *next - '0';
305 } else if (*next == ' ' || *next == '\t') {
306 if (bzMode) {
307 exponent = 10 * exponent;
309 } else {
310 break;
313 if (negExpo) {
314 exponent = -exponent;
317 if (decimalPoint) {
318 exponent += *decimalPoint;
319 } else {
320 // When no decimal point (or comma) appears in the value, the 'd'
321 // part of the edit descriptor must be interpreted as the number of
322 // digits in the value to be interpreted as being to the *right* of
323 // the assumed decimal point (13.7.2.3.2)
324 exponent += got - start - edit.digits.value_or(0);
326 } else {
327 // TODO: hex FP input
328 exponent = 0;
329 return 0;
331 // Consume the trailing ')' of a list-directed or NAMELIST complex
332 // input value.
333 if (edit.descriptor == DataEdit::ListDirectedImaginaryPart) {
334 if (next && (*next == ' ' || *next == '\t')) {
335 next = io.NextInField(remaining, edit);
337 if (!next) { // NextInField fails on separators like ')'
338 std::size_t byteCount{0};
339 next = io.GetCurrentChar(byteCount);
340 if (next && *next == ')') {
341 io.HandleRelativePosition(byteCount);
344 } else if (remaining) {
345 while (next && (*next == ' ' || *next == '\t')) {
346 next = io.NextInField(remaining, edit);
348 if (next) {
349 return 0; // error: unused nonblank character in fixed-width field
352 return got;
355 static void RaiseFPExceptions(decimal::ConversionResultFlags flags) {
356 #undef RAISE
357 #ifdef feraisexcept // a macro in some environments; omit std::
358 #define RAISE feraiseexcept
359 #else
360 #define RAISE std::feraiseexcept
361 #endif
362 if (flags & decimal::ConversionResultFlags::Overflow) {
363 RAISE(FE_OVERFLOW);
365 if (flags & decimal::ConversionResultFlags::Inexact) {
366 RAISE(FE_INEXACT);
368 if (flags & decimal::ConversionResultFlags::Invalid) {
369 RAISE(FE_INVALID);
371 #undef RAISE
374 // If no special modes are in effect and the form of the input value
375 // that's present in the input stream is acceptable to the decimal->binary
376 // converter without modification, this fast path for real input
377 // saves time by avoiding memory copies and reformatting of the exponent.
378 template <int PRECISION>
379 static bool TryFastPathRealInput(
380 IoStatementState &io, const DataEdit &edit, void *n) {
381 if (edit.modes.editingFlags & (blankZero | decimalComma)) {
382 return false;
384 if (edit.modes.scale != 0) {
385 return false;
387 const char *str{nullptr};
388 std::size_t got{io.GetNextInputBytes(str)};
389 if (got == 0 || str == nullptr ||
390 !io.GetConnectionState().recordLength.has_value()) {
391 return false; // could not access reliably-terminated input stream
393 const char *p{str};
394 std::int64_t maxConsume{
395 std::min<std::int64_t>(got, edit.width.value_or(got))};
396 const char *limit{str + maxConsume};
397 decimal::ConversionToBinaryResult<PRECISION> converted{
398 decimal::ConvertToBinary<PRECISION>(p, edit.modes.round, limit)};
399 if (converted.flags & (decimal::Invalid | decimal::Overflow)) {
400 return false;
402 if (edit.digits.value_or(0) != 0) {
403 // Edit descriptor is Fw.d (or other) with d != 0, which
404 // implies scaling
405 const char *q{str};
406 for (; q < limit; ++q) {
407 if (*q == '.' || *q == 'n' || *q == 'N') {
408 break;
411 if (q == limit) {
412 // No explicit decimal point, and not NaN/Inf.
413 return false;
416 for (; p < limit && (*p == ' ' || *p == '\t'); ++p) {
418 if (edit.descriptor == DataEdit::ListDirectedImaginaryPart) {
419 // Need to consume a trailing ')' and any white space after
420 if (p >= limit || *p != ')') {
421 return false;
423 for (++p; p < limit && (*p == ' ' || *p == '\t'); ++p) {
426 if (edit.width && p < str + *edit.width) {
427 return false; // unconverted characters remain in fixed width field
429 // Success on the fast path!
430 *reinterpret_cast<decimal::BinaryFloatingPointNumber<PRECISION> *>(n) =
431 converted.binary;
432 io.HandleRelativePosition(p - str);
433 // Set FP exception flags
434 if (converted.flags != decimal::ConversionResultFlags::Exact) {
435 RaiseFPExceptions(converted.flags);
437 return true;
440 template <int KIND>
441 bool EditCommonRealInput(IoStatementState &io, const DataEdit &edit, void *n) {
442 constexpr int binaryPrecision{common::PrecisionOfRealKind(KIND)};
443 if (TryFastPathRealInput<binaryPrecision>(io, edit, n)) {
444 return true;
446 // Fast path wasn't available or didn't work; go the more general route
447 static constexpr int maxDigits{
448 common::MaxDecimalConversionDigits(binaryPrecision)};
449 static constexpr int bufferSize{maxDigits + 18};
450 char buffer[bufferSize];
451 int exponent{0};
452 int got{ScanRealInput(buffer, maxDigits + 2, io, edit, exponent)};
453 if (got >= maxDigits + 2) {
454 io.GetIoErrorHandler().Crash("EditCommonRealInput: buffer was too small");
455 return false;
457 if (got == 0) {
458 io.GetIoErrorHandler().SignalError(IostatBadRealInput);
459 return false;
461 bool hadExtra{got > maxDigits};
462 if (exponent != 0) {
463 buffer[got++] = 'e';
464 if (exponent < 0) {
465 buffer[got++] = '-';
466 exponent = -exponent;
468 if (exponent > 9999) {
469 exponent = 9999; // will convert to +/-Inf
471 if (exponent > 999) {
472 int dig{exponent / 1000};
473 buffer[got++] = '0' + dig;
474 int rest{exponent - 1000 * dig};
475 dig = rest / 100;
476 buffer[got++] = '0' + dig;
477 rest -= 100 * dig;
478 dig = rest / 10;
479 buffer[got++] = '0' + dig;
480 buffer[got++] = '0' + (rest - 10 * dig);
481 } else if (exponent > 99) {
482 int dig{exponent / 100};
483 buffer[got++] = '0' + dig;
484 int rest{exponent - 100 * dig};
485 dig = rest / 10;
486 buffer[got++] = '0' + dig;
487 buffer[got++] = '0' + (rest - 10 * dig);
488 } else if (exponent > 9) {
489 int dig{exponent / 10};
490 buffer[got++] = '0' + dig;
491 buffer[got++] = '0' + (exponent - 10 * dig);
492 } else {
493 buffer[got++] = '0' + exponent;
496 buffer[got] = '\0';
497 const char *p{buffer};
498 decimal::ConversionToBinaryResult<binaryPrecision> converted{
499 decimal::ConvertToBinary<binaryPrecision>(p, edit.modes.round)};
500 if (hadExtra) {
501 converted.flags = static_cast<enum decimal::ConversionResultFlags>(
502 converted.flags | decimal::Inexact);
504 if (*p) { // unprocessed junk after value
505 io.GetIoErrorHandler().SignalError(IostatBadRealInput);
506 return false;
508 *reinterpret_cast<decimal::BinaryFloatingPointNumber<binaryPrecision> *>(n) =
509 converted.binary;
510 // Set FP exception flags
511 if (converted.flags != decimal::ConversionResultFlags::Exact) {
512 if (converted.flags & decimal::ConversionResultFlags::Overflow) {
513 io.GetIoErrorHandler().SignalError(IostatRealInputOverflow);
514 return false;
516 RaiseFPExceptions(converted.flags);
518 return true;
521 template <int KIND>
522 bool EditRealInput(IoStatementState &io, const DataEdit &edit, void *n) {
523 switch (edit.descriptor) {
524 case DataEdit::ListDirected:
525 if (IsNamelistName(io)) {
526 return false;
528 return EditCommonRealInput<KIND>(io, edit, n);
529 case DataEdit::ListDirectedRealPart:
530 case DataEdit::ListDirectedImaginaryPart:
531 case 'F':
532 case 'E': // incl. EN, ES, & EX
533 case 'D':
534 case 'G':
535 return EditCommonRealInput<KIND>(io, edit, n);
536 case 'B':
537 return EditBOZInput<1>(io, edit, n,
538 common::BitsForBinaryPrecision(common::PrecisionOfRealKind(KIND)) >> 3);
539 case 'O':
540 return EditBOZInput<3>(io, edit, n,
541 common::BitsForBinaryPrecision(common::PrecisionOfRealKind(KIND)) >> 3);
542 case 'Z':
543 return EditBOZInput<4>(io, edit, n,
544 common::BitsForBinaryPrecision(common::PrecisionOfRealKind(KIND)) >> 3);
545 case 'A': // legacy extension
546 return EditCharacterInput(io, edit, reinterpret_cast<char *>(n), KIND);
547 default:
548 io.GetIoErrorHandler().SignalError(IostatErrorInFormat,
549 "Data edit descriptor '%c' may not be used for REAL input",
550 edit.descriptor);
551 return false;
555 // 13.7.3 in Fortran 2018
556 bool EditLogicalInput(IoStatementState &io, const DataEdit &edit, bool &x) {
557 switch (edit.descriptor) {
558 case DataEdit::ListDirected:
559 if (IsNamelistName(io)) {
560 return false;
562 break;
563 case 'L':
564 case 'G':
565 break;
566 default:
567 io.GetIoErrorHandler().SignalError(IostatErrorInFormat,
568 "Data edit descriptor '%c' may not be used for LOGICAL input",
569 edit.descriptor);
570 return false;
572 std::optional<int> remaining;
573 std::optional<char32_t> next{io.PrepareInput(edit, remaining)};
574 if (next && *next == '.') { // skip optional period
575 next = io.NextInField(remaining, edit);
577 if (!next) {
578 io.GetIoErrorHandler().SignalError("Empty LOGICAL input field");
579 return false;
581 switch (*next) {
582 case 'T':
583 case 't':
584 x = true;
585 break;
586 case 'F':
587 case 'f':
588 x = false;
589 break;
590 default:
591 io.GetIoErrorHandler().SignalError(
592 "Bad character '%lc' in LOGICAL input field", *next);
593 return false;
595 if (remaining) { // ignore the rest of the field
596 io.HandleRelativePosition(*remaining);
597 } else if (edit.descriptor == DataEdit::ListDirected) {
598 while (io.NextInField(remaining, edit)) { // discard rest of field
601 return true;
604 // See 13.10.3.1 paragraphs 7-9 in Fortran 2018
605 template <typename CHAR>
606 static bool EditDelimitedCharacterInput(
607 IoStatementState &io, CHAR *x, std::size_t length, char32_t delimiter) {
608 bool result{true};
609 while (true) {
610 std::size_t byteCount{0};
611 auto ch{io.GetCurrentChar(byteCount)};
612 if (!ch) {
613 if (io.AdvanceRecord()) {
614 continue;
615 } else {
616 result = false; // EOF in character value
617 break;
620 io.HandleRelativePosition(byteCount);
621 if (*ch == delimiter) {
622 auto next{io.GetCurrentChar(byteCount)};
623 if (next && *next == delimiter) {
624 // Repeated delimiter: use as character value
625 io.HandleRelativePosition(byteCount);
626 } else {
627 break; // closing delimiter
630 if (length > 0) {
631 *x++ = *ch;
632 --length;
635 std::fill_n(x, length, ' ');
636 return result;
639 template <typename CHAR>
640 static bool EditListDirectedCharacterInput(
641 IoStatementState &io, CHAR *x, std::size_t length, const DataEdit &edit) {
642 std::size_t byteCount{0};
643 auto ch{io.GetCurrentChar(byteCount)};
644 if (ch && (*ch == '\'' || *ch == '"')) {
645 io.HandleRelativePosition(byteCount);
646 return EditDelimitedCharacterInput(io, x, length, *ch);
648 if (IsNamelistName(io) || io.GetConnectionState().IsAtEOF()) {
649 return false;
651 // Undelimited list-directed character input: stop at a value separator
652 // or the end of the current record. Subtlety: the "remaining" count
653 // here is a dummy that's used to avoid the interpretation of separators
654 // in NextInField.
655 std::optional<int> remaining{length > 0 ? maxUTF8Bytes : 0};
656 while (std::optional<char32_t> next{io.NextInField(remaining, edit)}) {
657 bool isSep{false};
658 switch (*next) {
659 case ' ':
660 case '\t':
661 case '/':
662 isSep = true;
663 break;
664 case ',':
665 isSep = !(edit.modes.editingFlags & decimalComma);
666 break;
667 case ';':
668 isSep = !!(edit.modes.editingFlags & decimalComma);
669 break;
670 default:
671 break;
673 if (isSep) {
674 remaining = 0;
675 } else {
676 *x++ = *next;
677 remaining = --length > 0 ? maxUTF8Bytes : 0;
680 std::fill_n(x, length, ' ');
681 return true;
684 template <typename CHAR>
685 bool EditCharacterInput(
686 IoStatementState &io, const DataEdit &edit, CHAR *x, std::size_t length) {
687 switch (edit.descriptor) {
688 case DataEdit::ListDirected:
689 return EditListDirectedCharacterInput(io, x, length, edit);
690 case 'A':
691 case 'G':
692 break;
693 case 'B':
694 return EditBOZInput<1>(io, edit, x, length * sizeof *x);
695 case 'O':
696 return EditBOZInput<3>(io, edit, x, length * sizeof *x);
697 case 'Z':
698 return EditBOZInput<4>(io, edit, x, length * sizeof *x);
699 default:
700 io.GetIoErrorHandler().SignalError(IostatErrorInFormat,
701 "Data edit descriptor '%c' may not be used with a CHARACTER data item",
702 edit.descriptor);
703 return false;
705 const ConnectionState &connection{io.GetConnectionState()};
706 std::size_t remaining{length};
707 if (edit.width && *edit.width > 0) {
708 remaining = *edit.width;
710 // When the field is wider than the variable, we drop the leading
711 // characters. When the variable is wider than the field, there can be
712 // trailing padding.
713 const char *input{nullptr};
714 std::size_t ready{0};
715 // Skip leading bytes.
716 // These bytes don't count towards INQUIRE(IOLENGTH=).
717 std::size_t skip{remaining > length ? remaining - length : 0};
718 // Transfer payload bytes; these do count.
719 while (remaining > 0) {
720 if (ready == 0) {
721 ready = io.GetNextInputBytes(input);
722 if (ready == 0) {
723 if (io.CheckForEndOfRecord()) {
724 std::fill_n(x, length, ' '); // PAD='YES'
726 return !io.GetIoErrorHandler().InError();
729 std::size_t chunk;
730 bool skipping{skip > 0};
731 if (connection.isUTF8) {
732 chunk = MeasureUTF8Bytes(*input);
733 if (skipping) {
734 --skip;
735 } else if (auto ucs{DecodeUTF8(input)}) {
736 *x++ = *ucs;
737 --length;
738 } else if (chunk == 0) {
739 // error recovery: skip bad encoding
740 chunk = 1;
742 --remaining;
743 } else if constexpr (sizeof *x > 1) {
744 // Read single byte with expansion into multi-byte CHARACTER
745 chunk = 1;
746 if (skipping) {
747 --skip;
748 } else {
749 *x++ = static_cast<unsigned char>(*input);
750 --length;
752 --remaining;
753 } else { // single bytes -> default CHARACTER
754 if (skipping) {
755 chunk = std::min<std::size_t>(skip, ready);
756 skip -= chunk;
757 } else {
758 chunk = std::min<std::size_t>(remaining, ready);
759 std::memcpy(x, input, chunk);
760 x += chunk;
761 length -= chunk;
763 remaining -= chunk;
765 input += chunk;
766 if (!skipping) {
767 io.GotChar(chunk);
769 io.HandleRelativePosition(chunk);
770 ready -= chunk;
772 // Pad the remainder of the input variable, if any.
773 std::fill_n(x, length, ' ');
774 return true;
777 template bool EditRealInput<2>(IoStatementState &, const DataEdit &, void *);
778 template bool EditRealInput<3>(IoStatementState &, const DataEdit &, void *);
779 template bool EditRealInput<4>(IoStatementState &, const DataEdit &, void *);
780 template bool EditRealInput<8>(IoStatementState &, const DataEdit &, void *);
781 template bool EditRealInput<10>(IoStatementState &, const DataEdit &, void *);
782 // TODO: double/double
783 template bool EditRealInput<16>(IoStatementState &, const DataEdit &, void *);
785 template bool EditCharacterInput(
786 IoStatementState &, const DataEdit &, char *, std::size_t);
787 template bool EditCharacterInput(
788 IoStatementState &, const DataEdit &, char16_t *, std::size_t);
789 template bool EditCharacterInput(
790 IoStatementState &, const DataEdit &, char32_t *, std::size_t);
792 } // namespace Fortran::runtime::io