PR c++/37276
[official-gcc.git] / gcc / ada / a-cbdlli.adb
blob5db2d58f3d7abfe156cc74c18a406a899f226077
1 ------------------------------------------------------------------------------
2 -- --
3 -- GNAT LIBRARY COMPONENTS --
4 -- --
5 -- ADA.CONTAINERS.BOUNDED_DOUBLY_LINKED_LISTS --
6 -- --
7 -- B o d y --
8 -- --
9 -- Copyright (C) 2004-2012, Free Software Foundation, Inc. --
10 -- --
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. --
17 -- --
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. --
21 -- --
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/>. --
26 -- --
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
38 record
39 Container : List_Access;
40 Node : Count_Type;
41 end record;
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
49 (Object : Iterator;
50 Position : Cursor) return Cursor;
52 overriding function Previous
53 (Object : Iterator;
54 Position : Cursor) return Cursor;
56 -----------------------
57 -- Local Subprograms --
58 -----------------------
60 procedure Allocate
61 (Container : in out List;
62 New_Item : Element_Type;
63 New_Node : out Count_Type);
65 procedure Allocate
66 (Container : in out List;
67 New_Node : out Count_Type);
69 procedure Allocate
70 (Container : in out List;
71 Stream : not null access Root_Stream_Type'Class;
72 New_Node : out Count_Type);
74 procedure Free
75 (Container : in out List;
76 X : Count_Type);
78 procedure Insert_Internal
79 (Container : in out List;
80 Before : Count_Type;
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.
90 ---------
91 -- "=" --
92 ---------
94 function "=" (Left, Right : List) return Boolean is
95 LN : Node_Array renames Left.Nodes;
96 RN : Node_Array renames Right.Nodes;
98 LI, RI : Count_Type;
100 begin
101 if Left'Address = Right'Address then
102 return True;
103 end if;
105 if Left.Length /= Right.Length then
106 return False;
107 end if;
109 LI := Left.First;
110 RI := Right.First;
111 for J in 1 .. Left.Length loop
112 if LN (LI).Element /= RN (RI).Element then
113 return False;
114 end if;
116 LI := LN (LI).Next;
117 RI := RN (RI).Next;
118 end loop;
120 return True;
121 end "=";
123 --------------
124 -- Allocate --
125 --------------
127 procedure Allocate
128 (Container : in out List;
129 New_Item : Element_Type;
130 New_Node : out Count_Type)
132 N : Node_Array renames Container.Nodes;
134 begin
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;
144 else
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
154 -- container state.
156 N (New_Node).Element := New_Item;
157 Container.Free := Container.Free - 1;
158 end if;
159 end Allocate;
161 procedure Allocate
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;
168 begin
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;
178 else
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
188 -- container state.
190 Element_Type'Read (Stream, N (New_Node).Element);
191 Container.Free := Container.Free - 1;
192 end if;
193 end Allocate;
195 procedure Allocate
196 (Container : in out List;
197 New_Node : out Count_Type)
199 N : Node_Array renames Container.Nodes;
201 begin
202 if Container.Free >= 0 then
203 New_Node := Container.Free;
204 Container.Free := N (New_Node).Next;
206 else
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;
212 end if;
213 end Allocate;
215 ------------
216 -- Append --
217 ------------
219 procedure Append
220 (Container : in out List;
221 New_Item : Element_Type;
222 Count : Count_Type := 1)
224 begin
225 Insert (Container, No_Element, New_Item, Count);
226 end Append;
228 ------------
229 -- Assign --
230 ------------
232 procedure Assign (Target : in out List; Source : List) is
233 SN : Node_Array renames Source.Nodes;
234 J : Count_Type;
236 begin
237 if Target'Address = Source'Address then
238 return;
239 end if;
241 if Target.Capacity < Source.Length then
242 raise Capacity_Error -- ???
243 with "Target capacity is less than Source length";
244 end if;
246 Target.Clear;
248 J := Source.First;
249 while J /= 0 loop
250 Target.Append (SN (J).Element);
251 J := SN (J).Next;
252 end loop;
253 end Assign;
255 -----------
256 -- Clear --
257 -----------
259 procedure Clear (Container : in out List) is
260 N : Node_Array renames Container.Nodes;
261 X : Count_Type;
263 begin
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);
269 return;
270 end if;
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)";
280 end if;
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;
291 Free (Container, X);
292 end loop;
294 X := Container.First;
295 pragma Assert (X = Container.Last);
297 Container.First := 0;
298 Container.Last := 0;
299 Container.Length := 0;
301 Free (Container, X);
302 end Clear;
304 ------------------------
305 -- Constant_Reference --
306 ------------------------
308 function Constant_Reference
309 (Container : aliased List;
310 Position : Cursor) return Constant_Reference_Type
312 begin
313 if Position.Container = null then
314 raise Constraint_Error with "Position cursor has no element";
315 end if;
317 if Position.Container /= Container'Unrestricted_Access then
318 raise Program_Error with
319 "Position cursor designates wrong container";
320 end if;
322 pragma Assert (Vet (Position), "bad cursor in Constant_Reference");
324 declare
325 N : Node_Type renames Container.Nodes (Position.Node);
326 begin
327 return (Element => N.Element'Access);
328 end;
329 end Constant_Reference;
331 --------------
332 -- Contains --
333 --------------
335 function Contains
336 (Container : List;
337 Item : Element_Type) return Boolean
339 begin
340 return Find (Container, Item) /= No_Element;
341 end Contains;
343 ----------
344 -- Copy --
345 ----------
347 function Copy (Source : List; Capacity : Count_Type := 0) return List is
348 C : Count_Type;
350 begin
351 if Capacity = 0 then
352 C := Source.Length;
354 elsif Capacity >= Source.Length then
355 C := Capacity;
357 else
358 raise Capacity_Error with "Capacity value too small";
359 end if;
361 return Target : List (Capacity => C) do
362 Assign (Target => Target, Source => Source);
363 end return;
364 end Copy;
366 ------------
367 -- Delete --
368 ------------
370 procedure Delete
371 (Container : in out List;
372 Position : in out Cursor;
373 Count : Count_Type := 1)
375 N : Node_Array renames Container.Nodes;
376 X : Count_Type;
378 begin
379 if Position.Node = 0 then
380 raise Constraint_Error with
381 "Position cursor has no element";
382 end if;
384 if Position.Container /= Container'Unrestricted_Access then
385 raise Program_Error with
386 "Position cursor designates wrong container";
387 end if;
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;
398 return;
399 end if;
401 if Count = 0 then
402 Position := No_Element;
403 return;
404 end if;
406 if Container.Busy > 0 then
407 raise Program_Error with
408 "attempt to tamper with cursors (list is busy)";
409 end if;
411 for Index in 1 .. Count loop
412 pragma Assert (Container.Length >= 2);
414 X := Position.Node;
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;
423 Free (Container, X);
424 return;
425 end if;
427 Position.Node := N (X).Next;
429 N (N (X).Next).Prev := N (X).Prev;
430 N (N (X).Prev).Next := N (X).Next;
432 Free (Container, X);
433 end loop;
435 Position := No_Element;
436 end Delete;
438 ------------------
439 -- Delete_First --
440 ------------------
442 procedure Delete_First
443 (Container : in out List;
444 Count : Count_Type := 1)
446 N : Node_Array renames Container.Nodes;
447 X : Count_Type;
449 begin
450 if Count >= Container.Length then
451 Clear (Container);
452 return;
453 end if;
455 if Count = 0 then
456 return;
457 end if;
459 if Container.Busy > 0 then
460 raise Program_Error with
461 "attempt to tamper with cursors (list is busy)";
462 end if;
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;
473 Free (Container, X);
474 end loop;
475 end Delete_First;
477 -----------------
478 -- Delete_Last --
479 -----------------
481 procedure Delete_Last
482 (Container : in out List;
483 Count : Count_Type := 1)
485 N : Node_Array renames Container.Nodes;
486 X : Count_Type;
488 begin
489 if Count >= Container.Length then
490 Clear (Container);
491 return;
492 end if;
494 if Count = 0 then
495 return;
496 end if;
498 if Container.Busy > 0 then
499 raise Program_Error with
500 "attempt to tamper with cursors (list is busy)";
501 end if;
503 for I in 1 .. Count loop
504 X := Container.Last;
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;
512 Free (Container, X);
513 end loop;
514 end Delete_Last;
516 -------------
517 -- Element --
518 -------------
520 function Element (Position : Cursor) return Element_Type is
521 begin
522 if Position.Node = 0 then
523 raise Constraint_Error with
524 "Position cursor has no element";
525 end if;
527 pragma Assert (Vet (Position), "bad cursor in Element");
529 return Position.Container.Nodes (Position.Node).Element;
530 end Element;
532 --------------
533 -- Finalize --
534 --------------
536 procedure Finalize (Object : in out Iterator) is
537 begin
538 if Object.Container /= null then
539 declare
540 B : Natural renames Object.Container.all.Busy;
542 begin
543 B := B - 1;
544 end;
545 end if;
546 end Finalize;
548 ----------
549 -- Find --
550 ----------
552 function Find
553 (Container : List;
554 Item : Element_Type;
555 Position : Cursor := No_Element) return Cursor
557 Nodes : Node_Array renames Container.Nodes;
558 Node : Count_Type := Position.Node;
560 begin
561 if Node = 0 then
562 Node := Container.First;
564 else
565 if Position.Container /= Container'Unrestricted_Access then
566 raise Program_Error with
567 "Position cursor designates wrong container";
568 end if;
570 pragma Assert (Vet (Position), "bad cursor in Find");
571 end if;
573 while Node /= 0 loop
574 if Nodes (Node).Element = Item then
575 return Cursor'(Container'Unrestricted_Access, Node);
576 end if;
578 Node := Nodes (Node).Next;
579 end loop;
581 return No_Element;
582 end Find;
584 -----------
585 -- First --
586 -----------
588 function First (Container : List) return Cursor is
589 begin
590 if Container.First = 0 then
591 return No_Element;
592 end if;
594 return Cursor'(Container'Unrestricted_Access, Container.First);
595 end First;
597 function First (Object : Iterator) return Cursor is
598 begin
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);
614 else
615 return Cursor'(Object.Container, Object.Node);
616 end if;
617 end First;
619 -------------------
620 -- First_Element --
621 -------------------
623 function First_Element (Container : List) return Element_Type is
624 begin
625 if Container.First = 0 then
626 raise Constraint_Error with "list is empty";
627 end if;
629 return Container.Nodes (Container.First).Element;
630 end First_Element;
632 ----------
633 -- Free --
634 ----------
636 procedure Free
637 (Container : in out List;
638 X : Count_Type)
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
646 begin
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.
678 -- ???
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;
700 Container.Free := X;
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.
711 N (X).Next := 0;
712 Container.Free := Container.Free + 1;
714 else
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.
721 -- ???
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
730 Container.Free := 0;
732 else
733 for I in Container.Free .. Container.Capacity - 1 loop
734 N (I).Next := I + 1;
735 end loop;
737 N (Container.Capacity).Next := 0;
738 end if;
740 N (X).Next := Container.Free;
741 Container.Free := X;
742 end if;
743 end Free;
745 ---------------------
746 -- Generic_Sorting --
747 ---------------------
749 package body Generic_Sorting is
751 ---------------
752 -- Is_Sorted --
753 ---------------
755 function Is_Sorted (Container : List) return Boolean is
756 Nodes : Node_Array renames Container.Nodes;
757 Node : Count_Type := Container.First;
759 begin
760 for J in 2 .. Container.Length loop
761 if Nodes (Nodes (Node).Next).Element < Nodes (Node).Element then
762 return False;
763 end if;
765 Node := Nodes (Node).Next;
766 end loop;
768 return True;
769 end Is_Sorted;
771 -----------
772 -- Merge --
773 -----------
775 procedure Merge
776 (Target : in out List;
777 Source : in out List)
779 LN : Node_Array renames Target.Nodes;
780 RN : Node_Array renames Source.Nodes;
781 LI, RI : Cursor;
783 begin
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
794 return;
795 end if;
797 if Target'Address = Source'Address then
798 raise Program_Error with
799 "Target and Source denote same non-empty container";
800 end if;
802 if Target.Busy > 0 then
803 raise Program_Error with
804 "attempt to tamper with cursors of Target (list is busy)";
805 end if;
807 if Source.Busy > 0 then
808 raise Program_Error with
809 "attempt to tamper with cursors of Source (list is busy)";
810 end if;
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));
819 if LI.Node = 0 then
820 Splice (Target, No_Element, Source);
821 return;
822 end if;
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
829 declare
830 RJ : Cursor := RI;
831 begin
832 RI.Node := RN (RI.Node).Next;
833 Splice (Target, LI, Source, RJ);
834 end;
836 else
837 LI.Node := LN (LI.Node).Next;
838 end if;
839 end loop;
840 end Merge;
842 ----------
843 -- Sort --
844 ----------
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???
855 ---------------
856 -- Partition --
857 ---------------
859 procedure Partition (Pivot, Back : Count_Type) is
860 Node : Count_Type;
862 begin
863 Node := N (Pivot).Next;
864 while Node /= Back loop
865 if N (Node).Element < N (Pivot).Element then
866 declare
867 Prev : constant Count_Type := N (Node).Prev;
868 Next : constant Count_Type := N (Node).Next;
870 begin
871 N (Prev).Next := Next;
873 if Next = 0 then
874 Container.Last := Prev;
875 else
876 N (Next).Prev := Prev;
877 end if;
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;
886 else
887 N (N (Node).Prev).Next := Node;
888 end if;
890 Node := Next;
891 end;
893 else
894 Node := N (Node).Next;
895 end if;
896 end loop;
897 end Partition;
899 ----------
900 -- Sort --
901 ----------
903 procedure Sort (Front, Back : Count_Type) is
904 Pivot : constant Count_Type :=
905 (if Front = 0 then Container.First else N (Front).Next);
906 begin
907 if Pivot /= Back then
908 Partition (Pivot, Back);
909 Sort (Front, Pivot);
910 Sort (Pivot, Back);
911 end if;
912 end Sort;
914 -- Start of processing for Sort
916 begin
917 if Container.Length <= 1 then
918 return;
919 end if;
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)";
927 end if;
929 Sort (Front => 0, Back => 0);
931 pragma Assert (N (Container.First).Prev = 0);
932 pragma Assert (N (Container.Last).Next = 0);
933 end Sort;
935 end Generic_Sorting;
937 -----------------
938 -- Has_Element --
939 -----------------
941 function Has_Element (Position : Cursor) return Boolean is
942 begin
943 pragma Assert (Vet (Position), "bad cursor in Has_Element");
944 return Position.Node /= 0;
945 end Has_Element;
947 ------------
948 -- Insert --
949 ------------
951 procedure Insert
952 (Container : in out List;
953 Before : Cursor;
954 New_Item : Element_Type;
955 Position : out Cursor;
956 Count : Count_Type := 1)
958 New_Node : Count_Type;
960 begin
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";
965 end if;
967 pragma Assert (Vet (Before), "bad cursor in Insert");
968 end if;
970 if Count = 0 then
971 Position := Before;
972 return;
973 end if;
975 if Container.Length > Container.Capacity - Count then
976 raise Constraint_Error with "new length exceeds capacity";
977 end if;
979 if Container.Busy > 0 then
980 raise Program_Error with
981 "attempt to tamper with cursors (list is busy)";
982 end if;
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);
991 end loop;
992 end Insert;
994 procedure Insert
995 (Container : in out List;
996 Before : Cursor;
997 New_Item : Element_Type;
998 Count : Count_Type := 1)
1000 Position : Cursor;
1001 pragma Unreferenced (Position);
1002 begin
1003 Insert (Container, Before, New_Item, Position, Count);
1004 end Insert;
1006 procedure Insert
1007 (Container : in out List;
1008 Before : Cursor;
1009 Position : out Cursor;
1010 Count : Count_Type := 1)
1012 New_Node : Count_Type;
1014 begin
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";
1019 end if;
1021 pragma Assert (Vet (Before), "bad cursor in Insert");
1022 end if;
1024 if Count = 0 then
1025 Position := Before;
1026 return;
1027 end if;
1029 if Container.Length > Container.Capacity - Count then
1030 raise Constraint_Error with "new length exceeds capacity";
1031 end if;
1033 if Container.Busy > 0 then
1034 raise Program_Error with
1035 "attempt to tamper with cursors (list is busy)";
1036 end if;
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);
1045 end loop;
1046 end Insert;
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;
1059 begin
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;
1093 else
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;
1102 end if;
1104 Container.Length := Container.Length + 1;
1105 end Insert_Internal;
1107 --------------
1108 -- Is_Empty --
1109 --------------
1111 function Is_Empty (Container : List) return Boolean is
1112 begin
1113 return Container.Length = 0;
1114 end Is_Empty;
1116 -------------
1117 -- Iterate --
1118 -------------
1120 procedure Iterate
1121 (Container : List;
1122 Process : not null access procedure (Position : Cursor))
1124 B : Natural renames Container'Unrestricted_Access.all.Busy;
1125 Node : Count_Type := Container.First;
1127 begin
1128 B := B + 1;
1130 begin
1131 while Node /= 0 loop
1132 Process (Cursor'(Container'Unrestricted_Access, Node));
1133 Node := Container.Nodes (Node).Next;
1134 end loop;
1136 exception
1137 when others =>
1138 B := B - 1;
1139 raise;
1140 end;
1142 B := B - 1;
1143 end Iterate;
1145 function Iterate
1146 (Container : List)
1147 return List_Iterator_Interfaces.Reversible_Iterator'Class
1149 B : Natural renames Container'Unrestricted_Access.all.Busy;
1151 begin
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,
1165 Node => 0)
1167 B := B + 1;
1168 end return;
1169 end Iterate;
1171 function Iterate
1172 (Container : List;
1173 Start : Cursor)
1174 return List_Iterator_Interfaces.Reversible_Iterator'class
1176 B : Natural renames Container'Unrestricted_Access.all.Busy;
1178 begin
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";
1193 end if;
1195 if Start.Container /= Container'Unrestricted_Access then
1196 raise Program_Error with
1197 "Start cursor of Iterate designates wrong list";
1198 end if;
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,
1214 Node => Start.Node)
1216 B := B + 1;
1217 end return;
1218 end Iterate;
1220 ----------
1221 -- Last --
1222 ----------
1224 function Last (Container : List) return Cursor is
1225 begin
1226 if Container.Last = 0 then
1227 return No_Element;
1228 end if;
1230 return Cursor'(Container'Unrestricted_Access, Container.Last);
1231 end Last;
1233 function Last (Object : Iterator) return Cursor is
1234 begin
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);
1250 else
1251 return Cursor'(Object.Container, Object.Node);
1252 end if;
1253 end Last;
1255 ------------------
1256 -- Last_Element --
1257 ------------------
1259 function Last_Element (Container : List) return Element_Type is
1260 begin
1261 if Container.Last = 0 then
1262 raise Constraint_Error with "list is empty";
1263 end if;
1265 return Container.Nodes (Container.Last).Element;
1266 end Last_Element;
1268 ------------
1269 -- Length --
1270 ------------
1272 function Length (Container : List) return Count_Type is
1273 begin
1274 return Container.Length;
1275 end Length;
1277 ----------
1278 -- Move --
1279 ----------
1281 procedure Move
1282 (Target : in out List;
1283 Source : in out List)
1285 N : Node_Array renames Source.Nodes;
1286 X : Count_Type;
1288 begin
1289 if Target'Address = Source'Address then
1290 return;
1291 end if;
1293 if Target.Capacity < Source.Length then
1294 raise Capacity_Error with "Source length exceeds Target capacity";
1295 end if;
1297 if Source.Busy > 0 then
1298 raise Program_Error with
1299 "attempt to tamper with cursors of Source (list is busy)";
1300 end if;
1302 -- Clear target, note that this checks busy bits of Target
1304 Clear (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
1314 X := Source.First;
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.
1338 Free (Source, X);
1339 end loop;
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
1349 X := Source.First;
1350 Append (Target, N (X).Element);
1352 -- Unlink node of Source
1354 Source.First := 0;
1355 Source.Last := 0;
1356 Source.Length := 0;
1358 -- Return the unlinked node to the free store
1360 Free (Source, X);
1361 end if;
1362 end Move;
1364 ----------
1365 -- Next --
1366 ----------
1368 procedure Next (Position : in out Cursor) is
1369 begin
1370 Position := Next (Position);
1371 end Next;
1373 function Next (Position : Cursor) return Cursor is
1374 begin
1375 if Position.Node = 0 then
1376 return No_Element;
1377 end if;
1379 pragma Assert (Vet (Position), "bad cursor in Next");
1381 declare
1382 Nodes : Node_Array renames Position.Container.Nodes;
1383 Node : constant Count_Type := Nodes (Position.Node).Next;
1385 begin
1386 if Node = 0 then
1387 return No_Element;
1388 end if;
1390 return Cursor'(Position.Container, Node);
1391 end;
1392 end Next;
1394 function Next
1395 (Object : Iterator;
1396 Position : Cursor) return Cursor
1398 begin
1399 if Position.Container = null then
1400 return No_Element;
1401 end if;
1403 if Position.Container /= Object.Container then
1404 raise Program_Error with
1405 "Position cursor of Next designates wrong list";
1406 end if;
1408 return Next (Position);
1409 end Next;
1411 -------------
1412 -- Prepend --
1413 -------------
1415 procedure Prepend
1416 (Container : in out List;
1417 New_Item : Element_Type;
1418 Count : Count_Type := 1)
1420 begin
1421 Insert (Container, First (Container), New_Item, Count);
1422 end Prepend;
1424 --------------
1425 -- Previous --
1426 --------------
1428 procedure Previous (Position : in out Cursor) is
1429 begin
1430 Position := Previous (Position);
1431 end Previous;
1433 function Previous (Position : Cursor) return Cursor is
1434 begin
1435 if Position.Node = 0 then
1436 return No_Element;
1437 end if;
1439 pragma Assert (Vet (Position), "bad cursor in Previous");
1441 declare
1442 Nodes : Node_Array renames Position.Container.Nodes;
1443 Node : constant Count_Type := Nodes (Position.Node).Prev;
1444 begin
1445 if Node = 0 then
1446 return No_Element;
1447 end if;
1449 return Cursor'(Position.Container, Node);
1450 end;
1451 end Previous;
1453 function Previous
1454 (Object : Iterator;
1455 Position : Cursor) return Cursor
1457 begin
1458 if Position.Container = null then
1459 return No_Element;
1460 end if;
1462 if Position.Container /= Object.Container then
1463 raise Program_Error with
1464 "Position cursor of Previous designates wrong list";
1465 end if;
1467 return Previous (Position);
1468 end Previous;
1470 -------------------
1471 -- Query_Element --
1472 -------------------
1474 procedure Query_Element
1475 (Position : Cursor;
1476 Process : not null access procedure (Element : Element_Type))
1478 begin
1479 if Position.Node = 0 then
1480 raise Constraint_Error with
1481 "Position cursor has no element";
1482 end if;
1484 pragma Assert (Vet (Position), "bad cursor in Query_Element");
1486 declare
1487 C : List renames Position.Container.all'Unrestricted_Access.all;
1488 B : Natural renames C.Busy;
1489 L : Natural renames C.Lock;
1491 begin
1492 B := B + 1;
1493 L := L + 1;
1495 declare
1496 N : Node_Type renames C.Nodes (Position.Node);
1497 begin
1498 Process (N.Element);
1499 exception
1500 when others =>
1501 L := L - 1;
1502 B := B - 1;
1503 raise;
1504 end;
1506 L := L - 1;
1507 B := B - 1;
1508 end;
1509 end Query_Element;
1511 ----------
1512 -- Read --
1513 ----------
1515 procedure Read
1516 (Stream : not null access Root_Stream_Type'Class;
1517 Item : out List)
1519 N : Count_Type'Base;
1520 X : Count_Type;
1522 begin
1523 Clear (Item);
1524 Count_Type'Base'Read (Stream, N);
1526 if N < 0 then
1527 raise Program_Error with "bad list length (corrupt stream)";
1528 end if;
1530 if N = 0 then
1531 return;
1532 end if;
1534 if N > Item.Capacity then
1535 raise Constraint_Error with "length exceeds capacity";
1536 end if;
1538 for Idx in 1 .. N loop
1539 Allocate (Item, Stream, New_Node => X);
1540 Insert_Internal (Item, Before => 0, New_Node => X);
1541 end loop;
1542 end Read;
1544 procedure Read
1545 (Stream : not null access Root_Stream_Type'Class;
1546 Item : out Cursor)
1548 begin
1549 raise Program_Error with "attempt to stream list cursor";
1550 end Read;
1552 procedure Read
1553 (Stream : not null access Root_Stream_Type'Class;
1554 Item : out Reference_Type)
1556 begin
1557 raise Program_Error with "attempt to stream reference";
1558 end Read;
1560 procedure Read
1561 (Stream : not null access Root_Stream_Type'Class;
1562 Item : out Constant_Reference_Type)
1564 begin
1565 raise Program_Error with "attempt to stream reference";
1566 end Read;
1568 ---------------
1569 -- Reference --
1570 ---------------
1572 function Reference
1573 (Container : aliased in out List;
1574 Position : Cursor) return Reference_Type
1576 begin
1577 if Position.Container = null then
1578 raise Constraint_Error with "Position cursor has no element";
1579 end if;
1581 if Position.Container /= Container'Unrestricted_Access then
1582 raise Program_Error with
1583 "Position cursor designates wrong container";
1584 end if;
1586 pragma Assert (Vet (Position), "bad cursor in function Reference");
1588 declare
1589 N : Node_Type renames Container.Nodes (Position.Node);
1590 begin
1591 return (Element => N.Element'Access);
1592 end;
1593 end Reference;
1595 ---------------------
1596 -- Replace_Element --
1597 ---------------------
1599 procedure Replace_Element
1600 (Container : in out List;
1601 Position : Cursor;
1602 New_Item : Element_Type)
1604 begin
1605 if Position.Container = null then
1606 raise Constraint_Error with "Position cursor has no element";
1607 end if;
1609 if Position.Container /= Container'Unchecked_Access then
1610 raise Program_Error with
1611 "Position cursor designates wrong container";
1612 end if;
1614 if Container.Lock > 0 then
1615 raise Program_Error with
1616 "attempt to tamper with elements (list is locked)";
1617 end if;
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);
1635 ----------
1636 -- Swap --
1637 ----------
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;
1646 begin
1647 if LP /= 0 then
1648 N (LP).Next := R;
1649 end if;
1651 if RN /= 0 then
1652 N (RN).Prev := L;
1653 end if;
1655 N (L).Next := RN;
1656 N (R).Prev := LP;
1658 if LN = R then
1659 pragma Assert (RP = L);
1661 N (L).Prev := R;
1662 N (R).Next := L;
1664 else
1665 N (L).Prev := RP;
1666 N (RP).Next := L;
1668 N (R).Next := LN;
1669 N (LN).Prev := R;
1670 end if;
1671 end Swap;
1673 -- Start of processing for Reverse_Elements
1675 begin
1676 if Container.Length <= 1 then
1677 return;
1678 end if;
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)";
1686 end if;
1688 Container.First := J;
1689 Container.Last := I;
1690 loop
1691 Swap (L => I, R => J);
1693 J := N (J).Next;
1694 exit when I = J;
1696 I := N (I).Prev;
1697 exit when I = J;
1699 Swap (L => J, R => I);
1701 I := N (I).Next;
1702 exit when I = J;
1704 J := N (J).Prev;
1705 exit when I = J;
1706 end loop;
1708 pragma Assert (N (Container.First).Prev = 0);
1709 pragma Assert (N (Container.Last).Next = 0);
1710 end Reverse_Elements;
1712 ------------------
1713 -- Reverse_Find --
1714 ------------------
1716 function Reverse_Find
1717 (Container : List;
1718 Item : Element_Type;
1719 Position : Cursor := No_Element) return Cursor
1721 Node : Count_Type := Position.Node;
1723 begin
1724 if Node = 0 then
1725 Node := Container.Last;
1727 else
1728 if Position.Container /= Container'Unrestricted_Access then
1729 raise Program_Error with
1730 "Position cursor designates wrong container";
1731 end if;
1733 pragma Assert (Vet (Position), "bad cursor in Reverse_Find");
1734 end if;
1736 while Node /= 0 loop
1737 if Container.Nodes (Node).Element = Item then
1738 return Cursor'(Container'Unrestricted_Access, Node);
1739 end if;
1741 Node := Container.Nodes (Node).Prev;
1742 end loop;
1744 return No_Element;
1745 end Reverse_Find;
1747 ---------------------
1748 -- Reverse_Iterate --
1749 ---------------------
1751 procedure Reverse_Iterate
1752 (Container : List;
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;
1760 begin
1761 B := B + 1;
1763 begin
1764 while Node /= 0 loop
1765 Process (Cursor'(Container'Unrestricted_Access, Node));
1766 Node := Container.Nodes (Node).Prev;
1767 end loop;
1769 exception
1770 when others =>
1771 B := B - 1;
1772 raise;
1773 end;
1775 B := B - 1;
1776 end Reverse_Iterate;
1778 ------------
1779 -- Splice --
1780 ------------
1782 procedure Splice
1783 (Target : in out List;
1784 Before : Cursor;
1785 Source : in out List)
1787 begin
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";
1792 end if;
1794 pragma Assert (Vet (Before), "bad cursor in Splice");
1795 end if;
1797 if Target'Address = Source'Address
1798 or else Source.Length = 0
1799 then
1800 return;
1801 end if;
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";
1808 end if;
1810 if Target.Length + Source.Length > Target.Capacity then
1811 raise Capacity_Error with "new length exceeds target capacity";
1812 end if;
1814 if Target.Busy > 0 then
1815 raise Program_Error with
1816 "attempt to tamper with cursors of Target (list is busy)";
1817 end if;
1819 if Source.Busy > 0 then
1820 raise Program_Error with
1821 "attempt to tamper with cursors of Source (list is busy)";
1822 end if;
1824 while not Is_Empty (Source) loop
1825 Insert (Target, Before, Source.Nodes (Source.First).Element);
1826 Delete_First (Source);
1827 end loop;
1828 end Splice;
1830 procedure Splice
1831 (Container : in out List;
1832 Before : Cursor;
1833 Position : Cursor)
1835 N : Node_Array renames Container.Nodes;
1837 begin
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";
1842 end if;
1844 pragma Assert (Vet (Before), "bad Before cursor in Splice");
1845 end if;
1847 if Position.Node = 0 then
1848 raise Constraint_Error with "Position cursor has no element";
1849 end if;
1851 if Position.Container /= Container'Unrestricted_Access then
1852 raise Program_Error with
1853 "Position cursor designates wrong container";
1854 end if;
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
1860 then
1861 return;
1862 end if;
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)";
1869 end if;
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;
1877 else
1878 N (N (Position.Node).Prev).Next := N (Position.Node).Next;
1879 N (N (Position.Node).Next).Prev := N (Position.Node).Prev;
1880 end if;
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;
1888 return;
1889 end if;
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;
1897 else
1898 N (N (Position.Node).Prev).Next := N (Position.Node).Next;
1899 N (N (Position.Node).Next).Prev := N (Position.Node).Prev;
1900 end if;
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;
1908 return;
1909 end if;
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;
1919 else
1920 N (N (Position.Node).Prev).Next := N (Position.Node).Next;
1921 N (N (Position.Node).Next).Prev := N (Position.Node).Prev;
1922 end if;
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);
1932 end Splice;
1934 procedure Splice
1935 (Target : in out List;
1936 Before : Cursor;
1937 Source : in out List;
1938 Position : in out Cursor)
1940 Target_Position : Cursor;
1942 begin
1943 if Target'Address = Source'Address then
1944 Splice (Target, Before, Position);
1945 return;
1946 end if;
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";
1952 end if;
1954 pragma Assert (Vet (Before), "bad Before cursor in Splice");
1955 end if;
1957 if Position.Node = 0 then
1958 raise Constraint_Error with "Position cursor has no element";
1959 end if;
1961 if Position.Container /= Source'Unrestricted_Access then
1962 raise Program_Error with
1963 "Position cursor designates wrong container";
1964 end if;
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";
1970 end if;
1972 if Target.Busy > 0 then
1973 raise Program_Error with
1974 "attempt to tamper with cursors of Target (list is busy)";
1975 end if;
1977 if Source.Busy > 0 then
1978 raise Program_Error with
1979 "attempt to tamper with cursors of Source (list is busy)";
1980 end if;
1982 Insert
1983 (Container => Target,
1984 Before => Before,
1985 New_Item => Source.Nodes (Position.Node).Element,
1986 Position => Target_Position);
1988 Delete (Source, Position);
1989 Position := Target_Position;
1990 end Splice;
1992 ----------
1993 -- Swap --
1994 ----------
1996 procedure Swap
1997 (Container : in out List;
1998 I, J : Cursor)
2000 begin
2001 if I.Node = 0 then
2002 raise Constraint_Error with "I cursor has no element";
2003 end if;
2005 if J.Node = 0 then
2006 raise Constraint_Error with "J cursor has no element";
2007 end if;
2009 if I.Container /= Container'Unchecked_Access then
2010 raise Program_Error with "I cursor designates wrong container";
2011 end if;
2013 if J.Container /= Container'Unchecked_Access then
2014 raise Program_Error with "J cursor designates wrong container";
2015 end if;
2017 if I.Node = J.Node then
2018 return;
2019 end if;
2021 if Container.Lock > 0 then
2022 raise Program_Error with
2023 "attempt to tamper with elements (list is locked)";
2024 end if;
2026 pragma Assert (Vet (I), "bad I cursor in Swap");
2027 pragma Assert (Vet (J), "bad J cursor in Swap");
2029 declare
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;
2035 begin
2036 EI := EJ;
2037 EJ := EI_Copy;
2038 end;
2039 end Swap;
2041 ----------------
2042 -- Swap_Links --
2043 ----------------
2045 procedure Swap_Links
2046 (Container : in out List;
2047 I, J : Cursor)
2049 begin
2050 if I.Node = 0 then
2051 raise Constraint_Error with "I cursor has no element";
2052 end if;
2054 if J.Node = 0 then
2055 raise Constraint_Error with "J cursor has no element";
2056 end if;
2058 if I.Container /= Container'Unrestricted_Access then
2059 raise Program_Error with "I cursor designates wrong container";
2060 end if;
2062 if J.Container /= Container'Unrestricted_Access then
2063 raise Program_Error with "J cursor designates wrong container";
2064 end if;
2066 if I.Node = J.Node then
2067 return;
2068 end if;
2070 if Container.Busy > 0 then
2071 raise Program_Error with
2072 "attempt to tamper with cursors (list is busy)";
2073 end if;
2075 pragma Assert (Vet (I), "bad I cursor in Swap_Links");
2076 pragma Assert (Vet (J), "bad J cursor in Swap_Links");
2078 declare
2079 I_Next : constant Cursor := Next (I);
2081 begin
2082 if I_Next = J then
2083 Splice (Container, Before => I, Position => J);
2085 else
2086 declare
2087 J_Next : constant Cursor := Next (J);
2089 begin
2090 if J_Next = I then
2091 Splice (Container, Before => J, Position => I);
2093 else
2094 pragma Assert (Container.Length >= 3);
2096 Splice (Container, Before => I_Next, Position => J);
2097 Splice (Container, Before => J_Next, Position => I);
2098 end if;
2099 end;
2100 end if;
2101 end;
2102 end Swap_Links;
2104 --------------------
2105 -- Update_Element --
2106 --------------------
2108 procedure Update_Element
2109 (Container : in out List;
2110 Position : Cursor;
2111 Process : not null access procedure (Element : in out Element_Type))
2113 begin
2114 if Position.Node = 0 then
2115 raise Constraint_Error with "Position cursor has no element";
2116 end if;
2118 if Position.Container /= Container'Unchecked_Access then
2119 raise Program_Error with
2120 "Position cursor designates wrong container";
2121 end if;
2123 pragma Assert (Vet (Position), "bad cursor in Update_Element");
2125 declare
2126 B : Natural renames Container.Busy;
2127 L : Natural renames Container.Lock;
2129 begin
2130 B := B + 1;
2131 L := L + 1;
2133 declare
2134 N : Node_Type renames Container.Nodes (Position.Node);
2135 begin
2136 Process (N.Element);
2137 exception
2138 when others =>
2139 L := L - 1;
2140 B := B - 1;
2141 raise;
2142 end;
2144 L := L - 1;
2145 B := B - 1;
2146 end;
2147 end Update_Element;
2149 ---------
2150 -- Vet --
2151 ---------
2153 function Vet (Position : Cursor) return Boolean is
2154 begin
2155 if Position.Node = 0 then
2156 return Position.Container = null;
2157 end if;
2159 if Position.Container = null then
2160 return False;
2161 end if;
2163 declare
2164 L : List renames Position.Container.all;
2165 N : Node_Array renames L.Nodes;
2167 begin
2168 if L.Length = 0 then
2169 return False;
2170 end if;
2172 if L.First = 0 or L.First > L.Capacity then
2173 return False;
2174 end if;
2176 if L.Last = 0 or L.Last > L.Capacity then
2177 return False;
2178 end if;
2180 if N (L.First).Prev /= 0 then
2181 return False;
2182 end if;
2184 if N (L.Last).Next /= 0 then
2185 return False;
2186 end if;
2188 if Position.Node > L.Capacity then
2189 return False;
2190 end if;
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
2201 return False;
2202 end if;
2204 if N (Position.Node).Prev > L.Capacity then
2205 return False;
2206 end if;
2208 if N (Position.Node).Next = Position.Node then
2209 return False;
2210 end if;
2212 if N (Position.Node).Prev = Position.Node then
2213 return False;
2214 end if;
2216 if N (Position.Node).Prev = 0
2217 and then Position.Node /= L.First
2218 then
2219 return False;
2220 end if;
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
2227 then
2228 return False;
2229 end if;
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;
2236 end if;
2238 if L.First = L.Last then
2239 return False;
2240 end if;
2242 if N (L.First).Next = 0 then
2243 return False;
2244 end if;
2246 if N (L.Last).Prev = 0 then
2247 return False;
2248 end if;
2250 if N (N (L.First).Next).Prev /= L.First then
2251 return False;
2252 end if;
2254 if N (N (L.Last).Prev).Next /= L.Last then
2255 return False;
2256 end if;
2258 if L.Length = 2 then
2259 if N (L.First).Next /= L.Last then
2260 return False;
2261 end if;
2263 if N (L.Last).Prev /= L.First then
2264 return False;
2265 end if;
2267 return True;
2268 end if;
2270 if N (L.First).Next = L.Last then
2271 return False;
2272 end if;
2274 if N (L.Last).Prev = L.First then
2275 return False;
2276 end if;
2278 -- Eliminate earlier possibility
2280 if Position.Node = L.First then
2281 return True;
2282 end if;
2284 pragma Assert (N (Position.Node).Prev /= 0);
2286 -- ELiminate another possibility
2288 if Position.Node = L.Last then
2289 return True;
2290 end if;
2292 pragma Assert (N (Position.Node).Next /= 0);
2294 if N (N (Position.Node).Next).Prev /= Position.Node then
2295 return False;
2296 end if;
2298 if N (N (Position.Node).Prev).Next /= Position.Node then
2299 return False;
2300 end if;
2302 if L.Length = 3 then
2303 if N (L.First).Next /= Position.Node then
2304 return False;
2305 end if;
2307 if N (L.Last).Prev /= Position.Node then
2308 return False;
2309 end if;
2310 end if;
2312 return True;
2313 end;
2314 end Vet;
2316 -----------
2317 -- Write --
2318 -----------
2320 procedure Write
2321 (Stream : not null access Root_Stream_Type'Class;
2322 Item : List)
2324 Node : Count_Type;
2326 begin
2327 Count_Type'Base'Write (Stream, Item.Length);
2329 Node := Item.First;
2330 while Node /= 0 loop
2331 Element_Type'Write (Stream, Item.Nodes (Node).Element);
2332 Node := Item.Nodes (Node).Next;
2333 end loop;
2334 end Write;
2336 procedure Write
2337 (Stream : not null access Root_Stream_Type'Class;
2338 Item : Cursor)
2340 begin
2341 raise Program_Error with "attempt to stream list cursor";
2342 end Write;
2344 procedure Write
2345 (Stream : not null access Root_Stream_Type'Class;
2346 Item : Reference_Type)
2348 begin
2349 raise Program_Error with "attempt to stream reference";
2350 end Write;
2352 procedure Write
2353 (Stream : not null access Root_Stream_Type'Class;
2354 Item : Constant_Reference_Type)
2356 begin
2357 raise Program_Error with "attempt to stream reference";
2358 end Write;
2360 end Ada.Containers.Bounded_Doubly_Linked_Lists;