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
;