-- Copyright 1994 Grady Booch -- Copyright 1994-1997 David Weller -- Copyright 1998-2002 Simon Wright -- This package is free software; you can redistribute it and/or -- modify it under terms of the GNU General Public License as -- published by the Free Software Foundation; either version 2, or -- (at your option) any later version. This package is distributed in -- the hope that it will be useful, but WITHOUT ANY WARRANTY; without -- even the implied warranty of MERCHANTABILITY or FITNESS FOR A -- PARTICULAR PURPOSE. See the GNU General Public License for more -- details. You should have received a copy of the GNU General Public -- License distributed with this package; see file COPYING. If not, -- write to the Free Software Foundation, 59 Temple Place - Suite -- 330, Boston, MA 02111-1307, USA. -- As a special exception, if other files instantiate generics from -- this unit, or you link this unit with other files to produce an -- executable, this unit does not by itself cause the resulting -- executable to be covered by the GNU General Public License. This -- exception does not however invalidate any other reasons why the -- executable file might be covered by the GNU Public License. -- $RCSfile: bc-containers-lists-single.adb,v $ -- $Revision: 1.18.2.1 $ -- $Date: 2002/12/29 16:41:12 $ -- $Author: simon $ with Ada.Unchecked_Deallocation; with BC.Support.Exceptions; with System.Address_To_Access_Conversions; package body BC.Containers.Lists.Single is package BSE renames BC.Support.Exceptions; procedure Assert is new BSE.Assert ("BC.Containers.Lists.Single"); -- We can't take 'Access of non-aliased components. But if we -- alias discriminated objects they become constrained - even if -- the discriminant has a default. package Allow_Element_Access is new System.Address_To_Access_Conversions (Item); function Create (I : Item; Next : Single_Node_Ref) return Single_Node_Ref; pragma Inline (Create); function Create (I : Item; Next : Single_Node_Ref) return Single_Node_Ref is begin return new Single_Node'(Element => I, Next => Next, Count => 1); end Create; procedure Delete is new Ada.Unchecked_Deallocation (Single_Node, Single_Node_Ref); function "=" (L, R : List) return Boolean is begin return L.Rep = R.Rep; end "="; procedure Clear (L : in out List) is Curr : Single_Node_Ref := L.Rep; Ptr : Single_Node_Ref; begin while Curr /= null loop Ptr := Curr; Curr := Curr.Next; if Ptr.Count > 1 then Ptr.Count := Ptr.Count - 1; exit; else Delete (Ptr); end if; end loop; L.Rep := null; end Clear; procedure Insert (L : in out List; Elem : Item) is begin L.Rep := Create (Elem, Next => L.Rep); end Insert; procedure Insert (L : in out List; From_List : in out List) is Ptr : Single_Node_Ref := From_List.Rep; begin if Ptr /= null then while Ptr.Next /= null loop Ptr := Ptr.Next; end loop; end if; Ptr.Next := L.Rep; L.Rep := From_List.Rep; L.Rep.Count := L.Rep.Count + 1; end Insert; procedure Insert (L : in out List; Elem : Item; Before : Positive) is Prev : Single_Node_Ref; Curr : Single_Node_Ref := L.Rep; Index : Positive := 1; begin if Curr = null or else Before = 1 then Insert (L, Elem); else while Curr /= null and then Index < Before loop Prev := Curr; Curr := Curr.Next; Index := Index + 1; end loop; Assert (Curr /= null, BC.Range_Error'Identity, "Insert", BSE.Invalid_Index); Prev.Next := Create (Elem, Next => Curr); end if; end Insert; procedure Insert (L : in out List; From_List : in out List; Before : Positive) is Prev : Single_Node_Ref; Curr : Single_Node_Ref := L.Rep; Ptr : Single_Node_Ref := From_List.Rep; Index : Positive := 1; begin if Ptr /= null then if Curr = null or else Before = 1 then Insert (L, From_List); else while Curr /= null and then Index < Before loop Prev := Curr; Curr := Curr.Next; Index := Index + 1; end loop; Assert (Curr /= null, BC.Range_Error'Identity, "Insert", BSE.Invalid_Index); while Ptr.Next /= null loop Ptr := Ptr.Next; end loop; Ptr.Next := Curr; Prev.Next := From_List.Rep; From_List.Rep.Count := From_List.Rep.Count + 1; end if; end if; end Insert; procedure Append (L : in out List; Elem : Item) is Curr : Single_Node_Ref := L.Rep; begin if Curr /= null then while Curr.Next /= null loop Curr := Curr.Next; end loop; Curr.Next := Create (Elem, Next => null); else L.Rep := Create (Elem, Next => null); end if; end Append; procedure Append (L : in out List; From_List : in out List) is Curr : Single_Node_Ref := L.Rep; begin if From_List.Rep /= null then if Curr /= null then while Curr.Next /= null loop Curr := Curr.Next; end loop; end if; if Curr /= null then Curr.Next := From_List.Rep; else L.Rep := From_List.Rep; end if; From_List.Rep.Count := From_List.Rep.Count + 1; end if; end Append; procedure Append (L : in out List; Elem : Item; After : Positive) is Curr : Single_Node_Ref := L.Rep; Index : Positive := 1; begin if Curr = null then Append (L, Elem); else while Curr /= null and then Index < After loop Curr := Curr.Next; Index := Index + 1; end loop; Assert (Curr /= null, BC.Range_Error'Identity, "Append", BSE.Invalid_Index); Curr.Next := Create (Elem, Next => Curr.Next); end if; end Append; procedure Append (L : in out List; From_List : in out List; After : Positive) is Curr : Single_Node_Ref := L.Rep; Ptr : Single_Node_Ref := From_List.Rep; Index : Positive := 1; begin if Ptr /= null then if Curr = null then Append (L, From_List); else while Curr /= null and then Index < After loop Curr := Curr.Next; Index := Index + 1; end loop; Assert (Curr /= null, BC.Range_Error'Identity, "Append", BSE.Invalid_Index); while Ptr.Next /= null loop Ptr := Ptr.Next; end loop; Ptr.Next := Curr.Next; Curr.Next := From_List.Rep; From_List.Rep.Count := From_List.Rep.Count + 1; end if; end if; end Append; procedure Remove (L : in out List; From : Positive) is Prev : Single_Node_Ref; Curr : Single_Node_Ref := L.Rep; Index : Positive := 1; begin while Curr /= null and then Index < From loop Prev := Curr; Curr := Curr.Next; Index := Index + 1; end loop; Assert (Curr /= null, BC.Range_Error'Identity, "Remove", BSE.Invalid_Index); -- Ensure we're not removing an aliased element. Assert (Curr.Count = 1, BC.Referenced'Identity, "Remove", BSE.Referenced); if Prev /= null then Prev.Next := Curr.Next; else L.Rep := Curr.Next; end if; if Curr.Count > 1 then Curr.Count := Curr.Count - 1; else Delete (Curr); end if; end Remove; procedure Purge (L : in out List; From : Positive) is Prev : Single_Node_Ref; Curr : Single_Node_Ref := L.Rep; Ptr : Single_Node_Ref; Index : Positive := 1; begin while Curr /= null and then Index < From loop Prev := Curr; Curr := Curr.Next; Index := Index + 1; end loop; Assert (Curr /= null, BC.Range_Error'Identity, "Purge", BSE.Invalid_Index); if Prev /= null then Prev.Next := null; else L.Rep := null; end if; while Curr /= null loop Ptr := Curr; Curr := Curr.Next; if Ptr.Count > 1 then Ptr.Count := Ptr.Count - 1; exit; else Delete (Ptr); end if; end loop; end Purge; procedure Purge (L : in out List; From : Positive; Count : Positive) is Prev, Ptr : Single_Node_Ref; Curr : Single_Node_Ref := L.Rep; Index : Positive := 1; Shared_Node_Found : Boolean := False; begin while Curr /= null and then Index < From loop Prev := Curr; Curr := Curr.Next; Index := Index + 1; end loop; Assert (Curr /= null, BC.Range_Error'Identity, "Purge", BSE.Invalid_Index); if Prev /= null then Prev.Next := null; else L.Rep := null; end if; Index := 1; while Curr /= null and then Index <= Count loop Ptr := Curr; Curr := Curr.Next; if not Shared_Node_Found then if Ptr.Count > 1 then Ptr.Count := Ptr.Count - 1; Shared_Node_Found := True; else Delete (Ptr); end if; end if; Index := Index + 1; end loop; if Shared_Node_Found then Ptr.Next := null; end if; if Curr /= null then if Prev /= null then Prev.Next := Curr; else L.Rep := Curr; end if; end if; end Purge; procedure Preserve (L : in out List; From : Positive) is Temp : List; begin Share (Temp, L, From); Share_Head (L, Temp); end Preserve; procedure Preserve (L : in out List; From : Positive; Count : Positive) is begin Preserve (L, From); if Length (L) > Count then Purge (L, Count + 1); -- we start at 1, remember! end if; end Preserve; procedure Share (L : in out List; With_List : List; Starting_At : Positive) is Ptr : Single_Node_Ref := With_List.Rep; Index : Positive := 1; begin Assert (Ptr /= null, BC.Is_Null'Identity, "Share", BSE.Is_Null); while Ptr /= null and then Index < Starting_At loop Ptr := Ptr.Next; Index := Index + 1; end loop; Assert (Ptr /= null, BC.Range_Error'Identity, "Share", BSE.Invalid_Index); Clear (L); L.Rep := Ptr; L.Rep.Count := L.Rep.Count + 1; end Share; procedure Share_Head (L : in out List; With_List : in List) is begin Assert (With_List.Rep /= null, BC.Is_Null'Identity, "Share_Head", BSE.Is_Null); Clear (L); L.Rep := With_List.Rep; L.Rep.Count := L.Rep.Count + 1; end Share_Head; procedure Share_Foot (L : in out List; With_List : in List) is Ptr : Single_Node_Ref := With_List.Rep; begin Assert (Ptr /= null, BC.Is_Null'Identity, "Share_Foot", BSE.Is_Null); Clear (L); while Ptr.Next /= null loop Ptr := Ptr.Next; end loop; L.Rep := Ptr; L.Rep.Count := L.Rep.Count + 1; end Share_Foot; procedure Swap_Tail (L : in out List; With_List : in out List) is Curr : Single_Node_Ref; begin Assert (L.Rep /= null, BC.Is_Null'Identity, "Swap_Tail", BSE.Is_Null); Curr := L.Rep.Next; L.Rep.Next := With_List.Rep; With_List.Rep := Curr; end Swap_Tail; procedure Tail (L : in out List) is Curr : Single_Node_Ref := L.Rep; begin Assert (L.Rep /= null, BC.Is_Null'Identity, "Tail", BSE.Is_Null); L.Rep := L.Rep.Next; if L.Rep /= null then L.Rep.Count := L.Rep.Count + 1; end if; if Curr.Count > 1 then Curr.Count := Curr.Count - 1; else Delete (Curr); end if; end Tail; procedure Set_Head (L : in out List; Elem : Item) is begin Assert (L.Rep /= null, BC.Is_Null'Identity, "Set_Head", BSE.Is_Null); L.Rep.Element := Elem; end Set_Head; procedure Set_Item (L : in out List; Elem : Item; At_Loc : Positive) is Curr : Single_Node_Ref := L.Rep; Index : Positive := 1; begin while Curr /= null and then Index < At_Loc loop Curr := Curr.Next; Index := Index + 1; end loop; Assert (Curr /= null, BC.Range_Error'Identity, "Set_Item", BSE.Invalid_Index); Curr.Element := Elem; end Set_Item; function Length (L : List) return Natural is Curr : Single_Node_Ref := L.Rep; Count : Natural := 0; begin while Curr /= null loop Curr := Curr.Next; Count := Count + 1; end loop; return Count; end Length; function Is_Null (L : List) return Boolean is begin return L.Rep = null; end Is_Null; function Is_Shared (L : List) return Boolean is begin return L.Rep /= null and then L.Rep.Count > 1; end Is_Shared; function Head (L : List) return Item is begin Assert (L.Rep /= null, BC.Is_Null'Identity, "Head", BSE.Is_Null); return L.Rep.Element; end Head; procedure Process_Head (L : in out List) is begin Assert (L.Rep /= null, BC.Is_Null'Identity, "Process_Head", BSE.Is_Null); Process (L.Rep.Element); end Process_Head; function Foot (L : List) return Item is Curr : Single_Node_Ref := L.Rep; begin Assert (L.Rep /= null, BC.Is_Null'Identity, "Foot", BSE.Is_Null); while Curr.Next /= null loop Curr := Curr.Next; end loop; return Curr.Element; end Foot; procedure Process_Foot (L : in out List) is Curr : Single_Node_Ref := L.Rep; begin Assert (L.Rep /= null, BC.Is_Null'Identity, "Process_Foot", BSE.Is_Null); while Curr.Next /= null loop Curr := Curr.Next; end loop; Process (Curr.Element); end Process_Foot; function Item_At (L : List; Index : Positive) return Item is begin return Item_At (L, Index).all; end Item_At; package Address_Conversions is new System.Address_To_Access_Conversions (List); function New_Iterator (For_The_List : List) return Iterator'Class is Result : List_Iterator; begin Result.For_The_Container := Address_Conversions.To_Pointer (For_The_List'Address).all'Access; Reset (Result); return Result; end New_Iterator; function Item_At (L : List; Index : Positive) return Item_Ptr is Curr : Single_Node_Ref := L.Rep; Loc : Positive := 1; begin Assert (L.Rep /= null, BC.Is_Null'Identity, "Item_At", BSE.Is_Null); while Curr /= null and then Loc < Index loop Curr := Curr.Next; Loc := Loc + 1; end loop; Assert (Curr /= null, BC.Range_Error'Identity, "Item_At", BSE.Invalid_Index); return Item_Ptr (Allow_Element_Access.To_Pointer (Curr.Element'Address)); end Item_At; procedure Initialize (L : in out List) is pragma Warnings (Off, L); begin null; end Initialize; procedure Adjust (L : in out List) is begin if L.Rep /= null then L.Rep.Count := L.Rep.Count + 1; end if; end Adjust; procedure Finalize (L : in out List) is begin Clear (L); end Finalize; procedure Reset (It : in out List_Iterator) is L : List'Class renames List'Class (It.For_The_Container.all); begin It.Index := L.Rep; end Reset; procedure Next (It : in out List_Iterator) is begin if It.Index /= null then It.Index := It.Index.Next; end if; end Next; function Is_Done (It : List_Iterator) return Boolean is begin return It.Index = null; end Is_Done; function Current_Item (It : List_Iterator) return Item is begin if Is_Done (It) then raise BC.Not_Found; end if; return It.Index.Element; end Current_Item; function Current_Item_Ptr (It : List_Iterator) return Item_Ptr is begin if Is_Done (It) then raise BC.Not_Found; end if; return Item_Ptr (Allow_Element_Access.To_Pointer (It.Index.Element'Address)); end Current_Item_Ptr; procedure Delete_Item_At (It : in out List_Iterator) is L : List'Class renames List'Class (It.For_The_Container.all); Prev : Single_Node_Ref; Curr : Single_Node_Ref := L.Rep; begin if Is_Done (It) then raise BC.Not_Found; end if; while Curr /= null and then Curr /= It.Index loop Prev := Curr; Curr := Curr.Next; end loop; Assert (Curr /= null, BC.Range_Error'Identity, "Delete_Item_At", BSE.Invalid_Index); -- we need a writable version of the Iterator declare package Conversions is new System.Address_To_Access_Conversions (List_Iterator'Class); P : Conversions.Object_Pointer := Conversions.To_Pointer (It'Address); begin P.Index := Curr.Next; end; if Prev /= null then Prev.Next := Curr.Next; else L.Rep := Curr.Next; end if; if Curr.Count > 1 then Curr.Count := Curr.Count - 1; else Delete (Curr); end if; end Delete_Item_At; Empty_Container : List; pragma Warnings (Off, Empty_Container); function Null_Container return List is begin return Empty_Container; end Null_Container; end BC.Containers.Lists.Single;