1 ------------------------------------------------------------------------------
3 -- GNAT RUN-TIME COMPONENTS --
5 -- A D A . S T R I N G S . U N B O U N D E D --
9 -- Copyright (C) 1992-2023, Free Software Foundation, Inc. --
11 -- This specification is derived from the Ada Reference Manual for use with --
12 -- GNAT. The copyright notice above, and the license provisions that follow --
13 -- apply solely to the contents of the part following the private keyword. --
15 -- GNAT is free software; you can redistribute it and/or modify it under --
16 -- terms of the GNU General Public License as published by the Free Soft- --
17 -- ware Foundation; either version 3, or (at your option) any later ver- --
18 -- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
19 -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
20 -- or FITNESS FOR A PARTICULAR PURPOSE. --
22 -- As a special exception under Section 7 of GPL version 3, you are granted --
23 -- additional permissions described in the GCC Runtime Library Exception, --
24 -- version 3.1, as published by the Free Software Foundation. --
26 -- You should have received a copy of the GNU General Public License and --
27 -- a copy of the GCC Runtime Library Exception along with this program; --
28 -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see --
29 -- <http://www.gnu.org/licenses/>. --
31 -- GNAT was originally developed by the GNAT team at New York University. --
32 -- Extensive contributions were provided by Ada Core Technologies Inc. --
34 ------------------------------------------------------------------------------
36 -- Preconditions in this unit are meant for analysis only, not for run-time
37 -- checking, so that the expected exceptions are raised. This is enforced by
38 -- setting the corresponding assertion policy to Ignore.
40 pragma Assertion_Policy
(Pre
=> Ignore
);
42 -- This package provides an implementation of Ada.Strings.Unbounded that uses
43 -- reference counts to implement copy on modification (rather than copy on
44 -- assignment). This is significantly more efficient on many targets.
46 -- This version is supported on:
47 -- - all Alpha platforms
48 -- - all AARCH64 platforms
49 -- - all ARM platforms
50 -- - all ia64 platforms
51 -- - all PowerPC platforms
52 -- - all SPARC V9 platforms
53 -- - all x86 platforms
54 -- - all x86_64 platforms
56 -- This package uses several techniques to increase speed:
58 -- - Implicit sharing or copy-on-write. An Unbounded_String contains only
59 -- the reference to the data which is shared between several instances.
60 -- The shared data is reallocated only when its value is changed and
61 -- the object mutation can't be used or it is inefficient to use it.
63 -- - Object mutation. Shared data object can be reused without memory
64 -- reallocation when all of the following requirements are met:
65 -- - the shared data object is no longer used by anyone else;
66 -- - the size is sufficient to store the new value;
67 -- - the gap after reuse is less than a defined threshold.
69 -- - Memory preallocation. Most of used memory allocation algorithms
70 -- align allocated segments on the some boundary, thus some amount of
71 -- additional memory can be preallocated without any impact. Such
72 -- preallocated memory can used later by Append/Insert operations
73 -- without reallocation.
75 -- Reference counting uses GCC builtin atomic operations, which allows safe
76 -- sharing of internal data between Ada tasks. Nevertheless, this does not
77 -- make objects of Unbounded_String thread-safe: an instance cannot be
78 -- accessed by several tasks simultaneously.
80 with Ada
.Strings
.Maps
;
81 private with Ada
.Finalization
;
82 private with System
.Atomic_Counters
;
83 private with Ada
.Strings
.Text_Buffers
;
85 package Ada
.Strings
.Unbounded
with
86 Initial_Condition
=> Length
(Null_Unbounded_String
) = 0,
91 type Unbounded_String
is private with
92 Default_Initial_Condition
=> Length
(Unbounded_String
) = 0;
93 pragma Preelaborable_Initialization
(Unbounded_String
);
95 Null_Unbounded_String
: constant Unbounded_String
;
97 function Length
(Source
: Unbounded_String
) return Natural with
100 type String_Access
is access all String;
102 procedure Free
(X
: in out String_Access
);
104 --------------------------------------------------------
105 -- Conversion, Concatenation, and Selection Functions --
106 --------------------------------------------------------
108 function To_Unbounded_String
109 (Source
: String) return Unbounded_String
111 Post
=> To_String
(To_Unbounded_String
'Result) = Source
,
114 function To_Unbounded_String
115 (Length
: Natural) return Unbounded_String
120 function To_String
(Source
: Unbounded_String
) return String with
122 To_String
'Result'First = 1
123 and then To_String'Result'Length
= Length
(Source
),
126 procedure Set_Unbounded_String
127 (Target
: out Unbounded_String
;
130 Post
=> To_String
(Target
) = Source
,
132 pragma Ada_05
(Set_Unbounded_String
);
135 (Source
: in out Unbounded_String
;
136 New_Item
: Unbounded_String
)
138 Pre
=> Length
(New_Item
) <= Natural'Last - Length
(Source
),
139 Post
=> Length
(Source
) = Length
(Source
)'Old + Length
(New_Item
),
143 (Source
: in out Unbounded_String
;
146 Pre
=> New_Item
'Length <= Natural'Last - Length
(Source
),
147 Post
=> Length
(Source
) = Length
(Source
)'Old + New_Item
'Length,
151 (Source
: in out Unbounded_String
;
152 New_Item
: Character)
154 Pre
=> Length
(Source
) < Natural'Last,
155 Post
=> Length
(Source
) = Length
(Source
)'Old + 1,
159 (Left
: Unbounded_String
;
160 Right
: Unbounded_String
) return Unbounded_String
162 Pre
=> Length
(Right
) <= Natural'Last - Length
(Left
),
163 Post
=> Length
("&"'Result) = Length (Left) + Length (Right),
167 (Left : Unbounded_String;
168 Right : String) return Unbounded_String
170 Pre => Right'Length <= Natural'Last - Length (Left),
171 Post => Length ("&"'Result
) = Length
(Left
) + Right
'Length,
176 Right
: Unbounded_String
) return Unbounded_String
178 Pre
=> Left
'Length <= Natural'Last - Length
(Right
),
179 Post
=> Length
("&"'Result) = Left'Length + Length (Right),
183 (Left : Unbounded_String;
184 Right : Character) return Unbounded_String
186 Pre => Length (Left) < Natural'Last,
187 Post => Length ("&"'Result
) = Length
(Left
) + 1,
192 Right
: Unbounded_String
) return Unbounded_String
194 Pre
=> Length
(Right
) < Natural'Last,
195 Post
=> Length
("&"'Result) = Length (Right) + 1,
199 (Source : Unbounded_String;
200 Index : Positive) return Character
202 Pre => Index <= Length (Source),
203 Post => Element'Result = To_String (Source) (Index),
206 procedure Replace_Element
207 (Source : in out Unbounded_String;
211 Pre => Index <= Length (Source),
212 Post => Length (Source) = Length (Source)'Old,
216 (Source : Unbounded_String;
218 High : Natural) return String
220 Pre => Low - 1 <= Length (Source) and then High <= Length (Source),
221 Post => Slice'Result'Length
= Natural'Max (0, High
- Low
+ 1),
224 function Unbounded_Slice
225 (Source
: Unbounded_String
;
227 High
: Natural) return Unbounded_String
229 Pre
=> Low
- 1 <= Length
(Source
) and then High
<= Length
(Source
),
231 Length
(Unbounded_Slice
'Result) = Natural'Max (0, High
- Low
+ 1),
233 pragma Ada_05
(Unbounded_Slice
);
235 procedure Unbounded_Slice
236 (Source
: Unbounded_String
;
237 Target
: out Unbounded_String
;
241 Pre
=> Low
- 1 <= Length
(Source
) and then High
<= Length
(Source
),
242 Post
=> Length
(Target
) = Natural'Max (0, High
- Low
+ 1),
244 pragma Ada_05
(Unbounded_Slice
);
247 (Left
: Unbounded_String
;
248 Right
: Unbounded_String
) return Boolean
250 Post
=> "="'Result = (To_String (Left) = To_String (Right)),
254 (Left : Unbounded_String;
255 Right : String) return Boolean
257 Post => "="'Result
= (To_String
(Left
) = Right
),
262 Right
: Unbounded_String
) return Boolean
264 Post
=> "="'Result = (Left = To_String (Right)),
268 (Left : Unbounded_String;
269 Right : Unbounded_String) return Boolean
274 (Left : Unbounded_String;
275 Right : String) return Boolean
281 Right : Unbounded_String) return Boolean
286 (Left : Unbounded_String;
287 Right : Unbounded_String) return Boolean
292 (Left : Unbounded_String;
293 Right : String) return Boolean
299 Right : Unbounded_String) return Boolean
304 (Left : Unbounded_String;
305 Right : Unbounded_String) return Boolean
310 (Left : Unbounded_String;
311 Right : String) return Boolean
317 Right : Unbounded_String) return Boolean
322 (Left : Unbounded_String;
323 Right : Unbounded_String) return Boolean
328 (Left : Unbounded_String;
329 Right : String) return Boolean
335 Right : Unbounded_String) return Boolean
339 ------------------------
340 -- Search Subprograms --
341 ------------------------
344 (Source : Unbounded_String;
346 Going : Direction := Forward;
347 Mapping : Maps.Character_Mapping := Maps.Identity) return Natural
349 Pre => Pattern'Length /= 0,
353 (Source : Unbounded_String;
355 Going : Direction := Forward;
356 Mapping : Maps.Character_Mapping_Function) return Natural
358 Pre => Pattern'Length /= 0,
362 (Source : Unbounded_String;
363 Set : Maps.Character_Set;
364 Test : Membership := Inside;
365 Going : Direction := Forward) return Natural
370 (Source : Unbounded_String;
373 Going : Direction := Forward;
374 Mapping : Maps.Character_Mapping := Maps.Identity) return Natural
376 Pre => (if Length (Source) /= 0 then From <= Length (Source))
377 and then Pattern'Length /= 0,
379 pragma Ada_05 (Index);
382 (Source : Unbounded_String;
385 Going : Direction := Forward;
386 Mapping : Maps.Character_Mapping_Function) return Natural
388 Pre => (if Length (Source) /= 0 then From <= Length (Source))
389 and then Pattern'Length /= 0,
391 pragma Ada_05 (Index);
394 (Source : Unbounded_String;
395 Set : Maps.Character_Set;
397 Test : Membership := Inside;
398 Going : Direction := Forward) return Natural
400 Pre => (if Length (Source) /= 0 then From <= Length (Source)),
402 pragma Ada_05 (Index);
404 function Index_Non_Blank
405 (Source : Unbounded_String;
406 Going : Direction := Forward) return Natural
410 function Index_Non_Blank
411 (Source : Unbounded_String;
413 Going : Direction := Forward) return Natural
415 Pre => (if Length (Source) /= 0 then From <= Length (Source)),
417 pragma Ada_05 (Index_Non_Blank);
420 (Source : Unbounded_String;
422 Mapping : Maps.Character_Mapping := Maps.Identity) return Natural
424 Pre => Pattern'Length /= 0,
428 (Source : Unbounded_String;
430 Mapping : Maps.Character_Mapping_Function) return Natural
432 Pre => Pattern'Length /= 0,
436 (Source : Unbounded_String;
437 Set : Maps.Character_Set) return Natural
442 (Source : Unbounded_String;
443 Set : Maps.Character_Set;
446 First : out Positive;
449 Pre => (if Length (Source) /= 0 then From <= Length (Source)),
451 pragma Ada_2012 (Find_Token);
454 (Source : Unbounded_String;
455 Set : Maps.Character_Set;
457 First : out Positive;
462 ------------------------------------
463 -- String Translation Subprograms --
464 ------------------------------------
467 (Source : Unbounded_String;
468 Mapping : Maps.Character_Mapping) return Unbounded_String
470 Post => Length (Translate'Result) = Length (Source),
474 (Source : in out Unbounded_String;
475 Mapping : Maps.Character_Mapping)
477 Post => Length (Source) = Length (Source)'Old,
481 (Source : Unbounded_String;
482 Mapping : Maps.Character_Mapping_Function) return Unbounded_String
484 Post => Length (Translate'Result) = Length (Source),
488 (Source : in out Unbounded_String;
489 Mapping : Maps.Character_Mapping_Function)
491 Post => Length (Source) = Length (Source)'Old,
494 ---------------------------------------
495 -- String Transformation Subprograms --
496 ---------------------------------------
498 function Replace_Slice
499 (Source : Unbounded_String;
502 By : String) return Unbounded_String
505 Low - 1 <= Length (Source)
506 and then (if High >= Low
508 <= Natural'Last - By'Length
509 - Natural'Max (Length (Source) - High, 0)
510 else Length (Source) <= Natural'Last - By'Length),
513 Length (Replace_Slice'Result)
514 = Low - 1 + By'Length + Natural'Max (Length (Source)'Old - High, 0),
516 Length (Replace_Slice'Result) = Length (Source)'Old + By'Length),
519 procedure Replace_Slice
520 (Source : in out Unbounded_String;
526 Low - 1 <= Length (Source)
527 and then (if High >= Low
529 <= Natural'Last - By'Length
530 - Natural'Max (Length (Source) - High, 0)
531 else Length (Source) <= Natural'Last - By'Length),
535 = Low - 1 + By'Length + Natural'Max (Length (Source)'Old - High, 0),
537 Length (Source) = Length (Source)'Old + By'Length),
541 (Source : Unbounded_String;
543 New_Item : String) return Unbounded_String
545 Pre => Before - 1 <= Length (Source)
546 and then New_Item'Length <= Natural'Last - Length (Source),
547 Post => Length (Insert'Result) = Length (Source) + New_Item'Length,
551 (Source : in out Unbounded_String;
555 Pre => Before - 1 <= Length (Source)
556 and then New_Item'Length <= Natural'Last - Length (Source),
557 Post => Length (Source) = Length (Source)'Old + New_Item'Length,
561 (Source : Unbounded_String;
563 New_Item : String) return Unbounded_String
565 Pre => Position - 1 <= Length (Source)
566 and then (if New_Item'Length /= 0
568 New_Item'Length <= Natural'Last - (Position - 1)),
570 Length (Overwrite'Result)
571 = Natural'Max (Length (Source), Position - 1 + New_Item'Length),
575 (Source : in out Unbounded_String;
579 Pre => Position - 1 <= Length (Source)
580 and then (if New_Item'Length /= 0
582 New_Item'Length <= Natural'Last - (Position - 1)),
585 = Natural'Max (Length (Source)'Old, Position - 1 + New_Item'Length),
590 (Source : Unbounded_String;
592 Through : Natural) return Unbounded_String
594 Pre => (if Through <= From then From - 1 <= Length (Source)),
597 Length (Delete'Result) = Length (Source) - (Through - From + 1),
599 Length (Delete'Result) = Length (Source)),
603 (Source : in out Unbounded_String;
607 Pre => (if Through <= From then From - 1 <= Length (Source)),
610 Length (Source) = Length (Source)'Old - (Through - From + 1),
612 Length (Source) = Length (Source)'Old),
616 (Source : Unbounded_String;
617 Side : Trim_End) return Unbounded_String
619 Post => Length (Trim'Result) <= Length (Source),
623 (Source : in out Unbounded_String;
626 Post => Length (Source) <= Length (Source)'Old,
630 (Source : Unbounded_String;
631 Left : Maps.Character_Set;
632 Right : Maps.Character_Set) return Unbounded_String
634 Post => Length (Trim'Result) <= Length (Source),
638 (Source : in out Unbounded_String;
639 Left : Maps.Character_Set;
640 Right : Maps.Character_Set)
642 Post => Length (Source) <= Length (Source)'Old,
646 (Source : Unbounded_String;
648 Pad : Character := Space) return Unbounded_String
650 Post => Length (Head'Result) = Count,
654 (Source : in out Unbounded_String;
656 Pad : Character := Space)
658 Post => Length (Source) = Count,
662 (Source : Unbounded_String;
664 Pad : Character := Space) return Unbounded_String
666 Post => Length (Tail'Result) = Count,
670 (Source : in out Unbounded_String;
672 Pad : Character := Space)
674 Post => Length (Source) = Count,
679 Right : Character) return Unbounded_String
681 Pre => Left <= Natural'Last,
682 Post => Length ("*"'Result
) = Left
,
687 Right
: String) return Unbounded_String
689 Pre
=> (if Left
/= 0 then Right
'Length <= Natural'Last / Left
),
690 Post
=> Length
("*"'Result) = Left * Right'Length,
695 Right : Unbounded_String) return Unbounded_String
697 Pre => (if Left /= 0 then Length (Right) <= Natural'Last / Left),
698 Post => Length ("*"'Result
) = Left
* Length
(Right
),
702 pragma Inline
(Length
);
704 package AF
renames Ada
.Finalization
;
706 type Shared_String
(Max_Length
: Natural) is limited record
707 Counter
: System
.Atomic_Counters
.Atomic_Counter
;
711 Data
: String (1 .. Max_Length
);
712 -- Last is the index of last significant element of the Data. All
713 -- elements with larger indexes are currently insignificant.
716 type Shared_String_Access
is access all Shared_String
;
718 procedure Reference
(Item
: not null Shared_String_Access
);
719 -- Increment reference counter.
720 -- Do nothing if Item points to Empty_Shared_String.
722 procedure Unreference
(Item
: not null Shared_String_Access
);
723 -- Decrement reference counter, deallocate Item when counter goes to zero.
724 -- Do nothing if Item points to Empty_Shared_String.
726 function Can_Be_Reused
727 (Item
: not null Shared_String_Access
;
728 Length
: Natural) return Boolean;
729 -- Returns True if Shared_String can be reused. There are two criteria when
730 -- Shared_String can be reused: its reference counter must be one (thus
731 -- Shared_String is owned exclusively) and its size is sufficient to
732 -- store string with specified length effectively.
735 (Required_Length
: Natural;
736 Reserved_Length
: Natural := 0) return not null Shared_String_Access
;
737 -- Allocates new Shared_String. Actual maximum length of allocated object
738 -- is at least the specified required length. Additional storage is
739 -- allocated to allow to store up to the specified reserved length when
740 -- possible. Returns reference to Empty_Shared_String when requested length
743 Empty_Shared_String
: aliased Shared_String
(0);
745 function To_Unbounded
(S
: String) return Unbounded_String
746 renames To_Unbounded_String
;
747 -- This renames are here only to be used in the pragma Stream_Convert
749 type Unbounded_String
is new AF
.Controlled
with record
750 Reference
: not null Shared_String_Access
:= Empty_Shared_String
'Access;
751 end record with Put_Image
=> Put_Image
;
754 (S
: in out Ada
.Strings
.Text_Buffers
.Root_Buffer_Type
'Class;
755 V
: Unbounded_String
);
757 pragma Stream_Convert
(Unbounded_String
, To_Unbounded
, To_String
);
758 -- Provide stream routines without dragging in Ada.Streams
760 pragma Finalize_Storage_Only
(Unbounded_String
);
761 -- Finalization is required only for freeing storage
763 overriding
procedure Initialize
(Object
: in out Unbounded_String
);
764 overriding
procedure Adjust
(Object
: in out Unbounded_String
);
765 overriding
procedure Finalize
(Object
: in out Unbounded_String
);
766 pragma Inline
(Initialize
, Adjust
);
768 Null_Unbounded_String
: constant Unbounded_String
:=
770 Reference
=> Empty_Shared_String
'Access);
772 end Ada
.Strings
.Unbounded
;