1------------------------------------------------------------------------------ 2-- -- 3-- GNAT RUN-TIME COMPONENTS -- 4-- -- 5-- A D A . S T R I N G S . W I D E _ W I D E _ S E A R C H -- 6-- -- 7-- B o d y -- 8-- -- 9-- Copyright (C) 1992-2018, Free Software Foundation, Inc. -- 10-- -- 11-- GNAT is free software; you can redistribute it and/or modify it under -- 12-- terms of the GNU General Public License as published by the Free Soft- -- 13-- ware Foundation; either version 3, or (at your option) any later ver- -- 14-- sion. GNAT is distributed in the hope that it will be useful, but WITH- -- 15-- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY -- 16-- or FITNESS FOR A PARTICULAR PURPOSE. -- 17-- -- 18-- As a special exception under Section 7 of GPL version 3, you are granted -- 19-- additional permissions described in the GCC Runtime Library Exception, -- 20-- version 3.1, as published by the Free Software Foundation. -- 21-- -- 22-- You should have received a copy of the GNU General Public License and -- 23-- a copy of the GCC Runtime Library Exception along with this program; -- 24-- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- 25-- <http://www.gnu.org/licenses/>. -- 26-- -- 27-- GNAT was originally developed by the GNAT team at New York University. -- 28-- Extensive contributions were provided by Ada Core Technologies Inc. -- 29-- -- 30------------------------------------------------------------------------------ 31 32with Ada.Strings.Wide_Wide_Maps; use Ada.Strings.Wide_Wide_Maps; 33with System; use System; 34 35package body Ada.Strings.Wide_Wide_Search is 36 37 ----------------------- 38 -- Local Subprograms -- 39 ----------------------- 40 41 function Belongs 42 (Element : Wide_Wide_Character; 43 Set : Wide_Wide_Maps.Wide_Wide_Character_Set; 44 Test : Membership) return Boolean; 45 pragma Inline (Belongs); 46 -- Determines if the given element is in (Test = Inside) or not in 47 -- (Test = Outside) the given character set. 48 49 ------------- 50 -- Belongs -- 51 ------------- 52 53 function Belongs 54 (Element : Wide_Wide_Character; 55 Set : Wide_Wide_Maps.Wide_Wide_Character_Set; 56 Test : Membership) return Boolean 57 is 58 begin 59 if Test = Inside then 60 return Is_In (Element, Set); 61 else 62 return not Is_In (Element, Set); 63 end if; 64 end Belongs; 65 66 ----------- 67 -- Count -- 68 ----------- 69 70 function Count 71 (Source : Wide_Wide_String; 72 Pattern : Wide_Wide_String; 73 Mapping : Wide_Wide_Maps.Wide_Wide_Character_Mapping := 74 Wide_Wide_Maps.Identity) return Natural 75 is 76 PL1 : constant Integer := Pattern'Length - 1; 77 Num : Natural; 78 Ind : Natural; 79 Cur : Natural; 80 81 begin 82 if Pattern = "" then 83 raise Pattern_Error; 84 end if; 85 86 Num := 0; 87 Ind := Source'First; 88 89 -- Unmapped case 90 91 if Mapping'Address = Wide_Wide_Maps.Identity'Address then 92 while Ind <= Source'Last - PL1 loop 93 if Pattern = Source (Ind .. Ind + PL1) then 94 Num := Num + 1; 95 Ind := Ind + Pattern'Length; 96 else 97 Ind := Ind + 1; 98 end if; 99 end loop; 100 101 -- Mapped case 102 103 else 104 while Ind <= Source'Last - PL1 loop 105 Cur := Ind; 106 for K in Pattern'Range loop 107 if Pattern (K) /= Value (Mapping, Source (Cur)) then 108 Ind := Ind + 1; 109 goto Cont; 110 else 111 Cur := Cur + 1; 112 end if; 113 end loop; 114 115 Num := Num + 1; 116 Ind := Ind + Pattern'Length; 117 118 <<Cont>> 119 null; 120 end loop; 121 end if; 122 123 -- Return result 124 125 return Num; 126 end Count; 127 128 function Count 129 (Source : Wide_Wide_String; 130 Pattern : Wide_Wide_String; 131 Mapping : Wide_Wide_Maps.Wide_Wide_Character_Mapping_Function) 132 return Natural 133 is 134 PL1 : constant Integer := Pattern'Length - 1; 135 Num : Natural; 136 Ind : Natural; 137 Cur : Natural; 138 139 begin 140 if Pattern = "" then 141 raise Pattern_Error; 142 end if; 143 144 -- Check for null pointer in case checks are off 145 146 if Mapping = null then 147 raise Constraint_Error; 148 end if; 149 150 Num := 0; 151 Ind := Source'First; 152 while Ind <= Source'Last - PL1 loop 153 Cur := Ind; 154 for K in Pattern'Range loop 155 if Pattern (K) /= Mapping (Source (Cur)) then 156 Ind := Ind + 1; 157 goto Cont; 158 else 159 Cur := Cur + 1; 160 end if; 161 end loop; 162 163 Num := Num + 1; 164 Ind := Ind + Pattern'Length; 165 166 <<Cont>> 167 null; 168 end loop; 169 170 return Num; 171 end Count; 172 173 function Count 174 (Source : Wide_Wide_String; 175 Set : Wide_Wide_Maps.Wide_Wide_Character_Set) return Natural 176 is 177 N : Natural := 0; 178 179 begin 180 for J in Source'Range loop 181 if Is_In (Source (J), Set) then 182 N := N + 1; 183 end if; 184 end loop; 185 186 return N; 187 end Count; 188 189 ---------------- 190 -- Find_Token -- 191 ---------------- 192 193 procedure Find_Token 194 (Source : Wide_Wide_String; 195 Set : Wide_Wide_Maps.Wide_Wide_Character_Set; 196 From : Positive; 197 Test : Membership; 198 First : out Positive; 199 Last : out Natural) 200 is 201 begin 202 for J in From .. Source'Last loop 203 if Belongs (Source (J), Set, Test) then 204 First := J; 205 206 for K in J + 1 .. Source'Last loop 207 if not Belongs (Source (K), Set, Test) then 208 Last := K - 1; 209 return; 210 end if; 211 end loop; 212 213 -- Here if J indexes first char of token, and all chars after J 214 -- are in the token. 215 216 Last := Source'Last; 217 return; 218 end if; 219 end loop; 220 221 -- Here if no token found 222 223 First := From; 224 Last := 0; 225 end Find_Token; 226 227 procedure Find_Token 228 (Source : Wide_Wide_String; 229 Set : Wide_Wide_Maps.Wide_Wide_Character_Set; 230 Test : Membership; 231 First : out Positive; 232 Last : out Natural) 233 is 234 begin 235 for J in Source'Range loop 236 if Belongs (Source (J), Set, Test) then 237 First := J; 238 239 for K in J + 1 .. Source'Last loop 240 if not Belongs (Source (K), Set, Test) then 241 Last := K - 1; 242 return; 243 end if; 244 end loop; 245 246 -- Here if J indexes first char of token, and all chars after J 247 -- are in the token. 248 249 Last := Source'Last; 250 return; 251 end if; 252 end loop; 253 254 -- Here if no token found 255 256 -- RM 2005 A.4.3 (68/1) specifies that an exception must be raised if 257 -- Source'First is not positive and is assigned to First. Formulation 258 -- is slightly different in RM 2012, but the intent seems similar, so 259 -- we check explicitly for that condition. 260 261 if Source'First not in Positive then 262 raise Constraint_Error; 263 264 else 265 First := Source'First; 266 Last := 0; 267 end if; 268 end Find_Token; 269 270 ----------- 271 -- Index -- 272 ----------- 273 274 function Index 275 (Source : Wide_Wide_String; 276 Pattern : Wide_Wide_String; 277 Going : Direction := Forward; 278 Mapping : Wide_Wide_Maps.Wide_Wide_Character_Mapping := 279 Wide_Wide_Maps.Identity) return Natural 280 is 281 PL1 : constant Integer := Pattern'Length - 1; 282 Cur : Natural; 283 284 Ind : Integer; 285 -- Index for start of match check. This can be negative if the pattern 286 -- length is greater than the string length, which is why this variable 287 -- is Integer instead of Natural. In this case, the search loops do not 288 -- execute at all, so this Ind value is never used. 289 290 begin 291 if Pattern = "" then 292 raise Pattern_Error; 293 end if; 294 295 -- Forwards case 296 297 if Going = Forward then 298 Ind := Source'First; 299 300 -- Unmapped forward case 301 302 if Mapping'Address = Wide_Wide_Maps.Identity'Address then 303 for J in 1 .. Source'Length - PL1 loop 304 if Pattern = Source (Ind .. Ind + PL1) then 305 return Ind; 306 else 307 Ind := Ind + 1; 308 end if; 309 end loop; 310 311 -- Mapped forward case 312 313 else 314 for J in 1 .. Source'Length - PL1 loop 315 Cur := Ind; 316 317 for K in Pattern'Range loop 318 if Pattern (K) /= Value (Mapping, Source (Cur)) then 319 goto Cont1; 320 else 321 Cur := Cur + 1; 322 end if; 323 end loop; 324 325 return Ind; 326 327 <<Cont1>> 328 Ind := Ind + 1; 329 end loop; 330 end if; 331 332 -- Backwards case 333 334 else 335 -- Unmapped backward case 336 337 Ind := Source'Last - PL1; 338 339 if Mapping'Address = Wide_Wide_Maps.Identity'Address then 340 for J in reverse 1 .. Source'Length - PL1 loop 341 if Pattern = Source (Ind .. Ind + PL1) then 342 return Ind; 343 else 344 Ind := Ind - 1; 345 end if; 346 end loop; 347 348 -- Mapped backward case 349 350 else 351 for J in reverse 1 .. Source'Length - PL1 loop 352 Cur := Ind; 353 354 for K in Pattern'Range loop 355 if Pattern (K) /= Value (Mapping, Source (Cur)) then 356 goto Cont2; 357 else 358 Cur := Cur + 1; 359 end if; 360 end loop; 361 362 return Ind; 363 364 <<Cont2>> 365 Ind := Ind - 1; 366 end loop; 367 end if; 368 end if; 369 370 -- Fall through if no match found. Note that the loops are skipped 371 -- completely in the case of the pattern being longer than the source. 372 373 return 0; 374 end Index; 375 376 function Index 377 (Source : Wide_Wide_String; 378 Pattern : Wide_Wide_String; 379 Going : Direction := Forward; 380 Mapping : Wide_Wide_Maps.Wide_Wide_Character_Mapping_Function) 381 return Natural 382 is 383 PL1 : constant Integer := Pattern'Length - 1; 384 Ind : Natural; 385 Cur : Natural; 386 387 begin 388 if Pattern = "" then 389 raise Pattern_Error; 390 end if; 391 392 -- Check for null pointer in case checks are off 393 394 if Mapping = null then 395 raise Constraint_Error; 396 end if; 397 398 -- If Pattern longer than Source it can't be found 399 400 if Pattern'Length > Source'Length then 401 return 0; 402 end if; 403 404 -- Forwards case 405 406 if Going = Forward then 407 Ind := Source'First; 408 for J in 1 .. Source'Length - PL1 loop 409 Cur := Ind; 410 411 for K in Pattern'Range loop 412 if Pattern (K) /= Mapping.all (Source (Cur)) then 413 goto Cont1; 414 else 415 Cur := Cur + 1; 416 end if; 417 end loop; 418 419 return Ind; 420 421 <<Cont1>> 422 Ind := Ind + 1; 423 end loop; 424 425 -- Backwards case 426 427 else 428 Ind := Source'Last - PL1; 429 for J in reverse 1 .. Source'Length - PL1 loop 430 Cur := Ind; 431 432 for K in Pattern'Range loop 433 if Pattern (K) /= Mapping.all (Source (Cur)) then 434 goto Cont2; 435 else 436 Cur := Cur + 1; 437 end if; 438 end loop; 439 440 return Ind; 441 442 <<Cont2>> 443 Ind := Ind - 1; 444 end loop; 445 end if; 446 447 -- Fall through if no match found. Note that the loops are skipped 448 -- completely in the case of the pattern being longer than the source. 449 450 return 0; 451 end Index; 452 453 function Index 454 (Source : Wide_Wide_String; 455 Set : Wide_Wide_Maps.Wide_Wide_Character_Set; 456 Test : Membership := Inside; 457 Going : Direction := Forward) return Natural 458 is 459 begin 460 -- Forwards case 461 462 if Going = Forward then 463 for J in Source'Range loop 464 if Belongs (Source (J), Set, Test) then 465 return J; 466 end if; 467 end loop; 468 469 -- Backwards case 470 471 else 472 for J in reverse Source'Range loop 473 if Belongs (Source (J), Set, Test) then 474 return J; 475 end if; 476 end loop; 477 end if; 478 479 -- Fall through if no match 480 481 return 0; 482 end Index; 483 484 function Index 485 (Source : Wide_Wide_String; 486 Pattern : Wide_Wide_String; 487 From : Positive; 488 Going : Direction := Forward; 489 Mapping : Wide_Wide_Maps.Wide_Wide_Character_Mapping := 490 Wide_Wide_Maps.Identity) return Natural 491 is 492 begin 493 if Going = Forward then 494 if From < Source'First then 495 raise Index_Error; 496 end if; 497 498 return 499 Index (Source (From .. Source'Last), Pattern, Forward, Mapping); 500 501 else 502 if From > Source'Last then 503 raise Index_Error; 504 end if; 505 506 return 507 Index (Source (Source'First .. From), Pattern, Backward, Mapping); 508 end if; 509 end Index; 510 511 function Index 512 (Source : Wide_Wide_String; 513 Pattern : Wide_Wide_String; 514 From : Positive; 515 Going : Direction := Forward; 516 Mapping : Wide_Wide_Maps.Wide_Wide_Character_Mapping_Function) 517 return Natural 518 is 519 begin 520 if Going = Forward then 521 if From < Source'First then 522 raise Index_Error; 523 end if; 524 525 return Index 526 (Source (From .. Source'Last), Pattern, Forward, Mapping); 527 528 else 529 if From > Source'Last then 530 raise Index_Error; 531 end if; 532 533 return Index 534 (Source (Source'First .. From), Pattern, Backward, Mapping); 535 end if; 536 end Index; 537 538 function Index 539 (Source : Wide_Wide_String; 540 Set : Wide_Wide_Maps.Wide_Wide_Character_Set; 541 From : Positive; 542 Test : Membership := Inside; 543 Going : Direction := Forward) return Natural 544 is 545 begin 546 if Going = Forward then 547 if From < Source'First then 548 raise Index_Error; 549 end if; 550 551 return 552 Index (Source (From .. Source'Last), Set, Test, Forward); 553 554 else 555 if From > Source'Last then 556 raise Index_Error; 557 end if; 558 559 return 560 Index (Source (Source'First .. From), Set, Test, Backward); 561 end if; 562 end Index; 563 564 --------------------- 565 -- Index_Non_Blank -- 566 --------------------- 567 568 function Index_Non_Blank 569 (Source : Wide_Wide_String; 570 Going : Direction := Forward) return Natural 571 is 572 begin 573 if Going = Forward then 574 for J in Source'Range loop 575 if Source (J) /= Wide_Wide_Space then 576 return J; 577 end if; 578 end loop; 579 580 else -- Going = Backward 581 for J in reverse Source'Range loop 582 if Source (J) /= Wide_Wide_Space then 583 return J; 584 end if; 585 end loop; 586 end if; 587 588 -- Fall through if no match 589 590 return 0; 591 end Index_Non_Blank; 592 593 function Index_Non_Blank 594 (Source : Wide_Wide_String; 595 From : Positive; 596 Going : Direction := Forward) return Natural 597 is 598 begin 599 if Going = Forward then 600 if From < Source'First then 601 raise Index_Error; 602 end if; 603 604 return 605 Index_Non_Blank (Source (From .. Source'Last), Forward); 606 607 else 608 if From > Source'Last then 609 raise Index_Error; 610 end if; 611 612 return 613 Index_Non_Blank (Source (Source'First .. From), Backward); 614 end if; 615 end Index_Non_Blank; 616 617end Ada.Strings.Wide_Wide_Search; 618