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' 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 if (editWidth == 0) { // "the processor selects the field width" 265 if (edit.digits.has_value()) { // E0.d 266 if (editDigits == 0) { // E0.0 267 editWidth = 7; // -.0E+ee 268 } else { 269 editWidth = editDigits + 6; // -.666E+ee 270 } 271 } else { // E0 272 flags |= decimal::Minimize; 273 significantDigits = 274 sizeof buffer_ - 5; // sign, NUL, + 3 extra for EN scaling 275 } 276 } 277 bool isEN{edit.variation == 'N'}; 278 bool isES{edit.variation == 'S'}; 279 int scale{edit.modes.scale}; // 'kP' value 280 int zeroesAfterPoint{0}; 281 if (isEN) { 282 scale = IsZero() ? 1 : 3; 283 significantDigits += scale; 284 } else if (isES) { 285 scale = 1; 286 ++significantDigits; 287 } else if (scale < 0) { 288 if (scale <= -editDigits) { 289 io_.GetIoErrorHandler().SignalError(IostatBadScaleFactor, 290 "Scale factor (kP) %d cannot be less than -d (%d)", scale, 291 -editDigits); 292 return false; 293 } 294 zeroesAfterPoint = -scale; 295 significantDigits = std::max(0, significantDigits - zeroesAfterPoint); 296 } else if (scale > 0) { 297 if (scale >= editDigits + 2) { 298 io_.GetIoErrorHandler().SignalError(IostatBadScaleFactor, 299 "Scale factor (kP) %d cannot be greater than d+2 (%d)", scale, 300 editDigits + 2); 301 return false; 302 } 303 ++significantDigits; 304 scale = std::min(scale, significantDigits + 1); 305 } 306 // In EN editing, multiple attempts may be necessary, so this is a loop. 307 while (true) { 308 decimal::ConversionToDecimalResult converted{ 309 Convert(significantDigits, edit.modes.round, flags)}; 310 if (IsInfOrNaN(converted)) { 311 return EmitPrefix(edit, converted.length, editWidth) && 312 io_.Emit(converted.str, converted.length) && EmitSuffix(edit); 313 } 314 if (!IsZero()) { 315 converted.decimalExponent -= scale; 316 } 317 if (isEN) { 318 // EN mode: we need an effective exponent field that is 319 // a multiple of three. 320 if (int modulus{converted.decimalExponent % 3}; modulus != 0) { 321 if (significantDigits > 1) { 322 --significantDigits; 323 --scale; 324 continue; 325 } 326 // Rounded nines up to a 1. 327 scale += modulus; 328 converted.decimalExponent -= modulus; 329 } 330 if (scale > 3) { 331 int adjust{3 * (scale / 3)}; 332 scale -= adjust; 333 converted.decimalExponent += adjust; 334 } else if (scale < 1) { 335 int adjust{3 - 3 * (scale / 3)}; 336 scale += adjust; 337 converted.decimalExponent -= adjust; 338 } 339 significantDigits = editDigits + scale; 340 } 341 // Format the exponent (see table 13.1 for all the cases) 342 int expoLength{0}; 343 const char *exponent{ 344 FormatExponent(converted.decimalExponent, edit, expoLength)}; 345 int signLength{*converted.str == '-' || *converted.str == '+' ? 1 : 0}; 346 int convertedDigits{static_cast<int>(converted.length) - signLength}; 347 int zeroesBeforePoint{std::max(0, scale - convertedDigits)}; 348 int digitsBeforePoint{std::max(0, scale - zeroesBeforePoint)}; 349 int digitsAfterPoint{convertedDigits - digitsBeforePoint}; 350 int trailingZeroes{flags & decimal::Minimize 351 ? 0 352 : std::max(0, 353 significantDigits - (convertedDigits + zeroesBeforePoint))}; 354 int totalLength{signLength + digitsBeforePoint + zeroesBeforePoint + 355 1 /*'.'*/ + zeroesAfterPoint + digitsAfterPoint + trailingZeroes + 356 expoLength}; 357 int width{editWidth > 0 ? editWidth : totalLength}; 358 if (totalLength > width || !exponent) { 359 return io_.EmitRepeated('*', width); 360 } 361 if (totalLength < width && digitsBeforePoint == 0 && 362 zeroesBeforePoint == 0) { 363 zeroesBeforePoint = 1; 364 ++totalLength; 365 } 366 if (totalLength < width && noLeadingSpaces) { 367 width = totalLength; 368 } 369 return EmitPrefix(edit, totalLength, width) && 370 io_.Emit(converted.str, signLength + digitsBeforePoint) && 371 io_.EmitRepeated('0', zeroesBeforePoint) && 372 io_.Emit(edit.modes.editingFlags & decimalComma ? "," : ".", 1) && 373 io_.EmitRepeated('0', zeroesAfterPoint) && 374 io_.Emit( 375 converted.str + signLength + digitsBeforePoint, digitsAfterPoint) && 376 io_.EmitRepeated('0', trailingZeroes) && 377 io_.Emit(exponent, expoLength) && EmitSuffix(edit); 378 } 379 } 380 381 // 13.7.2.3.2 in F'2018 382 template <int binaryPrecision> 383 bool RealOutputEditing<binaryPrecision>::EditFOutput(const DataEdit &edit) { 384 int fracDigits{edit.digits.value_or(0)}; // 'd' field 385 const int editWidth{edit.width.value_or(0)}; // 'w' field 386 enum decimal::FortranRounding rounding{edit.modes.round}; 387 int flags{0}; 388 if (edit.modes.editingFlags & signPlus) { 389 flags |= decimal::AlwaysSign; 390 } 391 if (editWidth == 0) { // "the processor selects the field width" 392 if (!edit.digits.has_value()) { // F0 393 flags |= decimal::Minimize; 394 fracDigits = sizeof buffer_ - 2; // sign & NUL 395 } 396 } 397 // Multiple conversions may be needed to get the right number of 398 // effective rounded fractional digits. 399 int extraDigits{0}; 400 bool canIncrease{true}; 401 while (true) { 402 decimal::ConversionToDecimalResult converted{ 403 Convert(extraDigits + fracDigits, rounding, flags)}; 404 if (IsInfOrNaN(converted)) { 405 return EmitPrefix(edit, converted.length, editWidth) && 406 io_.Emit(converted.str, converted.length) && EmitSuffix(edit); 407 } 408 int expo{converted.decimalExponent + edit.modes.scale /*kP*/}; 409 int signLength{*converted.str == '-' || *converted.str == '+' ? 1 : 0}; 410 int convertedDigits{static_cast<int>(converted.length) - signLength}; 411 if (IsZero()) { // don't treat converted "0" as significant digit 412 expo = 0; 413 convertedDigits = 0; 414 } 415 int trailingOnes{0}; 416 if (expo > extraDigits && extraDigits >= 0 && canIncrease) { 417 extraDigits = expo; 418 if (!edit.digits.has_value()) { // F0 419 fracDigits = sizeof buffer_ - extraDigits - 2; // sign & NUL 420 } 421 canIncrease = false; // only once 422 continue; 423 } else if (expo == -fracDigits && convertedDigits > 0) { 424 if (rounding != decimal::FortranRounding::RoundToZero) { 425 // Convert again without rounding so that we can round here 426 rounding = decimal::FortranRounding::RoundToZero; 427 continue; 428 } else if (converted.str[signLength] >= '5') { 429 // Value rounds up to a scaled 1 (e.g., 0.06 for F5.1 -> 0.1) 430 ++expo; 431 convertedDigits = 0; 432 trailingOnes = 1; 433 } else { 434 // Value rounds down to zero 435 expo = 0; 436 convertedDigits = 0; 437 } 438 } else if (expo < extraDigits && extraDigits > -fracDigits) { 439 extraDigits = std::max(expo, -fracDigits); 440 continue; 441 } 442 int digitsBeforePoint{std::max(0, std::min(expo, convertedDigits))}; 443 int zeroesBeforePoint{std::max(0, expo - digitsBeforePoint)}; 444 int zeroesAfterPoint{std::min(fracDigits, std::max(0, -expo))}; 445 int digitsAfterPoint{convertedDigits - digitsBeforePoint}; 446 int trailingZeroes{flags & decimal::Minimize 447 ? 0 448 : std::max(0, 449 fracDigits - 450 (zeroesAfterPoint + digitsAfterPoint + trailingOnes))}; 451 if (digitsBeforePoint + zeroesBeforePoint + zeroesAfterPoint + 452 digitsAfterPoint + trailingOnes + trailingZeroes == 453 0) { 454 zeroesBeforePoint = 1; // "." -> "0." 455 } 456 int totalLength{signLength + digitsBeforePoint + zeroesBeforePoint + 457 1 /*'.'*/ + zeroesAfterPoint + digitsAfterPoint + trailingOnes + 458 trailingZeroes}; 459 int width{editWidth > 0 ? editWidth : totalLength}; 460 if (totalLength > width) { 461 return io_.EmitRepeated('*', width); 462 } 463 if (totalLength < width && digitsBeforePoint + zeroesBeforePoint == 0) { 464 zeroesBeforePoint = 1; 465 ++totalLength; 466 } 467 return EmitPrefix(edit, totalLength, width) && 468 io_.Emit(converted.str, signLength + digitsBeforePoint) && 469 io_.EmitRepeated('0', zeroesBeforePoint) && 470 io_.Emit(edit.modes.editingFlags & decimalComma ? "," : ".", 1) && 471 io_.EmitRepeated('0', zeroesAfterPoint) && 472 io_.Emit( 473 converted.str + signLength + digitsBeforePoint, digitsAfterPoint) && 474 io_.EmitRepeated('1', trailingOnes) && 475 io_.EmitRepeated('0', trailingZeroes) && 476 io_.EmitRepeated(' ', trailingBlanks_) && EmitSuffix(edit); 477 } 478 } 479 480 // 13.7.5.2.3 in F'2018 481 template <int binaryPrecision> 482 DataEdit RealOutputEditing<binaryPrecision>::EditForGOutput(DataEdit edit) { 483 edit.descriptor = 'E'; 484 int editWidth{edit.width.value_or(0)}; 485 int significantDigits{ 486 edit.digits.value_or(BinaryFloatingPoint::decimalPrecision)}; // 'd' 487 if (editWidth > 0 && significantDigits == 0) { 488 return edit; // Gw.0 -> Ew.0 for w > 0 489 } 490 int flags{0}; 491 if (edit.modes.editingFlags & signPlus) { 492 flags |= decimal::AlwaysSign; 493 } 494 decimal::ConversionToDecimalResult converted{ 495 Convert(significantDigits, edit.modes.round, flags)}; 496 if (IsInfOrNaN(converted)) { 497 return edit; // Inf/Nan -> Ew.d (same as Fw.d) 498 } 499 int expo{IsZero() ? 1 : converted.decimalExponent}; // 's' 500 if (expo < 0 || expo > significantDigits) { 501 return edit; // Ew.d 502 } 503 edit.descriptor = 'F'; 504 edit.modes.scale = 0; // kP is ignored for G when no exponent field 505 trailingBlanks_ = 0; 506 if (editWidth > 0) { 507 int expoDigits{edit.expoDigits.value_or(0)}; 508 trailingBlanks_ = expoDigits > 0 ? expoDigits + 2 : 4; // 'n' 509 *edit.width = std::max(0, editWidth - trailingBlanks_); 510 } 511 if (edit.digits.has_value()) { 512 *edit.digits = std::max(0, *edit.digits - expo); 513 } 514 return edit; 515 } 516 517 // 13.10.4 in F'2018 518 template <int binaryPrecision> 519 bool RealOutputEditing<binaryPrecision>::EditListDirectedOutput( 520 const DataEdit &edit) { 521 decimal::ConversionToDecimalResult converted{Convert(1, edit.modes.round)}; 522 if (IsInfOrNaN(converted)) { 523 return EditEorDOutput(edit); 524 } 525 int expo{converted.decimalExponent}; 526 if (expo < 0 || expo > BinaryFloatingPoint::decimalPrecision) { 527 DataEdit copy{edit}; 528 copy.modes.scale = 1; // 1P 529 return EditEorDOutput(copy); 530 } 531 return EditFOutput(edit); 532 } 533 534 // 13.7.5.2.6 in F'2018 535 template <int binaryPrecision> 536 bool RealOutputEditing<binaryPrecision>::EditEXOutput(const DataEdit &) { 537 io_.GetIoErrorHandler().Crash( 538 "not yet implemented: EX output editing"); // TODO 539 } 540 541 template <int KIND> bool RealOutputEditing<KIND>::Edit(const DataEdit &edit) { 542 switch (edit.descriptor) { 543 case 'D': 544 return EditEorDOutput(edit); 545 case 'E': 546 if (edit.variation == 'X') { 547 return EditEXOutput(edit); 548 } else { 549 return EditEorDOutput(edit); 550 } 551 case 'F': 552 return EditFOutput(edit); 553 case 'B': 554 return EditBOZOutput<1>(io_, edit, 555 reinterpret_cast<const unsigned char *>(&x_), 556 common::BitsForBinaryPrecision(common::PrecisionOfRealKind(KIND)) >> 3); 557 case 'O': 558 return EditBOZOutput<3>(io_, edit, 559 reinterpret_cast<const unsigned char *>(&x_), 560 common::BitsForBinaryPrecision(common::PrecisionOfRealKind(KIND)) >> 3); 561 case 'Z': 562 return EditBOZOutput<4>(io_, edit, 563 reinterpret_cast<const unsigned char *>(&x_), 564 common::BitsForBinaryPrecision(common::PrecisionOfRealKind(KIND)) >> 3); 565 case 'G': 566 return Edit(EditForGOutput(edit)); 567 case 'A': // legacy extension 568 return EditCharacterOutput( 569 io_, edit, reinterpret_cast<char *>(&x_), sizeof x_); 570 default: 571 if (edit.IsListDirected()) { 572 return EditListDirectedOutput(edit); 573 } 574 io_.GetIoErrorHandler().SignalError(IostatErrorInFormat, 575 "Data edit descriptor '%c' may not be used with a REAL data item", 576 edit.descriptor); 577 return false; 578 } 579 return false; 580 } 581 582 bool ListDirectedLogicalOutput(IoStatementState &io, 583 ListDirectedStatementState<Direction::Output> &list, bool truth) { 584 return list.EmitLeadingSpaceOrAdvance(io) && io.Emit(truth ? "T" : "F", 1); 585 } 586 587 bool EditLogicalOutput(IoStatementState &io, const DataEdit &edit, bool truth) { 588 switch (edit.descriptor) { 589 case 'L': 590 case 'G': 591 return io.EmitRepeated(' ', std::max(0, edit.width.value_or(1) - 1)) && 592 io.Emit(truth ? "T" : "F", 1); 593 case 'B': 594 return EditBOZOutput<1>(io, edit, 595 reinterpret_cast<const unsigned char *>(&truth), sizeof truth); 596 case 'O': 597 return EditBOZOutput<3>(io, edit, 598 reinterpret_cast<const unsigned char *>(&truth), sizeof truth); 599 case 'Z': 600 return EditBOZOutput<4>(io, edit, 601 reinterpret_cast<const unsigned char *>(&truth), sizeof truth); 602 default: 603 io.GetIoErrorHandler().SignalError(IostatErrorInFormat, 604 "Data edit descriptor '%c' may not be used with a LOGICAL data item", 605 edit.descriptor); 606 return false; 607 } 608 } 609 610 template <typename CHAR> 611 bool ListDirectedCharacterOutput(IoStatementState &io, 612 ListDirectedStatementState<Direction::Output> &list, const CHAR *x, 613 std::size_t length) { 614 bool ok{true}; 615 MutableModes &modes{io.mutableModes()}; 616 ConnectionState &connection{io.GetConnectionState()}; 617 if (modes.delim) { 618 ok = ok && list.EmitLeadingSpaceOrAdvance(io); 619 // Value is delimited with ' or " marks, and interior 620 // instances of that character are doubled. 621 auto EmitOne{[&](CHAR ch) { 622 if (connection.NeedAdvance(1)) { 623 ok = ok && io.AdvanceRecord(); 624 } 625 ok = ok && io.EmitEncoded(&ch, 1); 626 }}; 627 EmitOne(modes.delim); 628 for (std::size_t j{0}; j < length; ++j) { 629 // Doubled delimiters must be put on the same record 630 // in order to be acceptable as list-directed or NAMELIST 631 // input; however, this requirement is not always possible 632 // when the records have a fixed length, as is the case with 633 // internal output. The standard is silent on what should 634 // happen, and no two extant Fortran implementations do 635 // the same thing when tested with this case. 636 // This runtime splits the doubled delimiters across 637 // two records for lack of a better alternative. 638 if (x[j] == static_cast<CHAR>(modes.delim)) { 639 EmitOne(x[j]); 640 } 641 EmitOne(x[j]); 642 } 643 EmitOne(modes.delim); 644 } else { 645 // Undelimited list-directed output 646 ok = ok && list.EmitLeadingSpaceOrAdvance(io, length > 0 ? 1 : 0, true); 647 std::size_t put{0}; 648 std::size_t oneIfUTF8{connection.useUTF8<CHAR>() ? 1 : length}; 649 while (ok && put < length) { 650 if (std::size_t chunk{std::min<std::size_t>( 651 std::min<std::size_t>(length - put, oneIfUTF8), 652 connection.RemainingSpaceInRecord())}) { 653 ok = io.EmitEncoded(x + put, chunk); 654 put += chunk; 655 } else { 656 ok = io.AdvanceRecord() && io.Emit(" ", 1); 657 } 658 } 659 list.set_lastWasUndelimitedCharacter(true); 660 } 661 return ok; 662 } 663 664 template <typename CHAR> 665 bool EditCharacterOutput(IoStatementState &io, const DataEdit &edit, 666 const CHAR *x, std::size_t length) { 667 int len{static_cast<int>(length)}; 668 int width{edit.width.value_or(len)}; 669 switch (edit.descriptor) { 670 case 'A': 671 break; 672 case 'G': 673 if (width == 0) { 674 width = len; 675 } 676 break; 677 case 'B': 678 return EditBOZOutput<1>(io, edit, 679 reinterpret_cast<const unsigned char *>(x), sizeof(CHAR) * length); 680 case 'O': 681 return EditBOZOutput<3>(io, edit, 682 reinterpret_cast<const unsigned char *>(x), sizeof(CHAR) * length); 683 case 'Z': 684 return EditBOZOutput<4>(io, edit, 685 reinterpret_cast<const unsigned char *>(x), sizeof(CHAR) * length); 686 default: 687 io.GetIoErrorHandler().SignalError(IostatErrorInFormat, 688 "Data edit descriptor '%c' may not be used with a CHARACTER data item", 689 edit.descriptor); 690 return false; 691 } 692 return io.EmitRepeated(' ', std::max(0, width - len)) && 693 io.EmitEncoded(x, std::min(width, len)); 694 } 695 696 template bool EditIntegerOutput<1>( 697 IoStatementState &, const DataEdit &, std::int8_t); 698 template bool EditIntegerOutput<2>( 699 IoStatementState &, const DataEdit &, std::int16_t); 700 template bool EditIntegerOutput<4>( 701 IoStatementState &, const DataEdit &, std::int32_t); 702 template bool EditIntegerOutput<8>( 703 IoStatementState &, const DataEdit &, std::int64_t); 704 template bool EditIntegerOutput<16>( 705 IoStatementState &, const DataEdit &, common::int128_t); 706 707 template class RealOutputEditing<2>; 708 template class RealOutputEditing<3>; 709 template class RealOutputEditing<4>; 710 template class RealOutputEditing<8>; 711 template class RealOutputEditing<10>; 712 // TODO: double/double 713 template class RealOutputEditing<16>; 714 715 template bool ListDirectedCharacterOutput(IoStatementState &, 716 ListDirectedStatementState<Direction::Output> &, const char *, 717 std::size_t chars); 718 template bool ListDirectedCharacterOutput(IoStatementState &, 719 ListDirectedStatementState<Direction::Output> &, const char16_t *, 720 std::size_t chars); 721 template bool ListDirectedCharacterOutput(IoStatementState &, 722 ListDirectedStatementState<Direction::Output> &, const char32_t *, 723 std::size_t chars); 724 725 template bool EditCharacterOutput( 726 IoStatementState &, const DataEdit &, const char *, std::size_t chars); 727 template bool EditCharacterOutput( 728 IoStatementState &, const DataEdit &, const char16_t *, std::size_t chars); 729 template bool EditCharacterOutput( 730 IoStatementState &, const DataEdit &, const char32_t *, std::size_t chars); 731 732 } // namespace Fortran::runtime::io 733