| ... | @@ -90,15 +90,12 @@ const mem = std.mem; | ... | @@ -90,15 +90,12 @@ const mem = std.mem; |
| 90 | const Allocator = std.mem.Allocator; | 90 | const Allocator = std.mem.Allocator; |
| 91 | const StackTrace = std.builtin.StackTrace; | 91 | const StackTrace = std.builtin.StackTrace; |
| 92 | | 92 | |
| 93 | const page_size: usize = @max(std.heap.page_size_max, switch (builtin.os.tag) { | 93 | const default_page_size: usize = @max(std.heap.page_size_max, switch (builtin.os.tag) { |
| 94 | .windows => 64 * 1024, // Makes `std.heap.PageAllocator` take the happy path. | 94 | .windows => 64 * 1024, // Makes `std.heap.PageAllocator` take the happy path. |
| 95 | .wasi => 64 * 1024, // Max alignment supported by `std.heap.WasmAllocator`. | 95 | .wasi => 64 * 1024, // Max alignment supported by `std.heap.WasmAllocator`. |
| 96 | else => 128 * 1024, // Avoids too many active mappings when `page_size_max` is low. | 96 | else => 128 * 1024, // Avoids too many active mappings when `page_size_max` is low. |
| 97 | }); | 97 | }); |
| 98 | const page_align: mem.Alignment = .fromByteUnits(page_size); | | |
| 99 | | 98 | |
| 100 | /// Integer type for pointing to slots in a small allocation | | |
| 101 | const SlotIndex = std.meta.Int(.unsigned, math.log2(page_size) + 1); | | |
| 102 | const Log2USize = std.math.Log2Int(usize); | 99 | const Log2USize = std.math.Log2Int(usize); |
| 103 | | 100 | |
| 104 | const default_sys_stack_trace_frames: usize = if (std.debug.sys_can_stack_trace) 6 else 0; | 101 | const default_sys_stack_trace_frames: usize = if (std.debug.sys_can_stack_trace) 6 else 0; |
| ... | @@ -159,6 +156,12 @@ pub const Config = struct { | ... | @@ -159,6 +156,12 @@ pub const Config = struct { |
| 159 | /// Magic value that distinguishes allocations owned by this allocator from | 156 | /// Magic value that distinguishes allocations owned by this allocator from |
| 160 | /// other regions of memory. | 157 | /// other regions of memory. |
| 161 | canary: usize = @truncate(0x9232a6ff85dff10f), | 158 | canary: usize = @truncate(0x9232a6ff85dff10f), |
| | 159 | |
| | 160 | /// The size of allocations requested from the backing allocator for |
| | 161 | /// subdividing into slots for small allocations. |
| | 162 | /// |
| | 163 | /// Must be a power of two. |
| | 164 | page_size: usize = default_page_size, |
| 162 | }; | 165 | }; |
| 163 | | 166 | |
| 164 | /// Default initialization of this struct is deprecated; use `.init` instead. | 167 | /// Default initialization of this struct is deprecated; use `.init` instead. |
| ... | @@ -184,6 +187,15 @@ pub fn DebugAllocator(comptime config: Config) type { | ... | @@ -184,6 +187,15 @@ pub fn DebugAllocator(comptime config: Config) type { |
| 184 | break :init result; | 187 | break :init result; |
| 185 | }; | 188 | }; |
| 186 | | 189 | |
| | 190 | comptime { |
| | 191 | assert(math.isPowerOfTwo(page_size)); |
| | 192 | } |
| | 193 | |
| | 194 | const page_size = config.page_size; |
| | 195 | const page_align: mem.Alignment = .fromByteUnits(page_size); |
| | 196 | /// Integer type for pointing to slots in a small allocation |
| | 197 | const SlotIndex = std.meta.Int(.unsigned, math.log2(page_size) + 1); |
| | 198 | |
| 187 | const total_requested_bytes_init = if (config.enable_memory_limit) @as(usize, 0) else {}; | 199 | const total_requested_bytes_init = if (config.enable_memory_limit) @as(usize, 0) else {}; |
| 188 | const requested_memory_limit_init = if (config.enable_memory_limit) @as(usize, math.maxInt(usize)) else {}; | 200 | const requested_memory_limit_init = if (config.enable_memory_limit) @as(usize, math.maxInt(usize)) else {}; |
| 189 | | 201 | |
| ... | @@ -1138,17 +1150,17 @@ test "large object - grow" { | ... | @@ -1138,17 +1150,17 @@ test "large object - grow" { |
| 1138 | defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak"); | 1150 | defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak"); |
| 1139 | const allocator = gpa.allocator(); | 1151 | const allocator = gpa.allocator(); |
| 1140 | | 1152 | |
| 1141 | var slice1 = try allocator.alloc(u8, page_size * 2 - 20); | 1153 | var slice1 = try allocator.alloc(u8, default_page_size * 2 - 20); |
| 1142 | defer allocator.free(slice1); | 1154 | defer allocator.free(slice1); |
| 1143 | | 1155 | |
| 1144 | const old = slice1; | 1156 | const old = slice1; |
| 1145 | slice1 = try allocator.realloc(slice1, page_size * 2 - 10); | 1157 | slice1 = try allocator.realloc(slice1, default_page_size * 2 - 10); |
| 1146 | try std.testing.expect(slice1.ptr == old.ptr); | 1158 | try std.testing.expect(slice1.ptr == old.ptr); |
| 1147 | | 1159 | |
| 1148 | slice1 = try allocator.realloc(slice1, page_size * 2); | 1160 | slice1 = try allocator.realloc(slice1, default_page_size * 2); |
| 1149 | try std.testing.expect(slice1.ptr == old.ptr); | 1161 | try std.testing.expect(slice1.ptr == old.ptr); |
| 1150 | | 1162 | |
| 1151 | slice1 = try allocator.realloc(slice1, page_size * 2 + 1); | 1163 | slice1 = try allocator.realloc(slice1, default_page_size * 2 + 1); |
| 1152 | } | 1164 | } |
| 1153 | | 1165 | |
| 1154 | test "realloc small object to large object" { | 1166 | test "realloc small object to large object" { |
| ... | @@ -1162,7 +1174,7 @@ test "realloc small object to large object" { | ... | @@ -1162,7 +1174,7 @@ test "realloc small object to large object" { |
| 1162 | slice[60] = 0x34; | 1174 | slice[60] = 0x34; |
| 1163 | | 1175 | |
| 1164 | // This requires upgrading to a large object | 1176 | // This requires upgrading to a large object |
| 1165 | const large_object_size = page_size * 2 + 50; | 1177 | const large_object_size = default_page_size * 2 + 50; |
| 1166 | slice = try allocator.realloc(slice, large_object_size); | 1178 | slice = try allocator.realloc(slice, large_object_size); |
| 1167 | try std.testing.expect(slice[0] == 0x12); | 1179 | try std.testing.expect(slice[0] == 0x12); |
| 1168 | try std.testing.expect(slice[60] == 0x34); | 1180 | try std.testing.expect(slice[60] == 0x34); |
| ... | @@ -1173,22 +1185,22 @@ test "shrink large object to large object" { | ... | @@ -1173,22 +1185,22 @@ test "shrink large object to large object" { |
| 1173 | defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak"); | 1185 | defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak"); |
| 1174 | const allocator = gpa.allocator(); | 1186 | const allocator = gpa.allocator(); |
| 1175 | | 1187 | |
| 1176 | var slice = try allocator.alloc(u8, page_size * 2 + 50); | 1188 | var slice = try allocator.alloc(u8, default_page_size * 2 + 50); |
| 1177 | defer allocator.free(slice); | 1189 | defer allocator.free(slice); |
| 1178 | slice[0] = 0x12; | 1190 | slice[0] = 0x12; |
| 1179 | slice[60] = 0x34; | 1191 | slice[60] = 0x34; |
| 1180 | | 1192 | |
| 1181 | if (!allocator.resize(slice, page_size * 2 + 1)) return; | 1193 | if (!allocator.resize(slice, default_page_size * 2 + 1)) return; |
| 1182 | slice = slice.ptr[0 .. page_size * 2 + 1]; | 1194 | slice = slice.ptr[0 .. default_page_size * 2 + 1]; |
| 1183 | try std.testing.expect(slice[0] == 0x12); | 1195 | try std.testing.expect(slice[0] == 0x12); |
| 1184 | try std.testing.expect(slice[60] == 0x34); | 1196 | try std.testing.expect(slice[60] == 0x34); |
| 1185 | | 1197 | |
| 1186 | try std.testing.expect(allocator.resize(slice, page_size * 2 + 1)); | 1198 | try std.testing.expect(allocator.resize(slice, default_page_size * 2 + 1)); |
| 1187 | slice = slice[0 .. page_size * 2 + 1]; | 1199 | slice = slice[0 .. default_page_size * 2 + 1]; |
| 1188 | try std.testing.expect(slice[0] == 0x12); | 1200 | try std.testing.expect(slice[0] == 0x12); |
| 1189 | try std.testing.expect(slice[60] == 0x34); | 1201 | try std.testing.expect(slice[60] == 0x34); |
| 1190 | | 1202 | |
| 1191 | slice = try allocator.realloc(slice, page_size * 2); | 1203 | slice = try allocator.realloc(slice, default_page_size * 2); |
| 1192 | try std.testing.expect(slice[0] == 0x12); | 1204 | try std.testing.expect(slice[0] == 0x12); |
| 1193 | try std.testing.expect(slice[60] == 0x34); | 1205 | try std.testing.expect(slice[60] == 0x34); |
| 1194 | } | 1206 | } |
| ... | @@ -1204,13 +1216,13 @@ test "shrink large object to large object with larger alignment" { | ... | @@ -1204,13 +1216,13 @@ test "shrink large object to large object with larger alignment" { |
| 1204 | var fba = std.heap.FixedBufferAllocator.init(&debug_buffer); | 1216 | var fba = std.heap.FixedBufferAllocator.init(&debug_buffer); |
| 1205 | const debug_allocator = fba.allocator(); | 1217 | const debug_allocator = fba.allocator(); |
| 1206 | | 1218 | |
| 1207 | const alloc_size = page_size * 2 + 50; | 1219 | const alloc_size = default_page_size * 2 + 50; |
| 1208 | var slice = try allocator.alignedAlloc(u8, 16, alloc_size); | 1220 | var slice = try allocator.alignedAlloc(u8, 16, alloc_size); |
| 1209 | defer allocator.free(slice); | 1221 | defer allocator.free(slice); |
| 1210 | | 1222 | |
| 1211 | const big_alignment: usize = switch (builtin.os.tag) { | 1223 | const big_alignment: usize = switch (builtin.os.tag) { |
| 1212 | .windows => page_size * 32, // Windows aligns to 64K. | 1224 | .windows => default_page_size * 32, // Windows aligns to 64K. |
| 1213 | else => page_size * 2, | 1225 | else => default_page_size * 2, |
| 1214 | }; | 1226 | }; |
| 1215 | // This loop allocates until we find a page that is not aligned to the big | 1227 | // This loop allocates until we find a page that is not aligned to the big |
| 1216 | // alignment. Then we shrink the allocation after the loop, but increase the | 1228 | // alignment. Then we shrink the allocation after the loop, but increase the |
| ... | @@ -1236,7 +1248,7 @@ test "realloc large object to small object" { | ... | @@ -1236,7 +1248,7 @@ test "realloc large object to small object" { |
| 1236 | defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak"); | 1248 | defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak"); |
| 1237 | const allocator = gpa.allocator(); | 1249 | const allocator = gpa.allocator(); |
| 1238 | | 1250 | |
| 1239 | var slice = try allocator.alloc(u8, page_size * 2 + 50); | 1251 | var slice = try allocator.alloc(u8, default_page_size * 2 + 50); |
| 1240 | defer allocator.free(slice); | 1252 | defer allocator.free(slice); |
| 1241 | slice[0] = 0x12; | 1253 | slice[0] = 0x12; |
| 1242 | slice[16] = 0x34; | 1254 | slice[16] = 0x34; |
| ... | @@ -1282,18 +1294,18 @@ test "realloc large object to larger alignment" { | ... | @@ -1282,18 +1294,18 @@ test "realloc large object to larger alignment" { |
| 1282 | var fba = std.heap.FixedBufferAllocator.init(&debug_buffer); | 1294 | var fba = std.heap.FixedBufferAllocator.init(&debug_buffer); |
| 1283 | const debug_allocator = fba.allocator(); | 1295 | const debug_allocator = fba.allocator(); |
| 1284 | | 1296 | |
| 1285 | var slice = try allocator.alignedAlloc(u8, 16, page_size * 2 + 50); | 1297 | var slice = try allocator.alignedAlloc(u8, 16, default_page_size * 2 + 50); |
| 1286 | defer allocator.free(slice); | 1298 | defer allocator.free(slice); |
| 1287 | | 1299 | |
| 1288 | const big_alignment: usize = switch (builtin.os.tag) { | 1300 | const big_alignment: usize = switch (builtin.os.tag) { |
| 1289 | .windows => page_size * 32, // Windows aligns to 64K. | 1301 | .windows => default_page_size * 32, // Windows aligns to 64K. |
| 1290 | else => page_size * 2, | 1302 | else => default_page_size * 2, |
| 1291 | }; | 1303 | }; |
| 1292 | // This loop allocates until we find a page that is not aligned to the big alignment. | 1304 | // This loop allocates until we find a page that is not aligned to the big alignment. |
| 1293 | var stuff_to_free = std.ArrayList([]align(16) u8).init(debug_allocator); | 1305 | var stuff_to_free = std.ArrayList([]align(16) u8).init(debug_allocator); |
| 1294 | while (mem.isAligned(@intFromPtr(slice.ptr), big_alignment)) { | 1306 | while (mem.isAligned(@intFromPtr(slice.ptr), big_alignment)) { |
| 1295 | try stuff_to_free.append(slice); | 1307 | try stuff_to_free.append(slice); |
| 1296 | slice = try allocator.alignedAlloc(u8, 16, page_size * 2 + 50); | 1308 | slice = try allocator.alignedAlloc(u8, 16, default_page_size * 2 + 50); |
| 1297 | } | 1309 | } |
| 1298 | while (stuff_to_free.popOrNull()) |item| { | 1310 | while (stuff_to_free.popOrNull()) |item| { |
| 1299 | allocator.free(item); | 1311 | allocator.free(item); |
| ... | @@ -1301,15 +1313,15 @@ test "realloc large object to larger alignment" { | ... | @@ -1301,15 +1313,15 @@ test "realloc large object to larger alignment" { |
| 1301 | slice[0] = 0x12; | 1313 | slice[0] = 0x12; |
| 1302 | slice[16] = 0x34; | 1314 | slice[16] = 0x34; |
| 1303 | | 1315 | |
| 1304 | slice = try allocator.reallocAdvanced(slice, 32, page_size * 2 + 100); | 1316 | slice = try allocator.reallocAdvanced(slice, 32, default_page_size * 2 + 100); |
| 1305 | try std.testing.expect(slice[0] == 0x12); | 1317 | try std.testing.expect(slice[0] == 0x12); |
| 1306 | try std.testing.expect(slice[16] == 0x34); | 1318 | try std.testing.expect(slice[16] == 0x34); |
| 1307 | | 1319 | |
| 1308 | slice = try allocator.reallocAdvanced(slice, 32, page_size * 2 + 25); | 1320 | slice = try allocator.reallocAdvanced(slice, 32, default_page_size * 2 + 25); |
| 1309 | try std.testing.expect(slice[0] == 0x12); | 1321 | try std.testing.expect(slice[0] == 0x12); |
| 1310 | try std.testing.expect(slice[16] == 0x34); | 1322 | try std.testing.expect(slice[16] == 0x34); |
| 1311 | | 1323 | |
| 1312 | slice = try allocator.reallocAdvanced(slice, big_alignment, page_size * 2 + 100); | 1324 | slice = try allocator.reallocAdvanced(slice, big_alignment, default_page_size * 2 + 100); |
| 1313 | try std.testing.expect(slice[0] == 0x12); | 1325 | try std.testing.expect(slice[0] == 0x12); |
| 1314 | try std.testing.expect(slice[16] == 0x34); | 1326 | try std.testing.expect(slice[16] == 0x34); |
| 1315 | } | 1327 | } |
| ... | @@ -1327,7 +1339,7 @@ test "large object rejects shrinking to small" { | ... | @@ -1327,7 +1339,7 @@ test "large object rejects shrinking to small" { |
| 1327 | defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak"); | 1339 | defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak"); |
| 1328 | const allocator = gpa.allocator(); | 1340 | const allocator = gpa.allocator(); |
| 1329 | | 1341 | |
| 1330 | var slice = try allocator.alloc(u8, page_size * 2 + 50); | 1342 | var slice = try allocator.alloc(u8, default_page_size * 2 + 50); |
| 1331 | defer allocator.free(slice); | 1343 | defer allocator.free(slice); |
| 1332 | slice[0] = 0x12; | 1344 | slice[0] = 0x12; |
| 1333 | slice[3] = 0x34; | 1345 | slice[3] = 0x34; |
| ... | @@ -1379,8 +1391,8 @@ test "large allocations count requested size not backing size" { | ... | @@ -1379,8 +1391,8 @@ test "large allocations count requested size not backing size" { |
| 1379 | var gpa: DebugAllocator(.{ .enable_memory_limit = true }) = .{}; | 1391 | var gpa: DebugAllocator(.{ .enable_memory_limit = true }) = .{}; |
| 1380 | const allocator = gpa.allocator(); | 1392 | const allocator = gpa.allocator(); |
| 1381 | | 1393 | |
| 1382 | var buf = try allocator.alignedAlloc(u8, 1, page_size + 1); | 1394 | var buf = try allocator.alignedAlloc(u8, 1, default_page_size + 1); |
| 1383 | try std.testing.expectEqual(page_size + 1, gpa.total_requested_bytes); | 1395 | try std.testing.expectEqual(default_page_size + 1, gpa.total_requested_bytes); |
| 1384 | buf = try allocator.realloc(buf, 1); | 1396 | buf = try allocator.realloc(buf, 1); |
| 1385 | try std.testing.expectEqual(1, gpa.total_requested_bytes); | 1397 | try std.testing.expectEqual(1, gpa.total_requested_bytes); |
| 1386 | buf = try allocator.realloc(buf, 2); | 1398 | buf = try allocator.realloc(buf, 2); |