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> 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> 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> 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> 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> 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> 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> 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> 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> 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("LEN_TRIM: bad KIND=%d", kind); 257 } 258 } 259 260 // INDEX implementation 261 template <typename CHAR> 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> 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> 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> 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> 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("INDEX/SCAN/VERIFY: bad KIND=%d", kind); 458 } 459 } 460 461 template <typename TO, typename FROM> 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> 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> 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 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 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 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 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 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 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 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 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 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 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 844 void RTNAME(Adjustl)(Descriptor &result, const Descriptor &string, 845 const char *sourceFile, int sourceLine) { 846 AdjustLR<false>(result, string, sourceFile, sourceLine); 847 } 848 849 void RTNAME(Adjustr)(Descriptor &result, const Descriptor &string, 850 const char *sourceFile, int sourceLine) { 851 AdjustLR<true>(result, string, sourceFile, sourceLine); 852 } 853 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 } 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 } 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 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 890 std::size_t RTNAME(LenTrim1)(const char *x, std::size_t chars) { 891 return LenTrim(x, chars); 892 } 893 std::size_t RTNAME(LenTrim2)(const char16_t *x, std::size_t chars) { 894 return LenTrim(x, chars); 895 } 896 std::size_t RTNAME(LenTrim4)(const char32_t *x, std::size_t chars) { 897 return LenTrim(x, chars); 898 } 899 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 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 } 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 } 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 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 955 void RTNAME(Repeat)(Descriptor &result, const Descriptor &string, 956 std::size_t ncopies, const char *sourceFile, int sourceLine) { 957 Terminator terminator{sourceFile, sourceLine}; 958 std::size_t origBytes{string.ElementBytes()}; 959 result.Establish(string.type(), origBytes * ncopies, nullptr, 0, nullptr, 960 CFI_attribute_allocatable); 961 if (result.Allocate() != CFI_SUCCESS) { 962 terminator.Crash("REPEAT could not allocate storage for result"); 963 } 964 const char *from{string.OffsetElement()}; 965 for (char *to{result.OffsetElement()}; ncopies-- > 0; to += origBytes) { 966 std::memcpy(to, from, origBytes); 967 } 968 } 969 970 void RTNAME(Trim)(Descriptor &result, const Descriptor &string, 971 const char *sourceFile, int sourceLine) { 972 Terminator terminator{sourceFile, sourceLine}; 973 std::size_t resultBytes{0}; 974 switch (string.raw().type) { 975 case CFI_type_char: 976 resultBytes = 977 LenTrim(string.OffsetElement<const char>(), string.ElementBytes()); 978 break; 979 case CFI_type_char16_t: 980 resultBytes = LenTrim(string.OffsetElement<const char16_t>(), 981 string.ElementBytes() >> 1) 982 << 1; 983 break; 984 case CFI_type_char32_t: 985 resultBytes = LenTrim(string.OffsetElement<const char32_t>(), 986 string.ElementBytes() >> 2) 987 << 2; 988 break; 989 default: 990 terminator.Crash( 991 "TRIM: bad string type code %d", static_cast<int>(string.raw().type)); 992 } 993 result.Establish(string.type(), resultBytes, nullptr, 0, nullptr, 994 CFI_attribute_allocatable); 995 RUNTIME_CHECK(terminator, result.Allocate() == CFI_SUCCESS); 996 std::memcpy(result.OffsetElement(), string.OffsetElement(), resultBytes); 997 } 998 999 std::size_t RTNAME(Verify1)(const char *x, std::size_t xLen, const char *set, 1000 std::size_t setLen, bool back) { 1001 return ScanVerify<char, CharFunc::Verify>(x, xLen, set, setLen, back); 1002 } 1003 std::size_t RTNAME(Verify2)(const char16_t *x, std::size_t xLen, 1004 const char16_t *set, std::size_t setLen, bool back) { 1005 return ScanVerify<char16_t, CharFunc::Verify>(x, xLen, set, setLen, back); 1006 } 1007 std::size_t RTNAME(Verify4)(const char32_t *x, std::size_t xLen, 1008 const char32_t *set, std::size_t setLen, bool back) { 1009 return ScanVerify<char32_t, CharFunc::Verify>(x, xLen, set, setLen, back); 1010 } 1011 1012 void RTNAME(Verify)(Descriptor &result, const Descriptor &string, 1013 const Descriptor &set, const Descriptor *back, int kind, 1014 const char *sourceFile, int sourceLine) { 1015 Terminator terminator{sourceFile, sourceLine}; 1016 switch (string.raw().type) { 1017 case CFI_type_char: 1018 GeneralCharFuncKind<char, CharFunc::Verify>( 1019 result, string, set, back, kind, terminator); 1020 break; 1021 case CFI_type_char16_t: 1022 GeneralCharFuncKind<char16_t, CharFunc::Verify>( 1023 result, string, set, back, kind, terminator); 1024 break; 1025 case CFI_type_char32_t: 1026 GeneralCharFuncKind<char32_t, CharFunc::Verify>( 1027 result, string, set, back, kind, terminator); 1028 break; 1029 default: 1030 terminator.Crash( 1031 "VERIFY: bad string type code %d", static_cast<int>(string.raw().type)); 1032 } 1033 } 1034 1035 void RTNAME(CharacterMax)(Descriptor &accumulator, const Descriptor &x, 1036 const char *sourceFile, int sourceLine) { 1037 MaxMin<false>(accumulator, x, sourceFile, sourceLine); 1038 } 1039 1040 void RTNAME(CharacterMin)(Descriptor &accumulator, const Descriptor &x, 1041 const char *sourceFile, int sourceLine) { 1042 MaxMin<true>(accumulator, x, sourceFile, sourceLine); 1043 } 1044 } 1045 } // namespace Fortran::runtime 1046