1 //===-- runtime/character.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 "flang/Runtime/character.h"
10 #include "terminator.h"
12 #include "flang/Common/bit-population-count.h"
13 #include "flang/Common/uint128.h"
14 #include "flang/Runtime/character.h"
15 #include "flang/Runtime/cpp-type.h"
16 #include "flang/Runtime/descriptor.h"
20 namespace Fortran::runtime
{
22 template <typename CHAR
>
23 inline RT_API_ATTRS
int CompareToBlankPadding(
24 const CHAR
*x
, std::size_t chars
) {
25 using UNSIGNED_CHAR
= std::make_unsigned_t
<CHAR
>;
26 const auto blank
{static_cast<UNSIGNED_CHAR
>(' ')};
27 for (; chars
-- > 0; ++x
) {
28 const UNSIGNED_CHAR ux
{*reinterpret_cast<const UNSIGNED_CHAR
*>(x
)};
39 RT_OFFLOAD_API_GROUP_BEGIN
41 template <typename CHAR
>
42 RT_API_ATTRS
int CharacterScalarCompare(
43 const CHAR
*x
, const CHAR
*y
, std::size_t xChars
, std::size_t yChars
) {
44 auto minChars
{std::min(xChars
, yChars
)};
45 if constexpr (sizeof(CHAR
) == 1) {
46 // don't use for kind=2 or =4, that would fail on little-endian machines
47 int cmp
{Fortran::runtime::memcmp(x
, y
, minChars
)};
54 if (xChars
== yChars
) {
60 for (std::size_t n
{minChars
}; n
-- > 0; ++x
, ++y
) {
69 if (int cmp
{CompareToBlankPadding(x
, xChars
- minChars
)}) {
72 return -CompareToBlankPadding(y
, yChars
- minChars
);
75 template RT_API_ATTRS
int CharacterScalarCompare
<char>(
76 const char *x
, const char *y
, std::size_t xChars
, std::size_t yChars
);
77 template RT_API_ATTRS
int CharacterScalarCompare
<char16_t
>(const char16_t
*x
,
78 const char16_t
*y
, std::size_t xChars
, std::size_t yChars
);
79 template RT_API_ATTRS
int CharacterScalarCompare
<char32_t
>(const char32_t
*x
,
80 const char32_t
*y
, std::size_t xChars
, std::size_t yChars
);
82 RT_OFFLOAD_API_GROUP_END
84 // Shift count to use when converting between character lengths
86 template <typename CHAR
>
87 constexpr int shift
{common::TrailingZeroBitCount(sizeof(CHAR
))};
89 template <typename CHAR
>
90 static RT_API_ATTRS
void Compare(Descriptor
&result
, const Descriptor
&x
,
91 const Descriptor
&y
, const Terminator
&terminator
) {
93 terminator
, x
.rank() == y
.rank() || x
.rank() == 0 || y
.rank() == 0);
94 int rank
{std::max(x
.rank(), y
.rank())};
95 SubscriptValue ub
[maxRank
], xAt
[maxRank
], yAt
[maxRank
];
96 SubscriptValue elements
{1};
97 for (int j
{0}; j
< rank
; ++j
) {
98 if (x
.rank() > 0 && y
.rank() > 0) {
99 SubscriptValue xUB
{x
.GetDimension(j
).Extent()};
100 SubscriptValue yUB
{y
.GetDimension(j
).Extent()};
102 terminator
.Crash("Character array comparison: operands are not "
103 "conforming on dimension %d (%jd != %jd)",
104 j
+ 1, static_cast<std::intmax_t>(xUB
),
105 static_cast<std::intmax_t>(yUB
));
109 ub
[j
] = (x
.rank() ? x
: y
).GetDimension(j
).Extent();
113 x
.GetLowerBounds(xAt
);
114 y
.GetLowerBounds(yAt
);
116 TypeCategory::Logical
, 1, nullptr, rank
, ub
, CFI_attribute_allocatable
);
117 for (int j
{0}; j
< rank
; ++j
) {
118 result
.GetDimension(j
).SetBounds(1, ub
[j
]);
120 if (result
.Allocate() != CFI_SUCCESS
) {
121 terminator
.Crash("Compare: could not allocate storage for result");
123 std::size_t xChars
{x
.ElementBytes() >> shift
<CHAR
>};
124 std::size_t yChars
{y
.ElementBytes() >> shift
<char>};
125 for (SubscriptValue resultAt
{0}; elements
-- > 0;
126 ++resultAt
, x
.IncrementSubscripts(xAt
), y
.IncrementSubscripts(yAt
)) {
127 *result
.OffsetElement
<char>(resultAt
) = CharacterScalarCompare
<CHAR
>(
128 x
.Element
<CHAR
>(xAt
), y
.Element
<CHAR
>(yAt
), xChars
, yChars
);
132 template <typename CHAR
, bool ADJUSTR
>
133 static RT_API_ATTRS
void Adjust(CHAR
*to
, const CHAR
*from
, std::size_t chars
) {
134 if constexpr (ADJUSTR
) {
135 std::size_t j
{chars
}, k
{chars
};
136 for (; k
> 0 && from
[k
- 1] == ' '; --k
) {
145 std::size_t j
{0}, k
{0};
146 for (; k
< chars
&& from
[k
] == ' '; ++k
) {
157 template <typename CHAR
, bool ADJUSTR
>
158 static RT_API_ATTRS
void AdjustLRHelper(Descriptor
&result
,
159 const Descriptor
&string
, const Terminator
&terminator
) {
160 int rank
{string
.rank()};
161 SubscriptValue ub
[maxRank
], stringAt
[maxRank
];
162 SubscriptValue elements
{1};
163 for (int j
{0}; j
< rank
; ++j
) {
164 ub
[j
] = string
.GetDimension(j
).Extent();
168 string
.GetLowerBounds(stringAt
);
169 std::size_t elementBytes
{string
.ElementBytes()};
170 result
.Establish(string
.type(), elementBytes
, nullptr, rank
, ub
,
171 CFI_attribute_allocatable
);
172 for (int j
{0}; j
< rank
; ++j
) {
173 result
.GetDimension(j
).SetBounds(1, ub
[j
]);
175 if (result
.Allocate() != CFI_SUCCESS
) {
176 terminator
.Crash("ADJUSTL/R: could not allocate storage for result");
178 for (SubscriptValue resultAt
{0}; elements
-- > 0;
179 resultAt
+= elementBytes
, string
.IncrementSubscripts(stringAt
)) {
180 Adjust
<CHAR
, ADJUSTR
>(result
.OffsetElement
<CHAR
>(resultAt
),
181 string
.Element
<const CHAR
>(stringAt
), elementBytes
>> shift
<CHAR
>);
185 template <bool ADJUSTR
>
186 RT_API_ATTRS
void AdjustLR(Descriptor
&result
, const Descriptor
&string
,
187 const char *sourceFile
, int sourceLine
) {
188 Terminator terminator
{sourceFile
, sourceLine
};
189 switch (string
.raw().type
) {
191 AdjustLRHelper
<char, ADJUSTR
>(result
, string
, terminator
);
193 case CFI_type_char16_t
:
194 AdjustLRHelper
<char16_t
, ADJUSTR
>(result
, string
, terminator
);
196 case CFI_type_char32_t
:
197 AdjustLRHelper
<char32_t
, ADJUSTR
>(result
, string
, terminator
);
200 terminator
.Crash("ADJUSTL/R: bad string type code %d",
201 static_cast<int>(string
.raw().type
));
205 template <typename CHAR
>
206 inline RT_API_ATTRS
std::size_t LenTrim(const CHAR
*x
, std::size_t chars
) {
207 while (chars
> 0 && x
[chars
- 1] == ' ') {
213 template <typename INT
, typename CHAR
>
214 static RT_API_ATTRS
void LenTrim(Descriptor
&result
, const Descriptor
&string
,
215 const Terminator
&terminator
) {
216 int rank
{string
.rank()};
217 SubscriptValue ub
[maxRank
], stringAt
[maxRank
];
218 SubscriptValue elements
{1};
219 for (int j
{0}; j
< rank
; ++j
) {
220 ub
[j
] = string
.GetDimension(j
).Extent();
223 string
.GetLowerBounds(stringAt
);
224 result
.Establish(TypeCategory::Integer
, sizeof(INT
), nullptr, rank
, ub
,
225 CFI_attribute_allocatable
);
226 for (int j
{0}; j
< rank
; ++j
) {
227 result
.GetDimension(j
).SetBounds(1, ub
[j
]);
229 if (result
.Allocate() != CFI_SUCCESS
) {
230 terminator
.Crash("LEN_TRIM: could not allocate storage for result");
232 std::size_t stringElementChars
{string
.ElementBytes() >> shift
<CHAR
>};
233 for (SubscriptValue resultAt
{0}; elements
-- > 0;
234 resultAt
+= sizeof(INT
), string
.IncrementSubscripts(stringAt
)) {
235 *result
.OffsetElement
<INT
>(resultAt
) =
236 LenTrim(string
.Element
<CHAR
>(stringAt
), stringElementChars
);
240 template <typename CHAR
>
241 static RT_API_ATTRS
void LenTrimKind(Descriptor
&result
,
242 const Descriptor
&string
, int kind
, const Terminator
&terminator
) {
245 LenTrim
<CppTypeFor
<TypeCategory::Integer
, 1>, CHAR
>(
246 result
, string
, terminator
);
249 LenTrim
<CppTypeFor
<TypeCategory::Integer
, 2>, CHAR
>(
250 result
, string
, terminator
);
253 LenTrim
<CppTypeFor
<TypeCategory::Integer
, 4>, CHAR
>(
254 result
, string
, terminator
);
257 LenTrim
<CppTypeFor
<TypeCategory::Integer
, 8>, CHAR
>(
258 result
, string
, terminator
);
261 LenTrim
<CppTypeFor
<TypeCategory::Integer
, 16>, CHAR
>(
262 result
, string
, terminator
);
266 "not yet implemented: CHARACTER(KIND=%d) in LEN_TRIM intrinsic", kind
);
270 // INDEX implementation
271 template <typename CHAR
>
272 inline RT_API_ATTRS
std::size_t Index(const CHAR
*x
, std::size_t xLen
,
273 const CHAR
*want
, std::size_t wantLen
, bool back
) {
274 if (xLen
< wantLen
) {
278 return 1; // wantLen is also 0, so trivial match
281 // If wantLen==0, returns xLen + 1 per standard (and all other compilers)
282 std::size_t at
{xLen
- wantLen
+ 1};
283 for (; at
> 0; --at
) {
285 for (; j
<= wantLen
; ++j
) {
286 if (x
[at
+ j
- 2] != want
[j
- 1]) {
296 // Non-trivial forward substring search: use a simplified form of
297 // Boyer-Moore substring searching.
298 for (std::size_t at
{1}; at
+ wantLen
- 1 <= xLen
;) {
299 // Compare x(at:at+wantLen-1) with want(1:wantLen).
300 // The comparison proceeds from the ends of the substrings forward
301 // so that we can skip ahead by multiple positions on a miss.
302 std::size_t j
{wantLen
};
306 if (ch
!= want
[j
- 1]) {
311 return at
; // found a match
313 // Suppose we have at==2:
314 // "THAT FORTRAN THAT I RAN" <- the string (x) in which we search
315 // "THAT I RAN" <- the string (want) for which we search
316 // ^------------------ j==7, ch=='T'
317 // We can shift ahead 3 positions to at==5 to align the 'T's:
318 // "THAT FORTRAN THAT I RAN"
320 std::size_t shift
{1};
321 for (; shift
< j
; ++shift
) {
322 if (want
[j
- shift
- 1] == ch
) {
331 // SCAN and VERIFY implementation help. These intrinsic functions
332 // do pretty much the same thing, so they're templatized with a
333 // distinguishing flag.
335 enum class CharFunc
{ Index
, Scan
, Verify
};
337 template <typename CHAR
, CharFunc FUNC
>
338 inline RT_API_ATTRS
std::size_t ScanVerify(const CHAR
*x
, std::size_t xLen
,
339 const CHAR
*set
, std::size_t setLen
, bool back
) {
340 std::size_t at
{back
? xLen
: 1};
341 int increment
{back
? -1 : 1};
342 for (; xLen
-- > 0; at
+= increment
) {
345 // TODO: If set is sorted, could use binary search
346 for (std::size_t j
{0}; j
< setLen
; ++j
) {
352 if (inSet
!= (FUNC
== CharFunc::Verify
)) {
359 // Specialization for one-byte characters
360 template <bool IS_VERIFY
= false>
361 inline RT_API_ATTRS
std::size_t ScanVerify(const char *x
, std::size_t xLen
,
362 const char *set
, std::size_t setLen
, bool back
) {
363 std::size_t at
{back
? xLen
: 1};
364 int increment
{back
? -1 : 1};
366 std::uint64_t bitSet
[256 / 64]{0};
367 std::uint64_t one
{1};
368 for (std::size_t j
{0}; j
< setLen
; ++j
) {
369 unsigned setCh
{static_cast<unsigned char>(set
[j
])};
370 bitSet
[setCh
/ 64] |= one
<< (setCh
% 64);
372 for (; xLen
-- > 0; at
+= increment
) {
373 unsigned ch
{static_cast<unsigned char>(x
[at
- 1])};
374 bool inSet
{((bitSet
[ch
/ 64] >> (ch
% 64)) & 1) != 0};
375 if (inSet
!= IS_VERIFY
) {
383 template <typename INT
, typename CHAR
, CharFunc FUNC
>
384 static RT_API_ATTRS
void GeneralCharFunc(Descriptor
&result
,
385 const Descriptor
&string
, const Descriptor
&arg
, const Descriptor
*back
,
386 const Terminator
&terminator
) {
387 int rank
{string
.rank() ? string
.rank()
388 : arg
.rank() ? arg
.rank()
389 : back
? back
->rank()
391 SubscriptValue ub
[maxRank
], stringAt
[maxRank
], argAt
[maxRank
],
393 SubscriptValue elements
{1};
394 for (int j
{0}; j
< rank
; ++j
) {
395 ub
[j
] = string
.rank() ? string
.GetDimension(j
).Extent()
396 : arg
.rank() ? arg
.GetDimension(j
).Extent()
397 : back
? back
->GetDimension(j
).Extent()
401 string
.GetLowerBounds(stringAt
);
402 arg
.GetLowerBounds(argAt
);
404 back
->GetLowerBounds(backAt
);
406 result
.Establish(TypeCategory::Integer
, sizeof(INT
), nullptr, rank
, ub
,
407 CFI_attribute_allocatable
);
408 for (int j
{0}; j
< rank
; ++j
) {
409 result
.GetDimension(j
).SetBounds(1, ub
[j
]);
411 if (result
.Allocate() != CFI_SUCCESS
) {
412 terminator
.Crash("SCAN/VERIFY: could not allocate storage for result");
414 std::size_t stringElementChars
{string
.ElementBytes() >> shift
<CHAR
>};
415 std::size_t argElementChars
{arg
.ElementBytes() >> shift
<CHAR
>};
416 for (SubscriptValue resultAt
{0}; elements
-- > 0; resultAt
+= sizeof(INT
),
417 string
.IncrementSubscripts(stringAt
), arg
.IncrementSubscripts(argAt
),
418 back
&& back
->IncrementSubscripts(backAt
)) {
419 if constexpr (FUNC
== CharFunc::Index
) {
420 *result
.OffsetElement
<INT
>(resultAt
) =
421 Index
<CHAR
>(string
.Element
<CHAR
>(stringAt
), stringElementChars
,
422 arg
.Element
<CHAR
>(argAt
), argElementChars
,
423 back
&& IsLogicalElementTrue(*back
, backAt
));
424 } else if constexpr (FUNC
== CharFunc::Scan
) {
425 *result
.OffsetElement
<INT
>(resultAt
) =
426 ScanVerify
<CHAR
, CharFunc::Scan
>(string
.Element
<CHAR
>(stringAt
),
427 stringElementChars
, arg
.Element
<CHAR
>(argAt
), argElementChars
,
428 back
&& IsLogicalElementTrue(*back
, backAt
));
429 } else if constexpr (FUNC
== CharFunc::Verify
) {
430 *result
.OffsetElement
<INT
>(resultAt
) =
431 ScanVerify
<CHAR
, CharFunc::Verify
>(string
.Element
<CHAR
>(stringAt
),
432 stringElementChars
, arg
.Element
<CHAR
>(argAt
), argElementChars
,
433 back
&& IsLogicalElementTrue(*back
, backAt
));
435 static_assert(FUNC
== CharFunc::Index
|| FUNC
== CharFunc::Scan
||
436 FUNC
== CharFunc::Verify
);
441 template <typename CHAR
, CharFunc FUNC
>
442 static RT_API_ATTRS
void GeneralCharFuncKind(Descriptor
&result
,
443 const Descriptor
&string
, const Descriptor
&arg
, const Descriptor
*back
,
444 int kind
, const Terminator
&terminator
) {
447 GeneralCharFunc
<CppTypeFor
<TypeCategory::Integer
, 1>, CHAR
, FUNC
>(
448 result
, string
, arg
, back
, terminator
);
451 GeneralCharFunc
<CppTypeFor
<TypeCategory::Integer
, 2>, CHAR
, FUNC
>(
452 result
, string
, arg
, back
, terminator
);
455 GeneralCharFunc
<CppTypeFor
<TypeCategory::Integer
, 4>, CHAR
, FUNC
>(
456 result
, string
, arg
, back
, terminator
);
459 GeneralCharFunc
<CppTypeFor
<TypeCategory::Integer
, 8>, CHAR
, FUNC
>(
460 result
, string
, arg
, back
, terminator
);
463 GeneralCharFunc
<CppTypeFor
<TypeCategory::Integer
, 16>, CHAR
, FUNC
>(
464 result
, string
, arg
, back
, terminator
);
467 terminator
.Crash("not yet implemented: CHARACTER(KIND=%d) in "
468 "INDEX/SCAN/VERIFY intrinsic",
473 template <typename CHAR
, bool ISMIN
>
474 static RT_API_ATTRS
void MaxMinHelper(Descriptor
&accumulator
,
475 const Descriptor
&x
, const Terminator
&terminator
) {
476 RUNTIME_CHECK(terminator
,
477 accumulator
.rank() == 0 || x
.rank() == 0 ||
478 accumulator
.rank() == x
.rank());
479 SubscriptValue ub
[maxRank
], xAt
[maxRank
];
480 SubscriptValue elements
{1};
481 std::size_t accumChars
{accumulator
.ElementBytes() >> shift
<CHAR
>};
482 std::size_t xChars
{x
.ElementBytes() >> shift
<CHAR
>};
483 std::size_t chars
{std::max(accumChars
, xChars
)};
484 bool reallocate
{accumulator
.raw().base_addr
== nullptr ||
485 accumChars
!= chars
|| (accumulator
.rank() == 0 && x
.rank() > 0)};
486 int rank
{std::max(accumulator
.rank(), x
.rank())};
487 for (int j
{0}; j
< rank
; ++j
) {
489 ub
[j
] = x
.GetDimension(j
).Extent();
490 if (accumulator
.rank() > 0) {
491 SubscriptValue accumExt
{accumulator
.GetDimension(j
).Extent()};
492 if (accumExt
!= ub
[j
]) {
493 terminator
.Crash("Character MAX/MIN: operands are not "
494 "conforming on dimension %d (%jd != %jd)",
495 j
+ 1, static_cast<std::intmax_t>(accumExt
),
496 static_cast<std::intmax_t>(ub
[j
]));
500 ub
[j
] = accumulator
.GetDimension(j
).Extent();
504 x
.GetLowerBounds(xAt
);
506 const CHAR
*accumData
{accumulator
.OffsetElement
<CHAR
>()};
508 old
= accumulator
.raw().base_addr
;
509 accumulator
.set_base_addr(nullptr);
510 accumulator
.raw().elem_len
= chars
<< shift
<CHAR
>;
511 for (int j
{0}; j
< rank
; ++j
) {
512 accumulator
.GetDimension(j
).SetBounds(1, ub
[j
]);
514 RUNTIME_CHECK(terminator
, accumulator
.Allocate() == CFI_SUCCESS
);
516 for (CHAR
*result
{accumulator
.OffsetElement
<CHAR
>()}; elements
-- > 0;
517 accumData
+= accumChars
, result
+= chars
, x
.IncrementSubscripts(xAt
)) {
518 const CHAR
*xData
{x
.Element
<CHAR
>(xAt
)};
519 int cmp
{CharacterScalarCompare(accumData
, xData
, accumChars
, xChars
)};
520 if constexpr (ISMIN
) {
524 CopyAndPad(result
, xData
, chars
, xChars
);
525 } else if (result
!= accumData
) {
526 CopyAndPad(result
, accumData
, chars
, accumChars
);
532 template <bool ISMIN
>
533 static RT_API_ATTRS
void MaxMin(Descriptor
&accumulator
, const Descriptor
&x
,
534 const char *sourceFile
, int sourceLine
) {
535 Terminator terminator
{sourceFile
, sourceLine
};
536 RUNTIME_CHECK(terminator
, accumulator
.raw().type
== x
.raw().type
);
537 switch (accumulator
.raw().type
) {
539 MaxMinHelper
<char, ISMIN
>(accumulator
, x
, terminator
);
541 case CFI_type_char16_t
:
542 MaxMinHelper
<char16_t
, ISMIN
>(accumulator
, x
, terminator
);
544 case CFI_type_char32_t
:
545 MaxMinHelper
<char32_t
, ISMIN
>(accumulator
, x
, terminator
);
549 "Character MAX/MIN: result does not have a character type");
554 RT_EXT_API_GROUP_BEGIN
556 void RTDEF(CharacterConcatenate
)(Descriptor
&accumulator
,
557 const Descriptor
&from
, const char *sourceFile
, int sourceLine
) {
558 Terminator terminator
{sourceFile
, sourceLine
};
559 RUNTIME_CHECK(terminator
,
560 accumulator
.rank() == 0 || from
.rank() == 0 ||
561 accumulator
.rank() == from
.rank());
562 int rank
{std::max(accumulator
.rank(), from
.rank())};
563 SubscriptValue ub
[maxRank
], fromAt
[maxRank
];
564 SubscriptValue elements
{1};
565 for (int j
{0}; j
< rank
; ++j
) {
566 if (accumulator
.rank() > 0 && from
.rank() > 0) {
567 ub
[j
] = accumulator
.GetDimension(j
).Extent();
568 SubscriptValue fromUB
{from
.GetDimension(j
).Extent()};
569 if (ub
[j
] != fromUB
) {
570 terminator
.Crash("Character array concatenation: operands are not "
571 "conforming on dimension %d (%jd != %jd)",
572 j
+ 1, static_cast<std::intmax_t>(ub
[j
]),
573 static_cast<std::intmax_t>(fromUB
));
577 (accumulator
.rank() ? accumulator
: from
).GetDimension(j
).Extent();
581 std::size_t oldBytes
{accumulator
.ElementBytes()};
582 void *old
{accumulator
.raw().base_addr
};
583 accumulator
.set_base_addr(nullptr);
584 std::size_t fromBytes
{from
.ElementBytes()};
585 accumulator
.raw().elem_len
+= fromBytes
;
586 std::size_t newBytes
{accumulator
.ElementBytes()};
587 for (int j
{0}; j
< rank
; ++j
) {
588 accumulator
.GetDimension(j
).SetBounds(1, ub
[j
]);
590 if (accumulator
.Allocate() != CFI_SUCCESS
) {
592 "CharacterConcatenate: could not allocate storage for result");
594 const char *p
{static_cast<const char *>(old
)};
595 char *to
{static_cast<char *>(accumulator
.raw().base_addr
)};
596 from
.GetLowerBounds(fromAt
);
597 for (; elements
-- > 0;
598 to
+= newBytes
, p
+= oldBytes
, from
.IncrementSubscripts(fromAt
)) {
599 std::memcpy(to
, p
, oldBytes
);
600 std::memcpy(to
+ oldBytes
, from
.Element
<char>(fromAt
), fromBytes
);
605 void RTDEF(CharacterConcatenateScalar1
)(
606 Descriptor
&accumulator
, const char *from
, std::size_t chars
) {
607 Terminator terminator
{__FILE__
, __LINE__
};
608 RUNTIME_CHECK(terminator
, accumulator
.rank() == 0);
609 void *old
{accumulator
.raw().base_addr
};
610 accumulator
.set_base_addr(nullptr);
611 std::size_t oldLen
{accumulator
.ElementBytes()};
612 accumulator
.raw().elem_len
+= chars
;
613 RUNTIME_CHECK(terminator
, accumulator
.Allocate() == CFI_SUCCESS
);
614 std::memcpy(accumulator
.OffsetElement
<char>(oldLen
), from
, chars
);
618 int RTDEF(CharacterCompareScalar
)(const Descriptor
&x
, const Descriptor
&y
) {
619 Terminator terminator
{__FILE__
, __LINE__
};
620 RUNTIME_CHECK(terminator
, x
.rank() == 0);
621 RUNTIME_CHECK(terminator
, y
.rank() == 0);
622 RUNTIME_CHECK(terminator
, x
.raw().type
== y
.raw().type
);
623 switch (x
.raw().type
) {
625 return CharacterScalarCompare
<char>(x
.OffsetElement
<char>(),
626 y
.OffsetElement
<char>(), x
.ElementBytes(), y
.ElementBytes());
627 case CFI_type_char16_t
:
628 return CharacterScalarCompare
<char16_t
>(x
.OffsetElement
<char16_t
>(),
629 y
.OffsetElement
<char16_t
>(), x
.ElementBytes() >> 1,
630 y
.ElementBytes() >> 1);
631 case CFI_type_char32_t
:
632 return CharacterScalarCompare
<char32_t
>(x
.OffsetElement
<char32_t
>(),
633 y
.OffsetElement
<char32_t
>(), x
.ElementBytes() >> 2,
634 y
.ElementBytes() >> 2);
636 terminator
.Crash("CharacterCompareScalar: bad string type code %d",
637 static_cast<int>(x
.raw().type
));
642 int RTDEF(CharacterCompareScalar1
)(
643 const char *x
, const char *y
, std::size_t xChars
, std::size_t yChars
) {
644 return CharacterScalarCompare(x
, y
, xChars
, yChars
);
647 int RTDEF(CharacterCompareScalar2
)(const char16_t
*x
, const char16_t
*y
,
648 std::size_t xChars
, std::size_t yChars
) {
649 return CharacterScalarCompare(x
, y
, xChars
, yChars
);
652 int RTDEF(CharacterCompareScalar4
)(const char32_t
*x
, const char32_t
*y
,
653 std::size_t xChars
, std::size_t yChars
) {
654 return CharacterScalarCompare(x
, y
, xChars
, yChars
);
657 void RTDEF(CharacterCompare
)(
658 Descriptor
&result
, const Descriptor
&x
, const Descriptor
&y
) {
659 Terminator terminator
{__FILE__
, __LINE__
};
660 RUNTIME_CHECK(terminator
, x
.raw().type
== y
.raw().type
);
661 switch (x
.raw().type
) {
663 Compare
<char>(result
, x
, y
, terminator
);
665 case CFI_type_char16_t
:
666 Compare
<char16_t
>(result
, x
, y
, terminator
);
668 case CFI_type_char32_t
:
669 Compare
<char32_t
>(result
, x
, y
, terminator
);
672 terminator
.Crash("CharacterCompareScalar: bad string type code %d",
673 static_cast<int>(x
.raw().type
));
677 std::size_t RTDEF(CharacterAppend1
)(char *lhs
, std::size_t lhsBytes
,
678 std::size_t offset
, const char *rhs
, std::size_t rhsBytes
) {
679 if (auto n
{std::min(lhsBytes
- offset
, rhsBytes
)}) {
680 std::memcpy(lhs
+ offset
, rhs
, n
);
686 void RTDEF(CharacterPad1
)(char *lhs
, std::size_t bytes
, std::size_t offset
) {
687 if (bytes
> offset
) {
688 std::memset(lhs
+ offset
, ' ', bytes
- offset
);
692 // Intrinsic function entry points
694 void RTDEF(Adjustl
)(Descriptor
&result
, const Descriptor
&string
,
695 const char *sourceFile
, int sourceLine
) {
696 AdjustLR
<false>(result
, string
, sourceFile
, sourceLine
);
699 void RTDEF(Adjustr
)(Descriptor
&result
, const Descriptor
&string
,
700 const char *sourceFile
, int sourceLine
) {
701 AdjustLR
<true>(result
, string
, sourceFile
, sourceLine
);
704 std::size_t RTDEF(Index1
)(const char *x
, std::size_t xLen
, const char *set
,
705 std::size_t setLen
, bool back
) {
706 return Index
<char>(x
, xLen
, set
, setLen
, back
);
708 std::size_t RTDEF(Index2
)(const char16_t
*x
, std::size_t xLen
,
709 const char16_t
*set
, std::size_t setLen
, bool back
) {
710 return Index
<char16_t
>(x
, xLen
, set
, setLen
, back
);
712 std::size_t RTDEF(Index4
)(const char32_t
*x
, std::size_t xLen
,
713 const char32_t
*set
, std::size_t setLen
, bool back
) {
714 return Index
<char32_t
>(x
, xLen
, set
, setLen
, back
);
717 void RTDEF(Index
)(Descriptor
&result
, const Descriptor
&string
,
718 const Descriptor
&substring
, const Descriptor
*back
, int kind
,
719 const char *sourceFile
, int sourceLine
) {
720 Terminator terminator
{sourceFile
, sourceLine
};
721 switch (string
.raw().type
) {
723 GeneralCharFuncKind
<char, CharFunc::Index
>(
724 result
, string
, substring
, back
, kind
, terminator
);
726 case CFI_type_char16_t
:
727 GeneralCharFuncKind
<char16_t
, CharFunc::Index
>(
728 result
, string
, substring
, back
, kind
, terminator
);
730 case CFI_type_char32_t
:
731 GeneralCharFuncKind
<char32_t
, CharFunc::Index
>(
732 result
, string
, substring
, back
, kind
, terminator
);
736 "INDEX: bad string type code %d", static_cast<int>(string
.raw().type
));
740 std::size_t RTDEF(LenTrim1
)(const char *x
, std::size_t chars
) {
741 return LenTrim(x
, chars
);
743 std::size_t RTDEF(LenTrim2
)(const char16_t
*x
, std::size_t chars
) {
744 return LenTrim(x
, chars
);
746 std::size_t RTDEF(LenTrim4
)(const char32_t
*x
, std::size_t chars
) {
747 return LenTrim(x
, chars
);
750 void RTDEF(LenTrim
)(Descriptor
&result
, const Descriptor
&string
, int kind
,
751 const char *sourceFile
, int sourceLine
) {
752 Terminator terminator
{sourceFile
, sourceLine
};
753 switch (string
.raw().type
) {
755 LenTrimKind
<char>(result
, string
, kind
, terminator
);
757 case CFI_type_char16_t
:
758 LenTrimKind
<char16_t
>(result
, string
, kind
, terminator
);
760 case CFI_type_char32_t
:
761 LenTrimKind
<char32_t
>(result
, string
, kind
, terminator
);
764 terminator
.Crash("LEN_TRIM: bad string type code %d",
765 static_cast<int>(string
.raw().type
));
769 std::size_t RTDEF(Scan1
)(const char *x
, std::size_t xLen
, const char *set
,
770 std::size_t setLen
, bool back
) {
771 return ScanVerify
<char, CharFunc::Scan
>(x
, xLen
, set
, setLen
, back
);
773 std::size_t RTDEF(Scan2
)(const char16_t
*x
, std::size_t xLen
,
774 const char16_t
*set
, std::size_t setLen
, bool back
) {
775 return ScanVerify
<char16_t
, CharFunc::Scan
>(x
, xLen
, set
, setLen
, back
);
777 std::size_t RTDEF(Scan4
)(const char32_t
*x
, std::size_t xLen
,
778 const char32_t
*set
, std::size_t setLen
, bool back
) {
779 return ScanVerify
<char32_t
, CharFunc::Scan
>(x
, xLen
, set
, setLen
, back
);
782 void RTDEF(Scan
)(Descriptor
&result
, const Descriptor
&string
,
783 const Descriptor
&set
, const Descriptor
*back
, int kind
,
784 const char *sourceFile
, int sourceLine
) {
785 Terminator terminator
{sourceFile
, sourceLine
};
786 switch (string
.raw().type
) {
788 GeneralCharFuncKind
<char, CharFunc::Scan
>(
789 result
, string
, set
, back
, kind
, terminator
);
791 case CFI_type_char16_t
:
792 GeneralCharFuncKind
<char16_t
, CharFunc::Scan
>(
793 result
, string
, set
, back
, kind
, terminator
);
795 case CFI_type_char32_t
:
796 GeneralCharFuncKind
<char32_t
, CharFunc::Scan
>(
797 result
, string
, set
, back
, kind
, terminator
);
801 "SCAN: bad string type code %d", static_cast<int>(string
.raw().type
));
805 void RTDEF(Repeat
)(Descriptor
&result
, const Descriptor
&string
,
806 std::int64_t ncopies
, const char *sourceFile
, int sourceLine
) {
807 Terminator terminator
{sourceFile
, sourceLine
};
810 "REPEAT has negative NCOPIES=%jd", static_cast<std::intmax_t>(ncopies
));
812 std::size_t origBytes
{string
.ElementBytes()};
813 result
.Establish(string
.type(), origBytes
* ncopies
, nullptr, 0, nullptr,
814 CFI_attribute_allocatable
);
815 if (result
.Allocate() != CFI_SUCCESS
) {
816 terminator
.Crash("REPEAT could not allocate storage for result");
818 const char *from
{string
.OffsetElement()};
819 for (char *to
{result
.OffsetElement()}; ncopies
-- > 0; to
+= origBytes
) {
820 std::memcpy(to
, from
, origBytes
);
824 void RTDEF(Trim
)(Descriptor
&result
, const Descriptor
&string
,
825 const char *sourceFile
, int sourceLine
) {
826 Terminator terminator
{sourceFile
, sourceLine
};
827 std::size_t resultBytes
{0};
828 switch (string
.raw().type
) {
831 LenTrim(string
.OffsetElement
<const char>(), string
.ElementBytes());
833 case CFI_type_char16_t
:
834 resultBytes
= LenTrim(string
.OffsetElement
<const char16_t
>(),
835 string
.ElementBytes() >> 1)
838 case CFI_type_char32_t
:
839 resultBytes
= LenTrim(string
.OffsetElement
<const char32_t
>(),
840 string
.ElementBytes() >> 2)
845 "TRIM: bad string type code %d", static_cast<int>(string
.raw().type
));
847 result
.Establish(string
.type(), resultBytes
, nullptr, 0, nullptr,
848 CFI_attribute_allocatable
);
849 RUNTIME_CHECK(terminator
, result
.Allocate() == CFI_SUCCESS
);
850 std::memcpy(result
.OffsetElement(), string
.OffsetElement(), resultBytes
);
853 std::size_t RTDEF(Verify1
)(const char *x
, std::size_t xLen
, const char *set
,
854 std::size_t setLen
, bool back
) {
855 return ScanVerify
<char, CharFunc::Verify
>(x
, xLen
, set
, setLen
, back
);
857 std::size_t RTDEF(Verify2
)(const char16_t
*x
, std::size_t xLen
,
858 const char16_t
*set
, std::size_t setLen
, bool back
) {
859 return ScanVerify
<char16_t
, CharFunc::Verify
>(x
, xLen
, set
, setLen
, back
);
861 std::size_t RTDEF(Verify4
)(const char32_t
*x
, std::size_t xLen
,
862 const char32_t
*set
, std::size_t setLen
, bool back
) {
863 return ScanVerify
<char32_t
, CharFunc::Verify
>(x
, xLen
, set
, setLen
, back
);
866 void RTDEF(Verify
)(Descriptor
&result
, const Descriptor
&string
,
867 const Descriptor
&set
, const Descriptor
*back
, int kind
,
868 const char *sourceFile
, int sourceLine
) {
869 Terminator terminator
{sourceFile
, sourceLine
};
870 switch (string
.raw().type
) {
872 GeneralCharFuncKind
<char, CharFunc::Verify
>(
873 result
, string
, set
, back
, kind
, terminator
);
875 case CFI_type_char16_t
:
876 GeneralCharFuncKind
<char16_t
, CharFunc::Verify
>(
877 result
, string
, set
, back
, kind
, terminator
);
879 case CFI_type_char32_t
:
880 GeneralCharFuncKind
<char32_t
, CharFunc::Verify
>(
881 result
, string
, set
, back
, kind
, terminator
);
885 "VERIFY: bad string type code %d", static_cast<int>(string
.raw().type
));
889 void RTDEF(CharacterMax
)(Descriptor
&accumulator
, const Descriptor
&x
,
890 const char *sourceFile
, int sourceLine
) {
891 MaxMin
<false>(accumulator
, x
, sourceFile
, sourceLine
);
894 void RTDEF(CharacterMin
)(Descriptor
&accumulator
, const Descriptor
&x
,
895 const char *sourceFile
, int sourceLine
) {
896 MaxMin
<true>(accumulator
, x
, sourceFile
, sourceLine
);
901 } // namespace Fortran::runtime