e7333d8927575630b52128edd921de2b985ac9be
[gcc.git] / gcc / ada / a-cdlili.adb
1 ------------------------------------------------------------------------------
2 -- --
3 -- GNAT LIBRARY COMPONENTS --
4 -- --
5 -- A D A . C O N T A I N E R S . D O U B L Y _ L I N K E D _ L I S T S --
6 -- --
7 -- B o d y --
8 -- --
9 -- Copyright (C) 2004-2013, 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 ------------------------------------------------------------------------------
29
30 with Ada.Unchecked_Deallocation;
31
32 with System; use type System.Address;
33
34 package body Ada.Containers.Doubly_Linked_Lists is
35
36 type Iterator is new Limited_Controlled and
37 List_Iterator_Interfaces.Reversible_Iterator with
38 record
39 Container : List_Access;
40 Node : Node_Access;
41 end record;
42
43 overriding procedure Finalize (Object : in out Iterator);
44
45 overriding function First (Object : Iterator) return Cursor;
46 overriding function Last (Object : Iterator) return Cursor;
47
48 overriding function Next
49 (Object : Iterator;
50 Position : Cursor) return Cursor;
51
52 overriding function Previous
53 (Object : Iterator;
54 Position : Cursor) return Cursor;
55
56 -----------------------
57 -- Local Subprograms --
58 -----------------------
59
60 procedure Free (X : in out Node_Access);
61
62 procedure Insert_Internal
63 (Container : in out List;
64 Before : Node_Access;
65 New_Node : Node_Access);
66
67 procedure Splice_Internal
68 (Target : in out List;
69 Before : Node_Access;
70 Source : in out List);
71
72 procedure Splice_Internal
73 (Target : in out List;
74 Before : Node_Access;
75 Source : in out List;
76 Position : Node_Access);
77
78 function Vet (Position : Cursor) return Boolean;
79 -- Checks invariants of the cursor and its designated container, as a
80 -- simple way of detecting dangling references (see operation Free for a
81 -- description of the detection mechanism), returning True if all checks
82 -- pass. Invocations of Vet are used here as the argument of pragma Assert,
83 -- so the checks are performed only when assertions are enabled.
84
85 ---------
86 -- "=" --
87 ---------
88
89 function "=" (Left, Right : List) return Boolean is
90 BL : Natural renames Left'Unrestricted_Access.Busy;
91 LL : Natural renames Left'Unrestricted_Access.Lock;
92
93 BR : Natural renames Right'Unrestricted_Access.Busy;
94 LR : Natural renames Right'Unrestricted_Access.Lock;
95
96 L : Node_Access;
97 R : Node_Access;
98 Result : Boolean;
99
100 begin
101 if Left'Address = Right'Address then
102 return True;
103 end if;
104
105 if Left.Length /= Right.Length then
106 return False;
107 end if;
108
109 -- Per AI05-0022, the container implementation is required to detect
110 -- element tampering by a generic actual subprogram.
111
112 BL := BL + 1;
113 LL := LL + 1;
114
115 BR := BR + 1;
116 LR := LR + 1;
117
118 L := Left.First;
119 R := Right.First;
120 Result := True;
121 for J in 1 .. Left.Length loop
122 if L.Element /= R.Element then
123 Result := False;
124 exit;
125 end if;
126
127 L := L.Next;
128 R := R.Next;
129 end loop;
130
131 BL := BL - 1;
132 LL := LL - 1;
133
134 BR := BR - 1;
135 LR := LR - 1;
136
137 return Result;
138
139 exception
140 when others =>
141 BL := BL - 1;
142 LL := LL - 1;
143
144 BR := BR - 1;
145 LR := LR - 1;
146
147 raise;
148 end "=";
149
150 ------------
151 -- Adjust --
152 ------------
153
154 procedure Adjust (Container : in out List) is
155 Src : Node_Access := Container.First;
156
157 begin
158 if Src = null then
159 pragma Assert (Container.Last = null);
160 pragma Assert (Container.Length = 0);
161 pragma Assert (Container.Busy = 0);
162 pragma Assert (Container.Lock = 0);
163 return;
164 end if;
165
166 pragma Assert (Container.First.Prev = null);
167 pragma Assert (Container.Last.Next = null);
168 pragma Assert (Container.Length > 0);
169
170 Container.First := null;
171 Container.Last := null;
172 Container.Length := 0;
173 Container.Busy := 0;
174 Container.Lock := 0;
175
176 Container.First := new Node_Type'(Src.Element, null, null);
177 Container.Last := Container.First;
178 Container.Length := 1;
179
180 Src := Src.Next;
181 while Src /= null loop
182 Container.Last.Next := new Node_Type'(Element => Src.Element,
183 Prev => Container.Last,
184 Next => null);
185 Container.Last := Container.Last.Next;
186 Container.Length := Container.Length + 1;
187
188 Src := Src.Next;
189 end loop;
190 end Adjust;
191
192 procedure Adjust (Control : in out Reference_Control_Type) is
193 begin
194 if Control.Container /= null then
195 declare
196 C : List renames Control.Container.all;
197 B : Natural renames C.Busy;
198 L : Natural renames C.Lock;
199 begin
200 B := B + 1;
201 L := L + 1;
202 end;
203 end if;
204 end Adjust;
205
206 ------------
207 -- Append --
208 ------------
209
210 procedure Append
211 (Container : in out List;
212 New_Item : Element_Type;
213 Count : Count_Type := 1)
214 is
215 begin
216 Insert (Container, No_Element, New_Item, Count);
217 end Append;
218
219 ------------
220 -- Assign --
221 ------------
222
223 procedure Assign (Target : in out List; Source : List) is
224 Node : Node_Access;
225
226 begin
227 if Target'Address = Source'Address then
228 return;
229 end if;
230
231 Target.Clear;
232
233 Node := Source.First;
234 while Node /= null loop
235 Target.Append (Node.Element);
236 Node := Node.Next;
237 end loop;
238 end Assign;
239
240 -----------
241 -- Clear --
242 -----------
243
244 procedure Clear (Container : in out List) is
245 X : Node_Access;
246
247 begin
248 if Container.Length = 0 then
249 pragma Assert (Container.First = null);
250 pragma Assert (Container.Last = null);
251 pragma Assert (Container.Busy = 0);
252 pragma Assert (Container.Lock = 0);
253 return;
254 end if;
255
256 pragma Assert (Container.First.Prev = null);
257 pragma Assert (Container.Last.Next = null);
258
259 if Container.Busy > 0 then
260 raise Program_Error with
261 "attempt to tamper with cursors (list is busy)";
262 end if;
263
264 while Container.Length > 1 loop
265 X := Container.First;
266 pragma Assert (X.Next.Prev = Container.First);
267
268 Container.First := X.Next;
269 Container.First.Prev := null;
270
271 Container.Length := Container.Length - 1;
272
273 Free (X);
274 end loop;
275
276 X := Container.First;
277 pragma Assert (X = Container.Last);
278
279 Container.First := null;
280 Container.Last := null;
281 Container.Length := 0;
282
283 pragma Warnings (Off);
284 Free (X);
285 pragma Warnings (On);
286 end Clear;
287
288 ------------------------
289 -- Constant_Reference --
290 ------------------------
291
292 function Constant_Reference
293 (Container : aliased List;
294 Position : Cursor) return Constant_Reference_Type
295 is
296 begin
297 if Position.Container = null then
298 raise Constraint_Error with "Position cursor has no element";
299 end if;
300
301 if Position.Container /= Container'Unrestricted_Access then
302 raise Program_Error with
303 "Position cursor designates wrong container";
304 end if;
305
306 pragma Assert (Vet (Position), "bad cursor in Constant_Reference");
307
308 declare
309 C : List renames Position.Container.all;
310 B : Natural renames C.Busy;
311 L : Natural renames C.Lock;
312 begin
313 return R : constant Constant_Reference_Type :=
314 (Element => Position.Node.Element'Access,
315 Control => (Controlled with Container'Unrestricted_Access))
316 do
317 B := B + 1;
318 L := L + 1;
319 end return;
320 end;
321 end Constant_Reference;
322
323 --------------
324 -- Contains --
325 --------------
326
327 function Contains
328 (Container : List;
329 Item : Element_Type) return Boolean
330 is
331 begin
332 return Find (Container, Item) /= No_Element;
333 end Contains;
334
335 ----------
336 -- Copy --
337 ----------
338
339 function Copy (Source : List) return List is
340 begin
341 return Target : List do
342 Target.Assign (Source);
343 end return;
344 end Copy;
345
346 ------------
347 -- Delete --
348 ------------
349
350 procedure Delete
351 (Container : in out List;
352 Position : in out Cursor;
353 Count : Count_Type := 1)
354 is
355 X : Node_Access;
356
357 begin
358 if Position.Node = null then
359 raise Constraint_Error with
360 "Position cursor has no element";
361 end if;
362
363 if Position.Container /= Container'Unrestricted_Access then
364 raise Program_Error with
365 "Position cursor designates wrong container";
366 end if;
367
368 pragma Assert (Vet (Position), "bad cursor in Delete");
369
370 if Position.Node = Container.First then
371 Delete_First (Container, Count);
372 Position := No_Element; -- Post-York behavior
373 return;
374 end if;
375
376 if Count = 0 then
377 Position := No_Element; -- Post-York behavior
378 return;
379 end if;
380
381 if Container.Busy > 0 then
382 raise Program_Error with
383 "attempt to tamper with cursors (list is busy)";
384 end if;
385
386 for Index in 1 .. Count loop
387 X := Position.Node;
388 Container.Length := Container.Length - 1;
389
390 if X = Container.Last then
391 Position := No_Element;
392
393 Container.Last := X.Prev;
394 Container.Last.Next := null;
395
396 Free (X);
397 return;
398 end if;
399
400 Position.Node := X.Next;
401
402 X.Next.Prev := X.Prev;
403 X.Prev.Next := X.Next;
404
405 Free (X);
406 end loop;
407
408 -- The following comment is unacceptable, more detail needed ???
409
410 Position := No_Element; -- Post-York behavior
411 end Delete;
412
413 ------------------
414 -- Delete_First --
415 ------------------
416
417 procedure Delete_First
418 (Container : in out List;
419 Count : Count_Type := 1)
420 is
421 X : Node_Access;
422
423 begin
424 if Count >= Container.Length then
425 Clear (Container);
426 return;
427 end if;
428
429 if Count = 0 then
430 return;
431 end if;
432
433 if Container.Busy > 0 then
434 raise Program_Error with
435 "attempt to tamper with cursors (list is busy)";
436 end if;
437
438 for J in 1 .. Count loop
439 X := Container.First;
440 pragma Assert (X.Next.Prev = Container.First);
441
442 Container.First := X.Next;
443 Container.First.Prev := null;
444
445 Container.Length := Container.Length - 1;
446
447 Free (X);
448 end loop;
449 end Delete_First;
450
451 -----------------
452 -- Delete_Last --
453 -----------------
454
455 procedure Delete_Last
456 (Container : in out List;
457 Count : Count_Type := 1)
458 is
459 X : Node_Access;
460
461 begin
462 if Count >= Container.Length then
463 Clear (Container);
464 return;
465 end if;
466
467 if Count = 0 then
468 return;
469 end if;
470
471 if Container.Busy > 0 then
472 raise Program_Error with
473 "attempt to tamper with cursors (list is busy)";
474 end if;
475
476 for J in 1 .. Count loop
477 X := Container.Last;
478 pragma Assert (X.Prev.Next = Container.Last);
479
480 Container.Last := X.Prev;
481 Container.Last.Next := null;
482
483 Container.Length := Container.Length - 1;
484
485 Free (X);
486 end loop;
487 end Delete_Last;
488
489 -------------
490 -- Element --
491 -------------
492
493 function Element (Position : Cursor) return Element_Type is
494 begin
495 if Position.Node = null then
496 raise Constraint_Error with
497 "Position cursor has no element";
498 else
499 pragma Assert (Vet (Position), "bad cursor in Element");
500
501 return Position.Node.Element;
502 end if;
503 end Element;
504
505 --------------
506 -- Finalize --
507 --------------
508
509 procedure Finalize (Object : in out Iterator) is
510 begin
511 if Object.Container /= null then
512 declare
513 B : Natural renames Object.Container.all.Busy;
514 begin
515 B := B - 1;
516 end;
517 end if;
518 end Finalize;
519
520 procedure Finalize (Control : in out Reference_Control_Type) is
521 begin
522 if Control.Container /= null then
523 declare
524 C : List renames Control.Container.all;
525 B : Natural renames C.Busy;
526 L : Natural renames C.Lock;
527 begin
528 B := B - 1;
529 L := L - 1;
530 end;
531
532 Control.Container := null;
533 end if;
534 end Finalize;
535
536 ----------
537 -- Find --
538 ----------
539
540 function Find
541 (Container : List;
542 Item : Element_Type;
543 Position : Cursor := No_Element) return Cursor
544 is
545 Node : Node_Access := Position.Node;
546
547 begin
548 if Node = null then
549 Node := Container.First;
550
551 else
552 if Position.Container /= Container'Unrestricted_Access then
553 raise Program_Error with
554 "Position cursor designates wrong container";
555 else
556 pragma Assert (Vet (Position), "bad cursor in Find");
557 end if;
558 end if;
559
560 -- Per AI05-0022, the container implementation is required to detect
561 -- element tampering by a generic actual subprogram.
562
563 declare
564 B : Natural renames Container'Unrestricted_Access.Busy;
565 L : Natural renames Container'Unrestricted_Access.Lock;
566
567 Result : Node_Access;
568
569 begin
570 B := B + 1;
571 L := L + 1;
572
573 Result := null;
574 while Node /= null loop
575 if Node.Element = Item then
576 Result := Node;
577 exit;
578 else
579 Node := Node.Next;
580 end if;
581 end loop;
582
583 B := B - 1;
584 L := L - 1;
585
586 if Result = null then
587 return No_Element;
588 else
589 return Cursor'(Container'Unrestricted_Access, Result);
590 end if;
591
592 exception
593 when others =>
594 B := B - 1;
595 L := L - 1;
596 raise;
597 end;
598 end Find;
599
600 -----------
601 -- First --
602 -----------
603
604 function First (Container : List) return Cursor is
605 begin
606 if Container.First = null then
607 return No_Element;
608 else
609 return Cursor'(Container'Unrestricted_Access, Container.First);
610 end if;
611 end First;
612
613 function First (Object : Iterator) return Cursor is
614 begin
615 -- The value of the iterator object's Node component influences the
616 -- behavior of the First (and Last) selector function.
617
618 -- When the Node component is null, this means the iterator object was
619 -- constructed without a start expression, in which case the (forward)
620 -- iteration starts from the (logical) beginning of the entire sequence
621 -- of items (corresponding to Container.First, for a forward iterator).
622
623 -- Otherwise, this is iteration over a partial sequence of items. When
624 -- the Node component is non-null, the iterator object was constructed
625 -- with a start expression, that specifies the position from which the
626 -- (forward) partial iteration begins.
627
628 if Object.Node = null then
629 return Doubly_Linked_Lists.First (Object.Container.all);
630 else
631 return Cursor'(Object.Container, Object.Node);
632 end if;
633 end First;
634
635 -------------------
636 -- First_Element --
637 -------------------
638
639 function First_Element (Container : List) return Element_Type is
640 begin
641 if Container.First = null then
642 raise Constraint_Error with "list is empty";
643 else
644 return Container.First.Element;
645 end if;
646 end First_Element;
647
648 ----------
649 -- Free --
650 ----------
651
652 procedure Free (X : in out Node_Access) is
653 procedure Deallocate is
654 new Ada.Unchecked_Deallocation (Node_Type, Node_Access);
655
656 begin
657 -- While a node is in use, as an active link in a list, its Previous and
658 -- Next components must be null, or designate a different node; this is
659 -- a node invariant. Before actually deallocating the node, we set both
660 -- access value components of the node to point to the node itself, thus
661 -- falsifying the node invariant. Subprogram Vet inspects the value of
662 -- the node components when interrogating the node, in order to detect
663 -- whether the cursor's node access value is dangling.
664
665 -- Note that we have no guarantee that the storage for the node isn't
666 -- modified when it is deallocated, but there are other tests that Vet
667 -- does if node invariants appear to be satisifed. However, in practice
668 -- this simple test works well enough, detecting dangling references
669 -- immediately, without needing further interrogation.
670
671 X.Prev := X;
672 X.Next := X;
673
674 Deallocate (X);
675 end Free;
676
677 ---------------------
678 -- Generic_Sorting --
679 ---------------------
680
681 package body Generic_Sorting is
682
683 ---------------
684 -- Is_Sorted --
685 ---------------
686
687 function Is_Sorted (Container : List) return Boolean is
688 B : Natural renames Container'Unrestricted_Access.Busy;
689 L : Natural renames Container'Unrestricted_Access.Lock;
690
691 Node : Node_Access;
692 Result : Boolean;
693
694 begin
695 -- Per AI05-0022, the container implementation is required to detect
696 -- element tampering by a generic actual subprogram.
697
698 B := B + 1;
699 L := L + 1;
700
701 Node := Container.First;
702 Result := True;
703 for Idx in 2 .. Container.Length loop
704 if Node.Next.Element < Node.Element then
705 Result := False;
706 exit;
707 end if;
708
709 Node := Node.Next;
710 end loop;
711
712 B := B - 1;
713 L := L - 1;
714
715 return Result;
716
717 exception
718 when others =>
719 B := B - 1;
720 L := L - 1;
721 raise;
722 end Is_Sorted;
723
724 -----------
725 -- Merge --
726 -----------
727
728 procedure Merge
729 (Target : in out List;
730 Source : in out List)
731 is
732 begin
733 -- The semantics of Merge changed slightly per AI05-0021. It was
734 -- originally the case that if Target and Source denoted the same
735 -- container object, then the GNAT implementation of Merge did
736 -- nothing. However, it was argued that RM05 did not precisely
737 -- specify the semantics for this corner case. The decision of the
738 -- ARG was that if Target and Source denote the same non-empty
739 -- container object, then Program_Error is raised.
740
741 if Source.Is_Empty then
742 return;
743 end if;
744
745 if Target'Address = Source'Address then
746 raise Program_Error with
747 "Target and Source denote same non-empty container";
748 end if;
749
750 if Target.Length > Count_Type'Last - Source.Length then
751 raise Constraint_Error with "new length exceeds maximum";
752 end if;
753
754 if Target.Busy > 0 then
755 raise Program_Error with
756 "attempt to tamper with cursors of Target (list is busy)";
757 end if;
758
759 if Source.Busy > 0 then
760 raise Program_Error with
761 "attempt to tamper with cursors of Source (list is busy)";
762 end if;
763
764 -- Per AI05-0022, the container implementation is required to detect
765 -- element tampering by a generic actual subprogram.
766
767 declare
768 TB : Natural renames Target.Busy;
769 TL : Natural renames Target.Lock;
770
771 SB : Natural renames Source.Busy;
772 SL : Natural renames Source.Lock;
773
774 LI, RI, RJ : Node_Access;
775
776 begin
777 TB := TB + 1;
778 TL := TL + 1;
779
780 SB := SB + 1;
781 SL := SL + 1;
782
783 LI := Target.First;
784 RI := Source.First;
785 while RI /= null loop
786 pragma Assert (RI.Next = null
787 or else not (RI.Next.Element < RI.Element));
788
789 if LI = null then
790 Splice_Internal (Target, null, Source);
791 exit;
792 end if;
793
794 pragma Assert (LI.Next = null
795 or else not (LI.Next.Element < LI.Element));
796
797 if RI.Element < LI.Element then
798 RJ := RI;
799 RI := RI.Next;
800 Splice_Internal (Target, LI, Source, RJ);
801
802 else
803 LI := LI.Next;
804 end if;
805 end loop;
806
807 TB := TB - 1;
808 TL := TL - 1;
809
810 SB := SB - 1;
811 SL := SL - 1;
812
813 exception
814 when others =>
815 TB := TB - 1;
816 TL := TL - 1;
817
818 SB := SB - 1;
819 SL := SL - 1;
820
821 raise;
822 end;
823 end Merge;
824
825 ----------
826 -- Sort --
827 ----------
828
829 procedure Sort (Container : in out List) is
830
831 procedure Partition (Pivot : Node_Access; Back : Node_Access);
832
833 procedure Sort (Front, Back : Node_Access);
834
835 ---------------
836 -- Partition --
837 ---------------
838
839 procedure Partition (Pivot : Node_Access; Back : Node_Access) is
840 Node : Node_Access;
841
842 begin
843 Node := Pivot.Next;
844 while Node /= Back loop
845 if Node.Element < Pivot.Element then
846 declare
847 Prev : constant Node_Access := Node.Prev;
848 Next : constant Node_Access := Node.Next;
849
850 begin
851 Prev.Next := Next;
852
853 if Next = null then
854 Container.Last := Prev;
855 else
856 Next.Prev := Prev;
857 end if;
858
859 Node.Next := Pivot;
860 Node.Prev := Pivot.Prev;
861
862 Pivot.Prev := Node;
863
864 if Node.Prev = null then
865 Container.First := Node;
866 else
867 Node.Prev.Next := Node;
868 end if;
869
870 Node := Next;
871 end;
872
873 else
874 Node := Node.Next;
875 end if;
876 end loop;
877 end Partition;
878
879 ----------
880 -- Sort --
881 ----------
882
883 procedure Sort (Front, Back : Node_Access) is
884 Pivot : constant Node_Access :=
885 (if Front = null then Container.First else Front.Next);
886 begin
887 if Pivot /= Back then
888 Partition (Pivot, Back);
889 Sort (Front, Pivot);
890 Sort (Pivot, Back);
891 end if;
892 end Sort;
893
894 -- Start of processing for Sort
895
896 begin
897 if Container.Length <= 1 then
898 return;
899 end if;
900
901 pragma Assert (Container.First.Prev = null);
902 pragma Assert (Container.Last.Next = null);
903
904 if Container.Busy > 0 then
905 raise Program_Error with
906 "attempt to tamper with cursors (list is busy)";
907 end if;
908
909 -- Per AI05-0022, the container implementation is required to detect
910 -- element tampering by a generic actual subprogram.
911
912 declare
913 B : Natural renames Container.Busy;
914 L : Natural renames Container.Lock;
915
916 begin
917 B := B + 1;
918 L := L + 1;
919
920 Sort (Front => null, Back => null);
921
922 B := B - 1;
923 L := L - 1;
924
925 exception
926 when others =>
927 B := B - 1;
928 L := L - 1;
929 raise;
930 end;
931
932 pragma Assert (Container.First.Prev = null);
933 pragma Assert (Container.Last.Next = null);
934 end Sort;
935
936 end Generic_Sorting;
937
938 -----------------
939 -- Has_Element --
940 -----------------
941
942 function Has_Element (Position : Cursor) return Boolean is
943 begin
944 pragma Assert (Vet (Position), "bad cursor in Has_Element");
945 return Position.Node /= null;
946 end Has_Element;
947
948 ------------
949 -- Insert --
950 ------------
951
952 procedure Insert
953 (Container : in out List;
954 Before : Cursor;
955 New_Item : Element_Type;
956 Position : out Cursor;
957 Count : Count_Type := 1)
958 is
959 New_Node : Node_Access;
960
961 begin
962 if Before.Container /= null then
963 if Before.Container /= Container'Unrestricted_Access then
964 raise Program_Error with
965 "Before cursor designates wrong list";
966 else
967 pragma Assert (Vet (Before), "bad cursor in Insert");
968 end if;
969 end if;
970
971 if Count = 0 then
972 Position := Before;
973 return;
974
975 elsif Container.Length > Count_Type'Last - Count then
976 raise Constraint_Error with "new length exceeds maximum";
977
978 elsif Container.Busy > 0 then
979 raise Program_Error with
980 "attempt to tamper with cursors (list is busy)";
981
982 else
983 New_Node := new Node_Type'(New_Item, null, null);
984 Insert_Internal (Container, Before.Node, New_Node);
985
986 Position := Cursor'(Container'Unchecked_Access, New_Node);
987
988 for J in 2 .. Count loop
989 New_Node := new Node_Type'(New_Item, null, null);
990 Insert_Internal (Container, Before.Node, New_Node);
991 end loop;
992 end if;
993 end Insert;
994
995 procedure Insert
996 (Container : in out List;
997 Before : Cursor;
998 New_Item : Element_Type;
999 Count : Count_Type := 1)
1000 is
1001 Position : Cursor;
1002 pragma Unreferenced (Position);
1003 begin
1004 Insert (Container, Before, New_Item, Position, Count);
1005 end Insert;
1006
1007 procedure Insert
1008 (Container : in out List;
1009 Before : Cursor;
1010 Position : out Cursor;
1011 Count : Count_Type := 1)
1012 is
1013 New_Node : Node_Access;
1014
1015 begin
1016 if Before.Container /= null then
1017 if Before.Container /= Container'Unrestricted_Access then
1018 raise Program_Error with
1019 "Before cursor designates wrong list";
1020 else
1021 pragma Assert (Vet (Before), "bad cursor in Insert");
1022 end if;
1023 end if;
1024
1025 if Count = 0 then
1026 Position := Before;
1027 return;
1028 end if;
1029
1030 if Container.Length > Count_Type'Last - Count then
1031 raise Constraint_Error with "new length exceeds maximum";
1032
1033 elsif Container.Busy > 0 then
1034 raise Program_Error with
1035 "attempt to tamper with cursors (list is busy)";
1036
1037 else
1038 New_Node := new Node_Type;
1039 Insert_Internal (Container, Before.Node, New_Node);
1040
1041 Position := Cursor'(Container'Unchecked_Access, New_Node);
1042
1043 for J in 2 .. Count loop
1044 New_Node := new Node_Type;
1045 Insert_Internal (Container, Before.Node, New_Node);
1046 end loop;
1047 end if;
1048 end Insert;
1049
1050 ---------------------
1051 -- Insert_Internal --
1052 ---------------------
1053
1054 procedure Insert_Internal
1055 (Container : in out List;
1056 Before : Node_Access;
1057 New_Node : Node_Access)
1058 is
1059 begin
1060 if Container.Length = 0 then
1061 pragma Assert (Before = null);
1062 pragma Assert (Container.First = null);
1063 pragma Assert (Container.Last = null);
1064
1065 Container.First := New_Node;
1066 Container.Last := New_Node;
1067
1068 elsif Before = null then
1069 pragma Assert (Container.Last.Next = null);
1070
1071 Container.Last.Next := New_Node;
1072 New_Node.Prev := Container.Last;
1073
1074 Container.Last := New_Node;
1075
1076 elsif Before = Container.First then
1077 pragma Assert (Container.First.Prev = null);
1078
1079 Container.First.Prev := New_Node;
1080 New_Node.Next := Container.First;
1081
1082 Container.First := New_Node;
1083
1084 else
1085 pragma Assert (Container.First.Prev = null);
1086 pragma Assert (Container.Last.Next = null);
1087
1088 New_Node.Next := Before;
1089 New_Node.Prev := Before.Prev;
1090
1091 Before.Prev.Next := New_Node;
1092 Before.Prev := New_Node;
1093 end if;
1094
1095 Container.Length := Container.Length + 1;
1096 end Insert_Internal;
1097
1098 --------------
1099 -- Is_Empty --
1100 --------------
1101
1102 function Is_Empty (Container : List) return Boolean is
1103 begin
1104 return Container.Length = 0;
1105 end Is_Empty;
1106
1107 -------------
1108 -- Iterate --
1109 -------------
1110
1111 procedure Iterate
1112 (Container : List;
1113 Process : not null access procedure (Position : Cursor))
1114 is
1115 B : Natural renames Container'Unrestricted_Access.all.Busy;
1116 Node : Node_Access := Container.First;
1117
1118 begin
1119 B := B + 1;
1120
1121 begin
1122 while Node /= null loop
1123 Process (Cursor'(Container'Unrestricted_Access, Node));
1124 Node := Node.Next;
1125 end loop;
1126 exception
1127 when others =>
1128 B := B - 1;
1129 raise;
1130 end;
1131
1132 B := B - 1;
1133 end Iterate;
1134
1135 function Iterate (Container : List)
1136 return List_Iterator_Interfaces.Reversible_Iterator'Class
1137 is
1138 B : Natural renames Container'Unrestricted_Access.all.Busy;
1139
1140 begin
1141 -- The value of the Node component influences the behavior of the First
1142 -- and Last selector functions of the iterator object. When the Node
1143 -- component is null (as is the case here), this means the iterator
1144 -- object was constructed without a start expression. This is a
1145 -- complete iterator, meaning that the iteration starts from the
1146 -- (logical) beginning of the sequence of items.
1147
1148 -- Note: For a forward iterator, Container.First is the beginning, and
1149 -- for a reverse iterator, Container.Last is the beginning.
1150
1151 return It : constant Iterator :=
1152 Iterator'(Limited_Controlled with
1153 Container => Container'Unrestricted_Access,
1154 Node => null)
1155 do
1156 B := B + 1;
1157 end return;
1158 end Iterate;
1159
1160 function Iterate (Container : List; Start : Cursor)
1161 return List_Iterator_Interfaces.Reversible_Iterator'Class
1162 is
1163 B : Natural renames Container'Unrestricted_Access.all.Busy;
1164
1165 begin
1166 -- It was formerly the case that when Start = No_Element, the partial
1167 -- iterator was defined to behave the same as for a complete iterator,
1168 -- and iterate over the entire sequence of items. However, those
1169 -- semantics were unintuitive and arguably error-prone (it is too easy
1170 -- to accidentally create an endless loop), and so they were changed,
1171 -- per the ARG meeting in Denver on 2011/11. However, there was no
1172 -- consensus about what positive meaning this corner case should have,
1173 -- and so it was decided to simply raise an exception. This does imply,
1174 -- however, that it is not possible to use a partial iterator to specify
1175 -- an empty sequence of items.
1176
1177 if Start = No_Element then
1178 raise Constraint_Error with
1179 "Start position for iterator equals No_Element";
1180
1181 elsif Start.Container /= Container'Unrestricted_Access then
1182 raise Program_Error with
1183 "Start cursor of Iterate designates wrong list";
1184
1185 else
1186 pragma Assert (Vet (Start), "Start cursor of Iterate is bad");
1187
1188 -- The value of the Node component influences the behavior of the
1189 -- First and Last selector functions of the iterator object. When
1190 -- the Node component is non-null (as is the case here), it means
1191 -- that this is a partial iteration, over a subset of the complete
1192 -- sequence of items. The iterator object was constructed with
1193 -- a start expression, indicating the position from which the
1194 -- iteration begins. Note that the start position has the same value
1195 -- irrespective of whether this is a forward or reverse iteration.
1196
1197 return It : constant Iterator :=
1198 Iterator'(Limited_Controlled with
1199 Container => Container'Unrestricted_Access,
1200 Node => Start.Node)
1201 do
1202 B := B + 1;
1203 end return;
1204 end if;
1205 end Iterate;
1206
1207 ----------
1208 -- Last --
1209 ----------
1210
1211 function Last (Container : List) return Cursor is
1212 begin
1213 if Container.Last = null then
1214 return No_Element;
1215 else
1216 return Cursor'(Container'Unrestricted_Access, Container.Last);
1217 end if;
1218 end Last;
1219
1220 function Last (Object : Iterator) return Cursor is
1221 begin
1222 -- The value of the iterator object's Node component influences the
1223 -- behavior of the Last (and First) selector function.
1224
1225 -- When the Node component is null, this means the iterator object was
1226 -- constructed without a start expression, in which case the (reverse)
1227 -- iteration starts from the (logical) beginning of the entire sequence
1228 -- (corresponding to Container.Last, for a reverse iterator).
1229
1230 -- Otherwise, this is iteration over a partial sequence of items. When
1231 -- the Node component is non-null, the iterator object was constructed
1232 -- with a start expression, that specifies the position from which the
1233 -- (reverse) partial iteration begins.
1234
1235 if Object.Node = null then
1236 return Doubly_Linked_Lists.Last (Object.Container.all);
1237 else
1238 return Cursor'(Object.Container, Object.Node);
1239 end if;
1240 end Last;
1241
1242 ------------------
1243 -- Last_Element --
1244 ------------------
1245
1246 function Last_Element (Container : List) return Element_Type is
1247 begin
1248 if Container.Last = null then
1249 raise Constraint_Error with "list is empty";
1250 else
1251 return Container.Last.Element;
1252 end if;
1253 end Last_Element;
1254
1255 ------------
1256 -- Length --
1257 ------------
1258
1259 function Length (Container : List) return Count_Type is
1260 begin
1261 return Container.Length;
1262 end Length;
1263
1264 ----------
1265 -- Move --
1266 ----------
1267
1268 procedure Move
1269 (Target : in out List;
1270 Source : in out List)
1271 is
1272 begin
1273 if Target'Address = Source'Address then
1274 return;
1275
1276 elsif Source.Busy > 0 then
1277 raise Program_Error with
1278 "attempt to tamper with cursors of Source (list is busy)";
1279
1280 else
1281 Clear (Target);
1282
1283 Target.First := Source.First;
1284 Source.First := null;
1285
1286 Target.Last := Source.Last;
1287 Source.Last := null;
1288
1289 Target.Length := Source.Length;
1290 Source.Length := 0;
1291 end if;
1292 end Move;
1293
1294 ----------
1295 -- Next --
1296 ----------
1297
1298 procedure Next (Position : in out Cursor) is
1299 begin
1300 Position := Next (Position);
1301 end Next;
1302
1303 function Next (Position : Cursor) return Cursor is
1304 begin
1305 if Position.Node = null then
1306 return No_Element;
1307
1308 else
1309 pragma Assert (Vet (Position), "bad cursor in Next");
1310
1311 declare
1312 Next_Node : constant Node_Access := Position.Node.Next;
1313 begin
1314 if Next_Node = null then
1315 return No_Element;
1316 else
1317 return Cursor'(Position.Container, Next_Node);
1318 end if;
1319 end;
1320 end if;
1321 end Next;
1322
1323 function Next
1324 (Object : Iterator;
1325 Position : Cursor) return Cursor
1326 is
1327 begin
1328 if Position.Container = null then
1329 return No_Element;
1330 elsif Position.Container /= Object.Container then
1331 raise Program_Error with
1332 "Position cursor of Next designates wrong list";
1333 else
1334 return Next (Position);
1335 end if;
1336 end Next;
1337
1338 -------------
1339 -- Prepend --
1340 -------------
1341
1342 procedure Prepend
1343 (Container : in out List;
1344 New_Item : Element_Type;
1345 Count : Count_Type := 1)
1346 is
1347 begin
1348 Insert (Container, First (Container), New_Item, Count);
1349 end Prepend;
1350
1351 --------------
1352 -- Previous --
1353 --------------
1354
1355 procedure Previous (Position : in out Cursor) is
1356 begin
1357 Position := Previous (Position);
1358 end Previous;
1359
1360 function Previous (Position : Cursor) return Cursor is
1361 begin
1362 if Position.Node = null then
1363 return No_Element;
1364
1365 else
1366 pragma Assert (Vet (Position), "bad cursor in Previous");
1367
1368 declare
1369 Prev_Node : constant Node_Access := Position.Node.Prev;
1370 begin
1371 if Prev_Node = null then
1372 return No_Element;
1373 else
1374 return Cursor'(Position.Container, Prev_Node);
1375 end if;
1376 end;
1377 end if;
1378 end Previous;
1379
1380 function Previous
1381 (Object : Iterator;
1382 Position : Cursor) return Cursor
1383 is
1384 begin
1385 if Position.Container = null then
1386 return No_Element;
1387 elsif Position.Container /= Object.Container then
1388 raise Program_Error with
1389 "Position cursor of Previous designates wrong list";
1390 else
1391 return Previous (Position);
1392 end if;
1393 end Previous;
1394
1395 -------------------
1396 -- Query_Element --
1397 -------------------
1398
1399 procedure Query_Element
1400 (Position : Cursor;
1401 Process : not null access procedure (Element : Element_Type))
1402 is
1403 begin
1404 if Position.Node = null then
1405 raise Constraint_Error with
1406 "Position cursor has no element";
1407 end if;
1408
1409 pragma Assert (Vet (Position), "bad cursor in Query_Element");
1410
1411 declare
1412 C : List renames Position.Container.all'Unrestricted_Access.all;
1413 B : Natural renames C.Busy;
1414 L : Natural renames C.Lock;
1415
1416 begin
1417 B := B + 1;
1418 L := L + 1;
1419
1420 begin
1421 Process (Position.Node.Element);
1422 exception
1423 when others =>
1424 L := L - 1;
1425 B := B - 1;
1426 raise;
1427 end;
1428
1429 L := L - 1;
1430 B := B - 1;
1431 end;
1432 end Query_Element;
1433
1434 ----------
1435 -- Read --
1436 ----------
1437
1438 procedure Read
1439 (Stream : not null access Root_Stream_Type'Class;
1440 Item : out List)
1441 is
1442 N : Count_Type'Base;
1443 X : Node_Access;
1444
1445 begin
1446 Clear (Item);
1447 Count_Type'Base'Read (Stream, N);
1448
1449 if N = 0 then
1450 return;
1451 end if;
1452
1453 X := new Node_Type;
1454
1455 begin
1456 Element_Type'Read (Stream, X.Element);
1457 exception
1458 when others =>
1459 Free (X);
1460 raise;
1461 end;
1462
1463 Item.First := X;
1464 Item.Last := X;
1465
1466 loop
1467 Item.Length := Item.Length + 1;
1468 exit when Item.Length = N;
1469
1470 X := new Node_Type;
1471
1472 begin
1473 Element_Type'Read (Stream, X.Element);
1474 exception
1475 when others =>
1476 Free (X);
1477 raise;
1478 end;
1479
1480 X.Prev := Item.Last;
1481 Item.Last.Next := X;
1482 Item.Last := X;
1483 end loop;
1484 end Read;
1485
1486 procedure Read
1487 (Stream : not null access Root_Stream_Type'Class;
1488 Item : out Cursor)
1489 is
1490 begin
1491 raise Program_Error with "attempt to stream list cursor";
1492 end Read;
1493
1494 procedure Read
1495 (Stream : not null access Root_Stream_Type'Class;
1496 Item : out Reference_Type)
1497 is
1498 begin
1499 raise Program_Error with "attempt to stream reference";
1500 end Read;
1501
1502 procedure Read
1503 (Stream : not null access Root_Stream_Type'Class;
1504 Item : out Constant_Reference_Type)
1505 is
1506 begin
1507 raise Program_Error with "attempt to stream reference";
1508 end Read;
1509
1510 ---------------
1511 -- Reference --
1512 ---------------
1513
1514 function Reference
1515 (Container : aliased in out List;
1516 Position : Cursor) return Reference_Type
1517 is
1518 begin
1519 if Position.Container = null then
1520 raise Constraint_Error with "Position cursor has no element";
1521
1522 elsif Position.Container /= Container'Unchecked_Access then
1523 raise Program_Error with
1524 "Position cursor designates wrong container";
1525
1526 else
1527 pragma Assert (Vet (Position), "bad cursor in function Reference");
1528
1529 declare
1530 C : List renames Position.Container.all;
1531 B : Natural renames C.Busy;
1532 L : Natural renames C.Lock;
1533 begin
1534 return R : constant Reference_Type :=
1535 (Element => Position.Node.Element'Access,
1536 Control => (Controlled with Position.Container))
1537 do
1538 B := B + 1;
1539 L := L + 1;
1540 end return;
1541 end;
1542 end if;
1543 end Reference;
1544
1545 ---------------------
1546 -- Replace_Element --
1547 ---------------------
1548
1549 procedure Replace_Element
1550 (Container : in out List;
1551 Position : Cursor;
1552 New_Item : Element_Type)
1553 is
1554 begin
1555 if Position.Container = null then
1556 raise Constraint_Error with "Position cursor has no element";
1557
1558 elsif Position.Container /= Container'Unchecked_Access then
1559 raise Program_Error with
1560 "Position cursor designates wrong container";
1561
1562 elsif Container.Lock > 0 then
1563 raise Program_Error with
1564 "attempt to tamper with elements (list is locked)";
1565
1566 else
1567 pragma Assert (Vet (Position), "bad cursor in Replace_Element");
1568
1569 Position.Node.Element := New_Item;
1570 end if;
1571 end Replace_Element;
1572
1573 ----------------------
1574 -- Reverse_Elements --
1575 ----------------------
1576
1577 procedure Reverse_Elements (Container : in out List) is
1578 I : Node_Access := Container.First;
1579 J : Node_Access := Container.Last;
1580
1581 procedure Swap (L, R : Node_Access);
1582
1583 ----------
1584 -- Swap --
1585 ----------
1586
1587 procedure Swap (L, R : Node_Access) is
1588 LN : constant Node_Access := L.Next;
1589 LP : constant Node_Access := L.Prev;
1590
1591 RN : constant Node_Access := R.Next;
1592 RP : constant Node_Access := R.Prev;
1593
1594 begin
1595 if LP /= null then
1596 LP.Next := R;
1597 end if;
1598
1599 if RN /= null then
1600 RN.Prev := L;
1601 end if;
1602
1603 L.Next := RN;
1604 R.Prev := LP;
1605
1606 if LN = R then
1607 pragma Assert (RP = L);
1608
1609 L.Prev := R;
1610 R.Next := L;
1611
1612 else
1613 L.Prev := RP;
1614 RP.Next := L;
1615
1616 R.Next := LN;
1617 LN.Prev := R;
1618 end if;
1619 end Swap;
1620
1621 -- Start of processing for Reverse_Elements
1622
1623 begin
1624 if Container.Length <= 1 then
1625 return;
1626 end if;
1627
1628 pragma Assert (Container.First.Prev = null);
1629 pragma Assert (Container.Last.Next = null);
1630
1631 if Container.Busy > 0 then
1632 raise Program_Error with
1633 "attempt to tamper with cursors (list is busy)";
1634 end if;
1635
1636 Container.First := J;
1637 Container.Last := I;
1638 loop
1639 Swap (L => I, R => J);
1640
1641 J := J.Next;
1642 exit when I = J;
1643
1644 I := I.Prev;
1645 exit when I = J;
1646
1647 Swap (L => J, R => I);
1648
1649 I := I.Next;
1650 exit when I = J;
1651
1652 J := J.Prev;
1653 exit when I = J;
1654 end loop;
1655
1656 pragma Assert (Container.First.Prev = null);
1657 pragma Assert (Container.Last.Next = null);
1658 end Reverse_Elements;
1659
1660 ------------------
1661 -- Reverse_Find --
1662 ------------------
1663
1664 function Reverse_Find
1665 (Container : List;
1666 Item : Element_Type;
1667 Position : Cursor := No_Element) return Cursor
1668 is
1669 Node : Node_Access := Position.Node;
1670
1671 begin
1672 if Node = null then
1673 Node := Container.Last;
1674
1675 else
1676 if Position.Container /= Container'Unrestricted_Access then
1677 raise Program_Error with
1678 "Position cursor designates wrong container";
1679 else
1680 pragma Assert (Vet (Position), "bad cursor in Reverse_Find");
1681 end if;
1682 end if;
1683
1684 -- Per AI05-0022, the container implementation is required to detect
1685 -- element tampering by a generic actual subprogram.
1686
1687 declare
1688 B : Natural renames Container'Unrestricted_Access.Busy;
1689 L : Natural renames Container'Unrestricted_Access.Lock;
1690
1691 Result : Node_Access;
1692
1693 begin
1694 B := B + 1;
1695 L := L + 1;
1696
1697 Result := null;
1698 while Node /= null loop
1699 if Node.Element = Item then
1700 Result := Node;
1701 exit;
1702 end if;
1703
1704 Node := Node.Prev;
1705 end loop;
1706
1707 B := B - 1;
1708 L := L - 1;
1709
1710 if Result = null then
1711 return No_Element;
1712 else
1713 return Cursor'(Container'Unrestricted_Access, Result);
1714 end if;
1715
1716 exception
1717 when others =>
1718 B := B - 1;
1719 L := L - 1;
1720 raise;
1721 end;
1722 end Reverse_Find;
1723
1724 ---------------------
1725 -- Reverse_Iterate --
1726 ---------------------
1727
1728 procedure Reverse_Iterate
1729 (Container : List;
1730 Process : not null access procedure (Position : Cursor))
1731 is
1732 C : List renames Container'Unrestricted_Access.all;
1733 B : Natural renames C.Busy;
1734
1735 Node : Node_Access := Container.Last;
1736
1737 begin
1738 B := B + 1;
1739
1740 begin
1741 while Node /= null loop
1742 Process (Cursor'(Container'Unrestricted_Access, Node));
1743 Node := Node.Prev;
1744 end loop;
1745 exception
1746 when others =>
1747 B := B - 1;
1748 raise;
1749 end;
1750
1751 B := B - 1;
1752 end Reverse_Iterate;
1753
1754 ------------
1755 -- Splice --
1756 ------------
1757
1758 procedure Splice
1759 (Target : in out List;
1760 Before : Cursor;
1761 Source : in out List)
1762 is
1763 begin
1764 if Before.Container /= null then
1765 if Before.Container /= Target'Unrestricted_Access then
1766 raise Program_Error with
1767 "Before cursor designates wrong container";
1768 else
1769 pragma Assert (Vet (Before), "bad cursor in Splice");
1770 end if;
1771 end if;
1772
1773 if Target'Address = Source'Address or else Source.Length = 0 then
1774 return;
1775
1776 elsif Target.Length > Count_Type'Last - Source.Length then
1777 raise Constraint_Error with "new length exceeds maximum";
1778
1779 elsif Target.Busy > 0 then
1780 raise Program_Error with
1781 "attempt to tamper with cursors of Target (list is busy)";
1782
1783 elsif Source.Busy > 0 then
1784 raise Program_Error with
1785 "attempt to tamper with cursors of Source (list is busy)";
1786
1787 else
1788 Splice_Internal (Target, Before.Node, Source);
1789 end if;
1790 end Splice;
1791
1792 procedure Splice
1793 (Container : in out List;
1794 Before : Cursor;
1795 Position : Cursor)
1796 is
1797 begin
1798 if Before.Container /= null then
1799 if Before.Container /= Container'Unchecked_Access then
1800 raise Program_Error with
1801 "Before cursor designates wrong container";
1802 else
1803 pragma Assert (Vet (Before), "bad Before cursor in Splice");
1804 end if;
1805 end if;
1806
1807 if Position.Node = null then
1808 raise Constraint_Error with "Position cursor has no element";
1809 end if;
1810
1811 if Position.Container /= Container'Unrestricted_Access then
1812 raise Program_Error with
1813 "Position cursor designates wrong container";
1814 end if;
1815
1816 pragma Assert (Vet (Position), "bad Position cursor in Splice");
1817
1818 if Position.Node = Before.Node
1819 or else Position.Node.Next = Before.Node
1820 then
1821 return;
1822 end if;
1823
1824 pragma Assert (Container.Length >= 2);
1825
1826 if Container.Busy > 0 then
1827 raise Program_Error with
1828 "attempt to tamper with cursors (list is busy)";
1829 end if;
1830
1831 if Before.Node = null then
1832 pragma Assert (Position.Node /= Container.Last);
1833
1834 if Position.Node = Container.First then
1835 Container.First := Position.Node.Next;
1836 Container.First.Prev := null;
1837 else
1838 Position.Node.Prev.Next := Position.Node.Next;
1839 Position.Node.Next.Prev := Position.Node.Prev;
1840 end if;
1841
1842 Container.Last.Next := Position.Node;
1843 Position.Node.Prev := Container.Last;
1844
1845 Container.Last := Position.Node;
1846 Container.Last.Next := null;
1847
1848 return;
1849 end if;
1850
1851 if Before.Node = Container.First then
1852 pragma Assert (Position.Node /= Container.First);
1853
1854 if Position.Node = Container.Last then
1855 Container.Last := Position.Node.Prev;
1856 Container.Last.Next := null;
1857 else
1858 Position.Node.Prev.Next := Position.Node.Next;
1859 Position.Node.Next.Prev := Position.Node.Prev;
1860 end if;
1861
1862 Container.First.Prev := Position.Node;
1863 Position.Node.Next := Container.First;
1864
1865 Container.First := Position.Node;
1866 Container.First.Prev := null;
1867
1868 return;
1869 end if;
1870
1871 if Position.Node = Container.First then
1872 Container.First := Position.Node.Next;
1873 Container.First.Prev := null;
1874
1875 elsif Position.Node = Container.Last then
1876 Container.Last := Position.Node.Prev;
1877 Container.Last.Next := null;
1878
1879 else
1880 Position.Node.Prev.Next := Position.Node.Next;
1881 Position.Node.Next.Prev := Position.Node.Prev;
1882 end if;
1883
1884 Before.Node.Prev.Next := Position.Node;
1885 Position.Node.Prev := Before.Node.Prev;
1886
1887 Before.Node.Prev := Position.Node;
1888 Position.Node.Next := Before.Node;
1889
1890 pragma Assert (Container.First.Prev = null);
1891 pragma Assert (Container.Last.Next = null);
1892 end Splice;
1893
1894 procedure Splice
1895 (Target : in out List;
1896 Before : Cursor;
1897 Source : in out List;
1898 Position : in out Cursor)
1899 is
1900 begin
1901 if Target'Address = Source'Address then
1902 Splice (Target, Before, Position);
1903 return;
1904 end if;
1905
1906 if Before.Container /= null then
1907 if Before.Container /= Target'Unrestricted_Access then
1908 raise Program_Error with
1909 "Before cursor designates wrong container";
1910 else
1911 pragma Assert (Vet (Before), "bad Before cursor in Splice");
1912 end if;
1913 end if;
1914
1915 if Position.Node = null then
1916 raise Constraint_Error with "Position cursor has no element";
1917
1918 elsif Position.Container /= Source'Unrestricted_Access then
1919 raise Program_Error with
1920 "Position cursor designates wrong container";
1921
1922 else
1923 pragma Assert (Vet (Position), "bad Position cursor in Splice");
1924
1925 if Target.Length = Count_Type'Last then
1926 raise Constraint_Error with "Target is full";
1927
1928 elsif Target.Busy > 0 then
1929 raise Program_Error with
1930 "attempt to tamper with cursors of Target (list is busy)";
1931
1932 elsif Source.Busy > 0 then
1933 raise Program_Error with
1934 "attempt to tamper with cursors of Source (list is busy)";
1935
1936 else
1937 Splice_Internal (Target, Before.Node, Source, Position.Node);
1938 Position.Container := Target'Unchecked_Access;
1939 end if;
1940 end if;
1941 end Splice;
1942
1943 ---------------------
1944 -- Splice_Internal --
1945 ---------------------
1946
1947 procedure Splice_Internal
1948 (Target : in out List;
1949 Before : Node_Access;
1950 Source : in out List)
1951 is
1952 begin
1953 -- This implements the corresponding Splice operation, after the
1954 -- parameters have been vetted, and corner-cases disposed of.
1955
1956 pragma Assert (Target'Address /= Source'Address);
1957 pragma Assert (Source.Length > 0);
1958 pragma Assert (Source.First /= null);
1959 pragma Assert (Source.First.Prev = null);
1960 pragma Assert (Source.Last /= null);
1961 pragma Assert (Source.Last.Next = null);
1962 pragma Assert (Target.Length <= Count_Type'Last - Source.Length);
1963
1964 if Target.Length = 0 then
1965 pragma Assert (Target.First = null);
1966 pragma Assert (Target.Last = null);
1967 pragma Assert (Before = null);
1968
1969 Target.First := Source.First;
1970 Target.Last := Source.Last;
1971
1972 elsif Before = null then
1973 pragma Assert (Target.Last.Next = null);
1974
1975 Target.Last.Next := Source.First;
1976 Source.First.Prev := Target.Last;
1977
1978 Target.Last := Source.Last;
1979
1980 elsif Before = Target.First then
1981 pragma Assert (Target.First.Prev = null);
1982
1983 Source.Last.Next := Target.First;
1984 Target.First.Prev := Source.Last;
1985
1986 Target.First := Source.First;
1987
1988 else
1989 pragma Assert (Target.Length >= 2);
1990
1991 Before.Prev.Next := Source.First;
1992 Source.First.Prev := Before.Prev;
1993
1994 Before.Prev := Source.Last;
1995 Source.Last.Next := Before;
1996 end if;
1997
1998 Source.First := null;
1999 Source.Last := null;
2000
2001 Target.Length := Target.Length + Source.Length;
2002 Source.Length := 0;
2003 end Splice_Internal;
2004
2005 procedure Splice_Internal
2006 (Target : in out List;
2007 Before : Node_Access; -- node of Target
2008 Source : in out List;
2009 Position : Node_Access) -- node of Source
2010 is
2011 begin
2012 -- This implements the corresponding Splice operation, after the
2013 -- parameters have been vetted.
2014
2015 pragma Assert (Target'Address /= Source'Address);
2016 pragma Assert (Target.Length < Count_Type'Last);
2017 pragma Assert (Source.Length > 0);
2018 pragma Assert (Source.First /= null);
2019 pragma Assert (Source.First.Prev = null);
2020 pragma Assert (Source.Last /= null);
2021 pragma Assert (Source.Last.Next = null);
2022 pragma Assert (Position /= null);
2023
2024 if Position = Source.First then
2025 Source.First := Position.Next;
2026
2027 if Position = Source.Last then
2028 pragma Assert (Source.First = null);
2029 pragma Assert (Source.Length = 1);
2030 Source.Last := null;
2031
2032 else
2033 Source.First.Prev := null;
2034 end if;
2035
2036 elsif Position = Source.Last then
2037 pragma Assert (Source.Length >= 2);
2038 Source.Last := Position.Prev;
2039 Source.Last.Next := null;
2040
2041 else
2042 pragma Assert (Source.Length >= 3);
2043 Position.Prev.Next := Position.Next;
2044 Position.Next.Prev := Position.Prev;
2045 end if;
2046
2047 if Target.Length = 0 then
2048 pragma Assert (Target.First = null);
2049 pragma Assert (Target.Last = null);
2050 pragma Assert (Before = null);
2051
2052 Target.First := Position;
2053 Target.Last := Position;
2054
2055 Target.First.Prev := null;
2056 Target.Last.Next := null;
2057
2058 elsif Before = null then
2059 pragma Assert (Target.Last.Next = null);
2060 Target.Last.Next := Position;
2061 Position.Prev := Target.Last;
2062
2063 Target.Last := Position;
2064 Target.Last.Next := null;
2065
2066 elsif Before = Target.First then
2067 pragma Assert (Target.First.Prev = null);
2068 Target.First.Prev := Position;
2069 Position.Next := Target.First;
2070
2071 Target.First := Position;
2072 Target.First.Prev := null;
2073
2074 else
2075 pragma Assert (Target.Length >= 2);
2076 Before.Prev.Next := Position;
2077 Position.Prev := Before.Prev;
2078
2079 Before.Prev := Position;
2080 Position.Next := Before;
2081 end if;
2082
2083 Target.Length := Target.Length + 1;
2084 Source.Length := Source.Length - 1;
2085 end Splice_Internal;
2086
2087 ----------
2088 -- Swap --
2089 ----------
2090
2091 procedure Swap
2092 (Container : in out List;
2093 I, J : Cursor)
2094 is
2095 begin
2096 if I.Node = null then
2097 raise Constraint_Error with "I cursor has no element";
2098 end if;
2099
2100 if J.Node = null then
2101 raise Constraint_Error with "J cursor has no element";
2102 end if;
2103
2104 if I.Container /= Container'Unchecked_Access then
2105 raise Program_Error with "I cursor designates wrong container";
2106 end if;
2107
2108 if J.Container /= Container'Unchecked_Access then
2109 raise Program_Error with "J cursor designates wrong container";
2110 end if;
2111
2112 if I.Node = J.Node then
2113 return;
2114 end if;
2115
2116 if Container.Lock > 0 then
2117 raise Program_Error with
2118 "attempt to tamper with elements (list is locked)";
2119 end if;
2120
2121 pragma Assert (Vet (I), "bad I cursor in Swap");
2122 pragma Assert (Vet (J), "bad J cursor in Swap");
2123
2124 declare
2125 EI : Element_Type renames I.Node.Element;
2126 EJ : Element_Type renames J.Node.Element;
2127
2128 EI_Copy : constant Element_Type := EI;
2129
2130 begin
2131 EI := EJ;
2132 EJ := EI_Copy;
2133 end;
2134 end Swap;
2135
2136 ----------------
2137 -- Swap_Links --
2138 ----------------
2139
2140 procedure Swap_Links
2141 (Container : in out List;
2142 I, J : Cursor)
2143 is
2144 begin
2145 if I.Node = null then
2146 raise Constraint_Error with "I cursor has no element";
2147 end if;
2148
2149 if J.Node = null then
2150 raise Constraint_Error with "J cursor has no element";
2151 end if;
2152
2153 if I.Container /= Container'Unrestricted_Access then
2154 raise Program_Error with "I cursor designates wrong container";
2155 end if;
2156
2157 if J.Container /= Container'Unrestricted_Access then
2158 raise Program_Error with "J cursor designates wrong container";
2159 end if;
2160
2161 if I.Node = J.Node then
2162 return;
2163 end if;
2164
2165 if Container.Busy > 0 then
2166 raise Program_Error with
2167 "attempt to tamper with cursors (list is busy)";
2168 end if;
2169
2170 pragma Assert (Vet (I), "bad I cursor in Swap_Links");
2171 pragma Assert (Vet (J), "bad J cursor in Swap_Links");
2172
2173 declare
2174 I_Next : constant Cursor := Next (I);
2175
2176 begin
2177 if I_Next = J then
2178 Splice (Container, Before => I, Position => J);
2179
2180 else
2181 declare
2182 J_Next : constant Cursor := Next (J);
2183
2184 begin
2185 if J_Next = I then
2186 Splice (Container, Before => J, Position => I);
2187
2188 else
2189 pragma Assert (Container.Length >= 3);
2190
2191 Splice (Container, Before => I_Next, Position => J);
2192 Splice (Container, Before => J_Next, Position => I);
2193 end if;
2194 end;
2195 end if;
2196 end;
2197 end Swap_Links;
2198
2199 --------------------
2200 -- Update_Element --
2201 --------------------
2202
2203 procedure Update_Element
2204 (Container : in out List;
2205 Position : Cursor;
2206 Process : not null access procedure (Element : in out Element_Type))
2207 is
2208 begin
2209 if Position.Node = null then
2210 raise Constraint_Error with "Position cursor has no element";
2211
2212 elsif Position.Container /= Container'Unchecked_Access then
2213 raise Program_Error with
2214 "Position cursor designates wrong container";
2215
2216 else
2217 pragma Assert (Vet (Position), "bad cursor in Update_Element");
2218
2219 declare
2220 B : Natural renames Container.Busy;
2221 L : Natural renames Container.Lock;
2222
2223 begin
2224 B := B + 1;
2225 L := L + 1;
2226
2227 begin
2228 Process (Position.Node.Element);
2229 exception
2230 when others =>
2231 L := L - 1;
2232 B := B - 1;
2233 raise;
2234 end;
2235
2236 L := L - 1;
2237 B := B - 1;
2238 end;
2239 end if;
2240 end Update_Element;
2241
2242 ---------
2243 -- Vet --
2244 ---------
2245
2246 function Vet (Position : Cursor) return Boolean is
2247 begin
2248 if Position.Node = null then
2249 return Position.Container = null;
2250 end if;
2251
2252 if Position.Container = null then
2253 return False;
2254 end if;
2255
2256 -- An invariant of a node is that its Previous and Next components can
2257 -- be null, or designate a different node. Operation Free sets the
2258 -- access value components of the node to designate the node itself
2259 -- before actually deallocating the node, thus deliberately violating
2260 -- the node invariant. This gives us a simple way to detect a dangling
2261 -- reference to a node.
2262
2263 if Position.Node.Next = Position.Node then
2264 return False;
2265 end if;
2266
2267 if Position.Node.Prev = Position.Node then
2268 return False;
2269 end if;
2270
2271 -- In practice the tests above will detect most instances of a dangling
2272 -- reference. If we get here, it means that the invariants of the
2273 -- designated node are satisfied (they at least appear to be satisfied),
2274 -- so we perform some more tests, to determine whether invariants of the
2275 -- designated list are satisfied too.
2276
2277 declare
2278 L : List renames Position.Container.all;
2279
2280 begin
2281 if L.Length = 0 then
2282 return False;
2283 end if;
2284
2285 if L.First = null then
2286 return False;
2287 end if;
2288
2289 if L.Last = null then
2290 return False;
2291 end if;
2292
2293 if L.First.Prev /= null then
2294 return False;
2295 end if;
2296
2297 if L.Last.Next /= null then
2298 return False;
2299 end if;
2300
2301 if Position.Node.Prev = null and then Position.Node /= L.First then
2302 return False;
2303 end if;
2304
2305 pragma Assert
2306 (Position.Node.Prev /= null or else Position.Node = L.First);
2307
2308 if Position.Node.Next = null and then Position.Node /= L.Last then
2309 return False;
2310 end if;
2311
2312 pragma Assert
2313 (Position.Node.Next /= null
2314 or else Position.Node = L.Last);
2315
2316 if L.Length = 1 then
2317 return L.First = L.Last;
2318 end if;
2319
2320 if L.First = L.Last then
2321 return False;
2322 end if;
2323
2324 if L.First.Next = null then
2325 return False;
2326 end if;
2327
2328 if L.Last.Prev = null then
2329 return False;
2330 end if;
2331
2332 if L.First.Next.Prev /= L.First then
2333 return False;
2334 end if;
2335
2336 if L.Last.Prev.Next /= L.Last then
2337 return False;
2338 end if;
2339
2340 if L.Length = 2 then
2341 if L.First.Next /= L.Last then
2342 return False;
2343 elsif L.Last.Prev /= L.First then
2344 return False;
2345 else
2346 return True;
2347 end if;
2348 end if;
2349
2350 if L.First.Next = L.Last then
2351 return False;
2352 end if;
2353
2354 if L.Last.Prev = L.First then
2355 return False;
2356 end if;
2357
2358 -- Eliminate earlier possibility
2359
2360 if Position.Node = L.First then
2361 return True;
2362 end if;
2363
2364 pragma Assert (Position.Node.Prev /= null);
2365
2366 -- Eliminate earlier possibility
2367
2368 if Position.Node = L.Last then
2369 return True;
2370 end if;
2371
2372 pragma Assert (Position.Node.Next /= null);
2373
2374 if Position.Node.Next.Prev /= Position.Node then
2375 return False;
2376 end if;
2377
2378 if Position.Node.Prev.Next /= Position.Node then
2379 return False;
2380 end if;
2381
2382 if L.Length = 3 then
2383 if L.First.Next /= Position.Node then
2384 return False;
2385 elsif L.Last.Prev /= Position.Node then
2386 return False;
2387 end if;
2388 end if;
2389
2390 return True;
2391 end;
2392 end Vet;
2393
2394 -----------
2395 -- Write --
2396 -----------
2397
2398 procedure Write
2399 (Stream : not null access Root_Stream_Type'Class;
2400 Item : List)
2401 is
2402 Node : Node_Access;
2403
2404 begin
2405 Count_Type'Base'Write (Stream, Item.Length);
2406
2407 Node := Item.First;
2408 while Node /= null loop
2409 Element_Type'Write (Stream, Node.Element);
2410 Node := Node.Next;
2411 end loop;
2412 end Write;
2413
2414 procedure Write
2415 (Stream : not null access Root_Stream_Type'Class;
2416 Item : Cursor)
2417 is
2418 begin
2419 raise Program_Error with "attempt to stream list cursor";
2420 end Write;
2421
2422 procedure Write
2423 (Stream : not null access Root_Stream_Type'Class;
2424 Item : Reference_Type)
2425 is
2426 begin
2427 raise Program_Error with "attempt to stream reference";
2428 end Write;
2429
2430 procedure Write
2431 (Stream : not null access Root_Stream_Type'Class;
2432 Item : Constant_Reference_Type)
2433 is
2434 begin
2435 raise Program_Error with "attempt to stream reference";
2436 end Write;
2437
2438 end Ada.Containers.Doubly_Linked_Lists;