OSDN Git Service

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