| ... | ... | @@ -674,6 +674,26 @@ pub fn Aligned(comptime T: type, comptime alignment: ?mem.Alignment) type { |
| 674 | 674 | return result[0 .. result.len - 1 :sentinel]; |
| 675 | 675 | } |
| 676 | 676 | |
| 677 | /// The caller owns the returned memory. Empties this ArrayList. |
| 678 | /// Its capacity is cleared, making deinit() safe but unnecessary to call. |
| 679 | /// |
| 680 | /// Asserts what the capacity is equal to the length. |
| 681 | pub fn toOwnedSliceAssert(self: *Self) Slice { |
| 682 | assert(self.items.len == self.capacity); |
| 683 | const items = self.items; |
| 684 | self.* = .empty; |
| 685 | return items; |
| 686 | } |
| 687 | |
| 688 | /// The caller owns the returned memory. ArrayList becomes empty. |
| 689 | /// Asserts what the capacity is equal to the length + 1. |
| 690 | pub fn toOwnedSliceSentinelAssert(self: *Self, comptime sentinel: T) SentinelSlice(sentinel) { |
| 691 | std.debug.assert(self.items.len + 1 == self.capacity); |
| 692 | self.appendAssumeCapacity(sentinel); |
| 693 | const result = self.toOwnedSliceAssert(); |
| 694 | return result[0 .. result.len - 1 :sentinel]; |
| 695 | } |
| 696 | |
| 677 | 697 | /// Creates a copy of this ArrayList. |
| 678 | 698 | pub fn clone(self: Self, gpa: Allocator) Allocator.Error!Self { |
| 679 | 699 | var cloned = try Self.initCapacity(gpa, self.capacity); |
| ... | ... | @@ -1106,6 +1126,20 @@ pub fn Aligned(comptime T: type, comptime alignment: ?mem.Alignment) type { |
| 1106 | 1126 | /// May invalidate element pointers. |
| 1107 | 1127 | /// Asserts that the new length is less than or equal to the previous length. |
| 1108 | 1128 | pub fn shrinkAndFree(self: *Self, gpa: Allocator, new_len: usize) void { |
| 1129 | self.shrinkAndFreePrecise(gpa, new_len) catch |e| switch (e) { |
| 1130 | error.OutOfMemory => { |
| 1131 | // No problem, capacity is still correct then. |
| 1132 | self.items.len = new_len; |
| 1133 | return; |
| 1134 | }, |
| 1135 | }; |
| 1136 | } |
| 1137 | |
| 1138 | /// Reduce allocated capacity to `new_len`. |
| 1139 | /// May invalidate element pointers. |
| 1140 | /// Asserts that the new length is less than or equal to the previous length. |
| 1141 | /// If succeds capacity is guaranteed to be equal to the length. |
| 1142 | pub fn shrinkAndFreePrecise(self: *Self, gpa: Allocator, new_len: usize) Allocator.Error!void { |
| 1109 | 1143 | assert(new_len <= self.items.len); |
| 1110 | 1144 | |
| 1111 | 1145 | if (@sizeOf(T) == 0) { |
| ... | ... | @@ -1120,13 +1154,7 @@ pub fn Aligned(comptime T: type, comptime alignment: ?mem.Alignment) type { |
| 1120 | 1154 | return; |
| 1121 | 1155 | } |
| 1122 | 1156 | |
| 1123 | | const new_memory = gpa.alignedAlloc(T, alignment, new_len) catch |e| switch (e) { |
| 1124 | | error.OutOfMemory => { |
| 1125 | | // No problem, capacity is still correct then. |
| 1126 | | self.items.len = new_len; |
| 1127 | | return; |
| 1128 | | }, |
| 1129 | | }; |
| 1157 | const new_memory = try gpa.alignedAlloc(T, alignment, new_len); |
| 1130 | 1158 | |
| 1131 | 1159 | @memcpy(new_memory, self.items[0..new_len]); |
| 1132 | 1160 | gpa.free(old_memory); |
| ... | ... | @@ -1134,6 +1162,32 @@ pub fn Aligned(comptime T: type, comptime alignment: ?mem.Alignment) type { |
| 1134 | 1162 | self.capacity = new_memory.len; |
| 1135 | 1163 | } |
| 1136 | 1164 | |
| 1165 | /// Shrinks capacity to match length. |
| 1166 | /// May invalidate element pointers. |
| 1167 | /// If succeds it is safe to call toOwnedSliceAssert(). |
| 1168 | pub fn shrinkToLen(self: *Self, gpa: Allocator) Allocator.Error!void { |
| 1169 | try self.shrinkAndFreePrecise(gpa, self.items.len); |
| 1170 | } |
| 1171 | |
| 1172 | /// Shrinks or expands capacity to match length + 1. |
| 1173 | /// May invalidate element pointers. |
| 1174 | /// If succeds it is safe to call toOwnedSliceSentinelAssert(). |
| 1175 | pub fn shrinkToLenSentinel(self: *Self, gpa: Allocator) Allocator.Error!void { |
| 1176 | std.debug.assert(self.items.len <= self.capacity); |
| 1177 | const required_len = self.items.len + 1; |
| 1178 | switch (std.math.order(required_len, self.capacity)) { |
| 1179 | .eq => return, |
| 1180 | .gt => { |
| 1181 | try self.ensureTotalCapacityPrecise(gpa, required_len); |
| 1182 | }, |
| 1183 | .lt => { |
| 1184 | self.items.len += 1; |
| 1185 | defer self.items.len -= 1; |
| 1186 | try self.shrinkToLen(gpa); |
| 1187 | }, |
| 1188 | } |
| 1189 | } |
| 1190 | |
| 1137 | 1191 | /// Reduce length to `new_len`. |
| 1138 | 1192 | /// Invalidates pointers to elements `items[new_len..]`. |
| 1139 | 1193 | /// Keeps capacity the same. |
| ... | ... | @@ -2089,6 +2143,30 @@ test "shrinkAndFree with a copy" { |
| 2089 | 2143 | try testing.expect(mem.eql(i32, list.items, &.{ 3, 3, 3, 3 })); |
| 2090 | 2144 | } |
| 2091 | 2145 | |
| 2146 | test "shrinkAndFreePrecise without resize succeeds" { |
| 2147 | var failing_allocator = testing.FailingAllocator.init(testing.allocator, .{ .resize_fail_index = 0 }); |
| 2148 | const a = failing_allocator.allocator(); |
| 2149 | |
| 2150 | var list: Aligned(i32, null) = .empty; |
| 2151 | defer list.deinit(a); |
| 2152 | |
| 2153 | try list.appendNTimes(a, 3, 16); |
| 2154 | try list.shrinkAndFreePrecise(a, 4); |
| 2155 | try testing.expectEqualSlices(i32, &.{ 3, 3, 3, 3 }, list.items); |
| 2156 | try testing.expectEqual(list.items.len, list.capacity); |
| 2157 | } |
| 2158 | |
| 2159 | test "shrinkAndFreePrecise without resize and no copy failes" { |
| 2160 | var failing_allocator = testing.FailingAllocator.init(testing.allocator, .{ .resize_fail_index = 0, .fail_index = 1 }); |
| 2161 | const a = failing_allocator.allocator(); |
| 2162 | |
| 2163 | var list: Aligned(i32, null) = .empty; |
| 2164 | defer list.deinit(a); |
| 2165 | |
| 2166 | try list.appendNTimes(a, 3, 16); |
| 2167 | try std.testing.expectError(error.OutOfMemory, list.shrinkAndFreePrecise(a, 4)); |
| 2168 | } |
| 2169 | |
| 2092 | 2170 | test "addManyAsArray" { |
| 2093 | 2171 | const a = std.testing.allocator; |
| 2094 | 2172 | { |
| ... | ... | @@ -2219,6 +2297,45 @@ test "toOwnedSliceSentinel" { |
| 2219 | 2297 | } |
| 2220 | 2298 | } |
| 2221 | 2299 | |
| 2300 | test "toOwnedSliceAssert" { |
| 2301 | var failing_allocator: testing.FailingAllocator = .init(testing.allocator, .{ |
| 2302 | .fail_index = 2, |
| 2303 | }); |
| 2304 | const a = failing_allocator.allocator(); |
| 2305 | |
| 2306 | var list: Aligned(u8, null) = try .initCapacity(a, 6); // first alloc |
| 2307 | list.appendSliceAssumeCapacity(&.{ 1, 2, 3 }); |
| 2308 | |
| 2309 | try list.shrinkToLen(a); // first resize |
| 2310 | try std.testing.expectEqual(list.items.len, list.capacity); |
| 2311 | try list.shrinkToLen(a); // no alloc or resize |
| 2312 | |
| 2313 | const slice = list.toOwnedSliceAssert(); |
| 2314 | defer a.free(slice); |
| 2315 | |
| 2316 | try std.testing.expectEqual(Aligned(u8, null).empty, list); |
| 2317 | try std.testing.expectEqualSlices(u8, &.{ 1, 2, 3 }, slice); |
| 2318 | } |
| 2319 | |
| 2320 | test "toOwnedSliceSentinelAssert" { |
| 2321 | const a = testing.allocator; |
| 2322 | |
| 2323 | var list: Aligned(u8, null) = try .initCapacity(a, 6); |
| 2324 | list.appendSliceAssumeCapacity(&.{ 1, 2, 3 }); |
| 2325 | |
| 2326 | // shrinkToLenSentinel shrinks array |
| 2327 | try list.shrinkToLenSentinel(a); |
| 2328 | |
| 2329 | // shrinkToLenSentinel expands array |
| 2330 | try list.shrinkToLen(a); |
| 2331 | try list.shrinkToLenSentinel(a); |
| 2332 | |
| 2333 | const slice = list.toOwnedSliceSentinelAssert(10); |
| 2334 | defer a.free(slice); |
| 2335 | |
| 2336 | try std.testing.expectEqualSentinel(u8, 10, &.{ 1, 2, 3 }, slice); |
| 2337 | } |
| 2338 | |
| 2222 | 2339 | test "accepts unaligned slices" { |
| 2223 | 2340 | const a = testing.allocator; |
| 2224 | 2341 | { |