@@ -8,13 +8,13 @@ const Allocator = mem.Allocator;
8
8
/// A contiguous, growable list of items in memory.
9
9
/// This is a wrapper around an array of T values. Initialize with `init`.
10
10
pub fn ArrayList (comptime T : type ) type {
11
- return AlignedArrayList (T , null );
11
+ return ArrayListAligned (T , null );
12
12
}
13
13
14
- pub fn AlignedArrayList (comptime T : type , comptime alignment : ? u29 ) type {
14
+ pub fn ArrayListAligned (comptime T : type , comptime alignment : ? u29 ) type {
15
15
if (alignment ) | a | {
16
16
if (a == @alignOf (T )) {
17
- return AlignedArrayList (T , null );
17
+ return ArrayListAligned (T , null );
18
18
}
19
19
}
20
20
return struct {
@@ -76,6 +76,10 @@ pub fn AlignedArrayList(comptime T: type, comptime alignment: ?u29) type {
76
76
};
77
77
}
78
78
79
+ pub fn toUnmanaged (self : Self ) ArrayListAlignedUnmanaged (T , alignment ) {
80
+ return .{ .items = self .items , .capacity = self .capacity };
81
+ }
82
+
79
83
/// The caller owns the returned memory. ArrayList becomes empty.
80
84
pub fn toOwnedSlice (self : * Self ) Slice {
81
85
const allocator = self .allocator ;
@@ -84,8 +88,8 @@ pub fn AlignedArrayList(comptime T: type, comptime alignment: ?u29) type {
84
88
return result ;
85
89
}
86
90
87
- /// Insert `item` at index `n`. Moves `list[n .. list.len]`
88
- /// to make room .
91
+ /// Insert `item` at index `n` by moving `list[n .. list.len]` to make room.
92
+ /// This operation is O(N) .
89
93
pub fn insert (self : * Self , n : usize , item : T ) ! void {
90
94
try self .ensureCapacity (self .items .len + 1 );
91
95
self .items .len += 1 ;
@@ -94,8 +98,7 @@ pub fn AlignedArrayList(comptime T: type, comptime alignment: ?u29) type {
94
98
self .items [n ] = item ;
95
99
}
96
100
97
- /// Insert slice `items` at index `i`. Moves
98
- /// `list[i .. list.len]` to make room.
101
+ /// Insert slice `items` at index `i` by moving `list[i .. list.len]` to make room.
99
102
/// This operation is O(N).
100
103
pub fn insertSlice (self : * Self , i : usize , items : SliceConst ) ! void {
101
104
try self .ensureCapacity (self .items .len + items .len );
@@ -146,10 +149,15 @@ pub fn AlignedArrayList(comptime T: type, comptime alignment: ?u29) type {
146
149
/// Append the slice of items to the list. Allocates more
147
150
/// memory as necessary.
148
151
pub fn appendSlice (self : * Self , items : SliceConst ) ! void {
152
+ try self .ensureCapacity (self .items .len + items .len );
153
+ self .appendSliceAssumeCapacity (items );
154
+ }
155
+
156
+ /// Append the slice of items to the list, asserting the capacity is already
157
+ /// enough to store the new items.
158
+ pub fn appendSliceAssumeCapacity (self : * Self , items : SliceConst ) void {
149
159
const oldlen = self .items .len ;
150
160
const newlen = self .items .len + items .len ;
151
-
152
- try self .ensureCapacity (newlen );
153
161
self .items .len = newlen ;
154
162
mem .copy (T , self .items [oldlen .. ], items );
155
163
}
@@ -259,6 +267,231 @@ pub fn AlignedArrayList(comptime T: type, comptime alignment: ?u29) type {
259
267
};
260
268
}
261
269
270
+ /// Bring-your-own allocator with every function call.
271
+ /// Initialize directly and deinitialize with `deinit` or use `toOwnedSlice`.
272
+ pub fn ArrayListUnmanaged (comptime T : type ) type {
273
+ return ArrayListAlignedUnmanaged (T , null );
274
+ }
275
+
276
+ pub fn ArrayListAlignedUnmanaged (comptime T : type , comptime alignment : ? u29 ) type {
277
+ if (alignment ) | a | {
278
+ if (a == @alignOf (T )) {
279
+ return ArrayListAlignedUnmanaged (T , null );
280
+ }
281
+ }
282
+ return struct {
283
+ const Self = @This ();
284
+
285
+ /// Content of the ArrayList.
286
+ items : Slice = &[_ ]T {},
287
+ capacity : usize = 0 ,
288
+
289
+ pub const Slice = if (alignment ) | a | ([]align (a ) T ) else []T ;
290
+ pub const SliceConst = if (alignment ) | a | ([]align (a ) const T ) else []const T ;
291
+
292
+ /// Initialize with capacity to hold at least num elements.
293
+ /// Deinitialize with `deinit` or use `toOwnedSlice`.
294
+ pub fn initCapacity (allocator : * Allocator , num : usize ) ! Self {
295
+ var self = Self .init (allocator );
296
+ try self .ensureCapacity (allocator , num );
297
+ return self ;
298
+ }
299
+
300
+ /// Release all allocated memory.
301
+ pub fn deinit (self : * Self , allocator : * Allocator ) void {
302
+ allocator .free (self .allocatedSlice ());
303
+ self .* = undefined ;
304
+ }
305
+
306
+ pub fn toManaged (self : * Self , allocator : * Allocator ) ArrayListAligned (T , alignment ) {
307
+ return .{ .items = self .items , .capacity = self .capacity , .allocator = allocator };
308
+ }
309
+
310
+ /// The caller owns the returned memory. ArrayList becomes empty.
311
+ pub fn toOwnedSlice (self : * Self , allocator : * Allocator ) Slice {
312
+ const result = allocator .shrink (self .allocatedSlice (), self .items .len );
313
+ self .* = Self {};
314
+ return result ;
315
+ }
316
+
317
+ /// Insert `item` at index `n`. Moves `list[n .. list.len]`
318
+ /// to make room.
319
+ pub fn insert (self : * Self , allocator : * Allocator , n : usize , item : T ) ! void {
320
+ try self .ensureCapacity (allocator , self .items .len + 1 );
321
+ self .items .len += 1 ;
322
+
323
+ mem .copyBackwards (T , self .items [n + 1 .. self .items .len ], self .items [n .. self .items .len - 1 ]);
324
+ self .items [n ] = item ;
325
+ }
326
+
327
+ /// Insert slice `items` at index `i`. Moves
328
+ /// `list[i .. list.len]` to make room.
329
+ /// This operation is O(N).
330
+ pub fn insertSlice (self : * Self , allocator : * Allocator , i : usize , items : SliceConst ) ! void {
331
+ try self .ensureCapacity (allocator , self .items .len + items .len );
332
+ self .items .len += items .len ;
333
+
334
+ mem .copyBackwards (T , self .items [i + items .len .. self .items .len ], self .items [i .. self .items .len - items .len ]);
335
+ mem .copy (T , self .items [i .. i + items .len ], items );
336
+ }
337
+
338
+ /// Extend the list by 1 element. Allocates more memory as necessary.
339
+ pub fn append (self : * Self , allocator : * Allocator , item : T ) ! void {
340
+ const new_item_ptr = try self .addOne (allocator );
341
+ new_item_ptr .* = item ;
342
+ }
343
+
344
+ /// Extend the list by 1 element, but asserting `self.capacity`
345
+ /// is sufficient to hold an additional item.
346
+ pub fn appendAssumeCapacity (self : * Self , item : T ) void {
347
+ const new_item_ptr = self .addOneAssumeCapacity ();
348
+ new_item_ptr .* = item ;
349
+ }
350
+
351
+ /// Remove the element at index `i` from the list and return its value.
352
+ /// Asserts the array has at least one item.
353
+ /// This operation is O(N).
354
+ pub fn orderedRemove (self : * Self , i : usize ) T {
355
+ const newlen = self .items .len - 1 ;
356
+ if (newlen == i ) return self .pop ();
357
+
358
+ const old_item = self .items [i ];
359
+ for (self .items [i .. newlen ]) | * b , j | b .* = self .items [i + 1 + j ];
360
+ self .items [newlen ] = undefined ;
361
+ self .items .len = newlen ;
362
+ return old_item ;
363
+ }
364
+
365
+ /// Removes the element at the specified index and returns it.
366
+ /// The empty slot is filled from the end of the list.
367
+ /// This operation is O(1).
368
+ pub fn swapRemove (self : * Self , i : usize ) T {
369
+ if (self .items .len - 1 == i ) return self .pop ();
370
+
371
+ const old_item = self .items [i ];
372
+ self .items [i ] = self .pop ();
373
+ return old_item ;
374
+ }
375
+
376
+ /// Append the slice of items to the list. Allocates more
377
+ /// memory as necessary.
378
+ pub fn appendSlice (self : * Self , allocator : * Allocator , items : SliceConst ) ! void {
379
+ try self .ensureCapacity (allocator , self .items .len + items .len );
380
+ self .appendSliceAssumeCapacity (items );
381
+ }
382
+
383
+ /// Append the slice of items to the list, asserting the capacity is enough
384
+ /// to store the new items.
385
+ pub fn appendSliceAssumeCapacity (self : * Self , items : SliceConst ) void {
386
+ const oldlen = self .items .len ;
387
+ const newlen = self .items .len + items .len ;
388
+
389
+ self .items .len = newlen ;
390
+ mem .copy (T , self .items [oldlen .. ], items );
391
+ }
392
+
393
+ /// Same as `append` except it returns the number of bytes written, which is always the same
394
+ /// as `m.len`. The purpose of this function existing is to match `std.io.OutStream` API.
395
+ /// This function may be called only when `T` is `u8`.
396
+ fn appendWrite (self : * Self , allocator : * Allocator , m : []const u8 ) ! usize {
397
+ try self .appendSlice (allocator , m );
398
+ return m .len ;
399
+ }
400
+
401
+ /// Append a value to the list `n` times.
402
+ /// Allocates more memory as necessary.
403
+ pub fn appendNTimes (self : * Self , allocator : * Allocator , value : T , n : usize ) ! void {
404
+ const old_len = self .items .len ;
405
+ try self .resize (self .items .len + n );
406
+ mem .set (T , self .items [old_len .. self .items .len ], value );
407
+ }
408
+
409
+ /// Adjust the list's length to `new_len`.
410
+ /// Does not initialize added items if any.
411
+ pub fn resize (self : * Self , allocator : * Allocator , new_len : usize ) ! void {
412
+ try self .ensureCapacity (allocator , new_len );
413
+ self .items .len = new_len ;
414
+ }
415
+
416
+ /// Reduce allocated capacity to `new_len`.
417
+ /// Invalidates element pointers.
418
+ pub fn shrink (self : * Self , allocator : * Allocator , new_len : usize ) void {
419
+ assert (new_len <= self .items .len );
420
+
421
+ self .items = allocator .realloc (self .allocatedSlice (), new_len ) catch | e | switch (e ) {
422
+ error .OutOfMemory = > { // no problem, capacity is still correct then.
423
+ self .items .len = new_len ;
424
+ return ;
425
+ },
426
+ };
427
+ self .capacity = new_len ;
428
+ }
429
+
430
+ pub fn ensureCapacity (self : * Self , allocator : * Allocator , new_capacity : usize ) ! void {
431
+ var better_capacity = self .capacity ;
432
+ if (better_capacity >= new_capacity ) return ;
433
+
434
+ while (true ) {
435
+ better_capacity += better_capacity / 2 + 8 ;
436
+ if (better_capacity >= new_capacity ) break ;
437
+ }
438
+
439
+ const new_memory = try allocator .realloc (self .allocatedSlice (), better_capacity );
440
+ self .items .ptr = new_memory .ptr ;
441
+ self .capacity = new_memory .len ;
442
+ }
443
+
444
+ /// Increases the array's length to match the full capacity that is already allocated.
445
+ /// The new elements have `undefined` values.
446
+ /// This operation does not invalidate any element pointers.
447
+ pub fn expandToCapacity (self : * Self ) void {
448
+ self .items .len = self .capacity ;
449
+ }
450
+
451
+ /// Increase length by 1, returning pointer to the new item.
452
+ /// The returned pointer becomes invalid when the list is resized.
453
+ pub fn addOne (self : * Self , allocator : * Allocator ) ! * T {
454
+ const newlen = self .items .len + 1 ;
455
+ try self .ensureCapacity (allocator , newlen );
456
+ return self .addOneAssumeCapacity ();
457
+ }
458
+
459
+ /// Increase length by 1, returning pointer to the new item.
460
+ /// Asserts that there is already space for the new item without allocating more.
461
+ /// The returned pointer becomes invalid when the list is resized.
462
+ /// This operation does not invalidate any element pointers.
463
+ pub fn addOneAssumeCapacity (self : * Self ) * T {
464
+ assert (self .items .len < self .capacity );
465
+
466
+ self .items .len += 1 ;
467
+ return & self .items [self .items .len - 1 ];
468
+ }
469
+
470
+ /// Remove and return the last element from the list.
471
+ /// Asserts the list has at least one item.
472
+ /// This operation does not invalidate any element pointers.
473
+ pub fn pop (self : * Self ) T {
474
+ const val = self .items [self .items .len - 1 ];
475
+ self .items .len -= 1 ;
476
+ return val ;
477
+ }
478
+
479
+ /// Remove and return the last element from the list.
480
+ /// If the list is empty, returns `null`.
481
+ /// This operation does not invalidate any element pointers.
482
+ pub fn popOrNull (self : * Self ) ? T {
483
+ if (self .items .len == 0 ) return null ;
484
+ return self .pop ();
485
+ }
486
+
487
+ /// For a nicer API, `items.len` is the length, not the capacity.
488
+ /// This requires "unsafe" slicing.
489
+ fn allocatedSlice (self : Self ) Slice {
490
+ return self .items .ptr [0.. self .capacity ];
491
+ }
492
+ };
493
+ }
494
+
262
495
test "std.ArrayList.init" {
263
496
var list = ArrayList (i32 ).init (testing .allocator );
264
497
defer list .deinit ();
0 commit comments