| ... | ... | @@ -90,15 +90,12 @@ const mem = std.mem; |
| 90 | 90 | const Allocator = std.mem.Allocator; |
| 91 | 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 | 94 | .windows => 64 * 1024, // Makes `std.heap.PageAllocator` take the happy path. |
| 95 | 95 | .wasi => 64 * 1024, // Max alignment supported by `std.heap.WasmAllocator`. |
| 96 | 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 | 99 | const Log2USize = std.math.Log2Int(usize); |
| 103 | 100 | |
| 104 | 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 | 156 | /// Magic value that distinguishes allocations owned by this allocator from |
| 160 | 157 | /// other regions of memory. |
| 161 | 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 | 167 | /// Default initialization of this struct is deprecated; use `.init` instead. |
| ... | ... | @@ -184,6 +187,15 @@ pub fn DebugAllocator(comptime config: Config) type { |
| 184 | 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 | 199 | const total_requested_bytes_init = if (config.enable_memory_limit) @as(usize, 0) else {}; |
| 188 | 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 | 1150 | defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak"); |
| 1139 | 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 | 1154 | defer allocator.free(slice1); |
| 1143 | 1155 | |
| 1144 | 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 | 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 | 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 | 1166 | test "realloc small object to large object" { |
| ... | ... | @@ -1162,7 +1174,7 @@ test "realloc small object to large object" { |
| 1162 | 1174 | slice[60] = 0x34; |
| 1163 | 1175 | |
| 1164 | 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 | 1178 | slice = try allocator.realloc(slice, large_object_size); |
| 1167 | 1179 | try std.testing.expect(slice[0] == 0x12); |
| 1168 | 1180 | try std.testing.expect(slice[60] == 0x34); |
| ... | ... | @@ -1173,22 +1185,22 @@ test "shrink large object to large object" { |
| 1173 | 1185 | defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak"); |
| 1174 | 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 | 1189 | defer allocator.free(slice); |
| 1178 | 1190 | slice[0] = 0x12; |
| 1179 | 1191 | slice[60] = 0x34; |
| 1180 | 1192 | |
| 1181 | | if (!allocator.resize(slice, page_size * 2 + 1)) return; |
| 1182 | | slice = slice.ptr[0 .. page_size * 2 + 1]; |
| 1193 | if (!allocator.resize(slice, default_page_size * 2 + 1)) return; |
| 1194 | slice = slice.ptr[0 .. default_page_size * 2 + 1]; |
| 1183 | 1195 | try std.testing.expect(slice[0] == 0x12); |
| 1184 | 1196 | try std.testing.expect(slice[60] == 0x34); |
| 1185 | 1197 | |
| 1186 | | try std.testing.expect(allocator.resize(slice, page_size * 2 + 1)); |
| 1187 | | slice = slice[0 .. page_size * 2 + 1]; |
| 1198 | try std.testing.expect(allocator.resize(slice, default_page_size * 2 + 1)); |
| 1199 | slice = slice[0 .. default_page_size * 2 + 1]; |
| 1188 | 1200 | try std.testing.expect(slice[0] == 0x12); |
| 1189 | 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 | 1204 | try std.testing.expect(slice[0] == 0x12); |
| 1193 | 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 | 1216 | var fba = std.heap.FixedBufferAllocator.init(&debug_buffer); |
| 1205 | 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 | 1220 | var slice = try allocator.alignedAlloc(u8, 16, alloc_size); |
| 1209 | 1221 | defer allocator.free(slice); |
| 1210 | 1222 | |
| 1211 | 1223 | const big_alignment: usize = switch (builtin.os.tag) { |
| 1212 | | .windows => page_size * 32, // Windows aligns to 64K. |
| 1213 | | else => page_size * 2, |
| 1224 | .windows => default_page_size * 32, // Windows aligns to 64K. |
| 1225 | else => default_page_size * 2, |
| 1214 | 1226 | }; |
| 1215 | 1227 | // This loop allocates until we find a page that is not aligned to the big |
| 1216 | 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 | 1248 | defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak"); |
| 1237 | 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 | 1252 | defer allocator.free(slice); |
| 1241 | 1253 | slice[0] = 0x12; |
| 1242 | 1254 | slice[16] = 0x34; |
| ... | ... | @@ -1282,18 +1294,18 @@ test "realloc large object to larger alignment" { |
| 1282 | 1294 | var fba = std.heap.FixedBufferAllocator.init(&debug_buffer); |
| 1283 | 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 | 1298 | defer allocator.free(slice); |
| 1287 | 1299 | |
| 1288 | 1300 | const big_alignment: usize = switch (builtin.os.tag) { |
| 1289 | | .windows => page_size * 32, // Windows aligns to 64K. |
| 1290 | | else => page_size * 2, |
| 1301 | .windows => default_page_size * 32, // Windows aligns to 64K. |
| 1302 | else => default_page_size * 2, |
| 1291 | 1303 | }; |
| 1292 | 1304 | // This loop allocates until we find a page that is not aligned to the big alignment. |
| 1293 | 1305 | var stuff_to_free = std.ArrayList([]align(16) u8).init(debug_allocator); |
| 1294 | 1306 | while (mem.isAligned(@intFromPtr(slice.ptr), big_alignment)) { |
| 1295 | 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 | 1310 | while (stuff_to_free.popOrNull()) |item| { |
| 1299 | 1311 | allocator.free(item); |
| ... | ... | @@ -1301,15 +1313,15 @@ test "realloc large object to larger alignment" { |
| 1301 | 1313 | slice[0] = 0x12; |
| 1302 | 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 | 1317 | try std.testing.expect(slice[0] == 0x12); |
| 1306 | 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 | 1321 | try std.testing.expect(slice[0] == 0x12); |
| 1310 | 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 | 1325 | try std.testing.expect(slice[0] == 0x12); |
| 1314 | 1326 | try std.testing.expect(slice[16] == 0x34); |
| 1315 | 1327 | } |
| ... | ... | @@ -1327,7 +1339,7 @@ test "large object rejects shrinking to small" { |
| 1327 | 1339 | defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak"); |
| 1328 | 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 | 1343 | defer allocator.free(slice); |
| 1332 | 1344 | slice[0] = 0x12; |
| 1333 | 1345 | slice[3] = 0x34; |
| ... | ... | @@ -1379,8 +1391,8 @@ test "large allocations count requested size not backing size" { |
| 1379 | 1391 | var gpa: DebugAllocator(.{ .enable_memory_limit = true }) = .{}; |
| 1380 | 1392 | const allocator = gpa.allocator(); |
| 1381 | 1393 | |
| 1382 | | var buf = try allocator.alignedAlloc(u8, 1, page_size + 1); |
| 1383 | | try std.testing.expectEqual(page_size + 1, gpa.total_requested_bytes); |
| 1394 | var buf = try allocator.alignedAlloc(u8, 1, default_page_size + 1); |
| 1395 | try std.testing.expectEqual(default_page_size + 1, gpa.total_requested_bytes); |
| 1384 | 1396 | buf = try allocator.realloc(buf, 1); |
| 1385 | 1397 | try std.testing.expectEqual(1, gpa.total_requested_bytes); |
| 1386 | 1398 | buf = try allocator.realloc(buf, 2); |