1 ------------------------------------------------------------------------------
3 -- GNAT LIBRARY COMPONENTS --
5 -- ADA.CONTAINERS.BOUNDED_DOUBLY_LINKED_LISTS --
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.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;
84 -- Checks invariants of the cursor and its designated container, as a
85 -- simple way of detecting dangling references (see operation Free for a
86 -- description of the detection mechanism), returning True if all checks
87 -- pass. Invocations of Vet are used here as the argument of pragma Assert,
88 -- so the checks are performed only when assertions are enabled.
94 function "=" (Left, Right : List) return Boolean is
95 LN : Node_Array renames Left.Nodes;
96 RN : Node_Array renames Right.Nodes;
101 if Left'Address = Right'Address then
105 if Left.Length /= Right.Length then
111 for J in 1 .. Left.Length loop
112 if LN (LI).Element /= RN (RI).Element then
128 (Container : in out List;
129 New_Item : Element_Type;
130 New_Node : out Count_Type)
132 N : Node_Array renames Container.Nodes;
135 if Container.Free >= 0 then
136 New_Node := Container.Free;
138 -- We always perform the assignment first, before we change container
139 -- state, in order to defend against exceptions duration assignment.
141 N (New_Node).Element := New_Item;
142 Container.Free := N (New_Node).Next;
145 -- A negative free store value means that the links of the nodes in
146 -- the free store have not been initialized. In this case, the nodes
147 -- are physically contiguous in the array, starting at the index that
148 -- is the absolute value of the Container.Free, and continuing until
149 -- the end of the array (Nodes'Last).
151 New_Node := abs Container.Free;
153 -- As above, we perform this assignment first, before modifying any
156 N (New_Node).Element := New_Item;
157 Container.Free := Container.Free - 1;
162 (Container : in out List;
163 Stream : not null access Root_Stream_Type'Class;
164 New_Node : out Count_Type)
166 N : Node_Array renames Container.Nodes;
169 if Container.Free >= 0 then
170 New_Node := Container.Free;
172 -- We always perform the assignment first, before we change container
173 -- state, in order to defend against exceptions duration assignment.
175 Element_Type'Read (Stream, N (New_Node).Element);
176 Container.Free := N (New_Node).Next;
179 -- A negative free store value means that the links of the nodes in
180 -- the free store have not been initialized. In this case, the nodes
181 -- are physically contiguous in the array, starting at the index that
182 -- is the absolute value of the Container.Free, and continuing until
183 -- the end of the array (Nodes'Last).
185 New_Node := abs Container.Free;
187 -- As above, we perform this assignment first, before modifying any
190 Element_Type'Read (Stream, N (New_Node).Element);
191 Container.Free := Container.Free - 1;
196 (Container : in out List;
197 New_Node : out Count_Type)
199 N : Node_Array renames Container.Nodes;
202 if Container.Free >= 0 then
203 New_Node := Container.Free;
204 Container.Free := N (New_Node).Next;
207 -- As explained above, a negative free store value means that the
208 -- links for the nodes in the free store have not been initialized.
210 New_Node := abs Container.Free;
211 Container.Free := Container.Free - 1;
220 (Container : in out List;
221 New_Item : Element_Type;
222 Count : Count_Type := 1)
225 Insert (Container, No_Element, New_Item, Count);
232 procedure Assign (Target : in out List; Source : List) is
233 SN : Node_Array renames Source.Nodes;
237 if Target'Address = Source'Address then
241 if Target.Capacity < Source.Length then
242 raise Capacity_Error -- ???
243 with "Target capacity is less than Source length";
250 Target.Append (SN (J).Element);
259 procedure Clear (Container : in out List) is
260 N : Node_Array renames Container.Nodes;
264 if Container.Length = 0 then
265 pragma Assert (Container.First = 0);
266 pragma Assert (Container.Last = 0);
267 pragma Assert (Container.Busy = 0);
268 pragma Assert (Container.Lock = 0);
272 pragma Assert (Container.First >= 1);
273 pragma Assert (Container.Last >= 1);
274 pragma Assert (N (Container.First).Prev = 0);
275 pragma Assert (N (Container.Last).Next = 0);
277 if Container.Busy > 0 then
278 raise Program_Error with
279 "attempt to tamper with cursors (list is busy)";
282 while Container.Length > 1 loop
283 X := Container.First;
284 pragma Assert (N (N (X).Next).Prev = Container.First);
286 Container.First := N (X).Next;
287 N (Container.First).Prev := 0;
289 Container.Length := Container.Length - 1;
294 X := Container.First;
295 pragma Assert (X = Container.Last);
297 Container.First := 0;
299 Container.Length := 0;
304 ------------------------
305 -- Constant_Reference --
306 ------------------------
308 function Constant_Reference
309 (Container : aliased List;
310 Position : Cursor) return Constant_Reference_Type
313 if Position.Container = null then
314 raise Constraint_Error with "Position cursor has no element";
317 if Position.Container /= Container'Unrestricted_Access then
318 raise Program_Error with
319 "Position cursor designates wrong container";
322 pragma Assert (Vet (Position), "bad cursor in Constant_Reference");
325 N : Node_Type renames Container.Nodes (Position.Node);
327 return (Element => N.Element'Access);
329 end Constant_Reference;
337 Item : Element_Type) return Boolean
340 return Find (Container, Item) /= No_Element;
347 function Copy (Source : List; Capacity : Count_Type := 0) return List is
354 elsif Capacity >= Source.Length then
358 raise Capacity_Error with "Capacity value too small";
361 return Target : List (Capacity => C) do
362 Assign (Target => Target, Source => Source);
371 (Container : in out List;
372 Position : in out Cursor;
373 Count : Count_Type := 1)
375 N : Node_Array renames Container.Nodes;
379 if Position.Node = 0 then
380 raise Constraint_Error with
381 "Position cursor has no element";
384 if Position.Container /= Container'Unrestricted_Access then
385 raise Program_Error with
386 "Position cursor designates wrong container";
389 pragma Assert (Vet (Position), "bad cursor in Delete");
390 pragma Assert (Container.First >= 1);
391 pragma Assert (Container.Last >= 1);
392 pragma Assert (N (Container.First).Prev = 0);
393 pragma Assert (N (Container.Last).Next = 0);
395 if Position.Node = Container.First then
396 Delete_First (Container, Count);
397 Position := No_Element;
402 Position := No_Element;
406 if Container.Busy > 0 then
407 raise Program_Error with
408 "attempt to tamper with cursors (list is busy)";
411 for Index in 1 .. Count loop
412 pragma Assert (Container.Length >= 2);
415 Container.Length := Container.Length - 1;
417 if X = Container.Last then
418 Position := No_Element;
420 Container.Last := N (X).Prev;
421 N (Container.Last).Next := 0;
427 Position.Node := N (X).Next;
429 N (N (X).Next).Prev := N (X).Prev;
430 N (N (X).Prev).Next := N (X).Next;
435 Position := No_Element;
442 procedure Delete_First
443 (Container : in out List;
444 Count : Count_Type := 1)
446 N : Node_Array renames Container.Nodes;
450 if Count >= Container.Length then
459 if Container.Busy > 0 then
460 raise Program_Error with
461 "attempt to tamper with cursors (list is busy)";
464 for I in 1 .. Count loop
465 X := Container.First;
466 pragma Assert (N (N (X).Next).Prev = Container.First);
468 Container.First := N (X).Next;
469 N (Container.First).Prev := 0;
471 Container.Length := Container.Length - 1;
481 procedure Delete_Last
482 (Container : in out List;
483 Count : Count_Type := 1)
485 N : Node_Array renames Container.Nodes;
489 if Count >= Container.Length then
498 if Container.Busy > 0 then
499 raise Program_Error with
500 "attempt to tamper with cursors (list is busy)";
503 for I in 1 .. Count loop
505 pragma Assert (N (N (X).Prev).Next = Container.Last);
507 Container.Last := N (X).Prev;
508 N (Container.Last).Next := 0;
510 Container.Length := Container.Length - 1;
520 function Element (Position : Cursor) return Element_Type is
522 if Position.Node = 0 then
523 raise Constraint_Error with
524 "Position cursor has no element";
527 pragma Assert (Vet (Position), "bad cursor in Element");
529 return Position.Container.Nodes (Position.Node).Element;
536 procedure Finalize (Object : in out Iterator) is
538 if Object.Container /= null then
540 B : Natural renames Object.Container.all.Busy;
555 Position : Cursor := No_Element) return Cursor
557 Nodes : Node_Array renames Container.Nodes;
558 Node : Count_Type := Position.Node;
562 Node := Container.First;
565 if Position.Container /= Container'Unrestricted_Access then
566 raise Program_Error with
567 "Position cursor designates wrong container";
570 pragma Assert (Vet (Position), "bad cursor in Find");
574 if Nodes (Node).Element = Item then
575 return Cursor'(Container'Unrestricted_Access, Node);
578 Node := Nodes (Node).Next;
588 function First (Container : List) return Cursor is
590 if Container.First = 0 then
594 return Cursor'(Container'Unrestricted_Access, Container.First);
597 function First (Object : Iterator) return Cursor is
599 -- The value of the iterator object's Node component influences the
600 -- behavior of the First (and Last) selector function.
602 -- When the Node component is 0, this means the iterator object was
603 -- constructed without a start expression, in which case the (forward)
604 -- iteration starts from the (logical) beginning of the entire sequence
605 -- of items (corresponding to Container.First, for a forward iterator).
607 -- Otherwise, this is iteration over a partial sequence of items. When
608 -- the Node component is positive, the iterator object was constructed
609 -- with a start expression, that specifies the position from which the
610 -- (forward) partial iteration begins.
612 if Object.Node = 0 then
613 return Bounded_Doubly_Linked_Lists.First (Object.Container.all);
615 return Cursor'(Object.Container, Object.Node);
623 function First_Element (Container : List) return Element_Type is
625 if Container.First = 0 then
626 raise Constraint_Error with "list is empty";
629 return Container.Nodes (Container.First).Element;
637 (Container : in out List;
640 pragma Assert (X > 0);
641 pragma Assert (X <= Container.Capacity);
643 N : Node_Array renames Container.Nodes;
644 pragma Assert (N (X).Prev >= 0); -- node is active
647 -- The list container actually contains two lists: one for the "active"
648 -- nodes that contain elements that have been inserted onto the list,
649 -- and another for the "inactive" nodes for the free store.
651 -- We desire that merely declaring an object should have only minimal
652 -- cost; specially, we want to avoid having to initialize the free
653 -- store (to fill in the links), especially if the capacity is large.
655 -- The head of the free list is indicated by Container.Free. If its
656 -- value is non-negative, then the free store has been initialized in
657 -- the "normal" way: Container.Free points to the head of the list of
658 -- free (inactive) nodes, and the value 0 means the free list is empty.
659 -- Each node on the free list has been initialized to point to the next
660 -- free node (via its Next component), and the value 0 means that this
661 -- is the last free node.
663 -- If Container.Free is negative, then the links on the free store have
664 -- not been initialized. In this case the link values are implied: the
665 -- free store comprises the components of the node array started with
666 -- the absolute value of Container.Free, and continuing until the end of
667 -- the array (Nodes'Last).
669 -- If the list container is manipulated on one end only (for example if
670 -- the container were being used as a stack), then there is no need to
671 -- initialize the free store, since the inactive nodes are physically
672 -- contiguous (in fact, they lie immediately beyond the logical end
673 -- being manipulated). The only time we need to actually initialize the
674 -- nodes in the free store is if the node that becomes inactive is not
675 -- at the end of the list. The free store would then be discontiguous
676 -- and so its nodes would need to be linked in the traditional way.
679 -- It might be possible to perform an optimization here. Suppose that
680 -- the free store can be represented as having two parts: one comprising
681 -- the non-contiguous inactive nodes linked together in the normal way,
682 -- and the other comprising the contiguous inactive nodes (that are not
683 -- linked together, at the end of the nodes array). This would allow us
684 -- to never have to initialize the free store, except in a lazy way as
685 -- nodes become inactive.
687 -- When an element is deleted from the list container, its node becomes
688 -- inactive, and so we set its Prev component to a negative value, to
689 -- indicate that it is now inactive. This provides a useful way to
690 -- detect a dangling cursor reference (and which is used in Vet).
692 N (X).Prev := -1; -- Node is deallocated (not on active list)
694 if Container.Free >= 0 then
696 -- The free store has previously been initialized. All we need to
697 -- do here is link the newly-free'd node onto the free list.
699 N (X).Next := Container.Free;
702 elsif X + 1 = abs Container.Free then
704 -- The free store has not been initialized, and the node becoming
705 -- inactive immediately precedes the start of the free store. All
706 -- we need to do is move the start of the free store back by one.
708 -- Note: initializing Next to zero is not strictly necessary but
709 -- seems cleaner and marginally safer.
712 Container.Free := Container.Free + 1;
715 -- The free store has not been initialized, and the node becoming
716 -- inactive does not immediately precede the free store. Here we
717 -- first initialize the free store (meaning the links are given
718 -- values in the traditional way), and then link the newly-free'd
719 -- node onto the head of the free store.
722 -- See the comments above for an optimization opportunity. If the
723 -- next link for a node on the free store is negative, then this
724 -- means the remaining nodes on the free store are physically
725 -- contiguous, starting as the absolute value of that index value.
727 Container.Free := abs Container.Free;
729 if Container.Free > Container.Capacity then
733 for I in Container.Free .. Container.Capacity - 1 loop
737 N (Container.Capacity).Next := 0;
740 N (X).Next := Container.Free;
745 ---------------------
746 -- Generic_Sorting --
747 ---------------------
749 package body Generic_Sorting is
755 function Is_Sorted (Container : List) return Boolean is
756 Nodes : Node_Array renames Container.Nodes;
757 Node : Count_Type := Container.First;
760 for J in 2 .. Container.Length loop
761 if Nodes (Nodes (Node).Next).Element < Nodes (Node).Element then
765 Node := Nodes (Node).Next;
776 (Target : in out List;
777 Source : in out List)
779 LN : Node_Array renames Target.Nodes;
780 RN : Node_Array renames Source.Nodes;
785 -- The semantics of Merge changed slightly per AI05-0021. It was
786 -- originally the case that if Target and Source denoted the same
787 -- container object, then the GNAT implementation of Merge did
788 -- nothing. However, it was argued that RM05 did not precisely
789 -- specify the semantics for this corner case. The decision of the
790 -- ARG was that if Target and Source denote the same non-empty
791 -- container object, then Program_Error is raised.
793 if Source.Is_Empty then
797 if Target'Address = Source'Address then
798 raise Program_Error with
799 "Target and Source denote same non-empty container";
802 if Target.Busy > 0 then
803 raise Program_Error with
804 "attempt to tamper with cursors of Target (list is busy)";
807 if Source.Busy > 0 then
808 raise Program_Error with
809 "attempt to tamper with cursors of Source (list is busy)";
812 LI := First (Target);
813 RI := First (Source);
814 while RI.Node /= 0 loop
815 pragma Assert (RN (RI.Node).Next = 0
816 or else not (RN (RN (RI.Node).Next).Element <
817 RN (RI.Node).Element));
820 Splice (Target, No_Element, Source);
824 pragma Assert (LN (LI.Node).Next = 0
825 or else not (LN (LN (LI.Node).Next).Element <
826 LN (LI.Node).Element));
828 if RN (RI.Node).Element < LN (LI.Node).Element then
832 RI.Node := RN (RI.Node).Next;
833 Splice (Target, LI, Source, RJ);
837 LI.Node := LN (LI.Node).Next;
846 procedure Sort (Container : in out List) is
847 N : Node_Array renames Container.Nodes;
849 procedure Partition (Pivot, Back : Count_Type);
850 -- What does this do ???
852 procedure Sort (Front, Back : Count_Type);
853 -- Internal procedure, what does it do??? rename it???
859 procedure Partition (Pivot, Back : Count_Type) is
863 Node := N (Pivot).Next;
864 while Node /= Back loop
865 if N (Node).Element < N (Pivot).Element then
867 Prev : constant Count_Type := N (Node).Prev;
868 Next : constant Count_Type := N (Node).Next;
871 N (Prev).Next := Next;
874 Container.Last := Prev;
876 N (Next).Prev := Prev;
879 N (Node).Next := Pivot;
880 N (Node).Prev := N (Pivot).Prev;
882 N (Pivot).Prev := Node;
884 if N (Node).Prev = 0 then
885 Container.First := Node;
887 N (N (Node).Prev).Next := Node;
894 Node := N (Node).Next;
903 procedure Sort (Front, Back : Count_Type) is
904 Pivot : constant Count_Type :=
905 (if Front = 0 then Container.First else N (Front).Next);
907 if Pivot /= Back then
908 Partition (Pivot, Back);
914 -- Start of processing for Sort
917 if Container.Length <= 1 then
921 pragma Assert (N (Container.First).Prev = 0);
922 pragma Assert (N (Container.Last).Next = 0);
924 if Container.Busy > 0 then
925 raise Program_Error with
926 "attempt to tamper with cursors (list is busy)";
929 Sort (Front => 0, Back => 0);
931 pragma Assert (N (Container.First).Prev = 0);
932 pragma Assert (N (Container.Last).Next = 0);
941 function Has_Element (Position : Cursor) return Boolean is
943 pragma Assert (Vet (Position), "bad cursor in Has_Element");
944 return Position.Node /= 0;
952 (Container : in out List;
954 New_Item : Element_Type;
955 Position : out Cursor;
956 Count : Count_Type := 1)
958 New_Node : Count_Type;
961 if Before.Container /= null then
962 if Before.Container /= Container'Unrestricted_Access then
963 raise Program_Error with
964 "Before cursor designates wrong list";
967 pragma Assert (Vet (Before), "bad cursor in Insert");
975 if Container.Length > Container.Capacity - Count then
976 raise Constraint_Error with "new length exceeds capacity";
979 if Container.Busy > 0 then
980 raise Program_Error with
981 "attempt to tamper with cursors (list is busy)";
984 Allocate (Container, New_Item, New_Node);
985 Insert_Internal (Container, Before.Node, New_Node => New_Node);
986 Position := Cursor'(Container'Unchecked_Access, Node => New_Node);
988 for Index in Count_Type'(2) .. Count loop
989 Allocate (Container, New_Item, New_Node => New_Node);
990 Insert_Internal (Container, Before.Node, New_Node => New_Node);
995 (Container : in out List;
997 New_Item : Element_Type;
998 Count : Count_Type := 1)
1001 pragma Unreferenced (Position);
1003 Insert (Container, Before, New_Item, Position, Count);
1007 (Container : in out List;
1009 Position : out Cursor;
1010 Count : Count_Type := 1)
1012 New_Node : Count_Type;
1015 if Before.Container /= null then
1016 if Before.Container /= Container'Unrestricted_Access then
1017 raise Program_Error with
1018 "Before cursor designates wrong list";
1021 pragma Assert (Vet (Before), "bad cursor in Insert");
1029 if Container.Length > Container.Capacity - Count then
1030 raise Constraint_Error with "new length exceeds capacity";
1033 if Container.Busy > 0 then
1034 raise Program_Error with
1035 "attempt to tamper with cursors (list is busy)";
1038 Allocate (Container, New_Node => New_Node);
1039 Insert_Internal (Container, Before.Node, New_Node);
1040 Position := Cursor'(Container'Unchecked_Access, New_Node);
1042 for Index in Count_Type'(2) .. Count loop
1043 Allocate (Container, New_Node => New_Node);
1044 Insert_Internal (Container, Before.Node, New_Node);
1048 ---------------------
1049 -- Insert_Internal --
1050 ---------------------
1052 procedure Insert_Internal
1053 (Container : in out List;
1054 Before : Count_Type;
1055 New_Node : Count_Type)
1057 N : Node_Array renames Container.Nodes;
1060 if Container.Length = 0 then
1061 pragma Assert (Before = 0);
1062 pragma Assert (Container.First = 0);
1063 pragma Assert (Container.Last = 0);
1065 Container.First := New_Node;
1066 N (Container.First).Prev := 0;
1068 Container.Last := New_Node;
1069 N (Container.Last).Next := 0;
1071 -- Before = zero means append
1073 elsif Before = 0 then
1074 pragma Assert (N (Container.Last).Next = 0);
1076 N (Container.Last).Next := New_Node;
1077 N (New_Node).Prev := Container.Last;
1079 Container.Last := New_Node;
1080 N (Container.Last).Next := 0;
1082 -- Before = Container.First means prepend
1084 elsif Before = Container.First then
1085 pragma Assert (N (Container.First).Prev = 0);
1087 N (Container.First).Prev := New_Node;
1088 N (New_Node).Next := Container.First;
1090 Container.First := New_Node;
1091 N (Container.First).Prev := 0;
1094 pragma Assert (N (Container.First).Prev = 0);
1095 pragma Assert (N (Container.Last).Next = 0);
1097 N (New_Node).Next := Before;
1098 N (New_Node).Prev := N (Before).Prev;
1100 N (N (Before).Prev).Next := New_Node;
1101 N (Before).Prev := New_Node;
1104 Container.Length := Container.Length + 1;
1105 end Insert_Internal;
1111 function Is_Empty (Container : List) return Boolean is
1113 return Container.Length = 0;
1122 Process : not null access procedure (Position : Cursor))
1124 B : Natural renames Container'Unrestricted_Access.all.Busy;
1125 Node : Count_Type := Container.First;
1131 while Node /= 0 loop
1132 Process (Cursor'(Container'Unrestricted_Access, Node));
1133 Node := Container.Nodes (Node).Next;
1147 return List_Iterator_Interfaces.Reversible_Iterator'Class
1149 B : Natural renames Container'Unrestricted_Access.all.Busy;
1152 -- The value of the Node component influences the behavior of the First
1153 -- and Last selector functions of the iterator object. When the Node
1154 -- component is 0 (as is the case here), this means the iterator
1155 -- object was constructed without a start expression. This is a
1156 -- complete iterator, meaning that the iteration starts from the
1157 -- (logical) beginning of the sequence of items.
1159 -- Note: For a forward iterator, Container.First is the beginning, and
1160 -- for a reverse iterator, Container.Last is the beginning.
1162 return It : constant Iterator :=
1163 Iterator'(Limited_Controlled with
1164 Container => Container'Unrestricted_Access,
1174 return List_Iterator_Interfaces.Reversible_Iterator'class
1176 B : Natural renames Container'Unrestricted_Access.all.Busy;
1179 -- It was formerly the case that when Start = No_Element, the partial
1180 -- iterator was defined to behave the same as for a complete iterator,
1181 -- and iterate over the entire sequence of items. However, those
1182 -- semantics were unintuitive and arguably error-prone (it is too easy
1183 -- to accidentally create an endless loop), and so they were changed,
1184 -- per the ARG meeting in Denver on 2011/11. However, there was no
1185 -- consensus about what positive meaning this corner case should have,
1186 -- and so it was decided to simply raise an exception. This does imply,
1187 -- however, that it is not possible to use a partial iterator to specify
1188 -- an empty sequence of items.
1190 if Start = No_Element then
1191 raise Constraint_Error with
1192 "Start position for iterator equals No_Element";
1195 if Start.Container /= Container'Unrestricted_Access then
1196 raise Program_Error with
1197 "Start cursor of Iterate designates wrong list";
1200 pragma Assert (Vet (Start), "Start cursor of Iterate is bad");
1202 -- The value of the Node component influences the behavior of the First
1203 -- and Last selector functions of the iterator object. When the Node
1204 -- component is positive (as is the case here), it means that this
1205 -- is a partial iteration, over a subset of the complete sequence of
1206 -- items. The iterator object was constructed with a start expression,
1207 -- indicating the position from which the iteration begins. Note that
1208 -- the start position has the same value irrespective of whether this
1209 -- is a forward or reverse iteration.
1211 return It : constant Iterator :=
1212 Iterator'(Limited_Controlled with
1213 Container => Container'Unrestricted_Access,
1224 function Last (Container : List) return Cursor is
1226 if Container.Last = 0 then
1230 return Cursor'(Container'Unrestricted_Access, Container.Last);
1233 function Last (Object : Iterator) return Cursor is
1235 -- The value of the iterator object's Node component influences the
1236 -- behavior of the Last (and First) selector function.
1238 -- When the Node component is 0, this means the iterator object was
1239 -- constructed without a start expression, in which case the (reverse)
1240 -- iteration starts from the (logical) beginning of the entire sequence
1241 -- (corresponding to Container.Last, for a reverse iterator).
1243 -- Otherwise, this is iteration over a partial sequence of items. When
1244 -- the Node component is positive, the iterator object was constructed
1245 -- with a start expression, that specifies the position from which the
1246 -- (reverse) partial iteration begins.
1248 if Object.Node = 0 then
1249 return Bounded_Doubly_Linked_Lists.Last (Object.Container.all);
1251 return Cursor'(Object.Container, Object.Node);
1259 function Last_Element (Container : List) return Element_Type is
1261 if Container.Last = 0 then
1262 raise Constraint_Error with "list is empty";
1265 return Container.Nodes (Container.Last).Element;
1272 function Length (Container : List) return Count_Type is
1274 return Container.Length;
1282 (Target : in out List;
1283 Source : in out List)
1285 N : Node_Array renames Source.Nodes;
1289 if Target'Address = Source'Address then
1293 if Target.Capacity < Source.Length then
1294 raise Capacity_Error with "Source length exceeds Target capacity";
1297 if Source.Busy > 0 then
1298 raise Program_Error with
1299 "attempt to tamper with cursors of Source (list is busy)";
1302 -- Clear target, note that this checks busy bits of Target
1306 while Source.Length > 1 loop
1307 pragma Assert (Source.First in 1 .. Source.Capacity);
1308 pragma Assert (Source.Last /= Source.First);
1309 pragma Assert (N (Source.First).Prev = 0);
1310 pragma Assert (N (Source.Last).Next = 0);
1312 -- Copy first element from Source to Target
1315 Append (Target, N (X).Element);
1317 -- Unlink first node of Source
1319 Source.First := N (X).Next;
1320 N (Source.First).Prev := 0;
1322 Source.Length := Source.Length - 1;
1324 -- The representation invariants for Source have been restored. It is
1325 -- now safe to free the unlinked node, without fear of corrupting the
1326 -- active links of Source.
1328 -- Note that the algorithm we use here models similar algorithms used
1329 -- in the unbounded form of the doubly-linked list container. In that
1330 -- case, Free is an instantation of Unchecked_Deallocation, which can
1331 -- fail (because PE will be raised if controlled Finalize fails), so
1332 -- we must defer the call until the last step. Here in the bounded
1333 -- form, Free merely links the node we have just "deallocated" onto a
1334 -- list of inactive nodes, so technically Free cannot fail. However,
1335 -- for consistency, we handle Free the same way here as we do for the
1336 -- unbounded form, with the pessimistic assumption that it can fail.
1341 if Source.Length = 1 then
1342 pragma Assert (Source.First in 1 .. Source.Capacity);
1343 pragma Assert (Source.Last = Source.First);
1344 pragma Assert (N (Source.First).Prev = 0);
1345 pragma Assert (N (Source.Last).Next = 0);
1347 -- Copy element from Source to Target
1350 Append (Target, N (X).Element);
1352 -- Unlink node of Source
1358 -- Return the unlinked node to the free store
1368 procedure Next (Position : in out Cursor) is
1370 Position := Next (Position);
1373 function Next (Position : Cursor) return Cursor is
1375 if Position.Node = 0 then
1379 pragma Assert (Vet (Position), "bad cursor in Next");
1382 Nodes : Node_Array renames Position.Container.Nodes;
1383 Node : constant Count_Type := Nodes (Position.Node).Next;
1390 return Cursor'(Position.Container, Node);
1396 Position : Cursor) return Cursor
1399 if Position.Container = null then
1403 if Position.Container /= Object.Container then
1404 raise Program_Error with
1405 "Position cursor of Next designates wrong list";
1408 return Next (Position);
1416 (Container : in out List;
1417 New_Item : Element_Type;
1418 Count : Count_Type := 1)
1421 Insert (Container, First (Container), New_Item, Count);
1428 procedure Previous (Position : in out Cursor) is
1430 Position := Previous (Position);
1433 function Previous (Position : Cursor) return Cursor is
1435 if Position.Node = 0 then
1439 pragma Assert (Vet (Position), "bad cursor in Previous");
1442 Nodes : Node_Array renames Position.Container.Nodes;
1443 Node : constant Count_Type := Nodes (Position.Node).Prev;
1449 return Cursor'(Position.Container, Node);
1455 Position : Cursor) return Cursor
1458 if Position.Container = null then
1462 if Position.Container /= Object.Container then
1463 raise Program_Error with
1464 "Position cursor of Previous designates wrong list";
1467 return Previous (Position);
1474 procedure Query_Element
1476 Process : not null access procedure (Element : Element_Type))
1479 if Position.Node = 0 then
1480 raise Constraint_Error with
1481 "Position cursor has no element";
1484 pragma Assert (Vet (Position), "bad cursor in Query_Element");
1487 C : List renames Position.Container.all'Unrestricted_Access.all;
1488 B : Natural renames C.Busy;
1489 L : Natural renames C.Lock;
1496 N : Node_Type renames C.Nodes (Position.Node);
1498 Process (N.Element);
1516 (Stream : not null access Root_Stream_Type'Class;
1519 N : Count_Type'Base;
1524 Count_Type'Base'Read (Stream, N);
1527 raise Program_Error with "bad list length (corrupt stream)";
1534 if N > Item.Capacity then
1535 raise Constraint_Error with "length exceeds capacity";
1538 for Idx in 1 .. N loop
1539 Allocate (Item, Stream, New_Node => X);
1540 Insert_Internal (Item, Before => 0, New_Node => X);
1545 (Stream : not null access Root_Stream_Type'Class;
1549 raise Program_Error with "attempt to stream list cursor";
1553 (Stream : not null access Root_Stream_Type'Class;
1554 Item : out Reference_Type)
1557 raise Program_Error with "attempt to stream reference";
1561 (Stream : not null access Root_Stream_Type'Class;
1562 Item : out Constant_Reference_Type)
1565 raise Program_Error with "attempt to stream reference";
1573 (Container : aliased in out List;
1574 Position : Cursor) return Reference_Type
1577 if Position.Container = null then
1578 raise Constraint_Error with "Position cursor has no element";
1581 if Position.Container /= Container'Unrestricted_Access then
1582 raise Program_Error with
1583 "Position cursor designates wrong container";
1586 pragma Assert (Vet (Position), "bad cursor in function Reference");
1589 N : Node_Type renames Container.Nodes (Position.Node);
1591 return (Element => N.Element'Access);
1595 ---------------------
1596 -- Replace_Element --
1597 ---------------------
1599 procedure Replace_Element
1600 (Container : in out List;
1602 New_Item : Element_Type)
1605 if Position.Container = null then
1606 raise Constraint_Error with "Position cursor has no element";
1609 if Position.Container /= Container'Unchecked_Access then
1610 raise Program_Error with
1611 "Position cursor designates wrong container";
1614 if Container.Lock > 0 then
1615 raise Program_Error with
1616 "attempt to tamper with elements (list is locked)";
1619 pragma Assert (Vet (Position), "bad cursor in Replace_Element");
1621 Container.Nodes (Position.Node).Element := New_Item;
1622 end Replace_Element;
1624 ----------------------
1625 -- Reverse_Elements --
1626 ----------------------
1628 procedure Reverse_Elements (Container : in out List) is
1629 N : Node_Array renames Container.Nodes;
1630 I : Count_Type := Container.First;
1631 J : Count_Type := Container.Last;
1633 procedure Swap (L, R : Count_Type);
1639 procedure Swap (L, R : Count_Type) is
1640 LN : constant Count_Type := N (L).Next;
1641 LP : constant Count_Type := N (L).Prev;
1643 RN : constant Count_Type := N (R).Next;
1644 RP : constant Count_Type := N (R).Prev;
1659 pragma Assert (RP = L);
1673 -- Start of processing for Reverse_Elements
1676 if Container.Length <= 1 then
1680 pragma Assert (N (Container.First).Prev = 0);
1681 pragma Assert (N (Container.Last).Next = 0);
1683 if Container.Busy > 0 then
1684 raise Program_Error with
1685 "attempt to tamper with cursors (list is busy)";
1688 Container.First := J;
1689 Container.Last := I;
1691 Swap (L => I, R => J);
1699 Swap (L => J, R => I);
1708 pragma Assert (N (Container.First).Prev = 0);
1709 pragma Assert (N (Container.Last).Next = 0);
1710 end Reverse_Elements;
1716 function Reverse_Find
1718 Item : Element_Type;
1719 Position : Cursor := No_Element) return Cursor
1721 Node : Count_Type := Position.Node;
1725 Node := Container.Last;
1728 if Position.Container /= Container'Unrestricted_Access then
1729 raise Program_Error with
1730 "Position cursor designates wrong container";
1733 pragma Assert (Vet (Position), "bad cursor in Reverse_Find");
1736 while Node /= 0 loop
1737 if Container.Nodes (Node).Element = Item then
1738 return Cursor'(Container'Unrestricted_Access, Node);
1741 Node := Container.Nodes (Node).Prev;
1747 ---------------------
1748 -- Reverse_Iterate --
1749 ---------------------
1751 procedure Reverse_Iterate
1753 Process : not null access procedure (Position : Cursor))
1755 C : List renames Container'Unrestricted_Access.all;
1756 B : Natural renames C.Busy;
1758 Node : Count_Type := Container.Last;
1764 while Node /= 0 loop
1765 Process (Cursor'(Container'Unrestricted_Access, Node));
1766 Node := Container.Nodes (Node).Prev;
1776 end Reverse_Iterate;
1783 (Target : in out List;
1785 Source : in out List)
1788 if Before.Container /= null then
1789 if Before.Container /= Target'Unrestricted_Access then
1790 raise Program_Error with
1791 "Before cursor designates wrong container";
1794 pragma Assert (Vet (Before), "bad cursor in Splice");
1797 if Target'Address = Source'Address
1798 or else Source.Length = 0
1803 pragma Assert (Source.Nodes (Source.First).Prev = 0);
1804 pragma Assert (Source.Nodes (Source.Last).Next = 0);
1806 if Target.Length > Count_Type'Last - Source.Length then
1807 raise Constraint_Error with "new length exceeds maximum";
1810 if Target.Length + Source.Length > Target.Capacity then
1811 raise Capacity_Error with "new length exceeds target capacity";
1814 if Target.Busy > 0 then
1815 raise Program_Error with
1816 "attempt to tamper with cursors of Target (list is busy)";
1819 if Source.Busy > 0 then
1820 raise Program_Error with
1821 "attempt to tamper with cursors of Source (list is busy)";
1824 while not Is_Empty (Source) loop
1825 Insert (Target, Before, Source.Nodes (Source.First).Element);
1826 Delete_First (Source);
1831 (Container : in out List;
1835 N : Node_Array renames Container.Nodes;
1838 if Before.Container /= null then
1839 if Before.Container /= Container'Unchecked_Access then
1840 raise Program_Error with
1841 "Before cursor designates wrong container";
1844 pragma Assert (Vet (Before), "bad Before cursor in Splice");
1847 if Position.Node = 0 then
1848 raise Constraint_Error with "Position cursor has no element";
1851 if Position.Container /= Container'Unrestricted_Access then
1852 raise Program_Error with
1853 "Position cursor designates wrong container";
1856 pragma Assert (Vet (Position), "bad Position cursor in Splice");
1858 if Position.Node = Before.Node
1859 or else N (Position.Node).Next = Before.Node
1864 pragma Assert (Container.Length >= 2);
1866 if Container.Busy > 0 then
1867 raise Program_Error with
1868 "attempt to tamper with cursors (list is busy)";
1871 if Before.Node = 0 then
1872 pragma Assert (Position.Node /= Container.Last);
1874 if Position.Node = Container.First then
1875 Container.First := N (Position.Node).Next;
1876 N (Container.First).Prev := 0;
1878 N (N (Position.Node).Prev).Next := N (Position.Node).Next;
1879 N (N (Position.Node).Next).Prev := N (Position.Node).Prev;
1882 N (Container.Last).Next := Position.Node;
1883 N (Position.Node).Prev := Container.Last;
1885 Container.Last := Position.Node;
1886 N (Container.Last).Next := 0;
1891 if Before.Node = Container.First then
1892 pragma Assert (Position.Node /= Container.First);
1894 if Position.Node = Container.Last then
1895 Container.Last := N (Position.Node).Prev;
1896 N (Container.Last).Next := 0;
1898 N (N (Position.Node).Prev).Next := N (Position.Node).Next;
1899 N (N (Position.Node).Next).Prev := N (Position.Node).Prev;
1902 N (Container.First).Prev := Position.Node;
1903 N (Position.Node).Next := Container.First;
1905 Container.First := Position.Node;
1906 N (Container.First).Prev := 0;
1911 if Position.Node = Container.First then
1912 Container.First := N (Position.Node).Next;
1913 N (Container.First).Prev := 0;
1915 elsif Position.Node = Container.Last then
1916 Container.Last := N (Position.Node).Prev;
1917 N (Container.Last).Next := 0;
1920 N (N (Position.Node).Prev).Next := N (Position.Node).Next;
1921 N (N (Position.Node).Next).Prev := N (Position.Node).Prev;
1924 N (N (Before.Node).Prev).Next := Position.Node;
1925 N (Position.Node).Prev := N (Before.Node).Prev;
1927 N (Before.Node).Prev := Position.Node;
1928 N (Position.Node).Next := Before.Node;
1930 pragma Assert (N (Container.First).Prev = 0);
1931 pragma Assert (N (Container.Last).Next = 0);
1935 (Target : in out List;
1937 Source : in out List;
1938 Position : in out Cursor)
1940 Target_Position : Cursor;
1943 if Target'Address = Source'Address then
1944 Splice (Target, Before, Position);
1948 if Before.Container /= null then
1949 if Before.Container /= Target'Unrestricted_Access then
1950 raise Program_Error with
1951 "Before cursor designates wrong container";
1954 pragma Assert (Vet (Before), "bad Before cursor in Splice");
1957 if Position.Node = 0 then
1958 raise Constraint_Error with "Position cursor has no element";
1961 if Position.Container /= Source'Unrestricted_Access then
1962 raise Program_Error with
1963 "Position cursor designates wrong container";
1966 pragma Assert (Vet (Position), "bad Position cursor in Splice");
1968 if Target.Length >= Target.Capacity then
1969 raise Capacity_Error with "Target is full";
1972 if Target.Busy > 0 then
1973 raise Program_Error with
1974 "attempt to tamper with cursors of Target (list is busy)";
1977 if Source.Busy > 0 then
1978 raise Program_Error with
1979 "attempt to tamper with cursors of Source (list is busy)";
1983 (Container => Target,
1985 New_Item => Source.Nodes (Position.Node).Element,
1986 Position => Target_Position);
1988 Delete (Source, Position);
1989 Position := Target_Position;
1997 (Container : in out List;
2002 raise Constraint_Error with "I cursor has no element";
2006 raise Constraint_Error with "J cursor has no element";
2009 if I.Container /= Container'Unchecked_Access then
2010 raise Program_Error with "I cursor designates wrong container";
2013 if J.Container /= Container'Unchecked_Access then
2014 raise Program_Error with "J cursor designates wrong container";
2017 if I.Node = J.Node then
2021 if Container.Lock > 0 then
2022 raise Program_Error with
2023 "attempt to tamper with elements (list is locked)";
2026 pragma Assert (Vet (I), "bad I cursor in Swap");
2027 pragma Assert (Vet (J), "bad J cursor in Swap");
2030 EI : Element_Type renames Container.Nodes (I.Node).Element;
2031 EJ : Element_Type renames Container.Nodes (J.Node).Element;
2033 EI_Copy : constant Element_Type := EI;
2045 procedure Swap_Links
2046 (Container : in out List;
2051 raise Constraint_Error with "I cursor has no element";
2055 raise Constraint_Error with "J cursor has no element";
2058 if I.Container /= Container'Unrestricted_Access then
2059 raise Program_Error with "I cursor designates wrong container";
2062 if J.Container /= Container'Unrestricted_Access then
2063 raise Program_Error with "J cursor designates wrong container";
2066 if I.Node = J.Node then
2070 if Container.Busy > 0 then
2071 raise Program_Error with
2072 "attempt to tamper with cursors (list is busy)";
2075 pragma Assert (Vet (I), "bad I cursor in Swap_Links");
2076 pragma Assert (Vet (J), "bad J cursor in Swap_Links");
2079 I_Next : constant Cursor := Next (I);
2083 Splice (Container, Before => I, Position => J);
2087 J_Next : constant Cursor := Next (J);
2091 Splice (Container, Before => J, Position => I);
2094 pragma Assert (Container.Length >= 3);
2096 Splice (Container, Before => I_Next, Position => J);
2097 Splice (Container, Before => J_Next, Position => I);
2104 --------------------
2105 -- Update_Element --
2106 --------------------
2108 procedure Update_Element
2109 (Container : in out List;
2111 Process : not null access procedure (Element : in out Element_Type))
2114 if Position.Node = 0 then
2115 raise Constraint_Error with "Position cursor has no element";
2118 if Position.Container /= Container'Unchecked_Access then
2119 raise Program_Error with
2120 "Position cursor designates wrong container";
2123 pragma Assert (Vet (Position), "bad cursor in Update_Element");
2126 B : Natural renames Container.Busy;
2127 L : Natural renames Container.Lock;
2134 N : Node_Type renames Container.Nodes (Position.Node);
2136 Process (N.Element);
2153 function Vet (Position : Cursor) return Boolean is
2155 if Position.Node = 0 then
2156 return Position.Container = null;
2159 if Position.Container = null then
2164 L : List renames Position.Container.all;
2165 N : Node_Array renames L.Nodes;
2168 if L.Length = 0 then
2172 if L.First = 0 or L.First > L.Capacity then
2176 if L.Last = 0 or L.Last > L.Capacity then
2180 if N (L.First).Prev /= 0 then
2184 if N (L.Last).Next /= 0 then
2188 if Position.Node > L.Capacity then
2192 -- An invariant of an active node is that its Previous and Next
2193 -- components are non-negative. Operation Free sets the Previous
2194 -- component of the node to the value -1 before actually deallocating
2195 -- the node, to mark the node as inactive. (By "dellocating" we mean
2196 -- only that the node is linked onto a list of inactive nodes used
2197 -- for storage.) This marker gives us a simple way to detect a
2198 -- dangling reference to a node.
2200 if N (Position.Node).Prev < 0 then -- see Free
2204 if N (Position.Node).Prev > L.Capacity then
2208 if N (Position.Node).Next = Position.Node then
2212 if N (Position.Node).Prev = Position.Node then
2216 if N (Position.Node).Prev = 0
2217 and then Position.Node /= L.First
2222 pragma Assert (N (Position.Node).Prev /= 0
2223 or else Position.Node = L.First);
2225 if N (Position.Node).Next = 0
2226 and then Position.Node /= L.Last
2231 pragma Assert (N (Position.Node).Next /= 0
2232 or else Position.Node = L.Last);
2234 if L.Length = 1 then
2235 return L.First = L.Last;
2238 if L.First = L.Last then
2242 if N (L.First).Next = 0 then
2246 if N (L.Last).Prev = 0 then
2250 if N (N (L.First).Next).Prev /= L.First then
2254 if N (N (L.Last).Prev).Next /= L.Last then
2258 if L.Length = 2 then
2259 if N (L.First).Next /= L.Last then
2263 if N (L.Last).Prev /= L.First then
2270 if N (L.First).Next = L.Last then
2274 if N (L.Last).Prev = L.First then
2278 -- Eliminate earlier possibility
2280 if Position.Node = L.First then
2284 pragma Assert (N (Position.Node).Prev /= 0);
2286 -- ELiminate another possibility
2288 if Position.Node = L.Last then
2292 pragma Assert (N (Position.Node).Next /= 0);
2294 if N (N (Position.Node).Next).Prev /= Position.Node then
2298 if N (N (Position.Node).Prev).Next /= Position.Node then
2302 if L.Length = 3 then
2303 if N (L.First).Next /= Position.Node then
2307 if N (L.Last).Prev /= Position.Node then
2321 (Stream : not null access Root_Stream_Type'Class;
2327 Count_Type'Base'Write (Stream, Item.Length);
2330 while Node /= 0 loop
2331 Element_Type'Write (Stream, Item.Nodes (Node).Element);
2332 Node := Item.Nodes (Node).Next;
2337 (Stream : not null access Root_Stream_Type'Class;
2341 raise Program_Error with "attempt to stream list cursor";
2345 (Stream : not null access Root_Stream_Type'Class;
2346 Item : Reference_Type)
2349 raise Program_Error with "attempt to stream reference";
2353 (Stream : not null access Root_Stream_Type'Class;
2354 Item : Constant_Reference_Type)
2357 raise Program_Error with "attempt to stream reference";
2360 end Ada.Containers.Bounded_Doubly_Linked_Lists;