1 //===-- runtime/edit-output.cpp -------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "edit-output.h"
11 #include "flang/Common/uint128.h"
14 namespace Fortran::runtime::io
{
17 bool EditIntegerOutput(IoStatementState
&io
, const DataEdit
&edit
,
18 common::HostSignedIntType
<8 * KIND
> n
) {
19 char buffer
[130], *end
{&buffer
[sizeof buffer
]}, *p
{end
};
20 bool isNegative
{n
< 0};
21 using Unsigned
= common::HostUnsignedIntType
<8 * KIND
>;
22 Unsigned un
{static_cast<Unsigned
>(n
)};
24 switch (edit
.descriptor
) {
25 case DataEdit::ListDirected
:
31 if (isNegative
|| (edit
.modes
.editingFlags
& signPlus
)) {
32 signChars
= 1; // '-' or '+'
35 auto quotient
{un
/ 10u};
36 *--p
= '0' + static_cast<int>(un
- Unsigned
{10} * quotient
);
41 for (; un
> 0; un
>>= 1) {
42 *--p
= '0' + (static_cast<int>(un
) & 1);
46 for (; un
> 0; un
>>= 3) {
47 *--p
= '0' + (static_cast<int>(un
) & 7);
51 for (; un
> 0; un
>>= 4) {
52 int digit
= static_cast<int>(un
) & 0xf;
53 *--p
= digit
>= 10 ? 'A' + (digit
- 10) : '0' + digit
;
56 case 'A': // legacy extension
57 return EditCharacterOutput(
58 io
, edit
, reinterpret_cast<char *>(&n
), sizeof n
);
60 io
.GetIoErrorHandler().SignalError(IostatErrorInFormat
,
61 "Data edit descriptor '%c' may not be used with an INTEGER data item",
68 int editWidth
{edit
.width
.value_or(0)};
69 if (edit
.digits
&& digits
<= *edit
.digits
) { // Iw.m
70 if (*edit
.digits
== 0 && n
== 0) {
71 // Iw.0 with zero value: output field must be blank. For I0.0
72 // and a zero value, emit one blank character.
73 signChars
= 0; // in case of SP
74 editWidth
= std::max(1, editWidth
);
76 leadingZeroes
= *edit
.digits
- digits
;
81 int subTotal
{signChars
+ leadingZeroes
+ digits
};
82 int leadingSpaces
{std::max(0, editWidth
- subTotal
)};
83 if (editWidth
> 0 && leadingSpaces
+ subTotal
> editWidth
) {
84 return io
.EmitRepeated('*', editWidth
);
86 if (edit
.IsListDirected()) {
87 int total
{std::max(leadingSpaces
, 1) + subTotal
};
88 if (io
.GetConnectionState().NeedAdvance(static_cast<std::size_t>(total
)) &&
89 !io
.AdvanceRecord()) {
94 return io
.EmitRepeated(' ', leadingSpaces
) &&
95 io
.Emit(n
< 0 ? "-" : "+", signChars
) &&
96 io
.EmitRepeated('0', leadingZeroes
) && io
.Emit(p
, digits
);
99 // Formats the exponent (see table 13.1 for all the cases)
100 const char *RealOutputEditingBase::FormatExponent(
101 int expo
, const DataEdit
&edit
, int &length
) {
102 char *eEnd
{&exponent_
[sizeof exponent_
]};
103 char *exponent
{eEnd
};
104 for (unsigned e
{static_cast<unsigned>(std::abs(expo
))}; e
> 0;) {
105 unsigned quotient
{e
/ 10u};
106 *--exponent
= '0' + e
- 10 * quotient
;
109 if (edit
.expoDigits
) {
110 if (int ed
{*edit
.expoDigits
}) { // Ew.dEe with e > 0
111 while (exponent
> exponent_
+ 2 /*E+*/ && exponent
+ ed
> eEnd
) {
114 } else if (exponent
== eEnd
) {
115 *--exponent
= '0'; // Ew.dE0 with zero-valued exponent
117 } else { // ensure at least two exponent digits
118 while (exponent
+ 2 > eEnd
) {
122 *--exponent
= expo
< 0 ? '-' : '+';
123 if (edit
.expoDigits
|| edit
.IsListDirected() || exponent
+ 3 == eEnd
) {
124 *--exponent
= edit
.descriptor
== 'D' ? 'D' : 'E'; // not 'G'
126 length
= eEnd
- exponent
;
130 bool RealOutputEditingBase::EmitPrefix(
131 const DataEdit
&edit
, std::size_t length
, std::size_t width
) {
132 if (edit
.IsListDirected()) {
133 int prefixLength
{edit
.descriptor
== DataEdit::ListDirectedRealPart
? 2
134 : edit
.descriptor
== DataEdit::ListDirectedImaginaryPart
? 0
136 int suffixLength
{edit
.descriptor
== DataEdit::ListDirectedRealPart
||
137 edit
.descriptor
== DataEdit::ListDirectedImaginaryPart
140 length
+= prefixLength
+ suffixLength
;
141 ConnectionState
&connection
{io_
.GetConnectionState()};
142 return (!connection
.NeedAdvance(length
) || io_
.AdvanceRecord()) &&
143 io_
.Emit(" (", prefixLength
);
144 } else if (width
> length
) {
145 return io_
.EmitRepeated(' ', width
- length
);
151 bool RealOutputEditingBase::EmitSuffix(const DataEdit
&edit
) {
152 if (edit
.descriptor
== DataEdit::ListDirectedRealPart
) {
153 return io_
.Emit(edit
.modes
.editingFlags
& decimalComma
? ";" : ",", 1);
154 } else if (edit
.descriptor
== DataEdit::ListDirectedImaginaryPart
) {
155 return io_
.Emit(")", 1);
161 template <int binaryPrecision
>
162 decimal::ConversionToDecimalResult RealOutputEditing
<binaryPrecision
>::Convert(
163 int significantDigits
, enum decimal::FortranRounding rounding
, int flags
) {
164 auto converted
{decimal::ConvertToDecimal
<binaryPrecision
>(buffer_
,
165 sizeof buffer_
, static_cast<enum decimal::DecimalConversionFlags
>(flags
),
166 significantDigits
, rounding
, x_
)};
167 if (!converted
.str
) { // overflow
168 io_
.GetIoErrorHandler().Crash(
169 "RealOutputEditing::Convert : buffer size %zd was insufficient",
175 // 13.7.2.3.3 in F'2018
176 template <int binaryPrecision
>
177 bool RealOutputEditing
<binaryPrecision
>::EditEorDOutput(const DataEdit
&edit
) {
178 int editDigits
{edit
.digits
.value_or(0)}; // 'd' field
179 int editWidth
{edit
.width
.value_or(0)}; // 'w' field
180 int significantDigits
{editDigits
};
182 if (edit
.modes
.editingFlags
& signPlus
) {
183 flags
|= decimal::AlwaysSign
;
185 if (editWidth
== 0) { // "the processor selects the field width"
186 if (edit
.digits
.has_value()) { // E0.d
187 editWidth
= editDigits
+ 6; // -.666E+ee
189 flags
|= decimal::Minimize
;
191 sizeof buffer_
- 5; // sign, NUL, + 3 extra for EN scaling
194 bool isEN
{edit
.variation
== 'N'};
195 bool isES
{edit
.variation
== 'S'};
196 int scale
{isEN
|| isES
? 1 : edit
.modes
.scale
}; // 'kP' value
197 int zeroesAfterPoint
{0};
199 zeroesAfterPoint
= -scale
;
200 significantDigits
= std::max(0, significantDigits
- zeroesAfterPoint
);
201 } else if (scale
> 0) {
203 scale
= std::min(scale
, significantDigits
+ 1);
205 // In EN editing, multiple attempts may be necessary, so it's in a loop.
207 decimal::ConversionToDecimalResult converted
{
208 Convert(significantDigits
, edit
.modes
.round
, flags
)};
209 if (IsInfOrNaN(converted
)) {
210 return EmitPrefix(edit
, converted
.length
, editWidth
) &&
211 io_
.Emit(converted
.str
, converted
.length
) && EmitSuffix(edit
);
214 converted
.decimalExponent
-= scale
;
216 if (isEN
&& scale
< 3 && (converted
.decimalExponent
% 3) != 0) {
217 // EN mode: boost the scale and significant digits, try again; need
218 // an effective exponent field that's a multiple of three.
223 // Format the exponent (see table 13.1 for all the cases)
225 const char *exponent
{
226 FormatExponent(converted
.decimalExponent
, edit
, expoLength
)};
227 int signLength
{*converted
.str
== '-' || *converted
.str
== '+' ? 1 : 0};
228 int convertedDigits
{static_cast<int>(converted
.length
) - signLength
};
229 int zeroesBeforePoint
{std::max(0, scale
- convertedDigits
)};
230 int digitsBeforePoint
{std::max(0, scale
- zeroesBeforePoint
)};
231 int digitsAfterPoint
{convertedDigits
- digitsBeforePoint
};
232 int trailingZeroes
{flags
& decimal::Minimize
235 significantDigits
- (convertedDigits
+ zeroesBeforePoint
))};
236 int totalLength
{signLength
+ digitsBeforePoint
+ zeroesBeforePoint
+
237 1 /*'.'*/ + zeroesAfterPoint
+ digitsAfterPoint
+ trailingZeroes
+
239 int width
{editWidth
> 0 ? editWidth
: totalLength
};
240 if (totalLength
> width
) {
241 return io_
.EmitRepeated('*', width
);
243 if (totalLength
< width
&& digitsBeforePoint
== 0 &&
244 zeroesBeforePoint
== 0) {
245 zeroesBeforePoint
= 1;
248 return EmitPrefix(edit
, totalLength
, width
) &&
249 io_
.Emit(converted
.str
, signLength
+ digitsBeforePoint
) &&
250 io_
.EmitRepeated('0', zeroesBeforePoint
) &&
251 io_
.Emit(edit
.modes
.editingFlags
& decimalComma
? "," : ".", 1) &&
252 io_
.EmitRepeated('0', zeroesAfterPoint
) &&
254 converted
.str
+ signLength
+ digitsBeforePoint
, digitsAfterPoint
) &&
255 io_
.EmitRepeated('0', trailingZeroes
) &&
256 io_
.Emit(exponent
, expoLength
) && EmitSuffix(edit
);
260 // 13.7.2.3.2 in F'2018
261 template <int binaryPrecision
>
262 bool RealOutputEditing
<binaryPrecision
>::EditFOutput(const DataEdit
&edit
) {
263 int fracDigits
{edit
.digits
.value_or(0)}; // 'd' field
264 const int editWidth
{edit
.width
.value_or(0)}; // 'w' field
265 enum decimal::FortranRounding rounding
{edit
.modes
.round
};
267 if (edit
.modes
.editingFlags
& signPlus
) {
268 flags
|= decimal::AlwaysSign
;
270 if (editWidth
== 0) { // "the processor selects the field width"
271 if (!edit
.digits
.has_value()) { // F0
272 flags
|= decimal::Minimize
;
273 fracDigits
= sizeof buffer_
- 2; // sign & NUL
276 // Multiple conversions may be needed to get the right number of
277 // effective rounded fractional digits.
279 bool canIncrease
{true};
281 decimal::ConversionToDecimalResult converted
{
282 Convert(extraDigits
+ fracDigits
, rounding
, flags
)};
283 if (IsInfOrNaN(converted
)) {
284 return EmitPrefix(edit
, converted
.length
, editWidth
) &&
285 io_
.Emit(converted
.str
, converted
.length
) && EmitSuffix(edit
);
287 int expo
{converted
.decimalExponent
+ edit
.modes
.scale
/*kP*/};
288 int signLength
{*converted
.str
== '-' || *converted
.str
== '+' ? 1 : 0};
289 int convertedDigits
{static_cast<int>(converted
.length
) - signLength
};
290 if (IsZero()) { // don't treat converted "0" as significant digit
295 if (expo
> extraDigits
&& extraDigits
>= 0 && canIncrease
) {
297 if (!edit
.digits
.has_value()) { // F0
298 fracDigits
= sizeof buffer_
- extraDigits
- 2; // sign & NUL
300 canIncrease
= false; // only once
302 } else if (expo
== -fracDigits
&& convertedDigits
> 0) {
303 if (rounding
!= decimal::FortranRounding::RoundToZero
) {
304 // Convert again without rounding so that we can round here
305 rounding
= decimal::FortranRounding::RoundToZero
;
307 } else if (converted
.str
[signLength
] >= '5') {
308 // Value rounds up to a scaled 1 (e.g., 0.06 for F5.1 -> 0.1)
313 // Value rounds down to zero
317 } else if (expo
< extraDigits
&& extraDigits
> -fracDigits
) {
318 extraDigits
= std::max(expo
, -fracDigits
);
321 int digitsBeforePoint
{std::max(0, std::min(expo
, convertedDigits
))};
322 int zeroesBeforePoint
{std::max(0, expo
- digitsBeforePoint
)};
323 int zeroesAfterPoint
{std::min(fracDigits
, std::max(0, -expo
))};
324 int digitsAfterPoint
{convertedDigits
- digitsBeforePoint
};
325 int trailingZeroes
{flags
& decimal::Minimize
329 (zeroesAfterPoint
+ digitsAfterPoint
+ trailingOnes
))};
330 if (digitsBeforePoint
+ zeroesBeforePoint
+ zeroesAfterPoint
+
331 digitsAfterPoint
+ trailingOnes
+ trailingZeroes
==
333 zeroesBeforePoint
= 1; // "." -> "0."
335 int totalLength
{signLength
+ digitsBeforePoint
+ zeroesBeforePoint
+
336 1 /*'.'*/ + zeroesAfterPoint
+ digitsAfterPoint
+ trailingOnes
+
338 int width
{editWidth
> 0 ? editWidth
: totalLength
};
339 if (totalLength
> width
) {
340 return io_
.EmitRepeated('*', width
);
342 if (totalLength
< width
&& digitsBeforePoint
+ zeroesBeforePoint
== 0) {
343 zeroesBeforePoint
= 1;
346 return EmitPrefix(edit
, totalLength
, width
) &&
347 io_
.Emit(converted
.str
, signLength
+ digitsBeforePoint
) &&
348 io_
.EmitRepeated('0', zeroesBeforePoint
) &&
349 io_
.Emit(edit
.modes
.editingFlags
& decimalComma
? "," : ".", 1) &&
350 io_
.EmitRepeated('0', zeroesAfterPoint
) &&
352 converted
.str
+ signLength
+ digitsBeforePoint
, digitsAfterPoint
) &&
353 io_
.EmitRepeated('1', trailingOnes
) &&
354 io_
.EmitRepeated('0', trailingZeroes
) &&
355 io_
.EmitRepeated(' ', trailingBlanks_
) && EmitSuffix(edit
);
359 // 13.7.5.2.3 in F'2018
360 template <int binaryPrecision
>
361 DataEdit RealOutputEditing
<binaryPrecision
>::EditForGOutput(DataEdit edit
) {
362 edit
.descriptor
= 'E';
363 int significantDigits
{
364 edit
.digits
.value_or(BinaryFloatingPoint::decimalPrecision
)}; // 'd'
365 if (!edit
.width
.has_value() || (*edit
.width
> 0 && significantDigits
== 0)) {
366 return edit
; // Gw.0 -> Ew.0 for w > 0
369 if (edit
.modes
.editingFlags
& signPlus
) {
370 flags
|= decimal::AlwaysSign
;
372 decimal::ConversionToDecimalResult converted
{
373 Convert(significantDigits
, edit
.modes
.round
, flags
)};
374 if (IsInfOrNaN(converted
)) {
377 int expo
{IsZero() ? 1 : converted
.decimalExponent
}; // 's'
378 if (expo
< 0 || expo
> significantDigits
) {
381 edit
.descriptor
= 'F';
382 edit
.modes
.scale
= 0; // kP is ignored for G when no exponent field
384 int editWidth
{edit
.width
.value_or(0)};
386 int expoDigits
{edit
.expoDigits
.value_or(0)};
387 trailingBlanks_
= expoDigits
> 0 ? expoDigits
+ 2 : 4; // 'n'
388 *edit
.width
= std::max(0, editWidth
- trailingBlanks_
);
390 if (edit
.digits
.has_value()) {
391 *edit
.digits
= std::max(0, *edit
.digits
- expo
);
397 template <int binaryPrecision
>
398 bool RealOutputEditing
<binaryPrecision
>::EditListDirectedOutput(
399 const DataEdit
&edit
) {
400 decimal::ConversionToDecimalResult converted
{Convert(1, edit
.modes
.round
)};
401 if (IsInfOrNaN(converted
)) {
402 return EditEorDOutput(edit
);
404 int expo
{converted
.decimalExponent
};
405 if (expo
< 0 || expo
> BinaryFloatingPoint::decimalPrecision
) {
407 copy
.modes
.scale
= 1; // 1P
408 return EditEorDOutput(copy
);
410 return EditFOutput(edit
);
413 // 13.7.5.2.6 in F'2018
414 template <int binaryPrecision
>
415 bool RealOutputEditing
<binaryPrecision
>::EditEXOutput(const DataEdit
&) {
416 io_
.GetIoErrorHandler().Crash(
417 "not yet implemented: EX output editing"); // TODO
420 template <int KIND
> bool RealOutputEditing
<KIND
>::Edit(const DataEdit
&edit
) {
421 switch (edit
.descriptor
) {
423 return EditEorDOutput(edit
);
425 if (edit
.variation
== 'X') {
426 return EditEXOutput(edit
);
428 return EditEorDOutput(edit
);
431 return EditFOutput(edit
);
435 return EditIntegerOutput
<KIND
>(io_
, edit
,
436 static_cast<common::HostSignedIntType
<8 * KIND
>>(
437 decimal::BinaryFloatingPointNumber
<binaryPrecision
>{x_
}.raw()));
439 return Edit(EditForGOutput(edit
));
440 case 'A': // legacy extension
441 return EditCharacterOutput(
442 io_
, edit
, reinterpret_cast<char *>(&x_
), sizeof x_
);
444 if (edit
.IsListDirected()) {
445 return EditListDirectedOutput(edit
);
447 io_
.GetIoErrorHandler().SignalError(IostatErrorInFormat
,
448 "Data edit descriptor '%c' may not be used with a REAL data item",
455 bool ListDirectedLogicalOutput(IoStatementState
&io
,
456 ListDirectedStatementState
<Direction::Output
> &list
, bool truth
) {
457 return list
.EmitLeadingSpaceOrAdvance(io
) && io
.Emit(truth
? "T" : "F", 1);
460 bool EditLogicalOutput(IoStatementState
&io
, const DataEdit
&edit
, bool truth
) {
461 switch (edit
.descriptor
) {
464 return io
.EmitRepeated(' ', std::max(0, edit
.width
.value_or(1) - 1)) &&
465 io
.Emit(truth
? "T" : "F", 1);
467 io
.GetIoErrorHandler().SignalError(IostatErrorInFormat
,
468 "Data edit descriptor '%c' may not be used with a LOGICAL data item",
474 template <typename CHAR
>
475 bool ListDirectedCharacterOutput(IoStatementState
&io
,
476 ListDirectedStatementState
<Direction::Output
> &list
, const CHAR
*x
,
477 std::size_t length
) {
479 MutableModes
&modes
{io
.mutableModes()};
480 ConnectionState
&connection
{io
.GetConnectionState()};
482 ok
= ok
&& list
.EmitLeadingSpaceOrAdvance(io
);
483 // Value is delimited with ' or " marks, and interior
484 // instances of that character are doubled.
485 auto EmitOne
{[&](CHAR ch
) {
486 if (connection
.NeedAdvance(1)) {
487 ok
= ok
&& io
.AdvanceRecord();
489 ok
= ok
&& io
.EmitEncoded(&ch
, 1);
491 EmitOne(modes
.delim
);
492 for (std::size_t j
{0}; j
< length
; ++j
) {
493 // Doubled delimiters must be put on the same record
494 // in order to be acceptable as list-directed or NAMELIST
495 // input; however, this requirement is not always possible
496 // when the records have a fixed length, as is the case with
497 // internal output. The standard is silent on what should
498 // happen, and no two extant Fortran implementations do
499 // the same thing when tested with this case.
500 // This runtime splits the doubled delimiters across
501 // two records for lack of a better alternative.
502 if (x
[j
] == static_cast<CHAR
>(modes
.delim
)) {
507 EmitOne(modes
.delim
);
509 // Undelimited list-directed output
510 ok
= ok
&& list
.EmitLeadingSpaceOrAdvance(io
, length
> 0 ? 1 : 0, true);
512 std::size_t oneIfUTF8
{connection
.useUTF8
<CHAR
>() ? 1 : length
};
513 while (ok
&& put
< length
) {
514 if (std::size_t chunk
{std::min
<std::size_t>(
515 std::min
<std::size_t>(length
- put
, oneIfUTF8
),
516 connection
.RemainingSpaceInRecord())}) {
517 ok
= io
.EmitEncoded(x
+ put
, chunk
);
520 ok
= io
.AdvanceRecord() && io
.Emit(" ", 1);
523 list
.set_lastWasUndelimitedCharacter(true);
528 template <typename CHAR
>
529 bool EditCharacterOutput(IoStatementState
&io
, const DataEdit
&edit
,
530 const CHAR
*x
, std::size_t length
) {
531 switch (edit
.descriptor
) {
536 io
.GetIoErrorHandler().SignalError(IostatErrorInFormat
,
537 "Data edit descriptor '%c' may not be used with a CHARACTER data item",
541 int len
{static_cast<int>(length
)};
542 int width
{edit
.width
.value_or(len
)};
543 return io
.EmitRepeated(' ', std::max(0, width
- len
)) &&
544 io
.EmitEncoded(x
, std::min(width
, len
));
547 template bool EditIntegerOutput
<1>(
548 IoStatementState
&, const DataEdit
&, std::int8_t);
549 template bool EditIntegerOutput
<2>(
550 IoStatementState
&, const DataEdit
&, std::int16_t);
551 template bool EditIntegerOutput
<4>(
552 IoStatementState
&, const DataEdit
&, std::int32_t);
553 template bool EditIntegerOutput
<8>(
554 IoStatementState
&, const DataEdit
&, std::int64_t);
555 template bool EditIntegerOutput
<16>(
556 IoStatementState
&, const DataEdit
&, common::int128_t
);
558 template class RealOutputEditing
<2>;
559 template class RealOutputEditing
<3>;
560 template class RealOutputEditing
<4>;
561 template class RealOutputEditing
<8>;
562 template class RealOutputEditing
<10>;
563 // TODO: double/double
564 template class RealOutputEditing
<16>;
566 template bool ListDirectedCharacterOutput(IoStatementState
&,
567 ListDirectedStatementState
<Direction::Output
> &, const char *,
569 template bool ListDirectedCharacterOutput(IoStatementState
&,
570 ListDirectedStatementState
<Direction::Output
> &, const char16_t
*,
572 template bool ListDirectedCharacterOutput(IoStatementState
&,
573 ListDirectedStatementState
<Direction::Output
> &, const char32_t
*,
576 template bool EditCharacterOutput(
577 IoStatementState
&, const DataEdit
&, const char *, std::size_t chars
);
578 template bool EditCharacterOutput(
579 IoStatementState
&, const DataEdit
&, const char16_t
*, std::size_t chars
);
580 template bool EditCharacterOutput(
581 IoStatementState
&, const DataEdit
&, const char32_t
*, std::size_t chars
);
583 } // namespace Fortran::runtime::io