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"
10 #include "emit-encoded.h"
12 #include "flang/Common/uint128.h"
15 namespace Fortran::runtime::io
{
17 // B/O/Z output of arbitrarily sized data emits a binary/octal/hexadecimal
18 // representation of what is interpreted to be a single unsigned integer value.
19 // When used with character data, endianness is exposed.
20 template <int LOG2_BASE
>
21 static bool EditBOZOutput(IoStatementState
&io
, const DataEdit
&edit
,
22 const unsigned char *data0
, std::size_t bytes
) {
23 int digits
{static_cast<int>((bytes
* 8) / LOG2_BASE
)};
24 int get
{static_cast<int>(bytes
* 8) - digits
* LOG2_BASE
};
31 int increment
{isHostLittleEndian
? -1 : 1};
32 const unsigned char *data
{data0
+ (isHostLittleEndian
? bytes
- 1 : 0)};
35 // The same algorithm is used to generate digits for real (below)
36 // as well as for generating them only to skip leading zeroes (here).
37 // Bits are copied one at a time from the source data.
38 // TODO: Multiple bit copies for hexadecimal, where misalignment
39 // is not possible; or for octal when all 3 bits come from the
44 break; // first nonzero leading digit
48 } else if (shift
< 0) {
53 digit
= 2 * digit
+ ((*data
>> shift
--) & 1);
57 // Emit leading spaces and zeroes; detect field overflow
59 int editWidth
{edit
.width
.value_or(0)};
60 int significant
{digits
- skippedZeroes
};
61 if (edit
.digits
&& significant
<= *edit
.digits
) { // Bw.m, Ow.m, Zw.m
62 if (*edit
.digits
== 0 && bytes
== 0) {
63 editWidth
= std::max(1, editWidth
);
65 leadingZeroes
= *edit
.digits
- significant
;
67 } else if (bytes
== 0) {
70 int subTotal
{leadingZeroes
+ significant
};
71 int leadingSpaces
{std::max(0, editWidth
- subTotal
)};
72 if (editWidth
> 0 && leadingSpaces
+ subTotal
> editWidth
) {
73 return EmitRepeated(io
, '*', editWidth
);
75 if (!(EmitRepeated(io
, ' ', leadingSpaces
) &&
76 EmitRepeated(io
, '0', leadingZeroes
))) {
79 // Emit remaining digits
82 char ch
{static_cast<char>(digit
>= 10 ? 'A' + digit
- 10 : '0' + digit
)};
83 if (!EmitAscii(io
, &ch
, 1)) {
88 } else if (shift
< 0) {
93 digit
= 2 * digit
+ ((*data
>> shift
--) & 1);
101 bool EditIntegerOutput(IoStatementState
&io
, const DataEdit
&edit
,
102 common::HostSignedIntType
<8 * KIND
> n
) {
103 char buffer
[130], *end
{&buffer
[sizeof buffer
]}, *p
{end
};
104 bool isNegative
{n
< 0};
105 using Unsigned
= common::HostUnsignedIntType
<8 * KIND
>;
106 Unsigned un
{static_cast<Unsigned
>(n
)};
108 switch (edit
.descriptor
) {
109 case DataEdit::ListDirected
:
115 if (isNegative
|| (edit
.modes
.editingFlags
& signPlus
)) {
116 signChars
= 1; // '-' or '+'
119 auto quotient
{un
/ 10u};
120 *--p
= '0' + static_cast<int>(un
- Unsigned
{10} * quotient
);
125 return EditBOZOutput
<1>(
126 io
, edit
, reinterpret_cast<const unsigned char *>(&n
), KIND
);
128 return EditBOZOutput
<3>(
129 io
, edit
, reinterpret_cast<const unsigned char *>(&n
), KIND
);
131 return EditBOZOutput
<4>(
132 io
, edit
, reinterpret_cast<const unsigned char *>(&n
), KIND
);
133 case 'A': // legacy extension
134 return EditCharacterOutput(
135 io
, edit
, reinterpret_cast<char *>(&n
), sizeof n
);
137 io
.GetIoErrorHandler().SignalError(IostatErrorInFormat
,
138 "Data edit descriptor '%c' may not be used with an INTEGER data item",
143 int digits
= end
- p
;
144 int leadingZeroes
{0};
145 int editWidth
{edit
.width
.value_or(0)};
146 if (edit
.digits
&& digits
<= *edit
.digits
) { // Iw.m
147 if (*edit
.digits
== 0 && n
== 0) {
148 // Iw.0 with zero value: output field must be blank. For I0.0
149 // and a zero value, emit one blank character.
150 signChars
= 0; // in case of SP
151 editWidth
= std::max(1, editWidth
);
153 leadingZeroes
= *edit
.digits
- digits
;
158 int subTotal
{signChars
+ leadingZeroes
+ digits
};
159 int leadingSpaces
{std::max(0, editWidth
- subTotal
)};
160 if (editWidth
> 0 && leadingSpaces
+ subTotal
> editWidth
) {
161 return EmitRepeated(io
, '*', editWidth
);
163 if (edit
.IsListDirected()) {
164 int total
{std::max(leadingSpaces
, 1) + subTotal
};
165 if (io
.GetConnectionState().NeedAdvance(static_cast<std::size_t>(total
)) &&
166 !io
.AdvanceRecord()) {
171 return EmitRepeated(io
, ' ', leadingSpaces
) &&
172 EmitAscii(io
, n
< 0 ? "-" : "+", signChars
) &&
173 EmitRepeated(io
, '0', leadingZeroes
) && EmitAscii(io
, p
, digits
);
176 // Formats the exponent (see table 13.1 for all the cases)
177 const char *RealOutputEditingBase::FormatExponent(
178 int expo
, const DataEdit
&edit
, int &length
) {
179 char *eEnd
{&exponent_
[sizeof exponent_
]};
180 char *exponent
{eEnd
};
181 for (unsigned e
{static_cast<unsigned>(std::abs(expo
))}; e
> 0;) {
182 unsigned quotient
{e
/ 10u};
183 *--exponent
= '0' + e
- 10 * quotient
;
186 bool overflow
{false};
187 if (edit
.expoDigits
) {
188 if (int ed
{*edit
.expoDigits
}) { // Ew.dEe with e > 0
189 overflow
= exponent
+ ed
< eEnd
;
190 while (exponent
> exponent_
+ 2 /*E+*/ && exponent
+ ed
> eEnd
) {
193 } else if (exponent
== eEnd
) {
194 *--exponent
= '0'; // Ew.dE0 with zero-valued exponent
196 } else { // ensure at least two exponent digits
197 while (exponent
+ 2 > eEnd
) {
201 *--exponent
= expo
< 0 ? '-' : '+';
202 if (edit
.expoDigits
|| edit
.IsListDirected() || exponent
+ 3 == eEnd
) {
203 *--exponent
= edit
.descriptor
== 'D' ? 'D' : 'E'; // not 'G' or 'Q'
205 length
= eEnd
- exponent
;
206 return overflow
? nullptr : exponent
;
209 bool RealOutputEditingBase::EmitPrefix(
210 const DataEdit
&edit
, std::size_t length
, std::size_t width
) {
211 if (edit
.IsListDirected()) {
212 int prefixLength
{edit
.descriptor
== DataEdit::ListDirectedRealPart
? 2
213 : edit
.descriptor
== DataEdit::ListDirectedImaginaryPart
? 0
215 int suffixLength
{edit
.descriptor
== DataEdit::ListDirectedRealPart
||
216 edit
.descriptor
== DataEdit::ListDirectedImaginaryPart
219 length
+= prefixLength
+ suffixLength
;
220 ConnectionState
&connection
{io_
.GetConnectionState()};
221 return (!connection
.NeedAdvance(length
) || io_
.AdvanceRecord()) &&
222 EmitAscii(io_
, " (", prefixLength
);
223 } else if (width
> length
) {
224 return EmitRepeated(io_
, ' ', width
- length
);
230 bool RealOutputEditingBase::EmitSuffix(const DataEdit
&edit
) {
231 if (edit
.descriptor
== DataEdit::ListDirectedRealPart
) {
233 io_
, edit
.modes
.editingFlags
& decimalComma
? ";" : ",", 1);
234 } else if (edit
.descriptor
== DataEdit::ListDirectedImaginaryPart
) {
235 return EmitAscii(io_
, ")", 1);
241 template <int binaryPrecision
>
242 decimal::ConversionToDecimalResult RealOutputEditing
<binaryPrecision
>::Convert(
243 int significantDigits
, enum decimal::FortranRounding rounding
, int flags
) {
244 auto converted
{decimal::ConvertToDecimal
<binaryPrecision
>(buffer_
,
245 sizeof buffer_
, static_cast<enum decimal::DecimalConversionFlags
>(flags
),
246 significantDigits
, rounding
, x_
)};
247 if (!converted
.str
) { // overflow
248 io_
.GetIoErrorHandler().Crash(
249 "RealOutputEditing::Convert : buffer size %zd was insufficient",
255 // 13.7.2.3.3 in F'2018
256 template <int binaryPrecision
>
257 bool RealOutputEditing
<binaryPrecision
>::EditEorDOutput(const DataEdit
&edit
) {
258 int editDigits
{edit
.digits
.value_or(0)}; // 'd' field
259 int editWidth
{edit
.width
.value_or(0)}; // 'w' field
260 int significantDigits
{editDigits
};
262 if (edit
.modes
.editingFlags
& signPlus
) {
263 flags
|= decimal::AlwaysSign
;
265 bool noLeadingSpaces
{editWidth
== 0};
266 int scale
{edit
.modes
.scale
}; // 'kP' value
267 if (editWidth
== 0) { // "the processor selects the field width"
268 if (edit
.digits
.has_value()) { // E0.d
269 if (editDigits
== 0 && scale
<= 0) { // E0.0
270 significantDigits
= 1;
273 flags
|= decimal::Minimize
;
275 sizeof buffer_
- 5; // sign, NUL, + 3 extra for EN scaling
278 bool isEN
{edit
.variation
== 'N'};
279 bool isES
{edit
.variation
== 'S'};
280 int zeroesAfterPoint
{0};
282 scale
= IsZero() ? 1 : 3;
283 significantDigits
+= scale
;
287 } else if (scale
< 0) {
288 if (scale
<= -editDigits
) {
289 io_
.GetIoErrorHandler().SignalError(IostatBadScaleFactor
,
290 "Scale factor (kP) %d cannot be less than -d (%d)", scale
,
294 zeroesAfterPoint
= -scale
;
295 significantDigits
= std::max(0, significantDigits
- zeroesAfterPoint
);
296 } else if (scale
> 0) {
297 if (scale
>= editDigits
+ 2) {
298 io_
.GetIoErrorHandler().SignalError(IostatBadScaleFactor
,
299 "Scale factor (kP) %d cannot be greater than d+2 (%d)", scale
,
304 scale
= std::min(scale
, significantDigits
+ 1);
306 // In EN editing, multiple attempts may be necessary, so this is a loop.
308 decimal::ConversionToDecimalResult converted
{
309 Convert(significantDigits
, edit
.modes
.round
, flags
)};
310 if (IsInfOrNaN(converted
)) {
311 return EmitPrefix(edit
, converted
.length
, editWidth
) &&
312 EmitAscii(io_
, converted
.str
, converted
.length
) && EmitSuffix(edit
);
315 converted
.decimalExponent
-= scale
;
318 // EN mode: we need an effective exponent field that is
319 // a multiple of three.
320 if (int modulus
{converted
.decimalExponent
% 3}; modulus
!= 0) {
321 if (significantDigits
> 1) {
326 // Rounded nines up to a 1.
328 converted
.decimalExponent
-= modulus
;
331 int adjust
{3 * (scale
/ 3)};
333 converted
.decimalExponent
+= adjust
;
334 } else if (scale
< 1) {
335 int adjust
{3 - 3 * (scale
/ 3)};
337 converted
.decimalExponent
-= adjust
;
339 significantDigits
= editDigits
+ scale
;
341 // Format the exponent (see table 13.1 for all the cases)
343 const char *exponent
{
344 FormatExponent(converted
.decimalExponent
, edit
, expoLength
)};
345 int signLength
{*converted
.str
== '-' || *converted
.str
== '+' ? 1 : 0};
346 int convertedDigits
{static_cast<int>(converted
.length
) - signLength
};
347 int zeroesBeforePoint
{std::max(0, scale
- convertedDigits
)};
348 int digitsBeforePoint
{std::max(0, scale
- zeroesBeforePoint
)};
349 int digitsAfterPoint
{convertedDigits
- digitsBeforePoint
};
350 int trailingZeroes
{flags
& decimal::Minimize
353 significantDigits
- (convertedDigits
+ zeroesBeforePoint
))};
354 int totalLength
{signLength
+ digitsBeforePoint
+ zeroesBeforePoint
+
355 1 /*'.'*/ + zeroesAfterPoint
+ digitsAfterPoint
+ trailingZeroes
+
357 int width
{editWidth
> 0 ? editWidth
: totalLength
};
358 if (totalLength
> width
|| !exponent
) {
359 return EmitRepeated(io_
, '*', width
);
361 if (totalLength
< width
&& digitsBeforePoint
== 0 &&
362 zeroesBeforePoint
== 0) {
363 zeroesBeforePoint
= 1;
366 if (totalLength
< width
&& noLeadingSpaces
) {
369 return EmitPrefix(edit
, totalLength
, width
) &&
370 EmitAscii(io_
, converted
.str
, signLength
+ digitsBeforePoint
) &&
371 EmitRepeated(io_
, '0', zeroesBeforePoint
) &&
372 EmitAscii(io_
, edit
.modes
.editingFlags
& decimalComma
? "," : ".", 1) &&
373 EmitRepeated(io_
, '0', zeroesAfterPoint
) &&
374 EmitAscii(io_
, converted
.str
+ signLength
+ digitsBeforePoint
,
376 EmitRepeated(io_
, '0', trailingZeroes
) &&
377 EmitAscii(io_
, exponent
, expoLength
) && EmitSuffix(edit
);
381 // 13.7.2.3.2 in F'2018
382 template <int binaryPrecision
>
383 bool RealOutputEditing
<binaryPrecision
>::EditFOutput(const DataEdit
&edit
) {
384 int fracDigits
{edit
.digits
.value_or(0)}; // 'd' field
385 const int editWidth
{edit
.width
.value_or(0)}; // 'w' field
386 enum decimal::FortranRounding rounding
{edit
.modes
.round
};
388 if (edit
.modes
.editingFlags
& signPlus
) {
389 flags
|= decimal::AlwaysSign
;
391 if (editWidth
== 0) { // "the processor selects the field width"
392 if (!edit
.digits
.has_value()) { // F0
393 flags
|= decimal::Minimize
;
394 fracDigits
= sizeof buffer_
- 2; // sign & NUL
397 // Multiple conversions may be needed to get the right number of
398 // effective rounded fractional digits.
400 bool canIncrease
{true};
402 decimal::ConversionToDecimalResult converted
{
403 Convert(extraDigits
+ fracDigits
, rounding
, flags
)};
404 if (IsInfOrNaN(converted
)) {
405 return EmitPrefix(edit
, converted
.length
, editWidth
) &&
406 EmitAscii(io_
, converted
.str
, converted
.length
) && EmitSuffix(edit
);
408 int expo
{converted
.decimalExponent
+ edit
.modes
.scale
/*kP*/};
409 int signLength
{*converted
.str
== '-' || *converted
.str
== '+' ? 1 : 0};
410 int convertedDigits
{static_cast<int>(converted
.length
) - signLength
};
411 if (IsZero()) { // don't treat converted "0" as significant digit
416 if (expo
> extraDigits
&& extraDigits
>= 0 && canIncrease
) {
418 if (!edit
.digits
.has_value()) { // F0
419 fracDigits
= sizeof buffer_
- extraDigits
- 2; // sign & NUL
421 canIncrease
= false; // only once
423 } else if (expo
== -fracDigits
&& convertedDigits
> 0) {
424 if ((rounding
== decimal::FortranRounding::RoundUp
&&
425 *converted
.str
!= '-') ||
426 (rounding
== decimal::FortranRounding::RoundDown
&&
427 *converted
.str
== '-') ||
428 (rounding
== decimal::FortranRounding::RoundToZero
&&
429 rounding
!= edit
.modes
.round
&& // it changed below
430 converted
.str
[signLength
] >= '5')) {
431 // Round up/down to a scaled 1
435 } else if (rounding
!= decimal::FortranRounding::RoundToZero
) {
436 // Convert again with truncation so first digit can be checked
437 // on the next iteration by the code above
438 rounding
= decimal::FortranRounding::RoundToZero
;
441 // Value rounds down to zero
445 } else if (expo
< extraDigits
&& extraDigits
> -fracDigits
) {
446 extraDigits
= std::max(expo
, -fracDigits
);
449 int digitsBeforePoint
{std::max(0, std::min(expo
, convertedDigits
))};
450 int zeroesBeforePoint
{std::max(0, expo
- digitsBeforePoint
)};
451 int zeroesAfterPoint
{std::min(fracDigits
, std::max(0, -expo
))};
452 int digitsAfterPoint
{convertedDigits
- digitsBeforePoint
};
453 int trailingZeroes
{flags
& decimal::Minimize
457 (zeroesAfterPoint
+ digitsAfterPoint
+ trailingOnes
))};
458 if (digitsBeforePoint
+ zeroesBeforePoint
+ zeroesAfterPoint
+
459 digitsAfterPoint
+ trailingOnes
+ trailingZeroes
==
461 zeroesBeforePoint
= 1; // "." -> "0."
463 int totalLength
{signLength
+ digitsBeforePoint
+ zeroesBeforePoint
+
464 1 /*'.'*/ + zeroesAfterPoint
+ digitsAfterPoint
+ trailingOnes
+
466 int width
{editWidth
> 0 ? editWidth
: totalLength
};
467 if (totalLength
> width
) {
468 return EmitRepeated(io_
, '*', width
);
470 if (totalLength
< width
&& digitsBeforePoint
+ zeroesBeforePoint
== 0) {
471 zeroesBeforePoint
= 1;
474 return EmitPrefix(edit
, totalLength
, width
) &&
475 EmitAscii(io_
, converted
.str
, signLength
+ digitsBeforePoint
) &&
476 EmitRepeated(io_
, '0', zeroesBeforePoint
) &&
477 EmitAscii(io_
, edit
.modes
.editingFlags
& decimalComma
? "," : ".", 1) &&
478 EmitRepeated(io_
, '0', zeroesAfterPoint
) &&
479 EmitAscii(io_
, converted
.str
+ signLength
+ digitsBeforePoint
,
481 EmitRepeated(io_
, '1', trailingOnes
) &&
482 EmitRepeated(io_
, '0', trailingZeroes
) &&
483 EmitRepeated(io_
, ' ', trailingBlanks_
) && EmitSuffix(edit
);
487 // 13.7.5.2.3 in F'2018
488 template <int binaryPrecision
>
489 DataEdit RealOutputEditing
<binaryPrecision
>::EditForGOutput(DataEdit edit
) {
490 edit
.descriptor
= 'E';
491 int editWidth
{edit
.width
.value_or(0)};
492 int significantDigits
{
493 edit
.digits
.value_or(BinaryFloatingPoint::decimalPrecision
)}; // 'd'
494 if (editWidth
> 0 && significantDigits
== 0) {
495 return edit
; // Gw.0Ee -> Ew.0Ee for w > 0
498 if (edit
.modes
.editingFlags
& signPlus
) {
499 flags
|= decimal::AlwaysSign
;
501 decimal::ConversionToDecimalResult converted
{
502 Convert(significantDigits
, edit
.modes
.round
, flags
)};
503 if (IsInfOrNaN(converted
)) {
504 return edit
; // Inf/Nan -> Ew.d (same as Fw.d)
506 int expo
{IsZero() ? 1 : converted
.decimalExponent
}; // 's'
507 if (expo
< 0 || expo
> significantDigits
) {
508 if (editWidth
== 0 && !edit
.expoDigits
) { // G0.d -> G0.dE0
511 return edit
; // Ew.dEe
513 edit
.descriptor
= 'F';
514 edit
.modes
.scale
= 0; // kP is ignored for G when no exponent field
517 int expoDigits
{edit
.expoDigits
.value_or(0)};
518 trailingBlanks_
= expoDigits
> 0 ? expoDigits
+ 2 : 4; // 'n'
519 *edit
.width
= std::max(0, editWidth
- trailingBlanks_
);
521 if (edit
.digits
.has_value()) {
522 *edit
.digits
= std::max(0, *edit
.digits
- expo
);
528 template <int binaryPrecision
>
529 bool RealOutputEditing
<binaryPrecision
>::EditListDirectedOutput(
530 const DataEdit
&edit
) {
531 decimal::ConversionToDecimalResult converted
{Convert(1, edit
.modes
.round
)};
532 if (IsInfOrNaN(converted
)) {
533 return EditEorDOutput(edit
);
535 int expo
{converted
.decimalExponent
};
536 // The decimal precision of 16-bit floating-point types is very low,
537 // so use a reasonable cap of 6 to allow more values to be emitted
538 // with Fw.d editing.
539 static constexpr int maxExpo
{
540 std::max(6, BinaryFloatingPoint::decimalPrecision
)};
541 if (expo
< 0 || expo
> maxExpo
) {
543 copy
.modes
.scale
= 1; // 1P
544 return EditEorDOutput(copy
);
546 return EditFOutput(edit
);
549 // 13.7.5.2.6 in F'2018
550 template <int binaryPrecision
>
551 bool RealOutputEditing
<binaryPrecision
>::EditEXOutput(const DataEdit
&) {
552 io_
.GetIoErrorHandler().Crash(
553 "not yet implemented: EX output editing"); // TODO
556 template <int KIND
> bool RealOutputEditing
<KIND
>::Edit(const DataEdit
&edit
) {
557 switch (edit
.descriptor
) {
559 return EditEorDOutput(edit
);
561 if (edit
.variation
== 'X') {
562 return EditEXOutput(edit
);
564 return EditEorDOutput(edit
);
567 return EditFOutput(edit
);
569 return EditBOZOutput
<1>(io_
, edit
,
570 reinterpret_cast<const unsigned char *>(&x_
),
571 common::BitsForBinaryPrecision(common::PrecisionOfRealKind(KIND
)) >> 3);
573 return EditBOZOutput
<3>(io_
, edit
,
574 reinterpret_cast<const unsigned char *>(&x_
),
575 common::BitsForBinaryPrecision(common::PrecisionOfRealKind(KIND
)) >> 3);
577 return EditBOZOutput
<4>(io_
, edit
,
578 reinterpret_cast<const unsigned char *>(&x_
),
579 common::BitsForBinaryPrecision(common::PrecisionOfRealKind(KIND
)) >> 3);
581 return Edit(EditForGOutput(edit
));
582 case 'A': // legacy extension
583 return EditCharacterOutput(
584 io_
, edit
, reinterpret_cast<char *>(&x_
), sizeof x_
);
586 if (edit
.IsListDirected()) {
587 return EditListDirectedOutput(edit
);
589 io_
.GetIoErrorHandler().SignalError(IostatErrorInFormat
,
590 "Data edit descriptor '%c' may not be used with a REAL data item",
597 bool ListDirectedLogicalOutput(IoStatementState
&io
,
598 ListDirectedStatementState
<Direction::Output
> &list
, bool truth
) {
599 return list
.EmitLeadingSpaceOrAdvance(io
) &&
600 EmitAscii(io
, truth
? "T" : "F", 1);
603 bool EditLogicalOutput(IoStatementState
&io
, const DataEdit
&edit
, bool truth
) {
604 switch (edit
.descriptor
) {
607 return EmitRepeated(io
, ' ', std::max(0, edit
.width
.value_or(1) - 1)) &&
608 EmitAscii(io
, truth
? "T" : "F", 1);
610 return EditBOZOutput
<1>(io
, edit
,
611 reinterpret_cast<const unsigned char *>(&truth
), sizeof truth
);
613 return EditBOZOutput
<3>(io
, edit
,
614 reinterpret_cast<const unsigned char *>(&truth
), sizeof truth
);
616 return EditBOZOutput
<4>(io
, edit
,
617 reinterpret_cast<const unsigned char *>(&truth
), sizeof truth
);
619 io
.GetIoErrorHandler().SignalError(IostatErrorInFormat
,
620 "Data edit descriptor '%c' may not be used with a LOGICAL data item",
626 template <typename CHAR
>
627 bool ListDirectedCharacterOutput(IoStatementState
&io
,
628 ListDirectedStatementState
<Direction::Output
> &list
, const CHAR
*x
,
629 std::size_t length
) {
631 MutableModes
&modes
{io
.mutableModes()};
632 ConnectionState
&connection
{io
.GetConnectionState()};
634 ok
= ok
&& list
.EmitLeadingSpaceOrAdvance(io
);
635 // Value is delimited with ' or " marks, and interior
636 // instances of that character are doubled.
637 auto EmitOne
{[&](CHAR ch
) {
638 if (connection
.NeedAdvance(1)) {
639 ok
= ok
&& io
.AdvanceRecord();
641 ok
= ok
&& EmitEncoded(io
, &ch
, 1);
643 EmitOne(modes
.delim
);
644 for (std::size_t j
{0}; j
< length
; ++j
) {
645 // Doubled delimiters must be put on the same record
646 // in order to be acceptable as list-directed or NAMELIST
647 // input; however, this requirement is not always possible
648 // when the records have a fixed length, as is the case with
649 // internal output. The standard is silent on what should
650 // happen, and no two extant Fortran implementations do
651 // the same thing when tested with this case.
652 // This runtime splits the doubled delimiters across
653 // two records for lack of a better alternative.
654 if (x
[j
] == static_cast<CHAR
>(modes
.delim
)) {
659 EmitOne(modes
.delim
);
661 // Undelimited list-directed output
662 ok
= ok
&& list
.EmitLeadingSpaceOrAdvance(io
, length
> 0 ? 1 : 0, true);
664 std::size_t oneAtATime
{
665 connection
.useUTF8
<CHAR
>() || connection
.internalIoCharKind
> 1
668 while (ok
&& put
< length
) {
669 if (std::size_t chunk
{std::min
<std::size_t>(
670 std::min
<std::size_t>(length
- put
, oneAtATime
),
671 connection
.RemainingSpaceInRecord())}) {
672 ok
= EmitEncoded(io
, x
+ put
, chunk
);
675 ok
= io
.AdvanceRecord() && EmitAscii(io
, " ", 1);
678 list
.set_lastWasUndelimitedCharacter(true);
683 template <typename CHAR
>
684 bool EditCharacterOutput(IoStatementState
&io
, const DataEdit
&edit
,
685 const CHAR
*x
, std::size_t length
) {
686 int len
{static_cast<int>(length
)};
687 int width
{edit
.width
.value_or(len
)};
688 switch (edit
.descriptor
) {
697 return EditBOZOutput
<1>(io
, edit
,
698 reinterpret_cast<const unsigned char *>(x
), sizeof(CHAR
) * length
);
700 return EditBOZOutput
<3>(io
, edit
,
701 reinterpret_cast<const unsigned char *>(x
), sizeof(CHAR
) * length
);
703 return EditBOZOutput
<4>(io
, edit
,
704 reinterpret_cast<const unsigned char *>(x
), sizeof(CHAR
) * length
);
706 io
.GetIoErrorHandler().SignalError(IostatErrorInFormat
,
707 "Data edit descriptor '%c' may not be used with a CHARACTER data item",
711 return EmitRepeated(io
, ' ', std::max(0, width
- len
)) &&
712 EmitEncoded(io
, x
, std::min(width
, len
));
715 template bool EditIntegerOutput
<1>(
716 IoStatementState
&, const DataEdit
&, std::int8_t);
717 template bool EditIntegerOutput
<2>(
718 IoStatementState
&, const DataEdit
&, std::int16_t);
719 template bool EditIntegerOutput
<4>(
720 IoStatementState
&, const DataEdit
&, std::int32_t);
721 template bool EditIntegerOutput
<8>(
722 IoStatementState
&, const DataEdit
&, std::int64_t);
723 template bool EditIntegerOutput
<16>(
724 IoStatementState
&, const DataEdit
&, common::int128_t
);
726 template class RealOutputEditing
<2>;
727 template class RealOutputEditing
<3>;
728 template class RealOutputEditing
<4>;
729 template class RealOutputEditing
<8>;
730 template class RealOutputEditing
<10>;
731 // TODO: double/double
732 template class RealOutputEditing
<16>;
734 template bool ListDirectedCharacterOutput(IoStatementState
&,
735 ListDirectedStatementState
<Direction::Output
> &, const char *,
737 template bool ListDirectedCharacterOutput(IoStatementState
&,
738 ListDirectedStatementState
<Direction::Output
> &, const char16_t
*,
740 template bool ListDirectedCharacterOutput(IoStatementState
&,
741 ListDirectedStatementState
<Direction::Output
> &, const char32_t
*,
744 template bool EditCharacterOutput(
745 IoStatementState
&, const DataEdit
&, const char *, std::size_t chars
);
746 template bool EditCharacterOutput(
747 IoStatementState
&, const DataEdit
&, const char16_t
*, std::size_t chars
);
748 template bool EditCharacterOutput(
749 IoStatementState
&, const DataEdit
&, const char32_t
*, std::size_t chars
);
751 } // namespace Fortran::runtime::io