@@ -162,15 +162,75 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
162162 self .items [n ] = item ;
163163 }
164164
165+ /// Add `count` new elements at position `index`, which have
166+ /// `undefined` values. Returns a slice pointing to the newly allocated
167+ /// elements, which becomes invalid after various `ArrayList`
168+ /// operations.
169+ /// Invalidates pre-existing pointers to elements at and after `index`.
170+ /// Invalidates all pre-existing element pointers if capacity must be
171+ /// increased to accomodate the new elements.
172+ pub fn addManyAt (self : * Self , index : usize , count : usize ) Allocator.Error ! []T {
173+ const new_len = self .items .len + count ;
174+
175+ if (self .capacity >= new_len )
176+ return addManyAtAssumeCapacity (self , index , count );
177+
178+ // Here we avoid copying allocated but unused bytes by
179+ // attempting a resize in place, and falling back to allocating
180+ // a new buffer and doing our own copy. With a realloc() call,
181+ // the allocator implementation would pointlessly copy our
182+ // extra capacity.
183+ const new_capacity = growCapacity (self .capacity , new_len );
184+ const old_memory = self .allocatedSlice ();
185+ if (self .allocator .resize (old_memory , new_capacity )) {
186+ self .capacity = new_capacity ;
187+ return addManyAtAssumeCapacity (self , index , count );
188+ }
189+
190+ // Make a new allocation, avoiding `ensureTotalCapacity` in order
191+ // to avoid extra memory copies.
192+ const new_memory = try self .allocator .alignedAlloc (T , alignment , new_capacity );
193+ const to_move = self .items [index .. ];
194+ @memcpy (new_memory [0.. index ], self .items [0.. index ]);
195+ @memcpy (new_memory [index + count .. ][0.. to_move .len ], to_move );
196+ self .allocator .free (old_memory );
197+ self .items = new_memory [0.. new_len ];
198+ self .capacity = new_memory .len ;
199+ // The inserted elements at `new_memory[index..][0..count]` have
200+ // already been set to `undefined` by memory allocation.
201+ return new_memory [index .. ][0.. count ];
202+ }
203+
204+ /// Add `count` new elements at position `index`, which have
205+ /// `undefined` values. Returns a slice pointing to the newly allocated
206+ /// elements, which becomes invalid after various `ArrayList`
207+ /// operations.
208+ /// Asserts that there is enough capacity for the new elements.
209+ /// Invalidates pre-existing pointers to elements at and after `index`, but
210+ /// does not invalidate any before that.
211+ pub fn addManyAtAssumeCapacity (self : * Self , index : usize , count : usize ) []T {
212+ const new_len = self .items .len + count ;
213+ assert (self .capacity >= new_len );
214+ const to_move = self .items [index .. ];
215+ self .items .len = new_len ;
216+ mem .copyBackwards (T , self .items [index + count .. ], to_move );
217+ const result = self .items [index .. ][0.. count ];
218+ @memset (result , undefined );
219+ return result ;
220+ }
221+
165222 /// Insert slice `items` at index `i` by moving `list[i .. list.len]` to make room.
166223 /// This operation is O(N).
167- /// Invalidates pointers if additional memory is needed.
168- pub fn insertSlice (self : * Self , i : usize , items : []const T ) Allocator.Error ! void {
169- try self .ensureUnusedCapacity (items .len );
170- self .items .len += items .len ;
171-
172- mem .copyBackwards (T , self .items [i + items .len .. self .items .len ], self .items [i .. self .items .len - items .len ]);
173- @memcpy (self .items [i .. ][0.. items .len ], items );
224+ /// Invalidates pre-existing pointers to elements at and after `index`.
225+ /// Invalidates all pre-existing element pointers if capacity must be
226+ /// increased to accomodate the new elements.
227+ pub fn insertSlice (
228+ self : * Self ,
229+ index : usize ,
230+ items : []const T ,
231+ ) Allocator.Error ! void {
232+ const dst = try self .addManyAt (index , items .len );
233+ @memcpy (dst , items );
174234 }
175235
176236 /// Replace range of elements `list[start..][0..len]` with `new_items`.
@@ -370,12 +430,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
370430
371431 if (self .capacity >= new_capacity ) return ;
372432
373- var better_capacity = self .capacity ;
374- while (true ) {
375- better_capacity + |= better_capacity / 2 + 8 ;
376- if (better_capacity >= new_capacity ) break ;
377- }
378-
433+ const better_capacity = growCapacity (self .capacity , new_capacity );
379434 return self .ensureTotalCapacityPrecise (better_capacity );
380435 }
381436
@@ -663,26 +718,75 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
663718 self .items [n ] = item ;
664719 }
665720
666- /// Insert slice `items` at index `i`. Moves `list[i .. list.len]` to
667- /// higher indicices make room.
668- /// This operation is O(N).
669- /// Invalidates pointers if additional memory is needed.
670- pub fn insertSlice (self : * Self , allocator : Allocator , i : usize , items : []const T ) Allocator.Error ! void {
671- try self .ensureUnusedCapacity (allocator , items .len );
672- self .items .len += items .len ;
721+ /// Add `count` new elements at position `index`, which have
722+ /// `undefined` values. Returns a slice pointing to the newly allocated
723+ /// elements, which becomes invalid after various `ArrayList`
724+ /// operations.
725+ /// Invalidates pre-existing pointers to elements at and after `index`.
726+ /// Invalidates all pre-existing element pointers if capacity must be
727+ /// increased to accomodate the new elements.
728+ pub fn addManyAt (
729+ self : * Self ,
730+ allocator : Allocator ,
731+ index : usize ,
732+ count : usize ,
733+ ) Allocator.Error ! []T {
734+ var managed = self .toManaged (allocator );
735+ defer self .* = managed .moveToUnmanaged ();
736+ return managed .addManyAt (index , count );
737+ }
738+
739+ /// Add `count` new elements at position `index`, which have
740+ /// `undefined` values. Returns a slice pointing to the newly allocated
741+ /// elements, which becomes invalid after various `ArrayList`
742+ /// operations.
743+ /// Asserts that there is enough capacity for the new elements.
744+ /// Invalidates pre-existing pointers to elements at and after `index`, but
745+ /// does not invalidate any before that.
746+ pub fn addManyAtAssumeCapacity (self : * Self , index : usize , count : usize ) []T {
747+ const new_len = self .items .len + count ;
748+ assert (self .capacity >= new_len );
749+ const to_move = self .items [index .. ];
750+ self .items .len = new_len ;
751+ mem .copyBackwards (T , self .items [index + count .. ], to_move );
752+ const result = self .items [index .. ][0.. count ];
753+ @memset (result , undefined );
754+ return result ;
755+ }
673756
674- mem .copyBackwards (T , self .items [i + items .len .. self .items .len ], self .items [i .. self .items .len - items .len ]);
675- @memcpy (self .items [i .. ][0.. items .len ], items );
757+ /// Insert slice `items` at index `i` by moving `list[i .. list.len]` to make room.
758+ /// This operation is O(N).
759+ /// Invalidates pre-existing pointers to elements at and after `index`.
760+ /// Invalidates all pre-existing element pointers if capacity must be
761+ /// increased to accomodate the new elements.
762+ pub fn insertSlice (
763+ self : * Self ,
764+ allocator : Allocator ,
765+ index : usize ,
766+ items : []const T ,
767+ ) Allocator.Error ! void {
768+ const dst = try self .addManyAt (
769+ allocator ,
770+ index ,
771+ items .len ,
772+ );
773+ @memcpy (dst , items );
676774 }
677775
678776 /// Replace range of elements `list[start..][0..len]` with `new_items`
679777 /// Grows list if `len < new_items.len`.
680778 /// Shrinks list if `len > new_items.len`
681779 /// Invalidates pointers if this ArrayList is resized.
682- pub fn replaceRange (self : * Self , allocator : Allocator , start : usize , len : usize , new_items : []const T ) Allocator.Error ! void {
780+ pub fn replaceRange (
781+ self : * Self ,
782+ allocator : Allocator ,
783+ start : usize ,
784+ len : usize ,
785+ new_items : []const T ,
786+ ) Allocator.Error ! void {
683787 var managed = self .toManaged (allocator );
788+ defer self .* = managed .moveToUnmanaged ();
684789 try managed .replaceRange (start , len , new_items );
685- self .* = managed .moveToUnmanaged ();
686790 }
687791
688792 /// Extend the list by 1 element. Allocates more memory as necessary.
@@ -875,12 +979,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
875979 pub fn ensureTotalCapacity (self : * Self , allocator : Allocator , new_capacity : usize ) Allocator.Error ! void {
876980 if (self .capacity >= new_capacity ) return ;
877981
878- var better_capacity = self .capacity ;
879- while (true ) {
880- better_capacity + |= better_capacity / 2 + 8 ;
881- if (better_capacity >= new_capacity ) break ;
882- }
883-
982+ var better_capacity = growCapacity (self .capacity , new_capacity );
884983 return self .ensureTotalCapacityPrecise (allocator , better_capacity );
885984 }
886985
@@ -1039,6 +1138,17 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
10391138 };
10401139}
10411140
1141+ /// Called when memory growth is necessary. Returns a capacity larger than
1142+ /// minimum that grows super-linearly.
1143+ fn growCapacity (current : usize , minimum : usize ) usize {
1144+ var new = current ;
1145+ while (true ) {
1146+ new + |= new / 2 + 8 ;
1147+ if (new >= minimum )
1148+ return new ;
1149+ }
1150+ }
1151+
10421152test "std.ArrayList/ArrayListUnmanaged.init" {
10431153 {
10441154 var list = ArrayList (i32 ).init (testing .allocator );
@@ -1650,6 +1760,40 @@ test "std.ArrayList/ArrayListUnmanaged.addManyAsArray" {
16501760 }
16511761}
16521762
1763+ test "std.ArrayList/ArrayListUnmanaged growing memory preserves contents" {
1764+ const a = std .testing .allocator ;
1765+ {
1766+ var list = ArrayList (u8 ).init (a );
1767+ defer list .deinit ();
1768+ try list .ensureTotalCapacityPrecise (1 );
1769+
1770+ (try list .addManyAsArray (4 )).* = "abcd" .* ;
1771+ try list .ensureTotalCapacityPrecise (4 );
1772+
1773+ try list .appendSlice ("efgh" );
1774+ try testing .expectEqualSlices (u8 , list .items , "abcdefgh" );
1775+ try list .ensureTotalCapacityPrecise (8 );
1776+
1777+ try list .insertSlice (4 , "ijkl" );
1778+ try testing .expectEqualSlices (u8 , list .items , "abcdijklefgh" );
1779+ }
1780+ {
1781+ var list = ArrayListUnmanaged (u8 ){};
1782+ try list .ensureTotalCapacityPrecise (a , 1 );
1783+ defer list .deinit (a );
1784+
1785+ (try list .addManyAsArray (a , 4 )).* = "abcd" .* ;
1786+ try list .ensureTotalCapacityPrecise (a , 4 );
1787+
1788+ try list .appendSlice (a , "efgh" );
1789+ try testing .expectEqualSlices (u8 , list .items , "abcdefgh" );
1790+ try list .ensureTotalCapacityPrecise (a , 8 );
1791+
1792+ try list .insertSlice (a , 4 , "ijkl" );
1793+ try testing .expectEqualSlices (u8 , list .items , "abcdijklefgh" );
1794+ }
1795+ }
1796+
16531797test "std.ArrayList/ArrayList.fromOwnedSliceSentinel" {
16541798 const a = testing .allocator ;
16551799
0 commit comments