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