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>
EditBOZInput(IoStatementState & io,const DataEdit & edit,void * n,std::size_t bytes)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');
28 }
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;
37 }
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;
47 }
48 ++digits;
49 }
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;
55 }
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
67 }
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;
79 }
80 --digits;
81 if (shift < 0) {
82 shift += 8;
83 if (shift + LOG2_BASE > 8) { // misaligned octal
84 *data |= digit >> (8 - shift);
85 }
86 data += increment;
87 }
88 *data |= digit << shift;
89 shift -= LOG2_BASE;
90 }
91 return true;
92 }
93
GetDecimalPoint(const DataEdit & edit)94 static inline char32_t GetDecimalPoint(const DataEdit &edit) {
95 return edit.modes.editingFlags & decimalComma ? char32_t{','} : char32_t{'.'};
96 }
97
98 // Prepares input from a field, and consumes the sign, if any.
99 // Returns true if there's a '-' sign.
ScanNumericPrefix(IoStatementState & io,const DataEdit & edit,std::optional<char32_t> & next,std::optional<int> & remaining)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);
109 }
110 }
111 return negative;
112 }
113
EditIntegerInput(IoStatementState & io,const DataEdit & edit,void * n,int kind)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;
121 }
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;
139 }
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;
153 }
154 }
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;
162 }
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;
172 }
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;
179 }
180 if (negate) {
181 value = -value;
182 }
183 if (any || !io.GetConnectionState().IsAtEOF()) {
184 std::memcpy(n, &value, kind); // a blank field means zero
185 }
186 return any;
187 }
188
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.
ScanRealInput(char * buffer,int bufferSize,IoStatementState & io,const DataEdit & edit,int & exponent)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;
205 }
206 ++got;
207 }};
208 if (ScanNumericPrefix(io, edit, next, remaining)) {
209 Put('-');
210 }
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');
216 }
217 return got;
218 }
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);
231 }
232 }
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;
246 }
247 Put(*next);
248 }
249 }
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;
263 }
264 }
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;
274 }
275 }
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
281 }
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);
289 }
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);
299 }
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';
304 }
305 } else if (*next == ' ' || *next == '\t') {
306 if (bzMode) {
307 exponent = 10 * exponent;
308 }
309 } else {
310 break;
311 }
312 }
313 if (negExpo) {
314 exponent = -exponent;
315 }
316 }
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);
325 }
326 } else {
327 // TODO: hex FP input
328 exponent = 0;
329 return 0;
330 }
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);
336 }
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);
342 }
343 }
344 } else if (remaining) {
345 while (next && (*next == ' ' || *next == '\t')) {
346 next = io.NextInField(remaining, edit);
347 }
348 if (next) {
349 return 0; // error: unused nonblank character in fixed-width field
350 }
351 }
352 return got;
353 }
354
RaiseFPExceptions(decimal::ConversionResultFlags flags)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);
364 }
365 if (flags & decimal::ConversionResultFlags::Inexact) {
366 RAISE(FE_INEXACT);
367 }
368 if (flags & decimal::ConversionResultFlags::Invalid) {
369 RAISE(FE_INVALID);
370 }
371 #undef RAISE
372 }
373
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>
TryFastPathRealInput(IoStatementState & io,const DataEdit & edit,void * n)379 static bool TryFastPathRealInput(
380 IoStatementState &io, const DataEdit &edit, void *n) {
381 if (edit.modes.editingFlags & (blankZero | decimalComma)) {
382 return false;
383 }
384 if (edit.modes.scale != 0) {
385 return false;
386 }
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
392 }
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;
401 }
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;
409 }
410 }
411 if (q == limit) {
412 // No explicit decimal point, and not NaN/Inf.
413 return false;
414 }
415 }
416 for (; p < limit && (*p == ' ' || *p == '\t'); ++p) {
417 }
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;
422 }
423 for (++p; p < limit && (*p == ' ' || *p == '\t'); ++p) {
424 }
425 }
426 if (edit.width && p < str + *edit.width) {
427 return false; // unconverted characters remain in fixed width field
428 }
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);
436 }
437 return true;
438 }
439
440 template <int KIND>
EditCommonRealInput(IoStatementState & io,const DataEdit & edit,void * n)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;
445 }
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;
456 }
457 if (got == 0) {
458 io.GetIoErrorHandler().SignalError(IostatBadRealInput);
459 return false;
460 }
461 bool hadExtra{got > maxDigits};
462 if (exponent != 0) {
463 buffer[got++] = 'e';
464 if (exponent < 0) {
465 buffer[got++] = '-';
466 exponent = -exponent;
467 }
468 if (exponent > 9999) {
469 exponent = 9999; // will convert to +/-Inf
470 }
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;
494 }
495 }
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);
503 }
504 if (*p) { // unprocessed junk after value
505 io.GetIoErrorHandler().SignalError(IostatBadRealInput);
506 return false;
507 }
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;
515 }
516 RaiseFPExceptions(converted.flags);
517 }
518 return true;
519 }
520
521 template <int KIND>
EditRealInput(IoStatementState & io,const DataEdit & edit,void * n)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;
527 }
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;
552 }
553 }
554
555 // 13.7.3 in Fortran 2018
EditLogicalInput(IoStatementState & io,const DataEdit & edit,bool & x)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;
561 }
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;
571 }
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);
576 }
577 if (!next) {
578 io.GetIoErrorHandler().SignalError("Empty LOGICAL input field");
579 return false;
580 }
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;
594 }
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
599 }
600 }
601 return true;
602 }
603
604 // See 13.10.3.1 paragraphs 7-9 in Fortran 2018
605 template <typename CHAR>
EditDelimitedCharacterInput(IoStatementState & io,CHAR * x,std::size_t length,char32_t delimiter)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;
618 }
619 }
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
628 }
629 }
630 if (length > 0) {
631 *x++ = *ch;
632 --length;
633 }
634 }
635 std::fill_n(x, length, ' ');
636 return result;
637 }
638
639 template <typename CHAR>
EditListDirectedCharacterInput(IoStatementState & io,CHAR * x,std::size_t length,const DataEdit & edit)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);
647 }
648 if (IsNamelistName(io) || io.GetConnectionState().IsAtEOF()) {
649 return false;
650 }
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;
672 }
673 if (isSep) {
674 remaining = 0;
675 } else {
676 *x++ = *next;
677 remaining = --length > 0 ? maxUTF8Bytes : 0;
678 }
679 }
680 std::fill_n(x, length, ' ');
681 return true;
682 }
683
684 template <typename CHAR>
EditCharacterInput(IoStatementState & io,const DataEdit & edit,CHAR * x,std::size_t length)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;
704 }
705 const ConnectionState &connection{io.GetConnectionState()};
706 std::size_t remaining{length};
707 if (edit.width && *edit.width > 0) {
708 remaining = *edit.width;
709 }
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'
725 }
726 return !io.GetIoErrorHandler().InError();
727 }
728 }
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;
741 }
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;
751 }
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;
762 }
763 remaining -= chunk;
764 }
765 input += chunk;
766 if (!skipping) {
767 io.GotChar(chunk);
768 }
769 io.HandleRelativePosition(chunk);
770 ready -= chunk;
771 }
772 // Pad the remainder of the input variable, if any.
773 std::fill_n(x, length, ' ');
774 return true;
775 }
776
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 *);
784
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);
791
792 } // namespace Fortran::runtime::io
793