[flang] Accept polymorphic component element in storage_size
[llvm-project.git] / flang / runtime / edit-output.cpp
blob965c7ceb7de4aac05ec4579dcef6f32d63de7030
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 //===----------------------------------------------------------------------===//
9 #include "edit-output.h"
10 #include "emit-encoded.h"
11 #include "utf.h"
12 #include "flang/Common/uint128.h"
13 #include <algorithm>
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};
25 if (get > 0) {
26 ++digits;
27 } else {
28 get = LOG2_BASE;
30 int shift{7};
31 int increment{isHostLittleEndian ? -1 : 1};
32 const unsigned char *data{data0 + (isHostLittleEndian ? bytes - 1 : 0)};
33 int skippedZeroes{0};
34 int digit{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
40 // same byte.
41 while (bytes > 0) {
42 if (get == 0) {
43 if (digit != 0) {
44 break; // first nonzero leading digit
46 ++skippedZeroes;
47 get = LOG2_BASE;
48 } else if (shift < 0) {
49 data += increment;
50 --bytes;
51 shift = 7;
52 } else {
53 digit = 2 * digit + ((*data >> shift--) & 1);
54 --get;
57 // Emit leading spaces and zeroes; detect field overflow
58 int leadingZeroes{0};
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);
64 } else {
65 leadingZeroes = *edit.digits - significant;
67 } else if (bytes == 0) {
68 leadingZeroes = 1;
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))) {
77 return false;
79 // Emit remaining digits
80 while (bytes > 0) {
81 if (get == 0) {
82 char ch{static_cast<char>(digit >= 10 ? 'A' + digit - 10 : '0' + digit)};
83 if (!EmitAscii(io, &ch, 1)) {
84 return false;
86 get = LOG2_BASE;
87 digit = 0;
88 } else if (shift < 0) {
89 data += increment;
90 --bytes;
91 shift = 7;
92 } else {
93 digit = 2 * digit + ((*data >> shift--) & 1);
94 --get;
97 return true;
100 template <int KIND>
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)};
107 int signChars{0};
108 switch (edit.descriptor) {
109 case DataEdit::ListDirected:
110 case 'G':
111 case 'I':
112 if (isNegative) {
113 un = -un;
115 if (isNegative || (edit.modes.editingFlags & signPlus)) {
116 signChars = 1; // '-' or '+'
118 while (un > 0) {
119 auto quotient{un / 10u};
120 *--p = '0' + static_cast<int>(un - Unsigned{10} * quotient);
121 un = quotient;
123 break;
124 case 'B':
125 return EditBOZOutput<1>(
126 io, edit, reinterpret_cast<const unsigned char *>(&n), KIND);
127 case 'O':
128 return EditBOZOutput<3>(
129 io, edit, reinterpret_cast<const unsigned char *>(&n), KIND);
130 case 'Z':
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);
136 default:
137 io.GetIoErrorHandler().SignalError(IostatErrorInFormat,
138 "Data edit descriptor '%c' may not be used with an INTEGER data item",
139 edit.descriptor);
140 return false;
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);
152 } else {
153 leadingZeroes = *edit.digits - digits;
155 } else if (n == 0) {
156 leadingZeroes = 1;
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()) {
167 return false;
169 leadingSpaces = 1;
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;
184 e = 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) {
191 *--exponent = '0';
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) {
198 *--exponent = '0';
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
214 : 1};
215 int suffixLength{edit.descriptor == DataEdit::ListDirectedRealPart ||
216 edit.descriptor == DataEdit::ListDirectedImaginaryPart
218 : 0};
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);
225 } else {
226 return true;
230 bool RealOutputEditingBase::EmitSuffix(const DataEdit &edit) {
231 if (edit.descriptor == DataEdit::ListDirectedRealPart) {
232 return EmitAscii(
233 io_, edit.modes.editingFlags & decimalComma ? ";" : ",", 1);
234 } else if (edit.descriptor == DataEdit::ListDirectedImaginaryPart) {
235 return EmitAscii(io_, ")", 1);
236 } else {
237 return true;
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",
250 sizeof buffer_);
252 return converted;
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};
261 int flags{0};
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;
272 } else { // E0
273 flags |= decimal::Minimize;
274 significantDigits =
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};
281 if (isEN) {
282 scale = IsZero() ? 1 : 3;
283 significantDigits += scale;
284 } else if (isES) {
285 scale = 1;
286 ++significantDigits;
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,
291 -editDigits);
292 return false;
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,
300 editDigits + 2);
301 return false;
303 ++significantDigits;
304 scale = std::min(scale, significantDigits + 1);
306 // In EN editing, multiple attempts may be necessary, so this is a loop.
307 while (true) {
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);
314 if (!IsZero()) {
315 converted.decimalExponent -= scale;
317 if (isEN) {
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) {
322 --significantDigits;
323 --scale;
324 continue;
326 // Rounded nines up to a 1.
327 scale += modulus;
328 converted.decimalExponent -= modulus;
330 if (scale > 3) {
331 int adjust{3 * (scale / 3)};
332 scale -= adjust;
333 converted.decimalExponent += adjust;
334 } else if (scale < 1) {
335 int adjust{3 - 3 * (scale / 3)};
336 scale += adjust;
337 converted.decimalExponent -= adjust;
339 significantDigits = editDigits + scale;
341 // Format the exponent (see table 13.1 for all the cases)
342 int expoLength{0};
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
352 : std::max(0,
353 significantDigits - (convertedDigits + zeroesBeforePoint))};
354 int totalLength{signLength + digitsBeforePoint + zeroesBeforePoint +
355 1 /*'.'*/ + zeroesAfterPoint + digitsAfterPoint + trailingZeroes +
356 expoLength};
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;
364 ++totalLength;
366 if (totalLength < width && noLeadingSpaces) {
367 width = totalLength;
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,
375 digitsAfterPoint) &&
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};
387 int flags{0};
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.
399 int extraDigits{0};
400 bool canIncrease{true};
401 while (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
412 expo = 0;
413 convertedDigits = 0;
415 int trailingOnes{0};
416 if (expo > extraDigits && extraDigits >= 0 && canIncrease) {
417 extraDigits = expo;
418 if (!edit.digits.has_value()) { // F0
419 fracDigits = sizeof buffer_ - extraDigits - 2; // sign & NUL
421 canIncrease = false; // only once
422 continue;
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
432 ++expo;
433 convertedDigits = 0;
434 trailingOnes = 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;
439 continue;
440 } else {
441 // Value rounds down to zero
442 expo = 0;
443 convertedDigits = 0;
445 } else if (expo < extraDigits && extraDigits > -fracDigits) {
446 extraDigits = std::max(expo, -fracDigits);
447 continue;
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
455 : std::max(0,
456 fracDigits -
457 (zeroesAfterPoint + digitsAfterPoint + trailingOnes))};
458 if (digitsBeforePoint + zeroesBeforePoint + zeroesAfterPoint +
459 digitsAfterPoint + trailingOnes + trailingZeroes ==
460 0) {
461 zeroesBeforePoint = 1; // "." -> "0."
463 int totalLength{signLength + digitsBeforePoint + zeroesBeforePoint +
464 1 /*'.'*/ + zeroesAfterPoint + digitsAfterPoint + trailingOnes +
465 trailingZeroes};
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;
472 ++totalLength;
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,
480 digitsAfterPoint) &&
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
497 int flags{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
509 edit.expoDigits = 0;
511 return edit; // Ew.dEe
513 edit.descriptor = 'F';
514 edit.modes.scale = 0; // kP is ignored for G when no exponent field
515 trailingBlanks_ = 0;
516 if (editWidth > 0) {
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);
524 return edit;
527 // 13.10.4 in F'2018
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) {
542 DataEdit copy{edit};
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) {
558 case 'D':
559 return EditEorDOutput(edit);
560 case 'E':
561 if (edit.variation == 'X') {
562 return EditEXOutput(edit);
563 } else {
564 return EditEorDOutput(edit);
566 case 'F':
567 return EditFOutput(edit);
568 case 'B':
569 return EditBOZOutput<1>(io_, edit,
570 reinterpret_cast<const unsigned char *>(&x_),
571 common::BitsForBinaryPrecision(common::PrecisionOfRealKind(KIND)) >> 3);
572 case 'O':
573 return EditBOZOutput<3>(io_, edit,
574 reinterpret_cast<const unsigned char *>(&x_),
575 common::BitsForBinaryPrecision(common::PrecisionOfRealKind(KIND)) >> 3);
576 case 'Z':
577 return EditBOZOutput<4>(io_, edit,
578 reinterpret_cast<const unsigned char *>(&x_),
579 common::BitsForBinaryPrecision(common::PrecisionOfRealKind(KIND)) >> 3);
580 case 'G':
581 return Edit(EditForGOutput(edit));
582 case 'A': // legacy extension
583 return EditCharacterOutput(
584 io_, edit, reinterpret_cast<char *>(&x_), sizeof x_);
585 default:
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",
591 edit.descriptor);
592 return false;
594 return false;
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) {
605 case 'L':
606 case 'G':
607 return EmitRepeated(io, ' ', std::max(0, edit.width.value_or(1) - 1)) &&
608 EmitAscii(io, truth ? "T" : "F", 1);
609 case 'B':
610 return EditBOZOutput<1>(io, edit,
611 reinterpret_cast<const unsigned char *>(&truth), sizeof truth);
612 case 'O':
613 return EditBOZOutput<3>(io, edit,
614 reinterpret_cast<const unsigned char *>(&truth), sizeof truth);
615 case 'Z':
616 return EditBOZOutput<4>(io, edit,
617 reinterpret_cast<const unsigned char *>(&truth), sizeof truth);
618 default:
619 io.GetIoErrorHandler().SignalError(IostatErrorInFormat,
620 "Data edit descriptor '%c' may not be used with a LOGICAL data item",
621 edit.descriptor);
622 return false;
626 template <typename CHAR>
627 bool ListDirectedCharacterOutput(IoStatementState &io,
628 ListDirectedStatementState<Direction::Output> &list, const CHAR *x,
629 std::size_t length) {
630 bool ok{true};
631 MutableModes &modes{io.mutableModes()};
632 ConnectionState &connection{io.GetConnectionState()};
633 if (modes.delim) {
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)) {
655 EmitOne(x[j]);
657 EmitOne(x[j]);
659 EmitOne(modes.delim);
660 } else {
661 // Undelimited list-directed output
662 ok = ok && list.EmitLeadingSpaceOrAdvance(io, length > 0 ? 1 : 0, true);
663 std::size_t put{0};
664 std::size_t oneAtATime{
665 connection.useUTF8<CHAR>() || connection.internalIoCharKind > 1
667 : length};
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);
673 put += chunk;
674 } else {
675 ok = io.AdvanceRecord() && EmitAscii(io, " ", 1);
678 list.set_lastWasUndelimitedCharacter(true);
680 return ok;
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) {
689 case 'A':
690 break;
691 case 'G':
692 if (width == 0) {
693 width = len;
695 break;
696 case 'B':
697 return EditBOZOutput<1>(io, edit,
698 reinterpret_cast<const unsigned char *>(x), sizeof(CHAR) * length);
699 case 'O':
700 return EditBOZOutput<3>(io, edit,
701 reinterpret_cast<const unsigned char *>(x), sizeof(CHAR) * length);
702 case 'Z':
703 return EditBOZOutput<4>(io, edit,
704 reinterpret_cast<const unsigned char *>(x), sizeof(CHAR) * length);
705 default:
706 io.GetIoErrorHandler().SignalError(IostatErrorInFormat,
707 "Data edit descriptor '%c' may not be used with a CHARACTER data item",
708 edit.descriptor);
709 return false;
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 *,
736 std::size_t chars);
737 template bool ListDirectedCharacterOutput(IoStatementState &,
738 ListDirectedStatementState<Direction::Output> &, const char16_t *,
739 std::size_t chars);
740 template bool ListDirectedCharacterOutput(IoStatementState &,
741 ListDirectedStatementState<Direction::Output> &, const char32_t *,
742 std::size_t chars);
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