1 //===-- runtime/edit-output.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-output.h" 10 #include "emit-encoded.h" 11 #include "utf.h" 12 #include "flang/Common/uint128.h" 13 #include <algorithm> 14 15 namespace Fortran::runtime::io { 16 17 // In output statement, add a space between numbers and characters. 18 static void addSpaceBeforeCharacter(IoStatementState &io) { 19 if (auto *list{io.get_if<ListDirectedStatementState<Direction::Output>>()}) { 20 list->set_lastWasUndelimitedCharacter(false); 21 } 22 } 23 24 // B/O/Z output of arbitrarily sized data emits a binary/octal/hexadecimal 25 // representation of what is interpreted to be a single unsigned integer value. 26 // When used with character data, endianness is exposed. 27 template <int LOG2_BASE> 28 static bool EditBOZOutput(IoStatementState &io, const DataEdit &edit, 29 const unsigned char *data0, std::size_t bytes) { 30 addSpaceBeforeCharacter(io); 31 int digits{static_cast<int>((bytes * 8) / LOG2_BASE)}; 32 int get{static_cast<int>(bytes * 8) - digits * LOG2_BASE}; 33 if (get > 0) { 34 ++digits; 35 } else { 36 get = LOG2_BASE; 37 } 38 int shift{7}; 39 int increment{isHostLittleEndian ? -1 : 1}; 40 const unsigned char *data{data0 + (isHostLittleEndian ? bytes - 1 : 0)}; 41 int skippedZeroes{0}; 42 int digit{0}; 43 // The same algorithm is used to generate digits for real (below) 44 // as well as for generating them only to skip leading zeroes (here). 45 // Bits are copied one at a time from the source data. 46 // TODO: Multiple bit copies for hexadecimal, where misalignment 47 // is not possible; or for octal when all 3 bits come from the 48 // same byte. 49 while (bytes > 0) { 50 if (get == 0) { 51 if (digit != 0) { 52 break; // first nonzero leading digit 53 } 54 ++skippedZeroes; 55 get = LOG2_BASE; 56 } else if (shift < 0) { 57 data += increment; 58 --bytes; 59 shift = 7; 60 } else { 61 digit = 2 * digit + ((*data >> shift--) & 1); 62 --get; 63 } 64 } 65 // Emit leading spaces and zeroes; detect field overflow 66 int leadingZeroes{0}; 67 int editWidth{edit.width.value_or(0)}; 68 int significant{digits - skippedZeroes}; 69 if (edit.digits && significant <= *edit.digits) { // Bw.m, Ow.m, Zw.m 70 if (*edit.digits == 0 && bytes == 0) { 71 editWidth = std::max(1, editWidth); 72 } else { 73 leadingZeroes = *edit.digits - significant; 74 } 75 } else if (bytes == 0) { 76 leadingZeroes = 1; 77 } 78 int subTotal{leadingZeroes + significant}; 79 int leadingSpaces{std::max(0, editWidth - subTotal)}; 80 if (editWidth > 0 && leadingSpaces + subTotal > editWidth) { 81 return EmitRepeated(io, '*', editWidth); 82 } 83 if (!(EmitRepeated(io, ' ', leadingSpaces) && 84 EmitRepeated(io, '0', leadingZeroes))) { 85 return false; 86 } 87 // Emit remaining digits 88 while (bytes > 0) { 89 if (get == 0) { 90 char ch{static_cast<char>(digit >= 10 ? 'A' + digit - 10 : '0' + digit)}; 91 if (!EmitAscii(io, &ch, 1)) { 92 return false; 93 } 94 get = LOG2_BASE; 95 digit = 0; 96 } else if (shift < 0) { 97 data += increment; 98 --bytes; 99 shift = 7; 100 } else { 101 digit = 2 * digit + ((*data >> shift--) & 1); 102 --get; 103 } 104 } 105 return true; 106 } 107 108 template <int KIND> 109 bool EditIntegerOutput(IoStatementState &io, const DataEdit &edit, 110 common::HostSignedIntType<8 * KIND> n) { 111 addSpaceBeforeCharacter(io); 112 char buffer[130], *end{&buffer[sizeof buffer]}, *p{end}; 113 bool isNegative{n < 0}; 114 using Unsigned = common::HostUnsignedIntType<8 * KIND>; 115 Unsigned un{static_cast<Unsigned>(n)}; 116 int signChars{0}; 117 switch (edit.descriptor) { 118 case DataEdit::ListDirected: 119 case 'G': 120 case 'I': 121 if (isNegative) { 122 un = -un; 123 } 124 if (isNegative || (edit.modes.editingFlags & signPlus)) { 125 signChars = 1; // '-' or '+' 126 } 127 while (un > 0) { 128 auto quotient{un / 10u}; 129 *--p = '0' + static_cast<int>(un - Unsigned{10} * quotient); 130 un = quotient; 131 } 132 break; 133 case 'B': 134 return EditBOZOutput<1>( 135 io, edit, reinterpret_cast<const unsigned char *>(&n), KIND); 136 case 'O': 137 return EditBOZOutput<3>( 138 io, edit, reinterpret_cast<const unsigned char *>(&n), KIND); 139 case 'Z': 140 return EditBOZOutput<4>( 141 io, edit, reinterpret_cast<const unsigned char *>(&n), KIND); 142 case 'L': 143 return EditLogicalOutput(io, edit, *reinterpret_cast<const char *>(&n)); 144 case 'A': // legacy extension 145 return EditCharacterOutput( 146 io, edit, reinterpret_cast<char *>(&n), sizeof n); 147 default: 148 io.GetIoErrorHandler().SignalError(IostatErrorInFormat, 149 "Data edit descriptor '%c' may not be used with an INTEGER data item", 150 edit.descriptor); 151 return false; 152 } 153 154 int digits = end - p; 155 int leadingZeroes{0}; 156 int editWidth{edit.width.value_or(0)}; 157 if (edit.digits && digits <= *edit.digits) { // Iw.m 158 if (*edit.digits == 0 && n == 0) { 159 // Iw.0 with zero value: output field must be blank. For I0.0 160 // and a zero value, emit one blank character. 161 signChars = 0; // in case of SP 162 editWidth = std::max(1, editWidth); 163 } else { 164 leadingZeroes = *edit.digits - digits; 165 } 166 } else if (n == 0) { 167 leadingZeroes = 1; 168 } 169 int subTotal{signChars + leadingZeroes + digits}; 170 int leadingSpaces{std::max(0, editWidth - subTotal)}; 171 if (editWidth > 0 && leadingSpaces + subTotal > editWidth) { 172 return EmitRepeated(io, '*', editWidth); 173 } 174 if (edit.IsListDirected()) { 175 int total{std::max(leadingSpaces, 1) + subTotal}; 176 if (io.GetConnectionState().NeedAdvance(static_cast<std::size_t>(total)) && 177 !io.AdvanceRecord()) { 178 return false; 179 } 180 leadingSpaces = 1; 181 } 182 return EmitRepeated(io, ' ', leadingSpaces) && 183 EmitAscii(io, n < 0 ? "-" : "+", signChars) && 184 EmitRepeated(io, '0', leadingZeroes) && EmitAscii(io, p, digits); 185 } 186 187 // Formats the exponent (see table 13.1 for all the cases) 188 const char *RealOutputEditingBase::FormatExponent( 189 int expo, const DataEdit &edit, int &length) { 190 char *eEnd{&exponent_[sizeof exponent_]}; 191 char *exponent{eEnd}; 192 for (unsigned e{static_cast<unsigned>(std::abs(expo))}; e > 0;) { 193 unsigned quotient{e / 10u}; 194 *--exponent = '0' + e - 10 * quotient; 195 e = quotient; 196 } 197 bool overflow{false}; 198 if (edit.expoDigits) { 199 if (int ed{*edit.expoDigits}) { // Ew.dEe with e > 0 200 overflow = exponent + ed < eEnd; 201 while (exponent > exponent_ + 2 /*E+*/ && exponent + ed > eEnd) { 202 *--exponent = '0'; 203 } 204 } else if (exponent == eEnd) { 205 *--exponent = '0'; // Ew.dE0 with zero-valued exponent 206 } 207 } else { // ensure at least two exponent digits 208 while (exponent + 2 > eEnd) { 209 *--exponent = '0'; 210 } 211 } 212 *--exponent = expo < 0 ? '-' : '+'; 213 if (edit.expoDigits || edit.IsListDirected() || exponent + 3 == eEnd) { 214 *--exponent = edit.descriptor == 'D' ? 'D' : 'E'; // not 'G' or 'Q' 215 } 216 length = eEnd - exponent; 217 return overflow ? nullptr : exponent; 218 } 219 220 bool RealOutputEditingBase::EmitPrefix( 221 const DataEdit &edit, std::size_t length, std::size_t width) { 222 if (edit.IsListDirected()) { 223 int prefixLength{edit.descriptor == DataEdit::ListDirectedRealPart ? 2 224 : edit.descriptor == DataEdit::ListDirectedImaginaryPart ? 0 225 : 1}; 226 int suffixLength{edit.descriptor == DataEdit::ListDirectedRealPart || 227 edit.descriptor == DataEdit::ListDirectedImaginaryPart 228 ? 1 229 : 0}; 230 length += prefixLength + suffixLength; 231 ConnectionState &connection{io_.GetConnectionState()}; 232 return (!connection.NeedAdvance(length) || io_.AdvanceRecord()) && 233 EmitAscii(io_, " (", prefixLength); 234 } else if (width > length) { 235 return EmitRepeated(io_, ' ', width - length); 236 } else { 237 return true; 238 } 239 } 240 241 bool RealOutputEditingBase::EmitSuffix(const DataEdit &edit) { 242 if (edit.descriptor == DataEdit::ListDirectedRealPart) { 243 return EmitAscii( 244 io_, edit.modes.editingFlags & decimalComma ? ";" : ",", 1); 245 } else if (edit.descriptor == DataEdit::ListDirectedImaginaryPart) { 246 return EmitAscii(io_, ")", 1); 247 } else { 248 return true; 249 } 250 } 251 252 template <int KIND> 253 decimal::ConversionToDecimalResult RealOutputEditing<KIND>::Convert( 254 int significantDigits, enum decimal::FortranRounding rounding, int flags) { 255 auto converted{decimal::ConvertToDecimal<binaryPrecision>(buffer_, 256 sizeof buffer_, static_cast<enum decimal::DecimalConversionFlags>(flags), 257 significantDigits, rounding, x_)}; 258 if (!converted.str) { // overflow 259 io_.GetIoErrorHandler().Crash( 260 "RealOutputEditing::Convert : buffer size %zd was insufficient", 261 sizeof buffer_); 262 } 263 return converted; 264 } 265 266 // 13.7.2.3.3 in F'2018 267 template <int KIND> 268 bool RealOutputEditing<KIND>::EditEorDOutput(const DataEdit &edit) { 269 addSpaceBeforeCharacter(io_); 270 int editDigits{edit.digits.value_or(0)}; // 'd' field 271 int editWidth{edit.width.value_or(0)}; // 'w' field 272 int significantDigits{editDigits}; 273 int flags{0}; 274 if (edit.modes.editingFlags & signPlus) { 275 flags |= decimal::AlwaysSign; 276 } 277 bool noLeadingSpaces{editWidth == 0}; 278 int scale{edit.modes.scale}; // 'kP' value 279 if (editWidth == 0) { // "the processor selects the field width" 280 if (edit.digits.has_value()) { // E0.d 281 if (editDigits == 0 && scale <= 0) { // E0.0 282 significantDigits = 1; 283 } 284 } else { // E0 285 flags |= decimal::Minimize; 286 significantDigits = 287 sizeof buffer_ - 5; // sign, NUL, + 3 extra for EN scaling 288 } 289 } 290 bool isEN{edit.variation == 'N'}; 291 bool isES{edit.variation == 'S'}; 292 int zeroesAfterPoint{0}; 293 if (isEN) { 294 scale = IsZero() ? 1 : 3; 295 significantDigits += scale; 296 } else if (isES) { 297 scale = 1; 298 ++significantDigits; 299 } else if (scale < 0) { 300 if (scale <= -editDigits) { 301 io_.GetIoErrorHandler().SignalError(IostatBadScaleFactor, 302 "Scale factor (kP) %d cannot be less than -d (%d)", scale, 303 -editDigits); 304 return false; 305 } 306 zeroesAfterPoint = -scale; 307 significantDigits = std::max(0, significantDigits - zeroesAfterPoint); 308 } else if (scale > 0) { 309 if (scale >= editDigits + 2) { 310 io_.GetIoErrorHandler().SignalError(IostatBadScaleFactor, 311 "Scale factor (kP) %d cannot be greater than d+2 (%d)", scale, 312 editDigits + 2); 313 return false; 314 } 315 ++significantDigits; 316 scale = std::min(scale, significantDigits + 1); 317 } 318 // In EN editing, multiple attempts may be necessary, so this is a loop. 319 while (true) { 320 decimal::ConversionToDecimalResult converted{ 321 Convert(significantDigits, edit.modes.round, flags)}; 322 if (IsInfOrNaN(converted)) { 323 return editWidth > 0 && 324 converted.length > static_cast<std::size_t>(editWidth) 325 ? EmitRepeated(io_, '*', editWidth) 326 : EmitPrefix(edit, converted.length, editWidth) && 327 EmitAscii(io_, converted.str, converted.length) && 328 EmitSuffix(edit); 329 } 330 if (!IsZero()) { 331 converted.decimalExponent -= scale; 332 } 333 if (isEN) { 334 // EN mode: we need an effective exponent field that is 335 // a multiple of three. 336 if (int modulus{converted.decimalExponent % 3}; modulus != 0) { 337 if (significantDigits > 1) { 338 --significantDigits; 339 --scale; 340 continue; 341 } 342 // Rounded nines up to a 1. 343 scale += modulus; 344 converted.decimalExponent -= modulus; 345 } 346 if (scale > 3) { 347 int adjust{3 * (scale / 3)}; 348 scale -= adjust; 349 converted.decimalExponent += adjust; 350 } else if (scale < 1) { 351 int adjust{3 - 3 * (scale / 3)}; 352 scale += adjust; 353 converted.decimalExponent -= adjust; 354 } 355 significantDigits = editDigits + scale; 356 } 357 // Format the exponent (see table 13.1 for all the cases) 358 int expoLength{0}; 359 const char *exponent{ 360 FormatExponent(converted.decimalExponent, edit, expoLength)}; 361 int signLength{*converted.str == '-' || *converted.str == '+' ? 1 : 0}; 362 int convertedDigits{static_cast<int>(converted.length) - signLength}; 363 int zeroesBeforePoint{std::max(0, scale - convertedDigits)}; 364 int digitsBeforePoint{std::max(0, scale - zeroesBeforePoint)}; 365 int digitsAfterPoint{convertedDigits - digitsBeforePoint}; 366 int trailingZeroes{flags & decimal::Minimize 367 ? 0 368 : std::max(0, 369 significantDigits - (convertedDigits + zeroesBeforePoint))}; 370 int totalLength{signLength + digitsBeforePoint + zeroesBeforePoint + 371 1 /*'.'*/ + zeroesAfterPoint + digitsAfterPoint + trailingZeroes + 372 expoLength}; 373 int width{editWidth > 0 ? editWidth : totalLength}; 374 if (totalLength > width || !exponent) { 375 return EmitRepeated(io_, '*', width); 376 } 377 if (totalLength < width && digitsBeforePoint == 0 && 378 zeroesBeforePoint == 0) { 379 zeroesBeforePoint = 1; 380 ++totalLength; 381 } 382 if (totalLength < width && noLeadingSpaces) { 383 width = totalLength; 384 } 385 return EmitPrefix(edit, totalLength, width) && 386 EmitAscii(io_, converted.str, signLength + digitsBeforePoint) && 387 EmitRepeated(io_, '0', zeroesBeforePoint) && 388 EmitAscii(io_, edit.modes.editingFlags & decimalComma ? "," : ".", 1) && 389 EmitRepeated(io_, '0', zeroesAfterPoint) && 390 EmitAscii(io_, converted.str + signLength + digitsBeforePoint, 391 digitsAfterPoint) && 392 EmitRepeated(io_, '0', trailingZeroes) && 393 EmitAscii(io_, exponent, expoLength) && EmitSuffix(edit); 394 } 395 } 396 397 // 13.7.2.3.2 in F'2018 398 template <int KIND> 399 bool RealOutputEditing<KIND>::EditFOutput(const DataEdit &edit) { 400 addSpaceBeforeCharacter(io_); 401 int fracDigits{edit.digits.value_or(0)}; // 'd' field 402 const int editWidth{edit.width.value_or(0)}; // 'w' field 403 enum decimal::FortranRounding rounding{edit.modes.round}; 404 int flags{0}; 405 if (edit.modes.editingFlags & signPlus) { 406 flags |= decimal::AlwaysSign; 407 } 408 if (editWidth == 0) { // "the processor selects the field width" 409 if (!edit.digits.has_value()) { // F0 410 flags |= decimal::Minimize; 411 fracDigits = sizeof buffer_ - 2; // sign & NUL 412 } 413 } 414 // Multiple conversions may be needed to get the right number of 415 // effective rounded fractional digits. 416 int extraDigits{0}; 417 bool canIncrease{true}; 418 while (true) { 419 decimal::ConversionToDecimalResult converted{ 420 Convert(extraDigits + fracDigits, rounding, flags)}; 421 if (IsInfOrNaN(converted)) { 422 return editWidth > 0 && 423 converted.length > static_cast<std::size_t>(editWidth) 424 ? EmitRepeated(io_, '*', editWidth) 425 : EmitPrefix(edit, converted.length, editWidth) && 426 EmitAscii(io_, converted.str, converted.length) && 427 EmitSuffix(edit); 428 } 429 int expo{converted.decimalExponent + edit.modes.scale /*kP*/}; 430 int signLength{*converted.str == '-' || *converted.str == '+' ? 1 : 0}; 431 int convertedDigits{static_cast<int>(converted.length) - signLength}; 432 if (IsZero()) { // don't treat converted "0" as significant digit 433 expo = 0; 434 convertedDigits = 0; 435 } 436 int trailingOnes{0}; 437 if (expo > extraDigits && extraDigits >= 0 && canIncrease) { 438 extraDigits = expo; 439 if (!edit.digits.has_value()) { // F0 440 fracDigits = sizeof buffer_ - extraDigits - 2; // sign & NUL 441 } 442 canIncrease = false; // only once 443 continue; 444 } else if (expo == -fracDigits && convertedDigits > 0) { 445 if ((rounding == decimal::FortranRounding::RoundUp && 446 *converted.str != '-') || 447 (rounding == decimal::FortranRounding::RoundDown && 448 *converted.str == '-') || 449 (rounding == decimal::FortranRounding::RoundToZero && 450 rounding != edit.modes.round && // it changed below 451 converted.str[signLength] >= '5')) { 452 // Round up/down to a scaled 1 453 ++expo; 454 convertedDigits = 0; 455 trailingOnes = 1; 456 } else if (rounding != decimal::FortranRounding::RoundToZero) { 457 // Convert again with truncation so first digit can be checked 458 // on the next iteration by the code above 459 rounding = decimal::FortranRounding::RoundToZero; 460 continue; 461 } else { 462 // Value rounds down to zero 463 expo = 0; 464 convertedDigits = 0; 465 } 466 } else if (expo < extraDigits && extraDigits > -fracDigits) { 467 extraDigits = std::max(expo, -fracDigits); 468 continue; 469 } 470 int digitsBeforePoint{std::max(0, std::min(expo, convertedDigits))}; 471 int zeroesBeforePoint{std::max(0, expo - digitsBeforePoint)}; 472 int zeroesAfterPoint{std::min(fracDigits, std::max(0, -expo))}; 473 int digitsAfterPoint{convertedDigits - digitsBeforePoint}; 474 int trailingZeroes{flags & decimal::Minimize 475 ? 0 476 : std::max(0, 477 fracDigits - 478 (zeroesAfterPoint + digitsAfterPoint + trailingOnes))}; 479 if (digitsBeforePoint + zeroesBeforePoint + zeroesAfterPoint + 480 digitsAfterPoint + trailingOnes + trailingZeroes == 481 0) { 482 zeroesBeforePoint = 1; // "." -> "0." 483 } 484 int totalLength{signLength + digitsBeforePoint + zeroesBeforePoint + 485 1 /*'.'*/ + zeroesAfterPoint + digitsAfterPoint + trailingOnes + 486 trailingZeroes}; 487 int width{editWidth > 0 ? editWidth : totalLength}; 488 if (totalLength > width) { 489 return EmitRepeated(io_, '*', width); 490 } 491 if (totalLength < width && digitsBeforePoint + zeroesBeforePoint == 0) { 492 zeroesBeforePoint = 1; 493 ++totalLength; 494 } 495 return EmitPrefix(edit, totalLength, width) && 496 EmitAscii(io_, converted.str, signLength + digitsBeforePoint) && 497 EmitRepeated(io_, '0', zeroesBeforePoint) && 498 EmitAscii(io_, edit.modes.editingFlags & decimalComma ? "," : ".", 1) && 499 EmitRepeated(io_, '0', zeroesAfterPoint) && 500 EmitAscii(io_, converted.str + signLength + digitsBeforePoint, 501 digitsAfterPoint) && 502 EmitRepeated(io_, '1', trailingOnes) && 503 EmitRepeated(io_, '0', trailingZeroes) && 504 EmitRepeated(io_, ' ', trailingBlanks_) && EmitSuffix(edit); 505 } 506 } 507 508 // 13.7.5.2.3 in F'2018 509 template <int KIND> 510 DataEdit RealOutputEditing<KIND>::EditForGOutput(DataEdit edit) { 511 edit.descriptor = 'E'; 512 int editWidth{edit.width.value_or(0)}; 513 int significantDigits{ 514 edit.digits.value_or(BinaryFloatingPoint::decimalPrecision)}; // 'd' 515 if (editWidth > 0 && significantDigits == 0) { 516 return edit; // Gw.0Ee -> Ew.0Ee for w > 0 517 } 518 int flags{0}; 519 if (edit.modes.editingFlags & signPlus) { 520 flags |= decimal::AlwaysSign; 521 } 522 decimal::ConversionToDecimalResult converted{ 523 Convert(significantDigits, edit.modes.round, flags)}; 524 if (IsInfOrNaN(converted)) { 525 return edit; // Inf/Nan -> Ew.d (same as Fw.d) 526 } 527 int expo{IsZero() ? 1 : converted.decimalExponent}; // 's' 528 if (expo < 0 || expo > significantDigits) { 529 if (editWidth == 0 && !edit.expoDigits) { // G0.d -> G0.dE0 530 edit.expoDigits = 0; 531 } 532 return edit; // Ew.dEe 533 } 534 edit.descriptor = 'F'; 535 edit.modes.scale = 0; // kP is ignored for G when no exponent field 536 trailingBlanks_ = 0; 537 if (editWidth > 0) { 538 int expoDigits{edit.expoDigits.value_or(0)}; 539 trailingBlanks_ = expoDigits > 0 ? expoDigits + 2 : 4; // 'n' 540 *edit.width = std::max(0, editWidth - trailingBlanks_); 541 } 542 if (edit.digits.has_value()) { 543 *edit.digits = std::max(0, *edit.digits - expo); 544 } 545 return edit; 546 } 547 548 // 13.10.4 in F'2018 549 template <int KIND> 550 bool RealOutputEditing<KIND>::EditListDirectedOutput(const DataEdit &edit) { 551 decimal::ConversionToDecimalResult converted{Convert(1, edit.modes.round)}; 552 if (IsInfOrNaN(converted)) { 553 return EditEorDOutput(edit); 554 } 555 int expo{converted.decimalExponent}; 556 // The decimal precision of 16-bit floating-point types is very low, 557 // so use a reasonable cap of 6 to allow more values to be emitted 558 // with Fw.d editing. 559 static constexpr int maxExpo{ 560 std::max(6, BinaryFloatingPoint::decimalPrecision)}; 561 if (expo < 0 || expo > maxExpo) { 562 DataEdit copy{edit}; 563 copy.modes.scale = 1; // 1P 564 return EditEorDOutput(copy); 565 } 566 return EditFOutput(edit); 567 } 568 569 // 13.7.5.2.6 in F'2018 570 template <int KIND> 571 bool RealOutputEditing<KIND>::EditEXOutput(const DataEdit &) { 572 io_.GetIoErrorHandler().Crash( 573 "not yet implemented: EX output editing"); // TODO 574 } 575 576 template <int KIND> bool RealOutputEditing<KIND>::Edit(const DataEdit &edit) { 577 switch (edit.descriptor) { 578 case 'D': 579 return EditEorDOutput(edit); 580 case 'E': 581 if (edit.variation == 'X') { 582 return EditEXOutput(edit); 583 } else { 584 return EditEorDOutput(edit); 585 } 586 case 'F': 587 return EditFOutput(edit); 588 case 'B': 589 return EditBOZOutput<1>(io_, edit, 590 reinterpret_cast<const unsigned char *>(&x_), 591 common::BitsForBinaryPrecision(common::PrecisionOfRealKind(KIND)) >> 3); 592 case 'O': 593 return EditBOZOutput<3>(io_, edit, 594 reinterpret_cast<const unsigned char *>(&x_), 595 common::BitsForBinaryPrecision(common::PrecisionOfRealKind(KIND)) >> 3); 596 case 'Z': 597 return EditBOZOutput<4>(io_, edit, 598 reinterpret_cast<const unsigned char *>(&x_), 599 common::BitsForBinaryPrecision(common::PrecisionOfRealKind(KIND)) >> 3); 600 case 'G': 601 return Edit(EditForGOutput(edit)); 602 case 'L': 603 return EditLogicalOutput(io_, edit, *reinterpret_cast<const char *>(&x_)); 604 case 'A': // legacy extension 605 return EditCharacterOutput( 606 io_, edit, reinterpret_cast<char *>(&x_), sizeof x_); 607 default: 608 if (edit.IsListDirected()) { 609 return EditListDirectedOutput(edit); 610 } 611 io_.GetIoErrorHandler().SignalError(IostatErrorInFormat, 612 "Data edit descriptor '%c' may not be used with a REAL data item", 613 edit.descriptor); 614 return false; 615 } 616 return false; 617 } 618 619 bool ListDirectedLogicalOutput(IoStatementState &io, 620 ListDirectedStatementState<Direction::Output> &list, bool truth) { 621 return list.EmitLeadingSpaceOrAdvance(io) && 622 EmitAscii(io, truth ? "T" : "F", 1); 623 } 624 625 bool EditLogicalOutput(IoStatementState &io, const DataEdit &edit, bool truth) { 626 switch (edit.descriptor) { 627 case 'L': 628 case 'G': 629 return EmitRepeated(io, ' ', std::max(0, edit.width.value_or(1) - 1)) && 630 EmitAscii(io, truth ? "T" : "F", 1); 631 case 'B': 632 return EditBOZOutput<1>(io, edit, 633 reinterpret_cast<const unsigned char *>(&truth), sizeof truth); 634 case 'O': 635 return EditBOZOutput<3>(io, edit, 636 reinterpret_cast<const unsigned char *>(&truth), sizeof truth); 637 case 'Z': 638 return EditBOZOutput<4>(io, edit, 639 reinterpret_cast<const unsigned char *>(&truth), sizeof truth); 640 default: 641 io.GetIoErrorHandler().SignalError(IostatErrorInFormat, 642 "Data edit descriptor '%c' may not be used with a LOGICAL data item", 643 edit.descriptor); 644 return false; 645 } 646 } 647 648 template <typename CHAR> 649 bool ListDirectedCharacterOutput(IoStatementState &io, 650 ListDirectedStatementState<Direction::Output> &list, const CHAR *x, 651 std::size_t length) { 652 bool ok{true}; 653 MutableModes &modes{io.mutableModes()}; 654 ConnectionState &connection{io.GetConnectionState()}; 655 if (modes.delim) { 656 ok = ok && list.EmitLeadingSpaceOrAdvance(io); 657 // Value is delimited with ' or " marks, and interior 658 // instances of that character are doubled. 659 auto EmitOne{[&](CHAR ch) { 660 if (connection.NeedAdvance(1)) { 661 ok = ok && io.AdvanceRecord(); 662 } 663 ok = ok && EmitEncoded(io, &ch, 1); 664 }}; 665 EmitOne(modes.delim); 666 for (std::size_t j{0}; j < length; ++j) { 667 // Doubled delimiters must be put on the same record 668 // in order to be acceptable as list-directed or NAMELIST 669 // input; however, this requirement is not always possible 670 // when the records have a fixed length, as is the case with 671 // internal output. The standard is silent on what should 672 // happen, and no two extant Fortran implementations do 673 // the same thing when tested with this case. 674 // This runtime splits the doubled delimiters across 675 // two records for lack of a better alternative. 676 if (x[j] == static_cast<CHAR>(modes.delim)) { 677 EmitOne(x[j]); 678 } 679 EmitOne(x[j]); 680 } 681 EmitOne(modes.delim); 682 } else { 683 // Undelimited list-directed output 684 ok = ok && list.EmitLeadingSpaceOrAdvance(io, length > 0 ? 1 : 0, true); 685 std::size_t put{0}; 686 std::size_t oneAtATime{ 687 connection.useUTF8<CHAR>() || connection.internalIoCharKind > 1 688 ? 1 689 : length}; 690 while (ok && put < length) { 691 if (std::size_t chunk{std::min<std::size_t>( 692 std::min<std::size_t>(length - put, oneAtATime), 693 connection.RemainingSpaceInRecord())}) { 694 ok = EmitEncoded(io, x + put, chunk); 695 put += chunk; 696 } else { 697 ok = io.AdvanceRecord() && EmitAscii(io, " ", 1); 698 } 699 } 700 list.set_lastWasUndelimitedCharacter(true); 701 } 702 return ok; 703 } 704 705 template <typename CHAR> 706 bool EditCharacterOutput(IoStatementState &io, const DataEdit &edit, 707 const CHAR *x, std::size_t length) { 708 int len{static_cast<int>(length)}; 709 int width{edit.width.value_or(len)}; 710 switch (edit.descriptor) { 711 case 'A': 712 break; 713 case 'G': 714 if (width == 0) { 715 width = len; 716 } 717 break; 718 case 'B': 719 return EditBOZOutput<1>(io, edit, 720 reinterpret_cast<const unsigned char *>(x), sizeof(CHAR) * length); 721 case 'O': 722 return EditBOZOutput<3>(io, edit, 723 reinterpret_cast<const unsigned char *>(x), sizeof(CHAR) * length); 724 case 'Z': 725 return EditBOZOutput<4>(io, edit, 726 reinterpret_cast<const unsigned char *>(x), sizeof(CHAR) * length); 727 case 'L': 728 return EditLogicalOutput(io, edit, *reinterpret_cast<const char *>(x)); 729 default: 730 io.GetIoErrorHandler().SignalError(IostatErrorInFormat, 731 "Data edit descriptor '%c' may not be used with a CHARACTER data item", 732 edit.descriptor); 733 return false; 734 } 735 return EmitRepeated(io, ' ', std::max(0, width - len)) && 736 EmitEncoded(io, x, std::min(width, len)); 737 } 738 739 template bool EditIntegerOutput<1>( 740 IoStatementState &, const DataEdit &, std::int8_t); 741 template bool EditIntegerOutput<2>( 742 IoStatementState &, const DataEdit &, std::int16_t); 743 template bool EditIntegerOutput<4>( 744 IoStatementState &, const DataEdit &, std::int32_t); 745 template bool EditIntegerOutput<8>( 746 IoStatementState &, const DataEdit &, std::int64_t); 747 template bool EditIntegerOutput<16>( 748 IoStatementState &, const DataEdit &, common::int128_t); 749 750 template class RealOutputEditing<2>; 751 template class RealOutputEditing<3>; 752 template class RealOutputEditing<4>; 753 template class RealOutputEditing<8>; 754 template class RealOutputEditing<10>; 755 // TODO: double/double 756 template class RealOutputEditing<16>; 757 758 template bool ListDirectedCharacterOutput(IoStatementState &, 759 ListDirectedStatementState<Direction::Output> &, const char *, 760 std::size_t chars); 761 template bool ListDirectedCharacterOutput(IoStatementState &, 762 ListDirectedStatementState<Direction::Output> &, const char16_t *, 763 std::size_t chars); 764 template bool ListDirectedCharacterOutput(IoStatementState &, 765 ListDirectedStatementState<Direction::Output> &, const char32_t *, 766 std::size_t chars); 767 768 template bool EditCharacterOutput( 769 IoStatementState &, const DataEdit &, const char *, std::size_t chars); 770 template bool EditCharacterOutput( 771 IoStatementState &, const DataEdit &, const char16_t *, std::size_t chars); 772 template bool EditCharacterOutput( 773 IoStatementState &, const DataEdit &, const char32_t *, std::size_t chars); 774 775 } // namespace Fortran::runtime::io 776