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