| ... | @@ -674,6 +674,26 @@ pub fn Aligned(comptime T: type, comptime alignment: ?mem.Alignment) type { | ... | @@ -674,6 +674,26 @@ pub fn Aligned(comptime T: type, comptime alignment: ?mem.Alignment) type { |
| 674 | return result[0 .. result.len - 1 :sentinel]; | 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 | /// Creates a copy of this ArrayList. | 697 | /// Creates a copy of this ArrayList. |
| 678 | pub fn clone(self: Self, gpa: Allocator) Allocator.Error!Self { | 698 | pub fn clone(self: Self, gpa: Allocator) Allocator.Error!Self { |
| 679 | var cloned = try Self.initCapacity(gpa, self.capacity); | 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,6 +1126,20 @@ pub fn Aligned(comptime T: type, comptime alignment: ?mem.Alignment) type { |
| 1106 | /// May invalidate element pointers. | 1126 | /// May invalidate element pointers. |
| 1107 | /// Asserts that the new length is less than or equal to the previous length. | 1127 | /// Asserts that the new length is less than or equal to the previous length. |
| 1108 | pub fn shrinkAndFree(self: *Self, gpa: Allocator, new_len: usize) void { | 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 | assert(new_len <= self.items.len); | 1143 | assert(new_len <= self.items.len); |
| 1110 | | 1144 | |
| 1111 | if (@sizeOf(T) == 0) { | 1145 | if (@sizeOf(T) == 0) { |
| ... | @@ -1120,13 +1154,7 @@ pub fn Aligned(comptime T: type, comptime alignment: ?mem.Alignment) type { | ... | @@ -1120,13 +1154,7 @@ pub fn Aligned(comptime T: type, comptime alignment: ?mem.Alignment) type { |
| 1120 | return; | 1154 | return; |
| 1121 | } | 1155 | } |
| 1122 | | 1156 | |
| 1123 | const new_memory = gpa.alignedAlloc(T, alignment, new_len) catch |e| switch (e) { | 1157 | const new_memory = try gpa.alignedAlloc(T, alignment, new_len); |
| 1124 | error.OutOfMemory => { | | |
| 1125 | // No problem, capacity is still correct then. | | |
| 1126 | self.items.len = new_len; | | |
| 1127 | return; | | |
| 1128 | }, | | |
| 1129 | }; | | |
| 1130 | | 1158 | |
| 1131 | @memcpy(new_memory, self.items[0..new_len]); | 1159 | @memcpy(new_memory, self.items[0..new_len]); |
| 1132 | gpa.free(old_memory); | 1160 | gpa.free(old_memory); |
| ... | @@ -1134,6 +1162,32 @@ pub fn Aligned(comptime T: type, comptime alignment: ?mem.Alignment) type { | ... | @@ -1134,6 +1162,32 @@ pub fn Aligned(comptime T: type, comptime alignment: ?mem.Alignment) type { |
| 1134 | self.capacity = new_memory.len; | 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 | /// Reduce length to `new_len`. | 1191 | /// Reduce length to `new_len`. |
| 1138 | /// Invalidates pointers to elements `items[new_len..]`. | 1192 | /// Invalidates pointers to elements `items[new_len..]`. |
| 1139 | /// Keeps capacity the same. | 1193 | /// Keeps capacity the same. |
| ... | @@ -2089,6 +2143,30 @@ test "shrinkAndFree with a copy" { | ... | @@ -2089,6 +2143,30 @@ test "shrinkAndFree with a copy" { |
| 2089 | try testing.expect(mem.eql(i32, list.items, &.{ 3, 3, 3, 3 })); | 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 | test "addManyAsArray" { | 2170 | test "addManyAsArray" { |
| 2093 | const a = std.testing.allocator; | 2171 | const a = std.testing.allocator; |
| 2094 | { | 2172 | { |
| ... | @@ -2219,6 +2297,45 @@ test "toOwnedSliceSentinel" { | ... | @@ -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 | test "accepts unaligned slices" { | 2339 | test "accepts unaligned slices" { |
| 2223 | const a = testing.allocator; | 2340 | const a = testing.allocator; |
| 2224 | { | 2341 | { |