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