1 ------------------------------------------------------------------------------
3 -- GNAT LIBRARY COMPONENTS --
5 -- ADA.CONTAINERS.BOUNDED_DOUBLY_LINKED_LISTS --
9 -- Copyright (C) 2004-2011, 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.Finalization; use Ada.Finalization;
32 with System; use type System.Address;
34 package body Ada.Containers.Bounded_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 -----------------------
61 (Container : in out List;
62 New_Item : Element_Type;
63 New_Node : out Count_Type);
66 (Container : in out List;
67 New_Node : out Count_Type);
70 (Container : in out List;
71 Stream : not null access Root_Stream_Type'Class;
72 New_Node : out Count_Type);
75 (Container : in out List;
78 procedure Insert_Internal
79 (Container : in out List;
81 New_Node : Count_Type);
83 function Vet (Position : Cursor) return Boolean;
89 function "=" (Left, Right : List) return Boolean is
90 LN : Node_Array renames Left.Nodes;
91 RN : Node_Array renames Right.Nodes;
96 if Left'Address = Right'Address then
100 if Left.Length /= Right.Length then
106 for J in 1 .. Left.Length loop
107 if LN (LI).Element /= RN (RI).Element then
123 (Container : in out List;
124 New_Item : Element_Type;
125 New_Node : out Count_Type)
127 N : Node_Array renames Container.Nodes;
130 if Container.Free >= 0 then
131 New_Node := Container.Free;
133 -- We always perform the assignment first, before we change container
134 -- state, in order to defend against exceptions duration assignment.
136 N (New_Node).Element := New_Item;
137 Container.Free := N (New_Node).Next;
140 -- A negative free store value means that the links of the nodes in
141 -- the free store have not been initialized. In this case, the nodes
142 -- are physically contiguous in the array, starting at the index that
143 -- is the absolute value of the Container.Free, and continuing until
144 -- the end of the array (Nodes'Last).
146 New_Node := abs Container.Free;
148 -- As above, we perform this assignment first, before modifying any
151 N (New_Node).Element := New_Item;
152 Container.Free := Container.Free - 1;
157 (Container : in out List;
158 Stream : not null access Root_Stream_Type'Class;
159 New_Node : out Count_Type)
161 N : Node_Array renames Container.Nodes;
164 if Container.Free >= 0 then
165 New_Node := Container.Free;
167 -- We always perform the assignment first, before we change container
168 -- state, in order to defend against exceptions duration assignment.
170 Element_Type'Read (Stream, N (New_Node).Element);
171 Container.Free := N (New_Node).Next;
174 -- A negative free store value means that the links of the nodes in
175 -- the free store have not been initialized. In this case, the nodes
176 -- are physically contiguous in the array, starting at the index that
177 -- is the absolute value of the Container.Free, and continuing until
178 -- the end of the array (Nodes'Last).
180 New_Node := abs Container.Free;
182 -- As above, we perform this assignment first, before modifying any
185 Element_Type'Read (Stream, N (New_Node).Element);
186 Container.Free := Container.Free - 1;
191 (Container : in out List;
192 New_Node : out Count_Type)
194 N : Node_Array renames Container.Nodes;
197 if Container.Free >= 0 then
198 New_Node := Container.Free;
199 Container.Free := N (New_Node).Next;
202 -- As explained above, a negative free store value means that the
203 -- links for the nodes in the free store have not been initialized.
205 New_Node := abs Container.Free;
206 Container.Free := Container.Free - 1;
215 (Container : in out List;
216 New_Item : Element_Type;
217 Count : Count_Type := 1)
220 Insert (Container, No_Element, New_Item, Count);
227 procedure Assign (Target : in out List; Source : List) is
228 SN : Node_Array renames Source.Nodes;
232 if Target'Address = Source'Address then
236 if Target.Capacity < Source.Length then
237 raise Capacity_Error -- ???
238 with "Target capacity is less than Source length";
245 Target.Append (SN (J).Element);
254 procedure Clear (Container : in out List) is
255 N : Node_Array renames Container.Nodes;
259 if Container.Length = 0 then
260 pragma Assert (Container.First = 0);
261 pragma Assert (Container.Last = 0);
262 pragma Assert (Container.Busy = 0);
263 pragma Assert (Container.Lock = 0);
267 pragma Assert (Container.First >= 1);
268 pragma Assert (Container.Last >= 1);
269 pragma Assert (N (Container.First).Prev = 0);
270 pragma Assert (N (Container.Last).Next = 0);
272 if Container.Busy > 0 then
273 raise Program_Error with
274 "attempt to tamper with cursors (list is busy)";
277 while Container.Length > 1 loop
278 X := Container.First;
279 pragma Assert (N (N (X).Next).Prev = Container.First);
281 Container.First := N (X).Next;
282 N (Container.First).Prev := 0;
284 Container.Length := Container.Length - 1;
289 X := Container.First;
290 pragma Assert (X = Container.Last);
292 Container.First := 0;
294 Container.Length := 0;
305 Item : Element_Type) return Boolean
308 return Find (Container, Item) /= No_Element;
315 function Copy (Source : List; Capacity : Count_Type := 0) return List is
322 elsif Capacity >= Source.Length then
326 raise Capacity_Error with "Capacity value too small";
329 return Target : List (Capacity => C) do
330 Assign (Target => Target, Source => Source);
339 (Container : in out List;
340 Position : in out Cursor;
341 Count : Count_Type := 1)
343 N : Node_Array renames Container.Nodes;
347 if Position.Node = 0 then
348 raise Constraint_Error with
349 "Position cursor has no element";
352 if Position.Container /= Container'Unrestricted_Access then
353 raise Program_Error with
354 "Position cursor designates wrong container";
357 pragma Assert (Vet (Position), "bad cursor in Delete");
358 pragma Assert (Container.First >= 1);
359 pragma Assert (Container.Last >= 1);
360 pragma Assert (N (Container.First).Prev = 0);
361 pragma Assert (N (Container.Last).Next = 0);
363 if Position.Node = Container.First then
364 Delete_First (Container, Count);
365 Position := No_Element;
370 Position := No_Element;
374 if Container.Busy > 0 then
375 raise Program_Error with
376 "attempt to tamper with cursors (list is busy)";
379 for Index in 1 .. Count loop
380 pragma Assert (Container.Length >= 2);
383 Container.Length := Container.Length - 1;
385 if X = Container.Last then
386 Position := No_Element;
388 Container.Last := N (X).Prev;
389 N (Container.Last).Next := 0;
395 Position.Node := N (X).Next;
397 N (N (X).Next).Prev := N (X).Prev;
398 N (N (X).Prev).Next := N (X).Next;
403 Position := No_Element;
410 procedure Delete_First
411 (Container : in out List;
412 Count : Count_Type := 1)
414 N : Node_Array renames Container.Nodes;
418 if Count >= Container.Length then
427 if Container.Busy > 0 then
428 raise Program_Error with
429 "attempt to tamper with cursors (list is busy)";
432 for I in 1 .. Count loop
433 X := Container.First;
434 pragma Assert (N (N (X).Next).Prev = Container.First);
436 Container.First := N (X).Next;
437 N (Container.First).Prev := 0;
439 Container.Length := Container.Length - 1;
449 procedure Delete_Last
450 (Container : in out List;
451 Count : Count_Type := 1)
453 N : Node_Array renames Container.Nodes;
457 if Count >= Container.Length then
466 if Container.Busy > 0 then
467 raise Program_Error with
468 "attempt to tamper with cursors (list is busy)";
471 for I in 1 .. Count loop
473 pragma Assert (N (N (X).Prev).Next = Container.Last);
475 Container.Last := N (X).Prev;
476 N (Container.Last).Next := 0;
478 Container.Length := Container.Length - 1;
488 function Element (Position : Cursor) return Element_Type is
490 if Position.Node = 0 then
491 raise Constraint_Error with
492 "Position cursor has no element";
495 pragma Assert (Vet (Position), "bad cursor in Element");
497 return Position.Container.Nodes (Position.Node).Element;
504 procedure Finalize (Object : in out Iterator) is
506 if Object.Container /= null then
508 B : Natural renames Object.Container.all.Busy;
523 Position : Cursor := No_Element) return Cursor
525 Nodes : Node_Array renames Container.Nodes;
526 Node : Count_Type := Position.Node;
530 Node := Container.First;
533 if Position.Container /= Container'Unrestricted_Access then
534 raise Program_Error with
535 "Position cursor designates wrong container";
538 pragma Assert (Vet (Position), "bad cursor in Find");
542 if Nodes (Node).Element = Item then
543 return Cursor'(Container'Unrestricted_Access, Node);
546 Node := Nodes (Node).Next;
556 function First (Container : List) return Cursor is
558 if Container.First = 0 then
562 return Cursor'(Container'Unrestricted_Access, Container.First);
565 function First (Object : Iterator) return Cursor is
567 -- The value of the iterator object's Node component influences the
568 -- behavior of the First (and Last) selector function.
570 -- When the Node component is 0, this means the iterator object was
571 -- constructed without a start expression, in which case the (forward)
572 -- iteration starts from the (logical) beginning of the entire sequence
573 -- of items (corresponding to Container.First, for a forward iterator).
575 -- Otherwise, this is iteration over a partial sequence of items. When
576 -- the Node component is positive, the iterator object was constructed
577 -- with a start expression, that specifies the position from which the
578 -- (forward) partial iteration begins.
580 if Object.Node = 0 then
581 return Bounded_Doubly_Linked_Lists.First (Object.Container.all);
583 return Cursor'(Object.Container, Object.Node);
591 function First_Element (Container : List) return Element_Type is
593 if Container.First = 0 then
594 raise Constraint_Error with "list is empty";
597 return Container.Nodes (Container.First).Element;
605 (Container : in out List;
608 pragma Assert (X > 0);
609 pragma Assert (X <= Container.Capacity);
611 N : Node_Array renames Container.Nodes;
612 pragma Assert (N (X).Prev >= 0); -- node is active
615 -- The list container actually contains two lists: one for the "active"
616 -- nodes that contain elements that have been inserted onto the list,
617 -- and another for the "inactive" nodes for the free store.
619 -- We desire that merely declaring an object should have only minimal
620 -- cost; specially, we want to avoid having to initialize the free
621 -- store (to fill in the links), especially if the capacity is large.
623 -- The head of the free list is indicated by Container.Free. If its
624 -- value is non-negative, then the free store has been initialized in
625 -- the "normal" way: Container.Free points to the head of the list of
626 -- free (inactive) nodes, and the value 0 means the free list is empty.
627 -- Each node on the free list has been initialized to point to the next
628 -- free node (via its Next component), and the value 0 means that this
629 -- is the last free node.
631 -- If Container.Free is negative, then the links on the free store have
632 -- not been initialized. In this case the link values are implied: the
633 -- free store comprises the components of the node array started with
634 -- the absolute value of Container.Free, and continuing until the end of
635 -- the array (Nodes'Last).
637 -- If the list container is manipulated on one end only (for example if
638 -- the container were being used as a stack), then there is no need to
639 -- initialize the free store, since the inactive nodes are physically
640 -- contiguous (in fact, they lie immediately beyond the logical end
641 -- being manipulated). The only time we need to actually initialize the
642 -- nodes in the free store is if the node that becomes inactive is not
643 -- at the end of the list. The free store would then be discontiguous
644 -- and so its nodes would need to be linked in the traditional way.
647 -- It might be possible to perform an optimization here. Suppose that
648 -- the free store can be represented as having two parts: one comprising
649 -- the non-contiguous inactive nodes linked together in the normal way,
650 -- and the other comprising the contiguous inactive nodes (that are not
651 -- linked together, at the end of the nodes array). This would allow us
652 -- to never have to initialize the free store, except in a lazy way as
653 -- nodes become inactive.
655 -- When an element is deleted from the list container, its node becomes
656 -- inactive, and so we set its Prev component to a negative value, to
657 -- indicate that it is now inactive. This provides a useful way to
658 -- detect a dangling cursor reference.
660 N (X).Prev := -1; -- Node is deallocated (not on active list)
662 if Container.Free >= 0 then
664 -- The free store has previously been initialized. All we need to
665 -- do here is link the newly-free'd node onto the free list.
667 N (X).Next := Container.Free;
670 elsif X + 1 = abs Container.Free then
672 -- The free store has not been initialized, and the node becoming
673 -- inactive immediately precedes the start of the free store. All
674 -- we need to do is move the start of the free store back by one.
676 -- Note: initializing Next to zero is not strictly necessary but
677 -- seems cleaner and marginally safer.
680 Container.Free := Container.Free + 1;
683 -- The free store has not been initialized, and the node becoming
684 -- inactive does not immediately precede the free store. Here we
685 -- first initialize the free store (meaning the links are given
686 -- values in the traditional way), and then link the newly-free'd
687 -- node onto the head of the free store.
690 -- See the comments above for an optimization opportunity. If the
691 -- next link for a node on the free store is negative, then this
692 -- means the remaining nodes on the free store are physically
693 -- contiguous, starting as the absolute value of that index value.
695 Container.Free := abs Container.Free;
697 if Container.Free > Container.Capacity then
701 for I in Container.Free .. Container.Capacity - 1 loop
705 N (Container.Capacity).Next := 0;
708 N (X).Next := Container.Free;
713 ---------------------
714 -- Generic_Sorting --
715 ---------------------
717 package body Generic_Sorting is
723 function Is_Sorted (Container : List) return Boolean is
724 Nodes : Node_Array renames Container.Nodes;
725 Node : Count_Type := Container.First;
728 for J in 2 .. Container.Length loop
729 if Nodes (Nodes (Node).Next).Element < Nodes (Node).Element then
733 Node := Nodes (Node).Next;
744 (Target : in out List;
745 Source : in out List)
747 LN : Node_Array renames Target.Nodes;
748 RN : Node_Array renames Source.Nodes;
753 -- The semantics of Merge changed slightly per AI05-0021. It was
754 -- originally the case that if Target and Source denoted the same
755 -- container object, then the GNAT implementation of Merge did
756 -- nothing. However, it was argued that RM05 did not precisely
757 -- specify the semantics for this corner case. The decision of the
758 -- ARG was that if Target and Source denote the same non-empty
759 -- container object, then Program_Error is raised.
761 if Source.Is_Empty then
765 if Target'Address = Source'Address then
766 raise Program_Error with
767 "Target and Source denote same non-empty container";
770 if Target.Busy > 0 then
771 raise Program_Error with
772 "attempt to tamper with cursors of Target (list is busy)";
775 if Source.Busy > 0 then
776 raise Program_Error with
777 "attempt to tamper with cursors of Source (list is busy)";
780 LI := First (Target);
781 RI := First (Source);
782 while RI.Node /= 0 loop
783 pragma Assert (RN (RI.Node).Next = 0
784 or else not (RN (RN (RI.Node).Next).Element <
785 RN (RI.Node).Element));
788 Splice (Target, No_Element, Source);
792 pragma Assert (LN (LI.Node).Next = 0
793 or else not (LN (LN (LI.Node).Next).Element <
794 LN (LI.Node).Element));
796 if RN (RI.Node).Element < LN (LI.Node).Element then
800 RI.Node := RN (RI.Node).Next;
801 Splice (Target, LI, Source, RJ);
805 LI.Node := LN (LI.Node).Next;
814 procedure Sort (Container : in out List) is
815 N : Node_Array renames Container.Nodes;
817 procedure Partition (Pivot, Back : Count_Type);
818 -- What does this do ???
820 procedure Sort (Front, Back : Count_Type);
821 -- Internal procedure, what does it do??? rename it???
827 procedure Partition (Pivot, Back : Count_Type) is
831 Node := N (Pivot).Next;
832 while Node /= Back loop
833 if N (Node).Element < N (Pivot).Element then
835 Prev : constant Count_Type := N (Node).Prev;
836 Next : constant Count_Type := N (Node).Next;
839 N (Prev).Next := Next;
842 Container.Last := Prev;
844 N (Next).Prev := Prev;
847 N (Node).Next := Pivot;
848 N (Node).Prev := N (Pivot).Prev;
850 N (Pivot).Prev := Node;
852 if N (Node).Prev = 0 then
853 Container.First := Node;
855 N (N (Node).Prev).Next := Node;
862 Node := N (Node).Next;
871 procedure Sort (Front, Back : Count_Type) is
872 Pivot : constant Count_Type :=
873 (if Front = 0 then Container.First else N (Front).Next);
875 if Pivot /= Back then
876 Partition (Pivot, Back);
882 -- Start of processing for Sort
885 if Container.Length <= 1 then
889 pragma Assert (N (Container.First).Prev = 0);
890 pragma Assert (N (Container.Last).Next = 0);
892 if Container.Busy > 0 then
893 raise Program_Error with
894 "attempt to tamper with cursors (list is busy)";
897 Sort (Front => 0, Back => 0);
899 pragma Assert (N (Container.First).Prev = 0);
900 pragma Assert (N (Container.Last).Next = 0);
909 function Has_Element (Position : Cursor) return Boolean is
911 pragma Assert (Vet (Position), "bad cursor in Has_Element");
912 return Position.Node /= 0;
920 (Container : in out List;
922 New_Item : Element_Type;
923 Position : out Cursor;
924 Count : Count_Type := 1)
926 New_Node : Count_Type;
929 if Before.Container /= null then
930 if Before.Container /= Container'Unrestricted_Access then
931 raise Program_Error with
932 "Before cursor designates wrong list";
935 pragma Assert (Vet (Before), "bad cursor in Insert");
943 if Container.Length > Container.Capacity - Count then
944 raise Constraint_Error with "new length exceeds capacity";
947 if Container.Busy > 0 then
948 raise Program_Error with
949 "attempt to tamper with cursors (list is busy)";
952 Allocate (Container, New_Item, New_Node);
953 Insert_Internal (Container, Before.Node, New_Node => New_Node);
954 Position := Cursor'(Container'Unchecked_Access, Node => New_Node);
956 for Index in Count_Type'(2) .. Count loop
957 Allocate (Container, New_Item, New_Node => New_Node);
958 Insert_Internal (Container, Before.Node, New_Node => New_Node);
963 (Container : in out List;
965 New_Item : Element_Type;
966 Count : Count_Type := 1)
969 pragma Unreferenced (Position);
971 Insert (Container, Before, New_Item, Position, Count);
975 (Container : in out List;
977 Position : out Cursor;
978 Count : Count_Type := 1)
980 New_Node : Count_Type;
983 if Before.Container /= null then
984 if Before.Container /= Container'Unrestricted_Access then
985 raise Program_Error with
986 "Before cursor designates wrong list";
989 pragma Assert (Vet (Before), "bad cursor in Insert");
997 if Container.Length > Container.Capacity - Count then
998 raise Constraint_Error with "new length exceeds capacity";
1001 if Container.Busy > 0 then
1002 raise Program_Error with
1003 "attempt to tamper with cursors (list is busy)";
1006 Allocate (Container, New_Node => New_Node);
1007 Insert_Internal (Container, Before.Node, New_Node);
1008 Position := Cursor'(Container'Unchecked_Access, New_Node);
1010 for Index in Count_Type'(2) .. Count loop
1011 Allocate (Container, New_Node => New_Node);
1012 Insert_Internal (Container, Before.Node, New_Node);
1016 ---------------------
1017 -- Insert_Internal --
1018 ---------------------
1020 procedure Insert_Internal
1021 (Container : in out List;
1022 Before : Count_Type;
1023 New_Node : Count_Type)
1025 N : Node_Array renames Container.Nodes;
1028 if Container.Length = 0 then
1029 pragma Assert (Before = 0);
1030 pragma Assert (Container.First = 0);
1031 pragma Assert (Container.Last = 0);
1033 Container.First := New_Node;
1034 N (Container.First).Prev := 0;
1036 Container.Last := New_Node;
1037 N (Container.Last).Next := 0;
1039 -- Before = zero means append
1041 elsif Before = 0 then
1042 pragma Assert (N (Container.Last).Next = 0);
1044 N (Container.Last).Next := New_Node;
1045 N (New_Node).Prev := Container.Last;
1047 Container.Last := New_Node;
1048 N (Container.Last).Next := 0;
1050 -- Before = Container.First means prepend
1052 elsif Before = Container.First then
1053 pragma Assert (N (Container.First).Prev = 0);
1055 N (Container.First).Prev := New_Node;
1056 N (New_Node).Next := Container.First;
1058 Container.First := New_Node;
1059 N (Container.First).Prev := 0;
1062 pragma Assert (N (Container.First).Prev = 0);
1063 pragma Assert (N (Container.Last).Next = 0);
1065 N (New_Node).Next := Before;
1066 N (New_Node).Prev := N (Before).Prev;
1068 N (N (Before).Prev).Next := New_Node;
1069 N (Before).Prev := New_Node;
1072 Container.Length := Container.Length + 1;
1073 end Insert_Internal;
1079 function Is_Empty (Container : List) return Boolean is
1081 return Container.Length = 0;
1090 Process : not null access procedure (Position : Cursor))
1092 B : Natural renames Container'Unrestricted_Access.all.Busy;
1093 Node : Count_Type := Container.First;
1099 while Node /= 0 loop
1100 Process (Cursor'(Container'Unrestricted_Access, Node));
1101 Node := Container.Nodes (Node).Next;
1115 return List_Iterator_Interfaces.Reversible_Iterator'Class
1117 B : Natural renames Container'Unrestricted_Access.all.Busy;
1120 -- The value of the Node component influences the behavior of the First
1121 -- and Last selector functions of the iterator object. When the Node
1122 -- component is 0 (as is the case here), this means the iterator
1123 -- object was constructed without a start expression. This is a
1124 -- complete iterator, meaning that the iteration starts from the
1125 -- (logical) beginning of the sequence of items.
1127 -- Note: For a forward iterator, Container.First is the beginning, and
1128 -- for a reverse iterator, Container.Last is the beginning.
1130 return It : constant Iterator :=
1131 Iterator'(Limited_Controlled with
1132 Container => Container'Unrestricted_Access,
1142 return List_Iterator_Interfaces.Reversible_Iterator'class
1144 B : Natural renames Container'Unrestricted_Access.all.Busy;
1147 -- It was formerly the case that when Start = No_Element, the partial
1148 -- iterator was defined to behave the same as for a complete iterator,
1149 -- and iterate over the entire sequence of items. However, those
1150 -- semantics were unintuitive and arguably error-prone (it is too easy
1151 -- to accidentally create an endless loop), and so they were changed,
1152 -- per the ARG meeting in Denver on 2011/11. However, there was no
1153 -- consensus about what positive meaning this corner case should have,
1154 -- and so it was decided to simply raise an exception. This does imply,
1155 -- however, that it is not possible to use a partial iterator to specify
1156 -- an empty sequence of items.
1158 if Start = No_Element then
1159 raise Constraint_Error with
1160 "Start position for iterator equals No_Element";
1163 if Start.Container /= Container'Unrestricted_Access then
1164 raise Program_Error with
1165 "Start cursor of Iterate designates wrong list";
1168 pragma Assert (Vet (Start), "Start cursor of Iterate is bad");
1170 -- The value of the Node component influences the behavior of the First
1171 -- and Last selector functions of the iterator object. When the Node
1172 -- component is positive (as is the case here), it means that this
1173 -- is a partial iteration, over a subset of the complete sequence of
1174 -- items. The iterator object was constructed with a start expression,
1175 -- indicating the position from which the iteration begins. Note that
1176 -- the start position has the same value irrespective of whether this
1177 -- is a forward or reverse iteration.
1179 return It : constant Iterator :=
1180 Iterator'(Limited_Controlled with
1181 Container => Container'Unrestricted_Access,
1192 function Last (Container : List) return Cursor is
1194 if Container.Last = 0 then
1198 return Cursor'(Container'Unrestricted_Access, Container.Last);
1201 function Last (Object : Iterator) return Cursor is
1203 -- The value of the iterator object's Node component influences the
1204 -- behavior of the Last (and First) selector function.
1206 -- When the Node component is 0, this means the iterator object was
1207 -- constructed without a start expression, in which case the (reverse)
1208 -- iteration starts from the (logical) beginning of the entire sequence
1209 -- (corresponding to Container.Last, for a reverse iterator).
1211 -- Otherwise, this is iteration over a partial sequence of items. When
1212 -- the Node component is positive, the iterator object was constructed
1213 -- with a start expression, that specifies the position from which the
1214 -- (reverse) partial iteration begins.
1216 if Object.Node = 0 then
1217 return Bounded_Doubly_Linked_Lists.Last (Object.Container.all);
1219 return Cursor'(Object.Container, Object.Node);
1227 function Last_Element (Container : List) return Element_Type is
1229 if Container.Last = 0 then
1230 raise Constraint_Error with "list is empty";
1233 return Container.Nodes (Container.Last).Element;
1240 function Length (Container : List) return Count_Type is
1242 return Container.Length;
1250 (Target : in out List;
1251 Source : in out List)
1253 N : Node_Array renames Source.Nodes;
1257 if Target'Address = Source'Address then
1261 if Target.Capacity < Source.Length then
1262 raise Capacity_Error with "Source length exceeds Target capacity";
1265 if Source.Busy > 0 then
1266 raise Program_Error with
1267 "attempt to tamper with cursors of Source (list is busy)";
1270 -- Clear target, note that this checks busy bits of Target
1274 while Source.Length > 1 loop
1275 pragma Assert (Source.First in 1 .. Source.Capacity);
1276 pragma Assert (Source.Last /= Source.First);
1277 pragma Assert (N (Source.First).Prev = 0);
1278 pragma Assert (N (Source.Last).Next = 0);
1280 -- Copy first element from Source to Target
1283 Append (Target, N (X).Element);
1285 -- Unlink first node of Source
1287 Source.First := N (X).Next;
1288 N (Source.First).Prev := 0;
1290 Source.Length := Source.Length - 1;
1292 -- The representation invariants for Source have been restored. It is
1293 -- now safe to free the unlinked node, without fear of corrupting the
1294 -- active links of Source.
1296 -- Note that the algorithm we use here models similar algorithms used
1297 -- in the unbounded form of the doubly-linked list container. In that
1298 -- case, Free is an instantation of Unchecked_Deallocation, which can
1299 -- fail (because PE will be raised if controlled Finalize fails), so
1300 -- we must defer the call until the last step. Here in the bounded
1301 -- form, Free merely links the node we have just "deallocated" onto a
1302 -- list of inactive nodes, so technically Free cannot fail. However,
1303 -- for consistency, we handle Free the same way here as we do for the
1304 -- unbounded form, with the pessimistic assumption that it can fail.
1309 if Source.Length = 1 then
1310 pragma Assert (Source.First in 1 .. Source.Capacity);
1311 pragma Assert (Source.Last = Source.First);
1312 pragma Assert (N (Source.First).Prev = 0);
1313 pragma Assert (N (Source.Last).Next = 0);
1315 -- Copy element from Source to Target
1318 Append (Target, N (X).Element);
1320 -- Unlink node of Source
1326 -- Return the unlinked node to the free store
1336 procedure Next (Position : in out Cursor) is
1338 Position := Next (Position);
1341 function Next (Position : Cursor) return Cursor is
1343 if Position.Node = 0 then
1347 pragma Assert (Vet (Position), "bad cursor in Next");
1350 Nodes : Node_Array renames Position.Container.Nodes;
1351 Node : constant Count_Type := Nodes (Position.Node).Next;
1358 return Cursor'(Position.Container, Node);
1364 Position : Cursor) return Cursor
1367 if Position.Container = null then
1371 if Position.Container /= Object.Container then
1372 raise Program_Error with
1373 "Position cursor of Next designates wrong list";
1376 return Next (Position);
1384 (Container : in out List;
1385 New_Item : Element_Type;
1386 Count : Count_Type := 1)
1389 Insert (Container, First (Container), New_Item, Count);
1396 procedure Previous (Position : in out Cursor) is
1398 Position := Previous (Position);
1401 function Previous (Position : Cursor) return Cursor is
1403 if Position.Node = 0 then
1407 pragma Assert (Vet (Position), "bad cursor in Previous");
1410 Nodes : Node_Array renames Position.Container.Nodes;
1411 Node : constant Count_Type := Nodes (Position.Node).Prev;
1417 return Cursor'(Position.Container, Node);
1423 Position : Cursor) return Cursor
1426 if Position.Container = null then
1430 if Position.Container /= Object.Container then
1431 raise Program_Error with
1432 "Position cursor of Previous designates wrong list";
1435 return Previous (Position);
1442 procedure Query_Element
1444 Process : not null access procedure (Element : Element_Type))
1447 if Position.Node = 0 then
1448 raise Constraint_Error with
1449 "Position cursor has no element";
1452 pragma Assert (Vet (Position), "bad cursor in Query_Element");
1455 C : List renames Position.Container.all'Unrestricted_Access.all;
1456 B : Natural renames C.Busy;
1457 L : Natural renames C.Lock;
1464 N : Node_Type renames C.Nodes (Position.Node);
1466 Process (N.Element);
1484 (Stream : not null access Root_Stream_Type'Class;
1487 N : Count_Type'Base;
1492 Count_Type'Base'Read (Stream, N);
1495 raise Program_Error with "bad list length (corrupt stream)";
1502 if N > Item.Capacity then
1503 raise Constraint_Error with "length exceeds capacity";
1506 for Idx in 1 .. N loop
1507 Allocate (Item, Stream, New_Node => X);
1508 Insert_Internal (Item, Before => 0, New_Node => X);
1513 (Stream : not null access Root_Stream_Type'Class;
1517 raise Program_Error with "attempt to stream list cursor";
1521 (Stream : not null access Root_Stream_Type'Class;
1522 Item : out Reference_Type)
1525 raise Program_Error with "attempt to stream reference";
1529 (Stream : not null access Root_Stream_Type'Class;
1530 Item : out Constant_Reference_Type)
1533 raise Program_Error with "attempt to stream reference";
1540 function Constant_Reference (Container : List; Position : Cursor)
1541 return Constant_Reference_Type is
1543 pragma Unreferenced (Container);
1545 if Position.Container = null then
1546 raise Constraint_Error with "Position cursor has no element";
1550 Position.Container.Nodes (Position.Node).Element'Unrestricted_Access);
1551 end Constant_Reference;
1553 function Reference (Container : List; Position : Cursor)
1554 return Reference_Type is
1556 pragma Unreferenced (Container);
1558 if Position.Container = null then
1559 raise Constraint_Error with "Position cursor has no element";
1563 Position.Container.Nodes (Position.Node).Element'Unrestricted_Access);
1566 ---------------------
1567 -- Replace_Element --
1568 ---------------------
1570 procedure Replace_Element
1571 (Container : in out List;
1573 New_Item : Element_Type)
1576 if Position.Container = null then
1577 raise Constraint_Error with "Position cursor has no element";
1580 if Position.Container /= Container'Unchecked_Access then
1581 raise Program_Error with
1582 "Position cursor designates wrong container";
1585 if Container.Lock > 0 then
1586 raise Program_Error with
1587 "attempt to tamper with elements (list is locked)";
1590 pragma Assert (Vet (Position), "bad cursor in Replace_Element");
1592 Container.Nodes (Position.Node).Element := New_Item;
1593 end Replace_Element;
1595 ----------------------
1596 -- Reverse_Elements --
1597 ----------------------
1599 procedure Reverse_Elements (Container : in out List) is
1600 N : Node_Array renames Container.Nodes;
1601 I : Count_Type := Container.First;
1602 J : Count_Type := Container.Last;
1604 procedure Swap (L, R : Count_Type);
1610 procedure Swap (L, R : Count_Type) is
1611 LN : constant Count_Type := N (L).Next;
1612 LP : constant Count_Type := N (L).Prev;
1614 RN : constant Count_Type := N (R).Next;
1615 RP : constant Count_Type := N (R).Prev;
1630 pragma Assert (RP = L);
1644 -- Start of processing for Reverse_Elements
1647 if Container.Length <= 1 then
1651 pragma Assert (N (Container.First).Prev = 0);
1652 pragma Assert (N (Container.Last).Next = 0);
1654 if Container.Busy > 0 then
1655 raise Program_Error with
1656 "attempt to tamper with cursors (list is busy)";
1659 Container.First := J;
1660 Container.Last := I;
1662 Swap (L => I, R => J);
1670 Swap (L => J, R => I);
1679 pragma Assert (N (Container.First).Prev = 0);
1680 pragma Assert (N (Container.Last).Next = 0);
1681 end Reverse_Elements;
1687 function Reverse_Find
1689 Item : Element_Type;
1690 Position : Cursor := No_Element) return Cursor
1692 Node : Count_Type := Position.Node;
1696 Node := Container.Last;
1699 if Position.Container /= Container'Unrestricted_Access then
1700 raise Program_Error with
1701 "Position cursor designates wrong container";
1704 pragma Assert (Vet (Position), "bad cursor in Reverse_Find");
1707 while Node /= 0 loop
1708 if Container.Nodes (Node).Element = Item then
1709 return Cursor'(Container'Unrestricted_Access, Node);
1712 Node := Container.Nodes (Node).Prev;
1718 ---------------------
1719 -- Reverse_Iterate --
1720 ---------------------
1722 procedure Reverse_Iterate
1724 Process : not null access procedure (Position : Cursor))
1726 C : List renames Container'Unrestricted_Access.all;
1727 B : Natural renames C.Busy;
1729 Node : Count_Type := Container.Last;
1735 while Node /= 0 loop
1736 Process (Cursor'(Container'Unrestricted_Access, Node));
1737 Node := Container.Nodes (Node).Prev;
1747 end Reverse_Iterate;
1754 (Target : in out List;
1756 Source : in out List)
1759 if Before.Container /= null then
1760 if Before.Container /= Target'Unrestricted_Access then
1761 raise Program_Error with
1762 "Before cursor designates wrong container";
1765 pragma Assert (Vet (Before), "bad cursor in Splice");
1768 if Target'Address = Source'Address
1769 or else Source.Length = 0
1774 pragma Assert (Source.Nodes (Source.First).Prev = 0);
1775 pragma Assert (Source.Nodes (Source.Last).Next = 0);
1777 if Target.Length > Count_Type'Last - Source.Length then
1778 raise Constraint_Error with "new length exceeds maximum";
1781 if Target.Length + Source.Length > Target.Capacity then
1782 raise Capacity_Error with "new length exceeds target capacity";
1785 if Target.Busy > 0 then
1786 raise Program_Error with
1787 "attempt to tamper with cursors of Target (list is busy)";
1790 if Source.Busy > 0 then
1791 raise Program_Error with
1792 "attempt to tamper with cursors of Source (list is busy)";
1795 while not Is_Empty (Source) loop
1796 Insert (Target, Before, Source.Nodes (Source.First).Element);
1797 Delete_First (Source);
1802 (Container : in out List;
1806 N : Node_Array renames Container.Nodes;
1809 if Before.Container /= null then
1810 if Before.Container /= Container'Unchecked_Access then
1811 raise Program_Error with
1812 "Before cursor designates wrong container";
1815 pragma Assert (Vet (Before), "bad Before cursor in Splice");
1818 if Position.Node = 0 then
1819 raise Constraint_Error with "Position cursor has no element";
1822 if Position.Container /= Container'Unrestricted_Access then
1823 raise Program_Error with
1824 "Position cursor designates wrong container";
1827 pragma Assert (Vet (Position), "bad Position cursor in Splice");
1829 if Position.Node = Before.Node
1830 or else N (Position.Node).Next = Before.Node
1835 pragma Assert (Container.Length >= 2);
1837 if Container.Busy > 0 then
1838 raise Program_Error with
1839 "attempt to tamper with cursors (list is busy)";
1842 if Before.Node = 0 then
1843 pragma Assert (Position.Node /= Container.Last);
1845 if Position.Node = Container.First then
1846 Container.First := N (Position.Node).Next;
1847 N (Container.First).Prev := 0;
1849 N (N (Position.Node).Prev).Next := N (Position.Node).Next;
1850 N (N (Position.Node).Next).Prev := N (Position.Node).Prev;
1853 N (Container.Last).Next := Position.Node;
1854 N (Position.Node).Prev := Container.Last;
1856 Container.Last := Position.Node;
1857 N (Container.Last).Next := 0;
1862 if Before.Node = Container.First then
1863 pragma Assert (Position.Node /= Container.First);
1865 if Position.Node = Container.Last then
1866 Container.Last := N (Position.Node).Prev;
1867 N (Container.Last).Next := 0;
1869 N (N (Position.Node).Prev).Next := N (Position.Node).Next;
1870 N (N (Position.Node).Next).Prev := N (Position.Node).Prev;
1873 N (Container.First).Prev := Position.Node;
1874 N (Position.Node).Next := Container.First;
1876 Container.First := Position.Node;
1877 N (Container.First).Prev := 0;
1882 if Position.Node = Container.First then
1883 Container.First := N (Position.Node).Next;
1884 N (Container.First).Prev := 0;
1886 elsif Position.Node = Container.Last then
1887 Container.Last := N (Position.Node).Prev;
1888 N (Container.Last).Next := 0;
1891 N (N (Position.Node).Prev).Next := N (Position.Node).Next;
1892 N (N (Position.Node).Next).Prev := N (Position.Node).Prev;
1895 N (N (Before.Node).Prev).Next := Position.Node;
1896 N (Position.Node).Prev := N (Before.Node).Prev;
1898 N (Before.Node).Prev := Position.Node;
1899 N (Position.Node).Next := Before.Node;
1901 pragma Assert (N (Container.First).Prev = 0);
1902 pragma Assert (N (Container.Last).Next = 0);
1906 (Target : in out List;
1908 Source : in out List;
1909 Position : in out Cursor)
1911 Target_Position : Cursor;
1914 if Target'Address = Source'Address then
1915 Splice (Target, Before, Position);
1919 if Before.Container /= null then
1920 if Before.Container /= Target'Unrestricted_Access then
1921 raise Program_Error with
1922 "Before cursor designates wrong container";
1925 pragma Assert (Vet (Before), "bad Before cursor in Splice");
1928 if Position.Node = 0 then
1929 raise Constraint_Error with "Position cursor has no element";
1932 if Position.Container /= Source'Unrestricted_Access then
1933 raise Program_Error with
1934 "Position cursor designates wrong container";
1937 pragma Assert (Vet (Position), "bad Position cursor in Splice");
1939 if Target.Length >= Target.Capacity then
1940 raise Capacity_Error with "Target is full";
1943 if Target.Busy > 0 then
1944 raise Program_Error with
1945 "attempt to tamper with cursors of Target (list is busy)";
1948 if Source.Busy > 0 then
1949 raise Program_Error with
1950 "attempt to tamper with cursors of Source (list is busy)";
1954 (Container => Target,
1956 New_Item => Source.Nodes (Position.Node).Element,
1957 Position => Target_Position);
1959 Delete (Source, Position);
1960 Position := Target_Position;
1968 (Container : in out List;
1973 raise Constraint_Error with "I cursor has no element";
1977 raise Constraint_Error with "J cursor has no element";
1980 if I.Container /= Container'Unchecked_Access then
1981 raise Program_Error with "I cursor designates wrong container";
1984 if J.Container /= Container'Unchecked_Access then
1985 raise Program_Error with "J cursor designates wrong container";
1988 if I.Node = J.Node then
1992 if Container.Lock > 0 then
1993 raise Program_Error with
1994 "attempt to tamper with elements (list is locked)";
1997 pragma Assert (Vet (I), "bad I cursor in Swap");
1998 pragma Assert (Vet (J), "bad J cursor in Swap");
2001 EI : Element_Type renames Container.Nodes (I.Node).Element;
2002 EJ : Element_Type renames Container.Nodes (J.Node).Element;
2004 EI_Copy : constant Element_Type := EI;
2016 procedure Swap_Links
2017 (Container : in out List;
2022 raise Constraint_Error with "I cursor has no element";
2026 raise Constraint_Error with "J cursor has no element";
2029 if I.Container /= Container'Unrestricted_Access then
2030 raise Program_Error with "I cursor designates wrong container";
2033 if J.Container /= Container'Unrestricted_Access then
2034 raise Program_Error with "J cursor designates wrong container";
2037 if I.Node = J.Node then
2041 if Container.Busy > 0 then
2042 raise Program_Error with
2043 "attempt to tamper with cursors (list is busy)";
2046 pragma Assert (Vet (I), "bad I cursor in Swap_Links");
2047 pragma Assert (Vet (J), "bad J cursor in Swap_Links");
2050 I_Next : constant Cursor := Next (I);
2054 Splice (Container, Before => I, Position => J);
2058 J_Next : constant Cursor := Next (J);
2062 Splice (Container, Before => J, Position => I);
2065 pragma Assert (Container.Length >= 3);
2067 Splice (Container, Before => I_Next, Position => J);
2068 Splice (Container, Before => J_Next, Position => I);
2075 --------------------
2076 -- Update_Element --
2077 --------------------
2079 procedure Update_Element
2080 (Container : in out List;
2082 Process : not null access procedure (Element : in out Element_Type))
2085 if Position.Node = 0 then
2086 raise Constraint_Error with "Position cursor has no element";
2089 if Position.Container /= Container'Unchecked_Access then
2090 raise Program_Error with
2091 "Position cursor designates wrong container";
2094 pragma Assert (Vet (Position), "bad cursor in Update_Element");
2097 B : Natural renames Container.Busy;
2098 L : Natural renames Container.Lock;
2105 N : Node_Type renames Container.Nodes (Position.Node);
2107 Process (N.Element);
2124 function Vet (Position : Cursor) return Boolean is
2126 if Position.Node = 0 then
2127 return Position.Container = null;
2130 if Position.Container = null then
2135 L : List renames Position.Container.all;
2136 N : Node_Array renames L.Nodes;
2139 if L.Length = 0 then
2143 if L.First = 0 or L.First > L.Capacity then
2147 if L.Last = 0 or L.Last > L.Capacity then
2151 if N (L.First).Prev /= 0 then
2155 if N (L.Last).Next /= 0 then
2159 if Position.Node > L.Capacity then
2163 if N (Position.Node).Prev < 0 then -- see Free
2167 if N (Position.Node).Prev > L.Capacity then
2171 if N (Position.Node).Next = Position.Node then
2175 if N (Position.Node).Prev = Position.Node then
2179 if N (Position.Node).Prev = 0
2180 and then Position.Node /= L.First
2185 -- If we get here, we know that this disjunction is true:
2186 -- N (Position.Node).Prev /= 0 or else Position.Node = L.First
2187 -- Why not do this with an assertion???
2189 if N (Position.Node).Next = 0
2190 and then Position.Node /= L.Last
2195 -- If we get here, we know that this disjunction is true:
2196 -- N (Position.Node).Next /= 0 or else Position.Node = L.Last
2197 -- Why not do this with an assertion???
2199 if L.Length = 1 then
2200 return L.First = L.Last;
2203 if L.First = L.Last then
2207 if N (L.First).Next = 0 then
2211 if N (L.Last).Prev = 0 then
2215 if N (N (L.First).Next).Prev /= L.First then
2219 if N (N (L.Last).Prev).Next /= L.Last then
2223 if L.Length = 2 then
2224 if N (L.First).Next /= L.Last then
2228 if N (L.Last).Prev /= L.First then
2235 if N (L.First).Next = L.Last then
2239 if N (L.Last).Prev = L.First then
2243 -- Eliminate earlier disjunct
2245 if Position.Node = L.First then
2249 -- If we get to this point, we know that this predicate is true:
2250 -- N (Position.Node).Prev /= 0
2252 if Position.Node = L.Last then -- eliminates earlier disjunct
2256 -- If we get to this point, we know that this predicate is true:
2257 -- N (Position.Node).Next /= 0
2259 if N (N (Position.Node).Next).Prev /= Position.Node then
2263 if N (N (Position.Node).Prev).Next /= Position.Node then
2267 if L.Length = 3 then
2268 if N (L.First).Next /= Position.Node then
2272 if N (L.Last).Prev /= Position.Node then
2286 (Stream : not null access Root_Stream_Type'Class;
2292 Count_Type'Base'Write (Stream, Item.Length);
2295 while Node /= 0 loop
2296 Element_Type'Write (Stream, Item.Nodes (Node).Element);
2297 Node := Item.Nodes (Node).Next;
2302 (Stream : not null access Root_Stream_Type'Class;
2306 raise Program_Error with "attempt to stream list cursor";
2310 (Stream : not null access Root_Stream_Type'Class;
2311 Item : Reference_Type)
2314 raise Program_Error with "attempt to stream reference";
2318 (Stream : not null access Root_Stream_Type'Class;
2319 Item : Constant_Reference_Type)
2322 raise Program_Error with "attempt to stream reference";
2325 end Ada.Containers.Bounded_Doubly_Linked_Lists;