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