1 ------------------------------------------------------------------------------
3 -- GNAT LIBRARY COMPONENTS --
5 -- ADA.CONTAINERS.INDEFINITE_DOUBLY_LINKED_LISTS --
9 -- Copyright (C) 2004-2013, Free Software Foundation, Inc. --
11 -- GNAT is free software; you can redistribute it and/or modify it under --
12 -- terms of the GNU General Public License as published by the Free Soft- --
13 -- ware Foundation; either version 3, or (at your option) any later ver- --
14 -- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
15 -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
16 -- or FITNESS FOR A PARTICULAR PURPOSE. --
18 -- As a special exception under Section 7 of GPL version 3, you are granted --
19 -- additional permissions described in the GCC Runtime Library Exception, --
20 -- version 3.1, as published by the Free Software Foundation. --
22 -- You should have received a copy of the GNU General Public License and --
23 -- a copy of the GCC Runtime Library Exception along with this program; --
24 -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see --
25 -- <http://www.gnu.org/licenses/>. --
27 -- This unit was originally developed by Matthew J Heaney. --
28 ------------------------------------------------------------------------------
30 with Ada
.Unchecked_Deallocation
;
32 with System
; use type System
.Address
;
34 package body Ada
.Containers
.Indefinite_Doubly_Linked_Lists
is
37 new Ada
.Unchecked_Deallocation
(Element_Type
, Element_Access
);
39 -----------------------
40 -- Local Subprograms --
41 -----------------------
43 procedure Free
(X
: in out Node_Access
);
45 procedure Insert_Internal
46 (Container
: in out List
;
48 New_Node
: Node_Access
);
50 procedure Splice_Internal
51 (Target
: in out List
;
53 Source
: in out List
);
55 procedure Splice_Internal
56 (Target
: in out List
;
59 Position
: Node_Access
);
61 function Vet
(Position
: Cursor
) return Boolean;
62 -- Checks invariants of the cursor and its designated container, as a
63 -- simple way of detecting dangling references (see operation Free for a
64 -- description of the detection mechanism), returning True if all checks
65 -- pass. Invocations of Vet are used here as the argument of pragma Assert,
66 -- so the checks are performed only when assertions are enabled.
72 function "=" (Left
, Right
: List
) return Boolean is
73 BL
: Natural renames Left
'Unrestricted_Access.Busy
;
74 LL
: Natural renames Left
'Unrestricted_Access.Lock
;
76 BR
: Natural renames Right
'Unrestricted_Access.Busy
;
77 LR
: Natural renames Right
'Unrestricted_Access.Lock
;
84 if Left
'Address = Right
'Address then
88 if Left
.Length
/= Right
.Length
then
92 -- Per AI05-0022, the container implementation is required to detect
93 -- element tampering by a generic actual subprogram.
104 for J
in 1 .. Left
.Length
loop
105 if L
.Element
.all /= R
.Element
.all then
137 procedure Adjust
(Container
: in out List
) is
138 Src
: Node_Access
:= Container
.First
;
143 pragma Assert
(Container
.Last
= null);
144 pragma Assert
(Container
.Length
= 0);
145 pragma Assert
(Container
.Busy
= 0);
146 pragma Assert
(Container
.Lock
= 0);
150 pragma Assert
(Container
.First
.Prev
= null);
151 pragma Assert
(Container
.Last
.Next
= null);
152 pragma Assert
(Container
.Length
> 0);
154 Container
.First
:= null;
155 Container
.Last
:= null;
156 Container
.Length
:= 0;
161 Element
: Element_Access
:= new Element_Type
'(Src.Element.all);
163 Dst := new Node_Type'(Element
, null, null);
170 Container
.First
:= Dst
;
171 Container
.Last
:= Dst
;
172 Container
.Length
:= 1;
175 while Src
/= null loop
177 Element
: Element_Access
:= new Element_Type
'(Src.Element.all);
179 Dst := new Node_Type'(Element
, null, Prev
=> Container
.Last
);
186 Container
.Last
.Next
:= Dst
;
187 Container
.Last
:= Dst
;
188 Container
.Length
:= Container
.Length
+ 1;
194 procedure Adjust
(Control
: in out Reference_Control_Type
) is
196 if Control
.Container
/= null then
198 C
: List
renames Control
.Container
.all;
199 B
: Natural renames C
.Busy
;
200 L
: Natural renames C
.Lock
;
213 (Container
: in out List
;
214 New_Item
: Element_Type
;
215 Count
: Count_Type
:= 1)
218 Insert
(Container
, No_Element
, New_Item
, Count
);
225 procedure Assign
(Target
: in out List
; Source
: List
) is
229 if Target
'Address = Source
'Address then
235 Node
:= Source
.First
;
236 while Node
/= null loop
237 Target
.Append
(Node
.Element
.all);
247 procedure Clear
(Container
: in out List
) is
249 pragma Warnings
(Off
, X
);
252 if Container
.Length
= 0 then
253 pragma Assert
(Container
.First
= null);
254 pragma Assert
(Container
.Last
= null);
255 pragma Assert
(Container
.Busy
= 0);
256 pragma Assert
(Container
.Lock
= 0);
260 pragma Assert
(Container
.First
.Prev
= null);
261 pragma Assert
(Container
.Last
.Next
= null);
263 if Container
.Busy
> 0 then
264 raise Program_Error
with
265 "attempt to tamper with cursors (list is busy)";
268 while Container
.Length
> 1 loop
269 X
:= Container
.First
;
270 pragma Assert
(X
.Next
.Prev
= Container
.First
);
272 Container
.First
:= X
.Next
;
273 Container
.First
.Prev
:= null;
275 Container
.Length
:= Container
.Length
- 1;
280 X
:= Container
.First
;
281 pragma Assert
(X
= Container
.Last
);
283 Container
.First
:= null;
284 Container
.Last
:= null;
285 Container
.Length
:= 0;
290 ------------------------
291 -- Constant_Reference --
292 ------------------------
294 function Constant_Reference
295 (Container
: aliased List
;
296 Position
: Cursor
) return Constant_Reference_Type
299 if Position
.Container
= null then
300 raise Constraint_Error
with "Position cursor has no element";
302 elsif Position
.Container
/= Container
'Unrestricted_Access then
303 raise Program_Error
with
304 "Position cursor designates wrong container";
305 elsif Position
.Node
.Element
= null then
306 raise Program_Error
with "Node has no element";
309 pragma Assert
(Vet
(Position
), "bad cursor in Constant_Reference");
312 C
: List
renames Position
.Container
.all;
313 B
: Natural renames C
.Busy
;
314 L
: Natural renames C
.Lock
;
316 return R
: constant Constant_Reference_Type
:=
317 (Element
=> Position
.Node
.Element
.all'Access,
318 Control
=> (Controlled
with Position
.Container
))
325 end Constant_Reference
;
333 Item
: Element_Type
) return Boolean
336 return Find
(Container
, Item
) /= No_Element
;
343 function Copy
(Source
: List
) return List
is
345 return Target
: List
do
346 Target
.Assign
(Source
);
355 (Container
: in out List
;
356 Position
: in out Cursor
;
357 Count
: Count_Type
:= 1)
362 if Position
.Node
= null then
363 raise Constraint_Error
with
364 "Position cursor has no element";
367 if Position
.Node
.Element
= null then
368 raise Program_Error
with
369 "Position cursor has no element";
372 if Position
.Container
/= Container
'Unrestricted_Access then
373 raise Program_Error
with
374 "Position cursor designates wrong container";
377 pragma Assert
(Vet
(Position
), "bad cursor in Delete");
379 if Position
.Node
= Container
.First
then
380 Delete_First
(Container
, Count
);
381 Position
:= No_Element
; -- Post-York behavior
386 Position
:= No_Element
; -- Post-York behavior
390 if Container
.Busy
> 0 then
391 raise Program_Error
with
392 "attempt to tamper with cursors (list is busy)";
395 for Index
in 1 .. Count
loop
397 Container
.Length
:= Container
.Length
- 1;
399 if X
= Container
.Last
then
400 Position
:= No_Element
;
402 Container
.Last
:= X
.Prev
;
403 Container
.Last
.Next
:= null;
409 Position
.Node
:= X
.Next
;
411 X
.Next
.Prev
:= X
.Prev
;
412 X
.Prev
.Next
:= X
.Next
;
417 -- Fix this junk comment ???
419 Position
:= No_Element
; -- Post-York behavior
426 procedure Delete_First
427 (Container
: in out List
;
428 Count
: Count_Type
:= 1)
433 if Count
>= Container
.Length
then
440 elsif Container
.Busy
> 0 then
441 raise Program_Error
with
442 "attempt to tamper with cursors (list is busy)";
445 for J
in 1 .. Count
loop
446 X
:= Container
.First
;
447 pragma Assert
(X
.Next
.Prev
= Container
.First
);
449 Container
.First
:= X
.Next
;
450 Container
.First
.Prev
:= null;
452 Container
.Length
:= Container
.Length
- 1;
463 procedure Delete_Last
464 (Container
: in out List
;
465 Count
: Count_Type
:= 1)
470 if Count
>= Container
.Length
then
477 elsif Container
.Busy
> 0 then
478 raise Program_Error
with
479 "attempt to tamper with cursors (list is busy)";
482 for J
in 1 .. Count
loop
484 pragma Assert
(X
.Prev
.Next
= Container
.Last
);
486 Container
.Last
:= X
.Prev
;
487 Container
.Last
.Next
:= null;
489 Container
.Length
:= Container
.Length
- 1;
500 function Element
(Position
: Cursor
) return Element_Type
is
502 if Position
.Node
= null then
503 raise Constraint_Error
with
504 "Position cursor has no element";
506 elsif Position
.Node
.Element
= null then
507 raise Program_Error
with
508 "Position cursor has no element";
511 pragma Assert
(Vet
(Position
), "bad cursor in Element");
513 return Position
.Node
.Element
.all;
521 procedure Finalize
(Object
: in out Iterator
) is
523 if Object
.Container
/= null then
525 B
: Natural renames Object
.Container
.all.Busy
;
532 procedure Finalize
(Control
: in out Reference_Control_Type
) is
534 if Control
.Container
/= null then
536 C
: List
renames Control
.Container
.all;
537 B
: Natural renames C
.Busy
;
538 L
: Natural renames C
.Lock
;
544 Control
.Container
:= null;
555 Position
: Cursor
:= No_Element
) return Cursor
557 Node
: Node_Access
:= Position
.Node
;
561 Node
:= Container
.First
;
564 if Node
.Element
= null then
567 elsif Position
.Container
/= Container
'Unrestricted_Access then
568 raise Program_Error
with
569 "Position cursor designates wrong container";
572 pragma Assert
(Vet
(Position
), "bad cursor in Find");
576 -- Per AI05-0022, the container implementation is required to detect
577 -- element tampering by a generic actual subprogram.
580 B
: Natural renames Container
'Unrestricted_Access.Busy
;
581 L
: Natural renames Container
'Unrestricted_Access.Lock
;
583 Result
: Node_Access
;
590 while Node
/= null loop
591 if Node
.Element
.all = Item
then
602 if Result
= null then
605 return Cursor
'(Container'Unrestricted_Access, Result);
621 function First (Container : List) return Cursor is
623 if Container.First = null then
626 return Cursor'(Container
'Unrestricted_Access, Container
.First
);
630 function First
(Object
: Iterator
) return Cursor
is
632 -- The value of the iterator object's Node component influences the
633 -- behavior of the First (and Last) selector function.
635 -- When the Node component is null, this means the iterator object was
636 -- constructed without a start expression, in which case the (forward)
637 -- iteration starts from the (logical) beginning of the entire sequence
638 -- of items (corresponding to Container.First, for a forward iterator).
640 -- Otherwise, this is iteration over a partial sequence of items. When
641 -- the Node component is non-null, the iterator object was constructed
642 -- with a start expression, that specifies the position from which the
643 -- (forward) partial iteration begins.
645 if Object
.Node
= null then
646 return Indefinite_Doubly_Linked_Lists
.First
(Object
.Container
.all);
648 return Cursor
'(Object.Container, Object.Node);
656 function First_Element (Container : List) return Element_Type is
658 if Container.First = null then
659 raise Constraint_Error with "list is empty";
661 return Container.First.Element.all;
669 procedure Free (X : in out Node_Access) is
670 procedure Deallocate is
671 new Ada.Unchecked_Deallocation (Node_Type, Node_Access);
674 -- While a node is in use, as an active link in a list, its Previous and
675 -- Next components must be null, or designate a different node; this is
676 -- a node invariant. For this indefinite list, there is an additional
677 -- invariant: that the element access value be non-null. Before actually
678 -- deallocating the node, we set the node access value components of the
679 -- node to point to the node itself, and set the element access value to
680 -- null (by deallocating the node's element), thus falsifying the node
681 -- invariant. Subprogram Vet inspects the value of the node components
682 -- when interrogating the node, in order to detect whether the cursor's
683 -- node access value is dangling.
685 -- Note that we have no guarantee that the storage for the node isn't
686 -- modified when it is deallocated, but there are other tests that Vet
687 -- does if node invariants appear to be satisifed. However, in practice
688 -- this simple test works well enough, detecting dangling references
689 -- immediately, without needing further interrogation.
706 ---------------------
707 -- Generic_Sorting --
708 ---------------------
710 package body Generic_Sorting is
716 function Is_Sorted (Container : List) return Boolean is
717 B : Natural renames Container'Unrestricted_Access.Busy;
718 L : Natural renames Container'Unrestricted_Access.Lock;
724 -- Per AI05-0022, the container implementation is required to detect
725 -- element tampering by a generic actual subprogram.
730 Node := Container.First;
732 for J in 2 .. Container.Length loop
733 if Node.Next.Element.all < Node.Element.all then
759 (Target : in out List;
760 Source : in out List)
763 -- The semantics of Merge changed slightly per AI05-0021. It was
764 -- originally the case that if Target and Source denoted the same
765 -- container object, then the GNAT implementation of Merge did
766 -- nothing. However, it was argued that RM05 did not precisely
767 -- specify the semantics for this corner case. The decision of the
768 -- ARG was that if Target and Source denote the same non-empty
769 -- container object, then Program_Error is raised.
771 if Source.Is_Empty then
774 elsif Target'Address = Source'Address then
775 raise Program_Error with
776 "Target and Source denote same non-empty container";
778 elsif Target.Length > Count_Type'Last - Source.Length then
779 raise Constraint_Error with "new length exceeds maximum";
781 elsif Target.Busy > 0 then
782 raise Program_Error with
783 "attempt to tamper with cursors of Target (list is busy)";
785 elsif Source.Busy > 0 then
786 raise Program_Error with
787 "attempt to tamper with cursors of Source (list is busy)";
791 TB : Natural renames Target.Busy;
792 TL : Natural renames Target.Lock;
794 SB : Natural renames Source.Busy;
795 SL : Natural renames Source.Lock;
797 LI, RI, RJ : Node_Access;
808 while RI /= null loop
809 pragma Assert (RI.Next = null
810 or else not (RI.Next.Element.all <
814 Splice_Internal (Target, null, Source);
818 pragma Assert (LI.Next = null
819 or else not (LI.Next.Element.all <
822 if RI.Element.all < LI.Element.all then
825 Splice_Internal (Target, LI, Source, RJ);
854 procedure Sort (Container : in out List) is
855 procedure Partition (Pivot : Node_Access; Back : Node_Access);
858 procedure Sort (Front, Back : Node_Access);
859 -- Comment??? Confusing name??? change name???
865 procedure Partition (Pivot : Node_Access; Back : Node_Access) is
870 while Node /= Back loop
871 if Node.Element.all < Pivot.Element.all then
873 Prev : constant Node_Access := Node.Prev;
874 Next : constant Node_Access := Node.Next;
880 Container.Last := Prev;
886 Node.Prev := Pivot.Prev;
890 if Node.Prev = null then
891 Container.First := Node;
893 Node.Prev.Next := Node;
909 procedure Sort (Front, Back : Node_Access) is
910 Pivot : constant Node_Access :=
911 (if Front = null then Container.First else Front.Next);
913 if Pivot /= Back then
914 Partition (Pivot, Back);
920 -- Start of processing for Sort
923 if Container.Length <= 1 then
927 pragma Assert (Container.First.Prev = null);
928 pragma Assert (Container.Last.Next = null);
930 if Container.Busy > 0 then
931 raise Program_Error with
932 "attempt to tamper with cursors (list is busy)";
935 -- Per AI05-0022, the container implementation is required to detect
936 -- element tampering by a generic actual subprogram.
939 B : Natural renames Container.Busy;
940 L : Natural renames Container.Lock;
946 Sort (Front => null, Back => null);
959 pragma Assert (Container.First.Prev = null);
960 pragma Assert (Container.Last.Next = null);
969 function Has_Element (Position : Cursor) return Boolean is
971 pragma Assert (Vet (Position), "bad cursor in Has_Element");
972 return Position.Node /= null;
980 (Container : in out List;
982 New_Item : Element_Type;
983 Position : out Cursor;
984 Count : Count_Type := 1)
986 New_Node : Node_Access;
989 if Before.Container /= null then
990 if Before.Container /= Container'Unrestricted_Access then
991 raise Program_Error with
992 "attempt to tamper with cursors (list is busy)";
994 elsif Before.Node = null or else Before.Node.Element = null then
995 raise Program_Error with
996 "Before cursor has no element";
999 pragma Assert (Vet (Before), "bad cursor in Insert");
1008 if Container.Length > Count_Type'Last - Count then
1009 raise Constraint_Error with "new length exceeds maximum";
1012 if Container.Busy > 0 then
1013 raise Program_Error with
1014 "attempt to tamper with cursors (list is busy)";
1018 -- The element allocator may need an accessibility check in the case
1019 -- the actual type is class-wide or has access discriminants (see
1020 -- RM 4.8(10.1) and AI12-0035). We don't unsuppress the check on the
1021 -- allocator in the loop below, because the one in this block would
1022 -- have failed already.
1024 pragma Unsuppress (Accessibility_Check);
1026 Element : Element_Access := new Element_Type'(New_Item
);
1029 New_Node
:= new Node_Type
'(Element, null, null);
1037 Insert_Internal (Container, Before.Node, New_Node);
1038 Position := Cursor'(Container
'Unchecked_Access, New_Node
);
1040 for J
in 2 .. Count
loop
1042 Element
: Element_Access
:= new Element_Type
'(New_Item);
1044 New_Node := new Node_Type'(Element
, null, null);
1051 Insert_Internal
(Container
, Before
.Node
, New_Node
);
1056 (Container
: in out List
;
1058 New_Item
: Element_Type
;
1059 Count
: Count_Type
:= 1)
1062 pragma Unreferenced
(Position
);
1064 Insert
(Container
, Before
, New_Item
, Position
, Count
);
1067 ---------------------
1068 -- Insert_Internal --
1069 ---------------------
1071 procedure Insert_Internal
1072 (Container
: in out List
;
1073 Before
: Node_Access
;
1074 New_Node
: Node_Access
)
1077 if Container
.Length
= 0 then
1078 pragma Assert
(Before
= null);
1079 pragma Assert
(Container
.First
= null);
1080 pragma Assert
(Container
.Last
= null);
1082 Container
.First
:= New_Node
;
1083 Container
.Last
:= New_Node
;
1085 elsif Before
= null then
1086 pragma Assert
(Container
.Last
.Next
= null);
1088 Container
.Last
.Next
:= New_Node
;
1089 New_Node
.Prev
:= Container
.Last
;
1091 Container
.Last
:= New_Node
;
1093 elsif Before
= Container
.First
then
1094 pragma Assert
(Container
.First
.Prev
= null);
1096 Container
.First
.Prev
:= New_Node
;
1097 New_Node
.Next
:= Container
.First
;
1099 Container
.First
:= New_Node
;
1102 pragma Assert
(Container
.First
.Prev
= null);
1103 pragma Assert
(Container
.Last
.Next
= null);
1105 New_Node
.Next
:= Before
;
1106 New_Node
.Prev
:= Before
.Prev
;
1108 Before
.Prev
.Next
:= New_Node
;
1109 Before
.Prev
:= New_Node
;
1112 Container
.Length
:= Container
.Length
+ 1;
1113 end Insert_Internal
;
1119 function Is_Empty
(Container
: List
) return Boolean is
1121 return Container
.Length
= 0;
1130 Process
: not null access procedure (Position
: Cursor
))
1132 B
: Natural renames Container
'Unrestricted_Access.all.Busy
;
1133 Node
: Node_Access
:= Container
.First
;
1139 while Node
/= null loop
1140 Process
(Cursor
'(Container'Unrestricted_Access, Node));
1154 return List_Iterator_Interfaces.Reversible_Iterator'class
1156 B : Natural renames Container'Unrestricted_Access.all.Busy;
1159 -- The value of the Node component influences the behavior of the First
1160 -- and Last selector functions of the iterator object. When the Node
1161 -- component is null (as is the case here), this means the iterator
1162 -- object was constructed without a start expression. This is a
1163 -- complete iterator, meaning that the iteration starts from the
1164 -- (logical) beginning of the sequence of items.
1166 -- Note: For a forward iterator, Container.First is the beginning, and
1167 -- for a reverse iterator, Container.Last is the beginning.
1169 return It : constant Iterator :=
1170 Iterator'(Limited_Controlled
with
1171 Container
=> Container
'Unrestricted_Access,
1181 return List_Iterator_Interfaces
.Reversible_Iterator
'Class
1183 B
: Natural renames Container
'Unrestricted_Access.all.Busy
;
1186 -- It was formerly the case that when Start = No_Element, the partial
1187 -- iterator was defined to behave the same as for a complete iterator,
1188 -- and iterate over the entire sequence of items. However, those
1189 -- semantics were unintuitive and arguably error-prone (it is too easy
1190 -- to accidentally create an endless loop), and so they were changed,
1191 -- per the ARG meeting in Denver on 2011/11. However, there was no
1192 -- consensus about what positive meaning this corner case should have,
1193 -- and so it was decided to simply raise an exception. This does imply,
1194 -- however, that it is not possible to use a partial iterator to specify
1195 -- an empty sequence of items.
1197 if Start
= No_Element
then
1198 raise Constraint_Error
with
1199 "Start position for iterator equals No_Element";
1201 elsif Start
.Container
/= Container
'Unrestricted_Access then
1202 raise Program_Error
with
1203 "Start cursor of Iterate designates wrong list";
1206 pragma Assert
(Vet
(Start
), "Start cursor of Iterate is bad");
1208 -- The value of the Node component influences the behavior of the
1209 -- First and Last selector functions of the iterator object. When
1210 -- the Node component is non-null (as is the case here), it means
1211 -- that this is a partial iteration, over a subset of the complete
1212 -- sequence of items. The iterator object was constructed with
1213 -- a start expression, indicating the position from which the
1214 -- iteration begins. Note that the start position has the same value
1215 -- irrespective of whether this is a forward or reverse iteration.
1217 return It
: constant Iterator
:=
1218 Iterator
'(Limited_Controlled with
1219 Container => Container'Unrestricted_Access,
1231 function Last (Container : List) return Cursor is
1233 if Container.Last = null then
1236 return Cursor'(Container
'Unrestricted_Access, Container
.Last
);
1240 function Last
(Object
: Iterator
) return Cursor
is
1242 -- The value of the iterator object's Node component influences the
1243 -- behavior of the Last (and First) selector function.
1245 -- When the Node component is null, this means the iterator object was
1246 -- constructed without a start expression, in which case the (reverse)
1247 -- iteration starts from the (logical) beginning of the entire sequence
1248 -- (corresponding to Container.Last, for a reverse iterator).
1250 -- Otherwise, this is iteration over a partial sequence of items. When
1251 -- the Node component is non-null, the iterator object was constructed
1252 -- with a start expression, that specifies the position from which the
1253 -- (reverse) partial iteration begins.
1255 if Object
.Node
= null then
1256 return Indefinite_Doubly_Linked_Lists
.Last
(Object
.Container
.all);
1258 return Cursor
'(Object.Container, Object.Node);
1266 function Last_Element (Container : List) return Element_Type is
1268 if Container.Last = null then
1269 raise Constraint_Error with "list is empty";
1271 return Container.Last.Element.all;
1279 function Length (Container : List) return Count_Type is
1281 return Container.Length;
1288 procedure Move (Target : in out List; Source : in out List) is
1290 if Target'Address = Source'Address then
1293 elsif Source.Busy > 0 then
1294 raise Program_Error with
1295 "attempt to tamper with cursors of Source (list is busy)";
1300 Target.First := Source.First;
1301 Source.First := null;
1303 Target.Last := Source.Last;
1304 Source.Last := null;
1306 Target.Length := Source.Length;
1315 procedure Next (Position : in out Cursor) is
1317 Position := Next (Position);
1320 function Next (Position : Cursor) return Cursor is
1322 if Position.Node = null then
1326 pragma Assert (Vet (Position), "bad cursor in Next");
1329 Next_Node : constant Node_Access := Position.Node.Next;
1331 if Next_Node = null then
1334 return Cursor'(Position
.Container
, Next_Node
);
1340 function Next
(Object
: Iterator
; Position
: Cursor
) return Cursor
is
1342 if Position
.Container
= null then
1344 elsif Position
.Container
/= Object
.Container
then
1345 raise Program_Error
with
1346 "Position cursor of Next designates wrong list";
1348 return Next
(Position
);
1357 (Container
: in out List
;
1358 New_Item
: Element_Type
;
1359 Count
: Count_Type
:= 1)
1362 Insert
(Container
, First
(Container
), New_Item
, Count
);
1369 procedure Previous
(Position
: in out Cursor
) is
1371 Position
:= Previous
(Position
);
1374 function Previous
(Position
: Cursor
) return Cursor
is
1376 if Position
.Node
= null then
1380 pragma Assert
(Vet
(Position
), "bad cursor in Previous");
1383 Prev_Node
: constant Node_Access
:= Position
.Node
.Prev
;
1385 if Prev_Node
= null then
1388 return Cursor
'(Position.Container, Prev_Node);
1394 function Previous (Object : Iterator; Position : Cursor) return Cursor is
1396 if Position.Container = null then
1398 elsif Position.Container /= Object.Container then
1399 raise Program_Error with
1400 "Position cursor of Previous designates wrong list";
1402 return Previous (Position);
1410 procedure Query_Element
1412 Process : not null access procedure (Element : Element_Type))
1415 if Position.Node = null then
1416 raise Constraint_Error with
1417 "Position cursor has no element";
1419 elsif Position.Node.Element = null then
1420 raise Program_Error with
1421 "Position cursor has no element";
1424 pragma Assert (Vet (Position), "bad cursor in Query_Element");
1427 C : List renames Position.Container.all'Unrestricted_Access.all;
1428 B : Natural renames C.Busy;
1429 L : Natural renames C.Lock;
1436 Process (Position.Node.Element.all);
1455 (Stream : not null access Root_Stream_Type'Class;
1458 N : Count_Type'Base;
1464 Count_Type'Base'Read
(Stream
, N
);
1471 Element
: Element_Access
:=
1472 new Element_Type
'(Element_Type'Input (Stream));
1474 Dst := new Node_Type'(Element
, null, null);
1485 while Item
.Length
< N
loop
1487 Element
: Element_Access
:=
1488 new Element_Type
'(Element_Type'Input (Stream));
1490 Dst := new Node_Type'(Element
, Next
=> null, Prev
=> Item
.Last
);
1497 Item
.Last
.Next
:= Dst
;
1499 Item
.Length
:= Item
.Length
+ 1;
1504 (Stream
: not null access Root_Stream_Type
'Class;
1508 raise Program_Error
with "attempt to stream list cursor";
1512 (Stream
: not null access Root_Stream_Type
'Class;
1513 Item
: out Reference_Type
)
1516 raise Program_Error
with "attempt to stream reference";
1520 (Stream
: not null access Root_Stream_Type
'Class;
1521 Item
: out Constant_Reference_Type
)
1524 raise Program_Error
with "attempt to stream reference";
1532 (Container
: aliased in out List
;
1533 Position
: Cursor
) return Reference_Type
1536 if Position
.Container
= null then
1537 raise Constraint_Error
with "Position cursor has no element";
1539 elsif Position
.Container
/= Container
'Unrestricted_Access then
1540 raise Program_Error
with
1541 "Position cursor designates wrong container";
1543 elsif Position
.Node
.Element
= null then
1544 raise Program_Error
with "Node has no element";
1547 pragma Assert
(Vet
(Position
), "bad cursor in function Reference");
1550 C
: List
renames Position
.Container
.all;
1551 B
: Natural renames C
.Busy
;
1552 L
: Natural renames C
.Lock
;
1554 return R
: constant Reference_Type
:=
1555 (Element
=> Position
.Node
.Element
.all'Access,
1556 Control
=> (Controlled
with Position
.Container
))
1565 ---------------------
1566 -- Replace_Element --
1567 ---------------------
1569 procedure Replace_Element
1570 (Container
: in out List
;
1572 New_Item
: Element_Type
)
1575 if Position
.Container
= null then
1576 raise Constraint_Error
with "Position cursor has no element";
1578 elsif Position
.Container
/= Container
'Unchecked_Access then
1579 raise Program_Error
with
1580 "Position cursor designates wrong container";
1582 elsif Container
.Lock
> 0 then
1583 raise Program_Error
with
1584 "attempt to tamper with elements (list is locked)";
1586 elsif Position
.Node
.Element
= null then
1587 raise Program_Error
with
1588 "Position cursor has no element";
1591 pragma Assert
(Vet
(Position
), "bad cursor in Replace_Element");
1594 -- The element allocator may need an accessibility check in the
1595 -- case the actual type is class-wide or has access discriminants
1596 -- (see RM 4.8(10.1) and AI12-0035).
1598 pragma Unsuppress
(Accessibility_Check
);
1600 X
: Element_Access
:= Position
.Node
.Element
;
1603 Position
.Node
.Element
:= new Element_Type
'(New_Item);
1607 end Replace_Element;
1609 ----------------------
1610 -- Reverse_Elements --
1611 ----------------------
1613 procedure Reverse_Elements (Container : in out List) is
1614 I : Node_Access := Container.First;
1615 J : Node_Access := Container.Last;
1617 procedure Swap (L, R : Node_Access);
1623 procedure Swap (L, R : Node_Access) is
1624 LN : constant Node_Access := L.Next;
1625 LP : constant Node_Access := L.Prev;
1627 RN : constant Node_Access := R.Next;
1628 RP : constant Node_Access := R.Prev;
1643 pragma Assert (RP = L);
1657 -- Start of processing for Reverse_Elements
1660 if Container.Length <= 1 then
1664 pragma Assert (Container.First.Prev = null);
1665 pragma Assert (Container.Last.Next = null);
1667 if Container.Busy > 0 then
1668 raise Program_Error with
1669 "attempt to tamper with cursors (list is busy)";
1672 Container.First := J;
1673 Container.Last := I;
1675 Swap (L => I, R => J);
1683 Swap (L => J, R => I);
1692 pragma Assert (Container.First.Prev = null);
1693 pragma Assert (Container.Last.Next = null);
1694 end Reverse_Elements;
1700 function Reverse_Find
1702 Item : Element_Type;
1703 Position : Cursor := No_Element) return Cursor
1705 Node : Node_Access := Position.Node;
1709 Node := Container.Last;
1712 if Node.Element = null then
1713 raise Program_Error with "Position cursor has no element";
1715 elsif Position.Container /= Container'Unrestricted_Access then
1716 raise Program_Error with
1717 "Position cursor designates wrong container";
1720 pragma Assert (Vet (Position), "bad cursor in Reverse_Find");
1724 -- Per AI05-0022, the container implementation is required to detect
1725 -- element tampering by a generic actual subprogram.
1728 B : Natural renames Container'Unrestricted_Access.Busy;
1729 L : Natural renames Container'Unrestricted_Access.Lock;
1731 Result : Node_Access;
1738 while Node /= null loop
1739 if Node.Element.all = Item then
1750 if Result = null then
1753 return Cursor'(Container
'Unrestricted_Access, Result
);
1765 ---------------------
1766 -- Reverse_Iterate --
1767 ---------------------
1769 procedure Reverse_Iterate
1771 Process
: not null access procedure (Position
: Cursor
))
1773 C
: List
renames Container
'Unrestricted_Access.all;
1774 B
: Natural renames C
.Busy
;
1776 Node
: Node_Access
:= Container
.Last
;
1782 while Node
/= null loop
1783 Process
(Cursor
'(Container'Unrestricted_Access, Node));
1793 end Reverse_Iterate;
1800 (Target : in out List;
1802 Source : in out List)
1805 if Before.Container /= null then
1806 if Before.Container /= Target'Unrestricted_Access then
1807 raise Program_Error with
1808 "Before cursor designates wrong container";
1810 elsif Before.Node = null or else Before.Node.Element = null then
1811 raise Program_Error with
1812 "Before cursor has no element";
1815 pragma Assert (Vet (Before), "bad cursor in Splice");
1819 if Target'Address = Source'Address or else Source.Length = 0 then
1822 elsif Target.Length > Count_Type'Last - Source.Length then
1823 raise Constraint_Error with "new length exceeds maximum";
1825 elsif Target.Busy > 0 then
1826 raise Program_Error with
1827 "attempt to tamper with cursors of Target (list is busy)";
1829 elsif Source.Busy > 0 then
1830 raise Program_Error with
1831 "attempt to tamper with cursors of Source (list is busy)";
1834 Splice_Internal (Target, Before.Node, Source);
1839 (Container : in out List;
1844 if Before.Container /= null then
1845 if Before.Container /= Container'Unchecked_Access then
1846 raise Program_Error with
1847 "Before cursor designates wrong container";
1849 elsif Before.Node = null or else Before.Node.Element = null then
1850 raise Program_Error with
1851 "Before cursor has no element";
1854 pragma Assert (Vet (Before), "bad Before cursor in Splice");
1858 if Position.Node = null then
1859 raise Constraint_Error with "Position cursor has no element";
1862 if Position.Node.Element = null then
1863 raise Program_Error with "Position cursor has no element";
1866 if Position.Container /= Container'Unrestricted_Access then
1867 raise Program_Error with
1868 "Position cursor designates wrong container";
1871 pragma Assert (Vet (Position), "bad Position cursor in Splice");
1873 if Position.Node = Before.Node
1874 or else Position.Node.Next = Before.Node
1879 pragma Assert (Container.Length >= 2);
1881 if Container.Busy > 0 then
1882 raise Program_Error with
1883 "attempt to tamper with cursors (list is busy)";
1886 if Before.Node = null then
1887 pragma Assert (Position.Node /= Container.Last);
1889 if Position.Node = Container.First then
1890 Container.First := Position.Node.Next;
1891 Container.First.Prev := null;
1893 Position.Node.Prev.Next := Position.Node.Next;
1894 Position.Node.Next.Prev := Position.Node.Prev;
1897 Container.Last.Next := Position.Node;
1898 Position.Node.Prev := Container.Last;
1900 Container.Last := Position.Node;
1901 Container.Last.Next := null;
1906 if Before.Node = Container.First then
1907 pragma Assert (Position.Node /= Container.First);
1909 if Position.Node = Container.Last then
1910 Container.Last := Position.Node.Prev;
1911 Container.Last.Next := null;
1913 Position.Node.Prev.Next := Position.Node.Next;
1914 Position.Node.Next.Prev := Position.Node.Prev;
1917 Container.First.Prev := Position.Node;
1918 Position.Node.Next := Container.First;
1920 Container.First := Position.Node;
1921 Container.First.Prev := null;
1926 if Position.Node = Container.First then
1927 Container.First := Position.Node.Next;
1928 Container.First.Prev := null;
1930 elsif Position.Node = Container.Last then
1931 Container.Last := Position.Node.Prev;
1932 Container.Last.Next := null;
1935 Position.Node.Prev.Next := Position.Node.Next;
1936 Position.Node.Next.Prev := Position.Node.Prev;
1939 Before.Node.Prev.Next := Position.Node;
1940 Position.Node.Prev := Before.Node.Prev;
1942 Before.Node.Prev := Position.Node;
1943 Position.Node.Next := Before.Node;
1945 pragma Assert (Container.First.Prev = null);
1946 pragma Assert (Container.Last.Next = null);
1950 (Target : in out List;
1952 Source : in out List;
1953 Position : in out Cursor)
1956 if Target'Address = Source'Address then
1957 Splice (Target, Before, Position);
1961 if Before.Container /= null then
1962 if Before.Container /= Target'Unrestricted_Access then
1963 raise Program_Error with
1964 "Before cursor designates wrong container";
1967 if Before.Node = null
1968 or else Before.Node.Element = null
1970 raise Program_Error with
1971 "Before cursor has no element";
1974 pragma Assert (Vet (Before), "bad Before cursor in Splice");
1977 if Position.Node = null then
1978 raise Constraint_Error with "Position cursor has no element";
1981 if Position.Node.Element = null then
1982 raise Program_Error with
1983 "Position cursor has no element";
1986 if Position.Container /= Source'Unrestricted_Access then
1987 raise Program_Error with
1988 "Position cursor designates wrong container";
1991 pragma Assert (Vet (Position), "bad Position cursor in Splice");
1993 if Target.Length = Count_Type'Last then
1994 raise Constraint_Error with "Target is full";
1997 if Target.Busy > 0 then
1998 raise Program_Error with
1999 "attempt to tamper with cursors of Target (list is busy)";
2002 if Source.Busy > 0 then
2003 raise Program_Error with
2004 "attempt to tamper with cursors of Source (list is busy)";
2007 Splice_Internal (Target, Before.Node, Source, Position.Node);
2008 Position.Container := Target'Unchecked_Access;
2011 ---------------------
2012 -- Splice_Internal --
2013 ---------------------
2015 procedure Splice_Internal
2016 (Target : in out List;
2017 Before : Node_Access;
2018 Source : in out List)
2021 -- This implements the corresponding Splice operation, after the
2022 -- parameters have been vetted, and corner-cases disposed of.
2024 pragma Assert (Target'Address /= Source'Address);
2025 pragma Assert (Source.Length > 0);
2026 pragma Assert (Source.First /= null);
2027 pragma Assert (Source.First.Prev = null);
2028 pragma Assert (Source.Last /= null);
2029 pragma Assert (Source.Last.Next = null);
2030 pragma Assert (Target.Length <= Count_Type'Last - Source.Length);
2032 if Target.Length = 0 then
2033 pragma Assert (Before = null);
2034 pragma Assert (Target.First = null);
2035 pragma Assert (Target.Last = null);
2037 Target.First := Source.First;
2038 Target.Last := Source.Last;
2040 elsif Before = null then
2041 pragma Assert (Target.Last.Next = null);
2043 Target.Last.Next := Source.First;
2044 Source.First.Prev := Target.Last;
2046 Target.Last := Source.Last;
2048 elsif Before = Target.First then
2049 pragma Assert (Target.First.Prev = null);
2051 Source.Last.Next := Target.First;
2052 Target.First.Prev := Source.Last;
2054 Target.First := Source.First;
2057 pragma Assert (Target.Length >= 2);
2058 Before.Prev.Next := Source.First;
2059 Source.First.Prev := Before.Prev;
2061 Before.Prev := Source.Last;
2062 Source.Last.Next := Before;
2065 Source.First := null;
2066 Source.Last := null;
2068 Target.Length := Target.Length + Source.Length;
2070 end Splice_Internal;
2072 procedure Splice_Internal
2073 (Target : in out List;
2074 Before : Node_Access; -- node of Target
2075 Source : in out List;
2076 Position : Node_Access) -- node of Source
2079 -- This implements the corresponding Splice operation, after the
2080 -- parameters have been vetted.
2082 pragma Assert (Target'Address /= Source'Address);
2083 pragma Assert (Target.Length < Count_Type'Last);
2084 pragma Assert (Source.Length > 0);
2085 pragma Assert (Source.First /= null);
2086 pragma Assert (Source.First.Prev = null);
2087 pragma Assert (Source.Last /= null);
2088 pragma Assert (Source.Last.Next = null);
2089 pragma Assert (Position /= null);
2091 if Position = Source.First then
2092 Source.First := Position.Next;
2094 if Position = Source.Last then
2095 pragma Assert (Source.First = null);
2096 pragma Assert (Source.Length = 1);
2097 Source.Last := null;
2100 Source.First.Prev := null;
2103 elsif Position = Source.Last then
2104 pragma Assert (Source.Length >= 2);
2105 Source.Last := Position.Prev;
2106 Source.Last.Next := null;
2109 pragma Assert (Source.Length >= 3);
2110 Position.Prev.Next := Position.Next;
2111 Position.Next.Prev := Position.Prev;
2114 if Target.Length = 0 then
2115 pragma Assert (Before = null);
2116 pragma Assert (Target.First = null);
2117 pragma Assert (Target.Last = null);
2119 Target.First := Position;
2120 Target.Last := Position;
2122 Target.First.Prev := null;
2123 Target.Last.Next := null;
2125 elsif Before = null then
2126 pragma Assert (Target.Last.Next = null);
2127 Target.Last.Next := Position;
2128 Position.Prev := Target.Last;
2130 Target.Last := Position;
2131 Target.Last.Next := null;
2133 elsif Before = Target.First then
2134 pragma Assert (Target.First.Prev = null);
2135 Target.First.Prev := Position;
2136 Position.Next := Target.First;
2138 Target.First := Position;
2139 Target.First.Prev := null;
2142 pragma Assert (Target.Length >= 2);
2143 Before.Prev.Next := Position;
2144 Position.Prev := Before.Prev;
2146 Before.Prev := Position;
2147 Position.Next := Before;
2150 Target.Length := Target.Length + 1;
2151 Source.Length := Source.Length - 1;
2152 end Splice_Internal;
2159 (Container : in out List;
2163 if I.Node = null then
2164 raise Constraint_Error with "I cursor has no element";
2167 if J.Node = null then
2168 raise Constraint_Error with "J cursor has no element";
2171 if I.Container /= Container'Unchecked_Access then
2172 raise Program_Error with "I cursor designates wrong container";
2175 if J.Container /= Container'Unchecked_Access then
2176 raise Program_Error with "J cursor designates wrong container";
2179 if I.Node = J.Node then
2183 if Container.Lock > 0 then
2184 raise Program_Error with
2185 "attempt to tamper with elements (list is locked)";
2188 pragma Assert (Vet (I), "bad I cursor in Swap");
2189 pragma Assert (Vet (J), "bad J cursor in Swap");
2192 EI_Copy : constant Element_Access := I.Node.Element;
2195 I.Node.Element := J.Node.Element;
2196 J.Node.Element := EI_Copy;
2204 procedure Swap_Links
2205 (Container : in out List;
2209 if I.Node = null then
2210 raise Constraint_Error with "I cursor has no element";
2213 if J.Node = null then
2214 raise Constraint_Error with "J cursor has no element";
2217 if I.Container /= Container'Unrestricted_Access then
2218 raise Program_Error with "I cursor designates wrong container";
2221 if J.Container /= Container'Unrestricted_Access then
2222 raise Program_Error with "J cursor designates wrong container";
2225 if I.Node = J.Node then
2229 if Container.Busy > 0 then
2230 raise Program_Error with
2231 "attempt to tamper with cursors (list is busy)";
2234 pragma Assert (Vet (I), "bad I cursor in Swap_Links");
2235 pragma Assert (Vet (J), "bad J cursor in Swap_Links");
2238 I_Next : constant Cursor := Next (I);
2242 Splice (Container, Before => I, Position => J);
2246 J_Next : constant Cursor := Next (J);
2250 Splice (Container, Before => J, Position => I);
2253 pragma Assert (Container.Length >= 3);
2255 Splice (Container, Before => I_Next, Position => J);
2256 Splice (Container, Before => J_Next, Position => I);
2262 pragma Assert (Container.First.Prev = null);
2263 pragma Assert (Container.Last.Next = null);
2266 --------------------
2267 -- Update_Element --
2268 --------------------
2270 procedure Update_Element
2271 (Container : in out List;
2273 Process : not null access procedure (Element : in out Element_Type))
2276 if Position.Node = null then
2277 raise Constraint_Error with "Position cursor has no element";
2280 if Position.Node.Element = null then
2281 raise Program_Error with
2282 "Position cursor has no element";
2285 if Position.Container /= Container'Unchecked_Access then
2286 raise Program_Error with
2287 "Position cursor designates wrong container";
2290 pragma Assert (Vet (Position), "bad cursor in Update_Element");
2293 B : Natural renames Container.Busy;
2294 L : Natural renames Container.Lock;
2301 Process (Position.Node.Element.all);
2318 function Vet (Position : Cursor) return Boolean is
2320 if Position.Node = null then
2321 return Position.Container = null;
2324 if Position.Container = null then
2328 -- An invariant of a node is that its Previous and Next components can
2329 -- be null, or designate a different node. Also, its element access
2330 -- value must be non-null. Operation Free sets the node access value
2331 -- components of the node to designate the node itself, and the element
2332 -- access value to null, before actually deallocating the node, thus
2333 -- deliberately violating the node invariant. This gives us a simple way
2334 -- to detect a dangling reference to a node.
2336 if Position.Node.Next = Position.Node then
2340 if Position.Node.Prev = Position.Node then
2344 if Position.Node.Element = null then
2348 -- In practice the tests above will detect most instances of a dangling
2349 -- reference. If we get here, it means that the invariants of the
2350 -- designated node are satisfied (they at least appear to be satisfied),
2351 -- so we perform some more tests, to determine whether invariants of the
2352 -- designated list are satisfied too.
2355 L : List renames Position.Container.all;
2358 if L.Length = 0 then
2362 if L.First = null then
2366 if L.Last = null then
2370 if L.First.Prev /= null then
2374 if L.Last.Next /= null then
2378 if Position.Node.Prev = null and then Position.Node /= L.First then
2382 if Position.Node.Next = null and then Position.Node /= L.Last then
2386 if L.Length = 1 then
2387 return L.First = L.Last;
2390 if L.First = L.Last then
2394 if L.First.Next = null then
2398 if L.Last.Prev = null then
2402 if L.First.Next.Prev /= L.First then
2406 if L.Last.Prev.Next /= L.Last then
2410 if L.Length = 2 then
2411 if L.First.Next /= L.Last then
2415 if L.Last.Prev /= L.First then
2422 if L.First.Next = L.Last then
2426 if L.Last.Prev = L.First then
2430 if Position.Node = L.First then
2434 if Position.Node = L.Last then
2438 if Position.Node.Next = null then
2442 if Position.Node.Prev = null then
2446 if Position.Node.Next.Prev /= Position.Node then
2450 if Position.Node.Prev.Next /= Position.Node then
2454 if L.Length = 3 then
2455 if L.First.Next /= Position.Node then
2459 if L.Last.Prev /= Position.Node then
2473 (Stream : not null access Root_Stream_Type'Class;
2476 Node : Node_Access := Item.First;
2479 Count_Type'Base'Write
(Stream
, Item
.Length
);
2481 while Node
/= null loop
2482 Element_Type
'Output (Stream
, Node
.Element
.all);
2488 (Stream
: not null access Root_Stream_Type
'Class;
2492 raise Program_Error
with "attempt to stream list cursor";
2496 (Stream
: not null access Root_Stream_Type
'Class;
2497 Item
: Reference_Type
)
2500 raise Program_Error
with "attempt to stream reference";
2504 (Stream
: not null access Root_Stream_Type
'Class;
2505 Item
: Constant_Reference_Type
)
2508 raise Program_Error
with "attempt to stream reference";
2511 end Ada
.Containers
.Indefinite_Doubly_Linked_Lists
;