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