1 //===-- runtime/character.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 "flang/Runtime/character.h"
10 #include "terminator.h"
11 #include "tools.h"
12 #include "flang/Common/bit-population-count.h"
13 #include "flang/Common/uint128.h"
14 #include "flang/Runtime/cpp-type.h"
15 #include "flang/Runtime/descriptor.h"
16 #include <algorithm>
17 #include <cstring>
18 
19 namespace Fortran::runtime {
20 
21 template <typename CHAR>
CompareToBlankPadding(const CHAR * x,std::size_t chars)22 inline int CompareToBlankPadding(const CHAR *x, std::size_t chars) {
23   for (; chars-- > 0; ++x) {
24     if (*x < ' ') {
25       return -1;
26     }
27     if (*x > ' ') {
28       return 1;
29     }
30   }
31   return 0;
32 }
33 
34 template <typename CHAR>
CharacterScalarCompare(const CHAR * x,const CHAR * y,std::size_t xChars,std::size_t yChars)35 int CharacterScalarCompare(
36     const CHAR *x, const CHAR *y, std::size_t xChars, std::size_t yChars) {
37   auto minChars{std::min(xChars, yChars)};
38   if constexpr (sizeof(CHAR) == 1) {
39     // don't use for kind=2 or =4, that would fail on little-endian machines
40     int cmp{std::memcmp(x, y, minChars)};
41     if (cmp < 0) {
42       return -1;
43     }
44     if (cmp > 0) {
45       return 1;
46     }
47     if (xChars == yChars) {
48       return 0;
49     }
50     x += minChars;
51     y += minChars;
52   } else {
53     for (std::size_t n{minChars}; n-- > 0; ++x, ++y) {
54       if (*x < *y) {
55         return -1;
56       }
57       if (*x > *y) {
58         return 1;
59       }
60     }
61   }
62   if (int cmp{CompareToBlankPadding(x, xChars - minChars)}) {
63     return cmp;
64   }
65   return -CompareToBlankPadding(y, yChars - minChars);
66 }
67 
68 template int CharacterScalarCompare<char>(
69     const char *x, const char *y, std::size_t xChars, std::size_t yChars);
70 template int CharacterScalarCompare<char16_t>(const char16_t *x,
71     const char16_t *y, std::size_t xChars, std::size_t yChars);
72 template int CharacterScalarCompare<char32_t>(const char32_t *x,
73     const char32_t *y, std::size_t xChars, std::size_t yChars);
74 
75 // Shift count to use when converting between character lengths
76 // and byte counts.
77 template <typename CHAR>
78 constexpr int shift{common::TrailingZeroBitCount(sizeof(CHAR))};
79 
80 template <typename CHAR>
Compare(Descriptor & result,const Descriptor & x,const Descriptor & y,const Terminator & terminator)81 static void Compare(Descriptor &result, const Descriptor &x,
82     const Descriptor &y, const Terminator &terminator) {
83   RUNTIME_CHECK(
84       terminator, x.rank() == y.rank() || x.rank() == 0 || y.rank() == 0);
85   int rank{std::max(x.rank(), y.rank())};
86   SubscriptValue ub[maxRank], xAt[maxRank], yAt[maxRank];
87   SubscriptValue elements{1};
88   for (int j{0}; j < rank; ++j) {
89     if (x.rank() > 0 && y.rank() > 0) {
90       SubscriptValue xUB{x.GetDimension(j).Extent()};
91       SubscriptValue yUB{y.GetDimension(j).Extent()};
92       if (xUB != yUB) {
93         terminator.Crash("Character array comparison: operands are not "
94                          "conforming on dimension %d (%jd != %jd)",
95             j + 1, static_cast<std::intmax_t>(xUB),
96             static_cast<std::intmax_t>(yUB));
97       }
98       ub[j] = xUB;
99     } else {
100       ub[j] = (x.rank() ? x : y).GetDimension(j).Extent();
101     }
102     elements *= ub[j];
103   }
104   x.GetLowerBounds(xAt);
105   y.GetLowerBounds(yAt);
106   result.Establish(
107       TypeCategory::Logical, 1, nullptr, rank, ub, CFI_attribute_allocatable);
108   for (int j{0}; j < rank; ++j) {
109     result.GetDimension(j).SetBounds(1, ub[j]);
110   }
111   if (result.Allocate() != CFI_SUCCESS) {
112     terminator.Crash("Compare: could not allocate storage for result");
113   }
114   std::size_t xChars{x.ElementBytes() >> shift<CHAR>};
115   std::size_t yChars{y.ElementBytes() >> shift<char>};
116   for (SubscriptValue resultAt{0}; elements-- > 0;
117        ++resultAt, x.IncrementSubscripts(xAt), y.IncrementSubscripts(yAt)) {
118     *result.OffsetElement<char>(resultAt) = CharacterScalarCompare<CHAR>(
119         x.Element<CHAR>(xAt), y.Element<CHAR>(yAt), xChars, yChars);
120   }
121 }
122 
123 template <typename CHAR, bool ADJUSTR>
Adjust(CHAR * to,const CHAR * from,std::size_t chars)124 static void Adjust(CHAR *to, const CHAR *from, std::size_t chars) {
125   if constexpr (ADJUSTR) {
126     std::size_t j{chars}, k{chars};
127     for (; k > 0 && from[k - 1] == ' '; --k) {
128     }
129     while (k > 0) {
130       to[--j] = from[--k];
131     }
132     while (j > 0) {
133       to[--j] = ' ';
134     }
135   } else { // ADJUSTL
136     std::size_t j{0}, k{0};
137     for (; k < chars && from[k] == ' '; ++k) {
138     }
139     while (k < chars) {
140       to[j++] = from[k++];
141     }
142     while (j < chars) {
143       to[j++] = ' ';
144     }
145   }
146 }
147 
148 template <typename CHAR, bool ADJUSTR>
AdjustLRHelper(Descriptor & result,const Descriptor & string,const Terminator & terminator)149 static void AdjustLRHelper(Descriptor &result, const Descriptor &string,
150     const Terminator &terminator) {
151   int rank{string.rank()};
152   SubscriptValue ub[maxRank], stringAt[maxRank];
153   SubscriptValue elements{1};
154   for (int j{0}; j < rank; ++j) {
155     ub[j] = string.GetDimension(j).Extent();
156     elements *= ub[j];
157     stringAt[j] = 1;
158   }
159   string.GetLowerBounds(stringAt);
160   std::size_t elementBytes{string.ElementBytes()};
161   result.Establish(string.type(), elementBytes, nullptr, rank, ub,
162       CFI_attribute_allocatable);
163   for (int j{0}; j < rank; ++j) {
164     result.GetDimension(j).SetBounds(1, ub[j]);
165   }
166   if (result.Allocate() != CFI_SUCCESS) {
167     terminator.Crash("ADJUSTL/R: could not allocate storage for result");
168   }
169   for (SubscriptValue resultAt{0}; elements-- > 0;
170        resultAt += elementBytes, string.IncrementSubscripts(stringAt)) {
171     Adjust<CHAR, ADJUSTR>(result.OffsetElement<CHAR>(resultAt),
172         string.Element<const CHAR>(stringAt), elementBytes >> shift<CHAR>);
173   }
174 }
175 
176 template <bool ADJUSTR>
AdjustLR(Descriptor & result,const Descriptor & string,const char * sourceFile,int sourceLine)177 void AdjustLR(Descriptor &result, const Descriptor &string,
178     const char *sourceFile, int sourceLine) {
179   Terminator terminator{sourceFile, sourceLine};
180   switch (string.raw().type) {
181   case CFI_type_char:
182     AdjustLRHelper<char, ADJUSTR>(result, string, terminator);
183     break;
184   case CFI_type_char16_t:
185     AdjustLRHelper<char16_t, ADJUSTR>(result, string, terminator);
186     break;
187   case CFI_type_char32_t:
188     AdjustLRHelper<char32_t, ADJUSTR>(result, string, terminator);
189     break;
190   default:
191     terminator.Crash("ADJUSTL/R: bad string type code %d",
192         static_cast<int>(string.raw().type));
193   }
194 }
195 
196 template <typename CHAR>
LenTrim(const CHAR * x,std::size_t chars)197 inline std::size_t LenTrim(const CHAR *x, std::size_t chars) {
198   while (chars > 0 && x[chars - 1] == ' ') {
199     --chars;
200   }
201   return chars;
202 }
203 
204 template <typename INT, typename CHAR>
LenTrim(Descriptor & result,const Descriptor & string,const Terminator & terminator)205 static void LenTrim(Descriptor &result, const Descriptor &string,
206     const Terminator &terminator) {
207   int rank{string.rank()};
208   SubscriptValue ub[maxRank], stringAt[maxRank];
209   SubscriptValue elements{1};
210   for (int j{0}; j < rank; ++j) {
211     ub[j] = string.GetDimension(j).Extent();
212     elements *= ub[j];
213   }
214   string.GetLowerBounds(stringAt);
215   result.Establish(TypeCategory::Integer, sizeof(INT), nullptr, rank, ub,
216       CFI_attribute_allocatable);
217   for (int j{0}; j < rank; ++j) {
218     result.GetDimension(j).SetBounds(1, ub[j]);
219   }
220   if (result.Allocate() != CFI_SUCCESS) {
221     terminator.Crash("LEN_TRIM: could not allocate storage for result");
222   }
223   std::size_t stringElementChars{string.ElementBytes() >> shift<CHAR>};
224   for (SubscriptValue resultAt{0}; elements-- > 0;
225        resultAt += sizeof(INT), string.IncrementSubscripts(stringAt)) {
226     *result.OffsetElement<INT>(resultAt) =
227         LenTrim(string.Element<CHAR>(stringAt), stringElementChars);
228   }
229 }
230 
231 template <typename CHAR>
LenTrimKind(Descriptor & result,const Descriptor & string,int kind,const Terminator & terminator)232 static void LenTrimKind(Descriptor &result, const Descriptor &string, int kind,
233     const Terminator &terminator) {
234   switch (kind) {
235   case 1:
236     LenTrim<CppTypeFor<TypeCategory::Integer, 1>, CHAR>(
237         result, string, terminator);
238     break;
239   case 2:
240     LenTrim<CppTypeFor<TypeCategory::Integer, 2>, CHAR>(
241         result, string, terminator);
242     break;
243   case 4:
244     LenTrim<CppTypeFor<TypeCategory::Integer, 4>, CHAR>(
245         result, string, terminator);
246     break;
247   case 8:
248     LenTrim<CppTypeFor<TypeCategory::Integer, 8>, CHAR>(
249         result, string, terminator);
250     break;
251   case 16:
252     LenTrim<CppTypeFor<TypeCategory::Integer, 16>, CHAR>(
253         result, string, terminator);
254     break;
255   default:
256     terminator.Crash("not yet implemented: LEN_TRIM: KIND=%d", kind);
257   }
258 }
259 
260 // INDEX implementation
261 template <typename CHAR>
Index(const CHAR * x,std::size_t xLen,const CHAR * want,std::size_t wantLen,bool back)262 inline std::size_t Index(const CHAR *x, std::size_t xLen, const CHAR *want,
263     std::size_t wantLen, bool back) {
264   if (xLen < wantLen) {
265     return 0;
266   }
267   if (xLen == 0) {
268     return 1; // wantLen is also 0, so trivial match
269   }
270   if (back) {
271     // If wantLen==0, returns xLen + 1 per standard (and all other compilers)
272     std::size_t at{xLen - wantLen + 1};
273     for (; at > 0; --at) {
274       std::size_t j{1};
275       for (; j <= wantLen; ++j) {
276         if (x[at + j - 2] != want[j - 1]) {
277           break;
278         }
279       }
280       if (j > wantLen) {
281         return at;
282       }
283     }
284     return 0;
285   }
286   // Non-trivial forward substring search: use a simplified form of
287   // Boyer-Moore substring searching.
288   for (std::size_t at{1}; at + wantLen - 1 <= xLen;) {
289     // Compare x(at:at+wantLen-1) with want(1:wantLen).
290     // The comparison proceeds from the ends of the substrings forward
291     // so that we can skip ahead by multiple positions on a miss.
292     std::size_t j{wantLen};
293     CHAR ch;
294     for (; j > 0; --j) {
295       ch = x[at + j - 2];
296       if (ch != want[j - 1]) {
297         break;
298       }
299     }
300     if (j == 0) {
301       return at; // found a match
302     }
303     // Suppose we have at==2:
304     // "THAT FORTRAN THAT I RAN" <- the string (x) in which we search
305     //   "THAT I RAN"            <- the string (want) for which we search
306     //          ^------------------ j==7, ch=='T'
307     // We can shift ahead 3 positions to at==5 to align the 'T's:
308     // "THAT FORTRAN THAT I RAN"
309     //      "THAT I RAN"
310     std::size_t shift{1};
311     for (; shift < j; ++shift) {
312       if (want[j - shift - 1] == ch) {
313         break;
314       }
315     }
316     at += shift;
317   }
318   return 0;
319 }
320 
321 // SCAN and VERIFY implementation help.  These intrinsic functions
322 // do pretty much the same thing, so they're templatized with a
323 // distinguishing flag.
324 
325 enum class CharFunc { Index, Scan, Verify };
326 
327 template <typename CHAR, CharFunc FUNC>
ScanVerify(const CHAR * x,std::size_t xLen,const CHAR * set,std::size_t setLen,bool back)328 inline std::size_t ScanVerify(const CHAR *x, std::size_t xLen, const CHAR *set,
329     std::size_t setLen, bool back) {
330   std::size_t at{back ? xLen : 1};
331   int increment{back ? -1 : 1};
332   for (; xLen-- > 0; at += increment) {
333     CHAR ch{x[at - 1]};
334     bool inSet{false};
335     // TODO: If set is sorted, could use binary search
336     for (std::size_t j{0}; j < setLen; ++j) {
337       if (set[j] == ch) {
338         inSet = true;
339         break;
340       }
341     }
342     if (inSet != (FUNC == CharFunc::Verify)) {
343       return at;
344     }
345   }
346   return 0;
347 }
348 
349 // Specialization for one-byte characters
350 template <bool IS_VERIFY = false>
ScanVerify(const char * x,std::size_t xLen,const char * set,std::size_t setLen,bool back)351 inline std::size_t ScanVerify(const char *x, std::size_t xLen, const char *set,
352     std::size_t setLen, bool back) {
353   std::size_t at{back ? xLen : 1};
354   int increment{back ? -1 : 1};
355   if (xLen > 0) {
356     std::uint64_t bitSet[256 / 64]{0};
357     std::uint64_t one{1};
358     for (std::size_t j{0}; j < setLen; ++j) {
359       unsigned setCh{static_cast<unsigned char>(set[j])};
360       bitSet[setCh / 64] |= one << (setCh % 64);
361     }
362     for (; xLen-- > 0; at += increment) {
363       unsigned ch{static_cast<unsigned char>(x[at - 1])};
364       bool inSet{((bitSet[ch / 64] >> (ch % 64)) & 1) != 0};
365       if (inSet != IS_VERIFY) {
366         return at;
367       }
368     }
369   }
370   return 0;
371 }
372 
373 template <typename INT, typename CHAR, CharFunc FUNC>
GeneralCharFunc(Descriptor & result,const Descriptor & string,const Descriptor & arg,const Descriptor * back,const Terminator & terminator)374 static void GeneralCharFunc(Descriptor &result, const Descriptor &string,
375     const Descriptor &arg, const Descriptor *back,
376     const Terminator &terminator) {
377   int rank{string.rank() ? string.rank()
378           : arg.rank()   ? arg.rank()
379           : back         ? back->rank()
380                          : 0};
381   SubscriptValue ub[maxRank], stringAt[maxRank], argAt[maxRank],
382       backAt[maxRank];
383   SubscriptValue elements{1};
384   for (int j{0}; j < rank; ++j) {
385     ub[j] = string.rank() ? string.GetDimension(j).Extent()
386         : arg.rank()      ? arg.GetDimension(j).Extent()
387         : back            ? back->GetDimension(j).Extent()
388                           : 1;
389     elements *= ub[j];
390   }
391   string.GetLowerBounds(stringAt);
392   arg.GetLowerBounds(argAt);
393   if (back) {
394     back->GetLowerBounds(backAt);
395   }
396   result.Establish(TypeCategory::Integer, sizeof(INT), nullptr, rank, ub,
397       CFI_attribute_allocatable);
398   for (int j{0}; j < rank; ++j) {
399     result.GetDimension(j).SetBounds(1, ub[j]);
400   }
401   if (result.Allocate() != CFI_SUCCESS) {
402     terminator.Crash("SCAN/VERIFY: could not allocate storage for result");
403   }
404   std::size_t stringElementChars{string.ElementBytes() >> shift<CHAR>};
405   std::size_t argElementChars{arg.ElementBytes() >> shift<CHAR>};
406   for (SubscriptValue resultAt{0}; elements-- > 0; resultAt += sizeof(INT),
407        string.IncrementSubscripts(stringAt), arg.IncrementSubscripts(argAt),
408        back && back->IncrementSubscripts(backAt)) {
409     if constexpr (FUNC == CharFunc::Index) {
410       *result.OffsetElement<INT>(resultAt) =
411           Index<CHAR>(string.Element<CHAR>(stringAt), stringElementChars,
412               arg.Element<CHAR>(argAt), argElementChars,
413               back && IsLogicalElementTrue(*back, backAt));
414     } else if constexpr (FUNC == CharFunc::Scan) {
415       *result.OffsetElement<INT>(resultAt) =
416           ScanVerify<CHAR, CharFunc::Scan>(string.Element<CHAR>(stringAt),
417               stringElementChars, arg.Element<CHAR>(argAt), argElementChars,
418               back && IsLogicalElementTrue(*back, backAt));
419     } else if constexpr (FUNC == CharFunc::Verify) {
420       *result.OffsetElement<INT>(resultAt) =
421           ScanVerify<CHAR, CharFunc::Verify>(string.Element<CHAR>(stringAt),
422               stringElementChars, arg.Element<CHAR>(argAt), argElementChars,
423               back && IsLogicalElementTrue(*back, backAt));
424     } else {
425       static_assert(FUNC == CharFunc::Index || FUNC == CharFunc::Scan ||
426           FUNC == CharFunc::Verify);
427     }
428   }
429 }
430 
431 template <typename CHAR, CharFunc FUNC>
GeneralCharFuncKind(Descriptor & result,const Descriptor & string,const Descriptor & arg,const Descriptor * back,int kind,const Terminator & terminator)432 static void GeneralCharFuncKind(Descriptor &result, const Descriptor &string,
433     const Descriptor &arg, const Descriptor *back, int kind,
434     const Terminator &terminator) {
435   switch (kind) {
436   case 1:
437     GeneralCharFunc<CppTypeFor<TypeCategory::Integer, 1>, CHAR, FUNC>(
438         result, string, arg, back, terminator);
439     break;
440   case 2:
441     GeneralCharFunc<CppTypeFor<TypeCategory::Integer, 2>, CHAR, FUNC>(
442         result, string, arg, back, terminator);
443     break;
444   case 4:
445     GeneralCharFunc<CppTypeFor<TypeCategory::Integer, 4>, CHAR, FUNC>(
446         result, string, arg, back, terminator);
447     break;
448   case 8:
449     GeneralCharFunc<CppTypeFor<TypeCategory::Integer, 8>, CHAR, FUNC>(
450         result, string, arg, back, terminator);
451     break;
452   case 16:
453     GeneralCharFunc<CppTypeFor<TypeCategory::Integer, 16>, CHAR, FUNC>(
454         result, string, arg, back, terminator);
455     break;
456   default:
457     terminator.Crash("not yet implemented: INDEX/SCAN/VERIFY: KIND=%d", kind);
458   }
459 }
460 
461 template <typename TO, typename FROM>
CopyAndPad(TO * to,const FROM * from,std::size_t toChars,std::size_t fromChars)462 static void CopyAndPad(
463     TO *to, const FROM *from, std::size_t toChars, std::size_t fromChars) {
464   if constexpr (sizeof(TO) != sizeof(FROM)) {
465     std::size_t copyChars{std::min(toChars, fromChars)};
466     for (std::size_t j{0}; j < copyChars; ++j) {
467       to[j] = from[j];
468     }
469     for (std::size_t j{copyChars}; j < toChars; ++j) {
470       to[j] = static_cast<TO>(' ');
471     }
472   } else if (toChars <= fromChars) {
473     std::memcpy(to, from, toChars * sizeof(TO));
474   } else {
475     std::memcpy(to, from, fromChars * sizeof(TO));
476     for (std::size_t j{fromChars}; j < toChars; ++j) {
477       to[j] = static_cast<TO>(' ');
478     }
479   }
480 }
481 
482 template <typename CHAR, bool ISMIN>
MaxMinHelper(Descriptor & accumulator,const Descriptor & x,const Terminator & terminator)483 static void MaxMinHelper(Descriptor &accumulator, const Descriptor &x,
484     const Terminator &terminator) {
485   RUNTIME_CHECK(terminator,
486       accumulator.rank() == 0 || x.rank() == 0 ||
487           accumulator.rank() == x.rank());
488   SubscriptValue ub[maxRank], xAt[maxRank];
489   SubscriptValue elements{1};
490   std::size_t accumChars{accumulator.ElementBytes() >> shift<CHAR>};
491   std::size_t xChars{x.ElementBytes() >> shift<CHAR>};
492   std::size_t chars{std::max(accumChars, xChars)};
493   bool reallocate{accumulator.raw().base_addr == nullptr ||
494       accumChars != chars || (accumulator.rank() == 0 && x.rank() > 0)};
495   int rank{std::max(accumulator.rank(), x.rank())};
496   for (int j{0}; j < rank; ++j) {
497     if (x.rank() > 0) {
498       ub[j] = x.GetDimension(j).Extent();
499       if (accumulator.rank() > 0) {
500         SubscriptValue accumExt{accumulator.GetDimension(j).Extent()};
501         if (accumExt != ub[j]) {
502           terminator.Crash("Character MAX/MIN: operands are not "
503                            "conforming on dimension %d (%jd != %jd)",
504               j + 1, static_cast<std::intmax_t>(accumExt),
505               static_cast<std::intmax_t>(ub[j]));
506         }
507       }
508     } else {
509       ub[j] = accumulator.GetDimension(j).Extent();
510     }
511     elements *= ub[j];
512   }
513   x.GetLowerBounds(xAt);
514   void *old{nullptr};
515   const CHAR *accumData{accumulator.OffsetElement<CHAR>()};
516   if (reallocate) {
517     old = accumulator.raw().base_addr;
518     accumulator.set_base_addr(nullptr);
519     accumulator.raw().elem_len = chars << shift<CHAR>;
520     for (int j{0}; j < rank; ++j) {
521       accumulator.GetDimension(j).SetBounds(1, ub[j]);
522     }
523     RUNTIME_CHECK(terminator, accumulator.Allocate() == CFI_SUCCESS);
524   }
525   for (CHAR *result{accumulator.OffsetElement<CHAR>()}; elements-- > 0;
526        accumData += accumChars, result += chars, x.IncrementSubscripts(xAt)) {
527     const CHAR *xData{x.Element<CHAR>(xAt)};
528     int cmp{CharacterScalarCompare(accumData, xData, accumChars, xChars)};
529     if constexpr (ISMIN) {
530       cmp = -cmp;
531     }
532     if (cmp < 0) {
533       CopyAndPad(result, xData, chars, xChars);
534     } else if (result != accumData) {
535       CopyAndPad(result, accumData, chars, accumChars);
536     }
537   }
538   FreeMemory(old);
539 }
540 
541 template <bool ISMIN>
MaxMin(Descriptor & accumulator,const Descriptor & x,const char * sourceFile,int sourceLine)542 static void MaxMin(Descriptor &accumulator, const Descriptor &x,
543     const char *sourceFile, int sourceLine) {
544   Terminator terminator{sourceFile, sourceLine};
545   RUNTIME_CHECK(terminator, accumulator.raw().type == x.raw().type);
546   switch (accumulator.raw().type) {
547   case CFI_type_char:
548     MaxMinHelper<char, ISMIN>(accumulator, x, terminator);
549     break;
550   case CFI_type_char16_t:
551     MaxMinHelper<char16_t, ISMIN>(accumulator, x, terminator);
552     break;
553   case CFI_type_char32_t:
554     MaxMinHelper<char32_t, ISMIN>(accumulator, x, terminator);
555     break;
556   default:
557     terminator.Crash(
558         "Character MAX/MIN: result does not have a character type");
559   }
560 }
561 
562 extern "C" {
563 
RTNAME(CharacterConcatenate)564 void RTNAME(CharacterConcatenate)(Descriptor &accumulator,
565     const Descriptor &from, const char *sourceFile, int sourceLine) {
566   Terminator terminator{sourceFile, sourceLine};
567   RUNTIME_CHECK(terminator,
568       accumulator.rank() == 0 || from.rank() == 0 ||
569           accumulator.rank() == from.rank());
570   int rank{std::max(accumulator.rank(), from.rank())};
571   SubscriptValue ub[maxRank], fromAt[maxRank];
572   SubscriptValue elements{1};
573   for (int j{0}; j < rank; ++j) {
574     if (accumulator.rank() > 0 && from.rank() > 0) {
575       ub[j] = accumulator.GetDimension(j).Extent();
576       SubscriptValue fromUB{from.GetDimension(j).Extent()};
577       if (ub[j] != fromUB) {
578         terminator.Crash("Character array concatenation: operands are not "
579                          "conforming on dimension %d (%jd != %jd)",
580             j + 1, static_cast<std::intmax_t>(ub[j]),
581             static_cast<std::intmax_t>(fromUB));
582       }
583     } else {
584       ub[j] =
585           (accumulator.rank() ? accumulator : from).GetDimension(j).Extent();
586     }
587     elements *= ub[j];
588   }
589   std::size_t oldBytes{accumulator.ElementBytes()};
590   void *old{accumulator.raw().base_addr};
591   accumulator.set_base_addr(nullptr);
592   std::size_t fromBytes{from.ElementBytes()};
593   accumulator.raw().elem_len += fromBytes;
594   std::size_t newBytes{accumulator.ElementBytes()};
595   for (int j{0}; j < rank; ++j) {
596     accumulator.GetDimension(j).SetBounds(1, ub[j]);
597   }
598   if (accumulator.Allocate() != CFI_SUCCESS) {
599     terminator.Crash(
600         "CharacterConcatenate: could not allocate storage for result");
601   }
602   const char *p{static_cast<const char *>(old)};
603   char *to{static_cast<char *>(accumulator.raw().base_addr)};
604   from.GetLowerBounds(fromAt);
605   for (; elements-- > 0;
606        to += newBytes, p += oldBytes, from.IncrementSubscripts(fromAt)) {
607     std::memcpy(to, p, oldBytes);
608     std::memcpy(to + oldBytes, from.Element<char>(fromAt), fromBytes);
609   }
610   FreeMemory(old);
611 }
612 
RTNAME(CharacterConcatenateScalar1)613 void RTNAME(CharacterConcatenateScalar1)(
614     Descriptor &accumulator, const char *from, std::size_t chars) {
615   Terminator terminator{__FILE__, __LINE__};
616   RUNTIME_CHECK(terminator, accumulator.rank() == 0);
617   void *old{accumulator.raw().base_addr};
618   accumulator.set_base_addr(nullptr);
619   std::size_t oldLen{accumulator.ElementBytes()};
620   accumulator.raw().elem_len += chars;
621   RUNTIME_CHECK(terminator, accumulator.Allocate() == CFI_SUCCESS);
622   std::memcpy(accumulator.OffsetElement<char>(oldLen), from, chars);
623   FreeMemory(old);
624 }
625 
RTNAME(CharacterAssign)626 void RTNAME(CharacterAssign)(Descriptor &lhs, const Descriptor &rhs,
627     const char *sourceFile, int sourceLine) {
628   Terminator terminator{sourceFile, sourceLine};
629   int rank{lhs.rank()};
630   RUNTIME_CHECK(terminator, rhs.rank() == 0 || rhs.rank() == rank);
631   SubscriptValue ub[maxRank], lhsAt[maxRank], rhsAt[maxRank];
632   SubscriptValue elements{1};
633   std::size_t lhsBytes{lhs.ElementBytes()};
634   std::size_t rhsBytes{rhs.ElementBytes()};
635   bool reallocate{lhs.IsAllocatable() &&
636       (lhs.raw().base_addr == nullptr || lhsBytes != rhsBytes)};
637   for (int j{0}; j < rank; ++j) {
638     lhsAt[j] = lhs.GetDimension(j).LowerBound();
639     if (rhs.rank() > 0) {
640       SubscriptValue lhsExt{lhs.GetDimension(j).Extent()};
641       SubscriptValue rhsExt{rhs.GetDimension(j).Extent()};
642       ub[j] = lhsAt[j] + rhsExt - 1;
643       if (lhsExt != rhsExt) {
644         if (lhs.IsAllocatable()) {
645           reallocate = true;
646         } else {
647           terminator.Crash("Character array assignment: operands are not "
648                            "conforming on dimension %d (%jd != %jd)",
649               j + 1, static_cast<std::intmax_t>(lhsExt),
650               static_cast<std::intmax_t>(rhsExt));
651         }
652       }
653       rhsAt[j] = rhs.GetDimension(j).LowerBound();
654     } else {
655       ub[j] = lhs.GetDimension(j).UpperBound();
656     }
657     elements *= ub[j] - lhsAt[j] + 1;
658   }
659   void *old{nullptr};
660   if (reallocate) {
661     old = lhs.raw().base_addr;
662     lhs.set_base_addr(nullptr);
663     lhs.raw().elem_len = lhsBytes = rhsBytes;
664     if (rhs.rank() > 0) {
665       // When the RHS is not scalar, the LHS acquires its bounds.
666       for (int j{0}; j < rank; ++j) {
667         lhsAt[j] = rhsAt[j];
668         ub[j] = rhs.GetDimension(j).UpperBound();
669         lhs.GetDimension(j).SetBounds(lhsAt[j], ub[j]);
670       }
671     }
672     RUNTIME_CHECK(terminator, lhs.Allocate() == CFI_SUCCESS);
673   }
674   switch (lhs.raw().type) {
675   case CFI_type_char:
676     switch (rhs.raw().type) {
677     case CFI_type_char:
678       for (; elements-- > 0;
679            lhs.IncrementSubscripts(lhsAt), rhs.IncrementSubscripts(rhsAt)) {
680         CopyAndPad(lhs.Element<char>(lhsAt), rhs.Element<char>(rhsAt), lhsBytes,
681             rhsBytes);
682       }
683       break;
684     case CFI_type_char16_t:
685       for (; elements-- > 0;
686            lhs.IncrementSubscripts(lhsAt), rhs.IncrementSubscripts(rhsAt)) {
687         CopyAndPad(lhs.Element<char>(lhsAt), rhs.Element<char16_t>(rhsAt),
688             lhsBytes, rhsBytes >> 1);
689       }
690       break;
691     case CFI_type_char32_t:
692       for (; elements-- > 0;
693            lhs.IncrementSubscripts(lhsAt), rhs.IncrementSubscripts(rhsAt)) {
694         CopyAndPad(lhs.Element<char>(lhsAt), rhs.Element<char32_t>(rhsAt),
695             lhsBytes, rhsBytes >> 2);
696       }
697       break;
698     default:
699       terminator.Crash(
700           "RHS of character assignment does not have a character type");
701     }
702     break;
703   case CFI_type_char16_t:
704     switch (rhs.raw().type) {
705     case CFI_type_char:
706       for (; elements-- > 0;
707            lhs.IncrementSubscripts(lhsAt), rhs.IncrementSubscripts(rhsAt)) {
708         CopyAndPad(lhs.Element<char16_t>(lhsAt), rhs.Element<char>(rhsAt),
709             lhsBytes >> 1, rhsBytes);
710       }
711       break;
712     case CFI_type_char16_t:
713       for (; elements-- > 0;
714            lhs.IncrementSubscripts(lhsAt), rhs.IncrementSubscripts(rhsAt)) {
715         CopyAndPad(lhs.Element<char16_t>(lhsAt), rhs.Element<char16_t>(rhsAt),
716             lhsBytes >> 1, rhsBytes >> 1);
717       }
718       break;
719     case CFI_type_char32_t:
720       for (; elements-- > 0;
721            lhs.IncrementSubscripts(lhsAt), rhs.IncrementSubscripts(rhsAt)) {
722         CopyAndPad(lhs.Element<char16_t>(lhsAt), rhs.Element<char32_t>(rhsAt),
723             lhsBytes >> 1, rhsBytes >> 2);
724       }
725       break;
726     default:
727       terminator.Crash(
728           "RHS of character assignment does not have a character type");
729     }
730     break;
731   case CFI_type_char32_t:
732     switch (rhs.raw().type) {
733     case CFI_type_char:
734       for (; elements-- > 0;
735            lhs.IncrementSubscripts(lhsAt), rhs.IncrementSubscripts(rhsAt)) {
736         CopyAndPad(lhs.Element<char32_t>(lhsAt), rhs.Element<char>(rhsAt),
737             lhsBytes >> 2, rhsBytes);
738       }
739       break;
740     case CFI_type_char16_t:
741       for (; elements-- > 0;
742            lhs.IncrementSubscripts(lhsAt), rhs.IncrementSubscripts(rhsAt)) {
743         CopyAndPad(lhs.Element<char32_t>(lhsAt), rhs.Element<char16_t>(rhsAt),
744             lhsBytes >> 2, rhsBytes >> 1);
745       }
746       break;
747     case CFI_type_char32_t:
748       for (; elements-- > 0;
749            lhs.IncrementSubscripts(lhsAt), rhs.IncrementSubscripts(rhsAt)) {
750         CopyAndPad(lhs.Element<char32_t>(lhsAt), rhs.Element<char32_t>(rhsAt),
751             lhsBytes >> 2, rhsBytes >> 2);
752       }
753       break;
754     default:
755       terminator.Crash(
756           "RHS of character assignment does not have a character type");
757     }
758     break;
759   default:
760     terminator.Crash(
761         "LHS of character assignment does not have a character type");
762   }
763   if (reallocate) {
764     FreeMemory(old);
765   }
766 }
767 
RTNAME(CharacterCompareScalar)768 int RTNAME(CharacterCompareScalar)(const Descriptor &x, const Descriptor &y) {
769   Terminator terminator{__FILE__, __LINE__};
770   RUNTIME_CHECK(terminator, x.rank() == 0);
771   RUNTIME_CHECK(terminator, y.rank() == 0);
772   RUNTIME_CHECK(terminator, x.raw().type == y.raw().type);
773   switch (x.raw().type) {
774   case CFI_type_char:
775     return CharacterScalarCompare<char>(x.OffsetElement<char>(),
776         y.OffsetElement<char>(), x.ElementBytes(), y.ElementBytes());
777   case CFI_type_char16_t:
778     return CharacterScalarCompare<char16_t>(x.OffsetElement<char16_t>(),
779         y.OffsetElement<char16_t>(), x.ElementBytes() >> 1,
780         y.ElementBytes() >> 1);
781   case CFI_type_char32_t:
782     return CharacterScalarCompare<char32_t>(x.OffsetElement<char32_t>(),
783         y.OffsetElement<char32_t>(), x.ElementBytes() >> 2,
784         y.ElementBytes() >> 2);
785   default:
786     terminator.Crash("CharacterCompareScalar: bad string type code %d",
787         static_cast<int>(x.raw().type));
788   }
789   return 0;
790 }
791 
RTNAME(CharacterCompareScalar1)792 int RTNAME(CharacterCompareScalar1)(
793     const char *x, const char *y, std::size_t xChars, std::size_t yChars) {
794   return CharacterScalarCompare(x, y, xChars, yChars);
795 }
796 
RTNAME(CharacterCompareScalar2)797 int RTNAME(CharacterCompareScalar2)(const char16_t *x, const char16_t *y,
798     std::size_t xChars, std::size_t yChars) {
799   return CharacterScalarCompare(x, y, xChars, yChars);
800 }
801 
RTNAME(CharacterCompareScalar4)802 int RTNAME(CharacterCompareScalar4)(const char32_t *x, const char32_t *y,
803     std::size_t xChars, std::size_t yChars) {
804   return CharacterScalarCompare(x, y, xChars, yChars);
805 }
806 
RTNAME(CharacterCompare)807 void RTNAME(CharacterCompare)(
808     Descriptor &result, const Descriptor &x, const Descriptor &y) {
809   Terminator terminator{__FILE__, __LINE__};
810   RUNTIME_CHECK(terminator, x.raw().type == y.raw().type);
811   switch (x.raw().type) {
812   case CFI_type_char:
813     Compare<char>(result, x, y, terminator);
814     break;
815   case CFI_type_char16_t:
816     Compare<char16_t>(result, x, y, terminator);
817     break;
818   case CFI_type_char32_t:
819     Compare<char32_t>(result, x, y, terminator);
820     break;
821   default:
822     terminator.Crash("CharacterCompareScalar: bad string type code %d",
823         static_cast<int>(x.raw().type));
824   }
825 }
826 
RTNAME(CharacterAppend1)827 std::size_t RTNAME(CharacterAppend1)(char *lhs, std::size_t lhsBytes,
828     std::size_t offset, const char *rhs, std::size_t rhsBytes) {
829   if (auto n{std::min(lhsBytes - offset, rhsBytes)}) {
830     std::memcpy(lhs + offset, rhs, n);
831     offset += n;
832   }
833   return offset;
834 }
835 
RTNAME(CharacterPad1)836 void RTNAME(CharacterPad1)(char *lhs, std::size_t bytes, std::size_t offset) {
837   if (bytes > offset) {
838     std::memset(lhs + offset, ' ', bytes - offset);
839   }
840 }
841 
842 // Intrinsic function entry points
843 
RTNAME(Adjustl)844 void RTNAME(Adjustl)(Descriptor &result, const Descriptor &string,
845     const char *sourceFile, int sourceLine) {
846   AdjustLR<false>(result, string, sourceFile, sourceLine);
847 }
848 
RTNAME(Adjustr)849 void RTNAME(Adjustr)(Descriptor &result, const Descriptor &string,
850     const char *sourceFile, int sourceLine) {
851   AdjustLR<true>(result, string, sourceFile, sourceLine);
852 }
853 
RTNAME(Index1)854 std::size_t RTNAME(Index1)(const char *x, std::size_t xLen, const char *set,
855     std::size_t setLen, bool back) {
856   return Index<char>(x, xLen, set, setLen, back);
857 }
RTNAME(Index2)858 std::size_t RTNAME(Index2)(const char16_t *x, std::size_t xLen,
859     const char16_t *set, std::size_t setLen, bool back) {
860   return Index<char16_t>(x, xLen, set, setLen, back);
861 }
RTNAME(Index4)862 std::size_t RTNAME(Index4)(const char32_t *x, std::size_t xLen,
863     const char32_t *set, std::size_t setLen, bool back) {
864   return Index<char32_t>(x, xLen, set, setLen, back);
865 }
866 
RTNAME(Index)867 void RTNAME(Index)(Descriptor &result, const Descriptor &string,
868     const Descriptor &substring, const Descriptor *back, int kind,
869     const char *sourceFile, int sourceLine) {
870   Terminator terminator{sourceFile, sourceLine};
871   switch (string.raw().type) {
872   case CFI_type_char:
873     GeneralCharFuncKind<char, CharFunc::Index>(
874         result, string, substring, back, kind, terminator);
875     break;
876   case CFI_type_char16_t:
877     GeneralCharFuncKind<char16_t, CharFunc::Index>(
878         result, string, substring, back, kind, terminator);
879     break;
880   case CFI_type_char32_t:
881     GeneralCharFuncKind<char32_t, CharFunc::Index>(
882         result, string, substring, back, kind, terminator);
883     break;
884   default:
885     terminator.Crash(
886         "INDEX: bad string type code %d", static_cast<int>(string.raw().type));
887   }
888 }
889 
RTNAME(LenTrim1)890 std::size_t RTNAME(LenTrim1)(const char *x, std::size_t chars) {
891   return LenTrim(x, chars);
892 }
RTNAME(LenTrim2)893 std::size_t RTNAME(LenTrim2)(const char16_t *x, std::size_t chars) {
894   return LenTrim(x, chars);
895 }
RTNAME(LenTrim4)896 std::size_t RTNAME(LenTrim4)(const char32_t *x, std::size_t chars) {
897   return LenTrim(x, chars);
898 }
899 
RTNAME(LenTrim)900 void RTNAME(LenTrim)(Descriptor &result, const Descriptor &string, int kind,
901     const char *sourceFile, int sourceLine) {
902   Terminator terminator{sourceFile, sourceLine};
903   switch (string.raw().type) {
904   case CFI_type_char:
905     LenTrimKind<char>(result, string, kind, terminator);
906     break;
907   case CFI_type_char16_t:
908     LenTrimKind<char16_t>(result, string, kind, terminator);
909     break;
910   case CFI_type_char32_t:
911     LenTrimKind<char32_t>(result, string, kind, terminator);
912     break;
913   default:
914     terminator.Crash("LEN_TRIM: bad string type code %d",
915         static_cast<int>(string.raw().type));
916   }
917 }
918 
RTNAME(Scan1)919 std::size_t RTNAME(Scan1)(const char *x, std::size_t xLen, const char *set,
920     std::size_t setLen, bool back) {
921   return ScanVerify<char, CharFunc::Scan>(x, xLen, set, setLen, back);
922 }
RTNAME(Scan2)923 std::size_t RTNAME(Scan2)(const char16_t *x, std::size_t xLen,
924     const char16_t *set, std::size_t setLen, bool back) {
925   return ScanVerify<char16_t, CharFunc::Scan>(x, xLen, set, setLen, back);
926 }
RTNAME(Scan4)927 std::size_t RTNAME(Scan4)(const char32_t *x, std::size_t xLen,
928     const char32_t *set, std::size_t setLen, bool back) {
929   return ScanVerify<char32_t, CharFunc::Scan>(x, xLen, set, setLen, back);
930 }
931 
RTNAME(Scan)932 void RTNAME(Scan)(Descriptor &result, const Descriptor &string,
933     const Descriptor &set, const Descriptor *back, int kind,
934     const char *sourceFile, int sourceLine) {
935   Terminator terminator{sourceFile, sourceLine};
936   switch (string.raw().type) {
937   case CFI_type_char:
938     GeneralCharFuncKind<char, CharFunc::Scan>(
939         result, string, set, back, kind, terminator);
940     break;
941   case CFI_type_char16_t:
942     GeneralCharFuncKind<char16_t, CharFunc::Scan>(
943         result, string, set, back, kind, terminator);
944     break;
945   case CFI_type_char32_t:
946     GeneralCharFuncKind<char32_t, CharFunc::Scan>(
947         result, string, set, back, kind, terminator);
948     break;
949   default:
950     terminator.Crash(
951         "SCAN: bad string type code %d", static_cast<int>(string.raw().type));
952   }
953 }
954 
RTNAME(Repeat)955 void RTNAME(Repeat)(Descriptor &result, const Descriptor &string,
956     std::int64_t ncopies, const char *sourceFile, int sourceLine) {
957   Terminator terminator{sourceFile, sourceLine};
958   if (ncopies < 0) {
959     terminator.Crash(
960         "REPEAT has negative NCOPIES=%jd", static_cast<std::intmax_t>(ncopies));
961   }
962   std::size_t origBytes{string.ElementBytes()};
963   result.Establish(string.type(), origBytes * ncopies, nullptr, 0, nullptr,
964       CFI_attribute_allocatable);
965   if (result.Allocate() != CFI_SUCCESS) {
966     terminator.Crash("REPEAT could not allocate storage for result");
967   }
968   const char *from{string.OffsetElement()};
969   for (char *to{result.OffsetElement()}; ncopies-- > 0; to += origBytes) {
970     std::memcpy(to, from, origBytes);
971   }
972 }
973 
RTNAME(Trim)974 void RTNAME(Trim)(Descriptor &result, const Descriptor &string,
975     const char *sourceFile, int sourceLine) {
976   Terminator terminator{sourceFile, sourceLine};
977   std::size_t resultBytes{0};
978   switch (string.raw().type) {
979   case CFI_type_char:
980     resultBytes =
981         LenTrim(string.OffsetElement<const char>(), string.ElementBytes());
982     break;
983   case CFI_type_char16_t:
984     resultBytes = LenTrim(string.OffsetElement<const char16_t>(),
985                       string.ElementBytes() >> 1)
986         << 1;
987     break;
988   case CFI_type_char32_t:
989     resultBytes = LenTrim(string.OffsetElement<const char32_t>(),
990                       string.ElementBytes() >> 2)
991         << 2;
992     break;
993   default:
994     terminator.Crash(
995         "TRIM: bad string type code %d", static_cast<int>(string.raw().type));
996   }
997   result.Establish(string.type(), resultBytes, nullptr, 0, nullptr,
998       CFI_attribute_allocatable);
999   RUNTIME_CHECK(terminator, result.Allocate() == CFI_SUCCESS);
1000   std::memcpy(result.OffsetElement(), string.OffsetElement(), resultBytes);
1001 }
1002 
RTNAME(Verify1)1003 std::size_t RTNAME(Verify1)(const char *x, std::size_t xLen, const char *set,
1004     std::size_t setLen, bool back) {
1005   return ScanVerify<char, CharFunc::Verify>(x, xLen, set, setLen, back);
1006 }
RTNAME(Verify2)1007 std::size_t RTNAME(Verify2)(const char16_t *x, std::size_t xLen,
1008     const char16_t *set, std::size_t setLen, bool back) {
1009   return ScanVerify<char16_t, CharFunc::Verify>(x, xLen, set, setLen, back);
1010 }
RTNAME(Verify4)1011 std::size_t RTNAME(Verify4)(const char32_t *x, std::size_t xLen,
1012     const char32_t *set, std::size_t setLen, bool back) {
1013   return ScanVerify<char32_t, CharFunc::Verify>(x, xLen, set, setLen, back);
1014 }
1015 
RTNAME(Verify)1016 void RTNAME(Verify)(Descriptor &result, const Descriptor &string,
1017     const Descriptor &set, const Descriptor *back, int kind,
1018     const char *sourceFile, int sourceLine) {
1019   Terminator terminator{sourceFile, sourceLine};
1020   switch (string.raw().type) {
1021   case CFI_type_char:
1022     GeneralCharFuncKind<char, CharFunc::Verify>(
1023         result, string, set, back, kind, terminator);
1024     break;
1025   case CFI_type_char16_t:
1026     GeneralCharFuncKind<char16_t, CharFunc::Verify>(
1027         result, string, set, back, kind, terminator);
1028     break;
1029   case CFI_type_char32_t:
1030     GeneralCharFuncKind<char32_t, CharFunc::Verify>(
1031         result, string, set, back, kind, terminator);
1032     break;
1033   default:
1034     terminator.Crash(
1035         "VERIFY: bad string type code %d", static_cast<int>(string.raw().type));
1036   }
1037 }
1038 
RTNAME(CharacterMax)1039 void RTNAME(CharacterMax)(Descriptor &accumulator, const Descriptor &x,
1040     const char *sourceFile, int sourceLine) {
1041   MaxMin<false>(accumulator, x, sourceFile, sourceLine);
1042 }
1043 
RTNAME(CharacterMin)1044 void RTNAME(CharacterMin)(Descriptor &accumulator, const Descriptor &x,
1045     const char *sourceFile, int sourceLine) {
1046   MaxMin<true>(accumulator, x, sourceFile, sourceLine);
1047 }
1048 }
1049 } // namespace Fortran::runtime
1050