2014-10-10 Robert Dewar <dewar@adacore.com>
[official-gcc.git] / gcc / ada / a-cbdlli.adb
blob796d87b7e1b6e2c50ece82812aa40278ae9856da
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 -----------------------
35 -- Local Subprograms --
36 -----------------------
38 procedure Allocate
39 (Container : in out List;
40 New_Item : Element_Type;
41 New_Node : out Count_Type);
43 procedure Allocate
44 (Container : in out List;
45 Stream : not null access Root_Stream_Type'Class;
46 New_Node : out Count_Type);
48 procedure Free
49 (Container : in out List;
50 X : Count_Type);
52 procedure Insert_Internal
53 (Container : in out List;
54 Before : Count_Type;
55 New_Node : Count_Type);
57 procedure Splice_Internal
58 (Target : in out List;
59 Before : Count_Type;
60 Source : in out List);
62 procedure Splice_Internal
63 (Target : in out List;
64 Before : Count_Type;
65 Source : in out List;
66 Src_Pos : Count_Type;
67 Tgt_Pos : out Count_Type);
69 function Vet (Position : Cursor) return Boolean;
70 -- Checks invariants of the cursor and its designated container, as a
71 -- simple way of detecting dangling references (see operation Free for a
72 -- description of the detection mechanism), returning True if all checks
73 -- pass. Invocations of Vet are used here as the argument of pragma Assert,
74 -- so the checks are performed only when assertions are enabled.
76 ---------
77 -- "=" --
78 ---------
80 function "=" (Left, Right : List) return Boolean is
81 BL : Natural renames Left'Unrestricted_Access.Busy;
82 LL : Natural renames Left'Unrestricted_Access.Lock;
84 BR : Natural renames Right'Unrestricted_Access.Busy;
85 LR : Natural renames Right'Unrestricted_Access.Lock;
87 LN : Node_Array renames Left.Nodes;
88 RN : Node_Array renames Right.Nodes;
90 LI : Count_Type;
91 RI : Count_Type;
93 Result : Boolean;
95 begin
96 if Left'Address = Right'Address then
97 return True;
98 end if;
100 if Left.Length /= Right.Length then
101 return False;
102 end if;
104 -- Per AI05-0022, the container implementation is required to detect
105 -- element tampering by a generic actual subprogram.
107 BL := BL + 1;
108 LL := LL + 1;
110 BR := BR + 1;
111 LR := LR + 1;
113 LI := Left.First;
114 RI := Right.First;
115 Result := True;
116 for J in 1 .. Left.Length loop
117 if LN (LI).Element /= RN (RI).Element then
118 Result := False;
119 exit;
120 end if;
122 LI := LN (LI).Next;
123 RI := RN (RI).Next;
124 end loop;
126 BL := BL - 1;
127 LL := LL - 1;
129 BR := BR - 1;
130 LR := LR - 1;
132 return Result;
134 exception
135 when others =>
136 BL := BL - 1;
137 LL := LL - 1;
139 BR := BR - 1;
140 LR := LR - 1;
142 raise;
143 end "=";
145 --------------
146 -- Allocate --
147 --------------
149 procedure Allocate
150 (Container : in out List;
151 New_Item : Element_Type;
152 New_Node : out Count_Type)
154 N : Node_Array renames Container.Nodes;
156 begin
157 if Container.Free >= 0 then
158 New_Node := Container.Free;
160 -- We always perform the assignment first, before we change container
161 -- state, in order to defend against exceptions duration assignment.
163 N (New_Node).Element := New_Item;
164 Container.Free := N (New_Node).Next;
166 else
167 -- A negative free store value means that the links of the nodes in
168 -- the free store have not been initialized. In this case, the nodes
169 -- are physically contiguous in the array, starting at the index that
170 -- is the absolute value of the Container.Free, and continuing until
171 -- the end of the array (Nodes'Last).
173 New_Node := abs Container.Free;
175 -- As above, we perform this assignment first, before modifying any
176 -- container state.
178 N (New_Node).Element := New_Item;
179 Container.Free := Container.Free - 1;
180 end if;
181 end Allocate;
183 procedure Allocate
184 (Container : in out List;
185 Stream : not null access Root_Stream_Type'Class;
186 New_Node : out Count_Type)
188 N : Node_Array renames Container.Nodes;
190 begin
191 if Container.Free >= 0 then
192 New_Node := Container.Free;
194 -- We always perform the assignment first, before we change container
195 -- state, in order to defend against exceptions duration assignment.
197 Element_Type'Read (Stream, N (New_Node).Element);
198 Container.Free := N (New_Node).Next;
200 else
201 -- A negative free store value means that the links of the nodes in
202 -- the free store have not been initialized. In this case, the nodes
203 -- are physically contiguous in the array, starting at the index that
204 -- is the absolute value of the Container.Free, and continuing until
205 -- the end of the array (Nodes'Last).
207 New_Node := abs Container.Free;
209 -- As above, we perform this assignment first, before modifying any
210 -- container state.
212 Element_Type'Read (Stream, N (New_Node).Element);
213 Container.Free := Container.Free - 1;
214 end if;
215 end Allocate;
217 ------------
218 -- Append --
219 ------------
221 procedure Append
222 (Container : in out List;
223 New_Item : Element_Type;
224 Count : Count_Type := 1)
226 begin
227 Insert (Container, No_Element, New_Item, Count);
228 end Append;
230 ------------
231 -- Adjust --
232 ------------
234 procedure Adjust (Control : in out Reference_Control_Type) is
235 begin
236 if Control.Container /= null then
237 declare
238 C : List renames Control.Container.all;
239 B : Natural renames C.Busy;
240 L : Natural renames C.Lock;
241 begin
242 B := B + 1;
243 L := L + 1;
244 end;
245 end if;
246 end Adjust;
248 ------------
249 -- Assign --
250 ------------
252 procedure Assign (Target : in out List; Source : List) is
253 SN : Node_Array renames Source.Nodes;
254 J : Count_Type;
256 begin
257 if Target'Address = Source'Address then
258 return;
259 end if;
261 if Target.Capacity < Source.Length then
262 raise Capacity_Error -- ???
263 with "Target capacity is less than Source length";
264 end if;
266 Target.Clear;
268 J := Source.First;
269 while J /= 0 loop
270 Target.Append (SN (J).Element);
271 J := SN (J).Next;
272 end loop;
273 end Assign;
275 -----------
276 -- Clear --
277 -----------
279 procedure Clear (Container : in out List) is
280 N : Node_Array renames Container.Nodes;
281 X : Count_Type;
283 begin
284 if Container.Length = 0 then
285 pragma Assert (Container.First = 0);
286 pragma Assert (Container.Last = 0);
287 pragma Assert (Container.Busy = 0);
288 pragma Assert (Container.Lock = 0);
289 return;
290 end if;
292 pragma Assert (Container.First >= 1);
293 pragma Assert (Container.Last >= 1);
294 pragma Assert (N (Container.First).Prev = 0);
295 pragma Assert (N (Container.Last).Next = 0);
297 if Container.Busy > 0 then
298 raise Program_Error with
299 "attempt to tamper with cursors (list is busy)";
300 end if;
302 while Container.Length > 1 loop
303 X := Container.First;
304 pragma Assert (N (N (X).Next).Prev = Container.First);
306 Container.First := N (X).Next;
307 N (Container.First).Prev := 0;
309 Container.Length := Container.Length - 1;
311 Free (Container, X);
312 end loop;
314 X := Container.First;
315 pragma Assert (X = Container.Last);
317 Container.First := 0;
318 Container.Last := 0;
319 Container.Length := 0;
321 Free (Container, X);
322 end Clear;
324 ------------------------
325 -- Constant_Reference --
326 ------------------------
328 function Constant_Reference
329 (Container : aliased List;
330 Position : Cursor) return Constant_Reference_Type
332 begin
333 if Position.Container = null then
334 raise Constraint_Error with "Position cursor has no element";
336 elsif Position.Container /= Container'Unrestricted_Access then
337 raise Program_Error with
338 "Position cursor designates wrong container";
340 else
341 pragma Assert (Vet (Position), "bad cursor in Constant_Reference");
343 declare
344 N : Node_Type renames Container.Nodes (Position.Node);
345 B : Natural renames Position.Container.Busy;
346 L : Natural renames Position.Container.Lock;
347 begin
348 return R : constant Constant_Reference_Type :=
349 (Element => N.Element'Access,
350 Control => (Controlled with Container'Unrestricted_Access))
352 B := B + 1;
353 L := L + 1;
354 end return;
355 end;
356 end if;
357 end Constant_Reference;
359 --------------
360 -- Contains --
361 --------------
363 function Contains
364 (Container : List;
365 Item : Element_Type) return Boolean
367 begin
368 return Find (Container, Item) /= No_Element;
369 end Contains;
371 ----------
372 -- Copy --
373 ----------
375 function Copy (Source : List; Capacity : Count_Type := 0) return List is
376 C : Count_Type;
378 begin
379 if Capacity = 0 then
380 C := Source.Length;
381 elsif Capacity >= Source.Length then
382 C := Capacity;
383 else
384 raise Capacity_Error with "Capacity value too small";
385 end if;
387 return Target : List (Capacity => C) do
388 Assign (Target => Target, Source => Source);
389 end return;
390 end Copy;
392 ------------
393 -- Delete --
394 ------------
396 procedure Delete
397 (Container : in out List;
398 Position : in out Cursor;
399 Count : Count_Type := 1)
401 N : Node_Array renames Container.Nodes;
402 X : Count_Type;
404 begin
405 if Position.Node = 0 then
406 raise Constraint_Error with
407 "Position cursor has no element";
408 end if;
410 if Position.Container /= Container'Unrestricted_Access then
411 raise Program_Error with
412 "Position cursor designates wrong container";
413 end if;
415 pragma Assert (Vet (Position), "bad cursor in Delete");
416 pragma Assert (Container.First >= 1);
417 pragma Assert (Container.Last >= 1);
418 pragma Assert (N (Container.First).Prev = 0);
419 pragma Assert (N (Container.Last).Next = 0);
421 if Position.Node = Container.First then
422 Delete_First (Container, Count);
423 Position := No_Element;
424 return;
425 end if;
427 if Count = 0 then
428 Position := No_Element;
429 return;
430 end if;
432 if Container.Busy > 0 then
433 raise Program_Error with
434 "attempt to tamper with cursors (list is busy)";
435 end if;
437 for Index in 1 .. Count loop
438 pragma Assert (Container.Length >= 2);
440 X := Position.Node;
441 Container.Length := Container.Length - 1;
443 if X = Container.Last then
444 Position := No_Element;
446 Container.Last := N (X).Prev;
447 N (Container.Last).Next := 0;
449 Free (Container, X);
450 return;
451 end if;
453 Position.Node := N (X).Next;
455 N (N (X).Next).Prev := N (X).Prev;
456 N (N (X).Prev).Next := N (X).Next;
458 Free (Container, X);
459 end loop;
461 Position := No_Element;
462 end Delete;
464 ------------------
465 -- Delete_First --
466 ------------------
468 procedure Delete_First
469 (Container : in out List;
470 Count : Count_Type := 1)
472 N : Node_Array renames Container.Nodes;
473 X : Count_Type;
475 begin
476 if Count >= Container.Length then
477 Clear (Container);
478 return;
479 end if;
481 if Count = 0 then
482 return;
483 end if;
485 if Container.Busy > 0 then
486 raise Program_Error with
487 "attempt to tamper with cursors (list is busy)";
488 end if;
490 for J in 1 .. Count loop
491 X := Container.First;
492 pragma Assert (N (N (X).Next).Prev = Container.First);
494 Container.First := N (X).Next;
495 N (Container.First).Prev := 0;
497 Container.Length := Container.Length - 1;
499 Free (Container, X);
500 end loop;
501 end Delete_First;
503 -----------------
504 -- Delete_Last --
505 -----------------
507 procedure Delete_Last
508 (Container : in out List;
509 Count : Count_Type := 1)
511 N : Node_Array renames Container.Nodes;
512 X : Count_Type;
514 begin
515 if Count >= Container.Length then
516 Clear (Container);
517 return;
518 end if;
520 if Count = 0 then
521 return;
522 end if;
524 if Container.Busy > 0 then
525 raise Program_Error with
526 "attempt to tamper with cursors (list is busy)";
527 end if;
529 for J in 1 .. Count loop
530 X := Container.Last;
531 pragma Assert (N (N (X).Prev).Next = Container.Last);
533 Container.Last := N (X).Prev;
534 N (Container.Last).Next := 0;
536 Container.Length := Container.Length - 1;
538 Free (Container, X);
539 end loop;
540 end Delete_Last;
542 -------------
543 -- Element --
544 -------------
546 function Element (Position : Cursor) return Element_Type is
547 begin
548 if Position.Node = 0 then
549 raise Constraint_Error with
550 "Position cursor has no element";
552 else
553 pragma Assert (Vet (Position), "bad cursor in Element");
555 return Position.Container.Nodes (Position.Node).Element;
556 end if;
557 end Element;
559 --------------
560 -- Finalize --
561 --------------
563 procedure Finalize (Object : in out Iterator) is
564 begin
565 if Object.Container /= null then
566 declare
567 B : Natural renames Object.Container.all.Busy;
568 begin
569 B := B - 1;
570 end;
571 end if;
572 end Finalize;
574 procedure Finalize (Control : in out Reference_Control_Type) is
575 begin
576 if Control.Container /= null then
577 declare
578 C : List renames Control.Container.all;
579 B : Natural renames C.Busy;
580 L : Natural renames C.Lock;
581 begin
582 B := B - 1;
583 L := L - 1;
584 end;
586 Control.Container := null;
587 end if;
588 end Finalize;
590 ----------
591 -- Find --
592 ----------
594 function Find
595 (Container : List;
596 Item : Element_Type;
597 Position : Cursor := No_Element) return Cursor
599 Nodes : Node_Array renames Container.Nodes;
600 Node : Count_Type := Position.Node;
602 begin
603 if Node = 0 then
604 Node := Container.First;
606 else
607 if Position.Container /= Container'Unrestricted_Access then
608 raise Program_Error with
609 "Position cursor designates wrong container";
610 end if;
612 pragma Assert (Vet (Position), "bad cursor in Find");
613 end if;
615 -- Per AI05-0022, the container implementation is required to detect
616 -- element tampering by a generic actual subprogram.
618 declare
619 B : Natural renames Container'Unrestricted_Access.Busy;
620 L : Natural renames Container'Unrestricted_Access.Lock;
622 Result : Count_Type;
624 begin
625 B := B + 1;
626 L := L + 1;
628 Result := 0;
629 while Node /= 0 loop
630 if Nodes (Node).Element = Item then
631 Result := Node;
632 exit;
633 end if;
635 Node := Nodes (Node).Next;
636 end loop;
638 B := B - 1;
639 L := L - 1;
641 if Result = 0 then
642 return No_Element;
643 else
644 return Cursor'(Container'Unrestricted_Access, Result);
645 end if;
647 exception
648 when others =>
649 B := B - 1;
650 L := L - 1;
651 raise;
652 end;
653 end Find;
655 -----------
656 -- First --
657 -----------
659 function First (Container : List) return Cursor is
660 begin
661 if Container.First = 0 then
662 return No_Element;
663 else
664 return Cursor'(Container'Unrestricted_Access, Container.First);
665 end if;
666 end First;
668 function First (Object : Iterator) return Cursor is
669 begin
670 -- The value of the iterator object's Node component influences the
671 -- behavior of the First (and Last) selector function.
673 -- When the Node component is 0, this means the iterator object was
674 -- constructed without a start expression, in which case the (forward)
675 -- iteration starts from the (logical) beginning of the entire sequence
676 -- of items (corresponding to Container.First, for a forward iterator).
678 -- Otherwise, this is iteration over a partial sequence of items. When
679 -- the Node component is positive, the iterator object was constructed
680 -- with a start expression, that specifies the position from which the
681 -- (forward) partial iteration begins.
683 if Object.Node = 0 then
684 return Bounded_Doubly_Linked_Lists.First (Object.Container.all);
685 else
686 return Cursor'(Object.Container, Object.Node);
687 end if;
688 end First;
690 -------------------
691 -- First_Element --
692 -------------------
694 function First_Element (Container : List) return Element_Type is
695 begin
696 if Container.First = 0 then
697 raise Constraint_Error with "list is empty";
698 else
699 return Container.Nodes (Container.First).Element;
700 end if;
701 end First_Element;
703 ----------
704 -- Free --
705 ----------
707 procedure Free
708 (Container : in out List;
709 X : Count_Type)
711 pragma Assert (X > 0);
712 pragma Assert (X <= Container.Capacity);
714 N : Node_Array renames Container.Nodes;
715 pragma Assert (N (X).Prev >= 0); -- node is active
717 begin
718 -- The list container actually contains two lists: one for the "active"
719 -- nodes that contain elements that have been inserted onto the list,
720 -- and another for the "inactive" nodes for the free store.
722 -- We desire that merely declaring an object should have only minimal
723 -- cost; specially, we want to avoid having to initialize the free
724 -- store (to fill in the links), especially if the capacity is large.
726 -- The head of the free list is indicated by Container.Free. If its
727 -- value is non-negative, then the free store has been initialized in
728 -- the "normal" way: Container.Free points to the head of the list of
729 -- free (inactive) nodes, and the value 0 means the free list is empty.
730 -- Each node on the free list has been initialized to point to the next
731 -- free node (via its Next component), and the value 0 means that this
732 -- is the last free node.
734 -- If Container.Free is negative, then the links on the free store have
735 -- not been initialized. In this case the link values are implied: the
736 -- free store comprises the components of the node array started with
737 -- the absolute value of Container.Free, and continuing until the end of
738 -- the array (Nodes'Last).
740 -- If the list container is manipulated on one end only (for example if
741 -- the container were being used as a stack), then there is no need to
742 -- initialize the free store, since the inactive nodes are physically
743 -- contiguous (in fact, they lie immediately beyond the logical end
744 -- being manipulated). The only time we need to actually initialize the
745 -- nodes in the free store is if the node that becomes inactive is not
746 -- at the end of the list. The free store would then be discontiguous
747 -- and so its nodes would need to be linked in the traditional way.
749 -- ???
750 -- It might be possible to perform an optimization here. Suppose that
751 -- the free store can be represented as having two parts: one comprising
752 -- the non-contiguous inactive nodes linked together in the normal way,
753 -- and the other comprising the contiguous inactive nodes (that are not
754 -- linked together, at the end of the nodes array). This would allow us
755 -- to never have to initialize the free store, except in a lazy way as
756 -- nodes become inactive.
758 -- When an element is deleted from the list container, its node becomes
759 -- inactive, and so we set its Prev component to a negative value, to
760 -- indicate that it is now inactive. This provides a useful way to
761 -- detect a dangling cursor reference (and which is used in Vet).
763 N (X).Prev := -1; -- Node is deallocated (not on active list)
765 if Container.Free >= 0 then
767 -- The free store has previously been initialized. All we need to
768 -- do here is link the newly-free'd node onto the free list.
770 N (X).Next := Container.Free;
771 Container.Free := X;
773 elsif X + 1 = abs Container.Free then
775 -- The free store has not been initialized, and the node becoming
776 -- inactive immediately precedes the start of the free store. All
777 -- we need to do is move the start of the free store back by one.
779 -- Note: initializing Next to zero is not strictly necessary but
780 -- seems cleaner and marginally safer.
782 N (X).Next := 0;
783 Container.Free := Container.Free + 1;
785 else
786 -- The free store has not been initialized, and the node becoming
787 -- inactive does not immediately precede the free store. Here we
788 -- first initialize the free store (meaning the links are given
789 -- values in the traditional way), and then link the newly-free'd
790 -- node onto the head of the free store.
792 -- ???
793 -- See the comments above for an optimization opportunity. If the
794 -- next link for a node on the free store is negative, then this
795 -- means the remaining nodes on the free store are physically
796 -- contiguous, starting as the absolute value of that index value.
798 Container.Free := abs Container.Free;
800 if Container.Free > Container.Capacity then
801 Container.Free := 0;
803 else
804 for I in Container.Free .. Container.Capacity - 1 loop
805 N (I).Next := I + 1;
806 end loop;
808 N (Container.Capacity).Next := 0;
809 end if;
811 N (X).Next := Container.Free;
812 Container.Free := X;
813 end if;
814 end Free;
816 ---------------------
817 -- Generic_Sorting --
818 ---------------------
820 package body Generic_Sorting is
822 ---------------
823 -- Is_Sorted --
824 ---------------
826 function Is_Sorted (Container : List) return Boolean is
827 B : Natural renames Container'Unrestricted_Access.Busy;
828 L : Natural renames Container'Unrestricted_Access.Lock;
830 Nodes : Node_Array renames Container.Nodes;
831 Node : Count_Type;
833 Result : Boolean;
835 begin
836 -- Per AI05-0022, the container implementation is required to detect
837 -- element tampering by a generic actual subprogram.
839 B := B + 1;
840 L := L + 1;
842 Node := Container.First;
843 Result := True;
844 for J in 2 .. Container.Length loop
845 if Nodes (Nodes (Node).Next).Element < Nodes (Node).Element then
846 Result := False;
847 exit;
848 end if;
850 Node := Nodes (Node).Next;
851 end loop;
853 B := B - 1;
854 L := L - 1;
856 return Result;
858 exception
859 when others =>
860 B := B - 1;
861 L := L - 1;
862 raise;
863 end Is_Sorted;
865 -----------
866 -- Merge --
867 -----------
869 procedure Merge
870 (Target : in out List;
871 Source : in out List)
873 begin
874 -- The semantics of Merge changed slightly per AI05-0021. It was
875 -- originally the case that if Target and Source denoted the same
876 -- container object, then the GNAT implementation of Merge did
877 -- nothing. However, it was argued that RM05 did not precisely
878 -- specify the semantics for this corner case. The decision of the
879 -- ARG was that if Target and Source denote the same non-empty
880 -- container object, then Program_Error is raised.
882 if Source.Is_Empty then
883 return;
884 end if;
886 if Target'Address = Source'Address then
887 raise Program_Error with
888 "Target and Source denote same non-empty container";
889 end if;
891 if Target.Length > Count_Type'Last - Source.Length then
892 raise Constraint_Error with "new length exceeds maximum";
893 end if;
895 if Target.Length + Source.Length > Target.Capacity then
896 raise Capacity_Error with "new length exceeds target capacity";
897 end if;
899 if Target.Busy > 0 then
900 raise Program_Error with
901 "attempt to tamper with cursors of Target (list is busy)";
902 end if;
904 if Source.Busy > 0 then
905 raise Program_Error with
906 "attempt to tamper with cursors of Source (list is busy)";
907 end if;
909 -- Per AI05-0022, the container implementation is required to detect
910 -- element tampering by a generic actual subprogram.
912 declare
913 TB : Natural renames Target.Busy;
914 TL : Natural renames Target.Lock;
916 SB : Natural renames Source.Busy;
917 SL : Natural renames Source.Lock;
919 LN : Node_Array renames Target.Nodes;
920 RN : Node_Array renames Source.Nodes;
922 LI, LJ, RI, RJ : Count_Type;
924 begin
925 TB := TB + 1;
926 TL := TL + 1;
928 SB := SB + 1;
929 SL := SL + 1;
931 LI := Target.First;
932 RI := Source.First;
933 while RI /= 0 loop
934 pragma Assert (RN (RI).Next = 0
935 or else not (RN (RN (RI).Next).Element <
936 RN (RI).Element));
938 if LI = 0 then
939 Splice_Internal (Target, 0, Source);
940 exit;
941 end if;
943 pragma Assert (LN (LI).Next = 0
944 or else not (LN (LN (LI).Next).Element <
945 LN (LI).Element));
947 if RN (RI).Element < LN (LI).Element then
948 RJ := RI;
949 RI := RN (RI).Next;
950 Splice_Internal (Target, LI, Source, RJ, LJ);
952 else
953 LI := LN (LI).Next;
954 end if;
955 end loop;
957 TB := TB - 1;
958 TL := TL - 1;
960 SB := SB - 1;
961 SL := SL - 1;
963 exception
964 when others =>
965 TB := TB - 1;
966 TL := TL - 1;
968 SB := SB - 1;
969 SL := SL - 1;
971 raise;
972 end;
973 end Merge;
975 ----------
976 -- Sort --
977 ----------
979 procedure Sort (Container : in out List) is
980 N : Node_Array renames Container.Nodes;
982 procedure Partition (Pivot, Back : Count_Type);
983 -- What does this do ???
985 procedure Sort (Front, Back : Count_Type);
986 -- Internal procedure, what does it do??? rename it???
988 ---------------
989 -- Partition --
990 ---------------
992 procedure Partition (Pivot, Back : Count_Type) is
993 Node : Count_Type;
995 begin
996 Node := N (Pivot).Next;
997 while Node /= Back loop
998 if N (Node).Element < N (Pivot).Element then
999 declare
1000 Prev : constant Count_Type := N (Node).Prev;
1001 Next : constant Count_Type := N (Node).Next;
1003 begin
1004 N (Prev).Next := Next;
1006 if Next = 0 then
1007 Container.Last := Prev;
1008 else
1009 N (Next).Prev := Prev;
1010 end if;
1012 N (Node).Next := Pivot;
1013 N (Node).Prev := N (Pivot).Prev;
1015 N (Pivot).Prev := Node;
1017 if N (Node).Prev = 0 then
1018 Container.First := Node;
1019 else
1020 N (N (Node).Prev).Next := Node;
1021 end if;
1023 Node := Next;
1024 end;
1026 else
1027 Node := N (Node).Next;
1028 end if;
1029 end loop;
1030 end Partition;
1032 ----------
1033 -- Sort --
1034 ----------
1036 procedure Sort (Front, Back : Count_Type) is
1037 Pivot : constant Count_Type :=
1038 (if Front = 0 then Container.First else N (Front).Next);
1039 begin
1040 if Pivot /= Back then
1041 Partition (Pivot, Back);
1042 Sort (Front, Pivot);
1043 Sort (Pivot, Back);
1044 end if;
1045 end Sort;
1047 -- Start of processing for Sort
1049 begin
1050 if Container.Length <= 1 then
1051 return;
1052 end if;
1054 pragma Assert (N (Container.First).Prev = 0);
1055 pragma Assert (N (Container.Last).Next = 0);
1057 if Container.Busy > 0 then
1058 raise Program_Error with
1059 "attempt to tamper with cursors (list is busy)";
1060 end if;
1062 -- Per AI05-0022, the container implementation is required to detect
1063 -- element tampering by a generic actual subprogram.
1065 declare
1066 B : Natural renames Container.Busy;
1067 L : Natural renames Container.Lock;
1069 begin
1070 B := B + 1;
1071 L := L + 1;
1073 Sort (Front => 0, Back => 0);
1075 B := B - 1;
1076 L := L - 1;
1078 exception
1079 when others =>
1080 B := B - 1;
1081 L := L - 1;
1082 raise;
1083 end;
1085 pragma Assert (N (Container.First).Prev = 0);
1086 pragma Assert (N (Container.Last).Next = 0);
1087 end Sort;
1089 end Generic_Sorting;
1091 -----------------
1092 -- Has_Element --
1093 -----------------
1095 function Has_Element (Position : Cursor) return Boolean is
1096 begin
1097 pragma Assert (Vet (Position), "bad cursor in Has_Element");
1098 return Position.Node /= 0;
1099 end Has_Element;
1101 ------------
1102 -- Insert --
1103 ------------
1105 procedure Insert
1106 (Container : in out List;
1107 Before : Cursor;
1108 New_Item : Element_Type;
1109 Position : out Cursor;
1110 Count : Count_Type := 1)
1112 First_Node : Count_Type;
1113 New_Node : Count_Type;
1115 begin
1116 if Before.Container /= null then
1117 if Before.Container /= Container'Unrestricted_Access then
1118 raise Program_Error with
1119 "Before cursor designates wrong list";
1120 end if;
1122 pragma Assert (Vet (Before), "bad cursor in Insert");
1123 end if;
1125 if Count = 0 then
1126 Position := Before;
1127 return;
1128 end if;
1130 if Container.Length > Container.Capacity - Count then
1131 raise Capacity_Error with "capacity exceeded";
1132 end if;
1134 if Container.Busy > 0 then
1135 raise Program_Error with
1136 "attempt to tamper with cursors (list is busy)";
1137 end if;
1139 Allocate (Container, New_Item, New_Node);
1140 First_Node := New_Node;
1141 Insert_Internal (Container, Before.Node, New_Node);
1143 for Index in Count_Type'(2) .. Count loop
1144 Allocate (Container, New_Item, New_Node);
1145 Insert_Internal (Container, Before.Node, New_Node);
1146 end loop;
1148 Position := Cursor'(Container'Unchecked_Access, First_Node);
1149 end Insert;
1151 procedure Insert
1152 (Container : in out List;
1153 Before : Cursor;
1154 New_Item : Element_Type;
1155 Count : Count_Type := 1)
1157 Position : Cursor;
1158 pragma Unreferenced (Position);
1159 begin
1160 Insert (Container, Before, New_Item, Position, Count);
1161 end Insert;
1163 procedure Insert
1164 (Container : in out List;
1165 Before : Cursor;
1166 Position : out Cursor;
1167 Count : Count_Type := 1)
1169 New_Item : Element_Type;
1170 pragma Unmodified (New_Item);
1171 -- OK to reference, see below
1173 begin
1174 -- There is no explicit element provided, but in an instance the element
1175 -- type may be a scalar with a Default_Value aspect, or a composite
1176 -- type with such a scalar component, or components with default
1177 -- initialization, so insert the specified number of possibly
1178 -- initialized elements at the given position.
1180 Insert (Container, Before, New_Item, Position, Count);
1181 end Insert;
1183 ---------------------
1184 -- Insert_Internal --
1185 ---------------------
1187 procedure Insert_Internal
1188 (Container : in out List;
1189 Before : Count_Type;
1190 New_Node : Count_Type)
1192 N : Node_Array renames Container.Nodes;
1194 begin
1195 if Container.Length = 0 then
1196 pragma Assert (Before = 0);
1197 pragma Assert (Container.First = 0);
1198 pragma Assert (Container.Last = 0);
1200 Container.First := New_Node;
1201 N (Container.First).Prev := 0;
1203 Container.Last := New_Node;
1204 N (Container.Last).Next := 0;
1206 -- Before = zero means append
1208 elsif Before = 0 then
1209 pragma Assert (N (Container.Last).Next = 0);
1211 N (Container.Last).Next := New_Node;
1212 N (New_Node).Prev := Container.Last;
1214 Container.Last := New_Node;
1215 N (Container.Last).Next := 0;
1217 -- Before = Container.First means prepend
1219 elsif Before = Container.First then
1220 pragma Assert (N (Container.First).Prev = 0);
1222 N (Container.First).Prev := New_Node;
1223 N (New_Node).Next := Container.First;
1225 Container.First := New_Node;
1226 N (Container.First).Prev := 0;
1228 else
1229 pragma Assert (N (Container.First).Prev = 0);
1230 pragma Assert (N (Container.Last).Next = 0);
1232 N (New_Node).Next := Before;
1233 N (New_Node).Prev := N (Before).Prev;
1235 N (N (Before).Prev).Next := New_Node;
1236 N (Before).Prev := New_Node;
1237 end if;
1239 Container.Length := Container.Length + 1;
1240 end Insert_Internal;
1242 --------------
1243 -- Is_Empty --
1244 --------------
1246 function Is_Empty (Container : List) return Boolean is
1247 begin
1248 return Container.Length = 0;
1249 end Is_Empty;
1251 -------------
1252 -- Iterate --
1253 -------------
1255 procedure Iterate
1256 (Container : List;
1257 Process : not null access procedure (Position : Cursor))
1259 B : Natural renames Container'Unrestricted_Access.all.Busy;
1260 Node : Count_Type := Container.First;
1262 begin
1263 B := B + 1;
1265 begin
1266 while Node /= 0 loop
1267 Process (Cursor'(Container'Unrestricted_Access, Node));
1268 Node := Container.Nodes (Node).Next;
1269 end loop;
1270 exception
1271 when others =>
1272 B := B - 1;
1273 raise;
1274 end;
1276 B := B - 1;
1277 end Iterate;
1279 function Iterate
1280 (Container : List)
1281 return List_Iterator_Interfaces.Reversible_Iterator'Class
1283 B : Natural renames Container'Unrestricted_Access.all.Busy;
1285 begin
1286 -- The value of the Node component influences the behavior of the First
1287 -- and Last selector functions of the iterator object. When the Node
1288 -- component is 0 (as is the case here), this means the iterator
1289 -- object was constructed without a start expression. This is a
1290 -- complete iterator, meaning that the iteration starts from the
1291 -- (logical) beginning of the sequence of items.
1293 -- Note: For a forward iterator, Container.First is the beginning, and
1294 -- for a reverse iterator, Container.Last is the beginning.
1296 return It : constant Iterator :=
1297 Iterator'(Limited_Controlled with
1298 Container => Container'Unrestricted_Access,
1299 Node => 0)
1301 B := B + 1;
1302 end return;
1303 end Iterate;
1305 function Iterate
1306 (Container : List;
1307 Start : Cursor)
1308 return List_Iterator_Interfaces.Reversible_Iterator'class
1310 B : Natural renames Container'Unrestricted_Access.all.Busy;
1312 begin
1313 -- It was formerly the case that when Start = No_Element, the partial
1314 -- iterator was defined to behave the same as for a complete iterator,
1315 -- and iterate over the entire sequence of items. However, those
1316 -- semantics were unintuitive and arguably error-prone (it is too easy
1317 -- to accidentally create an endless loop), and so they were changed,
1318 -- per the ARG meeting in Denver on 2011/11. However, there was no
1319 -- consensus about what positive meaning this corner case should have,
1320 -- and so it was decided to simply raise an exception. This does imply,
1321 -- however, that it is not possible to use a partial iterator to specify
1322 -- an empty sequence of items.
1324 if Start = No_Element then
1325 raise Constraint_Error with
1326 "Start position for iterator equals No_Element";
1327 end if;
1329 if Start.Container /= Container'Unrestricted_Access then
1330 raise Program_Error with
1331 "Start cursor of Iterate designates wrong list";
1332 end if;
1334 pragma Assert (Vet (Start), "Start cursor of Iterate is bad");
1336 -- The value of the Node component influences the behavior of the First
1337 -- and Last selector functions of the iterator object. When the Node
1338 -- component is positive (as is the case here), it means that this
1339 -- is a partial iteration, over a subset of the complete sequence of
1340 -- items. The iterator object was constructed with a start expression,
1341 -- indicating the position from which the iteration begins. Note that
1342 -- the start position has the same value irrespective of whether this
1343 -- is a forward or reverse iteration.
1345 return It : constant Iterator :=
1346 Iterator'(Limited_Controlled with
1347 Container => Container'Unrestricted_Access,
1348 Node => Start.Node)
1350 B := B + 1;
1351 end return;
1352 end Iterate;
1354 ----------
1355 -- Last --
1356 ----------
1358 function Last (Container : List) return Cursor is
1359 begin
1360 if Container.Last = 0 then
1361 return No_Element;
1362 else
1363 return Cursor'(Container'Unrestricted_Access, Container.Last);
1364 end if;
1365 end Last;
1367 function Last (Object : Iterator) return Cursor is
1368 begin
1369 -- The value of the iterator object's Node component influences the
1370 -- behavior of the Last (and First) selector function.
1372 -- When the Node component is 0, this means the iterator object was
1373 -- constructed without a start expression, in which case the (reverse)
1374 -- iteration starts from the (logical) beginning of the entire sequence
1375 -- (corresponding to Container.Last, for a reverse iterator).
1377 -- Otherwise, this is iteration over a partial sequence of items. When
1378 -- the Node component is positive, the iterator object was constructed
1379 -- with a start expression, that specifies the position from which the
1380 -- (reverse) partial iteration begins.
1382 if Object.Node = 0 then
1383 return Bounded_Doubly_Linked_Lists.Last (Object.Container.all);
1384 else
1385 return Cursor'(Object.Container, Object.Node);
1386 end if;
1387 end Last;
1389 ------------------
1390 -- Last_Element --
1391 ------------------
1393 function Last_Element (Container : List) return Element_Type is
1394 begin
1395 if Container.Last = 0 then
1396 raise Constraint_Error with "list is empty";
1397 else
1398 return Container.Nodes (Container.Last).Element;
1399 end if;
1400 end Last_Element;
1402 ------------
1403 -- Length --
1404 ------------
1406 function Length (Container : List) return Count_Type is
1407 begin
1408 return Container.Length;
1409 end Length;
1411 ----------
1412 -- Move --
1413 ----------
1415 procedure Move
1416 (Target : in out List;
1417 Source : in out List)
1419 N : Node_Array renames Source.Nodes;
1420 X : Count_Type;
1422 begin
1423 if Target'Address = Source'Address then
1424 return;
1425 end if;
1427 if Target.Capacity < Source.Length then
1428 raise Capacity_Error with "Source length exceeds Target capacity";
1429 end if;
1431 if Source.Busy > 0 then
1432 raise Program_Error with
1433 "attempt to tamper with cursors of Source (list is busy)";
1434 end if;
1436 -- Clear target, note that this checks busy bits of Target
1438 Clear (Target);
1440 while Source.Length > 1 loop
1441 pragma Assert (Source.First in 1 .. Source.Capacity);
1442 pragma Assert (Source.Last /= Source.First);
1443 pragma Assert (N (Source.First).Prev = 0);
1444 pragma Assert (N (Source.Last).Next = 0);
1446 -- Copy first element from Source to Target
1448 X := Source.First;
1449 Append (Target, N (X).Element);
1451 -- Unlink first node of Source
1453 Source.First := N (X).Next;
1454 N (Source.First).Prev := 0;
1456 Source.Length := Source.Length - 1;
1458 -- The representation invariants for Source have been restored. It is
1459 -- now safe to free the unlinked node, without fear of corrupting the
1460 -- active links of Source.
1462 -- Note that the algorithm we use here models similar algorithms used
1463 -- in the unbounded form of the doubly-linked list container. In that
1464 -- case, Free is an instantation of Unchecked_Deallocation, which can
1465 -- fail (because PE will be raised if controlled Finalize fails), so
1466 -- we must defer the call until the last step. Here in the bounded
1467 -- form, Free merely links the node we have just "deallocated" onto a
1468 -- list of inactive nodes, so technically Free cannot fail. However,
1469 -- for consistency, we handle Free the same way here as we do for the
1470 -- unbounded form, with the pessimistic assumption that it can fail.
1472 Free (Source, X);
1473 end loop;
1475 if Source.Length = 1 then
1476 pragma Assert (Source.First in 1 .. Source.Capacity);
1477 pragma Assert (Source.Last = Source.First);
1478 pragma Assert (N (Source.First).Prev = 0);
1479 pragma Assert (N (Source.Last).Next = 0);
1481 -- Copy element from Source to Target
1483 X := Source.First;
1484 Append (Target, N (X).Element);
1486 -- Unlink node of Source
1488 Source.First := 0;
1489 Source.Last := 0;
1490 Source.Length := 0;
1492 -- Return the unlinked node to the free store
1494 Free (Source, X);
1495 end if;
1496 end Move;
1498 ----------
1499 -- Next --
1500 ----------
1502 procedure Next (Position : in out Cursor) is
1503 begin
1504 Position := Next (Position);
1505 end Next;
1507 function Next (Position : Cursor) return Cursor is
1508 begin
1509 if Position.Node = 0 then
1510 return No_Element;
1511 end if;
1513 pragma Assert (Vet (Position), "bad cursor in Next");
1515 declare
1516 Nodes : Node_Array renames Position.Container.Nodes;
1517 Node : constant Count_Type := Nodes (Position.Node).Next;
1518 begin
1519 if Node = 0 then
1520 return No_Element;
1521 else
1522 return Cursor'(Position.Container, Node);
1523 end if;
1524 end;
1525 end Next;
1527 function Next
1528 (Object : Iterator;
1529 Position : Cursor) return Cursor
1531 begin
1532 if Position.Container = null then
1533 return No_Element;
1534 elsif Position.Container /= Object.Container then
1535 raise Program_Error with
1536 "Position cursor of Next designates wrong list";
1537 else
1538 return Next (Position);
1539 end if;
1540 end Next;
1542 -------------
1543 -- Prepend --
1544 -------------
1546 procedure Prepend
1547 (Container : in out List;
1548 New_Item : Element_Type;
1549 Count : Count_Type := 1)
1551 begin
1552 Insert (Container, First (Container), New_Item, Count);
1553 end Prepend;
1555 --------------
1556 -- Previous --
1557 --------------
1559 procedure Previous (Position : in out Cursor) is
1560 begin
1561 Position := Previous (Position);
1562 end Previous;
1564 function Previous (Position : Cursor) return Cursor is
1565 begin
1566 if Position.Node = 0 then
1567 return No_Element;
1568 end if;
1570 pragma Assert (Vet (Position), "bad cursor in Previous");
1572 declare
1573 Nodes : Node_Array renames Position.Container.Nodes;
1574 Node : constant Count_Type := Nodes (Position.Node).Prev;
1575 begin
1576 if Node = 0 then
1577 return No_Element;
1578 else
1579 return Cursor'(Position.Container, Node);
1580 end if;
1581 end;
1582 end Previous;
1584 function Previous
1585 (Object : Iterator;
1586 Position : Cursor) return Cursor
1588 begin
1589 if Position.Container = null then
1590 return No_Element;
1591 elsif Position.Container /= Object.Container then
1592 raise Program_Error with
1593 "Position cursor of Previous designates wrong list";
1594 else
1595 return Previous (Position);
1596 end if;
1597 end Previous;
1599 -------------------
1600 -- Query_Element --
1601 -------------------
1603 procedure Query_Element
1604 (Position : Cursor;
1605 Process : not null access procedure (Element : Element_Type))
1607 begin
1608 if Position.Node = 0 then
1609 raise Constraint_Error with
1610 "Position cursor has no element";
1611 end if;
1613 pragma Assert (Vet (Position), "bad cursor in Query_Element");
1615 declare
1616 C : List renames Position.Container.all'Unrestricted_Access.all;
1617 B : Natural renames C.Busy;
1618 L : Natural renames C.Lock;
1620 begin
1621 B := B + 1;
1622 L := L + 1;
1624 declare
1625 N : Node_Type renames C.Nodes (Position.Node);
1626 begin
1627 Process (N.Element);
1628 exception
1629 when others =>
1630 L := L - 1;
1631 B := B - 1;
1632 raise;
1633 end;
1635 L := L - 1;
1636 B := B - 1;
1637 end;
1638 end Query_Element;
1640 ----------
1641 -- Read --
1642 ----------
1644 procedure Read
1645 (Stream : not null access Root_Stream_Type'Class;
1646 Item : out List)
1648 N : Count_Type'Base;
1649 X : Count_Type;
1651 begin
1652 Clear (Item);
1653 Count_Type'Base'Read (Stream, N);
1655 if N < 0 then
1656 raise Program_Error with "bad list length (corrupt stream)";
1658 elsif N = 0 then
1659 return;
1661 elsif N > Item.Capacity then
1662 raise Constraint_Error with "length exceeds capacity";
1664 else
1665 for Idx in 1 .. N loop
1666 Allocate (Item, Stream, New_Node => X);
1667 Insert_Internal (Item, Before => 0, New_Node => X);
1668 end loop;
1669 end if;
1670 end Read;
1672 procedure Read
1673 (Stream : not null access Root_Stream_Type'Class;
1674 Item : out Cursor)
1676 begin
1677 raise Program_Error with "attempt to stream list cursor";
1678 end Read;
1680 procedure Read
1681 (Stream : not null access Root_Stream_Type'Class;
1682 Item : out Reference_Type)
1684 begin
1685 raise Program_Error with "attempt to stream reference";
1686 end Read;
1688 procedure Read
1689 (Stream : not null access Root_Stream_Type'Class;
1690 Item : out Constant_Reference_Type)
1692 begin
1693 raise Program_Error with "attempt to stream reference";
1694 end Read;
1696 ---------------
1697 -- Reference --
1698 ---------------
1700 function Reference
1701 (Container : aliased in out List;
1702 Position : Cursor) return Reference_Type
1704 begin
1705 if Position.Container = null then
1706 raise Constraint_Error with "Position cursor has no element";
1708 elsif Position.Container /= Container'Unrestricted_Access then
1709 raise Program_Error with
1710 "Position cursor designates wrong container";
1712 else
1713 pragma Assert (Vet (Position), "bad cursor in function Reference");
1715 declare
1716 N : Node_Type renames Container.Nodes (Position.Node);
1717 B : Natural renames Container.Busy;
1718 L : Natural renames Container.Lock;
1719 begin
1720 return R : constant Reference_Type :=
1721 (Element => N.Element'Access,
1722 Control => (Controlled with Container'Unrestricted_Access))
1724 B := B + 1;
1725 L := L + 1;
1726 end return;
1727 end;
1728 end if;
1729 end Reference;
1731 ---------------------
1732 -- Replace_Element --
1733 ---------------------
1735 procedure Replace_Element
1736 (Container : in out List;
1737 Position : Cursor;
1738 New_Item : Element_Type)
1740 begin
1741 if Position.Container = null then
1742 raise Constraint_Error with "Position cursor has no element";
1744 elsif Position.Container /= Container'Unchecked_Access then
1745 raise Program_Error with
1746 "Position cursor designates wrong container";
1748 elsif Container.Lock > 0 then
1749 raise Program_Error with
1750 "attempt to tamper with elements (list is locked)";
1752 else
1753 pragma Assert (Vet (Position), "bad cursor in Replace_Element");
1755 Container.Nodes (Position.Node).Element := New_Item;
1756 end if;
1757 end Replace_Element;
1759 ----------------------
1760 -- Reverse_Elements --
1761 ----------------------
1763 procedure Reverse_Elements (Container : in out List) is
1764 N : Node_Array renames Container.Nodes;
1765 I : Count_Type := Container.First;
1766 J : Count_Type := Container.Last;
1768 procedure Swap (L, R : Count_Type);
1770 ----------
1771 -- Swap --
1772 ----------
1774 procedure Swap (L, R : Count_Type) is
1775 LN : constant Count_Type := N (L).Next;
1776 LP : constant Count_Type := N (L).Prev;
1778 RN : constant Count_Type := N (R).Next;
1779 RP : constant Count_Type := N (R).Prev;
1781 begin
1782 if LP /= 0 then
1783 N (LP).Next := R;
1784 end if;
1786 if RN /= 0 then
1787 N (RN).Prev := L;
1788 end if;
1790 N (L).Next := RN;
1791 N (R).Prev := LP;
1793 if LN = R then
1794 pragma Assert (RP = L);
1796 N (L).Prev := R;
1797 N (R).Next := L;
1799 else
1800 N (L).Prev := RP;
1801 N (RP).Next := L;
1803 N (R).Next := LN;
1804 N (LN).Prev := R;
1805 end if;
1806 end Swap;
1808 -- Start of processing for Reverse_Elements
1810 begin
1811 if Container.Length <= 1 then
1812 return;
1813 end if;
1815 pragma Assert (N (Container.First).Prev = 0);
1816 pragma Assert (N (Container.Last).Next = 0);
1818 if Container.Busy > 0 then
1819 raise Program_Error with
1820 "attempt to tamper with cursors (list is busy)";
1821 end if;
1823 Container.First := J;
1824 Container.Last := I;
1825 loop
1826 Swap (L => I, R => J);
1828 J := N (J).Next;
1829 exit when I = J;
1831 I := N (I).Prev;
1832 exit when I = J;
1834 Swap (L => J, R => I);
1836 I := N (I).Next;
1837 exit when I = J;
1839 J := N (J).Prev;
1840 exit when I = J;
1841 end loop;
1843 pragma Assert (N (Container.First).Prev = 0);
1844 pragma Assert (N (Container.Last).Next = 0);
1845 end Reverse_Elements;
1847 ------------------
1848 -- Reverse_Find --
1849 ------------------
1851 function Reverse_Find
1852 (Container : List;
1853 Item : Element_Type;
1854 Position : Cursor := No_Element) return Cursor
1856 Node : Count_Type := Position.Node;
1858 begin
1859 if Node = 0 then
1860 Node := Container.Last;
1862 else
1863 if Position.Container /= Container'Unrestricted_Access then
1864 raise Program_Error with
1865 "Position cursor designates wrong container";
1866 end if;
1868 pragma Assert (Vet (Position), "bad cursor in Reverse_Find");
1869 end if;
1871 -- Per AI05-0022, the container implementation is required to detect
1872 -- element tampering by a generic actual subprogram.
1874 declare
1875 B : Natural renames Container'Unrestricted_Access.Busy;
1876 L : Natural renames Container'Unrestricted_Access.Lock;
1878 Result : Count_Type;
1880 begin
1881 B := B + 1;
1882 L := L + 1;
1884 Result := 0;
1885 while Node /= 0 loop
1886 if Container.Nodes (Node).Element = Item then
1887 Result := Node;
1888 exit;
1889 end if;
1891 Node := Container.Nodes (Node).Prev;
1892 end loop;
1894 B := B - 1;
1895 L := L - 1;
1897 if Result = 0 then
1898 return No_Element;
1899 else
1900 return Cursor'(Container'Unrestricted_Access, Result);
1901 end if;
1903 exception
1904 when others =>
1905 B := B - 1;
1906 L := L - 1;
1907 raise;
1908 end;
1909 end Reverse_Find;
1911 ---------------------
1912 -- Reverse_Iterate --
1913 ---------------------
1915 procedure Reverse_Iterate
1916 (Container : List;
1917 Process : not null access procedure (Position : Cursor))
1919 C : List renames Container'Unrestricted_Access.all;
1920 B : Natural renames C.Busy;
1922 Node : Count_Type := Container.Last;
1924 begin
1925 B := B + 1;
1927 begin
1928 while Node /= 0 loop
1929 Process (Cursor'(Container'Unrestricted_Access, Node));
1930 Node := Container.Nodes (Node).Prev;
1931 end loop;
1932 exception
1933 when others =>
1934 B := B - 1;
1935 raise;
1936 end;
1938 B := B - 1;
1939 end Reverse_Iterate;
1941 ------------
1942 -- Splice --
1943 ------------
1945 procedure Splice
1946 (Target : in out List;
1947 Before : Cursor;
1948 Source : in out List)
1950 begin
1951 if Before.Container /= null then
1952 if Before.Container /= Target'Unrestricted_Access then
1953 raise Program_Error with
1954 "Before cursor designates wrong container";
1955 end if;
1957 pragma Assert (Vet (Before), "bad cursor in Splice");
1958 end if;
1960 if Target'Address = Source'Address or else Source.Length = 0 then
1961 return;
1963 elsif Target.Length > Count_Type'Last - Source.Length then
1964 raise Constraint_Error with "new length exceeds maximum";
1966 elsif Target.Length + Source.Length > Target.Capacity then
1967 raise Capacity_Error with "new length exceeds target capacity";
1969 elsif Target.Busy > 0 then
1970 raise Program_Error with
1971 "attempt to tamper with cursors of Target (list is busy)";
1973 elsif Source.Busy > 0 then
1974 raise Program_Error with
1975 "attempt to tamper with cursors of Source (list is busy)";
1977 else
1978 Splice_Internal (Target, Before.Node, Source);
1979 end if;
1980 end Splice;
1982 procedure Splice
1983 (Container : in out List;
1984 Before : Cursor;
1985 Position : Cursor)
1987 N : Node_Array renames Container.Nodes;
1989 begin
1990 if Before.Container /= null then
1991 if Before.Container /= Container'Unchecked_Access then
1992 raise Program_Error with
1993 "Before cursor designates wrong container";
1994 end if;
1996 pragma Assert (Vet (Before), "bad Before cursor in Splice");
1997 end if;
1999 if Position.Node = 0 then
2000 raise Constraint_Error with "Position cursor has no element";
2001 end if;
2003 if Position.Container /= Container'Unrestricted_Access then
2004 raise Program_Error with
2005 "Position cursor designates wrong container";
2006 end if;
2008 pragma Assert (Vet (Position), "bad Position cursor in Splice");
2010 if Position.Node = Before.Node
2011 or else N (Position.Node).Next = Before.Node
2012 then
2013 return;
2014 end if;
2016 pragma Assert (Container.Length >= 2);
2018 if Container.Busy > 0 then
2019 raise Program_Error with
2020 "attempt to tamper with cursors (list is busy)";
2021 end if;
2023 if Before.Node = 0 then
2024 pragma Assert (Position.Node /= Container.Last);
2026 if Position.Node = Container.First then
2027 Container.First := N (Position.Node).Next;
2028 N (Container.First).Prev := 0;
2029 else
2030 N (N (Position.Node).Prev).Next := N (Position.Node).Next;
2031 N (N (Position.Node).Next).Prev := N (Position.Node).Prev;
2032 end if;
2034 N (Container.Last).Next := Position.Node;
2035 N (Position.Node).Prev := Container.Last;
2037 Container.Last := Position.Node;
2038 N (Container.Last).Next := 0;
2040 return;
2041 end if;
2043 if Before.Node = Container.First then
2044 pragma Assert (Position.Node /= Container.First);
2046 if Position.Node = Container.Last then
2047 Container.Last := N (Position.Node).Prev;
2048 N (Container.Last).Next := 0;
2049 else
2050 N (N (Position.Node).Prev).Next := N (Position.Node).Next;
2051 N (N (Position.Node).Next).Prev := N (Position.Node).Prev;
2052 end if;
2054 N (Container.First).Prev := Position.Node;
2055 N (Position.Node).Next := Container.First;
2057 Container.First := Position.Node;
2058 N (Container.First).Prev := 0;
2060 return;
2061 end if;
2063 if Position.Node = Container.First then
2064 Container.First := N (Position.Node).Next;
2065 N (Container.First).Prev := 0;
2067 elsif Position.Node = Container.Last then
2068 Container.Last := N (Position.Node).Prev;
2069 N (Container.Last).Next := 0;
2071 else
2072 N (N (Position.Node).Prev).Next := N (Position.Node).Next;
2073 N (N (Position.Node).Next).Prev := N (Position.Node).Prev;
2074 end if;
2076 N (N (Before.Node).Prev).Next := Position.Node;
2077 N (Position.Node).Prev := N (Before.Node).Prev;
2079 N (Before.Node).Prev := Position.Node;
2080 N (Position.Node).Next := Before.Node;
2082 pragma Assert (N (Container.First).Prev = 0);
2083 pragma Assert (N (Container.Last).Next = 0);
2084 end Splice;
2086 procedure Splice
2087 (Target : in out List;
2088 Before : Cursor;
2089 Source : in out List;
2090 Position : in out Cursor)
2092 Target_Position : Count_Type;
2094 begin
2095 if Target'Address = Source'Address then
2096 Splice (Target, Before, Position);
2097 return;
2098 end if;
2100 if Before.Container /= null then
2101 if Before.Container /= Target'Unrestricted_Access then
2102 raise Program_Error with
2103 "Before cursor designates wrong container";
2104 end if;
2106 pragma Assert (Vet (Before), "bad Before cursor in Splice");
2107 end if;
2109 if Position.Node = 0 then
2110 raise Constraint_Error with "Position cursor has no element";
2111 end if;
2113 if Position.Container /= Source'Unrestricted_Access then
2114 raise Program_Error with
2115 "Position cursor designates wrong container";
2116 end if;
2118 pragma Assert (Vet (Position), "bad Position cursor in Splice");
2120 if Target.Length >= Target.Capacity then
2121 raise Capacity_Error with "Target is full";
2122 end if;
2124 if Target.Busy > 0 then
2125 raise Program_Error with
2126 "attempt to tamper with cursors of Target (list is busy)";
2127 end if;
2129 if Source.Busy > 0 then
2130 raise Program_Error with
2131 "attempt to tamper with cursors of Source (list is busy)";
2132 end if;
2134 Splice_Internal
2135 (Target => Target,
2136 Before => Before.Node,
2137 Source => Source,
2138 Src_Pos => Position.Node,
2139 Tgt_Pos => Target_Position);
2141 Position := Cursor'(Target'Unrestricted_Access, Target_Position);
2142 end Splice;
2144 ---------------------
2145 -- Splice_Internal --
2146 ---------------------
2148 procedure Splice_Internal
2149 (Target : in out List;
2150 Before : Count_Type;
2151 Source : in out List)
2153 N : Node_Array renames Source.Nodes;
2154 X : Count_Type;
2156 begin
2157 -- This implements the corresponding Splice operation, after the
2158 -- parameters have been vetted, and corner-cases disposed of.
2160 pragma Assert (Target'Address /= Source'Address);
2161 pragma Assert (Source.Length > 0);
2162 pragma Assert (Source.First /= 0);
2163 pragma Assert (N (Source.First).Prev = 0);
2164 pragma Assert (Source.Last /= 0);
2165 pragma Assert (N (Source.Last).Next = 0);
2166 pragma Assert (Target.Length <= Count_Type'Last - Source.Length);
2167 pragma Assert (Target.Length + Source.Length <= Target.Capacity);
2169 while Source.Length > 1 loop
2170 -- Copy first element of Source onto Target
2172 Allocate (Target, N (Source.First).Element, New_Node => X);
2173 Insert_Internal (Target, Before => Before, New_Node => X);
2175 -- Unlink the first node from Source
2177 X := Source.First;
2178 pragma Assert (N (N (X).Next).Prev = X);
2180 Source.First := N (X).Next;
2181 N (Source.First).Prev := 0;
2183 Source.Length := Source.Length - 1;
2185 -- Return the Source node to its free store
2187 Free (Source, X);
2188 end loop;
2190 -- Copy first (and only remaining) element of Source onto Target
2192 Allocate (Target, N (Source.First).Element, New_Node => X);
2193 Insert_Internal (Target, Before => Before, New_Node => X);
2195 -- Unlink the node from Source
2197 X := Source.First;
2198 pragma Assert (X = Source.Last);
2200 Source.First := 0;
2201 Source.Last := 0;
2203 Source.Length := 0;
2205 -- Return the Source node to its free store
2207 Free (Source, X);
2208 end Splice_Internal;
2210 procedure Splice_Internal
2211 (Target : in out List;
2212 Before : Count_Type; -- node of Target
2213 Source : in out List;
2214 Src_Pos : Count_Type; -- node of Source
2215 Tgt_Pos : out Count_Type)
2217 N : Node_Array renames Source.Nodes;
2219 begin
2220 -- This implements the corresponding Splice operation, after the
2221 -- parameters have been vetted, and corner-cases handled.
2223 pragma Assert (Target'Address /= Source'Address);
2224 pragma Assert (Target.Length < Target.Capacity);
2225 pragma Assert (Source.Length > 0);
2226 pragma Assert (Source.First /= 0);
2227 pragma Assert (N (Source.First).Prev = 0);
2228 pragma Assert (Source.Last /= 0);
2229 pragma Assert (N (Source.Last).Next = 0);
2230 pragma Assert (Src_Pos /= 0);
2232 Allocate (Target, N (Src_Pos).Element, New_Node => Tgt_Pos);
2233 Insert_Internal (Target, Before => Before, New_Node => Tgt_Pos);
2235 if Source.Length = 1 then
2236 pragma Assert (Source.First = Source.Last);
2237 pragma Assert (Src_Pos = Source.First);
2239 Source.First := 0;
2240 Source.Last := 0;
2242 elsif Src_Pos = Source.First then
2243 pragma Assert (N (N (Src_Pos).Next).Prev = Src_Pos);
2245 Source.First := N (Src_Pos).Next;
2246 N (Source.First).Prev := 0;
2248 elsif Src_Pos = Source.Last then
2249 pragma Assert (N (N (Src_Pos).Prev).Next = Src_Pos);
2251 Source.Last := N (Src_Pos).Prev;
2252 N (Source.Last).Next := 0;
2254 else
2255 pragma Assert (Source.Length >= 3);
2256 pragma Assert (N (N (Src_Pos).Next).Prev = Src_Pos);
2257 pragma Assert (N (N (Src_Pos).Prev).Next = Src_Pos);
2259 N (N (Src_Pos).Next).Prev := N (Src_Pos).Prev;
2260 N (N (Src_Pos).Prev).Next := N (Src_Pos).Next;
2261 end if;
2263 Source.Length := Source.Length - 1;
2264 Free (Source, Src_Pos);
2265 end Splice_Internal;
2267 ----------
2268 -- Swap --
2269 ----------
2271 procedure Swap
2272 (Container : in out List;
2273 I, J : Cursor)
2275 begin
2276 if I.Node = 0 then
2277 raise Constraint_Error with "I cursor has no element";
2278 end if;
2280 if J.Node = 0 then
2281 raise Constraint_Error with "J cursor has no element";
2282 end if;
2284 if I.Container /= Container'Unchecked_Access then
2285 raise Program_Error with "I cursor designates wrong container";
2286 end if;
2288 if J.Container /= Container'Unchecked_Access then
2289 raise Program_Error with "J cursor designates wrong container";
2290 end if;
2292 if I.Node = J.Node then
2293 return;
2294 end if;
2296 if Container.Lock > 0 then
2297 raise Program_Error with
2298 "attempt to tamper with elements (list is locked)";
2299 end if;
2301 pragma Assert (Vet (I), "bad I cursor in Swap");
2302 pragma Assert (Vet (J), "bad J cursor in Swap");
2304 declare
2305 EI : Element_Type renames Container.Nodes (I.Node).Element;
2306 EJ : Element_Type renames Container.Nodes (J.Node).Element;
2308 EI_Copy : constant Element_Type := EI;
2310 begin
2311 EI := EJ;
2312 EJ := EI_Copy;
2313 end;
2314 end Swap;
2316 ----------------
2317 -- Swap_Links --
2318 ----------------
2320 procedure Swap_Links
2321 (Container : in out List;
2322 I, J : Cursor)
2324 begin
2325 if I.Node = 0 then
2326 raise Constraint_Error with "I cursor has no element";
2327 end if;
2329 if J.Node = 0 then
2330 raise Constraint_Error with "J cursor has no element";
2331 end if;
2333 if I.Container /= Container'Unrestricted_Access then
2334 raise Program_Error with "I cursor designates wrong container";
2335 end if;
2337 if J.Container /= Container'Unrestricted_Access then
2338 raise Program_Error with "J cursor designates wrong container";
2339 end if;
2341 if I.Node = J.Node then
2342 return;
2343 end if;
2345 if Container.Busy > 0 then
2346 raise Program_Error with
2347 "attempt to tamper with cursors (list is busy)";
2348 end if;
2350 pragma Assert (Vet (I), "bad I cursor in Swap_Links");
2351 pragma Assert (Vet (J), "bad J cursor in Swap_Links");
2353 declare
2354 I_Next : constant Cursor := Next (I);
2356 begin
2357 if I_Next = J then
2358 Splice (Container, Before => I, Position => J);
2360 else
2361 declare
2362 J_Next : constant Cursor := Next (J);
2364 begin
2365 if J_Next = I then
2366 Splice (Container, Before => J, Position => I);
2368 else
2369 pragma Assert (Container.Length >= 3);
2371 Splice (Container, Before => I_Next, Position => J);
2372 Splice (Container, Before => J_Next, Position => I);
2373 end if;
2374 end;
2375 end if;
2376 end;
2377 end Swap_Links;
2379 --------------------
2380 -- Update_Element --
2381 --------------------
2383 procedure Update_Element
2384 (Container : in out List;
2385 Position : Cursor;
2386 Process : not null access procedure (Element : in out Element_Type))
2388 begin
2389 if Position.Node = 0 then
2390 raise Constraint_Error with "Position cursor has no element";
2391 end if;
2393 if Position.Container /= Container'Unchecked_Access then
2394 raise Program_Error with
2395 "Position cursor designates wrong container";
2396 end if;
2398 pragma Assert (Vet (Position), "bad cursor in Update_Element");
2400 declare
2401 B : Natural renames Container.Busy;
2402 L : Natural renames Container.Lock;
2404 begin
2405 B := B + 1;
2406 L := L + 1;
2408 declare
2409 N : Node_Type renames Container.Nodes (Position.Node);
2410 begin
2411 Process (N.Element);
2412 exception
2413 when others =>
2414 L := L - 1;
2415 B := B - 1;
2416 raise;
2417 end;
2419 L := L - 1;
2420 B := B - 1;
2421 end;
2422 end Update_Element;
2424 ---------
2425 -- Vet --
2426 ---------
2428 function Vet (Position : Cursor) return Boolean is
2429 begin
2430 if Position.Node = 0 then
2431 return Position.Container = null;
2432 end if;
2434 if Position.Container = null then
2435 return False;
2436 end if;
2438 declare
2439 L : List renames Position.Container.all;
2440 N : Node_Array renames L.Nodes;
2442 begin
2443 if L.Length = 0 then
2444 return False;
2445 end if;
2447 if L.First = 0 or L.First > L.Capacity then
2448 return False;
2449 end if;
2451 if L.Last = 0 or L.Last > L.Capacity then
2452 return False;
2453 end if;
2455 if N (L.First).Prev /= 0 then
2456 return False;
2457 end if;
2459 if N (L.Last).Next /= 0 then
2460 return False;
2461 end if;
2463 if Position.Node > L.Capacity then
2464 return False;
2465 end if;
2467 -- An invariant of an active node is that its Previous and Next
2468 -- components are non-negative. Operation Free sets the Previous
2469 -- component of the node to the value -1 before actually deallocating
2470 -- the node, to mark the node as inactive. (By "dellocating" we mean
2471 -- only that the node is linked onto a list of inactive nodes used
2472 -- for storage.) This marker gives us a simple way to detect a
2473 -- dangling reference to a node.
2475 if N (Position.Node).Prev < 0 then -- see Free
2476 return False;
2477 end if;
2479 if N (Position.Node).Prev > L.Capacity then
2480 return False;
2481 end if;
2483 if N (Position.Node).Next = Position.Node then
2484 return False;
2485 end if;
2487 if N (Position.Node).Prev = Position.Node then
2488 return False;
2489 end if;
2491 if N (Position.Node).Prev = 0
2492 and then Position.Node /= L.First
2493 then
2494 return False;
2495 end if;
2497 pragma Assert (N (Position.Node).Prev /= 0
2498 or else Position.Node = L.First);
2500 if N (Position.Node).Next = 0
2501 and then Position.Node /= L.Last
2502 then
2503 return False;
2504 end if;
2506 pragma Assert (N (Position.Node).Next /= 0
2507 or else Position.Node = L.Last);
2509 if L.Length = 1 then
2510 return L.First = L.Last;
2511 end if;
2513 if L.First = L.Last then
2514 return False;
2515 end if;
2517 if N (L.First).Next = 0 then
2518 return False;
2519 end if;
2521 if N (L.Last).Prev = 0 then
2522 return False;
2523 end if;
2525 if N (N (L.First).Next).Prev /= L.First then
2526 return False;
2527 end if;
2529 if N (N (L.Last).Prev).Next /= L.Last then
2530 return False;
2531 end if;
2533 if L.Length = 2 then
2534 if N (L.First).Next /= L.Last then
2535 return False;
2536 end if;
2538 if N (L.Last).Prev /= L.First then
2539 return False;
2540 end if;
2542 return True;
2543 end if;
2545 if N (L.First).Next = L.Last then
2546 return False;
2547 end if;
2549 if N (L.Last).Prev = L.First then
2550 return False;
2551 end if;
2553 -- Eliminate earlier possibility
2555 if Position.Node = L.First then
2556 return True;
2557 end if;
2559 pragma Assert (N (Position.Node).Prev /= 0);
2561 -- Eliminate another possibility
2563 if Position.Node = L.Last then
2564 return True;
2565 end if;
2567 pragma Assert (N (Position.Node).Next /= 0);
2569 if N (N (Position.Node).Next).Prev /= Position.Node then
2570 return False;
2571 end if;
2573 if N (N (Position.Node).Prev).Next /= Position.Node then
2574 return False;
2575 end if;
2577 if L.Length = 3 then
2578 if N (L.First).Next /= Position.Node then
2579 return False;
2580 end if;
2582 if N (L.Last).Prev /= Position.Node then
2583 return False;
2584 end if;
2585 end if;
2587 return True;
2588 end;
2589 end Vet;
2591 -----------
2592 -- Write --
2593 -----------
2595 procedure Write
2596 (Stream : not null access Root_Stream_Type'Class;
2597 Item : List)
2599 Node : Count_Type;
2601 begin
2602 Count_Type'Base'Write (Stream, Item.Length);
2604 Node := Item.First;
2605 while Node /= 0 loop
2606 Element_Type'Write (Stream, Item.Nodes (Node).Element);
2607 Node := Item.Nodes (Node).Next;
2608 end loop;
2609 end Write;
2611 procedure Write
2612 (Stream : not null access Root_Stream_Type'Class;
2613 Item : Cursor)
2615 begin
2616 raise Program_Error with "attempt to stream list cursor";
2617 end Write;
2619 procedure Write
2620 (Stream : not null access Root_Stream_Type'Class;
2621 Item : Reference_Type)
2623 begin
2624 raise Program_Error with "attempt to stream reference";
2625 end Write;
2627 procedure Write
2628 (Stream : not null access Root_Stream_Type'Class;
2629 Item : Constant_Reference_Type)
2631 begin
2632 raise Program_Error with "attempt to stream reference";
2633 end Write;
2635 end Ada.Containers.Bounded_Doubly_Linked_Lists;