1 ------------------------------------------------------------------------------
3 -- GNAT RUN-TIME COMPONENTS --
5 -- G N A T . L I S T S --
9 -- Copyright (C) 2018-2019, Free Software Foundation, Inc. --
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. --
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. --
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/>. --
27 -- GNAT was originally developed by the GNAT team at New York University. --
28 -- Extensive contributions were provided by Ada Core Technologies Inc. --
30 ------------------------------------------------------------------------------
32 with Ada.Unchecked_Deallocation;
34 package body GNAT.Lists is
36 package body Doubly_Linked_Lists is
38 (L : Doubly_Linked_List;
40 pragma Inline (Delete_Node);
41 -- Detach and delete node Nod from list L
43 procedure Ensure_Circular (Head : Node_Ptr);
44 pragma Inline (Ensure_Circular);
45 -- Ensure that dummy head Head is circular with respect to itself
47 procedure Ensure_Created (L : Doubly_Linked_List);
48 pragma Inline (Ensure_Created);
49 -- Verify that list L is created. Raise Not_Created if this is not the
52 procedure Ensure_Full (L : Doubly_Linked_List);
53 pragma Inline (Ensure_Full);
54 -- Verify that list L contains at least one element. Raise List_Empty if
55 -- this is not the case.
57 procedure Ensure_Unlocked (L : Doubly_Linked_List);
58 pragma Inline (Ensure_Unlocked);
59 -- Verify that list L is unlocked. Raise Iterated if this is not the
64 Elem : Element_Type) return Node_Ptr;
65 pragma Inline (Find_Node);
66 -- Travers a list indicated by dummy head Head to determine whethe there
67 -- exists a node with element Elem. If such a node exists, return it,
68 -- otherwise return null;
71 new Ada.Unchecked_Deallocation
72 (Doubly_Linked_List_Attributes, Doubly_Linked_List);
74 procedure Free is new Ada.Unchecked_Deallocation (Node, Node_Ptr);
76 procedure Insert_Between
77 (L : Doubly_Linked_List;
81 pragma Inline (Insert_Between);
82 -- Insert element Elem between nodes Left and Right of list L
84 function Is_Valid (Iter : Iterator) return Boolean;
85 pragma Inline (Is_Valid);
86 -- Determine whether iterator Iter refers to a valid element
90 Head : Node_Ptr) return Boolean;
91 pragma Inline (Is_Valid);
92 -- Determine whether node Nod is non-null and does not refer to dummy
93 -- head Head, thus making it valid.
95 procedure Lock (L : Doubly_Linked_List);
97 -- Lock all mutation functionality of list L
99 function Present (Nod : Node_Ptr) return Boolean;
100 pragma Inline (Present);
101 -- Determine whether node Nod exists
103 procedure Unlock (L : Doubly_Linked_List);
104 pragma Inline (Unlock);
105 -- Unlock all mutation functionality of list L
112 (L : Doubly_Linked_List;
121 -- Ensure that the dummy head of an empty list is circular with
122 -- respect to itself.
124 Head := L.Nodes'Access;
125 Ensure_Circular (Head);
127 -- Append the node by inserting it between the last node and the
141 function Create return Doubly_Linked_List is
143 return new Doubly_Linked_List_Attributes;
151 (L : Doubly_Linked_List;
152 Elem : Element_Type) return Boolean
160 Head := L.Nodes'Access;
161 Nod := Find_Node (Head, Elem);
163 return Is_Valid (Nod, Head);
171 (L : Doubly_Linked_List;
182 Head := L.Nodes'Access;
183 Nod := Find_Node (Head, Elem);
185 if Is_Valid (Nod, Head) then
186 Delete_Node (L, Nod);
194 procedure Delete_First (L : Doubly_Linked_List) is
203 Head := L.Nodes'Access;
206 if Is_Valid (Nod, Head) then
207 Delete_Node (L, Nod);
215 procedure Delete_Last (L : Doubly_Linked_List) is
224 Head := L.Nodes'Access;
227 if Is_Valid (Nod, Head) then
228 Delete_Node (L, Nod);
236 procedure Delete_Node
237 (L : Doubly_Linked_List;
240 Ref : Node_Ptr := Nod;
242 pragma Assert (Present (Ref));
244 Next : constant Node_Ptr := Ref.Next;
245 Prev : constant Node_Ptr := Ref.Prev;
248 pragma Assert (Present (L));
249 pragma Assert (Present (Next));
250 pragma Assert (Present (Prev));
252 Prev.Next := Next; -- Prev ---> Next
253 Next.Prev := Prev; -- Prev <--> Next
258 L.Elements := L.Elements - 1;
260 -- Invoke the element destructor before deallocating the node
262 Destroy_Element (Nod.Elem);
271 procedure Destroy (L : in out Doubly_Linked_List) is
278 Head := L.Nodes'Access;
280 while Is_Valid (Head.Next, Head) loop
281 Delete_Node (L, Head.Next);
287 ---------------------
288 -- Ensure_Circular --
289 ---------------------
291 procedure Ensure_Circular (Head : Node_Ptr) is
292 pragma Assert (Present (Head));
295 if not Present (Head.Next) and then not Present (Head.Prev) then
305 procedure Ensure_Created (L : Doubly_Linked_List) is
307 if not Present (L) then
316 procedure Ensure_Full (L : Doubly_Linked_List) is
318 pragma Assert (Present (L));
320 if L.Elements = 0 then
325 ---------------------
326 -- Ensure_Unlocked --
327 ---------------------
329 procedure Ensure_Unlocked (L : Doubly_Linked_List) is
331 pragma Assert (Present (L));
333 -- The list has at least one outstanding iterator
335 if L.Iterators > 0 then
345 (Left : Doubly_Linked_List;
346 Right : Doubly_Linked_List) return Boolean
348 Left_Head : Node_Ptr;
350 Right_Head : Node_Ptr;
351 Right_Nod : Node_Ptr;
354 -- Two non-existent lists are considered equal
356 if Left = Nil and then Right = Nil then
359 -- A non-existent list is never equal to an already created list
361 elsif Left = Nil or else Right = Nil then
364 -- The two lists must contain the same number of elements to be equal
366 elsif Size (Left) /= Size (Right) then
370 -- Compare the two lists element by element
372 Left_Head := Left.Nodes'Access;
373 Left_Nod := Left_Head.Next;
374 Right_Head := Right.Nodes'Access;
375 Right_Nod := Right_Head.Next;
376 while Is_Valid (Left_Nod, Left_Head)
378 Is_Valid (Right_Nod, Right_Head)
380 if Left_Nod.Elem /= Right_Nod.Elem then
384 Left_Nod := Left_Nod.Next;
385 Right_Nod := Right_Nod.Next;
397 Elem : Element_Type) return Node_Ptr
399 pragma Assert (Present (Head));
404 -- Traverse the nodes of the list, looking for a matching element
407 while Is_Valid (Nod, Head) loop
408 if Nod.Elem = Elem then
422 function First (L : Doubly_Linked_List) return Element_Type is
427 return L.Nodes.Next.Elem;
434 function Has_Next (Iter : Iterator) return Boolean is
435 Is_OK : constant Boolean := Is_Valid (Iter);
438 -- The iterator is no longer valid which indicates that it has been
439 -- exhausted. Unlock all mutation functionality of the list because
440 -- the iterator cannot be advanced any further.
453 procedure Insert_After
454 (L : Doubly_Linked_List;
455 After : Element_Type;
465 Head := L.Nodes'Access;
466 Nod := Find_Node (Head, After);
468 if Is_Valid (Nod, Head) then
481 procedure Insert_Before
482 (L : Doubly_Linked_List;
483 Before : Element_Type;
493 Head := L.Nodes'Access;
494 Nod := Find_Node (Head, Before);
496 if Is_Valid (Nod, Head) then
509 procedure Insert_Between
510 (L : Doubly_Linked_List;
515 pragma Assert (Present (L));
516 pragma Assert (Present (Left));
517 pragma Assert (Present (Right));
519 Nod : constant Node_Ptr :=
520 new Node'(Elem => Elem,
521 Next => Right, -- Left Nod ---> Right
522 Prev => Left); -- Left <--- Nod ---> Right
525 Left.Next := Nod; -- Left <--> Nod ---> Right
526 Right.Prev := Nod; -- Left <--> Nod <--> Right
528 L.Elements := L.Elements + 1;
535 function Is_Empty (L : Doubly_Linked_List) return Boolean is
539 return L.Elements = 0;
546 function Is_Valid (Iter : Iterator) return Boolean is
548 -- The invariant of Iterate and Next ensures that the iterator always
549 -- refers to a valid node if there exists one.
551 return Is_Valid (Iter.Curr_Nod, Iter.List.Nodes'Access);
560 Head : Node_Ptr) return Boolean
563 -- A node is valid if it is non-null, and does not refer to the dummy
564 -- head of some list.
566 return Present (Nod) and then Nod /= Head;
573 function Iterate (L : Doubly_Linked_List) return Iterator is
577 -- Lock all mutation functionality of the list while it is being
582 return (List => L, Curr_Nod => L.Nodes.Next);
589 function Last (L : Doubly_Linked_List) return Element_Type is
594 return L.Nodes.Prev.Elem;
601 procedure Lock (L : Doubly_Linked_List) is
603 pragma Assert (Present (L));
605 -- The list may be locked multiple times if multiple iterators are
606 -- operating over it.
608 L.Iterators := L.Iterators + 1;
616 (Iter : in out Iterator;
617 Elem : out Element_Type)
619 Is_OK : constant Boolean := Is_Valid (Iter);
620 Saved : constant Node_Ptr := Iter.Curr_Nod;
623 -- The iterator is no linger valid which indicates that it has been
624 -- exhausted. Unlock all mutation functionality of the list as the
625 -- iterator cannot be advanced any further.
629 raise Iterator_Exhausted;
632 -- Advance to the next node along the list
634 Iter.Curr_Nod := Iter.Curr_Nod.Next;
644 (L : Doubly_Linked_List;
653 -- Ensure that the dummy head of an empty list is circular with
654 -- respect to itself.
656 Head := L.Nodes'Access;
657 Ensure_Circular (Head);
659 -- Append the node by inserting it between the dummy head and the
673 function Present (L : Doubly_Linked_List) return Boolean is
682 function Present (Nod : Node_Ptr) return Boolean is
692 (L : Doubly_Linked_List;
693 Old_Elem : Element_Type;
694 New_Elem : Element_Type)
703 Head := L.Nodes'Access;
704 Nod := Find_Node (Head, Old_Elem);
706 if Is_Valid (Nod, Head) then
707 Nod.Elem := New_Elem;
715 function Size (L : Doubly_Linked_List) return Natural is
726 procedure Unlock (L : Doubly_Linked_List) is
728 pragma Assert (Present (L));
730 -- The list may be locked multiple times if multiple iterators are
731 -- operating over it.
733 L.Iterators := L.Iterators - 1;
735 end Doubly_Linked_Lists;