------------------------------------------------------------------------------ -- -- -- GNAT RUN-TIME COMPONENTS -- -- -- -- A D A . S T R I N G S . W I D E _ M A P S -- -- -- -- B o d y -- -- -- -- Copyright (C) 1992-2024, 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 -- -- . -- -- -- -- GNAT was originally developed by the GNAT team at New York University. -- -- Extensive contributions were provided by Ada Core Technologies Inc. -- -- -- ------------------------------------------------------------------------------ with Ada.Unchecked_Deallocation; package body Ada.Strings.Wide_Maps is --------- -- "-" -- --------- function "-" (Left, Right : Wide_Character_Set) return Wide_Character_Set is LS : constant Wide_Character_Ranges_Access := Left.Set; RS : constant Wide_Character_Ranges_Access := Right.Set; Result : Wide_Character_Ranges (1 .. LS'Last + RS'Last); -- Each range on the right can generate at least one more range in -- the result, by splitting one of the left operand ranges. N : Natural := 0; R : Natural := 1; L : Natural := 1; Left_Low : Wide_Character; -- Left_Low is lowest character of the L'th range not yet dealt with begin if LS'Last = 0 or else RS'Last = 0 then return Left; end if; Left_Low := LS (L).Low; while R <= RS'Last loop -- If next right range is below current left range, skip it if RS (R).High < Left_Low then R := R + 1; -- If next right range above current left range, copy remainder -- of the left range to the result elsif RS (R).Low > LS (L).High then N := N + 1; Result (N).Low := Left_Low; Result (N).High := LS (L).High; L := L + 1; exit when L > LS'Last; Left_Low := LS (L).Low; else -- Next right range overlaps bottom of left range if RS (R).Low <= Left_Low then -- Case of right range complete overlaps left range if RS (R).High >= LS (L).High then L := L + 1; exit when L > LS'Last; Left_Low := LS (L).Low; -- Case of right range eats lower part of left range else Left_Low := Wide_Character'Succ (RS (R).High); R := R + 1; end if; -- Next right range overlaps some of left range, but not bottom else N := N + 1; Result (N).Low := Left_Low; Result (N).High := Wide_Character'Pred (RS (R).Low); -- Case of right range splits left range if RS (R).High < LS (L).High then Left_Low := Wide_Character'Succ (RS (R).High); R := R + 1; -- Case of right range overlaps top of left range else L := L + 1; exit when L > LS'Last; Left_Low := LS (L).Low; end if; end if; end if; end loop; -- Copy remainder of left ranges to result if L <= LS'Last then N := N + 1; Result (N).Low := Left_Low; Result (N).High := LS (L).High; loop L := L + 1; exit when L > LS'Last; N := N + 1; Result (N) := LS (L); end loop; end if; return (AF.Controlled with Set => new Wide_Character_Ranges'(Result (1 .. N))); end "-"; --------- -- "=" -- --------- -- The sorted, discontiguous form is canonical, so equality can be used function "=" (Left, Right : Wide_Character_Set) return Boolean is begin return Left.Set.all = Right.Set.all; end "="; ----------- -- "and" -- ----------- function "and" (Left, Right : Wide_Character_Set) return Wide_Character_Set is LS : constant Wide_Character_Ranges_Access := Left.Set; RS : constant Wide_Character_Ranges_Access := Right.Set; Result : Wide_Character_Ranges (1 .. LS'Last + RS'Last); N : Natural := 0; L, R : Natural := 1; begin -- Loop to search for overlapping character ranges while L <= LS'Last and then R <= RS'Last loop if LS (L).High < RS (R).Low then L := L + 1; elsif RS (R).High < LS (L).Low then R := R + 1; -- Here we have LS (L).High >= RS (R).Low -- and RS (R).High >= LS (L).Low -- so we have an overlapping range else N := N + 1; Result (N).Low := Wide_Character'Max (LS (L).Low, RS (R).Low); Result (N).High := Wide_Character'Min (LS (L).High, RS (R).High); if RS (R).High = LS (L).High then L := L + 1; R := R + 1; elsif RS (R).High < LS (L).High then R := R + 1; else L := L + 1; end if; end if; end loop; return (AF.Controlled with Set => new Wide_Character_Ranges'(Result (1 .. N))); end "and"; ----------- -- "not" -- ----------- function "not" (Right : Wide_Character_Set) return Wide_Character_Set is RS : constant Wide_Character_Ranges_Access := Right.Set; Result : Wide_Character_Ranges (1 .. RS'Last + 1); N : Natural := 0; begin if RS'Last = 0 then N := 1; Result (1) := (Low => Wide_Character'First, High => Wide_Character'Last); else if RS (1).Low /= Wide_Character'First then N := N + 1; Result (N).Low := Wide_Character'First; Result (N).High := Wide_Character'Pred (RS (1).Low); end if; for K in 1 .. RS'Last - 1 loop N := N + 1; Result (N).Low := Wide_Character'Succ (RS (K).High); Result (N).High := Wide_Character'Pred (RS (K + 1).Low); end loop; if RS (RS'Last).High /= Wide_Character'Last then N := N + 1; Result (N).Low := Wide_Character'Succ (RS (RS'Last).High); Result (N).High := Wide_Character'Last; end if; end if; return (AF.Controlled with Set => new Wide_Character_Ranges'(Result (1 .. N))); end "not"; ---------- -- "or" -- ---------- function "or" (Left, Right : Wide_Character_Set) return Wide_Character_Set is LS : constant Wide_Character_Ranges_Access := Left.Set; RS : constant Wide_Character_Ranges_Access := Right.Set; Result : Wide_Character_Ranges (1 .. LS'Last + RS'Last); N : Natural; L, R : Natural; begin N := 0; L := 1; R := 1; -- Loop through ranges in output file loop -- If no left ranges left, copy next right range if L > LS'Last then exit when R > RS'Last; N := N + 1; Result (N) := RS (R); R := R + 1; -- If no right ranges left, copy next left range elsif R > RS'Last then N := N + 1; Result (N) := LS (L); L := L + 1; else -- We have two ranges, choose lower one N := N + 1; if LS (L).Low <= RS (R).Low then Result (N) := LS (L); L := L + 1; else Result (N) := RS (R); R := R + 1; end if; -- Loop to collapse ranges into last range loop -- Collapse next length range into current result range -- if possible. if L <= LS'Last and then LS (L).Low <= Wide_Character'Succ (Result (N).High) then Result (N).High := Wide_Character'Max (Result (N).High, LS (L).High); L := L + 1; -- Collapse next right range into current result range -- if possible elsif R <= RS'Last and then RS (R).Low <= Wide_Character'Succ (Result (N).High) then Result (N).High := Wide_Character'Max (Result (N).High, RS (R).High); R := R + 1; -- If neither range collapses, then done with this range else exit; end if; end loop; end if; end loop; return (AF.Controlled with Set => new Wide_Character_Ranges'(Result (1 .. N))); end "or"; ----------- -- "xor" -- ----------- function "xor" (Left, Right : Wide_Character_Set) return Wide_Character_Set is begin return (Left or Right) - (Left and Right); end "xor"; ------------ -- Adjust -- ------------ procedure Adjust (Object : in out Wide_Character_Mapping) is begin Object.Map := new Wide_Character_Mapping_Values'(Object.Map.all); end Adjust; procedure Adjust (Object : in out Wide_Character_Set) is begin Object.Set := new Wide_Character_Ranges'(Object.Set.all); end Adjust; -------------- -- Finalize -- -------------- procedure Finalize (Object : in out Wide_Character_Mapping) is procedure Free is new Ada.Unchecked_Deallocation (Wide_Character_Mapping_Values, Wide_Character_Mapping_Values_Access); begin if Object.Map /= Null_Map'Unrestricted_Access then Free (Object.Map); end if; end Finalize; procedure Finalize (Object : in out Wide_Character_Set) is procedure Free is new Ada.Unchecked_Deallocation (Wide_Character_Ranges, Wide_Character_Ranges_Access); begin if Object.Set /= Null_Range'Unrestricted_Access then Free (Object.Set); end if; end Finalize; ---------------- -- Initialize -- ---------------- procedure Initialize (Object : in out Wide_Character_Mapping) is begin Object := Identity; end Initialize; procedure Initialize (Object : in out Wide_Character_Set) is begin Object := Null_Set; end Initialize; ----------- -- Is_In -- ----------- function Is_In (Element : Wide_Character; Set : Wide_Character_Set) return Boolean is L, R, M : Natural; SS : constant Wide_Character_Ranges_Access := Set.Set; begin L := 1; R := SS'Last; -- Binary search loop. The invariant is that if Element is in any of -- of the constituent ranges it is in one between Set (L) and Set (R). loop if L > R then return False; else M := (L + R) / 2; if Element > SS (M).High then L := M + 1; elsif Element < SS (M).Low then R := M - 1; else return True; end if; end if; end loop; end Is_In; --------------- -- Is_Subset -- --------------- function Is_Subset (Elements : Wide_Character_Set; Set : Wide_Character_Set) return Boolean is ES : constant Wide_Character_Ranges_Access := Elements.Set; SS : constant Wide_Character_Ranges_Access := Set.Set; S : Positive := 1; E : Positive := 1; begin loop -- If no more element ranges, done, and result is true if E > ES'Last then return True; -- If more element ranges, but no more set ranges, result is false elsif S > SS'Last then return False; -- Remove irrelevant set range elsif SS (S).High < ES (E).Low then S := S + 1; -- Get rid of element range that is properly covered by set elsif SS (S).Low <= ES (E).Low and then ES (E).High <= SS (S).High then E := E + 1; -- Otherwise we have a non-covered element range, result is false else return False; end if; end loop; end Is_Subset; --------------- -- To_Domain -- --------------- function To_Domain (Map : Wide_Character_Mapping) return Wide_Character_Sequence is begin return Map.Map.Domain; end To_Domain; ---------------- -- To_Mapping -- ---------------- function To_Mapping (From, To : Wide_Character_Sequence) return Wide_Character_Mapping is Domain : Wide_Character_Sequence (1 .. From'Length); Rangev : Wide_Character_Sequence (1 .. To'Length); N : Natural := 0; begin if From'Length /= To'Length then raise Translation_Error; else pragma Warnings (Off); -- apparent uninit use of Domain for J in From'Range loop for M in 1 .. N loop if From (J) = Domain (M) then raise Translation_Error; elsif From (J) < Domain (M) then Domain (M + 1 .. N + 1) := Domain (M .. N); Rangev (M + 1 .. N + 1) := Rangev (M .. N); Domain (M) := From (J); Rangev (M) := To (J); goto Continue; end if; end loop; Domain (N + 1) := From (J); Rangev (N + 1) := To (J); <> N := N + 1; end loop; pragma Warnings (On); return (AF.Controlled with Map => new Wide_Character_Mapping_Values'( Length => N, Domain => Domain (1 .. N), Rangev => Rangev (1 .. N))); end if; end To_Mapping; -------------- -- To_Range -- -------------- function To_Range (Map : Wide_Character_Mapping) return Wide_Character_Sequence is begin return Map.Map.Rangev; end To_Range; --------------- -- To_Ranges -- --------------- function To_Ranges (Set : Wide_Character_Set) return Wide_Character_Ranges is begin return Set.Set.all; end To_Ranges; ----------------- -- To_Sequence -- ----------------- function To_Sequence (Set : Wide_Character_Set) return Wide_Character_Sequence is SS : constant Wide_Character_Ranges_Access := Set.Set; N : Natural := 0; Count : Natural := 0; begin for J in SS'Range loop Count := Count + (Wide_Character'Pos (SS (J).High) - Wide_Character'Pos (SS (J).Low) + 1); end loop; return Result : Wide_String (1 .. Count) do for J in SS'Range loop for K in SS (J).Low .. SS (J).High loop N := N + 1; Result (N) := K; end loop; end loop; end return; end To_Sequence; ------------ -- To_Set -- ------------ -- Case of multiple range input function To_Set (Ranges : Wide_Character_Ranges) return Wide_Character_Set is Result : Wide_Character_Ranges (Ranges'Range); N : Natural := 0; J : Natural; begin -- The output of To_Set is required to be sorted by increasing Low -- values, and discontiguous, so first we sort them as we enter them, -- using a simple insertion sort. pragma Warnings (Off); -- Kill bogus warning on Result being uninitialized for J in Ranges'Range loop for K in 1 .. N loop if Ranges (J).Low < Result (K).Low then Result (K + 1 .. N + 1) := Result (K .. N); Result (K) := Ranges (J); goto Continue; end if; end loop; Result (N + 1) := Ranges (J); <> N := N + 1; end loop; pragma Warnings (On); -- Now collapse any contiguous or overlapping ranges J := 1; while J < N loop if Result (J).High < Result (J).Low then N := N - 1; Result (J .. N) := Result (J + 1 .. N + 1); elsif Wide_Character'Succ (Result (J).High) >= Result (J + 1).Low then Result (J).High := Wide_Character'Max (Result (J).High, Result (J + 1).High); N := N - 1; Result (J + 1 .. N) := Result (J + 2 .. N + 1); else J := J + 1; end if; end loop; if N > 0 and then Result (N).High < Result (N).Low then N := N - 1; end if; return (AF.Controlled with Set => new Wide_Character_Ranges'(Result (1 .. N))); end To_Set; -- Case of single range input function To_Set (Span : Wide_Character_Range) return Wide_Character_Set is begin if Span.Low > Span.High then return Null_Set; -- This is safe, because there is no procedure with parameter -- Wide_Character_Set of mode "out" or "in out". else return (AF.Controlled with Set => new Wide_Character_Ranges'(1 => Span)); end if; end To_Set; -- Case of wide string input function To_Set (Sequence : Wide_Character_Sequence) return Wide_Character_Set is R : Wide_Character_Ranges (1 .. Sequence'Length); begin for J in R'Range loop R (J) := (Sequence (J), Sequence (J)); end loop; return To_Set (R); end To_Set; -- Case of single wide character input function To_Set (Singleton : Wide_Character) return Wide_Character_Set is begin return (AF.Controlled with Set => new Wide_Character_Ranges'(1 => (Singleton, Singleton))); end To_Set; ----------- -- Value -- ----------- function Value (Map : Wide_Character_Mapping; Element : Wide_Character) return Wide_Character is L, R, M : Natural; MV : constant Wide_Character_Mapping_Values_Access := Map.Map; begin L := 1; R := MV.Domain'Last; -- Binary search loop loop -- If not found, identity if L > R then return Element; -- Otherwise do binary divide else M := (L + R) / 2; if Element < MV.Domain (M) then R := M - 1; elsif Element > MV.Domain (M) then L := M + 1; else -- Element = MV.Domain (M) then return MV.Rangev (M); end if; end if; end loop; end Value; end Ada.Strings.Wide_Maps;