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-2012, 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 System; use type System.Address;
34 package body Ada.Containers.Doubly_Linked_Lists is
36 type Iterator is new Limited_Controlled and
37 List_Iterator_Interfaces.Reversible_Iterator with
39 Container : List_Access;
43 overriding procedure Finalize (Object : in out Iterator);
45 overriding function First (Object : Iterator) return Cursor;
46 overriding function Last (Object : Iterator) return Cursor;
48 overriding function Next
50 Position : Cursor) return Cursor;
52 overriding function Previous
54 Position : Cursor) return Cursor;
56 -----------------------
57 -- Local Subprograms --
58 -----------------------
60 procedure Free (X : in out Node_Access);
62 procedure Insert_Internal
63 (Container : in out List;
65 New_Node : Node_Access);
67 function Vet (Position : Cursor) return Boolean;
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
79 L : Node_Access := Left.First;
80 R : Node_Access := Right.First;
83 if Left'Address = Right'Address then
87 if Left.Length /= Right.Length then
91 for J in 1 .. Left.Length loop
92 if L.Element /= R.Element then
107 procedure Adjust (Container : in out List) is
108 Src : Node_Access := Container.First;
112 pragma Assert (Container.Last = null);
113 pragma Assert (Container.Length = 0);
114 pragma Assert (Container.Busy = 0);
115 pragma Assert (Container.Lock = 0);
119 pragma Assert (Container.First.Prev = null);
120 pragma Assert (Container.Last.Next = null);
121 pragma Assert (Container.Length > 0);
123 Container.First := null;
124 Container.Last := null;
125 Container.Length := 0;
129 Container.First := new Node_Type'(Src.Element, null, null);
130 Container.Last := Container.First;
131 Container.Length := 1;
134 while Src /= null loop
135 Container.Last.Next := new Node_Type'(Element => Src.Element,
136 Prev => Container.Last,
138 Container.Last := Container.Last.Next;
139 Container.Length := Container.Length + 1;
150 (Container : in out List;
151 New_Item : Element_Type;
152 Count : Count_Type := 1)
155 Insert (Container, No_Element, New_Item, Count);
162 procedure Assign (Target : in out List; Source : List) is
166 if Target'Address = Source'Address then
172 Node := Source.First;
173 while Node /= null loop
174 Target.Append (Node.Element);
183 procedure Clear (Container : in out List) is
187 if Container.Length = 0 then
188 pragma Assert (Container.First = null);
189 pragma Assert (Container.Last = null);
190 pragma Assert (Container.Busy = 0);
191 pragma Assert (Container.Lock = 0);
195 pragma Assert (Container.First.Prev = null);
196 pragma Assert (Container.Last.Next = null);
198 if Container.Busy > 0 then
199 raise Program_Error with
200 "attempt to tamper with cursors (list is busy)";
203 while Container.Length > 1 loop
204 X := Container.First;
205 pragma Assert (X.Next.Prev = Container.First);
207 Container.First := X.Next;
208 Container.First.Prev := null;
210 Container.Length := Container.Length - 1;
215 X := Container.First;
216 pragma Assert (X = Container.Last);
218 Container.First := null;
219 Container.Last := null;
220 Container.Length := 0;
222 pragma Warnings (Off);
224 pragma Warnings (On);
227 ------------------------
228 -- Constant_Reference --
229 ------------------------
231 function Constant_Reference
232 (Container : aliased List;
233 Position : Cursor) return Constant_Reference_Type
236 if Position.Container = null then
237 raise Constraint_Error with "Position cursor has no element";
240 if Position.Container /= Container'Unrestricted_Access then
241 raise Program_Error with
242 "Position cursor designates wrong container";
245 pragma Assert (Vet (Position), "bad cursor in Constant_Reference");
247 return (Element => Position.Node.Element'Access);
248 end Constant_Reference;
256 Item : Element_Type) return Boolean
259 return Find (Container, Item) /= No_Element;
266 function Copy (Source : List) return List is
268 return Target : List do
269 Target.Assign (Source);
278 (Container : in out List;
279 Position : in out Cursor;
280 Count : Count_Type := 1)
285 if Position.Node = null then
286 raise Constraint_Error with
287 "Position cursor has no element";
290 if Position.Container /= Container'Unrestricted_Access then
291 raise Program_Error with
292 "Position cursor designates wrong container";
295 pragma Assert (Vet (Position), "bad cursor in Delete");
297 if Position.Node = Container.First then
298 Delete_First (Container, Count);
299 Position := No_Element; -- Post-York behavior
304 Position := No_Element; -- Post-York behavior
308 if Container.Busy > 0 then
309 raise Program_Error with
310 "attempt to tamper with cursors (list is busy)";
313 for Index in 1 .. Count loop
315 Container.Length := Container.Length - 1;
317 if X = Container.Last then
318 Position := No_Element;
320 Container.Last := X.Prev;
321 Container.Last.Next := null;
327 Position.Node := X.Next;
329 X.Next.Prev := X.Prev;
330 X.Prev.Next := X.Next;
335 Position := No_Element; -- Post-York behavior
342 procedure Delete_First
343 (Container : in out List;
344 Count : Count_Type := 1)
349 if Count >= Container.Length then
358 if Container.Busy > 0 then
359 raise Program_Error with
360 "attempt to tamper with cursors (list is busy)";
363 for I in 1 .. Count loop
364 X := Container.First;
365 pragma Assert (X.Next.Prev = Container.First);
367 Container.First := X.Next;
368 Container.First.Prev := null;
370 Container.Length := Container.Length - 1;
380 procedure Delete_Last
381 (Container : in out List;
382 Count : Count_Type := 1)
387 if Count >= Container.Length then
396 if Container.Busy > 0 then
397 raise Program_Error with
398 "attempt to tamper with cursors (list is busy)";
401 for I in 1 .. Count loop
403 pragma Assert (X.Prev.Next = Container.Last);
405 Container.Last := X.Prev;
406 Container.Last.Next := null;
408 Container.Length := Container.Length - 1;
418 function Element (Position : Cursor) return Element_Type is
420 if Position.Node = null then
421 raise Constraint_Error with
422 "Position cursor has no element";
425 pragma Assert (Vet (Position), "bad cursor in Element");
427 return Position.Node.Element;
434 procedure Finalize (Object : in out Iterator) is
436 if Object.Container /= null then
438 B : Natural renames Object.Container.all.Busy;
452 Position : Cursor := No_Element) return Cursor
454 Node : Node_Access := Position.Node;
458 Node := Container.First;
461 if Position.Container /= Container'Unrestricted_Access then
462 raise Program_Error with
463 "Position cursor designates wrong container";
466 pragma Assert (Vet (Position), "bad cursor in Find");
469 while Node /= null loop
470 if Node.Element = Item then
471 return Cursor'(Container'Unrestricted_Access, Node);
484 function First (Container : List) return Cursor is
486 if Container.First = null then
490 return Cursor'(Container'Unrestricted_Access, Container.First);
493 function First (Object : Iterator) return Cursor is
495 -- The value of the iterator object's Node component influences the
496 -- behavior of the First (and Last) selector function.
498 -- When the Node component is null, this means the iterator object was
499 -- constructed without a start expression, in which case the (forward)
500 -- iteration starts from the (logical) beginning of the entire sequence
501 -- of items (corresponding to Container.First, for a forward iterator).
503 -- Otherwise, this is iteration over a partial sequence of items. When
504 -- the Node component is non-null, the iterator object was constructed
505 -- with a start expression, that specifies the position from which the
506 -- (forward) partial iteration begins.
508 if Object.Node = null then
509 return Doubly_Linked_Lists.First (Object.Container.all);
511 return Cursor'(Object.Container, Object.Node);
519 function First_Element (Container : List) return Element_Type is
521 if Container.First = null then
522 raise Constraint_Error with "list is empty";
525 return Container.First.Element;
532 procedure Free (X : in out Node_Access) is
533 procedure Deallocate is
534 new Ada.Unchecked_Deallocation (Node_Type, Node_Access);
536 -- While a node is in use, as an active link in a list, its Previous and
537 -- Next components must be null, or designate a different node; this is
538 -- a node invariant. Before actually deallocating the node, we set both
539 -- access value components of the node to point to the node itself, thus
540 -- falsifying the node invariant. Subprogram Vet inspects the value of
541 -- the node components when interrogating the node, in order to detect
542 -- whether the cursor's node access value is dangling.
544 -- Note that we have no guarantee that the storage for the node isn't
545 -- modified when it is deallocated, but there are other tests that Vet
546 -- does if node invariants appear to be satisifed. However, in practice
547 -- this simple test works well enough, detecting dangling references
548 -- immediately, without needing further interrogation.
556 ---------------------
557 -- Generic_Sorting --
558 ---------------------
560 package body Generic_Sorting is
566 function Is_Sorted (Container : List) return Boolean is
567 Node : Node_Access := Container.First;
570 for I in 2 .. Container.Length loop
571 if Node.Next.Element < Node.Element then
586 (Target : in out List;
587 Source : in out List)
593 -- The semantics of Merge changed slightly per AI05-0021. It was
594 -- originally the case that if Target and Source denoted the same
595 -- container object, then the GNAT implementation of Merge did
596 -- nothing. However, it was argued that RM05 did not precisely
597 -- specify the semantics for this corner case. The decision of the
598 -- ARG was that if Target and Source denote the same non-empty
599 -- container object, then Program_Error is raised.
601 if Source.Is_Empty then
605 if Target'Address = Source'Address then
606 raise Program_Error with
607 "Target and Source denote same non-empty container";
610 if Target.Busy > 0 then
611 raise Program_Error with
612 "attempt to tamper with cursors of Target (list is busy)";
615 if Source.Busy > 0 then
616 raise Program_Error with
617 "attempt to tamper with cursors of Source (list is busy)";
620 LI := First (Target);
621 RI := First (Source);
622 while RI.Node /= null loop
623 pragma Assert (RI.Node.Next = null
624 or else not (RI.Node.Next.Element <
627 if LI.Node = null then
628 Splice (Target, No_Element, Source);
632 pragma Assert (LI.Node.Next = null
633 or else not (LI.Node.Next.Element <
636 if RI.Node.Element < LI.Node.Element then
639 pragma Warnings (Off, RJ);
641 RI.Node := RI.Node.Next;
642 Splice (Target, LI, Source, RJ);
646 LI.Node := LI.Node.Next;
655 procedure Sort (Container : in out List) is
657 procedure Partition (Pivot : Node_Access; Back : Node_Access);
659 procedure Sort (Front, Back : Node_Access);
665 procedure Partition (Pivot : Node_Access; Back : Node_Access) is
666 Node : Node_Access := Pivot.Next;
669 while Node /= Back loop
670 if Node.Element < Pivot.Element then
672 Prev : constant Node_Access := Node.Prev;
673 Next : constant Node_Access := Node.Next;
679 Container.Last := Prev;
685 Node.Prev := Pivot.Prev;
689 if Node.Prev = null then
690 Container.First := Node;
692 Node.Prev.Next := Node;
708 procedure Sort (Front, Back : Node_Access) is
709 Pivot : constant Node_Access :=
710 (if Front = null then Container.First else Front.Next);
712 if Pivot /= Back then
713 Partition (Pivot, Back);
719 -- Start of processing for Sort
722 if Container.Length <= 1 then
726 pragma Assert (Container.First.Prev = null);
727 pragma Assert (Container.Last.Next = null);
729 if Container.Busy > 0 then
730 raise Program_Error with
731 "attempt to tamper with cursors (list is busy)";
734 Sort (Front => null, Back => null);
736 pragma Assert (Container.First.Prev = null);
737 pragma Assert (Container.Last.Next = null);
746 function Has_Element (Position : Cursor) return Boolean is
748 pragma Assert (Vet (Position), "bad cursor in Has_Element");
749 return Position.Node /= null;
757 (Container : in out List;
759 New_Item : Element_Type;
760 Position : out Cursor;
761 Count : Count_Type := 1)
763 New_Node : Node_Access;
766 if Before.Container /= null then
767 if Before.Container /= Container'Unrestricted_Access then
768 raise Program_Error with
769 "Before cursor designates wrong list";
772 pragma Assert (Vet (Before), "bad cursor in Insert");
780 if Container.Length > Count_Type'Last - Count then
781 raise Constraint_Error with "new length exceeds maximum";
784 if Container.Busy > 0 then
785 raise Program_Error with
786 "attempt to tamper with cursors (list is busy)";
789 New_Node := new Node_Type'(New_Item, null, null);
790 Insert_Internal (Container, Before.Node, New_Node);
792 Position := Cursor'(Container'Unchecked_Access, New_Node);
794 for J in Count_Type'(2) .. Count loop
795 New_Node := new Node_Type'(New_Item, null, null);
796 Insert_Internal (Container, Before.Node, New_Node);
801 (Container : in out List;
803 New_Item : Element_Type;
804 Count : Count_Type := 1)
807 pragma Unreferenced (Position);
809 Insert (Container, Before, New_Item, Position, Count);
813 (Container : in out List;
815 Position : out Cursor;
816 Count : Count_Type := 1)
818 New_Node : Node_Access;
821 if Before.Container /= null then
822 if Before.Container /= Container'Unrestricted_Access then
823 raise Program_Error with
824 "Before cursor designates wrong list";
827 pragma Assert (Vet (Before), "bad cursor in Insert");
835 if Container.Length > Count_Type'Last - Count then
836 raise Constraint_Error with "new length exceeds maximum";
839 if Container.Busy > 0 then
840 raise Program_Error with
841 "attempt to tamper with cursors (list is busy)";
844 New_Node := new Node_Type;
845 Insert_Internal (Container, Before.Node, New_Node);
847 Position := Cursor'(Container'Unchecked_Access, New_Node);
849 for J in Count_Type'(2) .. Count loop
850 New_Node := new Node_Type;
851 Insert_Internal (Container, Before.Node, New_Node);
855 ---------------------
856 -- Insert_Internal --
857 ---------------------
859 procedure Insert_Internal
860 (Container : in out List;
861 Before : Node_Access;
862 New_Node : Node_Access)
865 if Container.Length = 0 then
866 pragma Assert (Before = null);
867 pragma Assert (Container.First = null);
868 pragma Assert (Container.Last = null);
870 Container.First := New_Node;
871 Container.Last := New_Node;
873 elsif Before = null then
874 pragma Assert (Container.Last.Next = null);
876 Container.Last.Next := New_Node;
877 New_Node.Prev := Container.Last;
879 Container.Last := New_Node;
881 elsif Before = Container.First then
882 pragma Assert (Container.First.Prev = null);
884 Container.First.Prev := New_Node;
885 New_Node.Next := Container.First;
887 Container.First := New_Node;
890 pragma Assert (Container.First.Prev = null);
891 pragma Assert (Container.Last.Next = null);
893 New_Node.Next := Before;
894 New_Node.Prev := Before.Prev;
896 Before.Prev.Next := New_Node;
897 Before.Prev := New_Node;
900 Container.Length := Container.Length + 1;
907 function Is_Empty (Container : List) return Boolean is
909 return Container.Length = 0;
918 Process : not null access procedure (Position : Cursor))
920 B : Natural renames Container'Unrestricted_Access.all.Busy;
921 Node : Node_Access := Container.First;
927 while Node /= null loop
928 Process (Cursor'(Container'Unrestricted_Access, Node));
940 function Iterate (Container : List)
941 return List_Iterator_Interfaces.Reversible_Iterator'Class
943 B : Natural renames Container'Unrestricted_Access.all.Busy;
946 -- The value of the Node component influences the behavior of the First
947 -- and Last selector functions of the iterator object. When the Node
948 -- component is null (as is the case here), this means the iterator
949 -- object was constructed without a start expression. This is a
950 -- complete iterator, meaning that the iteration starts from the
951 -- (logical) beginning of the sequence of items.
953 -- Note: For a forward iterator, Container.First is the beginning, and
954 -- for a reverse iterator, Container.Last is the beginning.
956 return It : constant Iterator :=
957 Iterator'(Limited_Controlled with
958 Container => Container'Unrestricted_Access,
965 function Iterate (Container : List; Start : Cursor)
966 return List_Iterator_Interfaces.Reversible_Iterator'Class
968 B : Natural renames Container'Unrestricted_Access.all.Busy;
971 -- It was formerly the case that when Start = No_Element, the partial
972 -- iterator was defined to behave the same as for a complete iterator,
973 -- and iterate over the entire sequence of items. However, those
974 -- semantics were unintuitive and arguably error-prone (it is too easy
975 -- to accidentally create an endless loop), and so they were changed,
976 -- per the ARG meeting in Denver on 2011/11. However, there was no
977 -- consensus about what positive meaning this corner case should have,
978 -- and so it was decided to simply raise an exception. This does imply,
979 -- however, that it is not possible to use a partial iterator to specify
980 -- an empty sequence of items.
982 if Start = No_Element then
983 raise Constraint_Error with
984 "Start position for iterator equals No_Element";
987 if Start.Container /= Container'Unrestricted_Access then
988 raise Program_Error with
989 "Start cursor of Iterate designates wrong list";
992 pragma Assert (Vet (Start), "Start cursor of Iterate is bad");
994 -- The value of the Node component influences the behavior of the First
995 -- and Last selector functions of the iterator object. When the Node
996 -- component is non-null (as is the case here), it means that this
997 -- is a partial iteration, over a subset of the complete sequence of
998 -- items. The iterator object was constructed with a start expression,
999 -- indicating the position from which the iteration begins. Note that
1000 -- the start position has the same value irrespective of whether this
1001 -- is a forward or reverse iteration.
1003 return It : constant Iterator :=
1004 Iterator'(Limited_Controlled with
1005 Container => Container'Unrestricted_Access,
1016 function Last (Container : List) return Cursor is
1018 if Container.Last = null then
1022 return Cursor'(Container'Unrestricted_Access, Container.Last);
1025 function Last (Object : Iterator) return Cursor is
1027 -- The value of the iterator object's Node component influences the
1028 -- behavior of the Last (and First) selector function.
1030 -- When the Node component is null, this means the iterator object was
1031 -- constructed without a start expression, in which case the (reverse)
1032 -- iteration starts from the (logical) beginning of the entire sequence
1033 -- (corresponding to Container.Last, for a reverse iterator).
1035 -- Otherwise, this is iteration over a partial sequence of items. When
1036 -- the Node component is non-null, the iterator object was constructed
1037 -- with a start expression, that specifies the position from which the
1038 -- (reverse) partial iteration begins.
1040 if Object.Node = null then
1041 return Doubly_Linked_Lists.Last (Object.Container.all);
1043 return Cursor'(Object.Container, Object.Node);
1051 function Last_Element (Container : List) return Element_Type is
1053 if Container.Last = null then
1054 raise Constraint_Error with "list is empty";
1057 return Container.Last.Element;
1064 function Length (Container : List) return Count_Type is
1066 return Container.Length;
1074 (Target : in out List;
1075 Source : in out List)
1078 if Target'Address = Source'Address then
1082 if Source.Busy > 0 then
1083 raise Program_Error with
1084 "attempt to tamper with cursors of Source (list is busy)";
1089 Target.First := Source.First;
1090 Source.First := null;
1092 Target.Last := Source.Last;
1093 Source.Last := null;
1095 Target.Length := Source.Length;
1103 procedure Next (Position : in out Cursor) is
1105 Position := Next (Position);
1108 function Next (Position : Cursor) return Cursor is
1110 if Position.Node = null then
1114 pragma Assert (Vet (Position), "bad cursor in Next");
1117 Next_Node : constant Node_Access := Position.Node.Next;
1120 if Next_Node = null then
1124 return Cursor'(Position.Container, Next_Node);
1130 Position : Cursor) return Cursor
1133 if Position.Container = null then
1137 if Position.Container /= Object.Container then
1138 raise Program_Error with
1139 "Position cursor of Next designates wrong list";
1142 return Next (Position);
1150 (Container : in out List;
1151 New_Item : Element_Type;
1152 Count : Count_Type := 1)
1155 Insert (Container, First (Container), New_Item, Count);
1162 procedure Previous (Position : in out Cursor) is
1164 Position := Previous (Position);
1167 function Previous (Position : Cursor) return Cursor is
1169 if Position.Node = null then
1173 pragma Assert (Vet (Position), "bad cursor in Previous");
1176 Prev_Node : constant Node_Access := Position.Node.Prev;
1179 if Prev_Node = null then
1183 return Cursor'(Position.Container, Prev_Node);
1189 Position : Cursor) return Cursor
1192 if Position.Container = null then
1196 if Position.Container /= Object.Container then
1197 raise Program_Error with
1198 "Position cursor of Previous designates wrong list";
1201 return Previous (Position);
1208 procedure Query_Element
1210 Process : not null access procedure (Element : Element_Type))
1213 if Position.Node = null then
1214 raise Constraint_Error with
1215 "Position cursor has no element";
1218 pragma Assert (Vet (Position), "bad cursor in Query_Element");
1221 C : List renames Position.Container.all'Unrestricted_Access.all;
1222 B : Natural renames C.Busy;
1223 L : Natural renames C.Lock;
1230 Process (Position.Node.Element);
1248 (Stream : not null access Root_Stream_Type'Class;
1251 N : Count_Type'Base;
1256 Count_Type'Base'Read (Stream, N);
1265 Element_Type'Read (Stream, X.Element);
1276 Item.Length := Item.Length + 1;
1277 exit when Item.Length = N;
1282 Element_Type'Read (Stream, X.Element);
1289 X.Prev := Item.Last;
1290 Item.Last.Next := X;
1296 (Stream : not null access Root_Stream_Type'Class;
1300 raise Program_Error with "attempt to stream list cursor";
1304 (Stream : not null access Root_Stream_Type'Class;
1305 Item : out Reference_Type)
1308 raise Program_Error with "attempt to stream reference";
1312 (Stream : not null access Root_Stream_Type'Class;
1313 Item : out Constant_Reference_Type)
1316 raise Program_Error with "attempt to stream reference";
1324 (Container : aliased in out List;
1325 Position : Cursor) return Reference_Type
1328 if Position.Container = null then
1329 raise Constraint_Error with "Position cursor has no element";
1332 if Position.Container /= Container'Unchecked_Access then
1333 raise Program_Error with
1334 "Position cursor designates wrong container";
1337 pragma Assert (Vet (Position), "bad cursor in function Reference");
1339 return (Element => Position.Node.Element'Access);
1342 ---------------------
1343 -- Replace_Element --
1344 ---------------------
1346 procedure Replace_Element
1347 (Container : in out List;
1349 New_Item : Element_Type)
1352 if Position.Container = null then
1353 raise Constraint_Error with "Position cursor has no element";
1356 if Position.Container /= Container'Unchecked_Access then
1357 raise Program_Error with
1358 "Position cursor designates wrong container";
1361 if Container.Lock > 0 then
1362 raise Program_Error with
1363 "attempt to tamper with elements (list is locked)";
1366 pragma Assert (Vet (Position), "bad cursor in Replace_Element");
1368 Position.Node.Element := New_Item;
1369 end Replace_Element;
1371 ----------------------
1372 -- Reverse_Elements --
1373 ----------------------
1375 procedure Reverse_Elements (Container : in out List) is
1376 I : Node_Access := Container.First;
1377 J : Node_Access := Container.Last;
1379 procedure Swap (L, R : Node_Access);
1385 procedure Swap (L, R : Node_Access) is
1386 LN : constant Node_Access := L.Next;
1387 LP : constant Node_Access := L.Prev;
1389 RN : constant Node_Access := R.Next;
1390 RP : constant Node_Access := R.Prev;
1405 pragma Assert (RP = L);
1419 -- Start of processing for Reverse_Elements
1422 if Container.Length <= 1 then
1426 pragma Assert (Container.First.Prev = null);
1427 pragma Assert (Container.Last.Next = null);
1429 if Container.Busy > 0 then
1430 raise Program_Error with
1431 "attempt to tamper with cursors (list is busy)";
1434 Container.First := J;
1435 Container.Last := I;
1437 Swap (L => I, R => J);
1445 Swap (L => J, R => I);
1454 pragma Assert (Container.First.Prev = null);
1455 pragma Assert (Container.Last.Next = null);
1456 end Reverse_Elements;
1462 function Reverse_Find
1464 Item : Element_Type;
1465 Position : Cursor := No_Element) return Cursor
1467 Node : Node_Access := Position.Node;
1471 Node := Container.Last;
1474 if Position.Container /= Container'Unrestricted_Access then
1475 raise Program_Error with
1476 "Position cursor designates wrong container";
1479 pragma Assert (Vet (Position), "bad cursor in Reverse_Find");
1482 while Node /= null loop
1483 if Node.Element = Item then
1484 return Cursor'(Container'Unrestricted_Access, Node);
1493 ---------------------
1494 -- Reverse_Iterate --
1495 ---------------------
1497 procedure Reverse_Iterate
1499 Process : not null access procedure (Position : Cursor))
1501 C : List renames Container'Unrestricted_Access.all;
1502 B : Natural renames C.Busy;
1504 Node : Node_Access := Container.Last;
1510 while Node /= null loop
1511 Process (Cursor'(Container'Unrestricted_Access, Node));
1522 end Reverse_Iterate;
1529 (Target : in out List;
1531 Source : in out List)
1534 if Before.Container /= null then
1535 if Before.Container /= Target'Unrestricted_Access then
1536 raise Program_Error with
1537 "Before cursor designates wrong container";
1540 pragma Assert (Vet (Before), "bad cursor in Splice");
1543 if Target'Address = Source'Address
1544 or else Source.Length = 0
1549 pragma Assert (Source.First.Prev = null);
1550 pragma Assert (Source.Last.Next = null);
1552 if Target.Length > Count_Type'Last - Source.Length then
1553 raise Constraint_Error with "new length exceeds maximum";
1556 if Target.Busy > 0 then
1557 raise Program_Error with
1558 "attempt to tamper with cursors of Target (list is busy)";
1561 if Source.Busy > 0 then
1562 raise Program_Error with
1563 "attempt to tamper with cursors of Source (list is busy)";
1566 if Target.Length = 0 then
1567 pragma Assert (Target.First = null);
1568 pragma Assert (Target.Last = null);
1569 pragma Assert (Before = No_Element);
1571 Target.First := Source.First;
1572 Target.Last := Source.Last;
1574 elsif Before.Node = null then
1575 pragma Assert (Target.Last.Next = null);
1577 Target.Last.Next := Source.First;
1578 Source.First.Prev := Target.Last;
1580 Target.Last := Source.Last;
1582 elsif Before.Node = Target.First then
1583 pragma Assert (Target.First.Prev = null);
1585 Source.Last.Next := Target.First;
1586 Target.First.Prev := Source.Last;
1588 Target.First := Source.First;
1591 pragma Assert (Target.Length >= 2);
1593 Before.Node.Prev.Next := Source.First;
1594 Source.First.Prev := Before.Node.Prev;
1596 Before.Node.Prev := Source.Last;
1597 Source.Last.Next := Before.Node;
1600 Source.First := null;
1601 Source.Last := null;
1603 Target.Length := Target.Length + Source.Length;
1608 (Container : in out List;
1613 if Before.Container /= null then
1614 if Before.Container /= Container'Unchecked_Access then
1615 raise Program_Error with
1616 "Before cursor designates wrong container";
1619 pragma Assert (Vet (Before), "bad Before cursor in Splice");
1622 if Position.Node = null then
1623 raise Constraint_Error with "Position cursor has no element";
1626 if Position.Container /= Container'Unrestricted_Access then
1627 raise Program_Error with
1628 "Position cursor designates wrong container";
1631 pragma Assert (Vet (Position), "bad Position cursor in Splice");
1633 if Position.Node = Before.Node
1634 or else Position.Node.Next = Before.Node
1639 pragma Assert (Container.Length >= 2);
1641 if Container.Busy > 0 then
1642 raise Program_Error with
1643 "attempt to tamper with cursors (list is busy)";
1646 if Before.Node = null then
1647 pragma Assert (Position.Node /= Container.Last);
1649 if Position.Node = Container.First then
1650 Container.First := Position.Node.Next;
1651 Container.First.Prev := null;
1653 Position.Node.Prev.Next := Position.Node.Next;
1654 Position.Node.Next.Prev := Position.Node.Prev;
1657 Container.Last.Next := Position.Node;
1658 Position.Node.Prev := Container.Last;
1660 Container.Last := Position.Node;
1661 Container.Last.Next := null;
1666 if Before.Node = Container.First then
1667 pragma Assert (Position.Node /= Container.First);
1669 if Position.Node = Container.Last then
1670 Container.Last := Position.Node.Prev;
1671 Container.Last.Next := null;
1673 Position.Node.Prev.Next := Position.Node.Next;
1674 Position.Node.Next.Prev := Position.Node.Prev;
1677 Container.First.Prev := Position.Node;
1678 Position.Node.Next := Container.First;
1680 Container.First := Position.Node;
1681 Container.First.Prev := null;
1686 if Position.Node = Container.First then
1687 Container.First := Position.Node.Next;
1688 Container.First.Prev := null;
1690 elsif Position.Node = Container.Last then
1691 Container.Last := Position.Node.Prev;
1692 Container.Last.Next := null;
1695 Position.Node.Prev.Next := Position.Node.Next;
1696 Position.Node.Next.Prev := Position.Node.Prev;
1699 Before.Node.Prev.Next := Position.Node;
1700 Position.Node.Prev := Before.Node.Prev;
1702 Before.Node.Prev := Position.Node;
1703 Position.Node.Next := Before.Node;
1705 pragma Assert (Container.First.Prev = null);
1706 pragma Assert (Container.Last.Next = null);
1710 (Target : in out List;
1712 Source : in out List;
1713 Position : in out Cursor)
1716 if Target'Address = Source'Address then
1717 Splice (Target, Before, Position);
1721 if Before.Container /= null then
1722 if Before.Container /= Target'Unrestricted_Access then
1723 raise Program_Error with
1724 "Before cursor designates wrong container";
1727 pragma Assert (Vet (Before), "bad Before cursor in Splice");
1730 if Position.Node = null then
1731 raise Constraint_Error with "Position cursor has no element";
1734 if Position.Container /= Source'Unrestricted_Access then
1735 raise Program_Error with
1736 "Position cursor designates wrong container";
1739 pragma Assert (Vet (Position), "bad Position cursor in Splice");
1741 if Target.Length = Count_Type'Last then
1742 raise Constraint_Error with "Target is full";
1745 if Target.Busy > 0 then
1746 raise Program_Error with
1747 "attempt to tamper with cursors of Target (list is busy)";
1750 if Source.Busy > 0 then
1751 raise Program_Error with
1752 "attempt to tamper with cursors of Source (list is busy)";
1755 if Position.Node = Source.First then
1756 Source.First := Position.Node.Next;
1758 if Position.Node = Source.Last then
1759 pragma Assert (Source.First = null);
1760 pragma Assert (Source.Length = 1);
1761 Source.Last := null;
1764 Source.First.Prev := null;
1767 elsif Position.Node = Source.Last then
1768 pragma Assert (Source.Length >= 2);
1769 Source.Last := Position.Node.Prev;
1770 Source.Last.Next := null;
1773 pragma Assert (Source.Length >= 3);
1774 Position.Node.Prev.Next := Position.Node.Next;
1775 Position.Node.Next.Prev := Position.Node.Prev;
1778 if Target.Length = 0 then
1779 pragma Assert (Target.First = null);
1780 pragma Assert (Target.Last = null);
1781 pragma Assert (Before = No_Element);
1783 Target.First := Position.Node;
1784 Target.Last := Position.Node;
1786 Target.First.Prev := null;
1787 Target.Last.Next := null;
1789 elsif Before.Node = null then
1790 pragma Assert (Target.Last.Next = null);
1791 Target.Last.Next := Position.Node;
1792 Position.Node.Prev := Target.Last;
1794 Target.Last := Position.Node;
1795 Target.Last.Next := null;
1797 elsif Before.Node = Target.First then
1798 pragma Assert (Target.First.Prev = null);
1799 Target.First.Prev := Position.Node;
1800 Position.Node.Next := Target.First;
1802 Target.First := Position.Node;
1803 Target.First.Prev := null;
1806 pragma Assert (Target.Length >= 2);
1807 Before.Node.Prev.Next := Position.Node;
1808 Position.Node.Prev := Before.Node.Prev;
1810 Before.Node.Prev := Position.Node;
1811 Position.Node.Next := Before.Node;
1814 Target.Length := Target.Length + 1;
1815 Source.Length := Source.Length - 1;
1817 Position.Container := Target'Unchecked_Access;
1825 (Container : in out List;
1829 if I.Node = null then
1830 raise Constraint_Error with "I cursor has no element";
1833 if J.Node = null then
1834 raise Constraint_Error with "J cursor has no element";
1837 if I.Container /= Container'Unchecked_Access then
1838 raise Program_Error with "I cursor designates wrong container";
1841 if J.Container /= Container'Unchecked_Access then
1842 raise Program_Error with "J cursor designates wrong container";
1845 if I.Node = J.Node then
1849 if Container.Lock > 0 then
1850 raise Program_Error with
1851 "attempt to tamper with elements (list is locked)";
1854 pragma Assert (Vet (I), "bad I cursor in Swap");
1855 pragma Assert (Vet (J), "bad J cursor in Swap");
1858 EI : Element_Type renames I.Node.Element;
1859 EJ : Element_Type renames J.Node.Element;
1861 EI_Copy : constant Element_Type := EI;
1873 procedure Swap_Links
1874 (Container : in out List;
1878 if I.Node = null then
1879 raise Constraint_Error with "I cursor has no element";
1882 if J.Node = null then
1883 raise Constraint_Error with "J cursor has no element";
1886 if I.Container /= Container'Unrestricted_Access then
1887 raise Program_Error with "I cursor designates wrong container";
1890 if J.Container /= Container'Unrestricted_Access then
1891 raise Program_Error with "J cursor designates wrong container";
1894 if I.Node = J.Node then
1898 if Container.Busy > 0 then
1899 raise Program_Error with
1900 "attempt to tamper with cursors (list is busy)";
1903 pragma Assert (Vet (I), "bad I cursor in Swap_Links");
1904 pragma Assert (Vet (J), "bad J cursor in Swap_Links");
1907 I_Next : constant Cursor := Next (I);
1911 Splice (Container, Before => I, Position => J);
1915 J_Next : constant Cursor := Next (J);
1919 Splice (Container, Before => J, Position => I);
1922 pragma Assert (Container.Length >= 3);
1924 Splice (Container, Before => I_Next, Position => J);
1925 Splice (Container, Before => J_Next, Position => I);
1932 --------------------
1933 -- Update_Element --
1934 --------------------
1936 procedure Update_Element
1937 (Container : in out List;
1939 Process : not null access procedure (Element : in out Element_Type))
1942 if Position.Node = null then
1943 raise Constraint_Error with "Position cursor has no element";
1946 if Position.Container /= Container'Unchecked_Access then
1947 raise Program_Error with
1948 "Position cursor designates wrong container";
1951 pragma Assert (Vet (Position), "bad cursor in Update_Element");
1954 B : Natural renames Container.Busy;
1955 L : Natural renames Container.Lock;
1962 Process (Position.Node.Element);
1979 function Vet (Position : Cursor) return Boolean is
1981 if Position.Node = null then
1982 return Position.Container = null;
1985 if Position.Container = null then
1989 -- An invariant of a node is that its Previous and Next components can
1990 -- be null, or designate a different node. Operation Free sets the
1991 -- access value components of the node to designate the node itself
1992 -- before actually deallocating the node, thus deliberately violating
1993 -- the node invariant. This gives us a simple way to detect a dangling
1994 -- reference to a node.
1996 if Position.Node.Next = Position.Node then
2000 if Position.Node.Prev = Position.Node then
2004 -- In practice the tests above will detect most instances of a dangling
2005 -- reference. If we get here, it means that the invariants of the
2006 -- designated node are satisfied (they at least appear to be satisfied),
2007 -- so we perform some more tests, to determine whether invariants of the
2008 -- designated list are satisfied too.
2011 L : List renames Position.Container.all;
2014 if L.Length = 0 then
2018 if L.First = null then
2022 if L.Last = null then
2026 if L.First.Prev /= null then
2030 if L.Last.Next /= null then
2034 if Position.Node.Prev = null and then Position.Node /= L.First then
2039 (Position.Node.Prev /= null
2040 or else Position.Node = L.First);
2042 if Position.Node.Next = null and then Position.Node /= L.Last then
2047 (Position.Node.Next /= null
2048 or else Position.Node = L.Last);
2050 if L.Length = 1 then
2051 return L.First = L.Last;
2054 if L.First = L.Last then
2058 if L.First.Next = null then
2062 if L.Last.Prev = null then
2066 if L.First.Next.Prev /= L.First then
2070 if L.Last.Prev.Next /= L.Last then
2074 if L.Length = 2 then
2075 if L.First.Next /= L.Last then
2077 elsif L.Last.Prev /= L.First then
2084 if L.First.Next = L.Last then
2088 if L.Last.Prev = L.First then
2092 -- Eliminate earlier possibility
2094 if Position.Node = L.First then
2098 pragma Assert (Position.Node.Prev /= null);
2100 -- Eliminate earlier possibility
2102 if Position.Node = L.Last then
2106 pragma Assert (Position.Node.Next /= null);
2108 if Position.Node.Next.Prev /= Position.Node then
2112 if Position.Node.Prev.Next /= Position.Node then
2116 if L.Length = 3 then
2117 if L.First.Next /= Position.Node then
2119 elsif L.Last.Prev /= Position.Node then
2133 (Stream : not null access Root_Stream_Type'Class;
2139 Count_Type'Base'Write (Stream, Item.Length);
2142 while Node /= null loop
2143 Element_Type'Write (Stream, Node.Element);
2149 (Stream : not null access Root_Stream_Type'Class;
2153 raise Program_Error with "attempt to stream list cursor";
2157 (Stream : not null access Root_Stream_Type'Class;
2158 Item : Reference_Type)
2161 raise Program_Error with "attempt to stream reference";
2165 (Stream : not null access Root_Stream_Type'Class;
2166 Item : Constant_Reference_Type)
2169 raise Program_Error with "attempt to stream reference";
2172 end Ada.Containers.Doubly_Linked_Lists;