1 ------------------------------------------------------------------------------
3 -- GNAT LIBRARY COMPONENTS --
5 -- A D A . C O N T A I N E R S . D O U B L Y _ L I N K E D _ L I S T S --
9 -- Copyright (C) 2004-2023, Free Software Foundation, Inc. --
11 -- GNAT is free software; you can redistribute it and/or modify it under --
12 -- terms of the GNU General Public License as published by the Free Soft- --
13 -- ware Foundation; either version 3, or (at your option) any later ver- --
14 -- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
15 -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
16 -- or FITNESS FOR A PARTICULAR PURPOSE. --
18 -- As a special exception under Section 7 of GPL version 3, you are granted --
19 -- additional permissions described in the GCC Runtime Library Exception, --
20 -- version 3.1, as published by the Free Software Foundation. --
22 -- You should have received a copy of the GNU General Public License and --
23 -- a copy of the GCC Runtime Library Exception along with this program; --
24 -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see --
25 -- <http://www.gnu.org/licenses/>. --
27 -- This unit was originally developed by Matthew J Heaney. --
28 ------------------------------------------------------------------------------
30 with Ada
.Unchecked_Deallocation
;
32 with Ada
.Containers
.Stable_Sorting
; use Ada
.Containers
.Stable_Sorting
;
34 with System
; use type System
.Address
;
35 with System
.Put_Images
;
37 package body Ada
.Containers
.Doubly_Linked_Lists
with
41 pragma Warnings
(Off
, "variable ""Busy*"" is not referenced");
42 pragma Warnings
(Off
, "variable ""Lock*"" is not referenced");
43 -- See comment in Ada.Containers.Helpers
45 -----------------------
46 -- Local Subprograms --
47 -----------------------
49 procedure Free
(X
: in out Node_Access
);
51 procedure Insert_Internal
52 (Container
: in out List
;
54 New_Node
: Node_Access
);
56 procedure Splice_Internal
57 (Target
: in out List
;
59 Source
: in out List
);
61 procedure Splice_Internal
62 (Target
: in out List
;
65 Position
: Node_Access
);
67 function Vet
(Position
: Cursor
) return Boolean with Inline
;
68 -- Checks invariants of the cursor and its designated container, as a
69 -- simple way of detecting dangling references (see operation Free for a
70 -- description of the detection mechanism), returning True if all checks
71 -- pass. Invocations of Vet are used here as the argument of pragma Assert,
72 -- so the checks are performed only when assertions are enabled.
78 function "=" (Left
, Right
: List
) return Boolean is
80 if Left
.Length
/= Right
.Length
then
84 if Left
.Length
= 0 then
89 -- Per AI05-0022, the container implementation is required to detect
90 -- element tampering by a generic actual subprogram.
92 Lock_Left
: With_Lock
(Left
.TC
'Unrestricted_Access);
93 Lock_Right
: With_Lock
(Right
.TC
'Unrestricted_Access);
95 L
: Node_Access
:= Left
.First
;
96 R
: Node_Access
:= Right
.First
;
98 for J
in 1 .. Left
.Length
loop
99 if L
.Element
/= R
.Element
then
115 procedure Adjust
(Container
: in out List
) is
116 Src
: Node_Access
:= Container
.First
;
119 -- If the counts are nonzero, execution is technically erroneous, but
120 -- it seems friendly to allow things like concurrent "=" on shared
123 Zero_Counts
(Container
.TC
);
126 pragma Assert
(Container
.Last
= null);
127 pragma Assert
(Container
.Length
= 0);
131 pragma Assert
(Container
.First
.Prev
= null);
132 pragma Assert
(Container
.Last
.Next
= null);
133 pragma Assert
(Container
.Length
> 0);
135 Container
.First
:= new Node_Type
'(Src.Element, null, null);
136 Container.Last := Container.First;
137 Container.Length := 1;
140 while Src /= null loop
141 Container.Last.Next := new Node_Type'(Element
=> Src
.Element
,
142 Prev
=> Container
.Last
,
144 Container
.Last
:= Container
.Last
.Next
;
145 Container
.Length
:= Container
.Length
+ 1;
156 (Container
: in out List
;
157 New_Item
: Element_Type
;
161 Insert
(Container
, No_Element
, New_Item
, Count
);
165 (Container
: in out List
;
166 New_Item
: Element_Type
)
169 Insert
(Container
, No_Element
, New_Item
, 1);
176 procedure Assign
(Target
: in out List
; Source
: List
) is
180 if Target
'Address = Source
'Address then
186 Node
:= Source
.First
;
187 while Node
/= null loop
188 Target
.Append
(Node
.Element
);
197 procedure Clear
(Container
: in out List
) is
201 if Container
.Length
= 0 then
202 pragma Assert
(Container
.First
= null);
203 pragma Assert
(Container
.Last
= null);
204 pragma Assert
(Container
.TC
= (Busy
=> 0, Lock
=> 0));
208 pragma Assert
(Container
.First
.Prev
= null);
209 pragma Assert
(Container
.Last
.Next
= null);
211 TC_Check
(Container
.TC
);
213 while Container
.Length
> 1 loop
214 X
:= Container
.First
;
215 pragma Assert
(X
.Next
.Prev
= Container
.First
);
217 Container
.First
:= X
.Next
;
218 Container
.First
.Prev
:= null;
220 Container
.Length
:= Container
.Length
- 1;
225 X
:= Container
.First
;
226 pragma Assert
(X
= Container
.Last
);
228 Container
.First
:= null;
229 Container
.Last
:= null;
230 Container
.Length
:= 0;
235 ------------------------
236 -- Constant_Reference --
237 ------------------------
239 function Constant_Reference
240 (Container
: aliased List
;
241 Position
: Cursor
) return Constant_Reference_Type
244 if Checks
and then Position
.Container
= null then
245 raise Constraint_Error
with "Position cursor has no element";
248 if Checks
and then Position
.Container
/= Container
'Unrestricted_Access
250 raise Program_Error
with
251 "Position cursor designates wrong container";
254 pragma Assert
(Vet
(Position
), "bad cursor in Constant_Reference");
257 TC
: constant Tamper_Counts_Access
:=
258 Container
.TC
'Unrestricted_Access;
260 return R
: constant Constant_Reference_Type
:=
261 (Element
=> Position
.Node
.Element
'Access,
262 Control
=> (Controlled
with TC
))
267 end Constant_Reference
;
275 Item
: Element_Type
) return Boolean
278 return Find
(Container
, Item
) /= No_Element
;
285 function Copy
(Source
: List
) return List
is
287 return Target
: List
do
288 Target
.Assign
(Source
);
297 (Container
: in out List
;
298 Position
: in out Cursor
;
299 Count
: Count_Type
:= 1)
304 TC_Check
(Container
.TC
);
306 if Checks
and then Position
.Node
= null then
307 raise Constraint_Error
with
308 "Position cursor has no element";
311 if Checks
and then Position
.Container
/= Container
'Unrestricted_Access
313 raise Program_Error
with
314 "Position cursor designates wrong container";
317 pragma Assert
(Vet
(Position
), "bad cursor in Delete");
319 if Position
.Node
= Container
.First
then
320 Delete_First
(Container
, Count
);
321 Position
:= No_Element
; -- Post-York behavior
326 Position
:= No_Element
; -- Post-York behavior
330 for Index
in 1 .. Count
loop
332 Container
.Length
:= Container
.Length
- 1;
334 if X
= Container
.Last
then
335 Position
:= No_Element
;
337 Container
.Last
:= X
.Prev
;
338 Container
.Last
.Next
:= null;
344 Position
.Node
:= X
.Next
;
346 X
.Next
.Prev
:= X
.Prev
;
347 X
.Prev
.Next
:= X
.Next
;
352 -- The following comment is unacceptable, more detail needed ???
354 Position
:= No_Element
; -- Post-York behavior
361 procedure Delete_First
362 (Container
: in out List
;
363 Count
: Count_Type
:= 1)
368 if Count
>= Container
.Length
then
377 TC_Check
(Container
.TC
);
379 for J
in 1 .. Count
loop
380 X
:= Container
.First
;
381 pragma Assert
(X
.Next
.Prev
= Container
.First
);
383 Container
.First
:= X
.Next
;
384 Container
.First
.Prev
:= null;
386 Container
.Length
:= Container
.Length
- 1;
396 procedure Delete_Last
397 (Container
: in out List
;
398 Count
: Count_Type
:= 1)
403 if Count
>= Container
.Length
then
412 TC_Check
(Container
.TC
);
414 for J
in 1 .. Count
loop
416 pragma Assert
(X
.Prev
.Next
= Container
.Last
);
418 Container
.Last
:= X
.Prev
;
419 Container
.Last
.Next
:= null;
421 Container
.Length
:= Container
.Length
- 1;
431 function Element
(Position
: Cursor
) return Element_Type
is
433 if Checks
and then Position
.Node
= null then
434 raise Constraint_Error
with
435 "Position cursor has no element";
438 pragma Assert
(Vet
(Position
), "bad cursor in Element");
440 return Position
.Node
.Element
;
447 procedure Finalize
(Object
: in out Iterator
) is
449 if Object
.Container
/= null then
450 Unbusy
(Object
.Container
.TC
);
461 Position
: Cursor
:= No_Element
) return Cursor
463 Node
: Node_Access
:= Position
.Node
;
467 Node
:= Container
.First
;
470 if Checks
and then Position
.Container
/= Container
'Unrestricted_Access
472 raise Program_Error
with
473 "Position cursor designates wrong container";
476 pragma Assert
(Vet
(Position
), "bad cursor in Find");
479 -- Per AI05-0022, the container implementation is required to detect
480 -- element tampering by a generic actual subprogram.
483 Lock
: With_Lock
(Container
.TC
'Unrestricted_Access);
485 while Node
/= null loop
486 if Node
.Element
= Item
then
487 return Cursor
'(Container'Unrestricted_Access, Node);
501 function First (Container : List) return Cursor is
503 if Container.First = null then
506 return Cursor'(Container
'Unrestricted_Access, Container
.First
);
510 function First
(Object
: Iterator
) return Cursor
is
512 -- The value of the iterator object's Node component influences the
513 -- behavior of the First (and Last) selector function.
515 -- When the Node component is null, this means the iterator object was
516 -- constructed without a start expression, in which case the (forward)
517 -- iteration starts from the (logical) beginning of the entire sequence
518 -- of items (corresponding to Container.First, for a forward iterator).
520 -- Otherwise, this is iteration over a partial sequence of items. When
521 -- the Node component is non-null, the iterator object was constructed
522 -- with a start expression, that specifies the position from which the
523 -- (forward) partial iteration begins.
525 if Object
.Node
= null then
526 return Doubly_Linked_Lists
.First
(Object
.Container
.all);
528 return Cursor
'(Object.Container, Object.Node);
536 function First_Element (Container : List) return Element_Type is
538 if Checks and then Container.First = null then
539 raise Constraint_Error with "list is empty";
542 return Container.First.Element;
549 procedure Free (X : in out Node_Access) is
550 procedure Deallocate is
551 new Ada.Unchecked_Deallocation (Node_Type, Node_Access);
554 -- While a node is in use, as an active link in a list, its Previous and
555 -- Next components must be null, or designate a different node; this is
556 -- a node invariant. Before actually deallocating the node, we set both
557 -- access value components of the node to point to the node itself, thus
558 -- falsifying the node invariant. Subprogram Vet inspects the value of
559 -- the node components when interrogating the node, in order to detect
560 -- whether the cursor's node access value is dangling.
562 -- Note that we have no guarantee that the storage for the node isn't
563 -- modified when it is deallocated, but there are other tests that Vet
564 -- does if node invariants appear to be satisifed. However, in practice
565 -- this simple test works well enough, detecting dangling references
566 -- immediately, without needing further interrogation.
574 ---------------------
575 -- Generic_Sorting --
576 ---------------------
578 package body Generic_Sorting is
584 function Is_Sorted (Container : List) return Boolean is
585 -- Per AI05-0022, the container implementation is required to detect
586 -- element tampering by a generic actual subprogram.
588 Lock : With_Lock (Container.TC'Unrestricted_Access);
592 Node := Container.First;
593 for Idx in 2 .. Container.Length loop
594 if Node.Next.Element < Node.Element then
609 (Target : in out List;
610 Source : in out List)
613 TC_Check (Target.TC);
614 TC_Check (Source.TC);
616 -- The semantics of Merge changed slightly per AI05-0021. It was
617 -- originally the case that if Target and Source denoted the same
618 -- container object, then the GNAT implementation of Merge did
619 -- nothing. However, it was argued that RM05 did not precisely
620 -- specify the semantics for this corner case. The decision of the
621 -- ARG was that if Target and Source denote the same non-empty
622 -- container object, then Program_Error is raised.
624 if Source.Is_Empty then
628 if Checks and then Target'Address = Source'Address then
629 raise Program_Error with
630 "Target and Source denote same non-empty container";
633 if Checks and then Target.Length > Count_Type'Last - Source.Length
635 raise Constraint_Error with "new length exceeds maximum";
638 -- Per AI05-0022, the container implementation is required to detect
639 -- element tampering by a generic actual subprogram.
642 Lock_Target : With_Lock (Target.TC'Unchecked_Access);
643 Lock_Source : With_Lock (Source.TC'Unchecked_Access);
645 LI, RI, RJ : Node_Access;
650 while RI /= null loop
651 pragma Assert (RI.Next = null
652 or else not (RI.Next.Element < RI.Element));
655 Splice_Internal (Target, null, Source);
659 pragma Assert (LI.Next = null
660 or else not (LI.Next.Element < LI.Element));
662 if RI.Element < LI.Element then
665 Splice_Internal (Target, LI, Source, RJ);
678 procedure Sort (Container : in out List) is
680 if Container.Length <= 1 then
684 pragma Assert (Container.First.Prev = null);
685 pragma Assert (Container.Last.Next = null);
687 TC_Check (Container.TC);
689 -- Per AI05-0022, the container implementation is required to detect
690 -- element tampering by a generic actual subprogram.
693 Lock : With_Lock (Container.TC'Unchecked_Access);
695 package Descriptors is new List_Descriptors
696 (Node_Ref => Node_Access, Nil => null);
699 function Next (N : Node_Access) return Node_Access is (N.Next);
700 procedure Set_Next (N : Node_Access; Next : Node_Access)
702 procedure Set_Prev (N : Node_Access; Prev : Node_Access)
704 function "<" (L, R : Node_Access) return Boolean is
705 (L.Element < R.Element);
706 procedure Update_Container (List : List_Descriptor) with Inline;
708 procedure Set_Next (N : Node_Access; Next : Node_Access) is
713 procedure Set_Prev (N : Node_Access; Prev : Node_Access) is
718 procedure Update_Container (List : List_Descriptor) is
720 Container.First := List.First;
721 Container.Last := List.Last;
722 Container.Length := List.Length;
723 end Update_Container;
725 procedure Sort_List is new Doubly_Linked_List_Sort;
727 Sort_List (List_Descriptor'(First
=> Container
.First
,
728 Last
=> Container
.Last
,
729 Length
=> Container
.Length
));
732 pragma Assert
(Container
.First
.Prev
= null);
733 pragma Assert
(Container
.Last
.Next
= null);
738 ------------------------
739 -- Get_Element_Access --
740 ------------------------
742 function Get_Element_Access
743 (Position
: Cursor
) return not null Element_Access
is
745 return Position
.Node
.Element
'Access;
746 end Get_Element_Access
;
752 function Has_Element
(Position
: Cursor
) return Boolean is
754 pragma Assert
(Vet
(Position
), "bad cursor in Has_Element");
755 return Position
.Node
/= null;
763 (Container
: in out List
;
765 New_Item
: Element_Type
;
766 Position
: out Cursor
;
767 Count
: Count_Type
:= 1)
769 First_Node
: Node_Access
;
770 New_Node
: Node_Access
;
773 TC_Check
(Container
.TC
);
775 if Before
.Container
/= null then
776 if Checks
and then Before
.Container
/= Container
'Unrestricted_Access
778 raise Program_Error
with
779 "Before cursor designates wrong list";
782 pragma Assert
(Vet
(Before
), "bad cursor in Insert");
790 if Checks
and then Container
.Length
> Count_Type
'Last - Count
then
791 raise Constraint_Error
with "new length exceeds maximum";
794 New_Node
:= new Node_Type
'(New_Item, null, null);
795 First_Node := New_Node;
796 Insert_Internal (Container, Before.Node, New_Node);
798 for J in 2 .. Count loop
799 New_Node := new Node_Type'(New_Item
, null, null);
800 Insert_Internal
(Container
, Before
.Node
, New_Node
);
803 Position
:= Cursor
'(Container'Unchecked_Access, First_Node);
807 (Container : in out List;
809 New_Item : Element_Type;
810 Count : Count_Type := 1)
814 Insert (Container, Before, New_Item, Position, Count);
818 (Container : in out List;
820 Position : out Cursor;
821 Count : Count_Type := 1)
823 First_Node : Node_Access;
824 New_Node : Node_Access;
827 TC_Check (Container.TC);
829 if Before.Container /= null then
830 if Checks and then Before.Container /= Container'Unrestricted_Access
832 raise Program_Error with
833 "Before cursor designates wrong list";
836 pragma Assert (Vet (Before), "bad cursor in Insert");
844 if Checks and then Container.Length > Count_Type'Last - Count then
845 raise Constraint_Error with "new length exceeds maximum";
848 New_Node := new Node_Type;
849 First_Node := New_Node;
850 Insert_Internal (Container, Before.Node, New_Node);
852 for J in 2 .. Count loop
853 New_Node := new Node_Type;
854 Insert_Internal (Container, Before.Node, New_Node);
857 Position := Cursor'(Container
'Unchecked_Access, First_Node
);
860 ---------------------
861 -- Insert_Internal --
862 ---------------------
864 procedure Insert_Internal
865 (Container
: in out List
;
866 Before
: Node_Access
;
867 New_Node
: Node_Access
)
870 if Container
.Length
= 0 then
871 pragma Assert
(Before
= null);
872 pragma Assert
(Container
.First
= null);
873 pragma Assert
(Container
.Last
= null);
875 Container
.First
:= New_Node
;
876 Container
.Last
:= New_Node
;
878 elsif Before
= null then
879 pragma Assert
(Container
.Last
.Next
= null);
881 Container
.Last
.Next
:= New_Node
;
882 New_Node
.Prev
:= Container
.Last
;
884 Container
.Last
:= New_Node
;
886 elsif Before
= Container
.First
then
887 pragma Assert
(Container
.First
.Prev
= null);
889 Container
.First
.Prev
:= New_Node
;
890 New_Node
.Next
:= Container
.First
;
892 Container
.First
:= New_Node
;
895 pragma Assert
(Container
.First
.Prev
= null);
896 pragma Assert
(Container
.Last
.Next
= null);
898 New_Node
.Next
:= Before
;
899 New_Node
.Prev
:= Before
.Prev
;
901 Before
.Prev
.Next
:= New_Node
;
902 Before
.Prev
:= New_Node
;
905 Container
.Length
:= Container
.Length
+ 1;
912 function Is_Empty
(Container
: List
) return Boolean is
914 return Container
.Length
= 0;
923 Process
: not null access procedure (Position
: Cursor
))
925 Busy
: With_Busy
(Container
.TC
'Unrestricted_Access);
926 Node
: Node_Access
:= Container
.First
;
929 while Node
/= null loop
930 Process
(Cursor
'(Container'Unrestricted_Access, Node));
935 function Iterate (Container : List)
936 return List_Iterator_Interfaces.Reversible_Iterator'Class
939 -- The value of the Node component influences the behavior of the First
940 -- and Last selector functions of the iterator object. When the Node
941 -- component is null (as is the case here), this means the iterator
942 -- object was constructed without a start expression. This is a
943 -- complete iterator, meaning that the iteration starts from the
944 -- (logical) beginning of the sequence of items.
946 -- Note: For a forward iterator, Container.First is the beginning, and
947 -- for a reverse iterator, Container.Last is the beginning.
949 return It : constant Iterator :=
950 Iterator'(Limited_Controlled
with
951 Container
=> Container
'Unrestricted_Access,
954 Busy
(Container
.TC
'Unrestricted_Access.all);
958 function Iterate
(Container
: List
; Start
: Cursor
)
959 return List_Iterator_Interfaces
.Reversible_Iterator
'Class
962 -- It was formerly the case that when Start = No_Element, the partial
963 -- iterator was defined to behave the same as for a complete iterator,
964 -- and iterate over the entire sequence of items. However, those
965 -- semantics were unintuitive and arguably error-prone (it is too easy
966 -- to accidentally create an endless loop), and so they were changed,
967 -- per the ARG meeting in Denver on 2011/11. However, there was no
968 -- consensus about what positive meaning this corner case should have,
969 -- and so it was decided to simply raise an exception. This does imply,
970 -- however, that it is not possible to use a partial iterator to specify
971 -- an empty sequence of items.
973 if Checks
and then Start
= No_Element
then
974 raise Constraint_Error
with
975 "Start position for iterator equals No_Element";
978 if Checks
and then Start
.Container
/= Container
'Unrestricted_Access then
979 raise Program_Error
with
980 "Start cursor of Iterate designates wrong list";
983 pragma Assert
(Vet
(Start
), "Start cursor of Iterate is bad");
985 -- The value of the Node component influences the behavior of the First
986 -- and Last selector functions of the iterator object. When the Node
987 -- component is non-null (as is the case here), it means that this is a
988 -- partial iteration, over a subset of the complete sequence of items.
989 -- The iterator object was constructed with a start expression,
990 -- indicating the position from which the iteration begins. Note that
991 -- the start position has the same value irrespective of whether this is
992 -- a forward or reverse iteration.
994 return It
: constant Iterator
:=
995 Iterator
'(Limited_Controlled with
996 Container => Container'Unrestricted_Access,
999 Busy (Container.TC'Unrestricted_Access.all);
1007 function Last (Container : List) return Cursor is
1009 if Container.Last = null then
1012 return Cursor'(Container
'Unrestricted_Access, Container
.Last
);
1016 function Last
(Object
: Iterator
) return Cursor
is
1018 -- The value of the iterator object's Node component influences the
1019 -- behavior of the Last (and First) selector function.
1021 -- When the Node component is null, this means the iterator object was
1022 -- constructed without a start expression, in which case the (reverse)
1023 -- iteration starts from the (logical) beginning of the entire sequence
1024 -- (corresponding to Container.Last, for a reverse iterator).
1026 -- Otherwise, this is iteration over a partial sequence of items. When
1027 -- the Node component is non-null, the iterator object was constructed
1028 -- with a start expression, that specifies the position from which the
1029 -- (reverse) partial iteration begins.
1031 if Object
.Node
= null then
1032 return Doubly_Linked_Lists
.Last
(Object
.Container
.all);
1034 return Cursor
'(Object.Container, Object.Node);
1042 function Last_Element (Container : List) return Element_Type is
1044 if Checks and then Container.Last = null then
1045 raise Constraint_Error with "list is empty";
1048 return Container.Last.Element;
1055 function Length (Container : List) return Count_Type is
1057 return Container.Length;
1065 (Target : in out List;
1066 Source : in out List)
1069 if Target'Address = Source'Address then
1073 TC_Check (Source.TC);
1077 Target.First := Source.First;
1078 Source.First := null;
1080 Target.Last := Source.Last;
1081 Source.Last := null;
1083 Target.Length := Source.Length;
1091 procedure Next (Position : in out Cursor) is
1093 Position := Next (Position);
1096 function Next (Position : Cursor) return Cursor is
1098 if Position.Node = null then
1102 pragma Assert (Vet (Position), "bad cursor in Next");
1105 Next_Node : constant Node_Access := Position.Node.Next;
1107 if Next_Node = null then
1110 return Cursor'(Position
.Container
, Next_Node
);
1118 Position
: Cursor
) return Cursor
1121 if Position
.Container
= null then
1125 if Checks
and then Position
.Container
/= Object
.Container
then
1126 raise Program_Error
with
1127 "Position cursor of Next designates wrong list";
1130 return Next
(Position
);
1138 (Container
: in out List
;
1139 New_Item
: Element_Type
;
1140 Count
: Count_Type
:= 1)
1143 Insert
(Container
, First
(Container
), New_Item
, Count
);
1150 procedure Previous
(Position
: in out Cursor
) is
1152 Position
:= Previous
(Position
);
1155 function Previous
(Position
: Cursor
) return Cursor
is
1157 if Position
.Node
= null then
1161 pragma Assert
(Vet
(Position
), "bad cursor in Previous");
1164 Prev_Node
: constant Node_Access
:= Position
.Node
.Prev
;
1166 if Prev_Node
= null then
1169 return Cursor
'(Position.Container, Prev_Node);
1177 Position : Cursor) return Cursor
1180 if Position.Container = null then
1184 if Checks and then Position.Container /= Object.Container then
1185 raise Program_Error with
1186 "Position cursor of Previous designates wrong list";
1189 return Previous (Position);
1192 ----------------------
1193 -- Pseudo_Reference --
1194 ----------------------
1196 function Pseudo_Reference
1197 (Container : aliased List'Class) return Reference_Control_Type
1199 TC : constant Tamper_Counts_Access := Container.TC'Unrestricted_Access;
1201 return R : constant Reference_Control_Type := (Controlled with TC) do
1204 end Pseudo_Reference;
1210 procedure Query_Element
1212 Process : not null access procedure (Element : Element_Type))
1215 if Checks and then Position.Node = null then
1216 raise Constraint_Error with
1217 "Position cursor has no element";
1220 pragma Assert (Vet (Position), "bad cursor in Query_Element");
1223 Lock : With_Lock (Position.Container.TC'Unrestricted_Access);
1225 Process (Position.Node.Element);
1234 (S : in out Ada.Strings.Text_Buffers.Root_Buffer_Type'Class; V : List)
1236 First_Time : Boolean := True;
1237 use System.Put_Images;
1243 First_Time := False;
1245 Simple_Array_Between (S);
1248 Element_Type'Put_Image (S, X);
1259 (Stream : not null access Root_Stream_Type'Class;
1262 N : Count_Type'Base;
1267 Count_Type'Base'Read
(Stream
, N
);
1276 Element_Type
'Read (Stream
, X
.Element
);
1287 Item
.Length
:= Item
.Length
+ 1;
1288 exit when Item
.Length
= N
;
1293 Element_Type
'Read (Stream
, X
.Element
);
1300 X
.Prev
:= Item
.Last
;
1301 Item
.Last
.Next
:= X
;
1307 (Stream
: not null access Root_Stream_Type
'Class;
1311 raise Program_Error
with "attempt to stream list cursor";
1315 (Stream
: not null access Root_Stream_Type
'Class;
1316 Item
: out Reference_Type
)
1319 raise Program_Error
with "attempt to stream reference";
1323 (Stream
: not null access Root_Stream_Type
'Class;
1324 Item
: out Constant_Reference_Type
)
1327 raise Program_Error
with "attempt to stream reference";
1335 (Container
: aliased in out List
;
1336 Position
: Cursor
) return Reference_Type
1339 if Checks
and then Position
.Container
= null then
1340 raise Constraint_Error
with "Position cursor has no element";
1343 if Checks
and then Position
.Container
/= Container
'Unchecked_Access then
1344 raise Program_Error
with
1345 "Position cursor designates wrong container";
1348 pragma Assert
(Vet
(Position
), "bad cursor in function Reference");
1351 TC
: constant Tamper_Counts_Access
:=
1352 Container
.TC
'Unrestricted_Access;
1354 return R
: constant Reference_Type
:=
1355 (Element
=> Position
.Node
.Element
'Access,
1356 Control
=> (Controlled
with TC
))
1363 ---------------------
1364 -- Replace_Element --
1365 ---------------------
1367 procedure Replace_Element
1368 (Container
: in out List
;
1370 New_Item
: Element_Type
)
1373 TE_Check
(Container
.TC
);
1375 if Checks
and then Position
.Container
= null then
1376 raise Constraint_Error
with "Position cursor has no element";
1379 if Checks
and then Position
.Container
/= Container
'Unchecked_Access then
1380 raise Program_Error
with
1381 "Position cursor designates wrong container";
1384 pragma Assert
(Vet
(Position
), "bad cursor in Replace_Element");
1386 Position
.Node
.Element
:= New_Item
;
1387 end Replace_Element
;
1389 ----------------------
1390 -- Reverse_Elements --
1391 ----------------------
1393 procedure Reverse_Elements
(Container
: in out List
) is
1394 I
: Node_Access
:= Container
.First
;
1395 J
: Node_Access
:= Container
.Last
;
1397 procedure Swap
(L
, R
: Node_Access
);
1403 procedure Swap
(L
, R
: Node_Access
) is
1404 LN
: constant Node_Access
:= L
.Next
;
1405 LP
: constant Node_Access
:= L
.Prev
;
1407 RN
: constant Node_Access
:= R
.Next
;
1408 RP
: constant Node_Access
:= R
.Prev
;
1423 pragma Assert
(RP
= L
);
1437 -- Start of processing for Reverse_Elements
1440 if Container
.Length
<= 1 then
1444 pragma Assert
(Container
.First
.Prev
= null);
1445 pragma Assert
(Container
.Last
.Next
= null);
1447 TC_Check
(Container
.TC
);
1449 Container
.First
:= J
;
1450 Container
.Last
:= I
;
1452 Swap
(L
=> I
, R
=> J
);
1460 Swap
(L
=> J
, R
=> I
);
1469 pragma Assert
(Container
.First
.Prev
= null);
1470 pragma Assert
(Container
.Last
.Next
= null);
1471 end Reverse_Elements
;
1477 function Reverse_Find
1479 Item
: Element_Type
;
1480 Position
: Cursor
:= No_Element
) return Cursor
1482 Node
: Node_Access
:= Position
.Node
;
1486 Node
:= Container
.Last
;
1489 if Checks
and then Position
.Container
/= Container
'Unrestricted_Access
1491 raise Program_Error
with
1492 "Position cursor designates wrong container";
1495 pragma Assert
(Vet
(Position
), "bad cursor in Reverse_Find");
1498 -- Per AI05-0022, the container implementation is required to detect
1499 -- element tampering by a generic actual subprogram.
1502 Lock
: With_Lock
(Container
.TC
'Unrestricted_Access);
1504 while Node
/= null loop
1505 if Node
.Element
= Item
then
1506 return Cursor
'(Container'Unrestricted_Access, Node);
1516 ---------------------
1517 -- Reverse_Iterate --
1518 ---------------------
1520 procedure Reverse_Iterate
1522 Process : not null access procedure (Position : Cursor))
1524 Busy : With_Busy (Container.TC'Unrestricted_Access);
1525 Node : Node_Access := Container.Last;
1528 while Node /= null loop
1529 Process (Cursor'(Container
'Unrestricted_Access, Node
));
1532 end Reverse_Iterate
;
1539 (Target
: in out List
;
1541 Source
: in out List
)
1544 TC_Check
(Target
.TC
);
1545 TC_Check
(Source
.TC
);
1547 if Before
.Container
/= null then
1548 if Checks
and then Before
.Container
/= Target
'Unrestricted_Access then
1549 raise Program_Error
with
1550 "Before cursor designates wrong container";
1553 pragma Assert
(Vet
(Before
), "bad cursor in Splice");
1556 if Target
'Address = Source
'Address or else Source
.Length
= 0 then
1560 if Checks
and then Target
.Length
> Count_Type
'Last - Source
.Length
then
1561 raise Constraint_Error
with "new length exceeds maximum";
1564 Splice_Internal
(Target
, Before
.Node
, Source
);
1568 (Container
: in out List
;
1573 TC_Check
(Container
.TC
);
1575 if Before
.Container
/= null then
1576 if Checks
and then Before
.Container
/= Container
'Unchecked_Access then
1577 raise Program_Error
with
1578 "Before cursor designates wrong container";
1581 pragma Assert
(Vet
(Before
), "bad Before cursor in Splice");
1584 if Checks
and then Position
.Node
= null then
1585 raise Constraint_Error
with "Position cursor has no element";
1588 if Checks
and then Position
.Container
/= Container
'Unrestricted_Access
1590 raise Program_Error
with
1591 "Position cursor designates wrong container";
1594 pragma Assert
(Vet
(Position
), "bad Position cursor in Splice");
1596 if Position
.Node
= Before
.Node
1597 or else Position
.Node
.Next
= Before
.Node
1602 pragma Assert
(Container
.Length
>= 2);
1604 if Before
.Node
= null then
1605 pragma Assert
(Position
.Node
/= Container
.Last
);
1607 if Position
.Node
= Container
.First
then
1608 Container
.First
:= Position
.Node
.Next
;
1609 Container
.First
.Prev
:= null;
1611 Position
.Node
.Prev
.Next
:= Position
.Node
.Next
;
1612 Position
.Node
.Next
.Prev
:= Position
.Node
.Prev
;
1615 Container
.Last
.Next
:= Position
.Node
;
1616 Position
.Node
.Prev
:= Container
.Last
;
1618 Container
.Last
:= Position
.Node
;
1619 Container
.Last
.Next
:= null;
1624 if Before
.Node
= Container
.First
then
1625 pragma Assert
(Position
.Node
/= Container
.First
);
1627 if Position
.Node
= Container
.Last
then
1628 Container
.Last
:= Position
.Node
.Prev
;
1629 Container
.Last
.Next
:= null;
1631 Position
.Node
.Prev
.Next
:= Position
.Node
.Next
;
1632 Position
.Node
.Next
.Prev
:= Position
.Node
.Prev
;
1635 Container
.First
.Prev
:= Position
.Node
;
1636 Position
.Node
.Next
:= Container
.First
;
1638 Container
.First
:= Position
.Node
;
1639 Container
.First
.Prev
:= null;
1644 if Position
.Node
= Container
.First
then
1645 Container
.First
:= Position
.Node
.Next
;
1646 Container
.First
.Prev
:= null;
1648 elsif Position
.Node
= Container
.Last
then
1649 Container
.Last
:= Position
.Node
.Prev
;
1650 Container
.Last
.Next
:= null;
1653 Position
.Node
.Prev
.Next
:= Position
.Node
.Next
;
1654 Position
.Node
.Next
.Prev
:= Position
.Node
.Prev
;
1657 Before
.Node
.Prev
.Next
:= Position
.Node
;
1658 Position
.Node
.Prev
:= Before
.Node
.Prev
;
1660 Before
.Node
.Prev
:= Position
.Node
;
1661 Position
.Node
.Next
:= Before
.Node
;
1663 pragma Assert
(Container
.First
.Prev
= null);
1664 pragma Assert
(Container
.Last
.Next
= null);
1668 (Target
: in out List
;
1670 Source
: in out List
;
1671 Position
: in out Cursor
)
1674 if Target
'Address = Source
'Address then
1675 Splice
(Target
, Before
, Position
);
1679 TC_Check
(Target
.TC
);
1680 TC_Check
(Source
.TC
);
1682 if Before
.Container
/= null then
1683 if Checks
and then Before
.Container
/= Target
'Unrestricted_Access then
1684 raise Program_Error
with
1685 "Before cursor designates wrong container";
1688 pragma Assert
(Vet
(Before
), "bad Before cursor in Splice");
1691 if Checks
and then Position
.Node
= null then
1692 raise Constraint_Error
with "Position cursor has no element";
1695 if Checks
and then Position
.Container
/= Source
'Unrestricted_Access then
1696 raise Program_Error
with
1697 "Position cursor designates wrong container";
1700 pragma Assert
(Vet
(Position
), "bad Position cursor in Splice");
1702 if Checks
and then Target
.Length
= Count_Type
'Last then
1703 raise Constraint_Error
with "Target is full";
1706 Splice_Internal
(Target
, Before
.Node
, Source
, Position
.Node
);
1707 Position
.Container
:= Target
'Unchecked_Access;
1710 ---------------------
1711 -- Splice_Internal --
1712 ---------------------
1714 procedure Splice_Internal
1715 (Target
: in out List
;
1716 Before
: Node_Access
;
1717 Source
: in out List
)
1720 -- This implements the corresponding Splice operation, after the
1721 -- parameters have been vetted, and corner-cases disposed of.
1723 pragma Assert
(Target
'Address /= Source
'Address);
1724 pragma Assert
(Source
.Length
> 0);
1725 pragma Assert
(Source
.First
/= null);
1726 pragma Assert
(Source
.First
.Prev
= null);
1727 pragma Assert
(Source
.Last
/= null);
1728 pragma Assert
(Source
.Last
.Next
= null);
1729 pragma Assert
(Target
.Length
<= Count_Type
'Last - Source
.Length
);
1731 if Target
.Length
= 0 then
1732 pragma Assert
(Target
.First
= null);
1733 pragma Assert
(Target
.Last
= null);
1734 pragma Assert
(Before
= null);
1736 Target
.First
:= Source
.First
;
1737 Target
.Last
:= Source
.Last
;
1739 elsif Before
= null then
1740 pragma Assert
(Target
.Last
.Next
= null);
1742 Target
.Last
.Next
:= Source
.First
;
1743 Source
.First
.Prev
:= Target
.Last
;
1745 Target
.Last
:= Source
.Last
;
1747 elsif Before
= Target
.First
then
1748 pragma Assert
(Target
.First
.Prev
= null);
1750 Source
.Last
.Next
:= Target
.First
;
1751 Target
.First
.Prev
:= Source
.Last
;
1753 Target
.First
:= Source
.First
;
1756 pragma Assert
(Target
.Length
>= 2);
1758 Before
.Prev
.Next
:= Source
.First
;
1759 Source
.First
.Prev
:= Before
.Prev
;
1761 Before
.Prev
:= Source
.Last
;
1762 Source
.Last
.Next
:= Before
;
1765 Source
.First
:= null;
1766 Source
.Last
:= null;
1768 Target
.Length
:= Target
.Length
+ Source
.Length
;
1770 end Splice_Internal
;
1772 procedure Splice_Internal
1773 (Target
: in out List
;
1774 Before
: Node_Access
; -- node of Target
1775 Source
: in out List
;
1776 Position
: Node_Access
) -- node of Source
1779 -- This implements the corresponding Splice operation, after the
1780 -- parameters have been vetted.
1782 pragma Assert
(Target
'Address /= Source
'Address);
1783 pragma Assert
(Target
.Length
< Count_Type
'Last);
1784 pragma Assert
(Source
.Length
> 0);
1785 pragma Assert
(Source
.First
/= null);
1786 pragma Assert
(Source
.First
.Prev
= null);
1787 pragma Assert
(Source
.Last
/= null);
1788 pragma Assert
(Source
.Last
.Next
= null);
1789 pragma Assert
(Position
/= null);
1791 if Position
= Source
.First
then
1792 Source
.First
:= Position
.Next
;
1794 if Position
= Source
.Last
then
1795 pragma Assert
(Source
.First
= null);
1796 pragma Assert
(Source
.Length
= 1);
1797 Source
.Last
:= null;
1800 Source
.First
.Prev
:= null;
1803 elsif Position
= Source
.Last
then
1804 pragma Assert
(Source
.Length
>= 2);
1805 Source
.Last
:= Position
.Prev
;
1806 Source
.Last
.Next
:= null;
1809 pragma Assert
(Source
.Length
>= 3);
1810 Position
.Prev
.Next
:= Position
.Next
;
1811 Position
.Next
.Prev
:= Position
.Prev
;
1814 if Target
.Length
= 0 then
1815 pragma Assert
(Target
.First
= null);
1816 pragma Assert
(Target
.Last
= null);
1817 pragma Assert
(Before
= null);
1819 Target
.First
:= Position
;
1820 Target
.Last
:= Position
;
1822 Target
.First
.Prev
:= null;
1823 Target
.Last
.Next
:= null;
1825 elsif Before
= null then
1826 pragma Assert
(Target
.Last
.Next
= null);
1827 Target
.Last
.Next
:= Position
;
1828 Position
.Prev
:= Target
.Last
;
1830 Target
.Last
:= Position
;
1831 Target
.Last
.Next
:= null;
1833 elsif Before
= Target
.First
then
1834 pragma Assert
(Target
.First
.Prev
= null);
1835 Target
.First
.Prev
:= Position
;
1836 Position
.Next
:= Target
.First
;
1838 Target
.First
:= Position
;
1839 Target
.First
.Prev
:= null;
1842 pragma Assert
(Target
.Length
>= 2);
1843 Before
.Prev
.Next
:= Position
;
1844 Position
.Prev
:= Before
.Prev
;
1846 Before
.Prev
:= Position
;
1847 Position
.Next
:= Before
;
1850 Target
.Length
:= Target
.Length
+ 1;
1851 Source
.Length
:= Source
.Length
- 1;
1852 end Splice_Internal
;
1859 (Container
: in out List
;
1863 TE_Check
(Container
.TC
);
1865 if Checks
and then I
.Node
= null then
1866 raise Constraint_Error
with "I cursor has no element";
1869 if Checks
and then J
.Node
= null then
1870 raise Constraint_Error
with "J cursor has no element";
1873 if Checks
and then I
.Container
/= Container
'Unchecked_Access then
1874 raise Program_Error
with "I cursor designates wrong container";
1877 if Checks
and then J
.Container
/= Container
'Unchecked_Access then
1878 raise Program_Error
with "J cursor designates wrong container";
1881 if I
.Node
= J
.Node
then
1885 pragma Assert
(Vet
(I
), "bad I cursor in Swap");
1886 pragma Assert
(Vet
(J
), "bad J cursor in Swap");
1889 EI
: Element_Type
renames I
.Node
.Element
;
1890 EJ
: Element_Type
renames J
.Node
.Element
;
1892 EI_Copy
: constant Element_Type
:= EI
;
1904 procedure Swap_Links
1905 (Container
: in out List
;
1909 TC_Check
(Container
.TC
);
1911 if Checks
and then I
.Node
= null then
1912 raise Constraint_Error
with "I cursor has no element";
1915 if Checks
and then J
.Node
= null then
1916 raise Constraint_Error
with "J cursor has no element";
1919 if Checks
and then I
.Container
/= Container
'Unrestricted_Access then
1920 raise Program_Error
with "I cursor designates wrong container";
1923 if Checks
and then J
.Container
/= Container
'Unrestricted_Access then
1924 raise Program_Error
with "J cursor designates wrong container";
1927 if I
.Node
= J
.Node
then
1931 pragma Assert
(Vet
(I
), "bad I cursor in Swap_Links");
1932 pragma Assert
(Vet
(J
), "bad J cursor in Swap_Links");
1935 I_Next
: constant Cursor
:= Next
(I
);
1939 Splice
(Container
, Before
=> I
, Position
=> J
);
1943 J_Next
: constant Cursor
:= Next
(J
);
1947 Splice
(Container
, Before
=> J
, Position
=> I
);
1950 pragma Assert
(Container
.Length
>= 3);
1952 Splice
(Container
, Before
=> I_Next
, Position
=> J
);
1953 Splice
(Container
, Before
=> J_Next
, Position
=> I
);
1960 --------------------
1961 -- Update_Element --
1962 --------------------
1964 procedure Update_Element
1965 (Container
: in out List
;
1967 Process
: not null access procedure (Element
: in out Element_Type
))
1970 if Checks
and then Position
.Node
= null then
1971 raise Constraint_Error
with "Position cursor has no element";
1974 if Checks
and then Position
.Container
/= Container
'Unchecked_Access then
1975 raise Program_Error
with
1976 "Position cursor designates wrong container";
1979 pragma Assert
(Vet
(Position
), "bad cursor in Update_Element");
1982 Lock
: With_Lock
(Container
.TC
'Unchecked_Access);
1984 Process
(Position
.Node
.Element
);
1992 function Vet
(Position
: Cursor
) return Boolean is
1994 if not Container_Checks
'Enabled then
1998 if Position
.Node
= null then
1999 return Position
.Container
= null;
2002 if Position
.Container
= null then
2006 -- An invariant of a node is that its Previous and Next components can
2007 -- be null, or designate a different node. Operation Free sets the
2008 -- access value components of the node to designate the node itself
2009 -- before actually deallocating the node, thus deliberately violating
2010 -- the node invariant. This gives us a simple way to detect a dangling
2011 -- reference to a node.
2013 if Position
.Node
.Next
= Position
.Node
then
2017 if Position
.Node
.Prev
= Position
.Node
then
2021 -- In practice the tests above will detect most instances of a dangling
2022 -- reference. If we get here, it means that the invariants of the
2023 -- designated node are satisfied (they at least appear to be satisfied),
2024 -- so we perform some more tests, to determine whether invariants of the
2025 -- designated list are satisfied too.
2028 L
: List
renames Position
.Container
.all;
2031 if L
.Length
= 0 then
2035 if L
.First
= null then
2039 if L
.Last
= null then
2043 if L
.First
.Prev
/= null then
2047 if L
.Last
.Next
/= null then
2051 if Position
.Node
.Prev
= null and then Position
.Node
/= L
.First
then
2056 (Position
.Node
.Prev
/= null or else Position
.Node
= L
.First
);
2058 if Position
.Node
.Next
= null and then Position
.Node
/= L
.Last
then
2063 (Position
.Node
.Next
/= null
2064 or else Position
.Node
= L
.Last
);
2066 if L
.Length
= 1 then
2067 return L
.First
= L
.Last
;
2070 if L
.First
= L
.Last
then
2074 if L
.First
.Next
= null then
2078 if L
.Last
.Prev
= null then
2082 if L
.First
.Next
.Prev
/= L
.First
then
2086 if L
.Last
.Prev
.Next
/= L
.Last
then
2090 if L
.Length
= 2 then
2091 if L
.First
.Next
/= L
.Last
then
2093 elsif L
.Last
.Prev
/= L
.First
then
2100 if L
.First
.Next
= L
.Last
then
2104 if L
.Last
.Prev
= L
.First
then
2108 -- Eliminate earlier possibility
2110 if Position
.Node
= L
.First
then
2114 pragma Assert
(Position
.Node
.Prev
/= null);
2116 -- Eliminate earlier possibility
2118 if Position
.Node
= L
.Last
then
2122 pragma Assert
(Position
.Node
.Next
/= null);
2124 if Position
.Node
.Next
.Prev
/= Position
.Node
then
2128 if Position
.Node
.Prev
.Next
/= Position
.Node
then
2132 if L
.Length
= 3 then
2133 if L
.First
.Next
/= Position
.Node
then
2135 elsif L
.Last
.Prev
/= Position
.Node
then
2149 (Stream
: not null access Root_Stream_Type
'Class;
2155 Count_Type
'Base'Write (Stream, Item.Length);
2158 while Node /= null loop
2159 Element_Type'Write (Stream, Node.Element);
2165 (Stream : not null access Root_Stream_Type'Class;
2169 raise Program_Error with "attempt to stream list cursor";
2173 (Stream : not null access Root_Stream_Type'Class;
2174 Item : Reference_Type)
2177 raise Program_Error with "attempt to stream reference";
2181 (Stream : not null access Root_Stream_Type'Class;
2182 Item : Constant_Reference_Type)
2185 raise Program_Error with "attempt to stream reference";
2188 end Ada.Containers.Doubly_Linked_Lists;