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::RoundToZero) { 423 // Convert again without rounding so that we can round here 424 rounding = decimal::FortranRounding::RoundToZero; 425 continue; 426 } else if (converted.str[signLength] >= '5') { 427 // Value rounds up to a scaled 1 (e.g., 0.06 for F5.1 -> 0.1) 428 ++expo; 429 convertedDigits = 0; 430 trailingOnes = 1; 431 } else { 432 // Value rounds down to zero 433 expo = 0; 434 convertedDigits = 0; 435 } 436 } else if (expo < extraDigits && extraDigits > -fracDigits) { 437 extraDigits = std::max(expo, -fracDigits); 438 continue; 439 } 440 int digitsBeforePoint{std::max(0, std::min(expo, convertedDigits))}; 441 int zeroesBeforePoint{std::max(0, expo - digitsBeforePoint)}; 442 int zeroesAfterPoint{std::min(fracDigits, std::max(0, -expo))}; 443 int digitsAfterPoint{convertedDigits - digitsBeforePoint}; 444 int trailingZeroes{flags & decimal::Minimize 445 ? 0 446 : std::max(0, 447 fracDigits - 448 (zeroesAfterPoint + digitsAfterPoint + trailingOnes))}; 449 if (digitsBeforePoint + zeroesBeforePoint + zeroesAfterPoint + 450 digitsAfterPoint + trailingOnes + trailingZeroes == 451 0) { 452 zeroesBeforePoint = 1; // "." -> "0." 453 } 454 int totalLength{signLength + digitsBeforePoint + zeroesBeforePoint + 455 1 /*'.'*/ + zeroesAfterPoint + digitsAfterPoint + trailingOnes + 456 trailingZeroes}; 457 int width{editWidth > 0 ? editWidth : totalLength}; 458 if (totalLength > width) { 459 return io_.EmitRepeated('*', width); 460 } 461 if (totalLength < width && digitsBeforePoint + zeroesBeforePoint == 0) { 462 zeroesBeforePoint = 1; 463 ++totalLength; 464 } 465 return EmitPrefix(edit, totalLength, width) && 466 io_.Emit(converted.str, signLength + digitsBeforePoint) && 467 io_.EmitRepeated('0', zeroesBeforePoint) && 468 io_.Emit(edit.modes.editingFlags & decimalComma ? "," : ".", 1) && 469 io_.EmitRepeated('0', zeroesAfterPoint) && 470 io_.Emit( 471 converted.str + signLength + digitsBeforePoint, digitsAfterPoint) && 472 io_.EmitRepeated('1', trailingOnes) && 473 io_.EmitRepeated('0', trailingZeroes) && 474 io_.EmitRepeated(' ', trailingBlanks_) && EmitSuffix(edit); 475 } 476 } 477 478 // 13.7.5.2.3 in F'2018 479 template <int binaryPrecision> 480 DataEdit RealOutputEditing<binaryPrecision>::EditForGOutput(DataEdit edit) { 481 edit.descriptor = 'E'; 482 int editWidth{edit.width.value_or(0)}; 483 int significantDigits{ 484 edit.digits.value_or(BinaryFloatingPoint::decimalPrecision)}; // 'd' 485 if (editWidth > 0 && significantDigits == 0) { 486 return edit; // Gw.0Ee -> Ew.0Ee for w > 0 487 } 488 int flags{0}; 489 if (edit.modes.editingFlags & signPlus) { 490 flags |= decimal::AlwaysSign; 491 } 492 decimal::ConversionToDecimalResult converted{ 493 Convert(significantDigits, edit.modes.round, flags)}; 494 if (IsInfOrNaN(converted)) { 495 return edit; // Inf/Nan -> Ew.d (same as Fw.d) 496 } 497 int expo{IsZero() ? 1 : converted.decimalExponent}; // 's' 498 if (expo < 0 || expo > significantDigits) { 499 if (editWidth == 0 && !edit.expoDigits) { // G0.d -> G0.dE0 500 edit.expoDigits = 0; 501 } 502 return edit; // Ew.dEe 503 } 504 edit.descriptor = 'F'; 505 edit.modes.scale = 0; // kP is ignored for G when no exponent field 506 trailingBlanks_ = 0; 507 if (editWidth > 0) { 508 int expoDigits{edit.expoDigits.value_or(0)}; 509 trailingBlanks_ = expoDigits > 0 ? expoDigits + 2 : 4; // 'n' 510 *edit.width = std::max(0, editWidth - trailingBlanks_); 511 } 512 if (edit.digits.has_value()) { 513 *edit.digits = std::max(0, *edit.digits - expo); 514 } 515 return edit; 516 } 517 518 // 13.10.4 in F'2018 519 template <int binaryPrecision> 520 bool RealOutputEditing<binaryPrecision>::EditListDirectedOutput( 521 const DataEdit &edit) { 522 decimal::ConversionToDecimalResult converted{Convert(1, edit.modes.round)}; 523 if (IsInfOrNaN(converted)) { 524 return EditEorDOutput(edit); 525 } 526 int expo{converted.decimalExponent}; 527 if (expo < 0 || expo > BinaryFloatingPoint::decimalPrecision) { 528 DataEdit copy{edit}; 529 copy.modes.scale = 1; // 1P 530 return EditEorDOutput(copy); 531 } 532 return EditFOutput(edit); 533 } 534 535 // 13.7.5.2.6 in F'2018 536 template <int binaryPrecision> 537 bool RealOutputEditing<binaryPrecision>::EditEXOutput(const DataEdit &) { 538 io_.GetIoErrorHandler().Crash( 539 "not yet implemented: EX output editing"); // TODO 540 } 541 542 template <int KIND> bool RealOutputEditing<KIND>::Edit(const DataEdit &edit) { 543 switch (edit.descriptor) { 544 case 'D': 545 return EditEorDOutput(edit); 546 case 'E': 547 if (edit.variation == 'X') { 548 return EditEXOutput(edit); 549 } else { 550 return EditEorDOutput(edit); 551 } 552 case 'F': 553 return EditFOutput(edit); 554 case 'B': 555 return EditBOZOutput<1>(io_, edit, 556 reinterpret_cast<const unsigned char *>(&x_), 557 common::BitsForBinaryPrecision(common::PrecisionOfRealKind(KIND)) >> 3); 558 case 'O': 559 return EditBOZOutput<3>(io_, edit, 560 reinterpret_cast<const unsigned char *>(&x_), 561 common::BitsForBinaryPrecision(common::PrecisionOfRealKind(KIND)) >> 3); 562 case 'Z': 563 return EditBOZOutput<4>(io_, edit, 564 reinterpret_cast<const unsigned char *>(&x_), 565 common::BitsForBinaryPrecision(common::PrecisionOfRealKind(KIND)) >> 3); 566 case 'G': 567 return Edit(EditForGOutput(edit)); 568 case 'A': // legacy extension 569 return EditCharacterOutput( 570 io_, edit, reinterpret_cast<char *>(&x_), sizeof x_); 571 default: 572 if (edit.IsListDirected()) { 573 return EditListDirectedOutput(edit); 574 } 575 io_.GetIoErrorHandler().SignalError(IostatErrorInFormat, 576 "Data edit descriptor '%c' may not be used with a REAL data item", 577 edit.descriptor); 578 return false; 579 } 580 return false; 581 } 582 583 bool ListDirectedLogicalOutput(IoStatementState &io, 584 ListDirectedStatementState<Direction::Output> &list, bool truth) { 585 return list.EmitLeadingSpaceOrAdvance(io) && io.Emit(truth ? "T" : "F", 1); 586 } 587 588 bool EditLogicalOutput(IoStatementState &io, const DataEdit &edit, bool truth) { 589 switch (edit.descriptor) { 590 case 'L': 591 case 'G': 592 return io.EmitRepeated(' ', std::max(0, edit.width.value_or(1) - 1)) && 593 io.Emit(truth ? "T" : "F", 1); 594 case 'B': 595 return EditBOZOutput<1>(io, edit, 596 reinterpret_cast<const unsigned char *>(&truth), sizeof truth); 597 case 'O': 598 return EditBOZOutput<3>(io, edit, 599 reinterpret_cast<const unsigned char *>(&truth), sizeof truth); 600 case 'Z': 601 return EditBOZOutput<4>(io, edit, 602 reinterpret_cast<const unsigned char *>(&truth), sizeof truth); 603 default: 604 io.GetIoErrorHandler().SignalError(IostatErrorInFormat, 605 "Data edit descriptor '%c' may not be used with a LOGICAL data item", 606 edit.descriptor); 607 return false; 608 } 609 } 610 611 template <typename CHAR> 612 bool ListDirectedCharacterOutput(IoStatementState &io, 613 ListDirectedStatementState<Direction::Output> &list, const CHAR *x, 614 std::size_t length) { 615 bool ok{true}; 616 MutableModes &modes{io.mutableModes()}; 617 ConnectionState &connection{io.GetConnectionState()}; 618 if (modes.delim) { 619 ok = ok && list.EmitLeadingSpaceOrAdvance(io); 620 // Value is delimited with ' or " marks, and interior 621 // instances of that character are doubled. 622 auto EmitOne{[&](CHAR ch) { 623 if (connection.NeedAdvance(1)) { 624 ok = ok && io.AdvanceRecord(); 625 } 626 ok = ok && io.EmitEncoded(&ch, 1); 627 }}; 628 EmitOne(modes.delim); 629 for (std::size_t j{0}; j < length; ++j) { 630 // Doubled delimiters must be put on the same record 631 // in order to be acceptable as list-directed or NAMELIST 632 // input; however, this requirement is not always possible 633 // when the records have a fixed length, as is the case with 634 // internal output. The standard is silent on what should 635 // happen, and no two extant Fortran implementations do 636 // the same thing when tested with this case. 637 // This runtime splits the doubled delimiters across 638 // two records for lack of a better alternative. 639 if (x[j] == static_cast<CHAR>(modes.delim)) { 640 EmitOne(x[j]); 641 } 642 EmitOne(x[j]); 643 } 644 EmitOne(modes.delim); 645 } else { 646 // Undelimited list-directed output 647 ok = ok && list.EmitLeadingSpaceOrAdvance(io, length > 0 ? 1 : 0, true); 648 std::size_t put{0}; 649 std::size_t oneIfUTF8{connection.useUTF8<CHAR>() ? 1 : length}; 650 while (ok && put < length) { 651 if (std::size_t chunk{std::min<std::size_t>( 652 std::min<std::size_t>(length - put, oneIfUTF8), 653 connection.RemainingSpaceInRecord())}) { 654 ok = io.EmitEncoded(x + put, chunk); 655 put += chunk; 656 } else { 657 ok = io.AdvanceRecord() && io.Emit(" ", 1); 658 } 659 } 660 list.set_lastWasUndelimitedCharacter(true); 661 } 662 return ok; 663 } 664 665 template <typename CHAR> 666 bool EditCharacterOutput(IoStatementState &io, const DataEdit &edit, 667 const CHAR *x, std::size_t length) { 668 int len{static_cast<int>(length)}; 669 int width{edit.width.value_or(len)}; 670 switch (edit.descriptor) { 671 case 'A': 672 break; 673 case 'G': 674 if (width == 0) { 675 width = len; 676 } 677 break; 678 case 'B': 679 return EditBOZOutput<1>(io, edit, 680 reinterpret_cast<const unsigned char *>(x), sizeof(CHAR) * length); 681 case 'O': 682 return EditBOZOutput<3>(io, edit, 683 reinterpret_cast<const unsigned char *>(x), sizeof(CHAR) * length); 684 case 'Z': 685 return EditBOZOutput<4>(io, edit, 686 reinterpret_cast<const unsigned char *>(x), sizeof(CHAR) * length); 687 default: 688 io.GetIoErrorHandler().SignalError(IostatErrorInFormat, 689 "Data edit descriptor '%c' may not be used with a CHARACTER data item", 690 edit.descriptor); 691 return false; 692 } 693 return io.EmitRepeated(' ', std::max(0, width - len)) && 694 io.EmitEncoded(x, std::min(width, len)); 695 } 696 697 template bool EditIntegerOutput<1>( 698 IoStatementState &, const DataEdit &, std::int8_t); 699 template bool EditIntegerOutput<2>( 700 IoStatementState &, const DataEdit &, std::int16_t); 701 template bool EditIntegerOutput<4>( 702 IoStatementState &, const DataEdit &, std::int32_t); 703 template bool EditIntegerOutput<8>( 704 IoStatementState &, const DataEdit &, std::int64_t); 705 template bool EditIntegerOutput<16>( 706 IoStatementState &, const DataEdit &, common::int128_t); 707 708 template class RealOutputEditing<2>; 709 template class RealOutputEditing<3>; 710 template class RealOutputEditing<4>; 711 template class RealOutputEditing<8>; 712 template class RealOutputEditing<10>; 713 // TODO: double/double 714 template class RealOutputEditing<16>; 715 716 template bool ListDirectedCharacterOutput(IoStatementState &, 717 ListDirectedStatementState<Direction::Output> &, const char *, 718 std::size_t chars); 719 template bool ListDirectedCharacterOutput(IoStatementState &, 720 ListDirectedStatementState<Direction::Output> &, const char16_t *, 721 std::size_t chars); 722 template bool ListDirectedCharacterOutput(IoStatementState &, 723 ListDirectedStatementState<Direction::Output> &, const char32_t *, 724 std::size_t chars); 725 726 template bool EditCharacterOutput( 727 IoStatementState &, const DataEdit &, const char *, std::size_t chars); 728 template bool EditCharacterOutput( 729 IoStatementState &, const DataEdit &, const char16_t *, std::size_t chars); 730 template bool EditCharacterOutput( 731 IoStatementState &, const DataEdit &, const char32_t *, std::size_t chars); 732 733 } // namespace Fortran::runtime::io 734