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