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