Skip to documentation
SLOP

tiny.bumpalo.BumpAllocator

Reference tiny.bumpalo BumpAllocator

Defined in tiny.bumpalo.

Called byCallstest sourcelib.bumpalo.src.bumptest: zero-length direct allocations ...test; no linktools.smg.src.scan.testtest: zig public projection admits pa...private sourcelib.bumpalo.src.chunkallocFasttiny.bumpaloBumpAllocator
Static calls · unresolved targets: 28 · external targets: 22.

Source

Source: lib/bumpalo/src/bump.zig:32

zig
pub fn BumpAllocator(comptime min_alignment: usize) type {    comptime {        if (!std.math.isPowerOfTwo(min_alignment)) {            @compileError("min_alignment must be a power of two");        }        if (min_alignment > chunk_alignment) {            @compileError("min_alignment may not be larger than chunk_alignment");        }    }    return struct {        const chunk_header_alignment = @max(chunk_alignment, @alignOf(Chunk));        const minimum_alignment = min_alignment;        const metadata_size = std.mem.alignForward(usize, @sizeOf(Chunk), chunk_alignment);        const overhead = std.mem.alignForward(usize, malloc_overhead + metadata_size, chunk_alignment);        pub const default_chunk_capacity = first_allocation_goal - overhead;        backing_allocator: Allocator,        current: ?*Chunk = null,        backing_data_capacity_limit: ?usize = null,        chunk_growth_goal_bytes: ?usize = null,        caller_root: CallerRootState = .none,        observation_id: if (observe.enabled) u64 else void,        observation_generation: if (observe.enabled) u64 else void =            if (observe.enabled) 0 else {},        const Self = @This();        pub const ChunkIterator: type = chunk_mod.Iterator;        pub const ResetMode: type = ResetPolicy;        pub fn init(backing_allocator: Allocator) Self {            return .{                .backing_allocator = backing_allocator,                .observation_id = if (comptime observe.enabled)                    observe.producerId()                else {},            };        }        pub inline fn observationId(arena: *const Self) u64 {            if (comptime !observe.enabled) return 0;            return arena.observation_id;        }        pub inline fn observationIdentity(            arena: *const Self,        ) observe.Identity {            if (comptime !observe.enabled) {                return .{                    .producer_id = 0,                    .producer = .bump,                    .generation = 0,                    .owner_cookie = 0,                };            }            return observe.Identity.movable(                arena.observation_id,                .bump,                arena.observation_generation,            );        }        pub fn initCapacity(backing_allocator: Allocator, capacity: usize) Allocator.Error!Self {            var arena = Self.init(backing_allocator);            if (capacity != 0) {                arena.current = try arena.newChunk(capacity, capacity, .@"1", null, null);                assert(arena.queryCapacity() >= capacity);            }            return arena;        }        pub fn deinit(arena: *Self) void {            var span = observe.beginLifecycle(                arena.observationIdentity(),                .end,                .deinit,                @returnAddress(),            );            arena.assertCallerRootInvariant();            chunk_mod.destroyList(arena.backing_allocator, arena.current);            arena.current = null;            arena.caller_root = .none;            span.finish(.{ .succeeded = true });        }        pub fn allocator(arena: *Self) Allocator {            const Table = allocator_vtable.VTable(Self);            return .{                .ptr = arena,                .vtable = &Table.vtable,            };        }        pub fn minAlign(_: *const Self) usize {            return min_alignment;        }        pub fn backingDataCapacityLimit(arena: *const Self) ?usize {            return arena.backing_data_capacity_limit;        }        pub fn setBackingDataCapacityLimit(arena: *Self, limit: ?usize) void {            arena.backing_data_capacity_limit = limit;        }        pub fn chunkGrowthGoal(arena: *const Self) ?usize {            return arena.chunk_growth_goal_bytes;        }        pub fn setChunkGrowthGoal(arena: *Self, goal: ?usize) void {            if (goal) |bytes| assert(bytes >= default_chunk_capacity);            arena.chunk_growth_goal_bytes = goal;        }        pub fn reset(arena: *Self, mode: ResetMode) bool {            var span = observe.beginLifecycle(                arena.observationIdentity(),                .invalidate,                .reset,                @returnAddress(),            );            const succeeded = switch (mode) {                .free_all => blk: {                    arena.freeAllChunks();                    break :blk true;                },                .retain_current => blk: {                    arena.retainCurrentChunk();                    break :blk true;                },                .retain_capacity => arena.retainSingleChunk(                    arena.queryCapacity(),                    null,                ),                .retain_with_limit => |limit| arena.retainSingleChunk(                    @min(arena.queryCapacity(), limit),                    limit,                ),            };            span.finish(.{ .succeeded = succeeded });            arena.advanceObservationGeneration();            return succeeded;        }        pub fn queryCapacity(arena: *const Self) usize {            const current = arena.current orelse return 0;            return current.total_capacity;        }        pub fn queryCallerDataCapacity(arena: *const Self) usize {            return switch (arena.caller_root) {                .linked => |root| root.capacity,                .none, .detached => 0,            };        }        pub fn queryBackingDataCapacity(arena: *const Self) usize {            const capacity = arena.queryCapacity();            const caller_capacity = arena.queryCallerDataCapacity();            assert(caller_capacity <= capacity);            return capacity - caller_capacity;        }        pub fn queryUsedCapacity(arena: *const Self) usize {            var used: usize = 0;            var chunk = arena.current;            while (chunk) |c| : (chunk = c.previous) {                used += c.usedSlice().len;            }            return used;        }        pub fn queryCurrentChunkAvailable(arena: *const Self) usize {            const current = arena.current orelse return 0;            return current.remaining();        }        pub fn queryCapacityIncludingMetadata(arena: *const Self) usize {            var bytes: usize = 0;            var chunk = arena.current;            while (chunk) |c| : (chunk = c.previous) {                bytes += chunk_mod.allocationFootprint(c);            }            return bytes;        }        pub fn clearAndFree(arena: *Self) void {            var span = observe.beginLifecycle(                arena.observationIdentity(),                .invalidate,                .clear_and_free,                @returnAddress(),            );            arena.freeAllChunks();            span.finish(.{ .succeeded = true });            arena.advanceObservationGeneration();        }        pub fn clearRetainingCurrent(arena: *Self) void {            var span = observe.beginLifecycle(                arena.observationIdentity(),                .invalidate,                .clear_retaining_current,                @returnAddress(),            );            arena.retainCurrentChunk();            span.finish(.{ .succeeded = true });            arena.advanceObservationGeneration();        }        pub fn clearRetainingLargest(arena: *Self) void {            var span = observe.beginLifecycle(                arena.observationIdentity(),                .invalidate,                .clear_retaining_largest,                @returnAddress(),            );            arena.retainLargestChunk();            span.finish(.{ .succeeded = true });            arena.advanceObservationGeneration();        }        pub fn clearRetainingCapacity(arena: *Self) bool {            var span = observe.beginLifecycle(                arena.observationIdentity(),                .invalidate,                .clear_retaining_capacity,                @returnAddress(),            );            const succeeded = arena.retainSingleChunk(                arena.queryCapacity(),                null,            );            span.finish(.{ .succeeded = succeeded });            arena.advanceObservationGeneration();            return succeeded;        }        pub fn clearRetainingCapacityLimit(arena: *Self, limit: usize) bool {            var span = observe.beginLifecycle(                arena.observationIdentity(),                .invalidate,                .clear_retaining_capacity_limit,                @returnAddress(),            );            const succeeded = arena.retainSingleChunk(                @min(arena.queryCapacity(), limit),                limit,            );            span.finish(.{ .succeeded = succeeded });            arena.advanceObservationGeneration();            return succeeded;        }        fn advanceObservationGeneration(arena: *Self) void {            if (comptime !observe.enabled) return;            arena.observation_generation = std.math.add(                u64,                arena.observation_generation,                1,            ) catch @panic("allocator observation generation exhausted");        }        fn freeAllChunks(arena: *Self) void {            arena.assertCallerRootInvariant();            chunk_mod.destroyList(arena.backing_allocator, arena.current);            arena.current = null;            arena.restoreCallerRoot();            arena.assertCallerRootInvariant();        }        fn retainCurrentChunk(arena: *Self) void {            arena.assertCallerRootInvariant();            const current = arena.current orelse return;            chunk_mod.destroyList(arena.backing_allocator, current.previous);            current.previous = null;            current.setCursor(current.dataEnd());            current.total_capacity = current.capacity;            switch (arena.caller_root) {                .linked => |root| {                    if (current != root) arena.caller_root = .{ .detached = root };                },                .none, .detached => {},            }            assert(current.remaining() == current.capacity);            assert(arena.queryCapacity() == arena.queryCurrentChunkAvailable());            arena.assertCallerRootInvariant();        }        fn retainLargestChunk(arena: *Self) void {            switch (arena.caller_root) {                .none => {},                .linked, .detached => {                    arena.freeAllChunks();                    return;                },            }            const current = arena.current orelse return;            var retained = current;            var candidate = current.previous;            while (candidate) |chunk| : (candidate = chunk.previous) {                if (chunk.capacity > retained.capacity) retained = chunk;            }            chunk_mod.destroyUntil(arena.backing_allocator, current, retained);            chunk_mod.destroyList(arena.backing_allocator, retained.previous);            retained.previous = null;            retained.setCursor(retained.dataEnd());            retained.total_capacity = retained.capacity;            arena.current = retained;            assert(retained.remaining() == retained.capacity);            assert(arena.queryCapacity() == arena.queryCurrentChunkAvailable());        }        fn retainSingleChunk(arena: *Self, requested_capacity: usize, max_capacity: ?usize) bool {            arena.assertCallerRootInvariant();            const bounded_capacity = if (max_capacity) |limit|                @min(requested_capacity, limit)            else                requested_capacity;            if (bounded_capacity == 0) {                arena.clearLiveAndDetachCallerRoot();                arena.assertCallerRootInvariant();                return true;            }            if (max_capacity) |limit| {                const aligned_limit = std.mem.alignBackward(                    usize,                    limit,                    @max(chunk_header_alignment, min_alignment),                );                if (aligned_limit == 0) {                    arena.clearLiveAndDetachCallerRoot();                    arena.assertCallerRootInvariant();                    return true;                }            }            assert(bounded_capacity > 0);            if (arena.current) |current| {                if (current.previous == null and current.capacity >= bounded_capacity) {                    if (max_capacity == null or current.capacity <= max_capacity.?) {                        current.setCursor(current.dataEnd());                        current.total_capacity = current.capacity;                        assert(current.remaining() == current.capacity);                        arena.assertCallerRootInvariant();                        return true;                    }                }            }            arena.clearLiveAndDetachCallerRoot();            const backing_capacity_remaining =                arena.backingDataCapacityRemaining();            if (backing_capacity_remaining) |remaining| {                const aligned_remaining = std.mem.alignBackward(                    usize,                    remaining,                    @max(chunk_header_alignment, min_alignment),                );                if (max_capacity != null and aligned_remaining == 0) {                    arena.assertCallerRootInvariant();                    return true;                }            }            arena.current = arena.newChunkBounded(                bounded_capacity,                if (max_capacity == null) bounded_capacity else 0,                .@"1",                null,                max_capacity,                backing_capacity_remaining,            ) catch {                arena.restoreCallerRoot();                arena.assertCallerRootInvariant();                return false;            };            arena.assertCallerRootInvariant();            return true;        }        fn clearLiveAndDetachCallerRoot(arena: *Self) void {            chunk_mod.destroyList(arena.backing_allocator, arena.current);            arena.current = null;            switch (arena.caller_root) {                .linked => |root| arena.caller_root = .{ .detached = root },                .none, .detached => {},            }        }        fn restoreCallerRoot(arena: *Self) void {            switch (arena.caller_root) {                .none => {},                .linked, .detached => |root| {                    root.previous = null;                    root.setCursor(root.dataEnd());                    root.total_capacity = root.capacity;                    arena.current = root;                    arena.caller_root = .{ .linked = root };                },            }        }        fn assertCallerRootInvariant(arena: *const Self) void {            switch (arena.caller_root) {                .none => {},                .linked => |root| {                    assert(root.previous == null);                    assert(arena.chunkReachable(root));                },                .detached => |root| {                    assert(root.previous == null);                    assert(!arena.chunkReachable(root));                },            }        }        fn chunkReachable(arena: *const Self, target: *Chunk) bool {            var chunk = arena.current;            while (chunk) |current| : (chunk = current.previous) {                if (current == target) return true;            }            return false;        }        pub fn iterAllocatedChunks(arena: *Self) ChunkIterator {            return .{ .next_chunk = arena.current };        }        pub inline fn allocValue(arena: *Self, value: anytype) Allocator.Error!*@TypeOf(value) {            const T = @TypeOf(value);            const ptr = try arena.create(T);            ptr.* = value;            return ptr;        }        pub inline fn create(arena: *Self, comptime T: type) Allocator.Error!*T {            if (@sizeOf(T) == 0) {                const address = comptime Alignment.of(T).backward(std.math.maxInt(usize));                return @ptrFromInt(address);            }            const raw = try arena.allocBytes(@sizeOf(T), .of(T));            return @ptrCast(@alignCast(raw));        }        pub inline fn alloc(arena: *Self, comptime T: type, len: usize) Allocator.Error![]T {            if (@sizeOf(T) == 0) {                const address = comptime Alignment.of(T).backward(std.math.maxInt(usize));                const ptr: [*]T = @ptrFromInt(address);                return ptr[0..len];            }            const byte_len = if (comptime @sizeOf(T) == 1)                len            else                std.math.mul(usize, @sizeOf(T), len) catch return error.OutOfMemory;            const raw = try arena.allocBytes(byte_len, .of(T));            const ptr: [*]T = @ptrCast(@alignCast(raw));            return ptr[0..len];        }        pub inline fn dupe(arena: *Self, comptime T: type, source: []const T) Allocator.Error![]T {            const dest = try arena.alloc(T, source.len);            @memcpy(dest, source);            return dest;        }        pub inline fn dupeSentinel(            arena: *Self,            comptime T: type,            source: []const T,            comptime sentinel: T,        ) Allocator.Error![:sentinel]T {            const dest = try arena.alloc(T, source.len + 1);            @memcpy(dest[0..source.len], source);            dest[source.len] = sentinel;            return dest[0..source.len :sentinel];        }        pub inline fn dupeZ(arena: *Self, comptime T: type, source: []const T) Allocator.Error![:0]T {            return arena.dupeSentinel(T, source, 0);        }        pub inline fn allocFill(arena: *Self, comptime T: type, len: usize, value: T) Allocator.Error![]T {            const dest = try arena.alloc(T, len);            @memset(dest, value);            return dest;        }        pub inline fn allocBytes(arena: *Self, len: usize, alignment: Alignment) Allocator.Error![*]u8 {            return arena.tryAllocBytes(len, alignment) orelse error.OutOfMemory;        }        inline fn tryAllocBytes(arena: *Self, len: usize, alignment: Alignment) ?[*]u8 {            if (len == 0) {                const effective_align = @max(alignment.toByteUnits(), min_alignment);                const address = Alignment.fromByteUnits(effective_align).backward(std.math.maxInt(usize));                return @ptrFromInt(address);            }            if (arena.tryAllocBytesFast(len, alignment)) |ptr| return ptr;            return arena.allocBytesSlow(len, alignment);        }        inline fn tryAllocBytesFast(arena: *Self, len: usize, alignment: Alignment) ?[*]u8 {            const current = arena.current orelse {                @branchHint(.unlikely);                return null;            };            return allocFast(current, len, alignment, min_alignment);        }        inline fn allocFast(current: *Chunk, len: usize, alignment: Alignment, comptime min_align: usize) ?[*]u8 {            return chunk_mod.allocFast(current, len, alignment, min_align);        }        fn allocBytesSlow(arena: *Self, len: usize, alignment: Alignment) ?[*]u8 {            assert(len != 0);            const remaining_limit = arena.backingDataCapacityRemaining();            const previous = arena.current;            const min_new_capacity = @max(len, default_chunk_capacity);            var base_capacity = min_new_capacity;            if (previous) |chunk| {                const effective_align = @max(alignment.toByteUnits(), min_alignment);                if (std.mem.isAligned(@intFromPtr(chunk.cursor()), effective_align)) {                    base_capacity = @max(chunk.capacity *| 2, min_new_capacity);                }            }            if (arena.chunk_growth_goal_bytes) |goal| {                base_capacity = @min(base_capacity, @max(goal, min_new_capacity));            }            if (remaining_limit) |remaining| {                if (remaining < len) return null;                if (base_capacity > remaining) base_capacity = remaining;            }            if (base_capacity < len) return null;            const new_chunk = arena.newChunk(                base_capacity,                len,                alignment,                previous,                remaining_limit,            ) catch return null;            arena.current = new_chunk;            const ptr = allocFast(new_chunk, len, alignment, min_alignment).?;            assert(std.mem.isAligned(@intFromPtr(ptr), @max(alignment.toByteUnits(), min_alignment)));            return ptr;        }        fn backingDataCapacityRemaining(arena: *const Self) ?usize {            const limit = arena.backing_data_capacity_limit orelse return null;            const allocated = arena.queryBackingDataCapacity();            if (allocated >= limit) return 0;            return limit - allocated;        }        fn newChunk(            arena: *Self,            requested_capacity: usize,            requested_len: usize,            requested_alignment: Alignment,            previous: ?*Chunk,            backing_data_capacity_remaining: ?usize,        ) Allocator.Error!*Chunk {            return arena.newChunkBounded(                requested_capacity,                requested_len,                requested_alignment,                previous,                null,                backing_data_capacity_remaining,            );        }        fn newChunkBounded(            arena: *Self,            requested_capacity: usize,            requested_len: usize,            requested_alignment: Alignment,            previous: ?*Chunk,            max_capacity: ?usize,            backing_data_capacity_remaining: ?usize,        ) Allocator.Error!*Chunk {            assert(requested_len <= requested_capacity);            return chunk_mod.create(arena.backing_allocator, .{                .requested_capacity = requested_capacity,                .requested_len = requested_len,                .requested_alignment = requested_alignment,                .previous = previous,                .max_capacity = max_capacity,                .min_alignment = min_alignment,                .chunk_header_alignment = chunk_header_alignment,                .overhead = overhead,                .typical_page_size = typical_page_size,                .backing_data_capacity_remaining = backing_data_capacity_remaining,            });        }        pub fn initBuffer(backing_allocator: Allocator, buffer: []u8) Allocator.Error!Self {            var arena = Self.init(backing_allocator);            if (buffer.len != 0) {                const initial = try Self.newChunkInBuffer(buffer, null);                arena.current = initial;                arena.caller_root = .{ .linked = initial };                arena.assertCallerRootInvariant();            }            return arena;        }        fn newChunkInBuffer(buffer: []u8, previous: ?*Chunk) Allocator.Error!*Chunk {            return chunk_mod.createInBuffer(buffer, .{                .previous = previous,                .min_alignment = min_alignment,                .chunk_header_alignment = chunk_header_alignment,            }) orelse error.OutOfMemory;        }    };}

Source: lib/bumpalo/src/root.zig:35

zig
pub const BumpAllocator = bump_impl.BumpAllocator;

Audit

Definitions1
Public names1
Members0
Version26.7.0
Revisiondaab053ee433