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