| ------------------------------------------------------------------------------ |
| -- -- |
| -- GNAT RUN-TIME COMPONENTS -- |
| -- -- |
| -- A D A . S T R I N G S . U N B O U N D E D -- |
| -- -- |
| -- B o d y -- |
| -- -- |
| -- Copyright (C) 1992-2014, Free Software Foundation, Inc. -- |
| -- -- |
| -- GNAT is free software; you can redistribute it and/or modify it under -- |
| -- terms of the GNU General Public License as published by the Free Soft- -- |
| -- ware Foundation; either version 3, or (at your option) any later ver- -- |
| -- sion. GNAT is distributed in the hope that it will be useful, but WITH- -- |
| -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY -- |
| -- or FITNESS FOR A PARTICULAR PURPOSE. -- |
| -- -- |
| -- As a special exception under Section 7 of GPL version 3, you are granted -- |
| -- additional permissions described in the GCC Runtime Library Exception, -- |
| -- version 3.1, as published by the Free Software Foundation. -- |
| -- -- |
| -- You should have received a copy of the GNU General Public License and -- |
| -- a copy of the GCC Runtime Library Exception along with this program; -- |
| -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- |
| -- <http://www.gnu.org/licenses/>. -- |
| -- -- |
| -- GNAT was originally developed by the GNAT team at New York University. -- |
| -- Extensive contributions were provided by Ada Core Technologies Inc. -- |
| -- -- |
| ------------------------------------------------------------------------------ |
| |
| with Ada.Strings.Search; |
| with Ada.Unchecked_Deallocation; |
| |
| package body Ada.Strings.Unbounded is |
| |
| use Ada.Strings.Maps; |
| |
| Growth_Factor : constant := 32; |
| -- The growth factor controls how much extra space is allocated when |
| -- we have to increase the size of an allocated unbounded string. By |
| -- allocating extra space, we avoid the need to reallocate on every |
| -- append, particularly important when a string is built up by repeated |
| -- append operations of small pieces. This is expressed as a factor so |
| -- 32 means add 1/32 of the length of the string as growth space. |
| |
| Min_Mul_Alloc : constant := Standard'Maximum_Alignment; |
| -- Allocation will be done by a multiple of Min_Mul_Alloc. This causes |
| -- no memory loss as most (all?) malloc implementations are obliged to |
| -- align the returned memory on the maximum alignment as malloc does not |
| -- know the target alignment. |
| |
| function Aligned_Max_Length (Max_Length : Natural) return Natural; |
| -- Returns recommended length of the shared string which is greater or |
| -- equal to specified length. Calculation take in sense alignment of the |
| -- allocated memory segments to use memory effectively by Append/Insert/etc |
| -- operations. |
| |
| --------- |
| -- "&" -- |
| --------- |
| |
| function "&" |
| (Left : Unbounded_String; |
| Right : Unbounded_String) return Unbounded_String |
| is |
| LR : constant Shared_String_Access := Left.Reference; |
| RR : constant Shared_String_Access := Right.Reference; |
| DL : constant Natural := LR.Last + RR.Last; |
| DR : Shared_String_Access; |
| |
| begin |
| -- Result is an empty string, reuse shared empty string |
| |
| if DL = 0 then |
| Reference (Empty_Shared_String'Access); |
| DR := Empty_Shared_String'Access; |
| |
| -- Left string is empty, return Right string |
| |
| elsif LR.Last = 0 then |
| Reference (RR); |
| DR := RR; |
| |
| -- Right string is empty, return Left string |
| |
| elsif RR.Last = 0 then |
| Reference (LR); |
| DR := LR; |
| |
| -- Otherwise, allocate new shared string and fill data |
| |
| else |
| DR := Allocate (DL); |
| DR.Data (1 .. LR.Last) := LR.Data (1 .. LR.Last); |
| DR.Data (LR.Last + 1 .. DL) := RR.Data (1 .. RR.Last); |
| DR.Last := DL; |
| end if; |
| |
| return (AF.Controlled with Reference => DR); |
| end "&"; |
| |
| function "&" |
| (Left : Unbounded_String; |
| Right : String) return Unbounded_String |
| is |
| LR : constant Shared_String_Access := Left.Reference; |
| DL : constant Natural := LR.Last + Right'Length; |
| DR : Shared_String_Access; |
| |
| begin |
| -- Result is an empty string, reuse shared empty string |
| |
| if DL = 0 then |
| Reference (Empty_Shared_String'Access); |
| DR := Empty_Shared_String'Access; |
| |
| -- Right is an empty string, return Left string |
| |
| elsif Right'Length = 0 then |
| Reference (LR); |
| DR := LR; |
| |
| -- Otherwise, allocate new shared string and fill it |
| |
| else |
| DR := Allocate (DL); |
| DR.Data (1 .. LR.Last) := LR.Data (1 .. LR.Last); |
| DR.Data (LR.Last + 1 .. DL) := Right; |
| DR.Last := DL; |
| end if; |
| |
| return (AF.Controlled with Reference => DR); |
| end "&"; |
| |
| function "&" |
| (Left : String; |
| Right : Unbounded_String) return Unbounded_String |
| is |
| RR : constant Shared_String_Access := Right.Reference; |
| DL : constant Natural := Left'Length + RR.Last; |
| DR : Shared_String_Access; |
| |
| begin |
| -- Result is an empty string, reuse shared one |
| |
| if DL = 0 then |
| Reference (Empty_Shared_String'Access); |
| DR := Empty_Shared_String'Access; |
| |
| -- Left is empty string, return Right string |
| |
| elsif Left'Length = 0 then |
| Reference (RR); |
| DR := RR; |
| |
| -- Otherwise, allocate new shared string and fill it |
| |
| else |
| DR := Allocate (DL); |
| DR.Data (1 .. Left'Length) := Left; |
| DR.Data (Left'Length + 1 .. DL) := RR.Data (1 .. RR.Last); |
| DR.Last := DL; |
| end if; |
| |
| return (AF.Controlled with Reference => DR); |
| end "&"; |
| |
| function "&" |
| (Left : Unbounded_String; |
| Right : Character) return Unbounded_String |
| is |
| LR : constant Shared_String_Access := Left.Reference; |
| DL : constant Natural := LR.Last + 1; |
| DR : Shared_String_Access; |
| |
| begin |
| DR := Allocate (DL); |
| DR.Data (1 .. LR.Last) := LR.Data (1 .. LR.Last); |
| DR.Data (DL) := Right; |
| DR.Last := DL; |
| |
| return (AF.Controlled with Reference => DR); |
| end "&"; |
| |
| function "&" |
| (Left : Character; |
| Right : Unbounded_String) return Unbounded_String |
| is |
| RR : constant Shared_String_Access := Right.Reference; |
| DL : constant Natural := 1 + RR.Last; |
| DR : Shared_String_Access; |
| |
| begin |
| DR := Allocate (DL); |
| DR.Data (1) := Left; |
| DR.Data (2 .. DL) := RR.Data (1 .. RR.Last); |
| DR.Last := DL; |
| |
| return (AF.Controlled with Reference => DR); |
| end "&"; |
| |
| --------- |
| -- "*" -- |
| --------- |
| |
| function "*" |
| (Left : Natural; |
| Right : Character) return Unbounded_String |
| is |
| DR : Shared_String_Access; |
| |
| begin |
| -- Result is an empty string, reuse shared empty string |
| |
| if Left = 0 then |
| Reference (Empty_Shared_String'Access); |
| DR := Empty_Shared_String'Access; |
| |
| -- Otherwise, allocate new shared string and fill it |
| |
| else |
| DR := Allocate (Left); |
| |
| for J in 1 .. Left loop |
| DR.Data (J) := Right; |
| end loop; |
| |
| DR.Last := Left; |
| end if; |
| |
| return (AF.Controlled with Reference => DR); |
| end "*"; |
| |
| function "*" |
| (Left : Natural; |
| Right : String) return Unbounded_String |
| is |
| DL : constant Natural := Left * Right'Length; |
| DR : Shared_String_Access; |
| K : Positive; |
| |
| begin |
| -- Result is an empty string, reuse shared empty string |
| |
| if DL = 0 then |
| Reference (Empty_Shared_String'Access); |
| DR := Empty_Shared_String'Access; |
| |
| -- Otherwise, allocate new shared string and fill it |
| |
| else |
| DR := Allocate (DL); |
| K := 1; |
| |
| for J in 1 .. Left loop |
| DR.Data (K .. K + Right'Length - 1) := Right; |
| K := K + Right'Length; |
| end loop; |
| |
| DR.Last := DL; |
| end if; |
| |
| return (AF.Controlled with Reference => DR); |
| end "*"; |
| |
| function "*" |
| (Left : Natural; |
| Right : Unbounded_String) return Unbounded_String |
| is |
| RR : constant Shared_String_Access := Right.Reference; |
| DL : constant Natural := Left * RR.Last; |
| DR : Shared_String_Access; |
| K : Positive; |
| |
| begin |
| -- Result is an empty string, reuse shared empty string |
| |
| if DL = 0 then |
| Reference (Empty_Shared_String'Access); |
| DR := Empty_Shared_String'Access; |
| |
| -- Coefficient is one, just return string itself |
| |
| elsif Left = 1 then |
| Reference (RR); |
| DR := RR; |
| |
| -- Otherwise, allocate new shared string and fill it |
| |
| else |
| DR := Allocate (DL); |
| K := 1; |
| |
| for J in 1 .. Left loop |
| DR.Data (K .. K + RR.Last - 1) := RR.Data (1 .. RR.Last); |
| K := K + RR.Last; |
| end loop; |
| |
| DR.Last := DL; |
| end if; |
| |
| return (AF.Controlled with Reference => DR); |
| end "*"; |
| |
| --------- |
| -- "<" -- |
| --------- |
| |
| function "<" |
| (Left : Unbounded_String; |
| Right : Unbounded_String) return Boolean |
| is |
| LR : constant Shared_String_Access := Left.Reference; |
| RR : constant Shared_String_Access := Right.Reference; |
| begin |
| return LR.Data (1 .. LR.Last) < RR.Data (1 .. RR.Last); |
| end "<"; |
| |
| function "<" |
| (Left : Unbounded_String; |
| Right : String) return Boolean |
| is |
| LR : constant Shared_String_Access := Left.Reference; |
| begin |
| return LR.Data (1 .. LR.Last) < Right; |
| end "<"; |
| |
| function "<" |
| (Left : String; |
| Right : Unbounded_String) return Boolean |
| is |
| RR : constant Shared_String_Access := Right.Reference; |
| begin |
| return Left < RR.Data (1 .. RR.Last); |
| end "<"; |
| |
| ---------- |
| -- "<=" -- |
| ---------- |
| |
| function "<=" |
| (Left : Unbounded_String; |
| Right : Unbounded_String) return Boolean |
| is |
| LR : constant Shared_String_Access := Left.Reference; |
| RR : constant Shared_String_Access := Right.Reference; |
| |
| begin |
| -- LR = RR means two strings shares shared string, thus they are equal |
| |
| return LR = RR or else LR.Data (1 .. LR.Last) <= RR.Data (1 .. RR.Last); |
| end "<="; |
| |
| function "<=" |
| (Left : Unbounded_String; |
| Right : String) return Boolean |
| is |
| LR : constant Shared_String_Access := Left.Reference; |
| begin |
| return LR.Data (1 .. LR.Last) <= Right; |
| end "<="; |
| |
| function "<=" |
| (Left : String; |
| Right : Unbounded_String) return Boolean |
| is |
| RR : constant Shared_String_Access := Right.Reference; |
| begin |
| return Left <= RR.Data (1 .. RR.Last); |
| end "<="; |
| |
| --------- |
| -- "=" -- |
| --------- |
| |
| function "=" |
| (Left : Unbounded_String; |
| Right : Unbounded_String) return Boolean |
| is |
| LR : constant Shared_String_Access := Left.Reference; |
| RR : constant Shared_String_Access := Right.Reference; |
| |
| begin |
| return LR = RR or else LR.Data (1 .. LR.Last) = RR.Data (1 .. RR.Last); |
| -- LR = RR means two strings shares shared string, thus they are equal |
| end "="; |
| |
| function "=" |
| (Left : Unbounded_String; |
| Right : String) return Boolean |
| is |
| LR : constant Shared_String_Access := Left.Reference; |
| begin |
| return LR.Data (1 .. LR.Last) = Right; |
| end "="; |
| |
| function "=" |
| (Left : String; |
| Right : Unbounded_String) return Boolean |
| is |
| RR : constant Shared_String_Access := Right.Reference; |
| begin |
| return Left = RR.Data (1 .. RR.Last); |
| end "="; |
| |
| --------- |
| -- ">" -- |
| --------- |
| |
| function ">" |
| (Left : Unbounded_String; |
| Right : Unbounded_String) return Boolean |
| is |
| LR : constant Shared_String_Access := Left.Reference; |
| RR : constant Shared_String_Access := Right.Reference; |
| begin |
| return LR.Data (1 .. LR.Last) > RR.Data (1 .. RR.Last); |
| end ">"; |
| |
| function ">" |
| (Left : Unbounded_String; |
| Right : String) return Boolean |
| is |
| LR : constant Shared_String_Access := Left.Reference; |
| begin |
| return LR.Data (1 .. LR.Last) > Right; |
| end ">"; |
| |
| function ">" |
| (Left : String; |
| Right : Unbounded_String) return Boolean |
| is |
| RR : constant Shared_String_Access := Right.Reference; |
| begin |
| return Left > RR.Data (1 .. RR.Last); |
| end ">"; |
| |
| ---------- |
| -- ">=" -- |
| ---------- |
| |
| function ">=" |
| (Left : Unbounded_String; |
| Right : Unbounded_String) return Boolean |
| is |
| LR : constant Shared_String_Access := Left.Reference; |
| RR : constant Shared_String_Access := Right.Reference; |
| |
| begin |
| -- LR = RR means two strings shares shared string, thus they are equal |
| |
| return LR = RR or else LR.Data (1 .. LR.Last) >= RR.Data (1 .. RR.Last); |
| end ">="; |
| |
| function ">=" |
| (Left : Unbounded_String; |
| Right : String) return Boolean |
| is |
| LR : constant Shared_String_Access := Left.Reference; |
| begin |
| return LR.Data (1 .. LR.Last) >= Right; |
| end ">="; |
| |
| function ">=" |
| (Left : String; |
| Right : Unbounded_String) return Boolean |
| is |
| RR : constant Shared_String_Access := Right.Reference; |
| begin |
| return Left >= RR.Data (1 .. RR.Last); |
| end ">="; |
| |
| ------------ |
| -- Adjust -- |
| ------------ |
| |
| procedure Adjust (Object : in out Unbounded_String) is |
| begin |
| Reference (Object.Reference); |
| end Adjust; |
| |
| ------------------------ |
| -- Aligned_Max_Length -- |
| ------------------------ |
| |
| function Aligned_Max_Length (Max_Length : Natural) return Natural is |
| Static_Size : constant Natural := |
| Empty_Shared_String'Size / Standard'Storage_Unit; |
| -- Total size of all static components |
| |
| begin |
| return |
| ((Static_Size + Max_Length - 1) / Min_Mul_Alloc + 2) * Min_Mul_Alloc |
| - Static_Size; |
| end Aligned_Max_Length; |
| |
| -------------- |
| -- Allocate -- |
| -------------- |
| |
| function Allocate (Max_Length : Natural) return Shared_String_Access is |
| begin |
| -- Empty string requested, return shared empty string |
| |
| if Max_Length = 0 then |
| Reference (Empty_Shared_String'Access); |
| return Empty_Shared_String'Access; |
| |
| -- Otherwise, allocate requested space (and probably some more room) |
| |
| else |
| return new Shared_String (Aligned_Max_Length (Max_Length)); |
| end if; |
| end Allocate; |
| |
| ------------ |
| -- Append -- |
| ------------ |
| |
| procedure Append |
| (Source : in out Unbounded_String; |
| New_Item : Unbounded_String) |
| is |
| SR : constant Shared_String_Access := Source.Reference; |
| NR : constant Shared_String_Access := New_Item.Reference; |
| DL : constant Natural := SR.Last + NR.Last; |
| DR : Shared_String_Access; |
| |
| begin |
| -- Source is an empty string, reuse New_Item data |
| |
| if SR.Last = 0 then |
| Reference (NR); |
| Source.Reference := NR; |
| Unreference (SR); |
| |
| -- New_Item is empty string, nothing to do |
| |
| elsif NR.Last = 0 then |
| null; |
| |
| -- Try to reuse existing shared string |
| |
| elsif Can_Be_Reused (SR, DL) then |
| SR.Data (SR.Last + 1 .. DL) := NR.Data (1 .. NR.Last); |
| SR.Last := DL; |
| |
| -- Otherwise, allocate new one and fill it |
| |
| else |
| DR := Allocate (DL + DL / Growth_Factor); |
| DR.Data (1 .. SR.Last) := SR.Data (1 .. SR.Last); |
| DR.Data (SR.Last + 1 .. DL) := NR.Data (1 .. NR.Last); |
| DR.Last := DL; |
| Source.Reference := DR; |
| Unreference (SR); |
| end if; |
| end Append; |
| |
| procedure Append |
| (Source : in out Unbounded_String; |
| New_Item : String) |
| is |
| SR : constant Shared_String_Access := Source.Reference; |
| DL : constant Natural := SR.Last + New_Item'Length; |
| DR : Shared_String_Access; |
| |
| begin |
| -- New_Item is an empty string, nothing to do |
| |
| if New_Item'Length = 0 then |
| null; |
| |
| -- Try to reuse existing shared string |
| |
| elsif Can_Be_Reused (SR, DL) then |
| SR.Data (SR.Last + 1 .. DL) := New_Item; |
| SR.Last := DL; |
| |
| -- Otherwise, allocate new one and fill it |
| |
| else |
| DR := Allocate (DL + DL / Growth_Factor); |
| DR.Data (1 .. SR.Last) := SR.Data (1 .. SR.Last); |
| DR.Data (SR.Last + 1 .. DL) := New_Item; |
| DR.Last := DL; |
| Source.Reference := DR; |
| Unreference (SR); |
| end if; |
| end Append; |
| |
| procedure Append |
| (Source : in out Unbounded_String; |
| New_Item : Character) |
| is |
| SR : constant Shared_String_Access := Source.Reference; |
| DL : constant Natural := SR.Last + 1; |
| DR : Shared_String_Access; |
| |
| begin |
| -- Try to reuse existing shared string |
| |
| if Can_Be_Reused (SR, SR.Last + 1) then |
| SR.Data (SR.Last + 1) := New_Item; |
| SR.Last := SR.Last + 1; |
| |
| -- Otherwise, allocate new one and fill it |
| |
| else |
| DR := Allocate (DL + DL / Growth_Factor); |
| DR.Data (1 .. SR.Last) := SR.Data (1 .. SR.Last); |
| DR.Data (DL) := New_Item; |
| DR.Last := DL; |
| Source.Reference := DR; |
| Unreference (SR); |
| end if; |
| end Append; |
| |
| ------------------- |
| -- Can_Be_Reused -- |
| ------------------- |
| |
| function Can_Be_Reused |
| (Item : Shared_String_Access; |
| Length : Natural) return Boolean is |
| begin |
| return |
| System.Atomic_Counters.Is_One (Item.Counter) |
| and then Item.Max_Length >= Length |
| and then Item.Max_Length <= |
| Aligned_Max_Length (Length + Length / Growth_Factor); |
| end Can_Be_Reused; |
| |
| ----------- |
| -- Count -- |
| ----------- |
| |
| function Count |
| (Source : Unbounded_String; |
| Pattern : String; |
| Mapping : Maps.Character_Mapping := Maps.Identity) return Natural |
| is |
| SR : constant Shared_String_Access := Source.Reference; |
| begin |
| return Search.Count (SR.Data (1 .. SR.Last), Pattern, Mapping); |
| end Count; |
| |
| function Count |
| (Source : Unbounded_String; |
| Pattern : String; |
| Mapping : Maps.Character_Mapping_Function) return Natural |
| is |
| SR : constant Shared_String_Access := Source.Reference; |
| begin |
| return Search.Count (SR.Data (1 .. SR.Last), Pattern, Mapping); |
| end Count; |
| |
| function Count |
| (Source : Unbounded_String; |
| Set : Maps.Character_Set) return Natural |
| is |
| SR : constant Shared_String_Access := Source.Reference; |
| begin |
| return Search.Count (SR.Data (1 .. SR.Last), Set); |
| end Count; |
| |
| ------------ |
| -- Delete -- |
| ------------ |
| |
| function Delete |
| (Source : Unbounded_String; |
| From : Positive; |
| Through : Natural) return Unbounded_String |
| is |
| SR : constant Shared_String_Access := Source.Reference; |
| DL : Natural; |
| DR : Shared_String_Access; |
| |
| begin |
| -- Empty slice is deleted, use the same shared string |
| |
| if From > Through then |
| Reference (SR); |
| DR := SR; |
| |
| -- Index is out of range |
| |
| elsif Through > SR.Last then |
| raise Index_Error; |
| |
| -- Compute size of the result |
| |
| else |
| DL := SR.Last - (Through - From + 1); |
| |
| -- Result is an empty string, reuse shared empty string |
| |
| if DL = 0 then |
| Reference (Empty_Shared_String'Access); |
| DR := Empty_Shared_String'Access; |
| |
| -- Otherwise, allocate new shared string and fill it |
| |
| else |
| DR := Allocate (DL); |
| DR.Data (1 .. From - 1) := SR.Data (1 .. From - 1); |
| DR.Data (From .. DL) := SR.Data (Through + 1 .. SR.Last); |
| DR.Last := DL; |
| end if; |
| end if; |
| |
| return (AF.Controlled with Reference => DR); |
| end Delete; |
| |
| procedure Delete |
| (Source : in out Unbounded_String; |
| From : Positive; |
| Through : Natural) |
| is |
| SR : constant Shared_String_Access := Source.Reference; |
| DL : Natural; |
| DR : Shared_String_Access; |
| |
| begin |
| -- Nothing changed, return |
| |
| if From > Through then |
| null; |
| |
| -- Through is outside of the range |
| |
| elsif Through > SR.Last then |
| raise Index_Error; |
| |
| else |
| DL := SR.Last - (Through - From + 1); |
| |
| -- Result is empty, reuse shared empty string |
| |
| if DL = 0 then |
| Reference (Empty_Shared_String'Access); |
| Source.Reference := Empty_Shared_String'Access; |
| Unreference (SR); |
| |
| -- Try to reuse existing shared string |
| |
| elsif Can_Be_Reused (SR, DL) then |
| SR.Data (From .. DL) := SR.Data (Through + 1 .. SR.Last); |
| SR.Last := DL; |
| |
| -- Otherwise, allocate new shared string |
| |
| else |
| DR := Allocate (DL); |
| DR.Data (1 .. From - 1) := SR.Data (1 .. From - 1); |
| DR.Data (From .. DL) := SR.Data (Through + 1 .. SR.Last); |
| DR.Last := DL; |
| Source.Reference := DR; |
| Unreference (SR); |
| end if; |
| end if; |
| end Delete; |
| |
| ------------- |
| -- Element -- |
| ------------- |
| |
| function Element |
| (Source : Unbounded_String; |
| Index : Positive) return Character |
| is |
| SR : constant Shared_String_Access := Source.Reference; |
| begin |
| if Index <= SR.Last then |
| return SR.Data (Index); |
| else |
| raise Index_Error; |
| end if; |
| end Element; |
| |
| -------------- |
| -- Finalize -- |
| -------------- |
| |
| procedure Finalize (Object : in out Unbounded_String) is |
| SR : constant Shared_String_Access := Object.Reference; |
| |
| begin |
| if SR /= null then |
| |
| -- The same controlled object can be finalized several times for |
| -- some reason. As per 7.6.1(24) this should have no ill effect, |
| -- so we need to add a guard for the case of finalizing the same |
| -- object twice. |
| |
| Object.Reference := null; |
| Unreference (SR); |
| end if; |
| end Finalize; |
| |
| ---------------- |
| -- Find_Token -- |
| ---------------- |
| |
| procedure Find_Token |
| (Source : Unbounded_String; |
| Set : Maps.Character_Set; |
| From : Positive; |
| Test : Strings.Membership; |
| First : out Positive; |
| Last : out Natural) |
| is |
| SR : constant Shared_String_Access := Source.Reference; |
| begin |
| Search.Find_Token (SR.Data (From .. SR.Last), Set, Test, First, Last); |
| end Find_Token; |
| |
| procedure Find_Token |
| (Source : Unbounded_String; |
| Set : Maps.Character_Set; |
| Test : Strings.Membership; |
| First : out Positive; |
| Last : out Natural) |
| is |
| SR : constant Shared_String_Access := Source.Reference; |
| begin |
| Search.Find_Token (SR.Data (1 .. SR.Last), Set, Test, First, Last); |
| end Find_Token; |
| |
| ---------- |
| -- Free -- |
| ---------- |
| |
| procedure Free (X : in out String_Access) is |
| procedure Deallocate is |
| new Ada.Unchecked_Deallocation (String, String_Access); |
| begin |
| Deallocate (X); |
| end Free; |
| |
| ---------- |
| -- Head -- |
| ---------- |
| |
| function Head |
| (Source : Unbounded_String; |
| Count : Natural; |
| Pad : Character := Space) return Unbounded_String |
| is |
| SR : constant Shared_String_Access := Source.Reference; |
| DR : Shared_String_Access; |
| |
| begin |
| -- Result is empty, reuse shared empty string |
| |
| if Count = 0 then |
| Reference (Empty_Shared_String'Access); |
| DR := Empty_Shared_String'Access; |
| |
| -- Length of the string is the same as requested, reuse source shared |
| -- string. |
| |
| elsif Count = SR.Last then |
| Reference (SR); |
| DR := SR; |
| |
| -- Otherwise, allocate new shared string and fill it |
| |
| else |
| DR := Allocate (Count); |
| |
| -- Length of the source string is more than requested, copy |
| -- corresponding slice. |
| |
| if Count < SR.Last then |
| DR.Data (1 .. Count) := SR.Data (1 .. Count); |
| |
| -- Length of the source string is less than requested, copy all |
| -- contents and fill others by Pad character. |
| |
| else |
| DR.Data (1 .. SR.Last) := SR.Data (1 .. SR.Last); |
| |
| for J in SR.Last + 1 .. Count loop |
| DR.Data (J) := Pad; |
| end loop; |
| end if; |
| |
| DR.Last := Count; |
| end if; |
| |
| return (AF.Controlled with Reference => DR); |
| end Head; |
| |
| procedure Head |
| (Source : in out Unbounded_String; |
| Count : Natural; |
| Pad : Character := Space) |
| is |
| SR : constant Shared_String_Access := Source.Reference; |
| DR : Shared_String_Access; |
| |
| begin |
| -- Result is empty, reuse empty shared string |
| |
| if Count = 0 then |
| Reference (Empty_Shared_String'Access); |
| Source.Reference := Empty_Shared_String'Access; |
| Unreference (SR); |
| |
| -- Result is same as source string, reuse source shared string |
| |
| elsif Count = SR.Last then |
| null; |
| |
| -- Try to reuse existing shared string |
| |
| elsif Can_Be_Reused (SR, Count) then |
| if Count > SR.Last then |
| for J in SR.Last + 1 .. Count loop |
| SR.Data (J) := Pad; |
| end loop; |
| end if; |
| |
| SR.Last := Count; |
| |
| -- Otherwise, allocate new shared string and fill it |
| |
| else |
| DR := Allocate (Count); |
| |
| -- Length of the source string is greater than requested, copy |
| -- corresponding slice. |
| |
| if Count < SR.Last then |
| DR.Data (1 .. Count) := SR.Data (1 .. Count); |
| |
| -- Length of the source string is less than requested, copy all |
| -- existing data and fill remaining positions with Pad characters. |
| |
| else |
| DR.Data (1 .. SR.Last) := SR.Data (1 .. SR.Last); |
| |
| for J in SR.Last + 1 .. Count loop |
| DR.Data (J) := Pad; |
| end loop; |
| end if; |
| |
| DR.Last := Count; |
| Source.Reference := DR; |
| Unreference (SR); |
| end if; |
| end Head; |
| |
| ----------- |
| -- Index -- |
| ----------- |
| |
| function Index |
| (Source : Unbounded_String; |
| Pattern : String; |
| Going : Strings.Direction := Strings.Forward; |
| Mapping : Maps.Character_Mapping := Maps.Identity) return Natural |
| is |
| SR : constant Shared_String_Access := Source.Reference; |
| begin |
| return Search.Index (SR.Data (1 .. SR.Last), Pattern, Going, Mapping); |
| end Index; |
| |
| function Index |
| (Source : Unbounded_String; |
| Pattern : String; |
| Going : Direction := Forward; |
| Mapping : Maps.Character_Mapping_Function) return Natural |
| is |
| SR : constant Shared_String_Access := Source.Reference; |
| begin |
| return Search.Index (SR.Data (1 .. SR.Last), Pattern, Going, Mapping); |
| end Index; |
| |
| function Index |
| (Source : Unbounded_String; |
| Set : Maps.Character_Set; |
| Test : Strings.Membership := Strings.Inside; |
| Going : Strings.Direction := Strings.Forward) return Natural |
| is |
| SR : constant Shared_String_Access := Source.Reference; |
| begin |
| return Search.Index (SR.Data (1 .. SR.Last), Set, Test, Going); |
| end Index; |
| |
| function Index |
| (Source : Unbounded_String; |
| Pattern : String; |
| From : Positive; |
| Going : Direction := Forward; |
| Mapping : Maps.Character_Mapping := Maps.Identity) return Natural |
| is |
| SR : constant Shared_String_Access := Source.Reference; |
| begin |
| return Search.Index |
| (SR.Data (1 .. SR.Last), Pattern, From, Going, Mapping); |
| end Index; |
| |
| function Index |
| (Source : Unbounded_String; |
| Pattern : String; |
| From : Positive; |
| Going : Direction := Forward; |
| Mapping : Maps.Character_Mapping_Function) return Natural |
| is |
| SR : constant Shared_String_Access := Source.Reference; |
| begin |
| return Search.Index |
| (SR.Data (1 .. SR.Last), Pattern, From, Going, Mapping); |
| end Index; |
| |
| function Index |
| (Source : Unbounded_String; |
| Set : Maps.Character_Set; |
| From : Positive; |
| Test : Membership := Inside; |
| Going : Direction := Forward) return Natural |
| is |
| SR : constant Shared_String_Access := Source.Reference; |
| begin |
| return Search.Index (SR.Data (1 .. SR.Last), Set, From, Test, Going); |
| end Index; |
| |
| --------------------- |
| -- Index_Non_Blank -- |
| --------------------- |
| |
| function Index_Non_Blank |
| (Source : Unbounded_String; |
| Going : Strings.Direction := Strings.Forward) return Natural |
| is |
| SR : constant Shared_String_Access := Source.Reference; |
| begin |
| return Search.Index_Non_Blank (SR.Data (1 .. SR.Last), Going); |
| end Index_Non_Blank; |
| |
| function Index_Non_Blank |
| (Source : Unbounded_String; |
| From : Positive; |
| Going : Direction := Forward) return Natural |
| is |
| SR : constant Shared_String_Access := Source.Reference; |
| begin |
| return Search.Index_Non_Blank (SR.Data (1 .. SR.Last), From, Going); |
| end Index_Non_Blank; |
| |
| ---------------- |
| -- Initialize -- |
| ---------------- |
| |
| procedure Initialize (Object : in out Unbounded_String) is |
| begin |
| Reference (Object.Reference); |
| end Initialize; |
| |
| ------------ |
| -- Insert -- |
| ------------ |
| |
| function Insert |
| (Source : Unbounded_String; |
| Before : Positive; |
| New_Item : String) return Unbounded_String |
| is |
| SR : constant Shared_String_Access := Source.Reference; |
| DL : constant Natural := SR.Last + New_Item'Length; |
| DR : Shared_String_Access; |
| |
| begin |
| -- Check index first |
| |
| if Before > SR.Last + 1 then |
| raise Index_Error; |
| end if; |
| |
| -- Result is empty, reuse empty shared string |
| |
| if DL = 0 then |
| Reference (Empty_Shared_String'Access); |
| DR := Empty_Shared_String'Access; |
| |
| -- Inserted string is empty, reuse source shared string |
| |
| elsif New_Item'Length = 0 then |
| Reference (SR); |
| DR := SR; |
| |
| -- Otherwise, allocate new shared string and fill it |
| |
| else |
| DR := Allocate (DL + DL / Growth_Factor); |
| DR.Data (1 .. Before - 1) := SR.Data (1 .. Before - 1); |
| DR.Data (Before .. Before + New_Item'Length - 1) := New_Item; |
| DR.Data (Before + New_Item'Length .. DL) := |
| SR.Data (Before .. SR.Last); |
| DR.Last := DL; |
| end if; |
| |
| return (AF.Controlled with Reference => DR); |
| end Insert; |
| |
| procedure Insert |
| (Source : in out Unbounded_String; |
| Before : Positive; |
| New_Item : String) |
| is |
| SR : constant Shared_String_Access := Source.Reference; |
| DL : constant Natural := SR.Last + New_Item'Length; |
| DR : Shared_String_Access; |
| |
| begin |
| -- Check bounds |
| |
| if Before > SR.Last + 1 then |
| raise Index_Error; |
| end if; |
| |
| -- Result is empty string, reuse empty shared string |
| |
| if DL = 0 then |
| Reference (Empty_Shared_String'Access); |
| Source.Reference := Empty_Shared_String'Access; |
| Unreference (SR); |
| |
| -- Inserted string is empty, nothing to do |
| |
| elsif New_Item'Length = 0 then |
| null; |
| |
| -- Try to reuse existing shared string first |
| |
| elsif Can_Be_Reused (SR, DL) then |
| SR.Data (Before + New_Item'Length .. DL) := |
| SR.Data (Before .. SR.Last); |
| SR.Data (Before .. Before + New_Item'Length - 1) := New_Item; |
| SR.Last := DL; |
| |
| -- Otherwise, allocate new shared string and fill it |
| |
| else |
| DR := Allocate (DL + DL / Growth_Factor); |
| DR.Data (1 .. Before - 1) := SR.Data (1 .. Before - 1); |
| DR.Data (Before .. Before + New_Item'Length - 1) := New_Item; |
| DR.Data (Before + New_Item'Length .. DL) := |
| SR.Data (Before .. SR.Last); |
| DR.Last := DL; |
| Source.Reference := DR; |
| Unreference (SR); |
| end if; |
| end Insert; |
| |
| ------------ |
| -- Length -- |
| ------------ |
| |
| function Length (Source : Unbounded_String) return Natural is |
| begin |
| return Source.Reference.Last; |
| end Length; |
| |
| --------------- |
| -- Overwrite -- |
| --------------- |
| |
| function Overwrite |
| (Source : Unbounded_String; |
| Position : Positive; |
| New_Item : String) return Unbounded_String |
| is |
| SR : constant Shared_String_Access := Source.Reference; |
| DL : Natural; |
| DR : Shared_String_Access; |
| |
| begin |
| -- Check bounds |
| |
| if Position > SR.Last + 1 then |
| raise Index_Error; |
| end if; |
| |
| DL := Integer'Max (SR.Last, Position + New_Item'Length - 1); |
| |
| -- Result is empty string, reuse empty shared string |
| |
| if DL = 0 then |
| Reference (Empty_Shared_String'Access); |
| DR := Empty_Shared_String'Access; |
| |
| -- Result is same as source string, reuse source shared string |
| |
| elsif New_Item'Length = 0 then |
| Reference (SR); |
| DR := SR; |
| |
| -- Otherwise, allocate new shared string and fill it |
| |
| else |
| DR := Allocate (DL); |
| DR.Data (1 .. Position - 1) := SR.Data (1 .. Position - 1); |
| DR.Data (Position .. Position + New_Item'Length - 1) := New_Item; |
| DR.Data (Position + New_Item'Length .. DL) := |
| SR.Data (Position + New_Item'Length .. SR.Last); |
| DR.Last := DL; |
| end if; |
| |
| return (AF.Controlled with Reference => DR); |
| end Overwrite; |
| |
| procedure Overwrite |
| (Source : in out Unbounded_String; |
| Position : Positive; |
| New_Item : String) |
| is |
| SR : constant Shared_String_Access := Source.Reference; |
| DL : Natural; |
| DR : Shared_String_Access; |
| |
| begin |
| -- Bounds check |
| |
| if Position > SR.Last + 1 then |
| raise Index_Error; |
| end if; |
| |
| DL := Integer'Max (SR.Last, Position + New_Item'Length - 1); |
| |
| -- Result is empty string, reuse empty shared string |
| |
| if DL = 0 then |
| Reference (Empty_Shared_String'Access); |
| Source.Reference := Empty_Shared_String'Access; |
| Unreference (SR); |
| |
| -- String unchanged, nothing to do |
| |
| elsif New_Item'Length = 0 then |
| null; |
| |
| -- Try to reuse existing shared string |
| |
| elsif Can_Be_Reused (SR, DL) then |
| SR.Data (Position .. Position + New_Item'Length - 1) := New_Item; |
| SR.Last := DL; |
| |
| -- Otherwise allocate new shared string and fill it |
| |
| else |
| DR := Allocate (DL); |
| DR.Data (1 .. Position - 1) := SR.Data (1 .. Position - 1); |
| DR.Data (Position .. Position + New_Item'Length - 1) := New_Item; |
| DR.Data (Position + New_Item'Length .. DL) := |
| SR.Data (Position + New_Item'Length .. SR.Last); |
| DR.Last := DL; |
| Source.Reference := DR; |
| Unreference (SR); |
| end if; |
| end Overwrite; |
| |
| --------------- |
| -- Reference -- |
| --------------- |
| |
| procedure Reference (Item : not null Shared_String_Access) is |
| begin |
| System.Atomic_Counters.Increment (Item.Counter); |
| end Reference; |
| |
| --------------------- |
| -- Replace_Element -- |
| --------------------- |
| |
| procedure Replace_Element |
| (Source : in out Unbounded_String; |
| Index : Positive; |
| By : Character) |
| is |
| SR : constant Shared_String_Access := Source.Reference; |
| DR : Shared_String_Access; |
| |
| begin |
| -- Bounds check |
| |
| if Index <= SR.Last then |
| |
| -- Try to reuse existing shared string |
| |
| if Can_Be_Reused (SR, SR.Last) then |
| SR.Data (Index) := By; |
| |
| -- Otherwise allocate new shared string and fill it |
| |
| else |
| DR := Allocate (SR.Last); |
| DR.Data (1 .. SR.Last) := SR.Data (1 .. SR.Last); |
| DR.Data (Index) := By; |
| DR.Last := SR.Last; |
| Source.Reference := DR; |
| Unreference (SR); |
| end if; |
| |
| else |
| raise Index_Error; |
| end if; |
| end Replace_Element; |
| |
| ------------------- |
| -- Replace_Slice -- |
| ------------------- |
| |
| function Replace_Slice |
| (Source : Unbounded_String; |
| Low : Positive; |
| High : Natural; |
| By : String) return Unbounded_String |
| is |
| SR : constant Shared_String_Access := Source.Reference; |
| DL : Natural; |
| DR : Shared_String_Access; |
| |
| begin |
| -- Check bounds |
| |
| if Low > SR.Last + 1 then |
| raise Index_Error; |
| end if; |
| |
| -- Do replace operation when removed slice is not empty |
| |
| if High >= Low then |
| DL := By'Length + SR.Last + Low - Integer'Min (High, SR.Last) - 1; |
| -- This is the number of characters remaining in the string after |
| -- replacing the slice. |
| |
| -- Result is empty string, reuse empty shared string |
| |
| if DL = 0 then |
| Reference (Empty_Shared_String'Access); |
| DR := Empty_Shared_String'Access; |
| |
| -- Otherwise allocate new shared string and fill it |
| |
| else |
| DR := Allocate (DL); |
| DR.Data (1 .. Low - 1) := SR.Data (1 .. Low - 1); |
| DR.Data (Low .. Low + By'Length - 1) := By; |
| DR.Data (Low + By'Length .. DL) := SR.Data (High + 1 .. SR.Last); |
| DR.Last := DL; |
| end if; |
| |
| return (AF.Controlled with Reference => DR); |
| |
| -- Otherwise just insert string |
| |
| else |
| return Insert (Source, Low, By); |
| end if; |
| end Replace_Slice; |
| |
| procedure Replace_Slice |
| (Source : in out Unbounded_String; |
| Low : Positive; |
| High : Natural; |
| By : String) |
| is |
| SR : constant Shared_String_Access := Source.Reference; |
| DL : Natural; |
| DR : Shared_String_Access; |
| |
| begin |
| -- Bounds check |
| |
| if Low > SR.Last + 1 then |
| raise Index_Error; |
| end if; |
| |
| -- Do replace operation only when replaced slice is not empty |
| |
| if High >= Low then |
| DL := By'Length + SR.Last + Low - Integer'Min (High, SR.Last) - 1; |
| -- This is the number of characters remaining in the string after |
| -- replacing the slice. |
| |
| -- Result is empty string, reuse empty shared string |
| |
| if DL = 0 then |
| Reference (Empty_Shared_String'Access); |
| Source.Reference := Empty_Shared_String'Access; |
| Unreference (SR); |
| |
| -- Try to reuse existing shared string |
| |
| elsif Can_Be_Reused (SR, DL) then |
| SR.Data (Low + By'Length .. DL) := SR.Data (High + 1 .. SR.Last); |
| SR.Data (Low .. Low + By'Length - 1) := By; |
| SR.Last := DL; |
| |
| -- Otherwise allocate new shared string and fill it |
| |
| else |
| DR := Allocate (DL); |
| DR.Data (1 .. Low - 1) := SR.Data (1 .. Low - 1); |
| DR.Data (Low .. Low + By'Length - 1) := By; |
| DR.Data (Low + By'Length .. DL) := SR.Data (High + 1 .. SR.Last); |
| DR.Last := DL; |
| Source.Reference := DR; |
| Unreference (SR); |
| end if; |
| |
| -- Otherwise just insert item |
| |
| else |
| Insert (Source, Low, By); |
| end if; |
| end Replace_Slice; |
| |
| -------------------------- |
| -- Set_Unbounded_String -- |
| -------------------------- |
| |
| procedure Set_Unbounded_String |
| (Target : out Unbounded_String; |
| Source : String) |
| is |
| TR : constant Shared_String_Access := Target.Reference; |
| DR : Shared_String_Access; |
| |
| begin |
| -- In case of empty string, reuse empty shared string |
| |
| if Source'Length = 0 then |
| Reference (Empty_Shared_String'Access); |
| Target.Reference := Empty_Shared_String'Access; |
| |
| else |
| -- Try to reuse existing shared string |
| |
| if Can_Be_Reused (TR, Source'Length) then |
| Reference (TR); |
| DR := TR; |
| |
| -- Otherwise allocate new shared string |
| |
| else |
| DR := Allocate (Source'Length); |
| Target.Reference := DR; |
| end if; |
| |
| DR.Data (1 .. Source'Length) := Source; |
| DR.Last := Source'Length; |
| end if; |
| |
| Unreference (TR); |
| end Set_Unbounded_String; |
| |
| ----------- |
| -- Slice -- |
| ----------- |
| |
| function Slice |
| (Source : Unbounded_String; |
| Low : Positive; |
| High : Natural) return String |
| is |
| SR : constant Shared_String_Access := Source.Reference; |
| |
| begin |
| -- Note: test of High > Length is in accordance with AI95-00128 |
| |
| if Low > SR.Last + 1 or else High > SR.Last then |
| raise Index_Error; |
| |
| else |
| return SR.Data (Low .. High); |
| end if; |
| end Slice; |
| |
| ---------- |
| -- Tail -- |
| ---------- |
| |
| function Tail |
| (Source : Unbounded_String; |
| Count : Natural; |
| Pad : Character := Space) return Unbounded_String |
| is |
| SR : constant Shared_String_Access := Source.Reference; |
| DR : Shared_String_Access; |
| |
| begin |
| -- For empty result reuse empty shared string |
| |
| if Count = 0 then |
| Reference (Empty_Shared_String'Access); |
| DR := Empty_Shared_String'Access; |
| |
| -- Result is whole source string, reuse source shared string |
| |
| elsif Count = SR.Last then |
| Reference (SR); |
| DR := SR; |
| |
| -- Otherwise allocate new shared string and fill it |
| |
| else |
| DR := Allocate (Count); |
| |
| if Count < SR.Last then |
| DR.Data (1 .. Count) := SR.Data (SR.Last - Count + 1 .. SR.Last); |
| |
| else |
| for J in 1 .. Count - SR.Last loop |
| DR.Data (J) := Pad; |
| end loop; |
| |
| DR.Data (Count - SR.Last + 1 .. Count) := SR.Data (1 .. SR.Last); |
| end if; |
| |
| DR.Last := Count; |
| end if; |
| |
| return (AF.Controlled with Reference => DR); |
| end Tail; |
| |
| procedure Tail |
| (Source : in out Unbounded_String; |
| Count : Natural; |
| Pad : Character := Space) |
| is |
| SR : constant Shared_String_Access := Source.Reference; |
| DR : Shared_String_Access; |
| |
| procedure Common |
| (SR : Shared_String_Access; |
| DR : Shared_String_Access; |
| Count : Natural); |
| -- Common code of tail computation. SR/DR can point to the same object |
| |
| ------------ |
| -- Common -- |
| ------------ |
| |
| procedure Common |
| (SR : Shared_String_Access; |
| DR : Shared_String_Access; |
| Count : Natural) is |
| begin |
| if Count < SR.Last then |
| DR.Data (1 .. Count) := SR.Data (SR.Last - Count + 1 .. SR.Last); |
| |
| else |
| DR.Data (Count - SR.Last + 1 .. Count) := SR.Data (1 .. SR.Last); |
| |
| for J in 1 .. Count - SR.Last loop |
| DR.Data (J) := Pad; |
| end loop; |
| end if; |
| |
| DR.Last := Count; |
| end Common; |
| |
| begin |
| -- Result is empty string, reuse empty shared string |
| |
| if Count = 0 then |
| Reference (Empty_Shared_String'Access); |
| Source.Reference := Empty_Shared_String'Access; |
| Unreference (SR); |
| |
| -- Length of the result is the same as length of the source string, |
| -- reuse source shared string. |
| |
| elsif Count = SR.Last then |
| null; |
| |
| -- Try to reuse existing shared string |
| |
| elsif Can_Be_Reused (SR, Count) then |
| Common (SR, SR, Count); |
| |
| -- Otherwise allocate new shared string and fill it |
| |
| else |
| DR := Allocate (Count); |
| Common (SR, DR, Count); |
| Source.Reference := DR; |
| Unreference (SR); |
| end if; |
| end Tail; |
| |
| --------------- |
| -- To_String -- |
| --------------- |
| |
| function To_String (Source : Unbounded_String) return String is |
| begin |
| return Source.Reference.Data (1 .. Source.Reference.Last); |
| end To_String; |
| |
| ------------------------- |
| -- To_Unbounded_String -- |
| ------------------------- |
| |
| function To_Unbounded_String (Source : String) return Unbounded_String is |
| DR : Shared_String_Access; |
| |
| begin |
| if Source'Length = 0 then |
| Reference (Empty_Shared_String'Access); |
| DR := Empty_Shared_String'Access; |
| |
| else |
| DR := Allocate (Source'Length); |
| DR.Data (1 .. Source'Length) := Source; |
| DR.Last := Source'Length; |
| end if; |
| |
| return (AF.Controlled with Reference => DR); |
| end To_Unbounded_String; |
| |
| function To_Unbounded_String (Length : Natural) return Unbounded_String is |
| DR : Shared_String_Access; |
| |
| begin |
| if Length = 0 then |
| Reference (Empty_Shared_String'Access); |
| DR := Empty_Shared_String'Access; |
| |
| else |
| DR := Allocate (Length); |
| DR.Last := Length; |
| end if; |
| |
| return (AF.Controlled with Reference => DR); |
| end To_Unbounded_String; |
| |
| --------------- |
| -- Translate -- |
| --------------- |
| |
| function Translate |
| (Source : Unbounded_String; |
| Mapping : Maps.Character_Mapping) return Unbounded_String |
| is |
| SR : constant Shared_String_Access := Source.Reference; |
| DR : Shared_String_Access; |
| |
| begin |
| -- Nothing to translate, reuse empty shared string |
| |
| if SR.Last = 0 then |
| Reference (Empty_Shared_String'Access); |
| DR := Empty_Shared_String'Access; |
| |
| -- Otherwise, allocate new shared string and fill it |
| |
| else |
| DR := Allocate (SR.Last); |
| |
| for J in 1 .. SR.Last loop |
| DR.Data (J) := Value (Mapping, SR.Data (J)); |
| end loop; |
| |
| DR.Last := SR.Last; |
| end if; |
| |
| return (AF.Controlled with Reference => DR); |
| end Translate; |
| |
| procedure Translate |
| (Source : in out Unbounded_String; |
| Mapping : Maps.Character_Mapping) |
| is |
| SR : constant Shared_String_Access := Source.Reference; |
| DR : Shared_String_Access; |
| |
| begin |
| -- Nothing to translate |
| |
| if SR.Last = 0 then |
| null; |
| |
| -- Try to reuse shared string |
| |
| elsif Can_Be_Reused (SR, SR.Last) then |
| for J in 1 .. SR.Last loop |
| SR.Data (J) := Value (Mapping, SR.Data (J)); |
| end loop; |
| |
| -- Otherwise, allocate new shared string |
| |
| else |
| DR := Allocate (SR.Last); |
| |
| for J in 1 .. SR.Last loop |
| DR.Data (J) := Value (Mapping, SR.Data (J)); |
| end loop; |
| |
| DR.Last := SR.Last; |
| Source.Reference := DR; |
| Unreference (SR); |
| end if; |
| end Translate; |
| |
| function Translate |
| (Source : Unbounded_String; |
| Mapping : Maps.Character_Mapping_Function) return Unbounded_String |
| is |
| SR : constant Shared_String_Access := Source.Reference; |
| DR : Shared_String_Access; |
| |
| begin |
| -- Nothing to translate, reuse empty shared string |
| |
| if SR.Last = 0 then |
| Reference (Empty_Shared_String'Access); |
| DR := Empty_Shared_String'Access; |
| |
| -- Otherwise, allocate new shared string and fill it |
| |
| else |
| DR := Allocate (SR.Last); |
| |
| for J in 1 .. SR.Last loop |
| DR.Data (J) := Mapping.all (SR.Data (J)); |
| end loop; |
| |
| DR.Last := SR.Last; |
| end if; |
| |
| return (AF.Controlled with Reference => DR); |
| |
| exception |
| when others => |
| Unreference (DR); |
| |
| raise; |
| end Translate; |
| |
| procedure Translate |
| (Source : in out Unbounded_String; |
| Mapping : Maps.Character_Mapping_Function) |
| is |
| SR : constant Shared_String_Access := Source.Reference; |
| DR : Shared_String_Access; |
| |
| begin |
| -- Nothing to translate |
| |
| if SR.Last = 0 then |
| null; |
| |
| -- Try to reuse shared string |
| |
| elsif Can_Be_Reused (SR, SR.Last) then |
| for J in 1 .. SR.Last loop |
| SR.Data (J) := Mapping.all (SR.Data (J)); |
| end loop; |
| |
| -- Otherwise allocate new shared string and fill it |
| |
| else |
| DR := Allocate (SR.Last); |
| |
| for J in 1 .. SR.Last loop |
| DR.Data (J) := Mapping.all (SR.Data (J)); |
| end loop; |
| |
| DR.Last := SR.Last; |
| Source.Reference := DR; |
| Unreference (SR); |
| end if; |
| |
| exception |
| when others => |
| if DR /= null then |
| Unreference (DR); |
| end if; |
| |
| raise; |
| end Translate; |
| |
| ---------- |
| -- Trim -- |
| ---------- |
| |
| function Trim |
| (Source : Unbounded_String; |
| Side : Trim_End) return Unbounded_String |
| is |
| SR : constant Shared_String_Access := Source.Reference; |
| DL : Natural; |
| DR : Shared_String_Access; |
| Low : Natural; |
| High : Natural; |
| |
| begin |
| Low := Index_Non_Blank (Source, Forward); |
| |
| -- All blanks, reuse empty shared string |
| |
| if Low = 0 then |
| Reference (Empty_Shared_String'Access); |
| DR := Empty_Shared_String'Access; |
| |
| else |
| case Side is |
| when Left => |
| High := SR.Last; |
| DL := SR.Last - Low + 1; |
| |
| when Right => |
| Low := 1; |
| High := Index_Non_Blank (Source, Backward); |
| DL := High; |
| |
| when Both => |
| High := Index_Non_Blank (Source, Backward); |
| DL := High - Low + 1; |
| end case; |
| |
| -- Length of the result is the same as length of the source string, |
| -- reuse source shared string. |
| |
| if DL = SR.Last then |
| Reference (SR); |
| DR := SR; |
| |
| -- Otherwise, allocate new shared string |
| |
| else |
| DR := Allocate (DL); |
| DR.Data (1 .. DL) := SR.Data (Low .. High); |
| DR.Last := DL; |
| end if; |
| end if; |
| |
| return (AF.Controlled with Reference => DR); |
| end Trim; |
| |
| procedure Trim |
| (Source : in out Unbounded_String; |
| Side : Trim_End) |
| is |
| SR : constant Shared_String_Access := Source.Reference; |
| DL : Natural; |
| DR : Shared_String_Access; |
| Low : Natural; |
| High : Natural; |
| |
| begin |
| Low := Index_Non_Blank (Source, Forward); |
| |
| -- All blanks, reuse empty shared string |
| |
| if Low = 0 then |
| Reference (Empty_Shared_String'Access); |
| Source.Reference := Empty_Shared_String'Access; |
| Unreference (SR); |
| |
| else |
| case Side is |
| when Left => |
| High := SR.Last; |
| DL := SR.Last - Low + 1; |
| |
| when Right => |
| Low := 1; |
| High := Index_Non_Blank (Source, Backward); |
| DL := High; |
| |
| when Both => |
| High := Index_Non_Blank (Source, Backward); |
| DL := High - Low + 1; |
| end case; |
| |
| -- Length of the result is the same as length of the source string, |
| -- nothing to do. |
| |
| if DL = SR.Last then |
| null; |
| |
| -- Try to reuse existing shared string |
| |
| elsif Can_Be_Reused (SR, DL) then |
| SR.Data (1 .. DL) := SR.Data (Low .. High); |
| SR.Last := DL; |
| |
| -- Otherwise, allocate new shared string |
| |
| else |
| DR := Allocate (DL); |
| DR.Data (1 .. DL) := SR.Data (Low .. High); |
| DR.Last := DL; |
| Source.Reference := DR; |
| Unreference (SR); |
| end if; |
| end if; |
| end Trim; |
| |
| function Trim |
| (Source : Unbounded_String; |
| Left : Maps.Character_Set; |
| Right : Maps.Character_Set) return Unbounded_String |
| is |
| SR : constant Shared_String_Access := Source.Reference; |
| DL : Natural; |
| DR : Shared_String_Access; |
| Low : Natural; |
| High : Natural; |
| |
| begin |
| Low := Index (Source, Left, Outside, Forward); |
| |
| -- Source includes only characters from Left set, reuse empty shared |
| -- string. |
| |
| if Low = 0 then |
| Reference (Empty_Shared_String'Access); |
| DR := Empty_Shared_String'Access; |
| |
| else |
| High := Index (Source, Right, Outside, Backward); |
| DL := Integer'Max (0, High - Low + 1); |
| |
| -- Source includes only characters from Right set or result string |
| -- is empty, reuse empty shared string. |
| |
| if High = 0 or else DL = 0 then |
| Reference (Empty_Shared_String'Access); |
| DR := Empty_Shared_String'Access; |
| |
| -- Otherwise, allocate new shared string and fill it |
| |
| else |
| DR := Allocate (DL); |
| DR.Data (1 .. DL) := SR.Data (Low .. High); |
| DR.Last := DL; |
| end if; |
| end if; |
| |
| return (AF.Controlled with Reference => DR); |
| end Trim; |
| |
| procedure Trim |
| (Source : in out Unbounded_String; |
| Left : Maps.Character_Set; |
| Right : Maps.Character_Set) |
| is |
| SR : constant Shared_String_Access := Source.Reference; |
| DL : Natural; |
| DR : Shared_String_Access; |
| Low : Natural; |
| High : Natural; |
| |
| begin |
| Low := Index (Source, Left, Outside, Forward); |
| |
| -- Source includes only characters from Left set, reuse empty shared |
| -- string. |
| |
| if Low = 0 then |
| Reference (Empty_Shared_String'Access); |
| Source.Reference := Empty_Shared_String'Access; |
| Unreference (SR); |
| |
| else |
| High := Index (Source, Right, Outside, Backward); |
| DL := Integer'Max (0, High - Low + 1); |
| |
| -- Source includes only characters from Right set or result string |
| -- is empty, reuse empty shared string. |
| |
| if High = 0 or else DL = 0 then |
| Reference (Empty_Shared_String'Access); |
| Source.Reference := Empty_Shared_String'Access; |
| Unreference (SR); |
| |
| -- Try to reuse existing shared string |
| |
| elsif Can_Be_Reused (SR, DL) then |
| SR.Data (1 .. DL) := SR.Data (Low .. High); |
| SR.Last := DL; |
| |
| -- Otherwise, allocate new shared string and fill it |
| |
| else |
| DR := Allocate (DL); |
| DR.Data (1 .. DL) := SR.Data (Low .. High); |
| DR.Last := DL; |
| Source.Reference := DR; |
| Unreference (SR); |
| end if; |
| end if; |
| end Trim; |
| |
| --------------------- |
| -- Unbounded_Slice -- |
| --------------------- |
| |
| function Unbounded_Slice |
| (Source : Unbounded_String; |
| Low : Positive; |
| High : Natural) return Unbounded_String |
| is |
| SR : constant Shared_String_Access := Source.Reference; |
| DL : Natural; |
| DR : Shared_String_Access; |
| |
| begin |
| -- Check bounds |
| |
| if Low > SR.Last + 1 or else High > SR.Last then |
| raise Index_Error; |
| |
| -- Result is empty slice, reuse empty shared string |
| |
| elsif Low > High then |
| Reference (Empty_Shared_String'Access); |
| DR := Empty_Shared_String'Access; |
| |
| -- Otherwise, allocate new shared string and fill it |
| |
| else |
| DL := High - Low + 1; |
| DR := Allocate (DL); |
| DR.Data (1 .. DL) := SR.Data (Low .. High); |
| DR.Last := DL; |
| end if; |
| |
| return (AF.Controlled with Reference => DR); |
| end Unbounded_Slice; |
| |
| procedure Unbounded_Slice |
| (Source : Unbounded_String; |
| Target : out Unbounded_String; |
| Low : Positive; |
| High : Natural) |
| is |
| SR : constant Shared_String_Access := Source.Reference; |
| TR : constant Shared_String_Access := Target.Reference; |
| DL : Natural; |
| DR : Shared_String_Access; |
| |
| begin |
| -- Check bounds |
| |
| if Low > SR.Last + 1 or else High > SR.Last then |
| raise Index_Error; |
| |
| -- Result is empty slice, reuse empty shared string |
| |
| elsif Low > High then |
| Reference (Empty_Shared_String'Access); |
| Target.Reference := Empty_Shared_String'Access; |
| Unreference (TR); |
| |
| else |
| DL := High - Low + 1; |
| |
| -- Try to reuse existing shared string |
| |
| if Can_Be_Reused (TR, DL) then |
| TR.Data (1 .. DL) := SR.Data (Low .. High); |
| TR.Last := DL; |
| |
| -- Otherwise, allocate new shared string and fill it |
| |
| else |
| DR := Allocate (DL); |
| DR.Data (1 .. DL) := SR.Data (Low .. High); |
| DR.Last := DL; |
| Target.Reference := DR; |
| Unreference (TR); |
| end if; |
| end if; |
| end Unbounded_Slice; |
| |
| ----------------- |
| -- Unreference -- |
| ----------------- |
| |
| procedure Unreference (Item : not null Shared_String_Access) is |
| |
| procedure Free is |
| new Ada.Unchecked_Deallocation (Shared_String, Shared_String_Access); |
| |
| Aux : Shared_String_Access := Item; |
| |
| begin |
| if System.Atomic_Counters.Decrement (Aux.Counter) then |
| |
| -- Reference counter of Empty_Shared_String must never reach zero |
| |
| pragma Assert (Aux /= Empty_Shared_String'Access); |
| |
| Free (Aux); |
| end if; |
| end Unreference; |
| |
| end Ada.Strings.Unbounded; |