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 <int KIND>
16 bool EditIntegerOutput(IoStatementState &io, const DataEdit &edit,
17     common::HostSignedIntType<8 * KIND> n) {
18   char buffer[130], *end{&buffer[sizeof buffer]}, *p{end};
19   bool isNegative{n < 0};
20   using Unsigned = common::HostUnsignedIntType<8 * KIND>;
21   Unsigned un{static_cast<Unsigned>(n)};
22   int signChars{0};
23   switch (edit.descriptor) {
24   case DataEdit::ListDirected:
25   case 'G':
26   case 'I':
27     if (isNegative) {
28       un = -un;
29     }
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 - Unsigned{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   case 'A': // legacy extension
56     return EditDefaultCharacterOutput(
57         io, edit, reinterpret_cast<char *>(&n), sizeof n);
58   default:
59     io.GetIoErrorHandler().Crash(
60         "Data edit descriptor '%c' may not be used with an INTEGER data item",
61         edit.descriptor);
62     return false;
63   }
64 
65   int digits = end - p;
66   int leadingZeroes{0};
67   int editWidth{edit.width.value_or(0)};
68   if (edit.digits && digits <= *edit.digits) { // Iw.m
69     if (*edit.digits == 0 && n == 0) {
70       // Iw.0 with zero value: output field must be blank.  For I0.0
71       // and a zero value, emit one blank character.
72       signChars = 0; // in case of SP
73       editWidth = std::max(1, editWidth);
74     } else {
75       leadingZeroes = *edit.digits - digits;
76     }
77   } else if (n == 0) {
78     leadingZeroes = 1;
79   }
80   int subTotal{signChars + leadingZeroes + digits};
81   int leadingSpaces{std::max(0, editWidth - subTotal)};
82   if (editWidth > 0 && leadingSpaces + subTotal > editWidth) {
83     return io.EmitRepeated('*', editWidth);
84   }
85   if (edit.IsListDirected()) {
86     int total{std::max(leadingSpaces, 1) + subTotal};
87     if (io.GetConnectionState().NeedAdvance(static_cast<std::size_t>(total)) &&
88         !io.AdvanceRecord()) {
89       return false;
90     }
91     leadingSpaces = 1;
92   }
93   return io.EmitRepeated(' ', leadingSpaces) &&
94       io.Emit(n < 0 ? "-" : "+", signChars) &&
95       io.EmitRepeated('0', leadingZeroes) && io.Emit(p, digits);
96 }
97 
98 // Formats the exponent (see table 13.1 for all the cases)
99 const char *RealOutputEditingBase::FormatExponent(
100     int expo, const DataEdit &edit, int &length) {
101   char *eEnd{&exponent_[sizeof exponent_]};
102   char *exponent{eEnd};
103   for (unsigned e{static_cast<unsigned>(std::abs(expo))}; e > 0;) {
104     unsigned quotient{e / 10u};
105     *--exponent = '0' + e - 10 * quotient;
106     e = quotient;
107   }
108   if (edit.expoDigits) {
109     if (int ed{*edit.expoDigits}) { // Ew.dEe with e > 0
110       while (exponent > exponent_ + 2 /*E+*/ && exponent + ed > eEnd) {
111         *--exponent = '0';
112       }
113     } else if (exponent == eEnd) {
114       *--exponent = '0'; // Ew.dE0 with zero-valued exponent
115     }
116   } else { // ensure at least two exponent digits
117     while (exponent + 2 > eEnd) {
118       *--exponent = '0';
119     }
120   }
121   *--exponent = expo < 0 ? '-' : '+';
122   if (edit.expoDigits || edit.IsListDirected() || exponent + 3 == eEnd) {
123     *--exponent = edit.descriptor == 'D' ? 'D' : 'E'; // not 'G'
124   }
125   length = eEnd - exponent;
126   return exponent;
127 }
128 
129 bool RealOutputEditingBase::EmitPrefix(
130     const DataEdit &edit, std::size_t length, std::size_t width) {
131   if (edit.IsListDirected()) {
132     int prefixLength{edit.descriptor == DataEdit::ListDirectedRealPart ? 2
133             : edit.descriptor == DataEdit::ListDirectedImaginaryPart   ? 0
134                                                                        : 1};
135     int suffixLength{edit.descriptor == DataEdit::ListDirectedRealPart ||
136                 edit.descriptor == DataEdit::ListDirectedImaginaryPart
137             ? 1
138             : 0};
139     length += prefixLength + suffixLength;
140     ConnectionState &connection{io_.GetConnectionState()};
141     return (!connection.NeedAdvance(length) || 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   bool canIncrease{true};
275   while (true) {
276     decimal::ConversionToDecimalResult converted{
277         Convert(extraDigits + fracDigits, edit, flags)};
278     if (IsInfOrNaN(converted)) {
279       return EmitPrefix(edit, converted.length, editWidth) &&
280           io_.Emit(converted.str, converted.length) && EmitSuffix(edit);
281     }
282     int scale{IsZero() ? 1 : edit.modes.scale}; // kP
283     int expo{converted.decimalExponent + scale};
284     if (expo > extraDigits && extraDigits >= 0 && canIncrease) {
285       extraDigits = expo;
286       if (!edit.digits.has_value()) { // F0
287         fracDigits = sizeof buffer_ - extraDigits - 2; // sign & NUL
288       }
289       canIncrease = false; // only once
290       continue;
291     } else if (expo < extraDigits && extraDigits > -fracDigits) {
292       extraDigits = std::max(expo, -fracDigits);
293       continue;
294     }
295     int signLength{*converted.str == '-' || *converted.str == '+' ? 1 : 0};
296     int convertedDigits{static_cast<int>(converted.length) - signLength};
297     int digitsBeforePoint{std::max(0, std::min(expo, convertedDigits))};
298     int zeroesBeforePoint{std::max(0, expo - digitsBeforePoint)};
299     int zeroesAfterPoint{std::min(fracDigits, std::max(0, -expo))};
300     int digitsAfterPoint{convertedDigits - digitsBeforePoint};
301     int trailingZeroes{flags & decimal::Minimize
302             ? 0
303             : std::max(0, fracDigits - (zeroesAfterPoint + digitsAfterPoint))};
304     if (digitsBeforePoint + zeroesBeforePoint + zeroesAfterPoint +
305             digitsAfterPoint + trailingZeroes ==
306         0) {
307       zeroesBeforePoint = 1; // "." -> "0."
308     }
309     int totalLength{signLength + digitsBeforePoint + zeroesBeforePoint +
310         1 /*'.'*/ + zeroesAfterPoint + digitsAfterPoint + trailingZeroes};
311     int width{editWidth > 0 ? editWidth : totalLength};
312     if (totalLength > width) {
313       return io_.EmitRepeated('*', width);
314     }
315     if (totalLength < width && digitsBeforePoint + zeroesBeforePoint == 0) {
316       zeroesBeforePoint = 1;
317       ++totalLength;
318     }
319     return EmitPrefix(edit, totalLength, width) &&
320         io_.Emit(converted.str, signLength + digitsBeforePoint) &&
321         io_.EmitRepeated('0', zeroesBeforePoint) &&
322         io_.Emit(edit.modes.editingFlags & decimalComma ? "," : ".", 1) &&
323         io_.EmitRepeated('0', zeroesAfterPoint) &&
324         io_.Emit(
325             converted.str + signLength + digitsBeforePoint, digitsAfterPoint) &&
326         io_.EmitRepeated('0', trailingZeroes) &&
327         io_.EmitRepeated(' ', trailingBlanks_) && EmitSuffix(edit);
328   }
329 }
330 
331 // 13.7.5.2.3 in F'2018
332 template <int binaryPrecision>
333 DataEdit RealOutputEditing<binaryPrecision>::EditForGOutput(DataEdit edit) {
334   edit.descriptor = 'E';
335   int significantDigits{
336       edit.digits.value_or(BinaryFloatingPoint::decimalPrecision)}; // 'd'
337   if (!edit.width.has_value() || (*edit.width > 0 && significantDigits == 0)) {
338     return edit; // Gw.0 -> Ew.0 for w > 0
339   }
340   decimal::ConversionToDecimalResult converted{
341       Convert(significantDigits, edit)};
342   if (IsInfOrNaN(converted)) {
343     return edit;
344   }
345   int expo{IsZero() ? 1 : converted.decimalExponent}; // 's'
346   if (expo < 0 || expo > significantDigits) {
347     return edit; // Ew.d
348   }
349   edit.descriptor = 'F';
350   edit.modes.scale = 0; // kP is ignored for G when no exponent field
351   trailingBlanks_ = 0;
352   int editWidth{edit.width.value_or(0)};
353   if (editWidth > 0) {
354     int expoDigits{edit.expoDigits.value_or(0)};
355     trailingBlanks_ = expoDigits > 0 ? expoDigits + 2 : 4; // 'n'
356     *edit.width = std::max(0, editWidth - trailingBlanks_);
357   }
358   if (edit.digits.has_value()) {
359     *edit.digits = std::max(0, *edit.digits - expo);
360   }
361   return edit;
362 }
363 
364 // 13.10.4 in F'2018
365 template <int binaryPrecision>
366 bool RealOutputEditing<binaryPrecision>::EditListDirectedOutput(
367     const DataEdit &edit) {
368   decimal::ConversionToDecimalResult converted{Convert(1, edit)};
369   if (IsInfOrNaN(converted)) {
370     return EditEorDOutput(edit);
371   }
372   int expo{converted.decimalExponent};
373   if (expo < 0 || expo > BinaryFloatingPoint::decimalPrecision) {
374     DataEdit copy{edit};
375     copy.modes.scale = 1; // 1P
376     return EditEorDOutput(copy);
377   }
378   return EditFOutput(edit);
379 }
380 
381 // 13.7.5.2.6 in F'2018
382 template <int binaryPrecision>
383 bool RealOutputEditing<binaryPrecision>::EditEXOutput(const DataEdit &) {
384   io_.GetIoErrorHandler().Crash(
385       "EX output editing is not yet implemented"); // TODO
386 }
387 
388 template <int KIND> bool RealOutputEditing<KIND>::Edit(const DataEdit &edit) {
389   switch (edit.descriptor) {
390   case 'D':
391     return EditEorDOutput(edit);
392   case 'E':
393     if (edit.variation == 'X') {
394       return EditEXOutput(edit);
395     } else {
396       return EditEorDOutput(edit);
397     }
398   case 'F':
399     return EditFOutput(edit);
400   case 'B':
401   case 'O':
402   case 'Z':
403     return EditIntegerOutput<KIND>(io_, edit,
404         static_cast<common::HostSignedIntType<8 * KIND>>(
405             decimal::BinaryFloatingPointNumber<binaryPrecision>{x_}.raw()));
406   case 'G':
407     return Edit(EditForGOutput(edit));
408   case 'A': // legacy extension
409     return EditDefaultCharacterOutput(
410         io_, edit, reinterpret_cast<char *>(&x_), sizeof x_);
411   default:
412     if (edit.IsListDirected()) {
413       return EditListDirectedOutput(edit);
414     }
415     io_.GetIoErrorHandler().SignalError(IostatErrorInFormat,
416         "Data edit descriptor '%c' may not be used with a REAL data item",
417         edit.descriptor);
418     return false;
419   }
420   return false;
421 }
422 
423 bool ListDirectedLogicalOutput(IoStatementState &io,
424     ListDirectedStatementState<Direction::Output> &list, bool truth) {
425   return list.EmitLeadingSpaceOrAdvance(io) && io.Emit(truth ? "T" : "F", 1);
426 }
427 
428 bool EditLogicalOutput(IoStatementState &io, const DataEdit &edit, bool truth) {
429   switch (edit.descriptor) {
430   case 'L':
431   case 'G':
432     return io.EmitRepeated(' ', std::max(0, edit.width.value_or(1) - 1)) &&
433         io.Emit(truth ? "T" : "F", 1);
434   default:
435     io.GetIoErrorHandler().SignalError(IostatErrorInFormat,
436         "Data edit descriptor '%c' may not be used with a LOGICAL data item",
437         edit.descriptor);
438     return false;
439   }
440 }
441 
442 bool ListDirectedDefaultCharacterOutput(IoStatementState &io,
443     ListDirectedStatementState<Direction::Output> &list, const char *x,
444     std::size_t length) {
445   bool ok{true};
446   MutableModes &modes{io.mutableModes()};
447   ConnectionState &connection{io.GetConnectionState()};
448   if (modes.delim) {
449     ok = ok && list.EmitLeadingSpaceOrAdvance(io);
450     // Value is delimited with ' or " marks, and interior
451     // instances of that character are doubled.
452     ok = ok && io.Emit(&modes.delim, 1);
453     auto EmitOne{[&](char ch) {
454       if (connection.NeedAdvance(1)) {
455         ok = ok && io.AdvanceRecord();
456       }
457       ok = ok && io.Emit(&ch, 1);
458     }};
459     for (std::size_t j{0}; j < length; ++j) {
460       // Doubled delimiters must be put on the same record
461       // in order to be acceptable as list-directed or NAMELIST
462       // input; however, this requirement is not always possible
463       // when the records have a fixed length, as is the case with
464       // internal output.  The standard is silent on what should
465       // happen, and no two extant Fortran implementations do
466       // the same thing when tested with this case.
467       // This runtime splits the doubled delimiters across
468       // two records for lack of a better alternative.
469       if (x[j] == modes.delim) {
470         EmitOne(x[j]);
471       }
472       EmitOne(x[j]);
473     }
474     EmitOne(modes.delim);
475   } else {
476     // Undelimited list-directed output
477     ok = ok &&
478         list.EmitLeadingSpaceOrAdvance(
479             io, length > 0 && !list.lastWasUndelimitedCharacter());
480     std::size_t put{0};
481     while (ok && put < length) {
482       auto chunk{std::min(length - put, connection.RemainingSpaceInRecord())};
483       ok = ok && io.Emit(x + put, chunk);
484       put += chunk;
485       if (put < length) {
486         ok = ok && io.AdvanceRecord() && io.Emit(" ", 1);
487       }
488     }
489     list.set_lastWasUndelimitedCharacter(true);
490   }
491   return ok;
492 }
493 
494 bool EditDefaultCharacterOutput(IoStatementState &io, const DataEdit &edit,
495     const char *x, std::size_t length) {
496   switch (edit.descriptor) {
497   case 'A':
498   case 'G':
499     break;
500   default:
501     io.GetIoErrorHandler().SignalError(IostatErrorInFormat,
502         "Data edit descriptor '%c' may not be used with a CHARACTER data item",
503         edit.descriptor);
504     return false;
505   }
506   int len{static_cast<int>(length)};
507   int width{edit.width.value_or(len)};
508   return io.EmitRepeated(' ', std::max(0, width - len)) &&
509       io.Emit(x, std::min(width, len));
510 }
511 
512 template bool EditIntegerOutput<1>(
513     IoStatementState &, const DataEdit &, std::int8_t);
514 template bool EditIntegerOutput<2>(
515     IoStatementState &, const DataEdit &, std::int16_t);
516 template bool EditIntegerOutput<4>(
517     IoStatementState &, const DataEdit &, std::int32_t);
518 template bool EditIntegerOutput<8>(
519     IoStatementState &, const DataEdit &, std::int64_t);
520 template bool EditIntegerOutput<16>(
521     IoStatementState &, const DataEdit &, common::int128_t);
522 
523 template class RealOutputEditing<2>;
524 template class RealOutputEditing<3>;
525 template class RealOutputEditing<4>;
526 template class RealOutputEditing<8>;
527 template class RealOutputEditing<10>;
528 // TODO: double/double
529 template class RealOutputEditing<16>;
530 } // namespace Fortran::runtime::io
531