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 "flang/Common/uint128.h" 11 #include <algorithm> 12 13 namespace Fortran::runtime::io { 14 15 template <typename INT, typename UINT> 16 bool EditIntegerOutput(IoStatementState &io, const DataEdit &edit, INT n) { 17 char buffer[130], *end = &buffer[sizeof buffer], *p = end; 18 bool isNegative{false}; 19 if constexpr (std::is_same_v<INT, UINT>) { 20 isNegative = (n >> (8 * sizeof(INT) - 1)) != 0; 21 } else { 22 isNegative = n < 0; 23 } 24 UINT un{static_cast<UINT>(isNegative ? -n : n)}; 25 int signChars{0}; 26 switch (edit.descriptor) { 27 case DataEdit::ListDirected: 28 case 'G': 29 case 'I': 30 if (isNegative || (edit.modes.editingFlags & signPlus)) { 31 signChars = 1; // '-' or '+' 32 } 33 while (un > 0) { 34 auto quotient{un / 10u}; 35 *--p = '0' + static_cast<int>(un - UINT{10} * quotient); 36 un = quotient; 37 } 38 break; 39 case 'B': 40 for (; un > 0; un >>= 1) { 41 *--p = '0' + (static_cast<int>(un) & 1); 42 } 43 break; 44 case 'O': 45 for (; un > 0; un >>= 3) { 46 *--p = '0' + (static_cast<int>(un) & 7); 47 } 48 break; 49 case 'Z': 50 for (; un > 0; un >>= 4) { 51 int digit = static_cast<int>(un) & 0xf; 52 *--p = digit >= 10 ? 'A' + (digit - 10) : '0' + digit; 53 } 54 break; 55 default: 56 io.GetIoErrorHandler().Crash( 57 "Data edit descriptor '%c' may not be used with an INTEGER data item", 58 edit.descriptor); 59 return false; 60 } 61 62 int digits = end - p; 63 int leadingZeroes{0}; 64 int editWidth{edit.width.value_or(0)}; 65 if (edit.digits && digits <= *edit.digits) { // Iw.m 66 if (*edit.digits == 0 && n == 0) { 67 // Iw.0 with zero value: output field must be blank. For I0.0 68 // and a zero value, emit one blank character. 69 signChars = 0; // in case of SP 70 editWidth = std::max(1, editWidth); 71 } else { 72 leadingZeroes = *edit.digits - digits; 73 } 74 } else if (n == 0) { 75 leadingZeroes = 1; 76 } 77 int subTotal{signChars + leadingZeroes + digits}; 78 int leadingSpaces{std::max(0, editWidth - subTotal)}; 79 if (editWidth > 0 && leadingSpaces + subTotal > editWidth) { 80 return io.EmitRepeated('*', editWidth); 81 } 82 if (edit.IsListDirected()) { 83 int total{std::max(leadingSpaces, 1) + subTotal}; 84 if (io.GetConnectionState().NeedAdvance(static_cast<std::size_t>(total)) && 85 !io.AdvanceRecord()) { 86 return false; 87 } 88 leadingSpaces = 1; 89 } 90 return io.EmitRepeated(' ', leadingSpaces) && 91 io.Emit(n < 0 ? "-" : "+", signChars) && 92 io.EmitRepeated('0', leadingZeroes) && io.Emit(p, digits); 93 } 94 95 // Formats the exponent (see table 13.1 for all the cases) 96 const char *RealOutputEditingBase::FormatExponent( 97 int expo, const DataEdit &edit, int &length) { 98 char *eEnd{&exponent_[sizeof exponent_]}; 99 char *exponent{eEnd}; 100 for (unsigned e{static_cast<unsigned>(std::abs(expo))}; e > 0;) { 101 unsigned quotient{e / 10u}; 102 *--exponent = '0' + e - 10 * quotient; 103 e = quotient; 104 } 105 if (edit.expoDigits) { 106 if (int ed{*edit.expoDigits}) { // Ew.dEe with e > 0 107 while (exponent > exponent_ + 2 /*E+*/ && exponent + ed > eEnd) { 108 *--exponent = '0'; 109 } 110 } else if (exponent == eEnd) { 111 *--exponent = '0'; // Ew.dE0 with zero-valued exponent 112 } 113 } else { // ensure at least two exponent digits 114 while (exponent + 2 > eEnd) { 115 *--exponent = '0'; 116 } 117 } 118 *--exponent = expo < 0 ? '-' : '+'; 119 if (edit.expoDigits || edit.IsListDirected() || exponent + 3 == eEnd) { 120 *--exponent = edit.descriptor == 'D' ? 'D' : 'E'; // not 'G' 121 } 122 length = eEnd - exponent; 123 return exponent; 124 } 125 126 bool RealOutputEditingBase::EmitPrefix( 127 const DataEdit &edit, std::size_t length, std::size_t width) { 128 if (edit.IsListDirected()) { 129 int prefixLength{edit.descriptor == DataEdit::ListDirectedRealPart ? 2 130 : edit.descriptor == DataEdit::ListDirectedImaginaryPart ? 0 131 : 1}; 132 int suffixLength{edit.descriptor == DataEdit::ListDirectedRealPart || 133 edit.descriptor == DataEdit::ListDirectedImaginaryPart 134 ? 1 135 : 0}; 136 length += prefixLength + suffixLength; 137 ConnectionState &connection{io_.GetConnectionState()}; 138 return (!connection.NeedAdvance(length) || io_.AdvanceRecord()) && 139 io_.Emit(" (", prefixLength); 140 } else if (width > length) { 141 return io_.EmitRepeated(' ', width - length); 142 } else { 143 return true; 144 } 145 } 146 147 bool RealOutputEditingBase::EmitSuffix(const DataEdit &edit) { 148 if (edit.descriptor == DataEdit::ListDirectedRealPart) { 149 return io_.Emit(edit.modes.editingFlags & decimalComma ? ";" : ",", 1); 150 } else if (edit.descriptor == DataEdit::ListDirectedImaginaryPart) { 151 return io_.Emit(")", 1); 152 } else { 153 return true; 154 } 155 } 156 157 template <int binaryPrecision> 158 decimal::ConversionToDecimalResult RealOutputEditing<binaryPrecision>::Convert( 159 int significantDigits, const DataEdit &edit, int flags) { 160 if (edit.modes.editingFlags & signPlus) { 161 flags |= decimal::AlwaysSign; 162 } 163 auto converted{decimal::ConvertToDecimal<binaryPrecision>(buffer_, 164 sizeof buffer_, static_cast<enum decimal::DecimalConversionFlags>(flags), 165 significantDigits, edit.modes.round, x_)}; 166 if (!converted.str) { // overflow 167 io_.GetIoErrorHandler().Crash( 168 "RealOutputEditing::Convert : buffer size %zd was insufficient", 169 sizeof buffer_); 170 } 171 return converted; 172 } 173 174 // 13.7.2.3.3 in F'2018 175 template <int binaryPrecision> 176 bool RealOutputEditing<binaryPrecision>::EditEorDOutput(const DataEdit &edit) { 177 int editDigits{edit.digits.value_or(0)}; // 'd' field 178 int editWidth{edit.width.value_or(0)}; // 'w' field 179 int significantDigits{editDigits}; 180 int flags{0}; 181 if (editWidth == 0) { // "the processor selects the field width" 182 if (edit.digits.has_value()) { // E0.d 183 editWidth = editDigits + 6; // -.666E+ee 184 } else { // E0 185 flags |= decimal::Minimize; 186 significantDigits = 187 sizeof buffer_ - 5; // sign, NUL, + 3 extra for EN scaling 188 } 189 } 190 bool isEN{edit.variation == 'N'}; 191 bool isES{edit.variation == 'S'}; 192 int scale{isEN || isES ? 1 : edit.modes.scale}; // 'kP' value 193 int zeroesAfterPoint{0}; 194 if (scale < 0) { 195 zeroesAfterPoint = -scale; 196 significantDigits = std::max(0, significantDigits - zeroesAfterPoint); 197 } else if (scale > 0) { 198 ++significantDigits; 199 scale = std::min(scale, significantDigits + 1); 200 } 201 // In EN editing, multiple attempts may be necessary, so it's in a loop. 202 while (true) { 203 decimal::ConversionToDecimalResult converted{ 204 Convert(significantDigits, edit, flags)}; 205 if (IsInfOrNaN(converted)) { 206 return EmitPrefix(edit, converted.length, editWidth) && 207 io_.Emit(converted.str, converted.length) && EmitSuffix(edit); 208 } 209 if (!IsZero()) { 210 converted.decimalExponent -= scale; 211 } 212 if (isEN && scale < 3 && (converted.decimalExponent % 3) != 0) { 213 // EN mode: boost the scale and significant digits, try again; need 214 // an effective exponent field that's a multiple of three. 215 ++scale; 216 ++significantDigits; 217 continue; 218 } 219 // Format the exponent (see table 13.1 for all the cases) 220 int expoLength{0}; 221 const char *exponent{ 222 FormatExponent(converted.decimalExponent, edit, expoLength)}; 223 int signLength{*converted.str == '-' || *converted.str == '+' ? 1 : 0}; 224 int convertedDigits{static_cast<int>(converted.length) - signLength}; 225 int zeroesBeforePoint{std::max(0, scale - convertedDigits)}; 226 int digitsBeforePoint{std::max(0, scale - zeroesBeforePoint)}; 227 int digitsAfterPoint{convertedDigits - digitsBeforePoint}; 228 int trailingZeroes{flags & decimal::Minimize 229 ? 0 230 : std::max(0, 231 significantDigits - (convertedDigits + zeroesBeforePoint))}; 232 int totalLength{signLength + digitsBeforePoint + zeroesBeforePoint + 233 1 /*'.'*/ + zeroesAfterPoint + digitsAfterPoint + trailingZeroes + 234 expoLength}; 235 int width{editWidth > 0 ? editWidth : totalLength}; 236 if (totalLength > width) { 237 return io_.EmitRepeated('*', width); 238 } 239 if (totalLength < width && digitsBeforePoint == 0 && 240 zeroesBeforePoint == 0) { 241 zeroesBeforePoint = 1; 242 ++totalLength; 243 } 244 return EmitPrefix(edit, totalLength, width) && 245 io_.Emit(converted.str, signLength + digitsBeforePoint) && 246 io_.EmitRepeated('0', zeroesBeforePoint) && 247 io_.Emit(edit.modes.editingFlags & decimalComma ? "," : ".", 1) && 248 io_.EmitRepeated('0', zeroesAfterPoint) && 249 io_.Emit( 250 converted.str + signLength + digitsBeforePoint, digitsAfterPoint) && 251 io_.EmitRepeated('0', trailingZeroes) && 252 io_.Emit(exponent, expoLength) && EmitSuffix(edit); 253 } 254 } 255 256 // 13.7.2.3.2 in F'2018 257 template <int binaryPrecision> 258 bool RealOutputEditing<binaryPrecision>::EditFOutput(const DataEdit &edit) { 259 int fracDigits{edit.digits.value_or(0)}; // 'd' field 260 const int editWidth{edit.width.value_or(0)}; // 'w' field 261 int flags{0}; 262 if (editWidth == 0) { // "the processor selects the field width" 263 if (!edit.digits.has_value()) { // F0 264 flags |= decimal::Minimize; 265 fracDigits = sizeof buffer_ - 2; // sign & NUL 266 } 267 } 268 // Multiple conversions may be needed to get the right number of 269 // effective rounded fractional digits. 270 int extraDigits{0}; 271 bool canIncrease{true}; 272 while (true) { 273 decimal::ConversionToDecimalResult converted{ 274 Convert(extraDigits + fracDigits, edit, flags)}; 275 if (IsInfOrNaN(converted)) { 276 return EmitPrefix(edit, converted.length, editWidth) && 277 io_.Emit(converted.str, converted.length) && EmitSuffix(edit); 278 } 279 int scale{IsZero() ? 1 : edit.modes.scale}; // kP 280 int expo{converted.decimalExponent + scale}; 281 if (expo > extraDigits && extraDigits >= 0 && canIncrease) { 282 extraDigits = expo; 283 if (!edit.digits.has_value()) { // F0 284 fracDigits = sizeof buffer_ - extraDigits - 2; // sign & NUL 285 } 286 canIncrease = false; // only once 287 continue; 288 } else if (expo < extraDigits && extraDigits > -fracDigits) { 289 extraDigits = std::max(expo, -fracDigits); 290 continue; 291 } 292 int signLength{*converted.str == '-' || *converted.str == '+' ? 1 : 0}; 293 int convertedDigits{static_cast<int>(converted.length) - signLength}; 294 int digitsBeforePoint{std::max(0, std::min(expo, convertedDigits))}; 295 int zeroesBeforePoint{std::max(0, expo - digitsBeforePoint)}; 296 int zeroesAfterPoint{std::min(fracDigits, std::max(0, -expo))}; 297 int digitsAfterPoint{convertedDigits - digitsBeforePoint}; 298 int trailingZeroes{flags & decimal::Minimize 299 ? 0 300 : std::max(0, fracDigits - (zeroesAfterPoint + digitsAfterPoint))}; 301 if (digitsBeforePoint + zeroesBeforePoint + zeroesAfterPoint + 302 digitsAfterPoint + trailingZeroes == 303 0) { 304 zeroesBeforePoint = 1; // "." -> "0." 305 } 306 int totalLength{signLength + digitsBeforePoint + zeroesBeforePoint + 307 1 /*'.'*/ + zeroesAfterPoint + digitsAfterPoint + trailingZeroes}; 308 int width{editWidth > 0 ? editWidth : totalLength}; 309 if (totalLength > width) { 310 return io_.EmitRepeated('*', width); 311 } 312 if (totalLength < width && digitsBeforePoint + zeroesBeforePoint == 0) { 313 zeroesBeforePoint = 1; 314 ++totalLength; 315 } 316 return EmitPrefix(edit, totalLength, width) && 317 io_.Emit(converted.str, signLength + digitsBeforePoint) && 318 io_.EmitRepeated('0', zeroesBeforePoint) && 319 io_.Emit(edit.modes.editingFlags & decimalComma ? "," : ".", 1) && 320 io_.EmitRepeated('0', zeroesAfterPoint) && 321 io_.Emit( 322 converted.str + signLength + digitsBeforePoint, digitsAfterPoint) && 323 io_.EmitRepeated('0', trailingZeroes) && 324 io_.EmitRepeated(' ', trailingBlanks_) && EmitSuffix(edit); 325 } 326 } 327 328 // 13.7.5.2.3 in F'2018 329 template <int binaryPrecision> 330 DataEdit RealOutputEditing<binaryPrecision>::EditForGOutput(DataEdit edit) { 331 edit.descriptor = 'E'; 332 int significantDigits{ 333 edit.digits.value_or(BinaryFloatingPoint::decimalPrecision)}; // 'd' 334 if (!edit.width.has_value() || (*edit.width > 0 && significantDigits == 0)) { 335 return edit; // Gw.0 -> Ew.0 for w > 0 336 } 337 decimal::ConversionToDecimalResult converted{ 338 Convert(significantDigits, edit)}; 339 if (IsInfOrNaN(converted)) { 340 return edit; 341 } 342 int expo{IsZero() ? 1 : converted.decimalExponent}; // 's' 343 if (expo < 0 || expo > significantDigits) { 344 return edit; // Ew.d 345 } 346 edit.descriptor = 'F'; 347 edit.modes.scale = 0; // kP is ignored for G when no exponent field 348 trailingBlanks_ = 0; 349 int editWidth{edit.width.value_or(0)}; 350 if (editWidth > 0) { 351 int expoDigits{edit.expoDigits.value_or(0)}; 352 trailingBlanks_ = expoDigits > 0 ? expoDigits + 2 : 4; // 'n' 353 *edit.width = std::max(0, editWidth - trailingBlanks_); 354 } 355 if (edit.digits.has_value()) { 356 *edit.digits = std::max(0, *edit.digits - expo); 357 } 358 return edit; 359 } 360 361 // 13.10.4 in F'2018 362 template <int binaryPrecision> 363 bool RealOutputEditing<binaryPrecision>::EditListDirectedOutput( 364 const DataEdit &edit) { 365 decimal::ConversionToDecimalResult converted{Convert(1, edit)}; 366 if (IsInfOrNaN(converted)) { 367 return EditEorDOutput(edit); 368 } 369 int expo{converted.decimalExponent}; 370 if (expo < 0 || expo > BinaryFloatingPoint::decimalPrecision) { 371 DataEdit copy{edit}; 372 copy.modes.scale = 1; // 1P 373 return EditEorDOutput(copy); 374 } 375 return EditFOutput(edit); 376 } 377 378 // 13.7.5.2.6 in F'2018 379 template <int binaryPrecision> 380 bool RealOutputEditing<binaryPrecision>::EditEXOutput(const DataEdit &) { 381 io_.GetIoErrorHandler().Crash( 382 "EX output editing is not yet implemented"); // TODO 383 } 384 385 template <int binaryPrecision> 386 bool RealOutputEditing<binaryPrecision>::Edit(const DataEdit &edit) { 387 switch (edit.descriptor) { 388 case 'D': 389 return EditEorDOutput(edit); 390 case 'E': 391 if (edit.variation == 'X') { 392 return EditEXOutput(edit); 393 } else { 394 return EditEorDOutput(edit); 395 } 396 case 'F': 397 return EditFOutput(edit); 398 case 'B': 399 case 'O': 400 case 'Z': 401 return EditIntegerOutput(io_, edit, 402 decimal::BinaryFloatingPointNumber<binaryPrecision>{x_}.raw()); 403 case 'G': 404 return Edit(EditForGOutput(edit)); 405 default: 406 if (edit.IsListDirected()) { 407 return EditListDirectedOutput(edit); 408 } 409 io_.GetIoErrorHandler().SignalError(IostatErrorInFormat, 410 "Data edit descriptor '%c' may not be used with a REAL data item", 411 edit.descriptor); 412 return false; 413 } 414 return false; 415 } 416 417 bool ListDirectedLogicalOutput(IoStatementState &io, 418 ListDirectedStatementState<Direction::Output> &list, bool truth) { 419 return list.EmitLeadingSpaceOrAdvance(io) && io.Emit(truth ? "T" : "F", 1); 420 } 421 422 bool EditLogicalOutput(IoStatementState &io, const DataEdit &edit, bool truth) { 423 switch (edit.descriptor) { 424 case 'L': 425 case 'G': 426 return io.EmitRepeated(' ', std::max(0, edit.width.value_or(1) - 1)) && 427 io.Emit(truth ? "T" : "F", 1); 428 default: 429 io.GetIoErrorHandler().SignalError(IostatErrorInFormat, 430 "Data edit descriptor '%c' may not be used with a LOGICAL data item", 431 edit.descriptor); 432 return false; 433 } 434 } 435 436 bool ListDirectedDefaultCharacterOutput(IoStatementState &io, 437 ListDirectedStatementState<Direction::Output> &list, const char *x, 438 std::size_t length) { 439 bool ok{true}; 440 MutableModes &modes{io.mutableModes()}; 441 ConnectionState &connection{io.GetConnectionState()}; 442 if (modes.delim) { 443 ok = ok && list.EmitLeadingSpaceOrAdvance(io); 444 // Value is delimited with ' or " marks, and interior 445 // instances of that character are doubled. 446 ok = ok && io.Emit(&modes.delim, 1); 447 auto EmitOne{[&](char ch) { 448 if (connection.NeedAdvance(1)) { 449 ok = ok && io.AdvanceRecord(); 450 } 451 ok = ok && io.Emit(&ch, 1); 452 }}; 453 for (std::size_t j{0}; j < length; ++j) { 454 // Doubled delimiters must be put on the same record 455 // in order to be acceptable as list-directed or NAMELIST 456 // input; however, this requirement is not always possible 457 // when the records have a fixed length, as is the case with 458 // internal output. The standard is silent on what should 459 // happen, and no two extant Fortran implementations do 460 // the same thing when tested with this case. 461 // This runtime splits the doubled delimiters across 462 // two records for lack of a better alternative. 463 if (x[j] == modes.delim) { 464 EmitOne(x[j]); 465 } 466 EmitOne(x[j]); 467 } 468 EmitOne(modes.delim); 469 } else { 470 // Undelimited list-directed output 471 ok = ok && 472 list.EmitLeadingSpaceOrAdvance( 473 io, length > 0 && !list.lastWasUndelimitedCharacter()); 474 std::size_t put{0}; 475 while (ok && put < length) { 476 auto chunk{std::min(length - put, connection.RemainingSpaceInRecord())}; 477 ok = ok && io.Emit(x + put, chunk); 478 put += chunk; 479 if (put < length) { 480 ok = ok && io.AdvanceRecord() && io.Emit(" ", 1); 481 } 482 } 483 list.set_lastWasUndelimitedCharacter(true); 484 } 485 return ok; 486 } 487 488 bool EditDefaultCharacterOutput(IoStatementState &io, const DataEdit &edit, 489 const char *x, std::size_t length) { 490 switch (edit.descriptor) { 491 case 'A': 492 case 'G': 493 break; 494 default: 495 io.GetIoErrorHandler().SignalError(IostatErrorInFormat, 496 "Data edit descriptor '%c' may not be used with a CHARACTER data item", 497 edit.descriptor); 498 return false; 499 } 500 int len{static_cast<int>(length)}; 501 int width{edit.width.value_or(len)}; 502 return io.EmitRepeated(' ', std::max(0, width - len)) && 503 io.Emit(x, std::min(width, len)); 504 } 505 506 template bool EditIntegerOutput<std::int64_t, std::uint64_t>( 507 IoStatementState &, const DataEdit &, std::int64_t); 508 template bool EditIntegerOutput<common::uint128_t, common::uint128_t>( 509 IoStatementState &, const DataEdit &, common::uint128_t); 510 511 template class RealOutputEditing<2>; 512 template class RealOutputEditing<3>; 513 template class RealOutputEditing<4>; 514 template class RealOutputEditing<8>; 515 template class RealOutputEditing<10>; 516 // TODO: double/double 517 template class RealOutputEditing<16>; 518 } // namespace Fortran::runtime::io 519