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