-- -- Copyright (C) 2022-2024, Free Software Foundation, Inc. -- -- SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -- with Ada.Containers.Generic_Array_Sort; with Ada.Unchecked_Deallocation; with System; use type System.Address; package body SPARK.Containers.Formal.Unbounded_Vectors with SPARK_Mode => Off is subtype Int is Long_Long_Integer; -- The minimum size of the underlying array to avoid too much resizing Min_Size : constant Capacity_Range := Count_Type'Min (100, Capacity_Range'Last); ----------------------- -- Local_Subprograms -- ----------------------- function To_Array_Index (Index : Index_Type) return Array_Index; procedure Free_Element is new Ada.Unchecked_Deallocation (Element_Type, Element_Access); procedure Free_Element_Array is new Ada.Unchecked_Deallocation (Element_Array, Element_Array_Access); procedure Insert_Space (Container : in out Vector; Before : Extended_Index; Count : Count_Type := 1); procedure Resize (Container : in out Vector; Size : Count_Type := 0) with -- Widen the sub array of the Vector Global => null, Post => Model (Container) = Model (Container)'Old; --------- -- "=" -- --------- function "=" (Left : Vector; Right : Vector) return Boolean is begin if Left'Address = Right'Address then return True; end if; if Length (Left) /= Length (Right) then return False; end if; for J in 1 .. Length (Left) loop if Left.Elements (J).all /= Right.Elements (J).all then return False; end if; end loop; return True; end "="; ------------ -- Adjust -- ------------ procedure Adjust (V : in out Vector) is Dest : Element_Array_Access; begin if V.Elements = null then return; end if; Dest := new Element_Array (1 .. V.Elements'Length); for Idx in 1 .. Length (V) loop Dest (Idx) := new Element_Type'(V.Elements (Idx).all); end loop; V.Elements := Dest; end Adjust; ------------ -- Append -- ------------ procedure Append (Container : in out Vector; New_Item : Vector) is begin if Is_Empty (New_Item) then return; end if; if Container.Last >= Index_Type'Last then raise Constraint_Error with "vector is already at its maximum length"; end if; Insert (Container, Container.Last + 1, New_Item); end Append; procedure Append (Container : in out Vector; New_Item : Element_Type) is begin Append (Container, New_Item, 1); end Append; procedure Append (Container : in out Vector; New_Item : Element_Type; Count : Count_Type) is begin if Count = 0 then return; end if; if Container.Last >= Index_Type'Last then raise Constraint_Error with "vector is already at its maximum length"; end if; Insert (Container, Container.Last + 1, New_Item, Count); end Append; ------------ -- Assign -- ------------ procedure Assign (Target : in out Vector; Source : Vector) is begin if Target'Address = Source'Address then return; end if; Clear (Target); Append (Target, Source); end Assign; ----------- -- Clear -- ----------- procedure Clear (Container : in out Vector) is begin for J in 1 .. Length (Container) loop if Container.Elements (J) /= null then Free_Element (Container.Elements (J)); end if; end loop; Container.Last := No_Index; end Clear; ------------------------ -- Constant_Reference -- ------------------------ function Constant_Reference (Container : aliased Vector; Index : Index_Type) return not null access constant Element_Type is begin if Index > Container.Last then raise Constraint_Error with "Index is out of range"; end if; return Container.Elements (To_Array_Index (Index)); end Constant_Reference; -------------- -- Contains -- -------------- function Contains (Container : Vector; Item : Element_Type) return Boolean is begin return Find_Index (Container, Item) /= No_Index; end Contains; ---------- -- Copy -- ---------- function Copy (Source : Vector) return Vector is begin return Target : Vector do Assign (Target, Source); end return; end Copy; ------------ -- Delete -- ------------ procedure Delete (Container : in out Vector; Index : Extended_Index) is begin Delete (Container, Index, 1); end Delete; procedure Delete (Container : in out Vector; Index : Extended_Index; Count : Count_Type) is Old_Last : constant Index_Type'Base := Container.Last; Old_Len : constant Count_Type := Length (Container); New_Last : Index_Type'Base; Count2 : Count_Type'Base; -- count of items from Index to Old_Last Off : Count_Type'Base; -- Index expressed as offset from IT'First begin -- Delete removes items from the vector, the number of which is the -- minimum of the specified Count and the items (if any) that exist from -- Index to Container.Last. There are no constraints on the specified -- value of Count (it can be larger than what's available at this -- position in the vector, for example), but there are constraints on -- the allowed values of the Index. -- As a precondition on the generic actual Index_Type, the base type -- must include Index_Type'Pred (Index_Type'First); this is the value -- that Container.Last assumes when the vector is empty. However, we do -- not allow that as the value for Index when specifying which items -- should be deleted, so we must manually check. (That the user is -- allowed to specify the value at all here is a consequence of the -- declaration of the Extended_Index subtype, which includes the values -- in the base range that immediately precede and immediately follow the -- values in the Index_Type.) if Index < Index_Type'First then raise Constraint_Error with "Index is out of range (too small)"; end if; -- We do allow a value greater than Container.Last to be specified as -- the Index, but only if it's immediately greater. This allows the -- corner case of deleting no items from the back end of the vector to -- be treated as a no-op. (It is assumed that specifying an index value -- greater than Last + 1 indicates some deeper flaw in the caller's -- algorithm, so that case is treated as a proper error.) if Index > Old_Last then if Index > Old_Last + 1 then raise Constraint_Error with "Index is out of range (too large)"; end if; return; end if; -- Here and elsewhere we treat deleting 0 items from the container as a -- no-op, so we simply return. if Count = 0 then return; end if; -- We first calculate what's available for deletion starting at -- Index. Here and elsewhere we use the wider of Index_Type'Base and -- Count_Type'Base as the type for intermediate values. (See function -- Length for more information.) if Count_Type'Base'Last >= Index_Type'Pos (Index_Type'Base'Last) then Count2 := Count_Type'Base (Old_Last) - Count_Type'Base (Index) + 1; else Count2 := Count_Type'Base (Old_Last - Index + 1); end if; -- If more elements are requested (Count) for deletion than are -- available (Count2) for deletion beginning at Index, then everything -- from Index is deleted. There are no elements to slide down, and so -- all we need to do is set the value of Container.Last. if Count >= Count2 then -- The elements between Index and Container.Last must be deallocated for Idx in To_Array_Index (Index) .. To_Array_Index (Container.Last) loop Free_Element (Container.Elements (Idx)); end loop; Container.Last := Index - 1; return; end if; -- There are some elements aren't being deleted (the requested count was -- less than the available count), so we must slide them down to Index. -- We first calculate the index values of the respective array slices, -- using the wider of Index_Type'Base and Count_Type'Base as the type -- for intermediate calculations. if Index_Type'Base'Last >= Count_Type'Pos (Count_Type'Last) then Off := Count_Type'Base (Index - Index_Type'First); New_Last := Old_Last - Index_Type'Base (Count); else Off := Count_Type'Base (Index) - Count_Type'Base (Index_Type'First); New_Last := Index_Type'Base (Count_Type'Base (Old_Last) - Count); end if; -- The array index values for each slice have already been determined, -- so we just slide down to Index the elements that weren't deleted. declare EA : Element_Array_Access renames Container.Elements; Idx : constant Count_Type := EA'First + Off; begin -- The elements between Idx and Idx + Count - 1 must be deallocated for J in 0 .. Count - 1 loop Free_Element (Container.Elements (Idx + J)); end loop; EA (Idx .. Old_Len - Count) := EA (Idx + Count .. Old_Len); Container.Last := New_Last; end; end Delete; ------------------ -- Delete_First -- ------------------ procedure Delete_First (Container : in out Vector) is begin Delete_First (Container, 1); end Delete_First; procedure Delete_First (Container : in out Vector; Count : Count_Type) is begin if Count = 0 then return; elsif Count >= Length (Container) then Clear (Container); return; else Delete (Container, Index_Type'First, Count); end if; end Delete_First; ----------------- -- Delete_Last -- ----------------- procedure Delete_Last (Container : in out Vector) is begin Delete_Last (Container, 1); end Delete_Last; procedure Delete_Last (Container : in out Vector; Count : Count_Type) is Index : Index_Type'Base; begin if Count = 0 then return; end if; -- There is no restriction on how large Count can be when deleting -- items. If it is equal or greater than the current length, then this -- is equivalent to clearing the vector. (In particular, there's no need -- for us to actually calculate the new value for Last.) -- If the requested count is less than the current length, then we must -- calculate the new value for Last. For the type we use the widest of -- Index_Type'Base and Count_Type'Base for the intermediate values of -- our calculation. if Count >= Length (Container) then Clear (Container); return; elsif Index_Type'Base'Last >= Count_Type'Pos (Count_Type'Last) then Index := Container.Last - Index_Type'Base (Count) + 1; else Index := Index_Type'Base (Count_Type'Base (Container.Last) - Count + 1); end if; Delete (Container, Index, Count); end Delete_Last; ------------- -- Element -- ------------- function Element (Container : Vector; Index : Extended_Index) return Element_Type is begin if Index > Container.Last or Index = No_Index then raise Constraint_Error with "Index is out of range"; end if; return Container.Elements (To_Array_Index (Index)).all; end Element; -------------- -- Finalize -- -------------- procedure Finalize (V : in out Vector) is begin if V.Elements = null then return; end if; if V.Last /= No_Index then for Idx in Array_Index'First .. To_Array_Index (V.Last) loop Free_Element (V.Elements (Idx)); end loop; end if; V.Last := No_Index; Free_Element_Array (V.Elements); end Finalize; ---------------- -- Find_Index -- ---------------- function Find_Index (Container : Vector; Item : Element_Type; Index : Index_Type := Index_Type'First) return Extended_Index is K : Count_Type; Last : constant Extended_Index := Last_Index (Container); begin K := To_Array_Index (Index); for Indx in Index .. Last loop if Container.Elements (K).all = Item then return Indx; end if; K := K + 1; end loop; return No_Index; end Find_Index; ------------------- -- First_Element -- ------------------- function First_Element (Container : Vector) return Element_Type is begin if Is_Empty (Container) then raise Constraint_Error with "Container is empty"; else return Container.Elements (1).all; end if; end First_Element; ----------------- -- First_Index -- ----------------- function First_Index (Container : Vector) return Index_Type is pragma Unreferenced (Container); begin return Index_Type'First; end First_Index; ------------------ -- Formal_Model -- ------------------ package body Formal_Model is ------------------------- -- Element_Logic_Equal -- ------------------------- function Element_Logic_Equal (Left, Right : Element_Type) return Boolean is begin Check_Or_Fail; return Left = Right; end Element_Logic_Equal; ------------------------- -- M_Elements_In_Union -- ------------------------- function M_Elements_In_Union (Container : M.Sequence; Left : M.Sequence; Right : M.Sequence) return Boolean is begin for Index in Index_Type'First .. M.Last (Container) loop declare Elem : constant Element_Type := Element (Container, Index); Found : Boolean := False; begin for J in Index_Type'First .. M.Last (Left) loop if Element_Logic_Equal (Element (Container, Index), Element (Left, J)) then Found := True; exit; end if; end loop; if not Found then for J in Index_Type'First .. M.Last (Right) loop if Element_Logic_Equal (Element (Container, Index), Element (Right, J)) then Found := True; exit; end if; end loop; end if; if not Found then return False; end if; end; end loop; return True; end M_Elements_In_Union; ------------------------- -- M_Elements_Included -- ------------------------- function M_Elements_Included (Left : M.Sequence; L_Fst : Index_Type := Index_Type'First; L_Lst : Extended_Index; Right : M.Sequence; R_Fst : Index_Type := Index_Type'First; R_Lst : Extended_Index) return Boolean is begin for I in L_Fst .. L_Lst loop declare Found : Boolean := False; J : Extended_Index := R_Fst - 1; begin while not Found and J < R_Lst loop J := J + 1; if Element_Logic_Equal (Element (Left, I), Element (Right, J)) then Found := True; end if; end loop; if not Found then return False; end if; end; end loop; return True; end M_Elements_Included; ------------------------- -- M_Elements_Reversed -- ------------------------- function M_Elements_Reversed (Left : M.Sequence; Right : M.Sequence) return Boolean is L : constant Index_Type := M.Last (Left); begin if L /= M.Last (Right) then return False; end if; for I in Index_Type'First .. L loop if not Element_Logic_Equal (Element (Left, I), Element (Right, L - I + 1)) then return False; end if; end loop; return True; end M_Elements_Reversed; ------------------------ -- M_Elements_Swapped -- ------------------------ function M_Elements_Swapped (Left : M.Sequence; Right : M.Sequence; X : Index_Type; Y : Index_Type) return Boolean is begin if M.Length (Left) /= M.Length (Right) or else not Element_Logic_Equal (Element (Left, X), Element (Right, Y)) or else not Element_Logic_Equal (Element (Left, Y), Element (Right, X)) then return False; end if; for I in Index_Type'First .. M.Last (Left) loop if I /= X and then I /= Y and then not Element_Logic_Equal (Element (Left, I), Element (Right, I)) then return False; end if; end loop; return True; end M_Elements_Swapped; ----------- -- Model -- ----------- function Model (Container : Vector) return M.Sequence is R : M.Sequence; begin for Position in 1 .. Length (Container) loop R := M.Add (R, Container.Elements (Position).all); end loop; return R; end Model; end Formal_Model; --------------------- -- Generic_Sorting -- --------------------- package body Generic_Sorting with SPARK_Mode => Off is ------------------ -- Formal_Model -- ------------------ function Strictly_Less (L, R : Element_Access) return Boolean with -- Compare Element with access check Global => null; package body Formal_Model is ----------------------- -- M_Elements_Sorted -- ----------------------- function M_Elements_Sorted (Container : M.Sequence) return Boolean is begin if M.Length (Container) = 0 then return True; end if; declare E1 : Element_Type := Element (Container, Index_Type'First); begin for I in Index_Type'First + 1 .. M.Last (Container) loop declare E2 : constant Element_Type := Element (Container, I); begin if E2 < E1 then return False; end if; E1 := E2; end; end loop; end; return True; end M_Elements_Sorted; end Formal_Model; --------------- -- Is_Sorted -- --------------- function Is_Sorted (Container : Vector) return Boolean is L : constant Capacity_Range := Length (Container); begin for J in 1 .. L - 1 loop if Container.Elements (J + 1).all < Container.Elements (J).all then return False; end if; end loop; return True; end Is_Sorted; ----------- -- Merge -- ----------- procedure Merge (Target : in out Vector; Source : in out Vector) is I : Count_Type; J : Count_Type; begin if Target'Address = Source'Address then raise Program_Error with "Target and Source denote same container"; end if; if Length (Source) = 0 then return; end if; if Length (Target) = 0 then Move (Target => Target, Source => Source); return; end if; I := Length (Target); -- Check if the merge of Source and Target will fit in a Vector if Count_Type'Base (Last_Count) - I < Length (Source) then raise Constraint_Error with "Source too large for Target"; end if; Insert_Space (Target, Target.Last + 1, Length (Source)); declare TA : Element_Array_Access renames Target.Elements; SA : Element_Array_Access renames Source.Elements; begin J := Length (Target); while Length (Source) /= 0 loop if I = 0 then TA (1 .. J) := SA (1 .. Length (Source)); Source.Last := No_Index; exit; end if; if SA (Length (Source)).all < TA (I).all then TA (J) := TA (I); I := I - 1; else TA (J) := SA (Length (Source)); Source.Last := Source.Last - 1; end if; J := J - 1; end loop; end; end Merge; ---------- -- Sort -- ---------- procedure Sort (Container : in out Vector) is procedure Sort is new Generic_Array_Sort (Index_Type => Array_Index, Element_Type => Element_Access, Array_Type => Element_Array, "<" => Strictly_Less); Len : constant Capacity_Range := Length (Container); begin if Container.Last <= Index_Type'First then return; else Sort (Container.Elements (1 .. Len)); end if; end Sort; ------------------- -- Strictly_Less -- ------------------- function Strictly_Less (L, R : Element_Access) return Boolean is (if L = null then R = null elsif R = null then L = null else L.all < R.all); end Generic_Sorting; ----------------- -- Has_Element -- ----------------- function Has_Element (Container : Vector; Position : Extended_Index) return Boolean is begin return Position in First_Index (Container) .. Last_Index (Container); end Has_Element; ------------ -- Insert -- ------------ procedure Insert (Container : in out Vector; Before : Extended_Index; New_Item : Element_Type) is begin Insert (Container, Before, New_Item, 1); end Insert; procedure Insert (Container : in out Vector; Before : Extended_Index; New_Item : Element_Type; Count : Count_Type) is J : Count_Type'Base; -- scratch begin -- Use Insert_Space to create the "hole" (the destination slice) Insert_Space (Container, Before, Count); J := To_Array_Index (Before); Container.Elements (J .. J - 1 + Count) := (others => new Element_Type'(New_Item)); end Insert; procedure Insert (Container : in out Vector; Before : Extended_Index; New_Item : Vector) is N : constant Count_Type := Length (New_Item); B : Count_Type; -- index Before converted to Count_Type begin if Container'Address = New_Item'Address then raise Program_Error with "Container and New_Item denote same container"; end if; -- Use Insert_Space to create the "hole" (the destination slice) into -- which we copy the source items. Insert_Space (Container, Before, Count => N); if N = 0 then -- There's nothing else to do here (vetting of parameters was -- performed already in Insert_Space), so we simply return. return; end if; B := To_Array_Index (Before); -- Make a copy of all the elements to avoid sharing for J in 1 .. N loop Container.Elements (B - 1 + J) := new Element_Type'(New_Item.Elements (J).all); end loop; end Insert; ------------------ -- Insert_Space -- ------------------ procedure Insert_Space (Container : in out Vector; Before : Extended_Index; Count : Count_Type := 1) is Old_Length : constant Count_Type := Length (Container); Max_Length : Count_Type'Base; -- determined from range of Index_Type New_Length : Count_Type'Base; -- sum of current length and Count Index : Index_Type'Base; -- scratch for intermediate values J : Count_Type'Base; -- scratch begin -- As a precondition on the generic actual Index_Type, the base type -- must include Index_Type'Pred (Index_Type'First); this is the value -- that Container.Last assumes when the vector is empty. However, we do -- not allow that as the value for Index when specifying where the new -- items should be inserted, so we must manually check. (That the user -- is allowed to specify the value at all here is a consequence of the -- declaration of the Extended_Index subtype, which includes the values -- in the base range that immediately precede and immediately follow the -- values in the Index_Type.) if Before < Index_Type'First then raise Constraint_Error with "Before index is out of range (too small)"; end if; -- We do allow a value greater than Container.Last to be specified as -- the Index, but only if it's immediately greater. This allows for the -- case of appending items to the back end of the vector. (It is assumed -- that specifying an index value greater than Last + 1 indicates some -- deeper flaw in the caller's algorithm, so that case is treated as a -- proper error.) if Before > Container.Last and then Before - 1 > Container.Last then raise Constraint_Error with "Before index is out of range (too large)"; end if; -- We treat inserting 0 items into the container as a no-op, so we -- simply return. if Count = 0 then return; end if; -- There are two constraints we need to satisfy. The first constraint is -- that a container cannot have more than Count_Type'Last elements, so -- we must check the sum of the current length and the insertion count. -- Note that the value cannot be simply added because the result may -- overflow. if Old_Length > Count_Type'Last - Count then raise Constraint_Error with "Count is out of range"; end if; -- It is now safe compute the length of the new vector, without fear of -- overflow. New_Length := Old_Length + Count; -- The second constraint is that the new Last index value cannot exceed -- Index_Type'Last. In each branch below, we calculate the maximum -- length (computed from the range of values in Index_Type), and then -- compare the new length to the maximum length. If the new length is -- acceptable, then we compute the new last index from that. if Index_Type'Base'Last >= Count_Type'Pos (Count_Type'Last) then -- We have to handle the case when there might be more values in the -- range of Index_Type than in the range of Count_Type. if Index_Type'First <= 0 then -- We know that No_Index (the same as Index_Type'First - 1) is -- less than 0, so it is safe to compute the following sum without -- fear of overflow. Index := No_Index + Index_Type'Base (Count_Type'Last); if Index <= Index_Type'Last then -- We have determined that range of Index_Type has at least as -- many values as in Count_Type, so Count_Type'Last is the -- maximum number of items that are allowed. Max_Length := Count_Type'Last; else -- The range of Index_Type has fewer values than in Count_Type, -- so the maximum number of items is computed from the range of -- the Index_Type. Max_Length := Count_Type'Base (Index_Type'Last - No_Index); end if; else -- No_Index is equal or greater than 0, so we can safely compute -- the difference without fear of overflow (which we would have to -- worry about if No_Index were less than 0, but that case is -- handled above). if Index_Type'Last - No_Index >= Count_Type'Pos (Count_Type'Last) then -- We have determined that range of Index_Type has at least as -- many values as in Count_Type, so Count_Type'Last is the -- maximum number of items that are allowed. Max_Length := Count_Type'Last; else -- The range of Index_Type has fewer values than in Count_Type, -- so the maximum number of items is computed from the range of -- the Index_Type. Max_Length := Count_Type'Base (Index_Type'Last - No_Index); end if; end if; elsif Index_Type'First <= 0 then -- We know that No_Index (the same as Index_Type'First - 1) is less -- than 0, so it is safe to compute the following sum without fear of -- overflow. J := Count_Type'Base (No_Index) + Count_Type'Last; if J <= Count_Type'Base (Index_Type'Last) then -- We have determined that range of Index_Type has at least as -- many values as in Count_Type, so Count_Type'Last is the maximum -- number of items that are allowed. Max_Length := Count_Type'Last; else -- The range of Index_Type has fewer values than Count_Type does, -- so the maximum number of items is computed from the range of -- the Index_Type. Max_Length := Count_Type'Base (Index_Type'Last) - Count_Type'Base (No_Index); end if; else -- No_Index is equal or greater than 0, so we can safely compute the -- difference without fear of overflow (which we would have to worry -- about if No_Index were less than 0, but that case is handled -- above). Max_Length := Count_Type'Base (Index_Type'Last) - Count_Type'Base (No_Index); end if; -- We have just computed the maximum length (number of items). We must -- now compare the requested length to the maximum length, as we do not -- allow a vector expand beyond the maximum (because that would create -- an internal array with a last index value greater than -- Index_Type'Last, with no way to index those elements). if New_Length > Max_Length then raise Constraint_Error with "Count is out of range"; -- Raise Capacity_Error if the new length exceeds the container's -- capacity. elsif New_Length > Last_Count then raise Capacity_Error with "New length is larger than capacity"; end if; J := To_Array_Index (Before); declare EA : Element_Array_Access renames Container.Elements; begin if Container.Elements = null or else Container.Elements'Length < New_Length then -- The array must be resized Resize (Container, New_Length); end if; if Before <= Container.Last then -- The new items are being inserted before some existing -- elements, so we must slide the existing elements up to their -- new home. EA (J + Count .. New_Length) := EA (J .. Old_Length); EA (J .. J + Count - 1) := (others => null); end if; end; if Index_Type'Base'Last >= Count_Type'Pos (Count_Type'Last) then Container.Last := No_Index + Index_Type'Base (New_Length); else Container.Last := Index_Type'Base (Count_Type'Base (No_Index) + New_Length); end if; end Insert_Space; -------------- -- Is_Empty -- -------------- function Is_Empty (Container : Vector) return Boolean is begin return Last_Index (Container) < Index_Type'First; end Is_Empty; ------------------ -- Last_Element -- ------------------ function Last_Element (Container : Vector) return Element_Type is begin if Is_Empty (Container) then raise Constraint_Error with "Container is empty"; else return Container.Elements (Length (Container)).all; end if; end Last_Element; ---------------- -- Last_Index -- ---------------- function Last_Index (Container : Vector) return Extended_Index is begin return Container.Last; end Last_Index; ------------ -- Length -- ------------ function Length (Container : Vector) return Capacity_Range is L : constant Int := Int (Container.Last); F : constant Int := Int (Index_Type'First); N : constant Int'Base := L - F + 1; begin return Capacity_Range (N); end Length; ---------- -- Move -- ---------- procedure Move (Target : in out Vector; Source : in out Vector) is begin if Target'Address = Source'Address then return; end if; if Target.Elements /= null then Clear (Target); Free_Element_Array (Target.Elements); end if; Target.Elements := Source.Elements; Target.Last := Source.Last; Source.Elements := null; Source.Last := No_Index; end Move; ------------ -- Prepend -- ------------ procedure Prepend (Container : in out Vector; New_Item : Vector) is begin Insert (Container, Index_Type'First, New_Item); end Prepend; procedure Prepend (Container : in out Vector; New_Item : Element_Type) is begin Prepend (Container, New_Item, 1); end Prepend; procedure Prepend (Container : in out Vector; New_Item : Element_Type; Count : Count_Type) is begin Insert (Container, Index_Type'First, New_Item, Count); end Prepend; --------------- -- Reference -- --------------- function Reference (Container : not null access Vector; Index : Index_Type) return not null access Element_Type is begin if Index > Container.Last then raise Constraint_Error with "Index is out of range"; end if; return Container.Elements (To_Array_Index (Index)); end Reference; --------------------- -- Replace_Element -- --------------------- procedure Replace_Element (Container : in out Vector; Index : Index_Type; New_Item : Element_Type) is Index_Arr : constant Array_Index := To_Array_Index (Index); begin if Index > Container.Last then raise Constraint_Error with "Index is out of range"; end if; Free_Element (Container.Elements (Index_Arr)); Container.Elements (Index_Arr) := new Element_Type'(New_Item); end Replace_Element; ------------ -- Resize -- ------------ procedure Resize (Container : in out Vector; Size : Count_Type := 0) is EA : Element_Array_Access renames Container.Elements; begin if EA = null then EA := new Element_Array (1 .. Count_Type'Max (Min_Size, Size)); Container.Last := No_Index; return; end if; declare New_Length : constant Capacity_Range := (if Container.Elements'Length < Capacity_Range'Last / 2 then (if Container.Elements'Length * 2 < Size then Size else Container.Elements'Length * 2) else Capacity_Range'Last); New_Array : constant Element_Array_Access := new Element_Array (1 .. Count_Type'Max (Min_Size, New_Length)); Last : constant Count_Type := Length (Container); begin New_Array (1 .. Last) := EA (1 .. Last); Free_Element_Array (EA); EA := New_Array; end; end Resize; ---------------------- -- Reverse_Elements -- ---------------------- procedure Reverse_Elements (Container : in out Vector) is begin if Length (Container) <= 1 then return; end if; declare I, J : Capacity_Range; E : Element_Array_Access renames Container.Elements; begin I := 1; J := Length (Container); while I < J loop declare EI : constant Element_Access := E (I); begin E (I) := E (J); E (J) := EI; end; I := I + 1; J := J - 1; end loop; end; end Reverse_Elements; ------------------------ -- Reverse_Find_Index -- ------------------------ function Reverse_Find_Index (Container : Vector; Item : Element_Type; Index : Index_Type := Index_Type'Last) return Extended_Index is Last : Index_Type'Base; K : Count_Type'Base; begin if Is_Empty (Container) then return No_Index; end if; if Index > Last_Index (Container) then Last := Last_Index (Container); else Last := Index; end if; K := To_Array_Index (Last); for Indx in reverse Index_Type'First .. Last loop if Container.Elements (K).all = Item then return Indx; end if; K := K - 1; end loop; return No_Index; end Reverse_Find_Index; ---------- -- Swap -- ---------- procedure Swap (Container : in out Vector; I : Index_Type; J : Index_Type) is begin if I > Container.Last then raise Constraint_Error with "I index is out of range"; end if; if J > Container.Last then raise Constraint_Error with "J index is out of range"; end if; if I = J then return; end if; declare II : constant Array_Index := To_Array_Index (I); JJ : constant Array_Index := To_Array_Index (J); EI : Element_Access renames Container.Elements (II); EJ : Element_Access renames Container.Elements (JJ); EI_Copy : constant Element_Access := EI; begin EI := EJ; EJ := EI_Copy; end; end Swap; -------------------- -- To_Array_Index -- -------------------- function To_Array_Index (Index : Index_Type) return Array_Index is Offset : Count_Type'Base; begin -- We know that -- Index >= Index_Type'First -- hence we also know that -- Index - Index_Type'First >= 0 -- The issue is that even though 0 is guaranteed to be a value in -- the type Index_Type'Base, there's no guarantee that the difference -- is a value in that type. To prevent overflow we use the wider -- of Count_Type'Base and Index_Type'Base to perform intermediate -- calculations. if Index_Type'Base'Last >= Count_Type'Pos (Count_Type'Last) then Offset := Count_Type'Base (Index - Index_Type'First); else Offset := Count_Type'Base (Index) - Count_Type'Base (Index_Type'First); end if; -- The array index subtype for all container element arrays always -- starts with 1. return 1 + Offset; end To_Array_Index; --------------- -- To_Vector -- --------------- function To_Vector (New_Item : Element_Type; Length : Capacity_Range) return Vector is begin if Length = 0 then return Empty_Vector; end if; declare Vec : Vector; begin Append (Vec, New_Item, Length); return Vec; end; end To_Vector; end SPARK.Containers.Formal.Unbounded_Vectors;