2019-03-02 13:46:04 -08:00
|
|
|
const std = @import("std.zig");
|
2017-12-23 19:08:53 -08:00
|
|
|
const debug = std.debug;
|
2017-10-31 01:47:55 -07:00
|
|
|
const assert = debug.assert;
|
2019-02-08 15:18:47 -08:00
|
|
|
const testing = std.testing;
|
2017-12-23 19:08:53 -08:00
|
|
|
const mem = std.mem;
|
|
|
|
const os = std.os;
|
2017-10-31 01:47:55 -07:00
|
|
|
const builtin = @import("builtin");
|
|
|
|
const Os = builtin.Os;
|
2017-12-23 19:08:53 -08:00
|
|
|
const c = std.c;
|
2018-10-26 11:59:58 -07:00
|
|
|
const maxInt = std.math.maxInt;
|
2017-10-31 01:47:55 -07:00
|
|
|
|
|
|
|
const Allocator = mem.Allocator;
|
|
|
|
|
2017-12-22 21:29:39 -08:00
|
|
|
pub const c_allocator = &c_allocator_state;
|
2018-11-13 05:08:37 -08:00
|
|
|
var c_allocator_state = Allocator{
|
2017-10-31 01:47:55 -07:00
|
|
|
.reallocFn = cRealloc,
|
2019-03-15 14:47:47 -07:00
|
|
|
.shrinkFn = cShrink,
|
2017-10-31 01:47:55 -07:00
|
|
|
};
|
|
|
|
|
2019-03-15 14:47:47 -07:00
|
|
|
fn cRealloc(self: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 {
|
|
|
|
assert(new_align <= @alignOf(c_longdouble));
|
|
|
|
const old_ptr = if (old_mem.len == 0) null else @ptrCast(*c_void, old_mem.ptr);
|
|
|
|
const buf = c.realloc(old_ptr, new_size) orelse return error.OutOfMemory;
|
|
|
|
return @ptrCast([*]u8, buf)[0..new_size];
|
2017-10-31 01:47:55 -07:00
|
|
|
}
|
|
|
|
|
2019-03-15 14:47:47 -07:00
|
|
|
fn cShrink(self: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 {
|
2018-06-05 15:03:21 -07:00
|
|
|
const old_ptr = @ptrCast(*c_void, old_mem.ptr);
|
2019-03-15 14:47:47 -07:00
|
|
|
const buf = c.realloc(old_ptr, new_size) orelse return old_mem[0..new_size];
|
|
|
|
return @ptrCast([*]u8, buf)[0..new_size];
|
2017-10-31 01:47:55 -07:00
|
|
|
}
|
|
|
|
|
2018-02-11 23:14:44 -08:00
|
|
|
/// This allocator makes a syscall directly for every allocation and free.
|
2018-07-05 12:09:02 -07:00
|
|
|
/// Thread-safe and lock-free.
|
2018-11-13 05:08:37 -08:00
|
|
|
pub const DirectAllocator = struct {
|
2018-02-11 23:14:44 -08:00
|
|
|
allocator: Allocator,
|
|
|
|
heap_handle: ?HeapHandle,
|
|
|
|
|
|
|
|
const HeapHandle = if (builtin.os == Os.windows) os.windows.HANDLE else void;
|
|
|
|
|
|
|
|
pub fn init() DirectAllocator {
|
2018-11-13 05:08:37 -08:00
|
|
|
return DirectAllocator{
|
|
|
|
.allocator = Allocator{
|
2018-02-11 23:14:44 -08:00
|
|
|
.reallocFn = realloc,
|
2019-03-15 14:47:47 -07:00
|
|
|
.shrinkFn = shrink,
|
2018-02-11 23:14:44 -08:00
|
|
|
},
|
|
|
|
.heap_handle = if (builtin.os == Os.windows) null else {},
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2018-05-31 07:56:59 -07:00
|
|
|
pub fn deinit(self: *DirectAllocator) void {
|
2018-02-11 23:14:44 -08:00
|
|
|
switch (builtin.os) {
|
|
|
|
Os.windows => if (self.heap_handle) |heap_handle| {
|
|
|
|
_ = os.windows.HeapDestroy(heap_handle);
|
|
|
|
},
|
|
|
|
else => {},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-12 17:19:46 -08:00
|
|
|
fn alloc(allocator: *Allocator, n: usize, alignment: u29) error{OutOfMemory}![]u8 {
|
2018-02-11 23:14:44 -08:00
|
|
|
const self = @fieldParentPtr(DirectAllocator, "allocator", allocator);
|
2019-04-09 08:48:09 -07:00
|
|
|
if (n == 0)
|
|
|
|
return (([*]u8)(undefined))[0..0];
|
2017-10-31 01:47:55 -07:00
|
|
|
|
|
|
|
switch (builtin.os) {
|
2019-02-16 02:29:12 -08:00
|
|
|
Os.linux, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => {
|
2017-10-31 01:47:55 -07:00
|
|
|
const p = os.posix;
|
2018-04-30 22:53:04 -07:00
|
|
|
const alloc_size = if (alignment <= os.page_size) n else n + alignment;
|
|
|
|
const addr = p.mmap(null, alloc_size, p.PROT_READ | p.PROT_WRITE, p.MAP_PRIVATE | p.MAP_ANONYMOUS, -1, 0);
|
|
|
|
if (addr == p.MAP_FAILED) return error.OutOfMemory;
|
2018-06-03 22:09:15 -07:00
|
|
|
if (alloc_size == n) return @intToPtr([*]u8, addr)[0..n];
|
2018-04-30 22:53:04 -07:00
|
|
|
|
2019-04-19 17:27:20 -07:00
|
|
|
const aligned_addr = mem.alignForward(addr, alignment);
|
|
|
|
|
|
|
|
// Unmap the extra bytes that were only requested in order to guarantee
|
|
|
|
// that the range of memory we were provided had a proper alignment in
|
|
|
|
// it somewhere. The extra bytes could be at the beginning, or end, or both.
|
|
|
|
const unused_start_len = aligned_addr - addr;
|
|
|
|
if (unused_start_len != 0) {
|
|
|
|
const err = p.munmap(addr, unused_start_len);
|
|
|
|
assert(p.getErrno(err) == 0);
|
|
|
|
}
|
|
|
|
const aligned_end_addr = std.mem.alignForward(aligned_addr + n, os.page_size);
|
|
|
|
const unused_end_len = addr + alloc_size - aligned_end_addr;
|
|
|
|
if (unused_end_len != 0) {
|
|
|
|
const err = p.munmap(aligned_end_addr, unused_end_len);
|
|
|
|
assert(p.getErrno(err) == 0);
|
|
|
|
}
|
2018-04-30 22:53:04 -07:00
|
|
|
|
2018-06-03 22:09:15 -07:00
|
|
|
return @intToPtr([*]u8, aligned_addr)[0..n];
|
2018-02-11 23:14:44 -08:00
|
|
|
},
|
|
|
|
Os.windows => {
|
|
|
|
const amt = n + alignment + @sizeOf(usize);
|
2018-07-09 10:19:11 -07:00
|
|
|
const optional_heap_handle = @atomicLoad(?HeapHandle, &self.heap_handle, builtin.AtomicOrder.SeqCst);
|
2018-07-05 12:09:02 -07:00
|
|
|
const heap_handle = optional_heap_handle orelse blk: {
|
2018-07-09 13:49:46 -07:00
|
|
|
const hh = os.windows.HeapCreate(0, amt, 0) orelse return error.OutOfMemory;
|
2018-07-05 12:09:02 -07:00
|
|
|
const other_hh = @cmpxchgStrong(?HeapHandle, &self.heap_handle, null, hh, builtin.AtomicOrder.SeqCst, builtin.AtomicOrder.SeqCst) orelse break :blk hh;
|
|
|
|
_ = os.windows.HeapDestroy(hh);
|
2018-07-09 10:19:11 -07:00
|
|
|
break :blk other_hh.?; // can't be null because of the cmpxchg
|
2017-10-31 01:47:55 -07:00
|
|
|
};
|
2018-06-09 22:13:51 -07:00
|
|
|
const ptr = os.windows.HeapAlloc(heap_handle, 0, amt) orelse return error.OutOfMemory;
|
2018-02-11 23:14:44 -08:00
|
|
|
const root_addr = @ptrToInt(ptr);
|
2019-02-06 10:48:04 -08:00
|
|
|
const adjusted_addr = mem.alignForward(root_addr, alignment);
|
2018-02-11 23:14:44 -08:00
|
|
|
const record_addr = adjusted_addr + n;
|
2018-05-31 07:56:59 -07:00
|
|
|
@intToPtr(*align(1) usize, record_addr).* = root_addr;
|
2018-06-03 22:09:15 -07:00
|
|
|
return @intToPtr([*]u8, adjusted_addr)[0..n];
|
2018-02-11 23:14:44 -08:00
|
|
|
},
|
|
|
|
else => @compileError("Unsupported OS"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-15 14:47:47 -07:00
|
|
|
fn shrink(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 {
|
2018-02-11 23:14:44 -08:00
|
|
|
switch (builtin.os) {
|
2019-02-16 02:29:12 -08:00
|
|
|
Os.linux, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => {
|
2019-03-15 14:47:47 -07:00
|
|
|
const base_addr = @ptrToInt(old_mem.ptr);
|
|
|
|
const old_addr_end = base_addr + old_mem.len;
|
|
|
|
const new_addr_end = base_addr + new_size;
|
|
|
|
const new_addr_end_rounded = mem.alignForward(new_addr_end, os.page_size);
|
|
|
|
if (old_addr_end > new_addr_end_rounded) {
|
|
|
|
_ = os.posix.munmap(new_addr_end_rounded, old_addr_end - new_addr_end_rounded);
|
2018-02-11 23:14:44 -08:00
|
|
|
}
|
2019-03-15 14:47:47 -07:00
|
|
|
return old_mem[0..new_size];
|
|
|
|
},
|
2019-04-16 16:11:37 -07:00
|
|
|
Os.windows => return realloc(allocator, old_mem, old_align, new_size, new_align) catch {
|
|
|
|
const old_adjusted_addr = @ptrToInt(old_mem.ptr);
|
|
|
|
const old_record_addr = old_adjusted_addr + old_mem.len;
|
|
|
|
const root_addr = @intToPtr(*align(1) usize, old_record_addr).*;
|
|
|
|
const old_ptr = @intToPtr(*c_void, root_addr);
|
|
|
|
const new_record_addr = old_record_addr - new_size + old_mem.len;
|
|
|
|
@intToPtr(*align(1) usize, new_record_addr).* = root_addr;
|
|
|
|
return old_mem[0..new_size];
|
2019-03-15 14:47:47 -07:00
|
|
|
},
|
|
|
|
else => @compileError("Unsupported OS"),
|
|
|
|
}
|
|
|
|
}
|
2018-02-11 23:14:44 -08:00
|
|
|
|
2019-03-15 14:47:47 -07:00
|
|
|
fn realloc(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 {
|
|
|
|
switch (builtin.os) {
|
|
|
|
Os.linux, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => {
|
|
|
|
if (new_size <= old_mem.len and new_align <= old_align) {
|
|
|
|
return shrink(allocator, old_mem, old_align, new_size, new_align);
|
|
|
|
}
|
|
|
|
const result = try alloc(allocator, new_size, new_align);
|
2019-04-25 13:41:19 -07:00
|
|
|
@memcpy(result.ptr, old_mem.ptr, std.math.min(old_mem.len, result.len));
|
2019-04-09 08:48:09 -07:00
|
|
|
if (old_mem.len > 0) {
|
|
|
|
_ = os.posix.munmap(@ptrToInt(old_mem.ptr), old_mem.len);
|
|
|
|
}
|
2018-02-11 23:14:44 -08:00
|
|
|
return result;
|
2017-10-31 01:47:55 -07:00
|
|
|
},
|
|
|
|
Os.windows => {
|
2019-03-15 15:57:07 -07:00
|
|
|
if (old_mem.len == 0) return alloc(allocator, new_size, new_align);
|
2019-03-15 14:47:47 -07:00
|
|
|
|
2019-03-15 15:57:07 -07:00
|
|
|
const self = @fieldParentPtr(DirectAllocator, "allocator", allocator);
|
2018-02-11 23:14:44 -08:00
|
|
|
const old_adjusted_addr = @ptrToInt(old_mem.ptr);
|
|
|
|
const old_record_addr = old_adjusted_addr + old_mem.len;
|
2018-05-31 07:56:59 -07:00
|
|
|
const root_addr = @intToPtr(*align(1) usize, old_record_addr).*;
|
2018-06-05 15:03:21 -07:00
|
|
|
const old_ptr = @intToPtr(*c_void, root_addr);
|
2019-04-20 10:24:40 -07:00
|
|
|
|
2019-04-22 02:20:01 -07:00
|
|
|
if(new_size == 0) {
|
|
|
|
if (os.windows.HeapFree(self.heap_handle.?, 0, old_ptr) == 0) unreachable;
|
2019-04-20 10:24:40 -07:00
|
|
|
return old_mem[0..0];
|
|
|
|
}
|
|
|
|
|
2019-03-15 14:47:47 -07:00
|
|
|
const amt = new_size + new_align + @sizeOf(usize);
|
|
|
|
const new_ptr = os.windows.HeapReAlloc(
|
|
|
|
self.heap_handle.?,
|
|
|
|
0,
|
|
|
|
old_ptr,
|
|
|
|
amt,
|
|
|
|
) orelse return error.OutOfMemory;
|
2018-02-11 23:14:44 -08:00
|
|
|
const offset = old_adjusted_addr - root_addr;
|
|
|
|
const new_root_addr = @ptrToInt(new_ptr);
|
2019-04-25 13:35:45 -07:00
|
|
|
var new_adjusted_addr = new_root_addr + offset;
|
|
|
|
const offset_is_valid = new_adjusted_addr + new_size + @sizeOf(usize) <= new_root_addr + amt;
|
|
|
|
const offset_is_aligned = new_adjusted_addr % new_align == 0;
|
|
|
|
if (!offset_is_valid or !offset_is_aligned) {
|
|
|
|
// If HeapReAlloc didn't happen to move the memory to the new alignment,
|
|
|
|
// or the memory starting at the old offset would be outside of the new allocation,
|
|
|
|
// then we need to copy the memory to a valid aligned address and use that
|
|
|
|
const new_aligned_addr = mem.alignForward(new_root_addr, new_align);
|
2019-04-22 02:20:01 -07:00
|
|
|
@memcpy(
|
2019-04-25 13:35:45 -07:00
|
|
|
@intToPtr([*]u8, new_aligned_addr),
|
2019-04-22 02:20:01 -07:00
|
|
|
@intToPtr([*]u8, new_adjusted_addr),
|
|
|
|
std.math.min(old_mem.len, new_size),
|
|
|
|
);
|
2019-04-25 13:35:45 -07:00
|
|
|
new_adjusted_addr = new_aligned_addr;
|
2019-04-22 02:20:01 -07:00
|
|
|
}
|
2018-02-11 23:14:44 -08:00
|
|
|
const new_record_addr = new_adjusted_addr + new_size;
|
2018-05-31 07:56:59 -07:00
|
|
|
@intToPtr(*align(1) usize, new_record_addr).* = new_root_addr;
|
2018-06-03 22:09:15 -07:00
|
|
|
return @intToPtr([*]u8, new_adjusted_addr)[0..new_size];
|
2017-10-31 01:47:55 -07:00
|
|
|
},
|
|
|
|
else => @compileError("Unsupported OS"),
|
|
|
|
}
|
|
|
|
}
|
2018-02-11 23:14:44 -08:00
|
|
|
};
|
2017-10-31 01:47:55 -07:00
|
|
|
|
2018-02-11 23:14:44 -08:00
|
|
|
/// This allocator takes an existing allocator, wraps it, and provides an interface
|
|
|
|
/// where you can allocate without freeing, and then free it all together.
|
2018-11-13 05:08:37 -08:00
|
|
|
pub const ArenaAllocator = struct {
|
2018-02-11 23:14:44 -08:00
|
|
|
pub allocator: Allocator,
|
|
|
|
|
2018-05-31 07:56:59 -07:00
|
|
|
child_allocator: *Allocator,
|
2018-02-11 23:14:44 -08:00
|
|
|
buffer_list: std.LinkedList([]u8),
|
|
|
|
end_index: usize,
|
|
|
|
|
|
|
|
const BufNode = std.LinkedList([]u8).Node;
|
|
|
|
|
2018-05-31 07:56:59 -07:00
|
|
|
pub fn init(child_allocator: *Allocator) ArenaAllocator {
|
2018-11-13 05:08:37 -08:00
|
|
|
return ArenaAllocator{
|
|
|
|
.allocator = Allocator{
|
2018-02-11 23:14:44 -08:00
|
|
|
.reallocFn = realloc,
|
2019-03-15 14:47:47 -07:00
|
|
|
.shrinkFn = shrink,
|
2018-02-11 23:14:44 -08:00
|
|
|
},
|
|
|
|
.child_allocator = child_allocator,
|
|
|
|
.buffer_list = std.LinkedList([]u8).init(),
|
|
|
|
.end_index = 0,
|
|
|
|
};
|
2017-10-31 01:47:55 -07:00
|
|
|
}
|
|
|
|
|
2018-05-31 07:56:59 -07:00
|
|
|
pub fn deinit(self: *ArenaAllocator) void {
|
2018-02-11 23:14:44 -08:00
|
|
|
var it = self.buffer_list.first;
|
|
|
|
while (it) |node| {
|
|
|
|
// this has to occur before the free because the free frees node
|
|
|
|
it = node.next;
|
|
|
|
|
|
|
|
self.child_allocator.free(node.data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-31 07:56:59 -07:00
|
|
|
fn createNode(self: *ArenaAllocator, prev_len: usize, minimum_size: usize) !*BufNode {
|
2018-02-11 23:14:44 -08:00
|
|
|
const actual_min_size = minimum_size + @sizeOf(BufNode);
|
|
|
|
var len = prev_len;
|
|
|
|
while (true) {
|
|
|
|
len += len / 2;
|
|
|
|
len += os.page_size - @rem(len, os.page_size);
|
|
|
|
if (len >= actual_min_size) break;
|
|
|
|
}
|
|
|
|
const buf = try self.child_allocator.alignedAlloc(u8, @alignOf(BufNode), len);
|
2018-06-18 14:25:29 -07:00
|
|
|
const buf_node_slice = @bytesToSlice(BufNode, buf[0..@sizeOf(BufNode)]);
|
2018-02-11 23:14:44 -08:00
|
|
|
const buf_node = &buf_node_slice[0];
|
2018-11-13 05:08:37 -08:00
|
|
|
buf_node.* = BufNode{
|
2018-02-11 23:14:44 -08:00
|
|
|
.data = buf,
|
|
|
|
.prev = null,
|
|
|
|
.next = null,
|
|
|
|
};
|
|
|
|
self.buffer_list.append(buf_node);
|
|
|
|
self.end_index = 0;
|
|
|
|
return buf_node;
|
2017-10-31 01:47:55 -07:00
|
|
|
}
|
|
|
|
|
2018-05-31 07:56:59 -07:00
|
|
|
fn alloc(allocator: *Allocator, n: usize, alignment: u29) ![]u8 {
|
2018-02-11 23:14:44 -08:00
|
|
|
const self = @fieldParentPtr(ArenaAllocator, "allocator", allocator);
|
|
|
|
|
|
|
|
var cur_node = if (self.buffer_list.last) |last_node| last_node else try self.createNode(0, n + alignment);
|
|
|
|
while (true) {
|
|
|
|
const cur_buf = cur_node.data[@sizeOf(BufNode)..];
|
2018-02-12 00:21:18 -08:00
|
|
|
const addr = @ptrToInt(cur_buf.ptr) + self.end_index;
|
2019-03-11 10:34:51 -07:00
|
|
|
const adjusted_addr = mem.alignForward(addr, alignment);
|
|
|
|
const adjusted_index = self.end_index + (adjusted_addr - addr);
|
2018-02-11 23:14:44 -08:00
|
|
|
const new_end_index = adjusted_index + n;
|
|
|
|
if (new_end_index > cur_buf.len) {
|
|
|
|
cur_node = try self.createNode(cur_buf.len, n + alignment);
|
|
|
|
continue;
|
|
|
|
}
|
2018-04-30 22:53:04 -07:00
|
|
|
const result = cur_buf[adjusted_index..new_end_index];
|
2018-02-11 23:14:44 -08:00
|
|
|
self.end_index = new_end_index;
|
|
|
|
return result;
|
2017-10-31 01:47:55 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-15 14:47:47 -07:00
|
|
|
fn realloc(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 {
|
|
|
|
if (new_size <= old_mem.len and new_align <= new_size) {
|
|
|
|
// We can't do anything with the memory, so tell the client to keep it.
|
|
|
|
return error.OutOfMemory;
|
2017-10-31 01:47:55 -07:00
|
|
|
} else {
|
2019-03-15 14:47:47 -07:00
|
|
|
const result = try alloc(allocator, new_size, new_align);
|
2019-04-25 13:41:19 -07:00
|
|
|
@memcpy(result.ptr, old_mem.ptr, std.math.min(old_mem.len, result.len));
|
2017-10-31 01:47:55 -07:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-15 14:47:47 -07:00
|
|
|
fn shrink(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 {
|
|
|
|
return old_mem[0..new_size];
|
|
|
|
}
|
2017-10-31 01:47:55 -07:00
|
|
|
};
|
|
|
|
|
2018-11-13 05:08:37 -08:00
|
|
|
pub const FixedBufferAllocator = struct {
|
2018-02-11 23:27:02 -08:00
|
|
|
allocator: Allocator,
|
|
|
|
end_index: usize,
|
|
|
|
buffer: []u8,
|
2018-02-11 23:14:44 -08:00
|
|
|
|
2018-02-11 23:27:02 -08:00
|
|
|
pub fn init(buffer: []u8) FixedBufferAllocator {
|
2018-11-13 05:08:37 -08:00
|
|
|
return FixedBufferAllocator{
|
|
|
|
.allocator = Allocator{
|
2018-02-11 23:27:02 -08:00
|
|
|
.reallocFn = realloc,
|
2019-03-15 14:47:47 -07:00
|
|
|
.shrinkFn = shrink,
|
2018-02-11 23:27:02 -08:00
|
|
|
},
|
|
|
|
.buffer = buffer,
|
|
|
|
.end_index = 0,
|
|
|
|
};
|
2017-11-05 09:27:56 -08:00
|
|
|
}
|
|
|
|
|
2018-05-31 07:56:59 -07:00
|
|
|
fn alloc(allocator: *Allocator, n: usize, alignment: u29) ![]u8 {
|
2018-02-11 23:27:02 -08:00
|
|
|
const self = @fieldParentPtr(FixedBufferAllocator, "allocator", allocator);
|
2018-04-28 14:53:06 -07:00
|
|
|
const addr = @ptrToInt(self.buffer.ptr) + self.end_index;
|
2019-03-11 10:34:51 -07:00
|
|
|
const adjusted_addr = mem.alignForward(addr, alignment);
|
|
|
|
const adjusted_index = self.end_index + (adjusted_addr - addr);
|
2018-02-11 23:27:02 -08:00
|
|
|
const new_end_index = adjusted_index + n;
|
|
|
|
if (new_end_index > self.buffer.len) {
|
|
|
|
return error.OutOfMemory;
|
|
|
|
}
|
2018-04-30 22:53:04 -07:00
|
|
|
const result = self.buffer[adjusted_index..new_end_index];
|
2018-02-11 23:27:02 -08:00
|
|
|
self.end_index = new_end_index;
|
2017-10-31 01:47:55 -07:00
|
|
|
|
2018-02-11 23:27:02 -08:00
|
|
|
return result;
|
|
|
|
}
|
2017-10-31 01:47:55 -07:00
|
|
|
|
2019-03-15 14:47:47 -07:00
|
|
|
fn realloc(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 {
|
2018-07-14 13:31:11 -07:00
|
|
|
const self = @fieldParentPtr(FixedBufferAllocator, "allocator", allocator);
|
|
|
|
assert(old_mem.len <= self.end_index);
|
2019-03-15 14:47:47 -07:00
|
|
|
if (old_mem.ptr == self.buffer.ptr + self.end_index - old_mem.len and
|
|
|
|
mem.alignForward(@ptrToInt(old_mem.ptr), new_align) == @ptrToInt(old_mem.ptr))
|
|
|
|
{
|
2018-07-14 13:31:11 -07:00
|
|
|
const start_index = self.end_index - old_mem.len;
|
|
|
|
const new_end_index = start_index + new_size;
|
|
|
|
if (new_end_index > self.buffer.len) return error.OutOfMemory;
|
|
|
|
const result = self.buffer[start_index..new_end_index];
|
|
|
|
self.end_index = new_end_index;
|
|
|
|
return result;
|
2019-03-15 14:47:47 -07:00
|
|
|
} else if (new_size <= old_mem.len and new_align <= old_align) {
|
|
|
|
// We can't do anything with the memory, so tell the client to keep it.
|
|
|
|
return error.OutOfMemory;
|
2018-02-11 23:27:02 -08:00
|
|
|
} else {
|
2019-03-15 14:47:47 -07:00
|
|
|
const result = try alloc(allocator, new_size, new_align);
|
2019-04-25 13:41:19 -07:00
|
|
|
@memcpy(result.ptr, old_mem.ptr, std.math.min(old_mem.len, result.len));
|
2018-02-11 23:27:02 -08:00
|
|
|
return result;
|
|
|
|
}
|
2017-10-31 01:47:55 -07:00
|
|
|
}
|
|
|
|
|
2019-03-15 14:47:47 -07:00
|
|
|
fn shrink(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 {
|
|
|
|
return old_mem[0..new_size];
|
|
|
|
}
|
2018-02-11 23:27:02 -08:00
|
|
|
};
|
|
|
|
|
2019-04-16 10:23:45 -07:00
|
|
|
// FIXME: Exposed LLVM intrinsics is a bug
|
|
|
|
// See: https://github.com/ziglang/zig/issues/2291
|
2019-04-15 23:40:16 -07:00
|
|
|
extern fn @"llvm.wasm.memory.size.i32"(u32) u32;
|
2019-04-15 19:21:46 -07:00
|
|
|
extern fn @"llvm.wasm.memory.grow.i32"(u32, u32) i32;
|
2019-04-15 23:40:16 -07:00
|
|
|
|
|
|
|
pub const wasm_allocator = &wasm_allocator_state.allocator;
|
|
|
|
var wasm_allocator_state = WasmAllocator{
|
|
|
|
.allocator = Allocator{
|
|
|
|
.reallocFn = WasmAllocator.realloc,
|
|
|
|
.shrinkFn = WasmAllocator.shrink,
|
|
|
|
},
|
|
|
|
.start_ptr = undefined,
|
|
|
|
.num_pages = 0,
|
|
|
|
.end_index = 0,
|
|
|
|
};
|
2019-04-15 19:21:46 -07:00
|
|
|
|
2019-04-16 10:23:45 -07:00
|
|
|
const WasmAllocator = struct {
|
2019-04-15 23:40:16 -07:00
|
|
|
allocator: Allocator,
|
|
|
|
start_ptr: [*]u8,
|
|
|
|
num_pages: usize,
|
|
|
|
end_index: usize,
|
2019-04-15 19:21:46 -07:00
|
|
|
|
2019-04-15 23:40:16 -07:00
|
|
|
fn alloc(allocator: *Allocator, size: usize, alignment: u29) ![]u8 {
|
|
|
|
const self = @fieldParentPtr(WasmAllocator, "allocator", allocator);
|
2019-04-15 19:21:46 -07:00
|
|
|
|
2019-04-15 23:40:16 -07:00
|
|
|
const addr = @ptrToInt(self.start_ptr) + self.end_index;
|
|
|
|
const adjusted_addr = mem.alignForward(addr, alignment);
|
|
|
|
const adjusted_index = self.end_index + (adjusted_addr - addr);
|
|
|
|
const new_end_index = adjusted_index + size;
|
2019-04-15 19:21:46 -07:00
|
|
|
|
2019-04-16 10:23:45 -07:00
|
|
|
if (new_end_index > self.num_pages * os.page_size) {
|
|
|
|
const required_memory = new_end_index - (self.num_pages * os.page_size);
|
2019-04-15 19:21:46 -07:00
|
|
|
|
2019-04-16 10:23:45 -07:00
|
|
|
var num_pages: u32 = required_memory / os.page_size;
|
|
|
|
if (required_memory % os.page_size != 0) {
|
2019-04-15 23:40:16 -07:00
|
|
|
num_pages += 1;
|
2019-04-15 19:21:46 -07:00
|
|
|
}
|
|
|
|
|
2019-04-15 23:40:16 -07:00
|
|
|
const prev_page = @"llvm.wasm.memory.grow.i32"(0, num_pages);
|
|
|
|
if (prev_page == -1) {
|
|
|
|
return error.OutOfMemory;
|
2019-04-15 19:21:46 -07:00
|
|
|
}
|
|
|
|
|
2019-04-15 23:40:16 -07:00
|
|
|
self.num_pages += num_pages;
|
|
|
|
}
|
|
|
|
|
|
|
|
const result = self.start_ptr[adjusted_index..new_end_index];
|
|
|
|
self.end_index = new_end_index;
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2019-04-16 10:23:45 -07:00
|
|
|
// Check if memory is the last "item" and is aligned correctly
|
2019-04-15 23:40:16 -07:00
|
|
|
fn is_last_item(allocator: *Allocator, memory: []u8, alignment: u29) bool {
|
|
|
|
const self = @fieldParentPtr(WasmAllocator, "allocator", allocator);
|
|
|
|
return memory.ptr == self.start_ptr + self.end_index - memory.len and mem.alignForward(@ptrToInt(memory.ptr), alignment) == @ptrToInt(memory.ptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn realloc(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 {
|
|
|
|
const self = @fieldParentPtr(WasmAllocator, "allocator", allocator);
|
|
|
|
|
|
|
|
// Initialize start_ptr at the first realloc
|
|
|
|
if (self.num_pages == 0) {
|
2019-04-16 10:23:45 -07:00
|
|
|
self.start_ptr = @intToPtr([*]u8, @intCast(usize, @"llvm.wasm.memory.size.i32"(0)) * os.page_size);
|
2019-04-15 23:40:16 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (is_last_item(allocator, old_mem, new_align)) {
|
|
|
|
const start_index = self.end_index - old_mem.len;
|
|
|
|
const new_end_index = start_index + new_size;
|
|
|
|
|
2019-04-16 10:23:45 -07:00
|
|
|
if (new_end_index > self.num_pages * os.page_size) {
|
2019-04-15 23:40:16 -07:00
|
|
|
_ = try alloc(allocator, new_end_index - self.end_index, new_align);
|
2019-04-15 19:21:46 -07:00
|
|
|
}
|
2019-04-15 23:40:16 -07:00
|
|
|
const result = self.start_ptr[start_index..new_end_index];
|
|
|
|
|
|
|
|
self.end_index = new_end_index;
|
|
|
|
return result;
|
|
|
|
} else if (new_size <= old_mem.len and new_align <= old_align) {
|
|
|
|
return error.OutOfMemory;
|
|
|
|
} else {
|
|
|
|
const result = try alloc(allocator, new_size, new_align);
|
|
|
|
mem.copy(u8, result, old_mem);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn shrink(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 {
|
2019-03-15 14:47:47 -07:00
|
|
|
return old_mem[0..new_size];
|
|
|
|
}
|
2018-02-11 23:27:02 -08:00
|
|
|
};
|
|
|
|
|
2019-02-07 12:28:37 -08:00
|
|
|
pub const ThreadSafeFixedBufferAllocator = blk: {
|
|
|
|
if (builtin.single_threaded) {
|
|
|
|
break :blk FixedBufferAllocator;
|
|
|
|
} else {
|
2019-03-11 10:34:51 -07:00
|
|
|
// lock free
|
2019-02-07 12:28:37 -08:00
|
|
|
break :blk struct {
|
|
|
|
allocator: Allocator,
|
|
|
|
end_index: usize,
|
|
|
|
buffer: []u8,
|
|
|
|
|
|
|
|
pub fn init(buffer: []u8) ThreadSafeFixedBufferAllocator {
|
|
|
|
return ThreadSafeFixedBufferAllocator{
|
|
|
|
.allocator = Allocator{
|
|
|
|
.reallocFn = realloc,
|
2019-03-15 14:47:47 -07:00
|
|
|
.shrinkFn = shrink,
|
2019-02-07 12:28:37 -08:00
|
|
|
},
|
|
|
|
.buffer = buffer,
|
|
|
|
.end_index = 0,
|
|
|
|
};
|
|
|
|
}
|
2018-04-28 14:53:06 -07:00
|
|
|
|
2019-02-07 12:28:37 -08:00
|
|
|
fn alloc(allocator: *Allocator, n: usize, alignment: u29) ![]u8 {
|
|
|
|
const self = @fieldParentPtr(ThreadSafeFixedBufferAllocator, "allocator", allocator);
|
|
|
|
var end_index = @atomicLoad(usize, &self.end_index, builtin.AtomicOrder.SeqCst);
|
|
|
|
while (true) {
|
|
|
|
const addr = @ptrToInt(self.buffer.ptr) + end_index;
|
2019-03-11 10:34:51 -07:00
|
|
|
const adjusted_addr = mem.alignForward(addr, alignment);
|
|
|
|
const adjusted_index = end_index + (adjusted_addr - addr);
|
2019-02-07 12:28:37 -08:00
|
|
|
const new_end_index = adjusted_index + n;
|
|
|
|
if (new_end_index > self.buffer.len) {
|
|
|
|
return error.OutOfMemory;
|
|
|
|
}
|
|
|
|
end_index = @cmpxchgWeak(usize, &self.end_index, end_index, new_end_index, builtin.AtomicOrder.SeqCst, builtin.AtomicOrder.SeqCst) orelse return self.buffer[adjusted_index..new_end_index];
|
|
|
|
}
|
|
|
|
}
|
2018-04-28 14:53:06 -07:00
|
|
|
|
2019-03-15 14:47:47 -07:00
|
|
|
fn realloc(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 {
|
|
|
|
if (new_size <= old_mem.len and new_align <= old_align) {
|
|
|
|
// We can't do anything useful with the memory, tell the client to keep it.
|
|
|
|
return error.OutOfMemory;
|
2019-02-07 12:28:37 -08:00
|
|
|
} else {
|
2019-03-15 14:47:47 -07:00
|
|
|
const result = try alloc(allocator, new_size, new_align);
|
2019-04-25 13:41:19 -07:00
|
|
|
@memcpy(result.ptr, old_mem.ptr, std.math.min(old_mem.len, result.len));
|
2019-02-07 12:28:37 -08:00
|
|
|
return result;
|
|
|
|
}
|
2018-04-28 14:53:06 -07:00
|
|
|
}
|
|
|
|
|
2019-03-15 14:47:47 -07:00
|
|
|
fn shrink(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 {
|
|
|
|
return old_mem[0..new_size];
|
|
|
|
}
|
2019-02-07 12:28:37 -08:00
|
|
|
};
|
2018-04-28 14:53:06 -07:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2018-07-06 22:23:18 -07:00
|
|
|
pub fn stackFallback(comptime size: usize, fallback_allocator: *Allocator) StackFallbackAllocator(size) {
|
2018-11-13 05:08:37 -08:00
|
|
|
return StackFallbackAllocator(size){
|
2018-07-06 22:23:18 -07:00
|
|
|
.buffer = undefined,
|
|
|
|
.fallback_allocator = fallback_allocator,
|
|
|
|
.fixed_buffer_allocator = undefined,
|
2018-11-13 05:08:37 -08:00
|
|
|
.allocator = Allocator{
|
2018-07-06 22:23:18 -07:00
|
|
|
.reallocFn = StackFallbackAllocator(size).realloc,
|
2019-03-15 14:47:47 -07:00
|
|
|
.shrinkFn = StackFallbackAllocator(size).shrink,
|
2018-07-06 22:23:18 -07:00
|
|
|
},
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn StackFallbackAllocator(comptime size: usize) type {
|
2018-11-13 05:08:37 -08:00
|
|
|
return struct {
|
2018-09-13 13:34:33 -07:00
|
|
|
const Self = @This();
|
2018-07-06 22:23:18 -07:00
|
|
|
|
|
|
|
buffer: [size]u8,
|
|
|
|
allocator: Allocator,
|
|
|
|
fallback_allocator: *Allocator,
|
|
|
|
fixed_buffer_allocator: FixedBufferAllocator,
|
|
|
|
|
|
|
|
pub fn get(self: *Self) *Allocator {
|
|
|
|
self.fixed_buffer_allocator = FixedBufferAllocator.init(self.buffer[0..]);
|
|
|
|
return &self.allocator;
|
|
|
|
}
|
|
|
|
|
2019-03-15 14:47:47 -07:00
|
|
|
fn realloc(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 {
|
2018-07-06 22:23:18 -07:00
|
|
|
const self = @fieldParentPtr(Self, "allocator", allocator);
|
|
|
|
const in_buffer = @ptrToInt(old_mem.ptr) >= @ptrToInt(&self.buffer) and
|
|
|
|
@ptrToInt(old_mem.ptr) < @ptrToInt(&self.buffer) + self.buffer.len;
|
|
|
|
if (in_buffer) {
|
|
|
|
return FixedBufferAllocator.realloc(
|
|
|
|
&self.fixed_buffer_allocator.allocator,
|
|
|
|
old_mem,
|
2019-03-15 14:47:47 -07:00
|
|
|
old_align,
|
2018-07-06 22:23:18 -07:00
|
|
|
new_size,
|
2019-03-15 14:47:47 -07:00
|
|
|
new_align,
|
2018-07-06 22:23:18 -07:00
|
|
|
) catch {
|
2019-03-15 14:47:47 -07:00
|
|
|
const result = try self.fallback_allocator.reallocFn(
|
2018-07-06 22:23:18 -07:00
|
|
|
self.fallback_allocator,
|
2019-03-15 14:47:47 -07:00
|
|
|
([*]u8)(undefined)[0..0],
|
|
|
|
undefined,
|
2018-07-06 22:23:18 -07:00
|
|
|
new_size,
|
2019-03-15 14:47:47 -07:00
|
|
|
new_align,
|
2018-07-06 22:23:18 -07:00
|
|
|
);
|
|
|
|
mem.copy(u8, result, old_mem);
|
|
|
|
return result;
|
|
|
|
};
|
|
|
|
}
|
2019-03-15 14:47:47 -07:00
|
|
|
return self.fallback_allocator.reallocFn(
|
|
|
|
self.fallback_allocator,
|
|
|
|
old_mem,
|
|
|
|
old_align,
|
|
|
|
new_size,
|
|
|
|
new_align,
|
|
|
|
);
|
2018-07-06 22:23:18 -07:00
|
|
|
}
|
|
|
|
|
2019-03-15 14:47:47 -07:00
|
|
|
fn shrink(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 {
|
2018-07-06 22:23:18 -07:00
|
|
|
const self = @fieldParentPtr(Self, "allocator", allocator);
|
2019-03-15 14:47:47 -07:00
|
|
|
const in_buffer = @ptrToInt(old_mem.ptr) >= @ptrToInt(&self.buffer) and
|
|
|
|
@ptrToInt(old_mem.ptr) < @ptrToInt(&self.buffer) + self.buffer.len;
|
|
|
|
if (in_buffer) {
|
|
|
|
return FixedBufferAllocator.shrink(
|
|
|
|
&self.fixed_buffer_allocator.allocator,
|
|
|
|
old_mem,
|
|
|
|
old_align,
|
|
|
|
new_size,
|
|
|
|
new_align,
|
|
|
|
);
|
2018-07-06 22:23:18 -07:00
|
|
|
}
|
2019-03-15 14:47:47 -07:00
|
|
|
return self.fallback_allocator.shrinkFn(
|
|
|
|
self.fallback_allocator,
|
|
|
|
old_mem,
|
|
|
|
old_align,
|
|
|
|
new_size,
|
|
|
|
new_align,
|
|
|
|
);
|
2018-07-06 22:23:18 -07:00
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2018-02-11 23:27:02 -08:00
|
|
|
test "c_allocator" {
|
|
|
|
if (builtin.link_libc) {
|
2019-03-15 14:47:47 -07:00
|
|
|
var slice = try c_allocator.alloc(u8, 50);
|
2018-02-11 23:27:02 -08:00
|
|
|
defer c_allocator.free(slice);
|
2019-03-15 14:47:47 -07:00
|
|
|
slice = try c_allocator.realloc(slice, 100);
|
2018-02-11 23:27:02 -08:00
|
|
|
}
|
2017-10-31 01:47:55 -07:00
|
|
|
}
|
|
|
|
|
2018-02-11 23:14:44 -08:00
|
|
|
test "DirectAllocator" {
|
|
|
|
var direct_allocator = DirectAllocator.init();
|
|
|
|
defer direct_allocator.deinit();
|
|
|
|
|
|
|
|
const allocator = &direct_allocator.allocator;
|
|
|
|
try testAllocator(allocator);
|
2018-07-14 09:03:06 -07:00
|
|
|
try testAllocatorAligned(allocator, 16);
|
2018-04-21 18:41:49 -07:00
|
|
|
try testAllocatorLargeAlignment(allocator);
|
2019-04-19 17:54:53 -07:00
|
|
|
try testAllocatorAlignedShrink(allocator);
|
2018-02-11 23:14:44 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
test "ArenaAllocator" {
|
|
|
|
var direct_allocator = DirectAllocator.init();
|
|
|
|
defer direct_allocator.deinit();
|
|
|
|
|
|
|
|
var arena_allocator = ArenaAllocator.init(&direct_allocator.allocator);
|
|
|
|
defer arena_allocator.deinit();
|
|
|
|
|
|
|
|
try testAllocator(&arena_allocator.allocator);
|
2018-07-14 09:03:06 -07:00
|
|
|
try testAllocatorAligned(&arena_allocator.allocator, 16);
|
2018-04-21 18:41:49 -07:00
|
|
|
try testAllocatorLargeAlignment(&arena_allocator.allocator);
|
2019-04-19 17:54:53 -07:00
|
|
|
try testAllocatorAlignedShrink(&arena_allocator.allocator);
|
2018-02-11 23:14:44 -08:00
|
|
|
}
|
|
|
|
|
2019-04-22 02:25:57 -07:00
|
|
|
var test_fixed_buffer_allocator_memory: [40000 * @sizeOf(usize)]u8 = undefined;
|
2018-02-11 23:27:02 -08:00
|
|
|
test "FixedBufferAllocator" {
|
|
|
|
var fixed_buffer_allocator = FixedBufferAllocator.init(test_fixed_buffer_allocator_memory[0..]);
|
|
|
|
|
|
|
|
try testAllocator(&fixed_buffer_allocator.allocator);
|
2018-07-14 09:03:06 -07:00
|
|
|
try testAllocatorAligned(&fixed_buffer_allocator.allocator, 16);
|
2018-04-21 18:41:49 -07:00
|
|
|
try testAllocatorLargeAlignment(&fixed_buffer_allocator.allocator);
|
2019-04-19 17:54:53 -07:00
|
|
|
try testAllocatorAlignedShrink(&fixed_buffer_allocator.allocator);
|
2018-02-11 23:27:02 -08:00
|
|
|
}
|
|
|
|
|
2018-07-14 13:31:11 -07:00
|
|
|
test "FixedBufferAllocator Reuse memory on realloc" {
|
|
|
|
var small_fixed_buffer: [10]u8 = undefined;
|
|
|
|
// check if we re-use the memory
|
|
|
|
{
|
|
|
|
var fixed_buffer_allocator = FixedBufferAllocator.init(small_fixed_buffer[0..]);
|
|
|
|
|
|
|
|
var slice0 = try fixed_buffer_allocator.allocator.alloc(u8, 5);
|
2019-02-08 15:18:47 -08:00
|
|
|
testing.expect(slice0.len == 5);
|
2019-03-15 14:47:47 -07:00
|
|
|
var slice1 = try fixed_buffer_allocator.allocator.realloc(slice0, 10);
|
2019-02-08 15:18:47 -08:00
|
|
|
testing.expect(slice1.ptr == slice0.ptr);
|
|
|
|
testing.expect(slice1.len == 10);
|
2019-03-15 14:47:47 -07:00
|
|
|
testing.expectError(error.OutOfMemory, fixed_buffer_allocator.allocator.realloc(slice1, 11));
|
2018-07-14 13:31:11 -07:00
|
|
|
}
|
|
|
|
// check that we don't re-use the memory if it's not the most recent block
|
|
|
|
{
|
|
|
|
var fixed_buffer_allocator = FixedBufferAllocator.init(small_fixed_buffer[0..]);
|
|
|
|
|
|
|
|
var slice0 = try fixed_buffer_allocator.allocator.alloc(u8, 2);
|
|
|
|
slice0[0] = 1;
|
|
|
|
slice0[1] = 2;
|
|
|
|
var slice1 = try fixed_buffer_allocator.allocator.alloc(u8, 2);
|
2019-03-15 14:47:47 -07:00
|
|
|
var slice2 = try fixed_buffer_allocator.allocator.realloc(slice0, 4);
|
2019-02-08 15:18:47 -08:00
|
|
|
testing.expect(slice0.ptr != slice2.ptr);
|
|
|
|
testing.expect(slice1.ptr != slice2.ptr);
|
|
|
|
testing.expect(slice2[0] == 1);
|
|
|
|
testing.expect(slice2[1] == 2);
|
2018-07-14 13:31:11 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-28 14:53:06 -07:00
|
|
|
test "ThreadSafeFixedBufferAllocator" {
|
|
|
|
var fixed_buffer_allocator = ThreadSafeFixedBufferAllocator.init(test_fixed_buffer_allocator_memory[0..]);
|
|
|
|
|
|
|
|
try testAllocator(&fixed_buffer_allocator.allocator);
|
2018-07-14 09:03:06 -07:00
|
|
|
try testAllocatorAligned(&fixed_buffer_allocator.allocator, 16);
|
2018-04-28 14:53:06 -07:00
|
|
|
try testAllocatorLargeAlignment(&fixed_buffer_allocator.allocator);
|
2019-04-19 17:54:53 -07:00
|
|
|
try testAllocatorAlignedShrink(&fixed_buffer_allocator.allocator);
|
2018-04-28 14:53:06 -07:00
|
|
|
}
|
|
|
|
|
2018-05-31 07:56:59 -07:00
|
|
|
fn testAllocator(allocator: *mem.Allocator) !void {
|
|
|
|
var slice = try allocator.alloc(*i32, 100);
|
2019-02-08 15:18:47 -08:00
|
|
|
testing.expect(slice.len == 100);
|
2018-02-11 23:14:44 -08:00
|
|
|
for (slice) |*item, i| {
|
2019-02-03 13:13:28 -08:00
|
|
|
item.* = try allocator.create(i32);
|
|
|
|
item.*.* = @intCast(i32, i);
|
2018-02-11 23:14:44 -08:00
|
|
|
}
|
|
|
|
|
2019-03-15 14:47:47 -07:00
|
|
|
slice = try allocator.realloc(slice, 20000);
|
2019-02-08 15:18:47 -08:00
|
|
|
testing.expect(slice.len == 20000);
|
2018-07-14 13:31:11 -07:00
|
|
|
|
|
|
|
for (slice[0..100]) |item, i| {
|
2019-02-08 15:18:47 -08:00
|
|
|
testing.expect(item.* == @intCast(i32, i));
|
2018-02-11 23:14:44 -08:00
|
|
|
allocator.destroy(item);
|
|
|
|
}
|
|
|
|
|
2019-03-15 14:47:47 -07:00
|
|
|
slice = allocator.shrink(slice, 50);
|
2019-02-08 15:18:47 -08:00
|
|
|
testing.expect(slice.len == 50);
|
2019-03-15 14:47:47 -07:00
|
|
|
slice = allocator.shrink(slice, 25);
|
2019-02-08 15:18:47 -08:00
|
|
|
testing.expect(slice.len == 25);
|
2019-03-15 14:47:47 -07:00
|
|
|
slice = allocator.shrink(slice, 0);
|
2019-02-08 15:18:47 -08:00
|
|
|
testing.expect(slice.len == 0);
|
2019-03-15 14:47:47 -07:00
|
|
|
slice = try allocator.realloc(slice, 10);
|
2019-02-08 15:18:47 -08:00
|
|
|
testing.expect(slice.len == 10);
|
2018-02-11 23:14:44 -08:00
|
|
|
|
|
|
|
allocator.free(slice);
|
|
|
|
}
|
2018-04-21 18:41:49 -07:00
|
|
|
|
2018-07-14 09:03:06 -07:00
|
|
|
fn testAllocatorAligned(allocator: *mem.Allocator, comptime alignment: u29) !void {
|
|
|
|
// initial
|
|
|
|
var slice = try allocator.alignedAlloc(u8, alignment, 10);
|
2019-02-08 15:18:47 -08:00
|
|
|
testing.expect(slice.len == 10);
|
2018-07-14 09:03:06 -07:00
|
|
|
// grow
|
2019-03-15 14:47:47 -07:00
|
|
|
slice = try allocator.realloc(slice, 100);
|
2019-02-08 15:18:47 -08:00
|
|
|
testing.expect(slice.len == 100);
|
2018-07-14 09:03:06 -07:00
|
|
|
// shrink
|
2019-03-15 14:47:47 -07:00
|
|
|
slice = allocator.shrink(slice, 10);
|
2019-02-08 15:18:47 -08:00
|
|
|
testing.expect(slice.len == 10);
|
2018-07-14 09:03:06 -07:00
|
|
|
// go to zero
|
2019-03-15 14:47:47 -07:00
|
|
|
slice = allocator.shrink(slice, 0);
|
2019-02-08 15:18:47 -08:00
|
|
|
testing.expect(slice.len == 0);
|
2018-07-14 09:03:06 -07:00
|
|
|
// realloc from zero
|
2019-03-15 14:47:47 -07:00
|
|
|
slice = try allocator.realloc(slice, 100);
|
2019-02-08 15:18:47 -08:00
|
|
|
testing.expect(slice.len == 100);
|
2018-07-14 09:03:06 -07:00
|
|
|
// shrink with shrink
|
2019-03-15 14:47:47 -07:00
|
|
|
slice = allocator.shrink(slice, 10);
|
2019-02-08 15:18:47 -08:00
|
|
|
testing.expect(slice.len == 10);
|
2018-07-14 09:03:06 -07:00
|
|
|
// shrink to zero
|
2019-03-15 14:47:47 -07:00
|
|
|
slice = allocator.shrink(slice, 0);
|
2019-02-08 15:18:47 -08:00
|
|
|
testing.expect(slice.len == 0);
|
2018-07-14 09:03:06 -07:00
|
|
|
}
|
|
|
|
|
2018-05-31 07:56:59 -07:00
|
|
|
fn testAllocatorLargeAlignment(allocator: *mem.Allocator) mem.Allocator.Error!void {
|
2018-05-28 17:23:55 -07:00
|
|
|
//Maybe a platform's page_size is actually the same as or
|
2018-04-21 18:41:49 -07:00
|
|
|
// very near usize?
|
2018-10-26 11:59:58 -07:00
|
|
|
if (os.page_size << 2 > maxInt(usize)) return;
|
2018-04-30 22:53:04 -07:00
|
|
|
|
2018-04-21 18:41:49 -07:00
|
|
|
const USizeShift = @IntType(false, std.math.log2(usize.bit_count));
|
|
|
|
const large_align = u29(os.page_size << 2);
|
2018-04-30 22:53:04 -07:00
|
|
|
|
2018-04-21 18:41:49 -07:00
|
|
|
var align_mask: usize = undefined;
|
|
|
|
_ = @shlWithOverflow(usize, ~usize(0), USizeShift(@ctz(large_align)), &align_mask);
|
2018-04-30 22:53:04 -07:00
|
|
|
|
2019-03-15 14:47:47 -07:00
|
|
|
var slice = try allocator.alignedAlloc(u8, large_align, 500);
|
2019-02-08 15:18:47 -08:00
|
|
|
testing.expect(@ptrToInt(slice.ptr) & align_mask == @ptrToInt(slice.ptr));
|
2018-04-30 22:53:04 -07:00
|
|
|
|
2019-03-15 14:47:47 -07:00
|
|
|
slice = allocator.shrink(slice, 100);
|
2019-02-08 15:18:47 -08:00
|
|
|
testing.expect(@ptrToInt(slice.ptr) & align_mask == @ptrToInt(slice.ptr));
|
2018-04-30 22:53:04 -07:00
|
|
|
|
2019-03-15 14:47:47 -07:00
|
|
|
slice = try allocator.realloc(slice, 5000);
|
2019-02-08 15:18:47 -08:00
|
|
|
testing.expect(@ptrToInt(slice.ptr) & align_mask == @ptrToInt(slice.ptr));
|
2018-04-30 22:53:04 -07:00
|
|
|
|
2019-03-15 14:47:47 -07:00
|
|
|
slice = allocator.shrink(slice, 10);
|
2019-02-08 15:18:47 -08:00
|
|
|
testing.expect(@ptrToInt(slice.ptr) & align_mask == @ptrToInt(slice.ptr));
|
2018-04-30 22:53:04 -07:00
|
|
|
|
2019-03-15 14:47:47 -07:00
|
|
|
slice = try allocator.realloc(slice, 20000);
|
2019-02-08 15:18:47 -08:00
|
|
|
testing.expect(@ptrToInt(slice.ptr) & align_mask == @ptrToInt(slice.ptr));
|
2018-04-21 18:41:49 -07:00
|
|
|
|
|
|
|
allocator.free(slice);
|
|
|
|
}
|
2019-04-19 17:54:53 -07:00
|
|
|
|
|
|
|
fn testAllocatorAlignedShrink(allocator: *mem.Allocator) mem.Allocator.Error!void {
|
|
|
|
var debug_buffer: [1000]u8 = undefined;
|
|
|
|
const debug_allocator = &FixedBufferAllocator.init(&debug_buffer).allocator;
|
|
|
|
|
|
|
|
const alloc_size = os.page_size * 2 + 50;
|
|
|
|
var slice = try allocator.alignedAlloc(u8, 16, alloc_size);
|
|
|
|
defer allocator.free(slice);
|
|
|
|
|
|
|
|
var stuff_to_free = std.ArrayList([]align(16) u8).init(debug_allocator);
|
|
|
|
while (@ptrToInt(slice.ptr) == mem.alignForward(@ptrToInt(slice.ptr), os.page_size * 2)) {
|
|
|
|
try stuff_to_free.append(slice);
|
|
|
|
slice = try allocator.alignedAlloc(u8, 16, alloc_size);
|
|
|
|
}
|
|
|
|
while (stuff_to_free.popOrNull()) |item| {
|
|
|
|
allocator.free(item);
|
|
|
|
}
|
|
|
|
slice[0] = 0x12;
|
|
|
|
slice[60] = 0x34;
|
|
|
|
|
|
|
|
// realloc to a smaller size but with a larger alignment
|
|
|
|
slice = try allocator.alignedRealloc(slice, os.page_size * 2, alloc_size / 2);
|
|
|
|
testing.expect(slice[0] == 0x12);
|
|
|
|
testing.expect(slice[60] == 0x34);
|
|
|
|
}
|