2015-09-28 Paul Thomas <pault@gcc.gnu.org>
[official-gcc.git] / gcc / ada / a-cbdlli.adb
blobc4e4945d702ab74626628a2bbfe983171dceb5a3
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-2014, 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 System; use type System.Address;
32 package body Ada.Containers.Bounded_Doubly_Linked_Lists is
34 pragma Annotate (CodePeer, Skip_Analysis);
36 -----------------------
37 -- Local Subprograms --
38 -----------------------
40 procedure Allocate
41 (Container : in out List;
42 New_Item : Element_Type;
43 New_Node : out Count_Type);
45 procedure Allocate
46 (Container : in out List;
47 Stream : not null access Root_Stream_Type'Class;
48 New_Node : out Count_Type);
50 procedure Free
51 (Container : in out List;
52 X : Count_Type);
54 procedure Insert_Internal
55 (Container : in out List;
56 Before : Count_Type;
57 New_Node : Count_Type);
59 procedure Splice_Internal
60 (Target : in out List;
61 Before : Count_Type;
62 Source : in out List);
64 procedure Splice_Internal
65 (Target : in out List;
66 Before : Count_Type;
67 Source : in out List;
68 Src_Pos : Count_Type;
69 Tgt_Pos : out Count_Type);
71 function Vet (Position : Cursor) return Boolean;
72 -- Checks invariants of the cursor and its designated container, as a
73 -- simple way of detecting dangling references (see operation Free for a
74 -- description of the detection mechanism), returning True if all checks
75 -- pass. Invocations of Vet are used here as the argument of pragma Assert,
76 -- so the checks are performed only when assertions are enabled.
78 ---------
79 -- "=" --
80 ---------
82 function "=" (Left, Right : List) return Boolean is
83 BL : Natural renames Left'Unrestricted_Access.Busy;
84 LL : Natural renames Left'Unrestricted_Access.Lock;
86 BR : Natural renames Right'Unrestricted_Access.Busy;
87 LR : Natural renames Right'Unrestricted_Access.Lock;
89 LN : Node_Array renames Left.Nodes;
90 RN : Node_Array renames Right.Nodes;
92 LI : Count_Type;
93 RI : Count_Type;
95 Result : Boolean;
97 begin
98 if Left'Address = Right'Address then
99 return True;
100 end if;
102 if Left.Length /= Right.Length then
103 return False;
104 end if;
106 -- Per AI05-0022, the container implementation is required to detect
107 -- element tampering by a generic actual subprogram.
109 BL := BL + 1;
110 LL := LL + 1;
112 BR := BR + 1;
113 LR := LR + 1;
115 LI := Left.First;
116 RI := Right.First;
117 Result := True;
118 for J in 1 .. Left.Length loop
119 if LN (LI).Element /= RN (RI).Element then
120 Result := False;
121 exit;
122 end if;
124 LI := LN (LI).Next;
125 RI := RN (RI).Next;
126 end loop;
128 BL := BL - 1;
129 LL := LL - 1;
131 BR := BR - 1;
132 LR := LR - 1;
134 return Result;
136 exception
137 when others =>
138 BL := BL - 1;
139 LL := LL - 1;
141 BR := BR - 1;
142 LR := LR - 1;
144 raise;
145 end "=";
147 --------------
148 -- Allocate --
149 --------------
151 procedure Allocate
152 (Container : in out List;
153 New_Item : Element_Type;
154 New_Node : out Count_Type)
156 N : Node_Array renames Container.Nodes;
158 begin
159 if Container.Free >= 0 then
160 New_Node := Container.Free;
162 -- We always perform the assignment first, before we change container
163 -- state, in order to defend against exceptions duration assignment.
165 N (New_Node).Element := New_Item;
166 Container.Free := N (New_Node).Next;
168 else
169 -- A negative free store value means that the links of the nodes in
170 -- the free store have not been initialized. In this case, the nodes
171 -- are physically contiguous in the array, starting at the index that
172 -- is the absolute value of the Container.Free, and continuing until
173 -- the end of the array (Nodes'Last).
175 New_Node := abs Container.Free;
177 -- As above, we perform this assignment first, before modifying any
178 -- container state.
180 N (New_Node).Element := New_Item;
181 Container.Free := Container.Free - 1;
182 end if;
183 end Allocate;
185 procedure Allocate
186 (Container : in out List;
187 Stream : not null access Root_Stream_Type'Class;
188 New_Node : out Count_Type)
190 N : Node_Array renames Container.Nodes;
192 begin
193 if Container.Free >= 0 then
194 New_Node := Container.Free;
196 -- We always perform the assignment first, before we change container
197 -- state, in order to defend against exceptions duration assignment.
199 Element_Type'Read (Stream, N (New_Node).Element);
200 Container.Free := N (New_Node).Next;
202 else
203 -- A negative free store value means that the links of the nodes in
204 -- the free store have not been initialized. In this case, the nodes
205 -- are physically contiguous in the array, starting at the index that
206 -- is the absolute value of the Container.Free, and continuing until
207 -- the end of the array (Nodes'Last).
209 New_Node := abs Container.Free;
211 -- As above, we perform this assignment first, before modifying any
212 -- container state.
214 Element_Type'Read (Stream, N (New_Node).Element);
215 Container.Free := Container.Free - 1;
216 end if;
217 end Allocate;
219 ------------
220 -- Append --
221 ------------
223 procedure Append
224 (Container : in out List;
225 New_Item : Element_Type;
226 Count : Count_Type := 1)
228 begin
229 Insert (Container, No_Element, New_Item, Count);
230 end Append;
232 ------------
233 -- Adjust --
234 ------------
236 procedure Adjust (Control : in out Reference_Control_Type) is
237 begin
238 if Control.Container /= null then
239 declare
240 C : List renames Control.Container.all;
241 B : Natural renames C.Busy;
242 L : Natural renames C.Lock;
243 begin
244 B := B + 1;
245 L := L + 1;
246 end;
247 end if;
248 end Adjust;
250 ------------
251 -- Assign --
252 ------------
254 procedure Assign (Target : in out List; Source : List) is
255 SN : Node_Array renames Source.Nodes;
256 J : Count_Type;
258 begin
259 if Target'Address = Source'Address then
260 return;
261 end if;
263 if Target.Capacity < Source.Length then
264 raise Capacity_Error -- ???
265 with "Target capacity is less than Source length";
266 end if;
268 Target.Clear;
270 J := Source.First;
271 while J /= 0 loop
272 Target.Append (SN (J).Element);
273 J := SN (J).Next;
274 end loop;
275 end Assign;
277 -----------
278 -- Clear --
279 -----------
281 procedure Clear (Container : in out List) is
282 N : Node_Array renames Container.Nodes;
283 X : Count_Type;
285 begin
286 if Container.Length = 0 then
287 pragma Assert (Container.First = 0);
288 pragma Assert (Container.Last = 0);
289 pragma Assert (Container.Busy = 0);
290 pragma Assert (Container.Lock = 0);
291 return;
292 end if;
294 pragma Assert (Container.First >= 1);
295 pragma Assert (Container.Last >= 1);
296 pragma Assert (N (Container.First).Prev = 0);
297 pragma Assert (N (Container.Last).Next = 0);
299 if Container.Busy > 0 then
300 raise Program_Error with
301 "attempt to tamper with cursors (list is busy)";
302 end if;
304 while Container.Length > 1 loop
305 X := Container.First;
306 pragma Assert (N (N (X).Next).Prev = Container.First);
308 Container.First := N (X).Next;
309 N (Container.First).Prev := 0;
311 Container.Length := Container.Length - 1;
313 Free (Container, X);
314 end loop;
316 X := Container.First;
317 pragma Assert (X = Container.Last);
319 Container.First := 0;
320 Container.Last := 0;
321 Container.Length := 0;
323 Free (Container, X);
324 end Clear;
326 ------------------------
327 -- Constant_Reference --
328 ------------------------
330 function Constant_Reference
331 (Container : aliased List;
332 Position : Cursor) return Constant_Reference_Type
334 begin
335 if Position.Container = null then
336 raise Constraint_Error with "Position cursor has no element";
338 elsif Position.Container /= Container'Unrestricted_Access then
339 raise Program_Error with
340 "Position cursor designates wrong container";
342 else
343 pragma Assert (Vet (Position), "bad cursor in Constant_Reference");
345 declare
346 N : Node_Type renames Container.Nodes (Position.Node);
347 B : Natural renames Position.Container.Busy;
348 L : Natural renames Position.Container.Lock;
349 begin
350 return R : constant Constant_Reference_Type :=
351 (Element => N.Element'Access,
352 Control => (Controlled with Container'Unrestricted_Access))
354 B := B + 1;
355 L := L + 1;
356 end return;
357 end;
358 end if;
359 end Constant_Reference;
361 --------------
362 -- Contains --
363 --------------
365 function Contains
366 (Container : List;
367 Item : Element_Type) return Boolean
369 begin
370 return Find (Container, Item) /= No_Element;
371 end Contains;
373 ----------
374 -- Copy --
375 ----------
377 function Copy (Source : List; Capacity : Count_Type := 0) return List is
378 C : Count_Type;
380 begin
381 if Capacity = 0 then
382 C := Source.Length;
383 elsif Capacity >= Source.Length then
384 C := Capacity;
385 else
386 raise Capacity_Error with "Capacity value too small";
387 end if;
389 return Target : List (Capacity => C) do
390 Assign (Target => Target, Source => Source);
391 end return;
392 end Copy;
394 ------------
395 -- Delete --
396 ------------
398 procedure Delete
399 (Container : in out List;
400 Position : in out Cursor;
401 Count : Count_Type := 1)
403 N : Node_Array renames Container.Nodes;
404 X : Count_Type;
406 begin
407 if Position.Node = 0 then
408 raise Constraint_Error with
409 "Position cursor has no element";
410 end if;
412 if Position.Container /= Container'Unrestricted_Access then
413 raise Program_Error with
414 "Position cursor designates wrong container";
415 end if;
417 pragma Assert (Vet (Position), "bad cursor in Delete");
418 pragma Assert (Container.First >= 1);
419 pragma Assert (Container.Last >= 1);
420 pragma Assert (N (Container.First).Prev = 0);
421 pragma Assert (N (Container.Last).Next = 0);
423 if Position.Node = Container.First then
424 Delete_First (Container, Count);
425 Position := No_Element;
426 return;
427 end if;
429 if Count = 0 then
430 Position := No_Element;
431 return;
432 end if;
434 if Container.Busy > 0 then
435 raise Program_Error with
436 "attempt to tamper with cursors (list is busy)";
437 end if;
439 for Index in 1 .. Count loop
440 pragma Assert (Container.Length >= 2);
442 X := Position.Node;
443 Container.Length := Container.Length - 1;
445 if X = Container.Last then
446 Position := No_Element;
448 Container.Last := N (X).Prev;
449 N (Container.Last).Next := 0;
451 Free (Container, X);
452 return;
453 end if;
455 Position.Node := N (X).Next;
457 N (N (X).Next).Prev := N (X).Prev;
458 N (N (X).Prev).Next := N (X).Next;
460 Free (Container, X);
461 end loop;
463 Position := No_Element;
464 end Delete;
466 ------------------
467 -- Delete_First --
468 ------------------
470 procedure Delete_First
471 (Container : in out List;
472 Count : Count_Type := 1)
474 N : Node_Array renames Container.Nodes;
475 X : Count_Type;
477 begin
478 if Count >= Container.Length then
479 Clear (Container);
480 return;
481 end if;
483 if Count = 0 then
484 return;
485 end if;
487 if Container.Busy > 0 then
488 raise Program_Error with
489 "attempt to tamper with cursors (list is busy)";
490 end if;
492 for J in 1 .. Count loop
493 X := Container.First;
494 pragma Assert (N (N (X).Next).Prev = Container.First);
496 Container.First := N (X).Next;
497 N (Container.First).Prev := 0;
499 Container.Length := Container.Length - 1;
501 Free (Container, X);
502 end loop;
503 end Delete_First;
505 -----------------
506 -- Delete_Last --
507 -----------------
509 procedure Delete_Last
510 (Container : in out List;
511 Count : Count_Type := 1)
513 N : Node_Array renames Container.Nodes;
514 X : Count_Type;
516 begin
517 if Count >= Container.Length then
518 Clear (Container);
519 return;
520 end if;
522 if Count = 0 then
523 return;
524 end if;
526 if Container.Busy > 0 then
527 raise Program_Error with
528 "attempt to tamper with cursors (list is busy)";
529 end if;
531 for J in 1 .. Count loop
532 X := Container.Last;
533 pragma Assert (N (N (X).Prev).Next = Container.Last);
535 Container.Last := N (X).Prev;
536 N (Container.Last).Next := 0;
538 Container.Length := Container.Length - 1;
540 Free (Container, X);
541 end loop;
542 end Delete_Last;
544 -------------
545 -- Element --
546 -------------
548 function Element (Position : Cursor) return Element_Type is
549 begin
550 if Position.Node = 0 then
551 raise Constraint_Error with
552 "Position cursor has no element";
554 else
555 pragma Assert (Vet (Position), "bad cursor in Element");
557 return Position.Container.Nodes (Position.Node).Element;
558 end if;
559 end Element;
561 --------------
562 -- Finalize --
563 --------------
565 procedure Finalize (Object : in out Iterator) is
566 begin
567 if Object.Container /= null then
568 declare
569 B : Natural renames Object.Container.all.Busy;
570 begin
571 B := B - 1;
572 end;
573 end if;
574 end Finalize;
576 procedure Finalize (Control : in out Reference_Control_Type) is
577 begin
578 if Control.Container /= null then
579 declare
580 C : List renames Control.Container.all;
581 B : Natural renames C.Busy;
582 L : Natural renames C.Lock;
583 begin
584 B := B - 1;
585 L := L - 1;
586 end;
588 Control.Container := null;
589 end if;
590 end Finalize;
592 ----------
593 -- Find --
594 ----------
596 function Find
597 (Container : List;
598 Item : Element_Type;
599 Position : Cursor := No_Element) return Cursor
601 Nodes : Node_Array renames Container.Nodes;
602 Node : Count_Type := Position.Node;
604 begin
605 if Node = 0 then
606 Node := Container.First;
608 else
609 if Position.Container /= Container'Unrestricted_Access then
610 raise Program_Error with
611 "Position cursor designates wrong container";
612 end if;
614 pragma Assert (Vet (Position), "bad cursor in Find");
615 end if;
617 -- Per AI05-0022, the container implementation is required to detect
618 -- element tampering by a generic actual subprogram.
620 declare
621 B : Natural renames Container'Unrestricted_Access.Busy;
622 L : Natural renames Container'Unrestricted_Access.Lock;
624 Result : Count_Type;
626 begin
627 B := B + 1;
628 L := L + 1;
630 Result := 0;
631 while Node /= 0 loop
632 if Nodes (Node).Element = Item then
633 Result := Node;
634 exit;
635 end if;
637 Node := Nodes (Node).Next;
638 end loop;
640 B := B - 1;
641 L := L - 1;
643 if Result = 0 then
644 return No_Element;
645 else
646 return Cursor'(Container'Unrestricted_Access, Result);
647 end if;
649 exception
650 when others =>
651 B := B - 1;
652 L := L - 1;
653 raise;
654 end;
655 end Find;
657 -----------
658 -- First --
659 -----------
661 function First (Container : List) return Cursor is
662 begin
663 if Container.First = 0 then
664 return No_Element;
665 else
666 return Cursor'(Container'Unrestricted_Access, Container.First);
667 end if;
668 end First;
670 function First (Object : Iterator) return Cursor is
671 begin
672 -- The value of the iterator object's Node component influences the
673 -- behavior of the First (and Last) selector function.
675 -- When the Node component is 0, this means the iterator object was
676 -- constructed without a start expression, in which case the (forward)
677 -- iteration starts from the (logical) beginning of the entire sequence
678 -- of items (corresponding to Container.First, for a forward iterator).
680 -- Otherwise, this is iteration over a partial sequence of items. When
681 -- the Node component is positive, the iterator object was constructed
682 -- with a start expression, that specifies the position from which the
683 -- (forward) partial iteration begins.
685 if Object.Node = 0 then
686 return Bounded_Doubly_Linked_Lists.First (Object.Container.all);
687 else
688 return Cursor'(Object.Container, Object.Node);
689 end if;
690 end First;
692 -------------------
693 -- First_Element --
694 -------------------
696 function First_Element (Container : List) return Element_Type is
697 begin
698 if Container.First = 0 then
699 raise Constraint_Error with "list is empty";
700 else
701 return Container.Nodes (Container.First).Element;
702 end if;
703 end First_Element;
705 ----------
706 -- Free --
707 ----------
709 procedure Free
710 (Container : in out List;
711 X : Count_Type)
713 pragma Assert (X > 0);
714 pragma Assert (X <= Container.Capacity);
716 N : Node_Array renames Container.Nodes;
717 pragma Assert (N (X).Prev >= 0); -- node is active
719 begin
720 -- The list container actually contains two lists: one for the "active"
721 -- nodes that contain elements that have been inserted onto the list,
722 -- and another for the "inactive" nodes for the free store.
724 -- We desire that merely declaring an object should have only minimal
725 -- cost; specially, we want to avoid having to initialize the free
726 -- store (to fill in the links), especially if the capacity is large.
728 -- The head of the free list is indicated by Container.Free. If its
729 -- value is non-negative, then the free store has been initialized in
730 -- the "normal" way: Container.Free points to the head of the list of
731 -- free (inactive) nodes, and the value 0 means the free list is empty.
732 -- Each node on the free list has been initialized to point to the next
733 -- free node (via its Next component), and the value 0 means that this
734 -- is the last free node.
736 -- If Container.Free is negative, then the links on the free store have
737 -- not been initialized. In this case the link values are implied: the
738 -- free store comprises the components of the node array started with
739 -- the absolute value of Container.Free, and continuing until the end of
740 -- the array (Nodes'Last).
742 -- If the list container is manipulated on one end only (for example if
743 -- the container were being used as a stack), then there is no need to
744 -- initialize the free store, since the inactive nodes are physically
745 -- contiguous (in fact, they lie immediately beyond the logical end
746 -- being manipulated). The only time we need to actually initialize the
747 -- nodes in the free store is if the node that becomes inactive is not
748 -- at the end of the list. The free store would then be discontiguous
749 -- and so its nodes would need to be linked in the traditional way.
751 -- ???
752 -- It might be possible to perform an optimization here. Suppose that
753 -- the free store can be represented as having two parts: one comprising
754 -- the non-contiguous inactive nodes linked together in the normal way,
755 -- and the other comprising the contiguous inactive nodes (that are not
756 -- linked together, at the end of the nodes array). This would allow us
757 -- to never have to initialize the free store, except in a lazy way as
758 -- nodes become inactive.
760 -- When an element is deleted from the list container, its node becomes
761 -- inactive, and so we set its Prev component to a negative value, to
762 -- indicate that it is now inactive. This provides a useful way to
763 -- detect a dangling cursor reference (and which is used in Vet).
765 N (X).Prev := -1; -- Node is deallocated (not on active list)
767 if Container.Free >= 0 then
769 -- The free store has previously been initialized. All we need to
770 -- do here is link the newly-free'd node onto the free list.
772 N (X).Next := Container.Free;
773 Container.Free := X;
775 elsif X + 1 = abs Container.Free then
777 -- The free store has not been initialized, and the node becoming
778 -- inactive immediately precedes the start of the free store. All
779 -- we need to do is move the start of the free store back by one.
781 -- Note: initializing Next to zero is not strictly necessary but
782 -- seems cleaner and marginally safer.
784 N (X).Next := 0;
785 Container.Free := Container.Free + 1;
787 else
788 -- The free store has not been initialized, and the node becoming
789 -- inactive does not immediately precede the free store. Here we
790 -- first initialize the free store (meaning the links are given
791 -- values in the traditional way), and then link the newly-free'd
792 -- node onto the head of the free store.
794 -- ???
795 -- See the comments above for an optimization opportunity. If the
796 -- next link for a node on the free store is negative, then this
797 -- means the remaining nodes on the free store are physically
798 -- contiguous, starting as the absolute value of that index value.
800 Container.Free := abs Container.Free;
802 if Container.Free > Container.Capacity then
803 Container.Free := 0;
805 else
806 for I in Container.Free .. Container.Capacity - 1 loop
807 N (I).Next := I + 1;
808 end loop;
810 N (Container.Capacity).Next := 0;
811 end if;
813 N (X).Next := Container.Free;
814 Container.Free := X;
815 end if;
816 end Free;
818 ---------------------
819 -- Generic_Sorting --
820 ---------------------
822 package body Generic_Sorting is
824 ---------------
825 -- Is_Sorted --
826 ---------------
828 function Is_Sorted (Container : List) return Boolean is
829 B : Natural renames Container'Unrestricted_Access.Busy;
830 L : Natural renames Container'Unrestricted_Access.Lock;
832 Nodes : Node_Array renames Container.Nodes;
833 Node : Count_Type;
835 Result : Boolean;
837 begin
838 -- Per AI05-0022, the container implementation is required to detect
839 -- element tampering by a generic actual subprogram.
841 B := B + 1;
842 L := L + 1;
844 Node := Container.First;
845 Result := True;
846 for J in 2 .. Container.Length loop
847 if Nodes (Nodes (Node).Next).Element < Nodes (Node).Element then
848 Result := False;
849 exit;
850 end if;
852 Node := Nodes (Node).Next;
853 end loop;
855 B := B - 1;
856 L := L - 1;
858 return Result;
860 exception
861 when others =>
862 B := B - 1;
863 L := L - 1;
864 raise;
865 end Is_Sorted;
867 -----------
868 -- Merge --
869 -----------
871 procedure Merge
872 (Target : in out List;
873 Source : in out List)
875 begin
876 -- The semantics of Merge changed slightly per AI05-0021. It was
877 -- originally the case that if Target and Source denoted the same
878 -- container object, then the GNAT implementation of Merge did
879 -- nothing. However, it was argued that RM05 did not precisely
880 -- specify the semantics for this corner case. The decision of the
881 -- ARG was that if Target and Source denote the same non-empty
882 -- container object, then Program_Error is raised.
884 if Source.Is_Empty then
885 return;
886 end if;
888 if Target'Address = Source'Address then
889 raise Program_Error with
890 "Target and Source denote same non-empty container";
891 end if;
893 if Target.Length > Count_Type'Last - Source.Length then
894 raise Constraint_Error with "new length exceeds maximum";
895 end if;
897 if Target.Length + Source.Length > Target.Capacity then
898 raise Capacity_Error with "new length exceeds target capacity";
899 end if;
901 if Target.Busy > 0 then
902 raise Program_Error with
903 "attempt to tamper with cursors of Target (list is busy)";
904 end if;
906 if Source.Busy > 0 then
907 raise Program_Error with
908 "attempt to tamper with cursors of Source (list is busy)";
909 end if;
911 -- Per AI05-0022, the container implementation is required to detect
912 -- element tampering by a generic actual subprogram.
914 declare
915 TB : Natural renames Target.Busy;
916 TL : Natural renames Target.Lock;
918 SB : Natural renames Source.Busy;
919 SL : Natural renames Source.Lock;
921 LN : Node_Array renames Target.Nodes;
922 RN : Node_Array renames Source.Nodes;
924 LI, LJ, RI, RJ : Count_Type;
926 begin
927 TB := TB + 1;
928 TL := TL + 1;
930 SB := SB + 1;
931 SL := SL + 1;
933 LI := Target.First;
934 RI := Source.First;
935 while RI /= 0 loop
936 pragma Assert (RN (RI).Next = 0
937 or else not (RN (RN (RI).Next).Element <
938 RN (RI).Element));
940 if LI = 0 then
941 Splice_Internal (Target, 0, Source);
942 exit;
943 end if;
945 pragma Assert (LN (LI).Next = 0
946 or else not (LN (LN (LI).Next).Element <
947 LN (LI).Element));
949 if RN (RI).Element < LN (LI).Element then
950 RJ := RI;
951 RI := RN (RI).Next;
952 Splice_Internal (Target, LI, Source, RJ, LJ);
954 else
955 LI := LN (LI).Next;
956 end if;
957 end loop;
959 TB := TB - 1;
960 TL := TL - 1;
962 SB := SB - 1;
963 SL := SL - 1;
965 exception
966 when others =>
967 TB := TB - 1;
968 TL := TL - 1;
970 SB := SB - 1;
971 SL := SL - 1;
973 raise;
974 end;
975 end Merge;
977 ----------
978 -- Sort --
979 ----------
981 procedure Sort (Container : in out List) is
982 N : Node_Array renames Container.Nodes;
984 procedure Partition (Pivot, Back : Count_Type);
985 -- What does this do ???
987 procedure Sort (Front, Back : Count_Type);
988 -- Internal procedure, what does it do??? rename it???
990 ---------------
991 -- Partition --
992 ---------------
994 procedure Partition (Pivot, Back : Count_Type) is
995 Node : Count_Type;
997 begin
998 Node := N (Pivot).Next;
999 while Node /= Back loop
1000 if N (Node).Element < N (Pivot).Element then
1001 declare
1002 Prev : constant Count_Type := N (Node).Prev;
1003 Next : constant Count_Type := N (Node).Next;
1005 begin
1006 N (Prev).Next := Next;
1008 if Next = 0 then
1009 Container.Last := Prev;
1010 else
1011 N (Next).Prev := Prev;
1012 end if;
1014 N (Node).Next := Pivot;
1015 N (Node).Prev := N (Pivot).Prev;
1017 N (Pivot).Prev := Node;
1019 if N (Node).Prev = 0 then
1020 Container.First := Node;
1021 else
1022 N (N (Node).Prev).Next := Node;
1023 end if;
1025 Node := Next;
1026 end;
1028 else
1029 Node := N (Node).Next;
1030 end if;
1031 end loop;
1032 end Partition;
1034 ----------
1035 -- Sort --
1036 ----------
1038 procedure Sort (Front, Back : Count_Type) is
1039 Pivot : constant Count_Type :=
1040 (if Front = 0 then Container.First else N (Front).Next);
1041 begin
1042 if Pivot /= Back then
1043 Partition (Pivot, Back);
1044 Sort (Front, Pivot);
1045 Sort (Pivot, Back);
1046 end if;
1047 end Sort;
1049 -- Start of processing for Sort
1051 begin
1052 if Container.Length <= 1 then
1053 return;
1054 end if;
1056 pragma Assert (N (Container.First).Prev = 0);
1057 pragma Assert (N (Container.Last).Next = 0);
1059 if Container.Busy > 0 then
1060 raise Program_Error with
1061 "attempt to tamper with cursors (list is busy)";
1062 end if;
1064 -- Per AI05-0022, the container implementation is required to detect
1065 -- element tampering by a generic actual subprogram.
1067 declare
1068 B : Natural renames Container.Busy;
1069 L : Natural renames Container.Lock;
1071 begin
1072 B := B + 1;
1073 L := L + 1;
1075 Sort (Front => 0, Back => 0);
1077 B := B - 1;
1078 L := L - 1;
1080 exception
1081 when others =>
1082 B := B - 1;
1083 L := L - 1;
1084 raise;
1085 end;
1087 pragma Assert (N (Container.First).Prev = 0);
1088 pragma Assert (N (Container.Last).Next = 0);
1089 end Sort;
1091 end Generic_Sorting;
1093 -----------------
1094 -- Has_Element --
1095 -----------------
1097 function Has_Element (Position : Cursor) return Boolean is
1098 begin
1099 pragma Assert (Vet (Position), "bad cursor in Has_Element");
1100 return Position.Node /= 0;
1101 end Has_Element;
1103 ------------
1104 -- Insert --
1105 ------------
1107 procedure Insert
1108 (Container : in out List;
1109 Before : Cursor;
1110 New_Item : Element_Type;
1111 Position : out Cursor;
1112 Count : Count_Type := 1)
1114 First_Node : Count_Type;
1115 New_Node : Count_Type;
1117 begin
1118 if Before.Container /= null then
1119 if Before.Container /= Container'Unrestricted_Access then
1120 raise Program_Error with
1121 "Before cursor designates wrong list";
1122 end if;
1124 pragma Assert (Vet (Before), "bad cursor in Insert");
1125 end if;
1127 if Count = 0 then
1128 Position := Before;
1129 return;
1130 end if;
1132 if Container.Length > Container.Capacity - Count then
1133 raise Capacity_Error with "capacity exceeded";
1134 end if;
1136 if Container.Busy > 0 then
1137 raise Program_Error with
1138 "attempt to tamper with cursors (list is busy)";
1139 end if;
1141 Allocate (Container, New_Item, New_Node);
1142 First_Node := New_Node;
1143 Insert_Internal (Container, Before.Node, New_Node);
1145 for Index in Count_Type'(2) .. Count loop
1146 Allocate (Container, New_Item, New_Node);
1147 Insert_Internal (Container, Before.Node, New_Node);
1148 end loop;
1150 Position := Cursor'(Container'Unchecked_Access, First_Node);
1151 end Insert;
1153 procedure Insert
1154 (Container : in out List;
1155 Before : Cursor;
1156 New_Item : Element_Type;
1157 Count : Count_Type := 1)
1159 Position : Cursor;
1160 pragma Unreferenced (Position);
1161 begin
1162 Insert (Container, Before, New_Item, Position, Count);
1163 end Insert;
1165 procedure Insert
1166 (Container : in out List;
1167 Before : Cursor;
1168 Position : out Cursor;
1169 Count : Count_Type := 1)
1171 New_Item : Element_Type;
1172 pragma Unmodified (New_Item);
1173 -- OK to reference, see below
1175 begin
1176 -- There is no explicit element provided, but in an instance the element
1177 -- type may be a scalar with a Default_Value aspect, or a composite
1178 -- type with such a scalar component, or components with default
1179 -- initialization, so insert the specified number of possibly
1180 -- initialized elements at the given position.
1182 Insert (Container, Before, New_Item, Position, Count);
1183 end Insert;
1185 ---------------------
1186 -- Insert_Internal --
1187 ---------------------
1189 procedure Insert_Internal
1190 (Container : in out List;
1191 Before : Count_Type;
1192 New_Node : Count_Type)
1194 N : Node_Array renames Container.Nodes;
1196 begin
1197 if Container.Length = 0 then
1198 pragma Assert (Before = 0);
1199 pragma Assert (Container.First = 0);
1200 pragma Assert (Container.Last = 0);
1202 Container.First := New_Node;
1203 N (Container.First).Prev := 0;
1205 Container.Last := New_Node;
1206 N (Container.Last).Next := 0;
1208 -- Before = zero means append
1210 elsif Before = 0 then
1211 pragma Assert (N (Container.Last).Next = 0);
1213 N (Container.Last).Next := New_Node;
1214 N (New_Node).Prev := Container.Last;
1216 Container.Last := New_Node;
1217 N (Container.Last).Next := 0;
1219 -- Before = Container.First means prepend
1221 elsif Before = Container.First then
1222 pragma Assert (N (Container.First).Prev = 0);
1224 N (Container.First).Prev := New_Node;
1225 N (New_Node).Next := Container.First;
1227 Container.First := New_Node;
1228 N (Container.First).Prev := 0;
1230 else
1231 pragma Assert (N (Container.First).Prev = 0);
1232 pragma Assert (N (Container.Last).Next = 0);
1234 N (New_Node).Next := Before;
1235 N (New_Node).Prev := N (Before).Prev;
1237 N (N (Before).Prev).Next := New_Node;
1238 N (Before).Prev := New_Node;
1239 end if;
1241 Container.Length := Container.Length + 1;
1242 end Insert_Internal;
1244 --------------
1245 -- Is_Empty --
1246 --------------
1248 function Is_Empty (Container : List) return Boolean is
1249 begin
1250 return Container.Length = 0;
1251 end Is_Empty;
1253 -------------
1254 -- Iterate --
1255 -------------
1257 procedure Iterate
1258 (Container : List;
1259 Process : not null access procedure (Position : Cursor))
1261 B : Natural renames Container'Unrestricted_Access.all.Busy;
1262 Node : Count_Type := Container.First;
1264 begin
1265 B := B + 1;
1267 begin
1268 while Node /= 0 loop
1269 Process (Cursor'(Container'Unrestricted_Access, Node));
1270 Node := Container.Nodes (Node).Next;
1271 end loop;
1272 exception
1273 when others =>
1274 B := B - 1;
1275 raise;
1276 end;
1278 B := B - 1;
1279 end Iterate;
1281 function Iterate
1282 (Container : List)
1283 return List_Iterator_Interfaces.Reversible_Iterator'Class
1285 B : Natural renames Container'Unrestricted_Access.all.Busy;
1287 begin
1288 -- The value of the Node component influences the behavior of the First
1289 -- and Last selector functions of the iterator object. When the Node
1290 -- component is 0 (as is the case here), this means the iterator
1291 -- object was constructed without a start expression. This is a
1292 -- complete iterator, meaning that the iteration starts from the
1293 -- (logical) beginning of the sequence of items.
1295 -- Note: For a forward iterator, Container.First is the beginning, and
1296 -- for a reverse iterator, Container.Last is the beginning.
1298 return It : constant Iterator :=
1299 Iterator'(Limited_Controlled with
1300 Container => Container'Unrestricted_Access,
1301 Node => 0)
1303 B := B + 1;
1304 end return;
1305 end Iterate;
1307 function Iterate
1308 (Container : List;
1309 Start : Cursor)
1310 return List_Iterator_Interfaces.Reversible_Iterator'class
1312 B : Natural renames Container'Unrestricted_Access.all.Busy;
1314 begin
1315 -- It was formerly the case that when Start = No_Element, the partial
1316 -- iterator was defined to behave the same as for a complete iterator,
1317 -- and iterate over the entire sequence of items. However, those
1318 -- semantics were unintuitive and arguably error-prone (it is too easy
1319 -- to accidentally create an endless loop), and so they were changed,
1320 -- per the ARG meeting in Denver on 2011/11. However, there was no
1321 -- consensus about what positive meaning this corner case should have,
1322 -- and so it was decided to simply raise an exception. This does imply,
1323 -- however, that it is not possible to use a partial iterator to specify
1324 -- an empty sequence of items.
1326 if Start = No_Element then
1327 raise Constraint_Error with
1328 "Start position for iterator equals No_Element";
1329 end if;
1331 if Start.Container /= Container'Unrestricted_Access then
1332 raise Program_Error with
1333 "Start cursor of Iterate designates wrong list";
1334 end if;
1336 pragma Assert (Vet (Start), "Start cursor of Iterate is bad");
1338 -- The value of the Node component influences the behavior of the First
1339 -- and Last selector functions of the iterator object. When the Node
1340 -- component is positive (as is the case here), it means that this
1341 -- is a partial iteration, over a subset of the complete sequence of
1342 -- items. The iterator object was constructed with a start expression,
1343 -- indicating the position from which the iteration begins. Note that
1344 -- the start position has the same value irrespective of whether this
1345 -- is a forward or reverse iteration.
1347 return It : constant Iterator :=
1348 Iterator'(Limited_Controlled with
1349 Container => Container'Unrestricted_Access,
1350 Node => Start.Node)
1352 B := B + 1;
1353 end return;
1354 end Iterate;
1356 ----------
1357 -- Last --
1358 ----------
1360 function Last (Container : List) return Cursor is
1361 begin
1362 if Container.Last = 0 then
1363 return No_Element;
1364 else
1365 return Cursor'(Container'Unrestricted_Access, Container.Last);
1366 end if;
1367 end Last;
1369 function Last (Object : Iterator) return Cursor is
1370 begin
1371 -- The value of the iterator object's Node component influences the
1372 -- behavior of the Last (and First) selector function.
1374 -- When the Node component is 0, this means the iterator object was
1375 -- constructed without a start expression, in which case the (reverse)
1376 -- iteration starts from the (logical) beginning of the entire sequence
1377 -- (corresponding to Container.Last, for a reverse iterator).
1379 -- Otherwise, this is iteration over a partial sequence of items. When
1380 -- the Node component is positive, the iterator object was constructed
1381 -- with a start expression, that specifies the position from which the
1382 -- (reverse) partial iteration begins.
1384 if Object.Node = 0 then
1385 return Bounded_Doubly_Linked_Lists.Last (Object.Container.all);
1386 else
1387 return Cursor'(Object.Container, Object.Node);
1388 end if;
1389 end Last;
1391 ------------------
1392 -- Last_Element --
1393 ------------------
1395 function Last_Element (Container : List) return Element_Type is
1396 begin
1397 if Container.Last = 0 then
1398 raise Constraint_Error with "list is empty";
1399 else
1400 return Container.Nodes (Container.Last).Element;
1401 end if;
1402 end Last_Element;
1404 ------------
1405 -- Length --
1406 ------------
1408 function Length (Container : List) return Count_Type is
1409 begin
1410 return Container.Length;
1411 end Length;
1413 ----------
1414 -- Move --
1415 ----------
1417 procedure Move
1418 (Target : in out List;
1419 Source : in out List)
1421 N : Node_Array renames Source.Nodes;
1422 X : Count_Type;
1424 begin
1425 if Target'Address = Source'Address then
1426 return;
1427 end if;
1429 if Target.Capacity < Source.Length then
1430 raise Capacity_Error with "Source length exceeds Target capacity";
1431 end if;
1433 if Source.Busy > 0 then
1434 raise Program_Error with
1435 "attempt to tamper with cursors of Source (list is busy)";
1436 end if;
1438 -- Clear target, note that this checks busy bits of Target
1440 Clear (Target);
1442 while Source.Length > 1 loop
1443 pragma Assert (Source.First in 1 .. Source.Capacity);
1444 pragma Assert (Source.Last /= Source.First);
1445 pragma Assert (N (Source.First).Prev = 0);
1446 pragma Assert (N (Source.Last).Next = 0);
1448 -- Copy first element from Source to Target
1450 X := Source.First;
1451 Append (Target, N (X).Element);
1453 -- Unlink first node of Source
1455 Source.First := N (X).Next;
1456 N (Source.First).Prev := 0;
1458 Source.Length := Source.Length - 1;
1460 -- The representation invariants for Source have been restored. It is
1461 -- now safe to free the unlinked node, without fear of corrupting the
1462 -- active links of Source.
1464 -- Note that the algorithm we use here models similar algorithms used
1465 -- in the unbounded form of the doubly-linked list container. In that
1466 -- case, Free is an instantation of Unchecked_Deallocation, which can
1467 -- fail (because PE will be raised if controlled Finalize fails), so
1468 -- we must defer the call until the last step. Here in the bounded
1469 -- form, Free merely links the node we have just "deallocated" onto a
1470 -- list of inactive nodes, so technically Free cannot fail. However,
1471 -- for consistency, we handle Free the same way here as we do for the
1472 -- unbounded form, with the pessimistic assumption that it can fail.
1474 Free (Source, X);
1475 end loop;
1477 if Source.Length = 1 then
1478 pragma Assert (Source.First in 1 .. Source.Capacity);
1479 pragma Assert (Source.Last = Source.First);
1480 pragma Assert (N (Source.First).Prev = 0);
1481 pragma Assert (N (Source.Last).Next = 0);
1483 -- Copy element from Source to Target
1485 X := Source.First;
1486 Append (Target, N (X).Element);
1488 -- Unlink node of Source
1490 Source.First := 0;
1491 Source.Last := 0;
1492 Source.Length := 0;
1494 -- Return the unlinked node to the free store
1496 Free (Source, X);
1497 end if;
1498 end Move;
1500 ----------
1501 -- Next --
1502 ----------
1504 procedure Next (Position : in out Cursor) is
1505 begin
1506 Position := Next (Position);
1507 end Next;
1509 function Next (Position : Cursor) return Cursor is
1510 begin
1511 if Position.Node = 0 then
1512 return No_Element;
1513 end if;
1515 pragma Assert (Vet (Position), "bad cursor in Next");
1517 declare
1518 Nodes : Node_Array renames Position.Container.Nodes;
1519 Node : constant Count_Type := Nodes (Position.Node).Next;
1520 begin
1521 if Node = 0 then
1522 return No_Element;
1523 else
1524 return Cursor'(Position.Container, Node);
1525 end if;
1526 end;
1527 end Next;
1529 function Next
1530 (Object : Iterator;
1531 Position : Cursor) return Cursor
1533 begin
1534 if Position.Container = null then
1535 return No_Element;
1536 elsif Position.Container /= Object.Container then
1537 raise Program_Error with
1538 "Position cursor of Next designates wrong list";
1539 else
1540 return Next (Position);
1541 end if;
1542 end Next;
1544 -------------
1545 -- Prepend --
1546 -------------
1548 procedure Prepend
1549 (Container : in out List;
1550 New_Item : Element_Type;
1551 Count : Count_Type := 1)
1553 begin
1554 Insert (Container, First (Container), New_Item, Count);
1555 end Prepend;
1557 --------------
1558 -- Previous --
1559 --------------
1561 procedure Previous (Position : in out Cursor) is
1562 begin
1563 Position := Previous (Position);
1564 end Previous;
1566 function Previous (Position : Cursor) return Cursor is
1567 begin
1568 if Position.Node = 0 then
1569 return No_Element;
1570 end if;
1572 pragma Assert (Vet (Position), "bad cursor in Previous");
1574 declare
1575 Nodes : Node_Array renames Position.Container.Nodes;
1576 Node : constant Count_Type := Nodes (Position.Node).Prev;
1577 begin
1578 if Node = 0 then
1579 return No_Element;
1580 else
1581 return Cursor'(Position.Container, Node);
1582 end if;
1583 end;
1584 end Previous;
1586 function Previous
1587 (Object : Iterator;
1588 Position : Cursor) return Cursor
1590 begin
1591 if Position.Container = null then
1592 return No_Element;
1593 elsif Position.Container /= Object.Container then
1594 raise Program_Error with
1595 "Position cursor of Previous designates wrong list";
1596 else
1597 return Previous (Position);
1598 end if;
1599 end Previous;
1601 -------------------
1602 -- Query_Element --
1603 -------------------
1605 procedure Query_Element
1606 (Position : Cursor;
1607 Process : not null access procedure (Element : Element_Type))
1609 begin
1610 if Position.Node = 0 then
1611 raise Constraint_Error with
1612 "Position cursor has no element";
1613 end if;
1615 pragma Assert (Vet (Position), "bad cursor in Query_Element");
1617 declare
1618 C : List renames Position.Container.all'Unrestricted_Access.all;
1619 B : Natural renames C.Busy;
1620 L : Natural renames C.Lock;
1622 begin
1623 B := B + 1;
1624 L := L + 1;
1626 declare
1627 N : Node_Type renames C.Nodes (Position.Node);
1628 begin
1629 Process (N.Element);
1630 exception
1631 when others =>
1632 L := L - 1;
1633 B := B - 1;
1634 raise;
1635 end;
1637 L := L - 1;
1638 B := B - 1;
1639 end;
1640 end Query_Element;
1642 ----------
1643 -- Read --
1644 ----------
1646 procedure Read
1647 (Stream : not null access Root_Stream_Type'Class;
1648 Item : out List)
1650 N : Count_Type'Base;
1651 X : Count_Type;
1653 begin
1654 Clear (Item);
1655 Count_Type'Base'Read (Stream, N);
1657 if N < 0 then
1658 raise Program_Error with "bad list length (corrupt stream)";
1660 elsif N = 0 then
1661 return;
1663 elsif N > Item.Capacity then
1664 raise Constraint_Error with "length exceeds capacity";
1666 else
1667 for Idx in 1 .. N loop
1668 Allocate (Item, Stream, New_Node => X);
1669 Insert_Internal (Item, Before => 0, New_Node => X);
1670 end loop;
1671 end if;
1672 end Read;
1674 procedure Read
1675 (Stream : not null access Root_Stream_Type'Class;
1676 Item : out Cursor)
1678 begin
1679 raise Program_Error with "attempt to stream list cursor";
1680 end Read;
1682 procedure Read
1683 (Stream : not null access Root_Stream_Type'Class;
1684 Item : out Reference_Type)
1686 begin
1687 raise Program_Error with "attempt to stream reference";
1688 end Read;
1690 procedure Read
1691 (Stream : not null access Root_Stream_Type'Class;
1692 Item : out Constant_Reference_Type)
1694 begin
1695 raise Program_Error with "attempt to stream reference";
1696 end Read;
1698 ---------------
1699 -- Reference --
1700 ---------------
1702 function Reference
1703 (Container : aliased in out List;
1704 Position : Cursor) return Reference_Type
1706 begin
1707 if Position.Container = null then
1708 raise Constraint_Error with "Position cursor has no element";
1710 elsif Position.Container /= Container'Unrestricted_Access then
1711 raise Program_Error with
1712 "Position cursor designates wrong container";
1714 else
1715 pragma Assert (Vet (Position), "bad cursor in function Reference");
1717 declare
1718 N : Node_Type renames Container.Nodes (Position.Node);
1719 B : Natural renames Container.Busy;
1720 L : Natural renames Container.Lock;
1721 begin
1722 return R : constant Reference_Type :=
1723 (Element => N.Element'Access,
1724 Control => (Controlled with Container'Unrestricted_Access))
1726 B := B + 1;
1727 L := L + 1;
1728 end return;
1729 end;
1730 end if;
1731 end Reference;
1733 ---------------------
1734 -- Replace_Element --
1735 ---------------------
1737 procedure Replace_Element
1738 (Container : in out List;
1739 Position : Cursor;
1740 New_Item : Element_Type)
1742 begin
1743 if Position.Container = null then
1744 raise Constraint_Error with "Position cursor has no element";
1746 elsif Position.Container /= Container'Unchecked_Access then
1747 raise Program_Error with
1748 "Position cursor designates wrong container";
1750 elsif Container.Lock > 0 then
1751 raise Program_Error with
1752 "attempt to tamper with elements (list is locked)";
1754 else
1755 pragma Assert (Vet (Position), "bad cursor in Replace_Element");
1757 Container.Nodes (Position.Node).Element := New_Item;
1758 end if;
1759 end Replace_Element;
1761 ----------------------
1762 -- Reverse_Elements --
1763 ----------------------
1765 procedure Reverse_Elements (Container : in out List) is
1766 N : Node_Array renames Container.Nodes;
1767 I : Count_Type := Container.First;
1768 J : Count_Type := Container.Last;
1770 procedure Swap (L, R : Count_Type);
1772 ----------
1773 -- Swap --
1774 ----------
1776 procedure Swap (L, R : Count_Type) is
1777 LN : constant Count_Type := N (L).Next;
1778 LP : constant Count_Type := N (L).Prev;
1780 RN : constant Count_Type := N (R).Next;
1781 RP : constant Count_Type := N (R).Prev;
1783 begin
1784 if LP /= 0 then
1785 N (LP).Next := R;
1786 end if;
1788 if RN /= 0 then
1789 N (RN).Prev := L;
1790 end if;
1792 N (L).Next := RN;
1793 N (R).Prev := LP;
1795 if LN = R then
1796 pragma Assert (RP = L);
1798 N (L).Prev := R;
1799 N (R).Next := L;
1801 else
1802 N (L).Prev := RP;
1803 N (RP).Next := L;
1805 N (R).Next := LN;
1806 N (LN).Prev := R;
1807 end if;
1808 end Swap;
1810 -- Start of processing for Reverse_Elements
1812 begin
1813 if Container.Length <= 1 then
1814 return;
1815 end if;
1817 pragma Assert (N (Container.First).Prev = 0);
1818 pragma Assert (N (Container.Last).Next = 0);
1820 if Container.Busy > 0 then
1821 raise Program_Error with
1822 "attempt to tamper with cursors (list is busy)";
1823 end if;
1825 Container.First := J;
1826 Container.Last := I;
1827 loop
1828 Swap (L => I, R => J);
1830 J := N (J).Next;
1831 exit when I = J;
1833 I := N (I).Prev;
1834 exit when I = J;
1836 Swap (L => J, R => I);
1838 I := N (I).Next;
1839 exit when I = J;
1841 J := N (J).Prev;
1842 exit when I = J;
1843 end loop;
1845 pragma Assert (N (Container.First).Prev = 0);
1846 pragma Assert (N (Container.Last).Next = 0);
1847 end Reverse_Elements;
1849 ------------------
1850 -- Reverse_Find --
1851 ------------------
1853 function Reverse_Find
1854 (Container : List;
1855 Item : Element_Type;
1856 Position : Cursor := No_Element) return Cursor
1858 Node : Count_Type := Position.Node;
1860 begin
1861 if Node = 0 then
1862 Node := Container.Last;
1864 else
1865 if Position.Container /= Container'Unrestricted_Access then
1866 raise Program_Error with
1867 "Position cursor designates wrong container";
1868 end if;
1870 pragma Assert (Vet (Position), "bad cursor in Reverse_Find");
1871 end if;
1873 -- Per AI05-0022, the container implementation is required to detect
1874 -- element tampering by a generic actual subprogram.
1876 declare
1877 B : Natural renames Container'Unrestricted_Access.Busy;
1878 L : Natural renames Container'Unrestricted_Access.Lock;
1880 Result : Count_Type;
1882 begin
1883 B := B + 1;
1884 L := L + 1;
1886 Result := 0;
1887 while Node /= 0 loop
1888 if Container.Nodes (Node).Element = Item then
1889 Result := Node;
1890 exit;
1891 end if;
1893 Node := Container.Nodes (Node).Prev;
1894 end loop;
1896 B := B - 1;
1897 L := L - 1;
1899 if Result = 0 then
1900 return No_Element;
1901 else
1902 return Cursor'(Container'Unrestricted_Access, Result);
1903 end if;
1905 exception
1906 when others =>
1907 B := B - 1;
1908 L := L - 1;
1909 raise;
1910 end;
1911 end Reverse_Find;
1913 ---------------------
1914 -- Reverse_Iterate --
1915 ---------------------
1917 procedure Reverse_Iterate
1918 (Container : List;
1919 Process : not null access procedure (Position : Cursor))
1921 C : List renames Container'Unrestricted_Access.all;
1922 B : Natural renames C.Busy;
1924 Node : Count_Type := Container.Last;
1926 begin
1927 B := B + 1;
1929 begin
1930 while Node /= 0 loop
1931 Process (Cursor'(Container'Unrestricted_Access, Node));
1932 Node := Container.Nodes (Node).Prev;
1933 end loop;
1934 exception
1935 when others =>
1936 B := B - 1;
1937 raise;
1938 end;
1940 B := B - 1;
1941 end Reverse_Iterate;
1943 ------------
1944 -- Splice --
1945 ------------
1947 procedure Splice
1948 (Target : in out List;
1949 Before : Cursor;
1950 Source : in out List)
1952 begin
1953 if Before.Container /= null then
1954 if Before.Container /= Target'Unrestricted_Access then
1955 raise Program_Error with
1956 "Before cursor designates wrong container";
1957 end if;
1959 pragma Assert (Vet (Before), "bad cursor in Splice");
1960 end if;
1962 if Target'Address = Source'Address or else Source.Length = 0 then
1963 return;
1965 elsif Target.Length > Count_Type'Last - Source.Length then
1966 raise Constraint_Error with "new length exceeds maximum";
1968 elsif Target.Length + Source.Length > Target.Capacity then
1969 raise Capacity_Error with "new length exceeds target capacity";
1971 elsif Target.Busy > 0 then
1972 raise Program_Error with
1973 "attempt to tamper with cursors of Target (list is busy)";
1975 elsif Source.Busy > 0 then
1976 raise Program_Error with
1977 "attempt to tamper with cursors of Source (list is busy)";
1979 else
1980 Splice_Internal (Target, Before.Node, Source);
1981 end if;
1982 end Splice;
1984 procedure Splice
1985 (Container : in out List;
1986 Before : Cursor;
1987 Position : Cursor)
1989 N : Node_Array renames Container.Nodes;
1991 begin
1992 if Before.Container /= null then
1993 if Before.Container /= Container'Unchecked_Access then
1994 raise Program_Error with
1995 "Before cursor designates wrong container";
1996 end if;
1998 pragma Assert (Vet (Before), "bad Before cursor in Splice");
1999 end if;
2001 if Position.Node = 0 then
2002 raise Constraint_Error with "Position cursor has no element";
2003 end if;
2005 if Position.Container /= Container'Unrestricted_Access then
2006 raise Program_Error with
2007 "Position cursor designates wrong container";
2008 end if;
2010 pragma Assert (Vet (Position), "bad Position cursor in Splice");
2012 if Position.Node = Before.Node
2013 or else N (Position.Node).Next = Before.Node
2014 then
2015 return;
2016 end if;
2018 pragma Assert (Container.Length >= 2);
2020 if Container.Busy > 0 then
2021 raise Program_Error with
2022 "attempt to tamper with cursors (list is busy)";
2023 end if;
2025 if Before.Node = 0 then
2026 pragma Assert (Position.Node /= Container.Last);
2028 if Position.Node = Container.First then
2029 Container.First := N (Position.Node).Next;
2030 N (Container.First).Prev := 0;
2031 else
2032 N (N (Position.Node).Prev).Next := N (Position.Node).Next;
2033 N (N (Position.Node).Next).Prev := N (Position.Node).Prev;
2034 end if;
2036 N (Container.Last).Next := Position.Node;
2037 N (Position.Node).Prev := Container.Last;
2039 Container.Last := Position.Node;
2040 N (Container.Last).Next := 0;
2042 return;
2043 end if;
2045 if Before.Node = Container.First then
2046 pragma Assert (Position.Node /= Container.First);
2048 if Position.Node = Container.Last then
2049 Container.Last := N (Position.Node).Prev;
2050 N (Container.Last).Next := 0;
2051 else
2052 N (N (Position.Node).Prev).Next := N (Position.Node).Next;
2053 N (N (Position.Node).Next).Prev := N (Position.Node).Prev;
2054 end if;
2056 N (Container.First).Prev := Position.Node;
2057 N (Position.Node).Next := Container.First;
2059 Container.First := Position.Node;
2060 N (Container.First).Prev := 0;
2062 return;
2063 end if;
2065 if Position.Node = Container.First then
2066 Container.First := N (Position.Node).Next;
2067 N (Container.First).Prev := 0;
2069 elsif Position.Node = Container.Last then
2070 Container.Last := N (Position.Node).Prev;
2071 N (Container.Last).Next := 0;
2073 else
2074 N (N (Position.Node).Prev).Next := N (Position.Node).Next;
2075 N (N (Position.Node).Next).Prev := N (Position.Node).Prev;
2076 end if;
2078 N (N (Before.Node).Prev).Next := Position.Node;
2079 N (Position.Node).Prev := N (Before.Node).Prev;
2081 N (Before.Node).Prev := Position.Node;
2082 N (Position.Node).Next := Before.Node;
2084 pragma Assert (N (Container.First).Prev = 0);
2085 pragma Assert (N (Container.Last).Next = 0);
2086 end Splice;
2088 procedure Splice
2089 (Target : in out List;
2090 Before : Cursor;
2091 Source : in out List;
2092 Position : in out Cursor)
2094 Target_Position : Count_Type;
2096 begin
2097 if Target'Address = Source'Address then
2098 Splice (Target, Before, Position);
2099 return;
2100 end if;
2102 if Before.Container /= null then
2103 if Before.Container /= Target'Unrestricted_Access then
2104 raise Program_Error with
2105 "Before cursor designates wrong container";
2106 end if;
2108 pragma Assert (Vet (Before), "bad Before cursor in Splice");
2109 end if;
2111 if Position.Node = 0 then
2112 raise Constraint_Error with "Position cursor has no element";
2113 end if;
2115 if Position.Container /= Source'Unrestricted_Access then
2116 raise Program_Error with
2117 "Position cursor designates wrong container";
2118 end if;
2120 pragma Assert (Vet (Position), "bad Position cursor in Splice");
2122 if Target.Length >= Target.Capacity then
2123 raise Capacity_Error with "Target is full";
2124 end if;
2126 if Target.Busy > 0 then
2127 raise Program_Error with
2128 "attempt to tamper with cursors of Target (list is busy)";
2129 end if;
2131 if Source.Busy > 0 then
2132 raise Program_Error with
2133 "attempt to tamper with cursors of Source (list is busy)";
2134 end if;
2136 Splice_Internal
2137 (Target => Target,
2138 Before => Before.Node,
2139 Source => Source,
2140 Src_Pos => Position.Node,
2141 Tgt_Pos => Target_Position);
2143 Position := Cursor'(Target'Unrestricted_Access, Target_Position);
2144 end Splice;
2146 ---------------------
2147 -- Splice_Internal --
2148 ---------------------
2150 procedure Splice_Internal
2151 (Target : in out List;
2152 Before : Count_Type;
2153 Source : in out List)
2155 N : Node_Array renames Source.Nodes;
2156 X : Count_Type;
2158 begin
2159 -- This implements the corresponding Splice operation, after the
2160 -- parameters have been vetted, and corner-cases disposed of.
2162 pragma Assert (Target'Address /= Source'Address);
2163 pragma Assert (Source.Length > 0);
2164 pragma Assert (Source.First /= 0);
2165 pragma Assert (N (Source.First).Prev = 0);
2166 pragma Assert (Source.Last /= 0);
2167 pragma Assert (N (Source.Last).Next = 0);
2168 pragma Assert (Target.Length <= Count_Type'Last - Source.Length);
2169 pragma Assert (Target.Length + Source.Length <= Target.Capacity);
2171 while Source.Length > 1 loop
2172 -- Copy first element of Source onto Target
2174 Allocate (Target, N (Source.First).Element, New_Node => X);
2175 Insert_Internal (Target, Before => Before, New_Node => X);
2177 -- Unlink the first node from Source
2179 X := Source.First;
2180 pragma Assert (N (N (X).Next).Prev = X);
2182 Source.First := N (X).Next;
2183 N (Source.First).Prev := 0;
2185 Source.Length := Source.Length - 1;
2187 -- Return the Source node to its free store
2189 Free (Source, X);
2190 end loop;
2192 -- Copy first (and only remaining) element of Source onto Target
2194 Allocate (Target, N (Source.First).Element, New_Node => X);
2195 Insert_Internal (Target, Before => Before, New_Node => X);
2197 -- Unlink the node from Source
2199 X := Source.First;
2200 pragma Assert (X = Source.Last);
2202 Source.First := 0;
2203 Source.Last := 0;
2205 Source.Length := 0;
2207 -- Return the Source node to its free store
2209 Free (Source, X);
2210 end Splice_Internal;
2212 procedure Splice_Internal
2213 (Target : in out List;
2214 Before : Count_Type; -- node of Target
2215 Source : in out List;
2216 Src_Pos : Count_Type; -- node of Source
2217 Tgt_Pos : out Count_Type)
2219 N : Node_Array renames Source.Nodes;
2221 begin
2222 -- This implements the corresponding Splice operation, after the
2223 -- parameters have been vetted, and corner-cases handled.
2225 pragma Assert (Target'Address /= Source'Address);
2226 pragma Assert (Target.Length < Target.Capacity);
2227 pragma Assert (Source.Length > 0);
2228 pragma Assert (Source.First /= 0);
2229 pragma Assert (N (Source.First).Prev = 0);
2230 pragma Assert (Source.Last /= 0);
2231 pragma Assert (N (Source.Last).Next = 0);
2232 pragma Assert (Src_Pos /= 0);
2234 Allocate (Target, N (Src_Pos).Element, New_Node => Tgt_Pos);
2235 Insert_Internal (Target, Before => Before, New_Node => Tgt_Pos);
2237 if Source.Length = 1 then
2238 pragma Assert (Source.First = Source.Last);
2239 pragma Assert (Src_Pos = Source.First);
2241 Source.First := 0;
2242 Source.Last := 0;
2244 elsif Src_Pos = Source.First then
2245 pragma Assert (N (N (Src_Pos).Next).Prev = Src_Pos);
2247 Source.First := N (Src_Pos).Next;
2248 N (Source.First).Prev := 0;
2250 elsif Src_Pos = Source.Last then
2251 pragma Assert (N (N (Src_Pos).Prev).Next = Src_Pos);
2253 Source.Last := N (Src_Pos).Prev;
2254 N (Source.Last).Next := 0;
2256 else
2257 pragma Assert (Source.Length >= 3);
2258 pragma Assert (N (N (Src_Pos).Next).Prev = Src_Pos);
2259 pragma Assert (N (N (Src_Pos).Prev).Next = Src_Pos);
2261 N (N (Src_Pos).Next).Prev := N (Src_Pos).Prev;
2262 N (N (Src_Pos).Prev).Next := N (Src_Pos).Next;
2263 end if;
2265 Source.Length := Source.Length - 1;
2266 Free (Source, Src_Pos);
2267 end Splice_Internal;
2269 ----------
2270 -- Swap --
2271 ----------
2273 procedure Swap
2274 (Container : in out List;
2275 I, J : Cursor)
2277 begin
2278 if I.Node = 0 then
2279 raise Constraint_Error with "I cursor has no element";
2280 end if;
2282 if J.Node = 0 then
2283 raise Constraint_Error with "J cursor has no element";
2284 end if;
2286 if I.Container /= Container'Unchecked_Access then
2287 raise Program_Error with "I cursor designates wrong container";
2288 end if;
2290 if J.Container /= Container'Unchecked_Access then
2291 raise Program_Error with "J cursor designates wrong container";
2292 end if;
2294 if I.Node = J.Node then
2295 return;
2296 end if;
2298 if Container.Lock > 0 then
2299 raise Program_Error with
2300 "attempt to tamper with elements (list is locked)";
2301 end if;
2303 pragma Assert (Vet (I), "bad I cursor in Swap");
2304 pragma Assert (Vet (J), "bad J cursor in Swap");
2306 declare
2307 EI : Element_Type renames Container.Nodes (I.Node).Element;
2308 EJ : Element_Type renames Container.Nodes (J.Node).Element;
2310 EI_Copy : constant Element_Type := EI;
2312 begin
2313 EI := EJ;
2314 EJ := EI_Copy;
2315 end;
2316 end Swap;
2318 ----------------
2319 -- Swap_Links --
2320 ----------------
2322 procedure Swap_Links
2323 (Container : in out List;
2324 I, J : Cursor)
2326 begin
2327 if I.Node = 0 then
2328 raise Constraint_Error with "I cursor has no element";
2329 end if;
2331 if J.Node = 0 then
2332 raise Constraint_Error with "J cursor has no element";
2333 end if;
2335 if I.Container /= Container'Unrestricted_Access then
2336 raise Program_Error with "I cursor designates wrong container";
2337 end if;
2339 if J.Container /= Container'Unrestricted_Access then
2340 raise Program_Error with "J cursor designates wrong container";
2341 end if;
2343 if I.Node = J.Node then
2344 return;
2345 end if;
2347 if Container.Busy > 0 then
2348 raise Program_Error with
2349 "attempt to tamper with cursors (list is busy)";
2350 end if;
2352 pragma Assert (Vet (I), "bad I cursor in Swap_Links");
2353 pragma Assert (Vet (J), "bad J cursor in Swap_Links");
2355 declare
2356 I_Next : constant Cursor := Next (I);
2358 begin
2359 if I_Next = J then
2360 Splice (Container, Before => I, Position => J);
2362 else
2363 declare
2364 J_Next : constant Cursor := Next (J);
2366 begin
2367 if J_Next = I then
2368 Splice (Container, Before => J, Position => I);
2370 else
2371 pragma Assert (Container.Length >= 3);
2373 Splice (Container, Before => I_Next, Position => J);
2374 Splice (Container, Before => J_Next, Position => I);
2375 end if;
2376 end;
2377 end if;
2378 end;
2379 end Swap_Links;
2381 --------------------
2382 -- Update_Element --
2383 --------------------
2385 procedure Update_Element
2386 (Container : in out List;
2387 Position : Cursor;
2388 Process : not null access procedure (Element : in out Element_Type))
2390 begin
2391 if Position.Node = 0 then
2392 raise Constraint_Error with "Position cursor has no element";
2393 end if;
2395 if Position.Container /= Container'Unchecked_Access then
2396 raise Program_Error with
2397 "Position cursor designates wrong container";
2398 end if;
2400 pragma Assert (Vet (Position), "bad cursor in Update_Element");
2402 declare
2403 B : Natural renames Container.Busy;
2404 L : Natural renames Container.Lock;
2406 begin
2407 B := B + 1;
2408 L := L + 1;
2410 declare
2411 N : Node_Type renames Container.Nodes (Position.Node);
2412 begin
2413 Process (N.Element);
2414 exception
2415 when others =>
2416 L := L - 1;
2417 B := B - 1;
2418 raise;
2419 end;
2421 L := L - 1;
2422 B := B - 1;
2423 end;
2424 end Update_Element;
2426 ---------
2427 -- Vet --
2428 ---------
2430 function Vet (Position : Cursor) return Boolean is
2431 begin
2432 if Position.Node = 0 then
2433 return Position.Container = null;
2434 end if;
2436 if Position.Container = null then
2437 return False;
2438 end if;
2440 declare
2441 L : List renames Position.Container.all;
2442 N : Node_Array renames L.Nodes;
2444 begin
2445 if L.Length = 0 then
2446 return False;
2447 end if;
2449 if L.First = 0 or L.First > L.Capacity then
2450 return False;
2451 end if;
2453 if L.Last = 0 or L.Last > L.Capacity then
2454 return False;
2455 end if;
2457 if N (L.First).Prev /= 0 then
2458 return False;
2459 end if;
2461 if N (L.Last).Next /= 0 then
2462 return False;
2463 end if;
2465 if Position.Node > L.Capacity then
2466 return False;
2467 end if;
2469 -- An invariant of an active node is that its Previous and Next
2470 -- components are non-negative. Operation Free sets the Previous
2471 -- component of the node to the value -1 before actually deallocating
2472 -- the node, to mark the node as inactive. (By "dellocating" we mean
2473 -- only that the node is linked onto a list of inactive nodes used
2474 -- for storage.) This marker gives us a simple way to detect a
2475 -- dangling reference to a node.
2477 if N (Position.Node).Prev < 0 then -- see Free
2478 return False;
2479 end if;
2481 if N (Position.Node).Prev > L.Capacity then
2482 return False;
2483 end if;
2485 if N (Position.Node).Next = Position.Node then
2486 return False;
2487 end if;
2489 if N (Position.Node).Prev = Position.Node then
2490 return False;
2491 end if;
2493 if N (Position.Node).Prev = 0
2494 and then Position.Node /= L.First
2495 then
2496 return False;
2497 end if;
2499 pragma Assert (N (Position.Node).Prev /= 0
2500 or else Position.Node = L.First);
2502 if N (Position.Node).Next = 0
2503 and then Position.Node /= L.Last
2504 then
2505 return False;
2506 end if;
2508 pragma Assert (N (Position.Node).Next /= 0
2509 or else Position.Node = L.Last);
2511 if L.Length = 1 then
2512 return L.First = L.Last;
2513 end if;
2515 if L.First = L.Last then
2516 return False;
2517 end if;
2519 if N (L.First).Next = 0 then
2520 return False;
2521 end if;
2523 if N (L.Last).Prev = 0 then
2524 return False;
2525 end if;
2527 if N (N (L.First).Next).Prev /= L.First then
2528 return False;
2529 end if;
2531 if N (N (L.Last).Prev).Next /= L.Last then
2532 return False;
2533 end if;
2535 if L.Length = 2 then
2536 if N (L.First).Next /= L.Last then
2537 return False;
2538 end if;
2540 if N (L.Last).Prev /= L.First then
2541 return False;
2542 end if;
2544 return True;
2545 end if;
2547 if N (L.First).Next = L.Last then
2548 return False;
2549 end if;
2551 if N (L.Last).Prev = L.First then
2552 return False;
2553 end if;
2555 -- Eliminate earlier possibility
2557 if Position.Node = L.First then
2558 return True;
2559 end if;
2561 pragma Assert (N (Position.Node).Prev /= 0);
2563 -- Eliminate another possibility
2565 if Position.Node = L.Last then
2566 return True;
2567 end if;
2569 pragma Assert (N (Position.Node).Next /= 0);
2571 if N (N (Position.Node).Next).Prev /= Position.Node then
2572 return False;
2573 end if;
2575 if N (N (Position.Node).Prev).Next /= Position.Node then
2576 return False;
2577 end if;
2579 if L.Length = 3 then
2580 if N (L.First).Next /= Position.Node then
2581 return False;
2582 end if;
2584 if N (L.Last).Prev /= Position.Node then
2585 return False;
2586 end if;
2587 end if;
2589 return True;
2590 end;
2591 end Vet;
2593 -----------
2594 -- Write --
2595 -----------
2597 procedure Write
2598 (Stream : not null access Root_Stream_Type'Class;
2599 Item : List)
2601 Node : Count_Type;
2603 begin
2604 Count_Type'Base'Write (Stream, Item.Length);
2606 Node := Item.First;
2607 while Node /= 0 loop
2608 Element_Type'Write (Stream, Item.Nodes (Node).Element);
2609 Node := Item.Nodes (Node).Next;
2610 end loop;
2611 end Write;
2613 procedure Write
2614 (Stream : not null access Root_Stream_Type'Class;
2615 Item : Cursor)
2617 begin
2618 raise Program_Error with "attempt to stream list cursor";
2619 end Write;
2621 procedure Write
2622 (Stream : not null access Root_Stream_Type'Class;
2623 Item : Reference_Type)
2625 begin
2626 raise Program_Error with "attempt to stream reference";
2627 end Write;
2629 procedure Write
2630 (Stream : not null access Root_Stream_Type'Class;
2631 Item : Constant_Reference_Type)
2633 begin
2634 raise Program_Error with "attempt to stream reference";
2635 end Write;
2637 end Ada.Containers.Bounded_Doubly_Linked_Lists;