2014-10-10 Robert Dewar <dewar@adacore.com>
[official-gcc.git] / gcc / ada / a-convec.adb
blobef4d75494df2d5b149bf62df88bab2d4eddf82fc
1 ------------------------------------------------------------------------------
2 -- --
3 -- GNAT LIBRARY COMPONENTS --
4 -- --
5 -- A D A . C O N T A I N E R S . V E C T O R S --
6 -- --
7 -- B o d y --
8 -- --
9 -- Copyright (C) 2004-2013, Free Software Foundation, Inc. --
10 -- --
11 -- GNAT is free software; you can redistribute it and/or modify it under --
12 -- terms of the GNU General Public License as published by the Free Soft- --
13 -- ware Foundation; either version 3, or (at your option) any later ver- --
14 -- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
15 -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
16 -- or FITNESS FOR A PARTICULAR PURPOSE. --
17 -- --
18 -- As a special exception under Section 7 of GPL version 3, you are granted --
19 -- additional permissions described in the GCC Runtime Library Exception, --
20 -- version 3.1, as published by the Free Software Foundation. --
21 -- --
22 -- You should have received a copy of the GNU General Public License and --
23 -- a copy of the GCC Runtime Library Exception along with this program; --
24 -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see --
25 -- <http://www.gnu.org/licenses/>. --
26 -- --
27 -- This unit was originally developed by Matthew J Heaney. --
28 ------------------------------------------------------------------------------
30 with Ada.Containers.Generic_Array_Sort;
31 with Ada.Unchecked_Deallocation;
33 with System; use type System.Address;
35 package body Ada.Containers.Vectors is
37 procedure Free is
38 new Ada.Unchecked_Deallocation (Elements_Type, Elements_Access);
40 type Iterator is new Limited_Controlled and
41 Vector_Iterator_Interfaces.Reversible_Iterator with
42 record
43 Container : Vector_Access;
44 Index : Index_Type'Base;
45 end record;
47 overriding procedure Finalize (Object : in out Iterator);
49 overriding function First (Object : Iterator) return Cursor;
50 overriding function Last (Object : Iterator) return Cursor;
52 overriding function Next
53 (Object : Iterator;
54 Position : Cursor) return Cursor;
56 overriding function Previous
57 (Object : Iterator;
58 Position : Cursor) return Cursor;
60 ---------
61 -- "&" --
62 ---------
64 function "&" (Left, Right : Vector) return Vector is
65 LN : constant Count_Type := Length (Left);
66 RN : constant Count_Type := Length (Right);
67 N : Count_Type'Base; -- length of result
68 J : Count_Type'Base; -- for computing intermediate index values
69 Last : Index_Type'Base; -- Last index of result
71 begin
72 -- We decide that the capacity of the result is the sum of the lengths
73 -- of the vector parameters. We could decide to make it larger, but we
74 -- have no basis for knowing how much larger, so we just allocate the
75 -- minimum amount of storage.
77 -- Here we handle the easy cases first, when one of the vector
78 -- parameters is empty. (We say "easy" because there's nothing to
79 -- compute, that can potentially overflow.)
81 if LN = 0 then
82 if RN = 0 then
83 return Empty_Vector;
84 end if;
86 declare
87 RE : Elements_Array renames
88 Right.Elements.EA (Index_Type'First .. Right.Last);
89 Elements : constant Elements_Access :=
90 new Elements_Type'(Right.Last, RE);
91 begin
92 return (Controlled with Elements, Right.Last, 0, 0);
93 end;
94 end if;
96 if RN = 0 then
97 declare
98 LE : Elements_Array renames
99 Left.Elements.EA (Index_Type'First .. Left.Last);
100 Elements : constant Elements_Access :=
101 new Elements_Type'(Left.Last, LE);
102 begin
103 return (Controlled with Elements, Left.Last, 0, 0);
104 end;
106 end if;
108 -- Neither of the vector parameters is empty, so must compute the length
109 -- of the result vector and its last index. (This is the harder case,
110 -- because our computations must avoid overflow.)
112 -- There are two constraints we need to satisfy. The first constraint is
113 -- that a container cannot have more than Count_Type'Last elements, so
114 -- we must check the sum of the combined lengths. Note that we cannot
115 -- simply add the lengths, because of the possibility of overflow.
117 if LN > Count_Type'Last - RN then
118 raise Constraint_Error with "new length is out of range";
119 end if;
121 -- It is now safe compute the length of the new vector, without fear of
122 -- overflow.
124 N := LN + RN;
126 -- The second constraint is that the new Last index value cannot
127 -- exceed Index_Type'Last. We use the wider of Index_Type'Base and
128 -- Count_Type'Base as the type for intermediate values.
130 if Index_Type'Base'Last >= Count_Type'Pos (Count_Type'Last) then
132 -- We perform a two-part test. First we determine whether the
133 -- computed Last value lies in the base range of the type, and then
134 -- determine whether it lies in the range of the index (sub)type.
136 -- Last must satisfy this relation:
137 -- First + Length - 1 <= Last
138 -- We regroup terms:
139 -- First - 1 <= Last - Length
140 -- Which can rewrite as:
141 -- No_Index <= Last - Length
143 if Index_Type'Base'Last - Index_Type'Base (N) < No_Index then
144 raise Constraint_Error with "new length is out of range";
145 end if;
147 -- We now know that the computed value of Last is within the base
148 -- range of the type, so it is safe to compute its value:
150 Last := No_Index + Index_Type'Base (N);
152 -- Finally we test whether the value is within the range of the
153 -- generic actual index subtype:
155 if Last > Index_Type'Last then
156 raise Constraint_Error with "new length is out of range";
157 end if;
159 elsif Index_Type'First <= 0 then
161 -- Here we can compute Last directly, in the normal way. We know that
162 -- No_Index is less than 0, so there is no danger of overflow when
163 -- adding the (positive) value of length.
165 J := Count_Type'Base (No_Index) + N; -- Last
167 if J > Count_Type'Base (Index_Type'Last) then
168 raise Constraint_Error with "new length is out of range";
169 end if;
171 -- We know that the computed value (having type Count_Type) of Last
172 -- is within the range of the generic actual index subtype, so it is
173 -- safe to convert to Index_Type:
175 Last := Index_Type'Base (J);
177 else
178 -- Here Index_Type'First (and Index_Type'Last) is positive, so we
179 -- must test the length indirectly (by working backwards from the
180 -- largest possible value of Last), in order to prevent overflow.
182 J := Count_Type'Base (Index_Type'Last) - N; -- No_Index
184 if J < Count_Type'Base (No_Index) then
185 raise Constraint_Error with "new length is out of range";
186 end if;
188 -- We have determined that the result length would not create a Last
189 -- index value outside of the range of Index_Type, so we can now
190 -- safely compute its value.
192 Last := Index_Type'Base (Count_Type'Base (No_Index) + N);
193 end if;
195 declare
196 LE : Elements_Array renames
197 Left.Elements.EA (Index_Type'First .. Left.Last);
198 RE : Elements_Array renames
199 Right.Elements.EA (Index_Type'First .. Right.Last);
200 Elements : constant Elements_Access :=
201 new Elements_Type'(Last, LE & RE);
202 begin
203 return (Controlled with Elements, Last, 0, 0);
204 end;
205 end "&";
207 function "&" (Left : Vector; Right : Element_Type) return Vector is
208 begin
209 -- We decide that the capacity of the result is the sum of the lengths
210 -- of the parameters. We could decide to make it larger, but we have no
211 -- basis for knowing how much larger, so we just allocate the minimum
212 -- amount of storage.
214 -- Handle easy case first, when the vector parameter (Left) is empty
216 if Left.Is_Empty then
217 declare
218 Elements : constant Elements_Access :=
219 new Elements_Type'
220 (Last => Index_Type'First,
221 EA => (others => Right));
223 begin
224 return (Controlled with Elements, Index_Type'First, 0, 0);
225 end;
226 end if;
228 -- The vector parameter is not empty, so we must compute the length of
229 -- the result vector and its last index, but in such a way that overflow
230 -- is avoided. We must satisfy two constraints: the new length cannot
231 -- exceed Count_Type'Last, and the new Last index cannot exceed
232 -- Index_Type'Last.
234 if Left.Length = Count_Type'Last then
235 raise Constraint_Error with "new length is out of range";
236 end if;
238 if Left.Last >= Index_Type'Last then
239 raise Constraint_Error with "new length is out of range";
240 end if;
242 declare
243 Last : constant Index_Type := Left.Last + 1;
244 LE : Elements_Array renames
245 Left.Elements.EA (Index_Type'First .. Left.Last);
246 Elements : constant Elements_Access :=
247 new Elements_Type'(Last => Last, EA => LE & Right);
248 begin
249 return (Controlled with Elements, Last, 0, 0);
250 end;
251 end "&";
253 function "&" (Left : Element_Type; Right : Vector) return Vector is
254 begin
255 -- We decide that the capacity of the result is the sum of the lengths
256 -- of the parameters. We could decide to make it larger, but we have no
257 -- basis for knowing how much larger, so we just allocate the minimum
258 -- amount of storage.
260 -- Handle easy case first, when the vector parameter (Right) is empty
262 if Right.Is_Empty then
263 declare
264 Elements : constant Elements_Access :=
265 new Elements_Type'
266 (Last => Index_Type'First,
267 EA => (others => Left));
268 begin
269 return (Controlled with Elements, Index_Type'First, 0, 0);
270 end;
271 end if;
273 -- The vector parameter is not empty, so we must compute the length of
274 -- the result vector and its last index, but in such a way that overflow
275 -- is avoided. We must satisfy two constraints: the new length cannot
276 -- exceed Count_Type'Last, and the new Last index cannot exceed
277 -- Index_Type'Last.
279 if Right.Length = Count_Type'Last then
280 raise Constraint_Error with "new length is out of range";
281 end if;
283 if Right.Last >= Index_Type'Last then
284 raise Constraint_Error with "new length is out of range";
285 end if;
287 declare
288 Last : constant Index_Type := Right.Last + 1;
290 RE : Elements_Array renames
291 Right.Elements.EA (Index_Type'First .. Right.Last);
293 Elements : constant Elements_Access :=
294 new Elements_Type'
295 (Last => Last,
296 EA => Left & RE);
298 begin
299 return (Controlled with Elements, Last, 0, 0);
300 end;
301 end "&";
303 function "&" (Left, Right : Element_Type) return Vector is
304 begin
305 -- We decide that the capacity of the result is the sum of the lengths
306 -- of the parameters. We could decide to make it larger, but we have no
307 -- basis for knowing how much larger, so we just allocate the minimum
308 -- amount of storage.
310 -- We must compute the length of the result vector and its last index,
311 -- but in such a way that overflow is avoided. We must satisfy two
312 -- constraints: the new length cannot exceed Count_Type'Last (here, we
313 -- know that that condition is satisfied), and the new Last index cannot
314 -- exceed Index_Type'Last.
316 if Index_Type'First >= Index_Type'Last then
317 raise Constraint_Error with "new length is out of range";
318 end if;
320 declare
321 Last : constant Index_Type := Index_Type'First + 1;
323 Elements : constant Elements_Access :=
324 new Elements_Type'
325 (Last => Last,
326 EA => (Left, Right));
328 begin
329 return (Controlled with Elements, Last, 0, 0);
330 end;
331 end "&";
333 ---------
334 -- "=" --
335 ---------
337 overriding function "=" (Left, Right : Vector) return Boolean is
338 BL : Natural renames Left'Unrestricted_Access.Busy;
339 LL : Natural renames Left'Unrestricted_Access.Lock;
341 BR : Natural renames Right'Unrestricted_Access.Busy;
342 LR : Natural renames Right'Unrestricted_Access.Lock;
344 Result : Boolean;
346 begin
347 if Left'Address = Right'Address then
348 return True;
349 end if;
351 if Left.Last /= Right.Last then
352 return False;
353 end if;
355 -- Per AI05-0022, the container implementation is required to detect
356 -- element tampering by a generic actual subprogram.
358 BL := BL + 1;
359 LL := LL + 1;
361 BR := BR + 1;
362 LR := LR + 1;
364 Result := True;
365 for J in Index_Type range Index_Type'First .. Left.Last loop
366 if Left.Elements.EA (J) /= Right.Elements.EA (J) then
367 Result := False;
368 exit;
369 end if;
370 end loop;
372 BL := BL - 1;
373 LL := LL - 1;
375 BR := BR - 1;
376 LR := LR - 1;
378 return Result;
380 exception
381 when others =>
382 BL := BL - 1;
383 LL := LL - 1;
385 BR := BR - 1;
386 LR := LR - 1;
388 raise;
389 end "=";
391 ------------
392 -- Adjust --
393 ------------
395 procedure Adjust (Container : in out Vector) is
396 begin
397 if Container.Last = No_Index then
398 Container.Elements := null;
399 return;
400 end if;
402 declare
403 L : constant Index_Type := Container.Last;
404 EA : Elements_Array renames
405 Container.Elements.EA (Index_Type'First .. L);
407 begin
408 Container.Elements := null;
409 Container.Busy := 0;
410 Container.Lock := 0;
412 -- Note: it may seem that the following assignment to Container.Last
413 -- is useless, since we assign it to L below. However this code is
414 -- used in case 'new Elements_Type' below raises an exception, to
415 -- keep Container in a consistent state.
417 Container.Last := No_Index;
418 Container.Elements := new Elements_Type'(L, EA);
419 Container.Last := L;
420 end;
421 end Adjust;
423 procedure Adjust (Control : in out Reference_Control_Type) is
424 begin
425 if Control.Container /= null then
426 declare
427 C : Vector renames Control.Container.all;
428 B : Natural renames C.Busy;
429 L : Natural renames C.Lock;
430 begin
431 B := B + 1;
432 L := L + 1;
433 end;
434 end if;
435 end Adjust;
437 ------------
438 -- Append --
439 ------------
441 procedure Append (Container : in out Vector; New_Item : Vector) is
442 begin
443 if Is_Empty (New_Item) then
444 return;
445 elsif Container.Last = Index_Type'Last then
446 raise Constraint_Error with "vector is already at its maximum length";
447 else
448 Insert (Container, Container.Last + 1, New_Item);
449 end if;
450 end Append;
452 procedure Append
453 (Container : in out Vector;
454 New_Item : Element_Type;
455 Count : Count_Type := 1)
457 begin
458 if Count = 0 then
459 return;
460 elsif Container.Last = Index_Type'Last then
461 raise Constraint_Error with "vector is already at its maximum length";
462 else
463 Insert (Container, Container.Last + 1, New_Item, Count);
464 end if;
465 end Append;
467 ------------
468 -- Assign --
469 ------------
471 procedure Assign (Target : in out Vector; Source : Vector) is
472 begin
473 if Target'Address = Source'Address then
474 return;
475 else
476 Target.Clear;
477 Target.Append (Source);
478 end if;
479 end Assign;
481 --------------
482 -- Capacity --
483 --------------
485 function Capacity (Container : Vector) return Count_Type is
486 begin
487 if Container.Elements = null then
488 return 0;
489 else
490 return Container.Elements.EA'Length;
491 end if;
492 end Capacity;
494 -----------
495 -- Clear --
496 -----------
498 procedure Clear (Container : in out Vector) is
499 begin
500 if Container.Busy > 0 then
501 raise Program_Error with
502 "attempt to tamper with cursors (vector is busy)";
503 else
504 Container.Last := No_Index;
505 end if;
506 end Clear;
508 ------------------------
509 -- Constant_Reference --
510 ------------------------
512 function Constant_Reference
513 (Container : aliased Vector;
514 Position : Cursor) return Constant_Reference_Type
516 begin
517 if Position.Container = null then
518 raise Constraint_Error with "Position cursor has no element";
519 end if;
521 if Position.Container /= Container'Unrestricted_Access then
522 raise Program_Error with "Position cursor denotes wrong container";
523 end if;
525 if Position.Index > Position.Container.Last then
526 raise Constraint_Error with "Position cursor is out of range";
527 end if;
529 declare
530 C : Vector renames Position.Container.all;
531 B : Natural renames C.Busy;
532 L : Natural renames C.Lock;
533 begin
534 return R : constant Constant_Reference_Type :=
535 (Element => Container.Elements.EA (Position.Index)'Access,
536 Control => (Controlled with Container'Unrestricted_Access))
538 B := B + 1;
539 L := L + 1;
540 end return;
541 end;
542 end Constant_Reference;
544 function Constant_Reference
545 (Container : aliased Vector;
546 Index : Index_Type) return Constant_Reference_Type
548 begin
549 if Index > Container.Last then
550 raise Constraint_Error with "Index is out of range";
551 else
552 declare
553 C : Vector renames Container'Unrestricted_Access.all;
554 B : Natural renames C.Busy;
555 L : Natural renames C.Lock;
556 begin
557 return R : constant Constant_Reference_Type :=
558 (Element => Container.Elements.EA (Index)'Access,
559 Control => (Controlled with Container'Unrestricted_Access))
561 B := B + 1;
562 L := L + 1;
563 end return;
564 end;
565 end if;
566 end Constant_Reference;
568 --------------
569 -- Contains --
570 --------------
572 function Contains
573 (Container : Vector;
574 Item : Element_Type) return Boolean
576 begin
577 return Find_Index (Container, Item) /= No_Index;
578 end Contains;
580 ----------
581 -- Copy --
582 ----------
584 function Copy
585 (Source : Vector;
586 Capacity : Count_Type := 0) return Vector
588 C : Count_Type;
590 begin
591 if Capacity = 0 then
592 C := Source.Length;
594 elsif Capacity >= Source.Length then
595 C := Capacity;
597 else
598 raise Capacity_Error with
599 "Requested capacity is less than Source length";
600 end if;
602 return Target : Vector do
603 Target.Reserve_Capacity (C);
604 Target.Assign (Source);
605 end return;
606 end Copy;
608 ------------
609 -- Delete --
610 ------------
612 procedure Delete
613 (Container : in out Vector;
614 Index : Extended_Index;
615 Count : Count_Type := 1)
617 Old_Last : constant Index_Type'Base := Container.Last;
618 New_Last : Index_Type'Base;
619 Count2 : Count_Type'Base; -- count of items from Index to Old_Last
620 J : Index_Type'Base; -- first index of items that slide down
622 begin
623 -- Delete removes items from the vector, the number of which is the
624 -- minimum of the specified Count and the items (if any) that exist from
625 -- Index to Container.Last. There are no constraints on the specified
626 -- value of Count (it can be larger than what's available at this
627 -- position in the vector, for example), but there are constraints on
628 -- the allowed values of the Index.
630 -- As a precondition on the generic actual Index_Type, the base type
631 -- must include Index_Type'Pred (Index_Type'First); this is the value
632 -- that Container.Last assumes when the vector is empty. However, we do
633 -- not allow that as the value for Index when specifying which items
634 -- should be deleted, so we must manually check. (That the user is
635 -- allowed to specify the value at all here is a consequence of the
636 -- declaration of the Extended_Index subtype, which includes the values
637 -- in the base range that immediately precede and immediately follow the
638 -- values in the Index_Type.)
640 if Index < Index_Type'First then
641 raise Constraint_Error with "Index is out of range (too small)";
642 end if;
644 -- We do allow a value greater than Container.Last to be specified as
645 -- the Index, but only if it's immediately greater. This allows the
646 -- corner case of deleting no items from the back end of the vector to
647 -- be treated as a no-op. (It is assumed that specifying an index value
648 -- greater than Last + 1 indicates some deeper flaw in the caller's
649 -- algorithm, so that case is treated as a proper error.)
651 if Index > Old_Last then
652 if Index > Old_Last + 1 then
653 raise Constraint_Error with "Index is out of range (too large)";
654 else
655 return;
656 end if;
657 end if;
659 -- Here and elsewhere we treat deleting 0 items from the container as a
660 -- no-op, even when the container is busy, so we simply return.
662 if Count = 0 then
663 return;
664 end if;
666 -- The tampering bits exist to prevent an item from being deleted (or
667 -- otherwise harmfully manipulated) while it is being visited. Query,
668 -- Update, and Iterate increment the busy count on entry, and decrement
669 -- the count on exit. Delete checks the count to determine whether it is
670 -- being called while the associated callback procedure is executing.
672 if Container.Busy > 0 then
673 raise Program_Error with
674 "attempt to tamper with cursors (vector is busy)";
675 end if;
677 -- We first calculate what's available for deletion starting at
678 -- Index. Here and elsewhere we use the wider of Index_Type'Base and
679 -- Count_Type'Base as the type for intermediate values. (See function
680 -- Length for more information.)
682 if Count_Type'Base'Last >= Index_Type'Pos (Index_Type'Base'Last) then
683 Count2 := Count_Type'Base (Old_Last) - Count_Type'Base (Index) + 1;
684 else
685 Count2 := Count_Type'Base (Old_Last - Index + 1);
686 end if;
688 -- If more elements are requested (Count) for deletion than are
689 -- available (Count2) for deletion beginning at Index, then everything
690 -- from Index is deleted. There are no elements to slide down, and so
691 -- all we need to do is set the value of Container.Last.
693 if Count >= Count2 then
694 Container.Last := Index - 1;
695 return;
696 end if;
698 -- There are some elements aren't being deleted (the requested count was
699 -- less than the available count), so we must slide them down to
700 -- Index. We first calculate the index values of the respective array
701 -- slices, using the wider of Index_Type'Base and Count_Type'Base as the
702 -- type for intermediate calculations. For the elements that slide down,
703 -- index value New_Last is the last index value of their new home, and
704 -- index value J is the first index of their old home.
706 if Index_Type'Base'Last >= Count_Type'Pos (Count_Type'Last) then
707 New_Last := Old_Last - Index_Type'Base (Count);
708 J := Index + Index_Type'Base (Count);
709 else
710 New_Last := Index_Type'Base (Count_Type'Base (Old_Last) - Count);
711 J := Index_Type'Base (Count_Type'Base (Index) + Count);
712 end if;
714 -- The internal elements array isn't guaranteed to exist unless we have
715 -- elements, but we have that guarantee here because we know we have
716 -- elements to slide. The array index values for each slice have
717 -- already been determined, so we just slide down to Index the elements
718 -- that weren't deleted.
720 declare
721 EA : Elements_Array renames Container.Elements.EA;
722 begin
723 EA (Index .. New_Last) := EA (J .. Old_Last);
724 Container.Last := New_Last;
725 end;
726 end Delete;
728 procedure Delete
729 (Container : in out Vector;
730 Position : in out Cursor;
731 Count : Count_Type := 1)
733 pragma Warnings (Off, Position);
735 begin
736 if Position.Container = null then
737 raise Constraint_Error with "Position cursor has no element";
739 elsif Position.Container /= Container'Unrestricted_Access then
740 raise Program_Error with "Position cursor denotes wrong container";
742 elsif Position.Index > Container.Last then
743 raise Program_Error with "Position index is out of range";
745 else
746 Delete (Container, Position.Index, Count);
747 Position := No_Element;
748 end if;
749 end Delete;
751 ------------------
752 -- Delete_First --
753 ------------------
755 procedure Delete_First
756 (Container : in out Vector;
757 Count : Count_Type := 1)
759 begin
760 if Count = 0 then
761 return;
763 elsif Count >= Length (Container) then
764 Clear (Container);
765 return;
767 else
768 Delete (Container, Index_Type'First, Count);
769 end if;
770 end Delete_First;
772 -----------------
773 -- Delete_Last --
774 -----------------
776 procedure Delete_Last
777 (Container : in out Vector;
778 Count : Count_Type := 1)
780 begin
781 -- It is not permitted to delete items while the container is busy (for
782 -- example, we're in the middle of a passive iteration). However, we
783 -- always treat deleting 0 items as a no-op, even when we're busy, so we
784 -- simply return without checking.
786 if Count = 0 then
787 return;
788 end if;
790 -- The tampering bits exist to prevent an item from being deleted (or
791 -- otherwise harmfully manipulated) while it is being visited. Query,
792 -- Update, and Iterate increment the busy count on entry, and decrement
793 -- the count on exit. Delete_Last checks the count to determine whether
794 -- it is being called while the associated callback procedure is
795 -- executing.
797 if Container.Busy > 0 then
798 raise Program_Error with
799 "attempt to tamper with cursors (vector is busy)";
800 end if;
802 -- There is no restriction on how large Count can be when deleting
803 -- items. If it is equal or greater than the current length, then this
804 -- is equivalent to clearing the vector. (In particular, there's no need
805 -- for us to actually calculate the new value for Last.)
807 -- If the requested count is less than the current length, then we must
808 -- calculate the new value for Last. For the type we use the widest of
809 -- Index_Type'Base and Count_Type'Base for the intermediate values of
810 -- our calculation. (See the comments in Length for more information.)
812 if Count >= Container.Length then
813 Container.Last := No_Index;
815 elsif Index_Type'Base'Last >= Count_Type'Pos (Count_Type'Last) then
816 Container.Last := Container.Last - Index_Type'Base (Count);
818 else
819 Container.Last :=
820 Index_Type'Base (Count_Type'Base (Container.Last) - Count);
821 end if;
822 end Delete_Last;
824 -------------
825 -- Element --
826 -------------
828 function Element
829 (Container : Vector;
830 Index : Index_Type) return Element_Type
832 begin
833 if Index > Container.Last then
834 raise Constraint_Error with "Index is out of range";
835 else
836 return Container.Elements.EA (Index);
837 end if;
838 end Element;
840 function Element (Position : Cursor) return Element_Type is
841 begin
842 if Position.Container = null then
843 raise Constraint_Error with "Position cursor has no element";
844 elsif Position.Index > Position.Container.Last then
845 raise Constraint_Error with "Position cursor is out of range";
846 else
847 return Position.Container.Elements.EA (Position.Index);
848 end if;
849 end Element;
851 --------------
852 -- Finalize --
853 --------------
855 procedure Finalize (Container : in out Vector) is
856 X : Elements_Access := Container.Elements;
858 begin
859 if Container.Busy > 0 then
860 raise Program_Error with
861 "attempt to tamper with cursors (vector is busy)";
863 else
864 Container.Elements := null;
865 Container.Last := No_Index;
866 Free (X);
867 end if;
868 end Finalize;
870 procedure Finalize (Object : in out Iterator) is
871 B : Natural renames Object.Container.Busy;
872 begin
873 B := B - 1;
874 end Finalize;
876 procedure Finalize (Control : in out Reference_Control_Type) is
877 begin
878 if Control.Container /= null then
879 declare
880 C : Vector renames Control.Container.all;
881 B : Natural renames C.Busy;
882 L : Natural renames C.Lock;
883 begin
884 B := B - 1;
885 L := L - 1;
886 end;
888 Control.Container := null;
889 end if;
890 end Finalize;
892 ----------
893 -- Find --
894 ----------
896 function Find
897 (Container : Vector;
898 Item : Element_Type;
899 Position : Cursor := No_Element) return Cursor
901 begin
902 if Position.Container /= null then
903 if Position.Container /= Container'Unrestricted_Access then
904 raise Program_Error with "Position cursor denotes wrong container";
905 end if;
907 if Position.Index > Container.Last then
908 raise Program_Error with "Position index is out of range";
909 end if;
910 end if;
912 -- Per AI05-0022, the container implementation is required to detect
913 -- element tampering by a generic actual subprogram.
915 declare
916 B : Natural renames Container'Unrestricted_Access.Busy;
917 L : Natural renames Container'Unrestricted_Access.Lock;
919 Result : Index_Type'Base;
921 begin
922 B := B + 1;
923 L := L + 1;
925 Result := No_Index;
926 for J in Position.Index .. Container.Last loop
927 if Container.Elements.EA (J) = Item then
928 Result := J;
929 exit;
930 end if;
931 end loop;
933 B := B - 1;
934 L := L - 1;
936 if Result = No_Index then
937 return No_Element;
938 else
939 return Cursor'(Container'Unrestricted_Access, Result);
940 end if;
942 exception
943 when others =>
944 B := B - 1;
945 L := L - 1;
947 raise;
948 end;
949 end Find;
951 ----------------
952 -- Find_Index --
953 ----------------
955 function Find_Index
956 (Container : Vector;
957 Item : Element_Type;
958 Index : Index_Type := Index_Type'First) return Extended_Index
960 B : Natural renames Container'Unrestricted_Access.Busy;
961 L : Natural renames Container'Unrestricted_Access.Lock;
963 Result : Index_Type'Base;
965 begin
966 -- Per AI05-0022, the container implementation is required to detect
967 -- element tampering by a generic actual subprogram.
969 B := B + 1;
970 L := L + 1;
972 Result := No_Index;
973 for Indx in Index .. Container.Last loop
974 if Container.Elements.EA (Indx) = Item then
975 Result := Indx;
976 exit;
977 end if;
978 end loop;
980 B := B - 1;
981 L := L - 1;
983 return Result;
985 exception
986 when others =>
987 B := B - 1;
988 L := L - 1;
990 raise;
991 end Find_Index;
993 -----------
994 -- First --
995 -----------
997 function First (Container : Vector) return Cursor is
998 begin
999 if Is_Empty (Container) then
1000 return No_Element;
1001 else
1002 return (Container'Unrestricted_Access, Index_Type'First);
1003 end if;
1004 end First;
1006 function First (Object : Iterator) return Cursor is
1007 begin
1008 -- The value of the iterator object's Index component influences the
1009 -- behavior of the First (and Last) selector function.
1011 -- When the Index component is No_Index, this means the iterator
1012 -- object was constructed without a start expression, in which case the
1013 -- (forward) iteration starts from the (logical) beginning of the entire
1014 -- sequence of items (corresponding to Container.First, for a forward
1015 -- iterator).
1017 -- Otherwise, this is iteration over a partial sequence of items.
1018 -- When the Index component isn't No_Index, the iterator object was
1019 -- constructed with a start expression, that specifies the position
1020 -- from which the (forward) partial iteration begins.
1022 if Object.Index = No_Index then
1023 return First (Object.Container.all);
1024 else
1025 return Cursor'(Object.Container, Object.Index);
1026 end if;
1027 end First;
1029 -------------------
1030 -- First_Element --
1031 -------------------
1033 function First_Element (Container : Vector) return Element_Type is
1034 begin
1035 if Container.Last = No_Index then
1036 raise Constraint_Error with "Container is empty";
1037 else
1038 return Container.Elements.EA (Index_Type'First);
1039 end if;
1040 end First_Element;
1042 -----------------
1043 -- First_Index --
1044 -----------------
1046 function First_Index (Container : Vector) return Index_Type is
1047 pragma Unreferenced (Container);
1048 begin
1049 return Index_Type'First;
1050 end First_Index;
1052 ---------------------
1053 -- Generic_Sorting --
1054 ---------------------
1056 package body Generic_Sorting is
1058 ---------------
1059 -- Is_Sorted --
1060 ---------------
1062 function Is_Sorted (Container : Vector) return Boolean is
1063 begin
1064 if Container.Last <= Index_Type'First then
1065 return True;
1066 end if;
1068 -- Per AI05-0022, the container implementation is required to detect
1069 -- element tampering by a generic actual subprogram.
1071 declare
1072 EA : Elements_Array renames Container.Elements.EA;
1074 B : Natural renames Container'Unrestricted_Access.Busy;
1075 L : Natural renames Container'Unrestricted_Access.Lock;
1077 Result : Boolean;
1079 begin
1080 B := B + 1;
1081 L := L + 1;
1083 Result := True;
1084 for J in Index_Type'First .. Container.Last - 1 loop
1085 if EA (J + 1) < EA (J) then
1086 Result := False;
1087 exit;
1088 end if;
1089 end loop;
1091 B := B - 1;
1092 L := L - 1;
1094 return Result;
1096 exception
1097 when others =>
1098 B := B - 1;
1099 L := L - 1;
1101 raise;
1102 end;
1103 end Is_Sorted;
1105 -----------
1106 -- Merge --
1107 -----------
1109 procedure Merge (Target, Source : in out Vector) is
1110 I : Index_Type'Base := Target.Last;
1111 J : Index_Type'Base;
1113 begin
1114 -- The semantics of Merge changed slightly per AI05-0021. It was
1115 -- originally the case that if Target and Source denoted the same
1116 -- container object, then the GNAT implementation of Merge did
1117 -- nothing. However, it was argued that RM05 did not precisely
1118 -- specify the semantics for this corner case. The decision of the
1119 -- ARG was that if Target and Source denote the same non-empty
1120 -- container object, then Program_Error is raised.
1122 if Source.Last < Index_Type'First then -- Source is empty
1123 return;
1124 end if;
1126 if Target'Address = Source'Address then
1127 raise Program_Error with
1128 "Target and Source denote same non-empty container";
1129 end if;
1131 if Target.Last < Index_Type'First then -- Target is empty
1132 Move (Target => Target, Source => Source);
1133 return;
1134 end if;
1136 if Source.Busy > 0 then
1137 raise Program_Error with
1138 "attempt to tamper with cursors (vector is busy)";
1139 end if;
1141 Target.Set_Length (Length (Target) + Length (Source));
1143 -- Per AI05-0022, the container implementation is required to detect
1144 -- element tampering by a generic actual subprogram.
1146 declare
1147 TA : Elements_Array renames Target.Elements.EA;
1148 SA : Elements_Array renames Source.Elements.EA;
1150 TB : Natural renames Target.Busy;
1151 TL : Natural renames Target.Lock;
1153 SB : Natural renames Source.Busy;
1154 SL : Natural renames Source.Lock;
1156 begin
1157 TB := TB + 1;
1158 TL := TL + 1;
1160 SB := SB + 1;
1161 SL := SL + 1;
1163 J := Target.Last;
1164 while Source.Last >= Index_Type'First loop
1165 pragma Assert (Source.Last <= Index_Type'First
1166 or else not (SA (Source.Last) <
1167 SA (Source.Last - 1)));
1169 if I < Index_Type'First then
1170 TA (Index_Type'First .. J) :=
1171 SA (Index_Type'First .. Source.Last);
1173 Source.Last := No_Index;
1174 exit;
1175 end if;
1177 pragma Assert (I <= Index_Type'First
1178 or else not (TA (I) < TA (I - 1)));
1180 if SA (Source.Last) < TA (I) then
1181 TA (J) := TA (I);
1182 I := I - 1;
1184 else
1185 TA (J) := SA (Source.Last);
1186 Source.Last := Source.Last - 1;
1187 end if;
1189 J := J - 1;
1190 end loop;
1192 TB := TB - 1;
1193 TL := TL - 1;
1195 SB := SB - 1;
1196 SL := SL - 1;
1198 exception
1199 when others =>
1200 TB := TB - 1;
1201 TL := TL - 1;
1203 SB := SB - 1;
1204 SL := SL - 1;
1206 raise;
1207 end;
1208 end Merge;
1210 ----------
1211 -- Sort --
1212 ----------
1214 procedure Sort (Container : in out Vector) is
1215 procedure Sort is
1216 new Generic_Array_Sort
1217 (Index_Type => Index_Type,
1218 Element_Type => Element_Type,
1219 Array_Type => Elements_Array,
1220 "<" => "<");
1222 begin
1223 if Container.Last <= Index_Type'First then
1224 return;
1225 end if;
1227 -- The exception behavior for the vector container must match that
1228 -- for the list container, so we check for cursor tampering here
1229 -- (which will catch more things) instead of for element tampering
1230 -- (which will catch fewer things). It's true that the elements of
1231 -- this vector container could be safely moved around while (say) an
1232 -- iteration is taking place (iteration only increments the busy
1233 -- counter), and so technically all we would need here is a test for
1234 -- element tampering (indicated by the lock counter), that's simply
1235 -- an artifact of our array-based implementation. Logically Sort
1236 -- requires a check for cursor tampering.
1238 if Container.Busy > 0 then
1239 raise Program_Error with
1240 "attempt to tamper with cursors (vector is busy)";
1241 end if;
1243 -- Per AI05-0022, the container implementation is required to detect
1244 -- element tampering by a generic actual subprogram.
1246 declare
1247 B : Natural renames Container.Busy;
1248 L : Natural renames Container.Lock;
1250 begin
1251 B := B + 1;
1252 L := L + 1;
1254 Sort (Container.Elements.EA (Index_Type'First .. Container.Last));
1256 B := B - 1;
1257 L := L - 1;
1259 exception
1260 when others =>
1261 B := B - 1;
1262 L := L - 1;
1264 raise;
1265 end;
1266 end Sort;
1268 end Generic_Sorting;
1270 -----------------
1271 -- Has_Element --
1272 -----------------
1274 function Has_Element (Position : Cursor) return Boolean is
1275 begin
1276 return Position /= No_Element;
1277 end Has_Element;
1279 ------------
1280 -- Insert --
1281 ------------
1283 procedure Insert
1284 (Container : in out Vector;
1285 Before : Extended_Index;
1286 New_Item : Element_Type;
1287 Count : Count_Type := 1)
1289 Old_Length : constant Count_Type := Container.Length;
1291 Max_Length : Count_Type'Base; -- determined from range of Index_Type
1292 New_Length : Count_Type'Base; -- sum of current length and Count
1293 New_Last : Index_Type'Base; -- last index of vector after insertion
1295 Index : Index_Type'Base; -- scratch for intermediate values
1296 J : Count_Type'Base; -- scratch
1298 New_Capacity : Count_Type'Base; -- length of new, expanded array
1299 Dst_Last : Index_Type'Base; -- last index of new, expanded array
1300 Dst : Elements_Access; -- new, expanded internal array
1302 begin
1303 -- As a precondition on the generic actual Index_Type, the base type
1304 -- must include Index_Type'Pred (Index_Type'First); this is the value
1305 -- that Container.Last assumes when the vector is empty. However, we do
1306 -- not allow that as the value for Index when specifying where the new
1307 -- items should be inserted, so we must manually check. (That the user
1308 -- is allowed to specify the value at all here is a consequence of the
1309 -- declaration of the Extended_Index subtype, which includes the values
1310 -- in the base range that immediately precede and immediately follow the
1311 -- values in the Index_Type.)
1313 if Before < Index_Type'First then
1314 raise Constraint_Error with
1315 "Before index is out of range (too small)";
1316 end if;
1318 -- We do allow a value greater than Container.Last to be specified as
1319 -- the Index, but only if it's immediately greater. This allows for the
1320 -- case of appending items to the back end of the vector. (It is assumed
1321 -- that specifying an index value greater than Last + 1 indicates some
1322 -- deeper flaw in the caller's algorithm, so that case is treated as a
1323 -- proper error.)
1325 if Before > Container.Last and then Before > Container.Last + 1 then
1326 raise Constraint_Error with
1327 "Before index is out of range (too large)";
1328 end if;
1330 -- We treat inserting 0 items into the container as a no-op, even when
1331 -- the container is busy, so we simply return.
1333 if Count = 0 then
1334 return;
1335 end if;
1337 -- There are two constraints we need to satisfy. The first constraint is
1338 -- that a container cannot have more than Count_Type'Last elements, so
1339 -- we must check the sum of the current length and the insertion count.
1340 -- Note: we cannot simply add these values, because of the possibility
1341 -- of overflow.
1343 if Old_Length > Count_Type'Last - Count then
1344 raise Constraint_Error with "Count is out of range";
1345 end if;
1347 -- It is now safe compute the length of the new vector, without fear of
1348 -- overflow.
1350 New_Length := Old_Length + Count;
1352 -- The second constraint is that the new Last index value cannot exceed
1353 -- Index_Type'Last. In each branch below, we calculate the maximum
1354 -- length (computed from the range of values in Index_Type), and then
1355 -- compare the new length to the maximum length. If the new length is
1356 -- acceptable, then we compute the new last index from that.
1358 if Index_Type'Base'Last >= Count_Type'Pos (Count_Type'Last) then
1360 -- We have to handle the case when there might be more values in the
1361 -- range of Index_Type than in the range of Count_Type.
1363 if Index_Type'First <= 0 then
1365 -- We know that No_Index (the same as Index_Type'First - 1) is
1366 -- less than 0, so it is safe to compute the following sum without
1367 -- fear of overflow.
1369 Index := No_Index + Index_Type'Base (Count_Type'Last);
1371 if Index <= Index_Type'Last then
1373 -- We have determined that range of Index_Type has at least as
1374 -- many values as in Count_Type, so Count_Type'Last is the
1375 -- maximum number of items that are allowed.
1377 Max_Length := Count_Type'Last;
1379 else
1380 -- The range of Index_Type has fewer values than in Count_Type,
1381 -- so the maximum number of items is computed from the range of
1382 -- the Index_Type.
1384 Max_Length := Count_Type'Base (Index_Type'Last - No_Index);
1385 end if;
1387 else
1388 -- No_Index is equal or greater than 0, so we can safely compute
1389 -- the difference without fear of overflow (which we would have to
1390 -- worry about if No_Index were less than 0, but that case is
1391 -- handled above).
1393 if Index_Type'Last - No_Index >=
1394 Count_Type'Pos (Count_Type'Last)
1395 then
1396 -- We have determined that range of Index_Type has at least as
1397 -- many values as in Count_Type, so Count_Type'Last is the
1398 -- maximum number of items that are allowed.
1400 Max_Length := Count_Type'Last;
1402 else
1403 -- The range of Index_Type has fewer values than in Count_Type,
1404 -- so the maximum number of items is computed from the range of
1405 -- the Index_Type.
1407 Max_Length := Count_Type'Base (Index_Type'Last - No_Index);
1408 end if;
1409 end if;
1411 elsif Index_Type'First <= 0 then
1413 -- We know that No_Index (the same as Index_Type'First - 1) is less
1414 -- than 0, so it is safe to compute the following sum without fear of
1415 -- overflow.
1417 J := Count_Type'Base (No_Index) + Count_Type'Last;
1419 if J <= Count_Type'Base (Index_Type'Last) then
1421 -- We have determined that range of Index_Type has at least as
1422 -- many values as in Count_Type, so Count_Type'Last is the maximum
1423 -- number of items that are allowed.
1425 Max_Length := Count_Type'Last;
1427 else
1428 -- The range of Index_Type has fewer values than Count_Type does,
1429 -- so the maximum number of items is computed from the range of
1430 -- the Index_Type.
1432 Max_Length :=
1433 Count_Type'Base (Index_Type'Last) - Count_Type'Base (No_Index);
1434 end if;
1436 else
1437 -- No_Index is equal or greater than 0, so we can safely compute the
1438 -- difference without fear of overflow (which we would have to worry
1439 -- about if No_Index were less than 0, but that case is handled
1440 -- above).
1442 Max_Length :=
1443 Count_Type'Base (Index_Type'Last) - Count_Type'Base (No_Index);
1444 end if;
1446 -- We have just computed the maximum length (number of items). We must
1447 -- now compare the requested length to the maximum length, as we do not
1448 -- allow a vector expand beyond the maximum (because that would create
1449 -- an internal array with a last index value greater than
1450 -- Index_Type'Last, with no way to index those elements).
1452 if New_Length > Max_Length then
1453 raise Constraint_Error with "Count is out of range";
1454 end if;
1456 -- New_Last is the last index value of the items in the container after
1457 -- insertion. Use the wider of Index_Type'Base and Count_Type'Base to
1458 -- compute its value from the New_Length.
1460 if Index_Type'Base'Last >= Count_Type'Pos (Count_Type'Last) then
1461 New_Last := No_Index + Index_Type'Base (New_Length);
1462 else
1463 New_Last := Index_Type'Base (Count_Type'Base (No_Index) + New_Length);
1464 end if;
1466 if Container.Elements = null then
1467 pragma Assert (Container.Last = No_Index);
1469 -- This is the simplest case, with which we must always begin: we're
1470 -- inserting items into an empty vector that hasn't allocated an
1471 -- internal array yet. Note that we don't need to check the busy bit
1472 -- here, because an empty container cannot be busy.
1474 -- In order to preserve container invariants, we allocate the new
1475 -- internal array first, before setting the Last index value, in case
1476 -- the allocation fails (which can happen either because there is no
1477 -- storage available, or because element initialization fails).
1479 Container.Elements := new Elements_Type'
1480 (Last => New_Last,
1481 EA => (others => New_Item));
1483 -- The allocation of the new, internal array succeeded, so it is now
1484 -- safe to update the Last index, restoring container invariants.
1486 Container.Last := New_Last;
1488 return;
1489 end if;
1491 -- The tampering bits exist to prevent an item from being harmfully
1492 -- manipulated while it is being visited. Query, Update, and Iterate
1493 -- increment the busy count on entry, and decrement the count on
1494 -- exit. Insert checks the count to determine whether it is being called
1495 -- while the associated callback procedure is executing.
1497 if Container.Busy > 0 then
1498 raise Program_Error with
1499 "attempt to tamper with cursors (vector is busy)";
1500 end if;
1502 -- An internal array has already been allocated, so we must determine
1503 -- whether there is enough unused storage for the new items.
1505 if New_Length <= Container.Elements.EA'Length then
1507 -- In this case, we're inserting elements into a vector that has
1508 -- already allocated an internal array, and the existing array has
1509 -- enough unused storage for the new items.
1511 declare
1512 EA : Elements_Array renames Container.Elements.EA;
1514 begin
1515 if Before > Container.Last then
1517 -- The new items are being appended to the vector, so no
1518 -- sliding of existing elements is required.
1520 EA (Before .. New_Last) := (others => New_Item);
1522 else
1523 -- The new items are being inserted before some existing
1524 -- elements, so we must slide the existing elements up to their
1525 -- new home. We use the wider of Index_Type'Base and
1526 -- Count_Type'Base as the type for intermediate index values.
1528 if Index_Type'Base'Last >= Count_Type'Pos (Count_Type'Last) then
1529 Index := Before + Index_Type'Base (Count);
1530 else
1531 Index := Index_Type'Base (Count_Type'Base (Before) + Count);
1532 end if;
1534 EA (Index .. New_Last) := EA (Before .. Container.Last);
1535 EA (Before .. Index - 1) := (others => New_Item);
1536 end if;
1537 end;
1539 Container.Last := New_Last;
1540 return;
1541 end if;
1543 -- In this case, we're inserting elements into a vector that has already
1544 -- allocated an internal array, but the existing array does not have
1545 -- enough storage, so we must allocate a new, longer array. In order to
1546 -- guarantee that the amortized insertion cost is O(1), we always
1547 -- allocate an array whose length is some power-of-two factor of the
1548 -- current array length. (The new array cannot have a length less than
1549 -- the New_Length of the container, but its last index value cannot be
1550 -- greater than Index_Type'Last.)
1552 New_Capacity := Count_Type'Max (1, Container.Elements.EA'Length);
1553 while New_Capacity < New_Length loop
1554 if New_Capacity > Count_Type'Last / 2 then
1555 New_Capacity := Count_Type'Last;
1556 exit;
1557 else
1558 New_Capacity := 2 * New_Capacity;
1559 end if;
1560 end loop;
1562 if New_Capacity > Max_Length then
1564 -- We have reached the limit of capacity, so no further expansion
1565 -- will occur. (This is not a problem, as there is never a need to
1566 -- have more capacity than the maximum container length.)
1568 New_Capacity := Max_Length;
1569 end if;
1571 -- We have computed the length of the new internal array (and this is
1572 -- what "vector capacity" means), so use that to compute its last index.
1574 if Index_Type'Base'Last >= Count_Type'Pos (Count_Type'Last) then
1575 Dst_Last := No_Index + Index_Type'Base (New_Capacity);
1576 else
1577 Dst_Last :=
1578 Index_Type'Base (Count_Type'Base (No_Index) + New_Capacity);
1579 end if;
1581 -- Now we allocate the new, longer internal array. If the allocation
1582 -- fails, we have not changed any container state, so no side-effect
1583 -- will occur as a result of propagating the exception.
1585 Dst := new Elements_Type (Dst_Last);
1587 -- We have our new internal array. All that needs to be done now is to
1588 -- copy the existing items (if any) from the old array (the "source"
1589 -- array, object SA below) to the new array (the "destination" array,
1590 -- object DA below), and then deallocate the old array.
1592 declare
1593 SA : Elements_Array renames Container.Elements.EA; -- source
1594 DA : Elements_Array renames Dst.EA; -- destination
1596 begin
1597 DA (Index_Type'First .. Before - 1) :=
1598 SA (Index_Type'First .. Before - 1);
1600 if Before > Container.Last then
1601 DA (Before .. New_Last) := (others => New_Item);
1603 else
1604 -- The new items are being inserted before some existing elements,
1605 -- so we must slide the existing elements up to their new home.
1607 if Index_Type'Base'Last >= Count_Type'Pos (Count_Type'Last) then
1608 Index := Before + Index_Type'Base (Count);
1609 else
1610 Index := Index_Type'Base (Count_Type'Base (Before) + Count);
1611 end if;
1613 DA (Before .. Index - 1) := (others => New_Item);
1614 DA (Index .. New_Last) := SA (Before .. Container.Last);
1615 end if;
1617 exception
1618 when others =>
1619 Free (Dst);
1620 raise;
1621 end;
1623 -- We have successfully copied the items onto the new array, so the
1624 -- final thing to do is deallocate the old array.
1626 declare
1627 X : Elements_Access := Container.Elements;
1629 begin
1630 -- We first isolate the old internal array, removing it from the
1631 -- container and replacing it with the new internal array, before we
1632 -- deallocate the old array (which can fail if finalization of
1633 -- elements propagates an exception).
1635 Container.Elements := Dst;
1636 Container.Last := New_Last;
1638 -- The container invariants have been restored, so it is now safe to
1639 -- attempt to deallocate the old array.
1641 Free (X);
1642 end;
1643 end Insert;
1645 procedure Insert
1646 (Container : in out Vector;
1647 Before : Extended_Index;
1648 New_Item : Vector)
1650 N : constant Count_Type := Length (New_Item);
1651 J : Index_Type'Base;
1653 begin
1654 -- Use Insert_Space to create the "hole" (the destination slice) into
1655 -- which we copy the source items.
1657 Insert_Space (Container, Before, Count => N);
1659 if N = 0 then
1661 -- There's nothing else to do here (vetting of parameters was
1662 -- performed already in Insert_Space), so we simply return.
1664 return;
1665 end if;
1667 -- We calculate the last index value of the destination slice using the
1668 -- wider of Index_Type'Base and count_Type'Base.
1670 if Index_Type'Base'Last >= Count_Type'Pos (Count_Type'Last) then
1671 J := (Before - 1) + Index_Type'Base (N);
1672 else
1673 J := Index_Type'Base (Count_Type'Base (Before - 1) + N);
1674 end if;
1676 if Container'Address /= New_Item'Address then
1678 -- This is the simple case. New_Item denotes an object different
1679 -- from Container, so there's nothing special we need to do to copy
1680 -- the source items to their destination, because all of the source
1681 -- items are contiguous.
1683 Container.Elements.EA (Before .. J) :=
1684 New_Item.Elements.EA (Index_Type'First .. New_Item.Last);
1686 return;
1687 end if;
1689 -- New_Item denotes the same object as Container, so an insertion has
1690 -- potentially split the source items. The destination is always the
1691 -- range [Before, J], but the source is [Index_Type'First, Before) and
1692 -- (J, Container.Last]. We perform the copy in two steps, using each of
1693 -- the two slices of the source items.
1695 declare
1696 L : constant Index_Type'Base := Before - 1;
1698 subtype Src_Index_Subtype is Index_Type'Base range
1699 Index_Type'First .. L;
1701 Src : Elements_Array renames
1702 Container.Elements.EA (Src_Index_Subtype);
1704 K : Index_Type'Base;
1706 begin
1707 -- We first copy the source items that precede the space we
1708 -- inserted. Index value K is the last index of that portion
1709 -- destination that receives this slice of the source. (If Before
1710 -- equals Index_Type'First, then this first source slice will be
1711 -- empty, which is harmless.)
1713 if Index_Type'Base'Last >= Count_Type'Pos (Count_Type'Last) then
1714 K := L + Index_Type'Base (Src'Length);
1715 else
1716 K := Index_Type'Base (Count_Type'Base (L) + Src'Length);
1717 end if;
1719 Container.Elements.EA (Before .. K) := Src;
1721 if Src'Length = N then
1723 -- The new items were effectively appended to the container, so we
1724 -- have already copied all of the items that need to be copied.
1725 -- We return early here, even though the source slice below is
1726 -- empty (so the assignment would be harmless), because we want to
1727 -- avoid computing J + 1, which will overflow if J equals
1728 -- Index_Type'Base'Last.
1730 return;
1731 end if;
1732 end;
1734 declare
1735 -- Note that we want to avoid computing J + 1 here, in case J equals
1736 -- Index_Type'Base'Last. We prevent that by returning early above,
1737 -- immediately after copying the first slice of the source, and
1738 -- determining that this second slice of the source is empty.
1740 F : constant Index_Type'Base := J + 1;
1742 subtype Src_Index_Subtype is Index_Type'Base range
1743 F .. Container.Last;
1745 Src : Elements_Array renames
1746 Container.Elements.EA (Src_Index_Subtype);
1748 K : Index_Type'Base;
1750 begin
1751 -- We next copy the source items that follow the space we inserted.
1752 -- Index value K is the first index of that portion of the
1753 -- destination that receives this slice of the source. (For the
1754 -- reasons given above, this slice is guaranteed to be non-empty.)
1756 if Index_Type'Base'Last >= Count_Type'Pos (Count_Type'Last) then
1757 K := F - Index_Type'Base (Src'Length);
1758 else
1759 K := Index_Type'Base (Count_Type'Base (F) - Src'Length);
1760 end if;
1762 Container.Elements.EA (K .. J) := Src;
1763 end;
1764 end Insert;
1766 procedure Insert
1767 (Container : in out Vector;
1768 Before : Cursor;
1769 New_Item : Vector)
1771 Index : Index_Type'Base;
1773 begin
1774 if Before.Container /= null
1775 and then Before.Container /= Container'Unrestricted_Access
1776 then
1777 raise Program_Error with "Before cursor denotes wrong container";
1778 end if;
1780 if Is_Empty (New_Item) then
1781 return;
1782 end if;
1784 if Before.Container = null or else Before.Index > Container.Last then
1785 if Container.Last = Index_Type'Last then
1786 raise Constraint_Error with
1787 "vector is already at its maximum length";
1788 end if;
1790 Index := Container.Last + 1;
1792 else
1793 Index := Before.Index;
1794 end if;
1796 Insert (Container, Index, New_Item);
1797 end Insert;
1799 procedure Insert
1800 (Container : in out Vector;
1801 Before : Cursor;
1802 New_Item : Vector;
1803 Position : out Cursor)
1805 Index : Index_Type'Base;
1807 begin
1808 if Before.Container /= null
1809 and then Before.Container /= Container'Unrestricted_Access
1810 then
1811 raise Program_Error with "Before cursor denotes wrong container";
1812 end if;
1814 if Is_Empty (New_Item) then
1815 if Before.Container = null or else Before.Index > Container.Last then
1816 Position := No_Element;
1817 else
1818 Position := (Container'Unrestricted_Access, Before.Index);
1819 end if;
1821 return;
1822 end if;
1824 if Before.Container = null or else Before.Index > Container.Last then
1825 if Container.Last = Index_Type'Last then
1826 raise Constraint_Error with
1827 "vector is already at its maximum length";
1828 end if;
1830 Index := Container.Last + 1;
1832 else
1833 Index := Before.Index;
1834 end if;
1836 Insert (Container, Index, New_Item);
1838 Position := (Container'Unrestricted_Access, Index);
1839 end Insert;
1841 procedure Insert
1842 (Container : in out Vector;
1843 Before : Cursor;
1844 New_Item : Element_Type;
1845 Count : Count_Type := 1)
1847 Index : Index_Type'Base;
1849 begin
1850 if Before.Container /= null
1851 and then Before.Container /= Container'Unrestricted_Access
1852 then
1853 raise Program_Error with "Before cursor denotes wrong container";
1854 end if;
1856 if Count = 0 then
1857 return;
1858 end if;
1860 if Before.Container = null or else Before.Index > Container.Last then
1861 if Container.Last = Index_Type'Last then
1862 raise Constraint_Error with
1863 "vector is already at its maximum length";
1864 else
1865 Index := Container.Last + 1;
1866 end if;
1868 else
1869 Index := Before.Index;
1870 end if;
1872 Insert (Container, Index, New_Item, Count);
1873 end Insert;
1875 procedure Insert
1876 (Container : in out Vector;
1877 Before : Cursor;
1878 New_Item : Element_Type;
1879 Position : out Cursor;
1880 Count : Count_Type := 1)
1882 Index : Index_Type'Base;
1884 begin
1885 if Before.Container /= null
1886 and then Before.Container /= Container'Unrestricted_Access
1887 then
1888 raise Program_Error with "Before cursor denotes wrong container";
1889 end if;
1891 if Count = 0 then
1892 if Before.Container = null or else Before.Index > Container.Last then
1893 Position := No_Element;
1894 else
1895 Position := (Container'Unrestricted_Access, Before.Index);
1896 end if;
1898 return;
1899 end if;
1901 if Before.Container = null or else Before.Index > Container.Last then
1902 if Container.Last = Index_Type'Last then
1903 raise Constraint_Error with
1904 "vector is already at its maximum length";
1905 end if;
1907 Index := Container.Last + 1;
1909 else
1910 Index := Before.Index;
1911 end if;
1913 Insert (Container, Index, New_Item, Count);
1915 Position := (Container'Unrestricted_Access, Index);
1916 end Insert;
1918 procedure Insert
1919 (Container : in out Vector;
1920 Before : Extended_Index;
1921 Count : Count_Type := 1)
1923 New_Item : Element_Type; -- Default-initialized value
1924 pragma Warnings (Off, New_Item);
1926 begin
1927 Insert (Container, Before, New_Item, Count);
1928 end Insert;
1930 procedure Insert
1931 (Container : in out Vector;
1932 Before : Cursor;
1933 Position : out Cursor;
1934 Count : Count_Type := 1)
1936 New_Item : Element_Type; -- Default-initialized value
1937 pragma Warnings (Off, New_Item);
1938 begin
1939 Insert (Container, Before, New_Item, Position, Count);
1940 end Insert;
1942 ------------------
1943 -- Insert_Space --
1944 ------------------
1946 procedure Insert_Space
1947 (Container : in out Vector;
1948 Before : Extended_Index;
1949 Count : Count_Type := 1)
1951 Old_Length : constant Count_Type := Container.Length;
1953 Max_Length : Count_Type'Base; -- determined from range of Index_Type
1954 New_Length : Count_Type'Base; -- sum of current length and Count
1955 New_Last : Index_Type'Base; -- last index of vector after insertion
1957 Index : Index_Type'Base; -- scratch for intermediate values
1958 J : Count_Type'Base; -- scratch
1960 New_Capacity : Count_Type'Base; -- length of new, expanded array
1961 Dst_Last : Index_Type'Base; -- last index of new, expanded array
1962 Dst : Elements_Access; -- new, expanded internal array
1964 begin
1965 -- As a precondition on the generic actual Index_Type, the base type
1966 -- must include Index_Type'Pred (Index_Type'First); this is the value
1967 -- that Container.Last assumes when the vector is empty. However, we do
1968 -- not allow that as the value for Index when specifying where the new
1969 -- items should be inserted, so we must manually check. (That the user
1970 -- is allowed to specify the value at all here is a consequence of the
1971 -- declaration of the Extended_Index subtype, which includes the values
1972 -- in the base range that immediately precede and immediately follow the
1973 -- values in the Index_Type.)
1975 if Before < Index_Type'First then
1976 raise Constraint_Error with
1977 "Before index is out of range (too small)";
1978 end if;
1980 -- We do allow a value greater than Container.Last to be specified as
1981 -- the Index, but only if it's immediately greater. This allows for the
1982 -- case of appending items to the back end of the vector. (It is assumed
1983 -- that specifying an index value greater than Last + 1 indicates some
1984 -- deeper flaw in the caller's algorithm, so that case is treated as a
1985 -- proper error.)
1987 if Before > Container.Last and then Before > Container.Last + 1 then
1988 raise Constraint_Error with
1989 "Before index is out of range (too large)";
1990 end if;
1992 -- We treat inserting 0 items into the container as a no-op, even when
1993 -- the container is busy, so we simply return.
1995 if Count = 0 then
1996 return;
1997 end if;
1999 -- There are two constraints we need to satisfy. The first constraint is
2000 -- that a container cannot have more than Count_Type'Last elements, so
2001 -- we must check the sum of the current length and the insertion count.
2002 -- Note: we cannot simply add these values, because of the possibility
2003 -- of overflow.
2005 if Old_Length > Count_Type'Last - Count then
2006 raise Constraint_Error with "Count is out of range";
2007 end if;
2009 -- It is now safe compute the length of the new vector, without fear of
2010 -- overflow.
2012 New_Length := Old_Length + Count;
2014 -- The second constraint is that the new Last index value cannot exceed
2015 -- Index_Type'Last. In each branch below, we calculate the maximum
2016 -- length (computed from the range of values in Index_Type), and then
2017 -- compare the new length to the maximum length. If the new length is
2018 -- acceptable, then we compute the new last index from that.
2020 if Index_Type'Base'Last >= Count_Type'Pos (Count_Type'Last) then
2022 -- We have to handle the case when there might be more values in the
2023 -- range of Index_Type than in the range of Count_Type.
2025 if Index_Type'First <= 0 then
2027 -- We know that No_Index (the same as Index_Type'First - 1) is
2028 -- less than 0, so it is safe to compute the following sum without
2029 -- fear of overflow.
2031 Index := No_Index + Index_Type'Base (Count_Type'Last);
2033 if Index <= Index_Type'Last then
2035 -- We have determined that range of Index_Type has at least as
2036 -- many values as in Count_Type, so Count_Type'Last is the
2037 -- maximum number of items that are allowed.
2039 Max_Length := Count_Type'Last;
2041 else
2042 -- The range of Index_Type has fewer values than in Count_Type,
2043 -- so the maximum number of items is computed from the range of
2044 -- the Index_Type.
2046 Max_Length := Count_Type'Base (Index_Type'Last - No_Index);
2047 end if;
2049 else
2050 -- No_Index is equal or greater than 0, so we can safely compute
2051 -- the difference without fear of overflow (which we would have to
2052 -- worry about if No_Index were less than 0, but that case is
2053 -- handled above).
2055 if Index_Type'Last - No_Index >=
2056 Count_Type'Pos (Count_Type'Last)
2057 then
2058 -- We have determined that range of Index_Type has at least as
2059 -- many values as in Count_Type, so Count_Type'Last is the
2060 -- maximum number of items that are allowed.
2062 Max_Length := Count_Type'Last;
2064 else
2065 -- The range of Index_Type has fewer values than in Count_Type,
2066 -- so the maximum number of items is computed from the range of
2067 -- the Index_Type.
2069 Max_Length := Count_Type'Base (Index_Type'Last - No_Index);
2070 end if;
2071 end if;
2073 elsif Index_Type'First <= 0 then
2075 -- We know that No_Index (the same as Index_Type'First - 1) is less
2076 -- than 0, so it is safe to compute the following sum without fear of
2077 -- overflow.
2079 J := Count_Type'Base (No_Index) + Count_Type'Last;
2081 if J <= Count_Type'Base (Index_Type'Last) then
2083 -- We have determined that range of Index_Type has at least as
2084 -- many values as in Count_Type, so Count_Type'Last is the maximum
2085 -- number of items that are allowed.
2087 Max_Length := Count_Type'Last;
2089 else
2090 -- The range of Index_Type has fewer values than Count_Type does,
2091 -- so the maximum number of items is computed from the range of
2092 -- the Index_Type.
2094 Max_Length :=
2095 Count_Type'Base (Index_Type'Last) - Count_Type'Base (No_Index);
2096 end if;
2098 else
2099 -- No_Index is equal or greater than 0, so we can safely compute the
2100 -- difference without fear of overflow (which we would have to worry
2101 -- about if No_Index were less than 0, but that case is handled
2102 -- above).
2104 Max_Length :=
2105 Count_Type'Base (Index_Type'Last) - Count_Type'Base (No_Index);
2106 end if;
2108 -- We have just computed the maximum length (number of items). We must
2109 -- now compare the requested length to the maximum length, as we do not
2110 -- allow a vector expand beyond the maximum (because that would create
2111 -- an internal array with a last index value greater than
2112 -- Index_Type'Last, with no way to index those elements).
2114 if New_Length > Max_Length then
2115 raise Constraint_Error with "Count is out of range";
2116 end if;
2118 -- New_Last is the last index value of the items in the container after
2119 -- insertion. Use the wider of Index_Type'Base and Count_Type'Base to
2120 -- compute its value from the New_Length.
2122 if Index_Type'Base'Last >= Count_Type'Pos (Count_Type'Last) then
2123 New_Last := No_Index + Index_Type'Base (New_Length);
2124 else
2125 New_Last := Index_Type'Base (Count_Type'Base (No_Index) + New_Length);
2126 end if;
2128 if Container.Elements = null then
2129 pragma Assert (Container.Last = No_Index);
2131 -- This is the simplest case, with which we must always begin: we're
2132 -- inserting items into an empty vector that hasn't allocated an
2133 -- internal array yet. Note that we don't need to check the busy bit
2134 -- here, because an empty container cannot be busy.
2136 -- In order to preserve container invariants, we allocate the new
2137 -- internal array first, before setting the Last index value, in case
2138 -- the allocation fails (which can happen either because there is no
2139 -- storage available, or because default-valued element
2140 -- initialization fails).
2142 Container.Elements := new Elements_Type (New_Last);
2144 -- The allocation of the new, internal array succeeded, so it is now
2145 -- safe to update the Last index, restoring container invariants.
2147 Container.Last := New_Last;
2149 return;
2150 end if;
2152 -- The tampering bits exist to prevent an item from being harmfully
2153 -- manipulated while it is being visited. Query, Update, and Iterate
2154 -- increment the busy count on entry, and decrement the count on
2155 -- exit. Insert checks the count to determine whether it is being called
2156 -- while the associated callback procedure is executing.
2158 if Container.Busy > 0 then
2159 raise Program_Error with
2160 "attempt to tamper with cursors (vector is busy)";
2161 end if;
2163 -- An internal array has already been allocated, so we must determine
2164 -- whether there is enough unused storage for the new items.
2166 if New_Last <= Container.Elements.Last then
2168 -- In this case, we're inserting space into a vector that has already
2169 -- allocated an internal array, and the existing array has enough
2170 -- unused storage for the new items.
2172 declare
2173 EA : Elements_Array renames Container.Elements.EA;
2175 begin
2176 if Before <= Container.Last then
2178 -- The space is being inserted before some existing elements,
2179 -- so we must slide the existing elements up to their new
2180 -- home. We use the wider of Index_Type'Base and
2181 -- Count_Type'Base as the type for intermediate index values.
2183 if Index_Type'Base'Last >= Count_Type'Pos (Count_Type'Last) then
2184 Index := Before + Index_Type'Base (Count);
2186 else
2187 Index := Index_Type'Base (Count_Type'Base (Before) + Count);
2188 end if;
2190 EA (Index .. New_Last) := EA (Before .. Container.Last);
2191 end if;
2192 end;
2194 Container.Last := New_Last;
2195 return;
2196 end if;
2198 -- In this case, we're inserting space into a vector that has already
2199 -- allocated an internal array, but the existing array does not have
2200 -- enough storage, so we must allocate a new, longer array. In order to
2201 -- guarantee that the amortized insertion cost is O(1), we always
2202 -- allocate an array whose length is some power-of-two factor of the
2203 -- current array length. (The new array cannot have a length less than
2204 -- the New_Length of the container, but its last index value cannot be
2205 -- greater than Index_Type'Last.)
2207 New_Capacity := Count_Type'Max (1, Container.Elements.EA'Length);
2208 while New_Capacity < New_Length loop
2209 if New_Capacity > Count_Type'Last / 2 then
2210 New_Capacity := Count_Type'Last;
2211 exit;
2212 end if;
2214 New_Capacity := 2 * New_Capacity;
2215 end loop;
2217 if New_Capacity > Max_Length then
2219 -- We have reached the limit of capacity, so no further expansion
2220 -- will occur. (This is not a problem, as there is never a need to
2221 -- have more capacity than the maximum container length.)
2223 New_Capacity := Max_Length;
2224 end if;
2226 -- We have computed the length of the new internal array (and this is
2227 -- what "vector capacity" means), so use that to compute its last index.
2229 if Index_Type'Base'Last >= Count_Type'Pos (Count_Type'Last) then
2230 Dst_Last := No_Index + Index_Type'Base (New_Capacity);
2231 else
2232 Dst_Last :=
2233 Index_Type'Base (Count_Type'Base (No_Index) + New_Capacity);
2234 end if;
2236 -- Now we allocate the new, longer internal array. If the allocation
2237 -- fails, we have not changed any container state, so no side-effect
2238 -- will occur as a result of propagating the exception.
2240 Dst := new Elements_Type (Dst_Last);
2242 -- We have our new internal array. All that needs to be done now is to
2243 -- copy the existing items (if any) from the old array (the "source"
2244 -- array, object SA below) to the new array (the "destination" array,
2245 -- object DA below), and then deallocate the old array.
2247 declare
2248 SA : Elements_Array renames Container.Elements.EA; -- source
2249 DA : Elements_Array renames Dst.EA; -- destination
2251 begin
2252 DA (Index_Type'First .. Before - 1) :=
2253 SA (Index_Type'First .. Before - 1);
2255 if Before <= Container.Last then
2257 -- The space is being inserted before some existing elements, so
2258 -- we must slide the existing elements up to their new home.
2260 if Index_Type'Base'Last >= Count_Type'Pos (Count_Type'Last) then
2261 Index := Before + Index_Type'Base (Count);
2262 else
2263 Index := Index_Type'Base (Count_Type'Base (Before) + Count);
2264 end if;
2266 DA (Index .. New_Last) := SA (Before .. Container.Last);
2267 end if;
2269 exception
2270 when others =>
2271 Free (Dst);
2272 raise;
2273 end;
2275 -- We have successfully copied the items onto the new array, so the
2276 -- final thing to do is restore invariants, and deallocate the old
2277 -- array.
2279 declare
2280 X : Elements_Access := Container.Elements;
2282 begin
2283 -- We first isolate the old internal array, removing it from the
2284 -- container and replacing it with the new internal array, before we
2285 -- deallocate the old array (which can fail if finalization of
2286 -- elements propagates an exception).
2288 Container.Elements := Dst;
2289 Container.Last := New_Last;
2291 -- The container invariants have been restored, so it is now safe to
2292 -- attempt to deallocate the old array.
2294 Free (X);
2295 end;
2296 end Insert_Space;
2298 procedure Insert_Space
2299 (Container : in out Vector;
2300 Before : Cursor;
2301 Position : out Cursor;
2302 Count : Count_Type := 1)
2304 Index : Index_Type'Base;
2306 begin
2307 if Before.Container /= null
2308 and then Before.Container /= Container'Unrestricted_Access
2309 then
2310 raise Program_Error with "Before cursor denotes wrong container";
2311 end if;
2313 if Count = 0 then
2314 if Before.Container = null or else Before.Index > Container.Last then
2315 Position := No_Element;
2316 else
2317 Position := (Container'Unrestricted_Access, Before.Index);
2318 end if;
2320 return;
2321 end if;
2323 if Before.Container = null or else Before.Index > Container.Last then
2324 if Container.Last = Index_Type'Last then
2325 raise Constraint_Error with
2326 "vector is already at its maximum length";
2327 else
2328 Index := Container.Last + 1;
2329 end if;
2331 else
2332 Index := Before.Index;
2333 end if;
2335 Insert_Space (Container, Index, Count => Count);
2337 Position := (Container'Unrestricted_Access, Index);
2338 end Insert_Space;
2340 --------------
2341 -- Is_Empty --
2342 --------------
2344 function Is_Empty (Container : Vector) return Boolean is
2345 begin
2346 return Container.Last < Index_Type'First;
2347 end Is_Empty;
2349 -------------
2350 -- Iterate --
2351 -------------
2353 procedure Iterate
2354 (Container : Vector;
2355 Process : not null access procedure (Position : Cursor))
2357 B : Natural renames Container'Unrestricted_Access.all.Busy;
2359 begin
2360 B := B + 1;
2362 begin
2363 for Indx in Index_Type'First .. Container.Last loop
2364 Process (Cursor'(Container'Unrestricted_Access, Indx));
2365 end loop;
2366 exception
2367 when others =>
2368 B := B - 1;
2369 raise;
2370 end;
2372 B := B - 1;
2373 end Iterate;
2375 function Iterate
2376 (Container : Vector)
2377 return Vector_Iterator_Interfaces.Reversible_Iterator'Class
2379 V : constant Vector_Access := Container'Unrestricted_Access;
2380 B : Natural renames V.Busy;
2382 begin
2383 -- The value of its Index component influences the behavior of the First
2384 -- and Last selector functions of the iterator object. When the Index
2385 -- component is No_Index (as is the case here), this means the iterator
2386 -- object was constructed without a start expression. This is a complete
2387 -- iterator, meaning that the iteration starts from the (logical)
2388 -- beginning of the sequence of items.
2390 -- Note: For a forward iterator, Container.First is the beginning, and
2391 -- for a reverse iterator, Container.Last is the beginning.
2393 return It : constant Iterator :=
2394 (Limited_Controlled with
2395 Container => V,
2396 Index => No_Index)
2398 B := B + 1;
2399 end return;
2400 end Iterate;
2402 function Iterate
2403 (Container : Vector;
2404 Start : Cursor)
2405 return Vector_Iterator_Interfaces.Reversible_Iterator'class
2407 V : constant Vector_Access := Container'Unrestricted_Access;
2408 B : Natural renames V.Busy;
2410 begin
2411 -- It was formerly the case that when Start = No_Element, the partial
2412 -- iterator was defined to behave the same as for a complete iterator,
2413 -- and iterate over the entire sequence of items. However, those
2414 -- semantics were unintuitive and arguably error-prone (it is too easy
2415 -- to accidentally create an endless loop), and so they were changed,
2416 -- per the ARG meeting in Denver on 2011/11. However, there was no
2417 -- consensus about what positive meaning this corner case should have,
2418 -- and so it was decided to simply raise an exception. This does imply,
2419 -- however, that it is not possible to use a partial iterator to specify
2420 -- an empty sequence of items.
2422 if Start.Container = null then
2423 raise Constraint_Error with
2424 "Start position for iterator equals No_Element";
2425 end if;
2427 if Start.Container /= V then
2428 raise Program_Error with
2429 "Start cursor of Iterate designates wrong vector";
2430 end if;
2432 if Start.Index > V.Last then
2433 raise Constraint_Error with
2434 "Start position for iterator equals No_Element";
2435 end if;
2437 -- The value of its Index component influences the behavior of the First
2438 -- and Last selector functions of the iterator object. When the Index
2439 -- component is not No_Index (as is the case here), it means that this
2440 -- is a partial iteration, over a subset of the complete sequence of
2441 -- items. The iterator object was constructed with a start expression,
2442 -- indicating the position from which the iteration begins. Note that
2443 -- the start position has the same value irrespective of whether this
2444 -- is a forward or reverse iteration.
2446 return It : constant Iterator :=
2447 (Limited_Controlled with
2448 Container => V,
2449 Index => Start.Index)
2451 B := B + 1;
2452 end return;
2453 end Iterate;
2455 ----------
2456 -- Last --
2457 ----------
2459 function Last (Container : Vector) return Cursor is
2460 begin
2461 if Is_Empty (Container) then
2462 return No_Element;
2463 else
2464 return (Container'Unrestricted_Access, Container.Last);
2465 end if;
2466 end Last;
2468 function Last (Object : Iterator) return Cursor is
2469 begin
2470 -- The value of the iterator object's Index component influences the
2471 -- behavior of the Last (and First) selector function.
2473 -- When the Index component is No_Index, this means the iterator
2474 -- object was constructed without a start expression, in which case the
2475 -- (reverse) iteration starts from the (logical) beginning of the entire
2476 -- sequence (corresponding to Container.Last, for a reverse iterator).
2478 -- Otherwise, this is iteration over a partial sequence of items.
2479 -- When the Index component is not No_Index, the iterator object was
2480 -- constructed with a start expression, that specifies the position
2481 -- from which the (reverse) partial iteration begins.
2483 if Object.Index = No_Index then
2484 return Last (Object.Container.all);
2485 else
2486 return Cursor'(Object.Container, Object.Index);
2487 end if;
2488 end Last;
2490 ------------------
2491 -- Last_Element --
2492 ------------------
2494 function Last_Element (Container : Vector) return Element_Type is
2495 begin
2496 if Container.Last = No_Index then
2497 raise Constraint_Error with "Container is empty";
2498 else
2499 return Container.Elements.EA (Container.Last);
2500 end if;
2501 end Last_Element;
2503 ----------------
2504 -- Last_Index --
2505 ----------------
2507 function Last_Index (Container : Vector) return Extended_Index is
2508 begin
2509 return Container.Last;
2510 end Last_Index;
2512 ------------
2513 -- Length --
2514 ------------
2516 function Length (Container : Vector) return Count_Type is
2517 L : constant Index_Type'Base := Container.Last;
2518 F : constant Index_Type := Index_Type'First;
2520 begin
2521 -- The base range of the index type (Index_Type'Base) might not include
2522 -- all values for length (Count_Type). Contrariwise, the index type
2523 -- might include values outside the range of length. Hence we use
2524 -- whatever type is wider for intermediate values when calculating
2525 -- length. Note that no matter what the index type is, the maximum
2526 -- length to which a vector is allowed to grow is always the minimum
2527 -- of Count_Type'Last and (IT'Last - IT'First + 1).
2529 -- For example, an Index_Type with range -127 .. 127 is only guaranteed
2530 -- to have a base range of -128 .. 127, but the corresponding vector
2531 -- would have lengths in the range 0 .. 255. In this case we would need
2532 -- to use Count_Type'Base for intermediate values.
2534 -- Another case would be the index range -2**63 + 1 .. -2**63 + 10. The
2535 -- vector would have a maximum length of 10, but the index values lie
2536 -- outside the range of Count_Type (which is only 32 bits). In this
2537 -- case we would need to use Index_Type'Base for intermediate values.
2539 if Count_Type'Base'Last >= Index_Type'Pos (Index_Type'Base'Last) then
2540 return Count_Type'Base (L) - Count_Type'Base (F) + 1;
2541 else
2542 return Count_Type (L - F + 1);
2543 end if;
2544 end Length;
2546 ----------
2547 -- Move --
2548 ----------
2550 procedure Move
2551 (Target : in out Vector;
2552 Source : in out Vector)
2554 begin
2555 if Target'Address = Source'Address then
2556 return;
2557 end if;
2559 if Target.Busy > 0 then
2560 raise Program_Error with
2561 "attempt to tamper with cursors (Target is busy)";
2562 end if;
2564 if Source.Busy > 0 then
2565 raise Program_Error with
2566 "attempt to tamper with cursors (Source is busy)";
2567 end if;
2569 declare
2570 Target_Elements : constant Elements_Access := Target.Elements;
2571 begin
2572 Target.Elements := Source.Elements;
2573 Source.Elements := Target_Elements;
2574 end;
2576 Target.Last := Source.Last;
2577 Source.Last := No_Index;
2578 end Move;
2580 ----------
2581 -- Next --
2582 ----------
2584 function Next (Position : Cursor) return Cursor is
2585 begin
2586 if Position.Container = null then
2587 return No_Element;
2588 elsif Position.Index < Position.Container.Last then
2589 return (Position.Container, Position.Index + 1);
2590 else
2591 return No_Element;
2592 end if;
2593 end Next;
2595 function Next (Object : Iterator; Position : Cursor) return Cursor is
2596 begin
2597 if Position.Container = null then
2598 return No_Element;
2599 elsif Position.Container /= Object.Container then
2600 raise Program_Error with
2601 "Position cursor of Next designates wrong vector";
2602 else
2603 return Next (Position);
2604 end if;
2605 end Next;
2607 procedure Next (Position : in out Cursor) is
2608 begin
2609 if Position.Container = null then
2610 return;
2611 elsif Position.Index < Position.Container.Last then
2612 Position.Index := Position.Index + 1;
2613 else
2614 Position := No_Element;
2615 end if;
2616 end Next;
2618 -------------
2619 -- Prepend --
2620 -------------
2622 procedure Prepend (Container : in out Vector; New_Item : Vector) is
2623 begin
2624 Insert (Container, Index_Type'First, New_Item);
2625 end Prepend;
2627 procedure Prepend
2628 (Container : in out Vector;
2629 New_Item : Element_Type;
2630 Count : Count_Type := 1)
2632 begin
2633 Insert (Container, Index_Type'First, New_Item, Count);
2634 end Prepend;
2636 --------------
2637 -- Previous --
2638 --------------
2640 function Previous (Position : Cursor) return Cursor is
2641 begin
2642 if Position.Container = null then
2643 return No_Element;
2644 elsif Position.Index > Index_Type'First then
2645 return (Position.Container, Position.Index - 1);
2646 else
2647 return No_Element;
2648 end if;
2649 end Previous;
2651 function Previous (Object : Iterator; Position : Cursor) return Cursor is
2652 begin
2653 if Position.Container = null then
2654 return No_Element;
2655 elsif Position.Container /= Object.Container then
2656 raise Program_Error with
2657 "Position cursor of Previous designates wrong vector";
2658 else
2659 return Previous (Position);
2660 end if;
2661 end Previous;
2663 procedure Previous (Position : in out Cursor) is
2664 begin
2665 if Position.Container = null then
2666 return;
2667 elsif Position.Index > Index_Type'First then
2668 Position.Index := Position.Index - 1;
2669 else
2670 Position := No_Element;
2671 end if;
2672 end Previous;
2674 -------------------
2675 -- Query_Element --
2676 -------------------
2678 procedure Query_Element
2679 (Container : Vector;
2680 Index : Index_Type;
2681 Process : not null access procedure (Element : Element_Type))
2683 V : Vector renames Container'Unrestricted_Access.all;
2684 B : Natural renames V.Busy;
2685 L : Natural renames V.Lock;
2687 begin
2688 if Index > Container.Last then
2689 raise Constraint_Error with "Index is out of range";
2690 end if;
2692 B := B + 1;
2693 L := L + 1;
2695 begin
2696 Process (V.Elements.EA (Index));
2697 exception
2698 when others =>
2699 L := L - 1;
2700 B := B - 1;
2701 raise;
2702 end;
2704 L := L - 1;
2705 B := B - 1;
2706 end Query_Element;
2708 procedure Query_Element
2709 (Position : Cursor;
2710 Process : not null access procedure (Element : Element_Type))
2712 begin
2713 if Position.Container = null then
2714 raise Constraint_Error with "Position cursor has no element";
2715 else
2716 Query_Element (Position.Container.all, Position.Index, Process);
2717 end if;
2718 end Query_Element;
2720 ----------
2721 -- Read --
2722 ----------
2724 procedure Read
2725 (Stream : not null access Root_Stream_Type'Class;
2726 Container : out Vector)
2728 Length : Count_Type'Base;
2729 Last : Index_Type'Base := No_Index;
2731 begin
2732 Clear (Container);
2734 Count_Type'Base'Read (Stream, Length);
2736 if Length > Capacity (Container) then
2737 Reserve_Capacity (Container, Capacity => Length);
2738 end if;
2740 for J in Count_Type range 1 .. Length loop
2741 Last := Last + 1;
2742 Element_Type'Read (Stream, Container.Elements.EA (Last));
2743 Container.Last := Last;
2744 end loop;
2745 end Read;
2747 procedure Read
2748 (Stream : not null access Root_Stream_Type'Class;
2749 Position : out Cursor)
2751 begin
2752 raise Program_Error with "attempt to stream vector cursor";
2753 end Read;
2755 procedure Read
2756 (Stream : not null access Root_Stream_Type'Class;
2757 Item : out Reference_Type)
2759 begin
2760 raise Program_Error with "attempt to stream reference";
2761 end Read;
2763 procedure Read
2764 (Stream : not null access Root_Stream_Type'Class;
2765 Item : out Constant_Reference_Type)
2767 begin
2768 raise Program_Error with "attempt to stream reference";
2769 end Read;
2771 ---------------
2772 -- Reference --
2773 ---------------
2775 function Reference
2776 (Container : aliased in out Vector;
2777 Position : Cursor) return Reference_Type
2779 begin
2780 if Position.Container = null then
2781 raise Constraint_Error with "Position cursor has no element";
2782 end if;
2784 if Position.Container /= Container'Unrestricted_Access then
2785 raise Program_Error with "Position cursor denotes wrong container";
2786 end if;
2788 if Position.Index > Position.Container.Last then
2789 raise Constraint_Error with "Position cursor is out of range";
2790 end if;
2792 declare
2793 C : Vector renames Position.Container.all;
2794 B : Natural renames C.Busy;
2795 L : Natural renames C.Lock;
2796 begin
2797 return R : constant Reference_Type :=
2798 (Element => Container.Elements.EA (Position.Index)'Access,
2799 Control => (Controlled with Position.Container))
2801 B := B + 1;
2802 L := L + 1;
2803 end return;
2804 end;
2805 end Reference;
2807 function Reference
2808 (Container : aliased in out Vector;
2809 Index : Index_Type) return Reference_Type
2811 begin
2812 if Index > Container.Last then
2813 raise Constraint_Error with "Index is out of range";
2815 else
2816 declare
2817 C : Vector renames Container'Unrestricted_Access.all;
2818 B : Natural renames C.Busy;
2819 L : Natural renames C.Lock;
2820 begin
2821 return R : constant Reference_Type :=
2822 (Element => Container.Elements.EA (Index)'Access,
2823 Control => (Controlled with Container'Unrestricted_Access))
2825 B := B + 1;
2826 L := L + 1;
2827 end return;
2828 end;
2829 end if;
2830 end Reference;
2832 ---------------------
2833 -- Replace_Element --
2834 ---------------------
2836 procedure Replace_Element
2837 (Container : in out Vector;
2838 Index : Index_Type;
2839 New_Item : Element_Type)
2841 begin
2842 if Index > Container.Last then
2843 raise Constraint_Error with "Index is out of range";
2844 elsif Container.Lock > 0 then
2845 raise Program_Error with
2846 "attempt to tamper with elements (vector is locked)";
2847 else
2848 Container.Elements.EA (Index) := New_Item;
2849 end if;
2850 end Replace_Element;
2852 procedure Replace_Element
2853 (Container : in out Vector;
2854 Position : Cursor;
2855 New_Item : Element_Type)
2857 begin
2858 if Position.Container = null then
2859 raise Constraint_Error with "Position cursor has no element";
2861 elsif Position.Container /= Container'Unrestricted_Access then
2862 raise Program_Error with "Position cursor denotes wrong container";
2864 elsif Position.Index > Container.Last then
2865 raise Constraint_Error with "Position cursor is out of range";
2867 else
2868 if Container.Lock > 0 then
2869 raise Program_Error with
2870 "attempt to tamper with elements (vector is locked)";
2871 end if;
2873 Container.Elements.EA (Position.Index) := New_Item;
2874 end if;
2875 end Replace_Element;
2877 ----------------------
2878 -- Reserve_Capacity --
2879 ----------------------
2881 procedure Reserve_Capacity
2882 (Container : in out Vector;
2883 Capacity : Count_Type)
2885 N : constant Count_Type := Length (Container);
2887 Index : Count_Type'Base;
2888 Last : Index_Type'Base;
2890 begin
2891 -- Reserve_Capacity can be used to either expand the storage available
2892 -- for elements (this would be its typical use, in anticipation of
2893 -- future insertion), or to trim back storage. In the latter case,
2894 -- storage can only be trimmed back to the limit of the container
2895 -- length. Note that Reserve_Capacity neither deletes (active) elements
2896 -- nor inserts elements; it only affects container capacity, never
2897 -- container length.
2899 if Capacity = 0 then
2901 -- This is a request to trim back storage, to the minimum amount
2902 -- possible given the current state of the container.
2904 if N = 0 then
2906 -- The container is empty, so in this unique case we can
2907 -- deallocate the entire internal array. Note that an empty
2908 -- container can never be busy, so there's no need to check the
2909 -- tampering bits.
2911 declare
2912 X : Elements_Access := Container.Elements;
2914 begin
2915 -- First we remove the internal array from the container, to
2916 -- handle the case when the deallocation raises an exception.
2918 Container.Elements := null;
2920 -- Container invariants have been restored, so it is now safe
2921 -- to attempt to deallocate the internal array.
2923 Free (X);
2924 end;
2926 elsif N < Container.Elements.EA'Length then
2928 -- The container is not empty, and the current length is less than
2929 -- the current capacity, so there's storage available to trim. In
2930 -- this case, we allocate a new internal array having a length
2931 -- that exactly matches the number of items in the
2932 -- container. (Reserve_Capacity does not delete active elements,
2933 -- so this is the best we can do with respect to minimizing
2934 -- storage).
2936 if Container.Busy > 0 then
2937 raise Program_Error with
2938 "attempt to tamper with cursors (vector is busy)";
2939 end if;
2941 declare
2942 subtype Src_Index_Subtype is Index_Type'Base range
2943 Index_Type'First .. Container.Last;
2945 Src : Elements_Array renames
2946 Container.Elements.EA (Src_Index_Subtype);
2948 X : Elements_Access := Container.Elements;
2950 begin
2951 -- Although we have isolated the old internal array that we're
2952 -- going to deallocate, we don't deallocate it until we have
2953 -- successfully allocated a new one. If there is an exception
2954 -- during allocation (either because there is not enough
2955 -- storage, or because initialization of the elements fails),
2956 -- we let it propagate without causing any side-effect.
2958 Container.Elements := new Elements_Type'(Container.Last, Src);
2960 -- We have successfully allocated a new internal array (with a
2961 -- smaller length than the old one, and containing a copy of
2962 -- just the active elements in the container), so it is now
2963 -- safe to attempt to deallocate the old array. The old array
2964 -- has been isolated, and container invariants have been
2965 -- restored, so if the deallocation fails (because finalization
2966 -- of the elements fails), we simply let it propagate.
2968 Free (X);
2969 end;
2970 end if;
2972 return;
2973 end if;
2975 -- Reserve_Capacity can be used to expand the storage available for
2976 -- elements, but we do not let the capacity grow beyond the number of
2977 -- values in Index_Type'Range. (Were it otherwise, there would be no way
2978 -- to refer to the elements with an index value greater than
2979 -- Index_Type'Last, so that storage would be wasted.) Here we compute
2980 -- the Last index value of the new internal array, in a way that avoids
2981 -- any possibility of overflow.
2983 if Index_Type'Base'Last >= Count_Type'Pos (Count_Type'Last) then
2985 -- We perform a two-part test. First we determine whether the
2986 -- computed Last value lies in the base range of the type, and then
2987 -- determine whether it lies in the range of the index (sub)type.
2989 -- Last must satisfy this relation:
2990 -- First + Length - 1 <= Last
2991 -- We regroup terms:
2992 -- First - 1 <= Last - Length
2993 -- Which can rewrite as:
2994 -- No_Index <= Last - Length
2996 if Index_Type'Base'Last - Index_Type'Base (Capacity) < No_Index then
2997 raise Constraint_Error with "Capacity is out of range";
2998 end if;
3000 -- We now know that the computed value of Last is within the base
3001 -- range of the type, so it is safe to compute its value:
3003 Last := No_Index + Index_Type'Base (Capacity);
3005 -- Finally we test whether the value is within the range of the
3006 -- generic actual index subtype:
3008 if Last > Index_Type'Last then
3009 raise Constraint_Error with "Capacity is out of range";
3010 end if;
3012 elsif Index_Type'First <= 0 then
3014 -- Here we can compute Last directly, in the normal way. We know that
3015 -- No_Index is less than 0, so there is no danger of overflow when
3016 -- adding the (positive) value of Capacity.
3018 Index := Count_Type'Base (No_Index) + Capacity; -- Last
3020 if Index > Count_Type'Base (Index_Type'Last) then
3021 raise Constraint_Error with "Capacity is out of range";
3022 end if;
3024 -- We know that the computed value (having type Count_Type) of Last
3025 -- is within the range of the generic actual index subtype, so it is
3026 -- safe to convert to Index_Type:
3028 Last := Index_Type'Base (Index);
3030 else
3031 -- Here Index_Type'First (and Index_Type'Last) is positive, so we
3032 -- must test the length indirectly (by working backwards from the
3033 -- largest possible value of Last), in order to prevent overflow.
3035 Index := Count_Type'Base (Index_Type'Last) - Capacity; -- No_Index
3037 if Index < Count_Type'Base (No_Index) then
3038 raise Constraint_Error with "Capacity is out of range";
3039 end if;
3041 -- We have determined that the value of Capacity would not create a
3042 -- Last index value outside of the range of Index_Type, so we can now
3043 -- safely compute its value.
3045 Last := Index_Type'Base (Count_Type'Base (No_Index) + Capacity);
3046 end if;
3048 -- The requested capacity is non-zero, but we don't know yet whether
3049 -- this is a request for expansion or contraction of storage.
3051 if Container.Elements = null then
3053 -- The container is empty (it doesn't even have an internal array),
3054 -- so this represents a request to allocate (expand) storage having
3055 -- the given capacity.
3057 Container.Elements := new Elements_Type (Last);
3058 return;
3059 end if;
3061 if Capacity <= N then
3063 -- This is a request to trim back storage, but only to the limit of
3064 -- what's already in the container. (Reserve_Capacity never deletes
3065 -- active elements, it only reclaims excess storage.)
3067 if N < Container.Elements.EA'Length then
3069 -- The container is not empty (because the requested capacity is
3070 -- positive, and less than or equal to the container length), and
3071 -- the current length is less than the current capacity, so
3072 -- there's storage available to trim. In this case, we allocate a
3073 -- new internal array having a length that exactly matches the
3074 -- number of items in the container.
3076 if Container.Busy > 0 then
3077 raise Program_Error with
3078 "attempt to tamper with cursors (vector is busy)";
3079 end if;
3081 declare
3082 subtype Src_Index_Subtype is Index_Type'Base range
3083 Index_Type'First .. Container.Last;
3085 Src : Elements_Array renames
3086 Container.Elements.EA (Src_Index_Subtype);
3088 X : Elements_Access := Container.Elements;
3090 begin
3091 -- Although we have isolated the old internal array that we're
3092 -- going to deallocate, we don't deallocate it until we have
3093 -- successfully allocated a new one. If there is an exception
3094 -- during allocation (either because there is not enough
3095 -- storage, or because initialization of the elements fails),
3096 -- we let it propagate without causing any side-effect.
3098 Container.Elements := new Elements_Type'(Container.Last, Src);
3100 -- We have successfully allocated a new internal array (with a
3101 -- smaller length than the old one, and containing a copy of
3102 -- just the active elements in the container), so it is now
3103 -- safe to attempt to deallocate the old array. The old array
3104 -- has been isolated, and container invariants have been
3105 -- restored, so if the deallocation fails (because finalization
3106 -- of the elements fails), we simply let it propagate.
3108 Free (X);
3109 end;
3110 end if;
3112 return;
3113 end if;
3115 -- The requested capacity is larger than the container length (the
3116 -- number of active elements). Whether this represents a request for
3117 -- expansion or contraction of the current capacity depends on what the
3118 -- current capacity is.
3120 if Capacity = Container.Elements.EA'Length then
3122 -- The requested capacity matches the existing capacity, so there's
3123 -- nothing to do here. We treat this case as a no-op, and simply
3124 -- return without checking the busy bit.
3126 return;
3127 end if;
3129 -- There is a change in the capacity of a non-empty container, so a new
3130 -- internal array will be allocated. (The length of the new internal
3131 -- array could be less or greater than the old internal array. We know
3132 -- only that the length of the new internal array is greater than the
3133 -- number of active elements in the container.) We must check whether
3134 -- the container is busy before doing anything else.
3136 if Container.Busy > 0 then
3137 raise Program_Error with
3138 "attempt to tamper with cursors (vector is busy)";
3139 end if;
3141 -- We now allocate a new internal array, having a length different from
3142 -- its current value.
3144 declare
3145 E : Elements_Access := new Elements_Type (Last);
3147 begin
3148 -- We have successfully allocated the new internal array. We first
3149 -- attempt to copy the existing elements from the old internal array
3150 -- ("src" elements) onto the new internal array ("tgt" elements).
3152 declare
3153 subtype Index_Subtype is Index_Type'Base range
3154 Index_Type'First .. Container.Last;
3156 Src : Elements_Array renames
3157 Container.Elements.EA (Index_Subtype);
3159 Tgt : Elements_Array renames E.EA (Index_Subtype);
3161 begin
3162 Tgt := Src;
3164 exception
3165 when others =>
3166 Free (E);
3167 raise;
3168 end;
3170 -- We have successfully copied the existing elements onto the new
3171 -- internal array, so now we can attempt to deallocate the old one.
3173 declare
3174 X : Elements_Access := Container.Elements;
3176 begin
3177 -- First we isolate the old internal array, and replace it in the
3178 -- container with the new internal array.
3180 Container.Elements := E;
3182 -- Container invariants have been restored, so it is now safe to
3183 -- attempt to deallocate the old internal array.
3185 Free (X);
3186 end;
3187 end;
3188 end Reserve_Capacity;
3190 ----------------------
3191 -- Reverse_Elements --
3192 ----------------------
3194 procedure Reverse_Elements (Container : in out Vector) is
3195 begin
3196 if Container.Length <= 1 then
3197 return;
3198 end if;
3200 -- The exception behavior for the vector container must match that for
3201 -- the list container, so we check for cursor tampering here (which will
3202 -- catch more things) instead of for element tampering (which will catch
3203 -- fewer things). It's true that the elements of this vector container
3204 -- could be safely moved around while (say) an iteration is taking place
3205 -- (iteration only increments the busy counter), and so technically
3206 -- all we would need here is a test for element tampering (indicated
3207 -- by the lock counter), that's simply an artifact of our array-based
3208 -- implementation. Logically Reverse_Elements requires a check for
3209 -- cursor tampering.
3211 if Container.Busy > 0 then
3212 raise Program_Error with
3213 "attempt to tamper with cursors (vector is busy)";
3214 end if;
3216 declare
3217 K : Index_Type;
3218 J : Index_Type;
3219 E : Elements_Type renames Container.Elements.all;
3221 begin
3222 K := Index_Type'First;
3223 J := Container.Last;
3224 while K < J loop
3225 declare
3226 EK : constant Element_Type := E.EA (K);
3227 begin
3228 E.EA (K) := E.EA (J);
3229 E.EA (J) := EK;
3230 end;
3232 K := K + 1;
3233 J := J - 1;
3234 end loop;
3235 end;
3236 end Reverse_Elements;
3238 ------------------
3239 -- Reverse_Find --
3240 ------------------
3242 function Reverse_Find
3243 (Container : Vector;
3244 Item : Element_Type;
3245 Position : Cursor := No_Element) return Cursor
3247 Last : Index_Type'Base;
3249 begin
3250 if Position.Container /= null
3251 and then Position.Container /= Container'Unrestricted_Access
3252 then
3253 raise Program_Error with "Position cursor denotes wrong container";
3254 end if;
3256 Last :=
3257 (if Position.Container = null or else Position.Index > Container.Last
3258 then Container.Last
3259 else Position.Index);
3261 -- Per AI05-0022, the container implementation is required to detect
3262 -- element tampering by a generic actual subprogram.
3264 declare
3265 B : Natural renames Container'Unrestricted_Access.Busy;
3266 L : Natural renames Container'Unrestricted_Access.Lock;
3268 Result : Index_Type'Base;
3270 begin
3271 B := B + 1;
3272 L := L + 1;
3274 Result := No_Index;
3275 for Indx in reverse Index_Type'First .. Last loop
3276 if Container.Elements.EA (Indx) = Item then
3277 Result := Indx;
3278 exit;
3279 end if;
3280 end loop;
3282 B := B - 1;
3283 L := L - 1;
3285 if Result = No_Index then
3286 return No_Element;
3287 else
3288 return Cursor'(Container'Unrestricted_Access, Result);
3289 end if;
3291 exception
3292 when others =>
3293 B := B - 1;
3294 L := L - 1;
3296 raise;
3297 end;
3298 end Reverse_Find;
3300 ------------------------
3301 -- Reverse_Find_Index --
3302 ------------------------
3304 function Reverse_Find_Index
3305 (Container : Vector;
3306 Item : Element_Type;
3307 Index : Index_Type := Index_Type'Last) return Extended_Index
3309 B : Natural renames Container'Unrestricted_Access.Busy;
3310 L : Natural renames Container'Unrestricted_Access.Lock;
3312 Last : constant Index_Type'Base :=
3313 Index_Type'Min (Container.Last, Index);
3315 Result : Index_Type'Base;
3317 begin
3318 -- Per AI05-0022, the container implementation is required to detect
3319 -- element tampering by a generic actual subprogram.
3321 B := B + 1;
3322 L := L + 1;
3324 Result := No_Index;
3325 for Indx in reverse Index_Type'First .. Last loop
3326 if Container.Elements.EA (Indx) = Item then
3327 Result := Indx;
3328 exit;
3329 end if;
3330 end loop;
3332 B := B - 1;
3333 L := L - 1;
3335 return Result;
3337 exception
3338 when others =>
3339 B := B - 1;
3340 L := L - 1;
3342 raise;
3343 end Reverse_Find_Index;
3345 ---------------------
3346 -- Reverse_Iterate --
3347 ---------------------
3349 procedure Reverse_Iterate
3350 (Container : Vector;
3351 Process : not null access procedure (Position : Cursor))
3353 V : Vector renames Container'Unrestricted_Access.all;
3354 B : Natural renames V.Busy;
3356 begin
3357 B := B + 1;
3359 begin
3360 for Indx in reverse Index_Type'First .. Container.Last loop
3361 Process (Cursor'(Container'Unrestricted_Access, Indx));
3362 end loop;
3363 exception
3364 when others =>
3365 B := B - 1;
3366 raise;
3367 end;
3369 B := B - 1;
3370 end Reverse_Iterate;
3372 ----------------
3373 -- Set_Length --
3374 ----------------
3376 procedure Set_Length (Container : in out Vector; Length : Count_Type) is
3377 Count : constant Count_Type'Base := Container.Length - Length;
3379 begin
3380 -- Set_Length allows the user to set the length explicitly, instead
3381 -- of implicitly as a side-effect of deletion or insertion. If the
3382 -- requested length is less than the current length, this is equivalent
3383 -- to deleting items from the back end of the vector. If the requested
3384 -- length is greater than the current length, then this is equivalent
3385 -- to inserting "space" (nonce items) at the end.
3387 if Count >= 0 then
3388 Container.Delete_Last (Count);
3390 elsif Container.Last >= Index_Type'Last then
3391 raise Constraint_Error with "vector is already at its maximum length";
3393 else
3394 Container.Insert_Space (Container.Last + 1, -Count);
3395 end if;
3396 end Set_Length;
3398 ----------
3399 -- Swap --
3400 ----------
3402 procedure Swap (Container : in out Vector; I, J : Index_Type) is
3403 begin
3404 if I > Container.Last then
3405 raise Constraint_Error with "I index is out of range";
3406 end if;
3408 if J > Container.Last then
3409 raise Constraint_Error with "J index is out of range";
3410 end if;
3412 if I = J then
3413 return;
3414 end if;
3416 if Container.Lock > 0 then
3417 raise Program_Error with
3418 "attempt to tamper with elements (vector is locked)";
3419 end if;
3421 declare
3422 EI_Copy : constant Element_Type := Container.Elements.EA (I);
3423 begin
3424 Container.Elements.EA (I) := Container.Elements.EA (J);
3425 Container.Elements.EA (J) := EI_Copy;
3426 end;
3427 end Swap;
3429 procedure Swap (Container : in out Vector; I, J : Cursor) is
3430 begin
3431 if I.Container = null then
3432 raise Constraint_Error with "I cursor has no element";
3434 elsif J.Container = null then
3435 raise Constraint_Error with "J cursor has no element";
3437 elsif I.Container /= Container'Unrestricted_Access then
3438 raise Program_Error with "I cursor denotes wrong container";
3440 elsif J.Container /= Container'Unrestricted_Access then
3441 raise Program_Error with "J cursor denotes wrong container";
3443 else
3444 Swap (Container, I.Index, J.Index);
3445 end if;
3446 end Swap;
3448 ---------------
3449 -- To_Cursor --
3450 ---------------
3452 function To_Cursor
3453 (Container : Vector;
3454 Index : Extended_Index) return Cursor
3456 begin
3457 if Index not in Index_Type'First .. Container.Last then
3458 return No_Element;
3459 else
3460 return (Container'Unrestricted_Access, Index);
3461 end if;
3462 end To_Cursor;
3464 --------------
3465 -- To_Index --
3466 --------------
3468 function To_Index (Position : Cursor) return Extended_Index is
3469 begin
3470 if Position.Container = null then
3471 return No_Index;
3472 elsif Position.Index <= Position.Container.Last then
3473 return Position.Index;
3474 else
3475 return No_Index;
3476 end if;
3477 end To_Index;
3479 ---------------
3480 -- To_Vector --
3481 ---------------
3483 function To_Vector (Length : Count_Type) return Vector is
3484 Index : Count_Type'Base;
3485 Last : Index_Type'Base;
3486 Elements : Elements_Access;
3488 begin
3489 if Length = 0 then
3490 return Empty_Vector;
3491 end if;
3493 -- We create a vector object with a capacity that matches the specified
3494 -- Length, but we do not allow the vector capacity (the length of the
3495 -- internal array) to exceed the number of values in Index_Type'Range
3496 -- (otherwise, there would be no way to refer to those components via an
3497 -- index). We must therefore check whether the specified Length would
3498 -- create a Last index value greater than Index_Type'Last.
3500 if Index_Type'Base'Last >= Count_Type'Pos (Count_Type'Last) then
3502 -- We perform a two-part test. First we determine whether the
3503 -- computed Last value lies in the base range of the type, and then
3504 -- determine whether it lies in the range of the index (sub)type.
3506 -- Last must satisfy this relation:
3507 -- First + Length - 1 <= Last
3508 -- We regroup terms:
3509 -- First - 1 <= Last - Length
3510 -- Which can rewrite as:
3511 -- No_Index <= Last - Length
3513 if Index_Type'Base'Last - Index_Type'Base (Length) < No_Index then
3514 raise Constraint_Error with "Length is out of range";
3515 end if;
3517 -- We now know that the computed value of Last is within the base
3518 -- range of the type, so it is safe to compute its value:
3520 Last := No_Index + Index_Type'Base (Length);
3522 -- Finally we test whether the value is within the range of the
3523 -- generic actual index subtype:
3525 if Last > Index_Type'Last then
3526 raise Constraint_Error with "Length is out of range";
3527 end if;
3529 elsif Index_Type'First <= 0 then
3531 -- Here we can compute Last directly, in the normal way. We know that
3532 -- No_Index is less than 0, so there is no danger of overflow when
3533 -- adding the (positive) value of Length.
3535 Index := Count_Type'Base (No_Index) + Length; -- Last
3537 if Index > Count_Type'Base (Index_Type'Last) then
3538 raise Constraint_Error with "Length is out of range";
3539 end if;
3541 -- We know that the computed value (having type Count_Type) of Last
3542 -- is within the range of the generic actual index subtype, so it is
3543 -- safe to convert to Index_Type:
3545 Last := Index_Type'Base (Index);
3547 else
3548 -- Here Index_Type'First (and Index_Type'Last) is positive, so we
3549 -- must test the length indirectly (by working backwards from the
3550 -- largest possible value of Last), in order to prevent overflow.
3552 Index := Count_Type'Base (Index_Type'Last) - Length; -- No_Index
3554 if Index < Count_Type'Base (No_Index) then
3555 raise Constraint_Error with "Length is out of range";
3556 end if;
3558 -- We have determined that the value of Length would not create a
3559 -- Last index value outside of the range of Index_Type, so we can now
3560 -- safely compute its value.
3562 Last := Index_Type'Base (Count_Type'Base (No_Index) + Length);
3563 end if;
3565 Elements := new Elements_Type (Last);
3567 return Vector'(Controlled with Elements, Last, 0, 0);
3568 end To_Vector;
3570 function To_Vector
3571 (New_Item : Element_Type;
3572 Length : Count_Type) return Vector
3574 Index : Count_Type'Base;
3575 Last : Index_Type'Base;
3576 Elements : Elements_Access;
3578 begin
3579 if Length = 0 then
3580 return Empty_Vector;
3581 end if;
3583 -- We create a vector object with a capacity that matches the specified
3584 -- Length, but we do not allow the vector capacity (the length of the
3585 -- internal array) to exceed the number of values in Index_Type'Range
3586 -- (otherwise, there would be no way to refer to those components via an
3587 -- index). We must therefore check whether the specified Length would
3588 -- create a Last index value greater than Index_Type'Last.
3590 if Index_Type'Base'Last >= Count_Type'Pos (Count_Type'Last) then
3592 -- We perform a two-part test. First we determine whether the
3593 -- computed Last value lies in the base range of the type, and then
3594 -- determine whether it lies in the range of the index (sub)type.
3596 -- Last must satisfy this relation:
3597 -- First + Length - 1 <= Last
3598 -- We regroup terms:
3599 -- First - 1 <= Last - Length
3600 -- Which can rewrite as:
3601 -- No_Index <= Last - Length
3603 if Index_Type'Base'Last - Index_Type'Base (Length) < No_Index then
3604 raise Constraint_Error with "Length is out of range";
3605 end if;
3607 -- We now know that the computed value of Last is within the base
3608 -- range of the type, so it is safe to compute its value:
3610 Last := No_Index + Index_Type'Base (Length);
3612 -- Finally we test whether the value is within the range of the
3613 -- generic actual index subtype:
3615 if Last > Index_Type'Last then
3616 raise Constraint_Error with "Length is out of range";
3617 end if;
3619 elsif Index_Type'First <= 0 then
3621 -- Here we can compute Last directly, in the normal way. We know that
3622 -- No_Index is less than 0, so there is no danger of overflow when
3623 -- adding the (positive) value of Length.
3625 Index := Count_Type'Base (No_Index) + Length; -- same value as V.Last
3627 if Index > Count_Type'Base (Index_Type'Last) then
3628 raise Constraint_Error with "Length is out of range";
3629 end if;
3631 -- We know that the computed value (having type Count_Type) of Last
3632 -- is within the range of the generic actual index subtype, so it is
3633 -- safe to convert to Index_Type:
3635 Last := Index_Type'Base (Index);
3637 else
3638 -- Here Index_Type'First (and Index_Type'Last) is positive, so we
3639 -- must test the length indirectly (by working backwards from the
3640 -- largest possible value of Last), in order to prevent overflow.
3642 Index := Count_Type'Base (Index_Type'Last) - Length; -- No_Index
3644 if Index < Count_Type'Base (No_Index) then
3645 raise Constraint_Error with "Length is out of range";
3646 end if;
3648 -- We have determined that the value of Length would not create a
3649 -- Last index value outside of the range of Index_Type, so we can now
3650 -- safely compute its value.
3652 Last := Index_Type'Base (Count_Type'Base (No_Index) + Length);
3653 end if;
3655 Elements := new Elements_Type'(Last, EA => (others => New_Item));
3657 return Vector'(Controlled with Elements, Last, 0, 0);
3658 end To_Vector;
3660 --------------------
3661 -- Update_Element --
3662 --------------------
3664 procedure Update_Element
3665 (Container : in out Vector;
3666 Index : Index_Type;
3667 Process : not null access procedure (Element : in out Element_Type))
3669 B : Natural renames Container.Busy;
3670 L : Natural renames Container.Lock;
3672 begin
3673 if Index > Container.Last then
3674 raise Constraint_Error with "Index is out of range";
3675 end if;
3677 B := B + 1;
3678 L := L + 1;
3680 begin
3681 Process (Container.Elements.EA (Index));
3682 exception
3683 when others =>
3684 L := L - 1;
3685 B := B - 1;
3686 raise;
3687 end;
3689 L := L - 1;
3690 B := B - 1;
3691 end Update_Element;
3693 procedure Update_Element
3694 (Container : in out Vector;
3695 Position : Cursor;
3696 Process : not null access procedure (Element : in out Element_Type))
3698 begin
3699 if Position.Container = null then
3700 raise Constraint_Error with "Position cursor has no element";
3701 elsif Position.Container /= Container'Unrestricted_Access then
3702 raise Program_Error with "Position cursor denotes wrong container";
3703 else
3704 Update_Element (Container, Position.Index, Process);
3705 end if;
3706 end Update_Element;
3708 -----------
3709 -- Write --
3710 -----------
3712 procedure Write
3713 (Stream : not null access Root_Stream_Type'Class;
3714 Container : Vector)
3716 begin
3717 Count_Type'Base'Write (Stream, Length (Container));
3719 for J in Index_Type'First .. Container.Last loop
3720 Element_Type'Write (Stream, Container.Elements.EA (J));
3721 end loop;
3722 end Write;
3724 procedure Write
3725 (Stream : not null access Root_Stream_Type'Class;
3726 Position : Cursor)
3728 begin
3729 raise Program_Error with "attempt to stream vector cursor";
3730 end Write;
3732 procedure Write
3733 (Stream : not null access Root_Stream_Type'Class;
3734 Item : Reference_Type)
3736 begin
3737 raise Program_Error with "attempt to stream reference";
3738 end Write;
3740 procedure Write
3741 (Stream : not null access Root_Stream_Type'Class;
3742 Item : Constant_Reference_Type)
3744 begin
3745 raise Program_Error with "attempt to stream reference";
3746 end Write;
3748 end Ada.Containers.Vectors;