summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/zisp/gc.zig22
-rw-r--r--src/zisp/gc/ListPool.zig290
-rw-r--r--src/zisp/value.zig29
-rw-r--r--src/zisp/value/istr.zig39
4 files changed, 182 insertions, 198 deletions
diff --git a/src/zisp/gc.zig b/src/zisp/gc.zig
index b36628f..9cb66d1 100644
--- a/src/zisp/gc.zig
+++ b/src/zisp/gc.zig
@@ -13,35 +13,37 @@ const HeapType = value.HeapType;
var main_alloc: Alloc = undefined;
-var list_heap_start: usize = undefined;
-var main_heap_start: usize = undefined;
-var istr_heap_start: usize = undefined;
+const u32max = std.math.maxInt(u32);
+
+var list_heap: *[u32max]Value = undefined;
+var main_heap: *[u32max]u64 = undefined;
+var istr_heap: *[u32max]u8 = undefined;
var main_list_pool: ListPool = undefined;
var main_istr_pool: IstrPool = undefined;
pub fn listPtrFromIdx(idx: u32) [*]Value {
- return @ptrFromInt(list_heap_start + idx * 8);
+ return &list_heap[idx];
}
pub fn heapPtrFromIdx(comptime typ: HeapType, idx: u32) typ.PtrType() {
- return @ptrFromInt(main_heap_start + idx * 8);
+ return @ptrCast(&main_heap[idx]);
}
pub fn istrPtrFromIdx(idx: u32) [*]const u8 {
- return @ptrFromInt(istr_heap_start + idx);
+ return &istr_heap[idx];
}
pub fn listIdxFromPtr(ptr: [*]Value) u32 {
- return @intCast(@intFromPtr(ptr - list_heap_start) / 8);
+ return @intCast(ptr - list_heap);
}
pub fn heapIdxFromPtr(ptr: HeapPtr) u32 {
- return @intCast(@intFromPtr(ptr - main_heap_start) / 8);
+ return @intCast(ptr - main_heap);
}
pub fn istrIdxFromPtr(ptr: [*]const u8) u32 {
- return @intCast(@intFromPtr(ptr - istr_heap_start));
+ return @intCast(ptr - istr_heap);
}
pub fn allocHeap(comptime typ: HeapType, len: usize) !typ.PtrType() {
@@ -92,5 +94,5 @@ pub fn mainListPool() *ListPool {
pub fn mainIstrPool() *IstrPool {
init() catch @panic("OOM"); // TODO this is only here for the test suite
- return &main_istr_set;
+ return &main_istr_pool;
}
diff --git a/src/zisp/gc/ListPool.zig b/src/zisp/gc/ListPool.zig
index 42bb50e..d63a6f8 100644
--- a/src/zisp/gc/ListPool.zig
+++ b/src/zisp/gc/ListPool.zig
@@ -1,99 +1,99 @@
-//! List allocation with optimization for short lists:
-//!
-//! The point of this is to ensure that whenever code consists of a bunch of
-//! short lists, which is almost always the case for Lisp code, their elements
-//! are allocated in tight bundles, without any padding, making them share a
-//! cache line, or at least a page, whenever possible.
-//!
-//! To this effect, we allocate memory in blocks, and create the next whenever
-//! the current one doesn't have enough space left for a requested allocation.
-//! A block should be the size of a page or a multiple thereof. We have three
-//! types of block; the first is the FlexiBlock which fulfills our goal of no
-//! padding among short lists.
-//!
-//! Example of how a series of short list allocations (labeled a to e) may have
-//! their elements allocated within a FlexiBlock:
-//!
-//! [ a0 a1 a2 b0 b1 b2 b3 b4 c0 d0 d1 e0 e1 e2 e3 __ __ __ __ ... ]
-//! ^ ^ ^ ^ ^ ^
-//! a[3] b[5] c[1] d[2] e[4] FREE ->
-//!
-//! For list element counts of up to 8, each count is its own size class, and
-//! they are allocated serially in a FlexiBlock without padding. Deallocation
-//! pushes the freed heap index into a free-list for that exact list size.
-//!
-//! If the array 'b' were to be freed, its heap index (pointing to b0) would be
-//! pushed onto free_lists[4] which holds the list of freed 5-element arrays in
-//! already allocated blocks.
-//!
-//! Over time, each FlexiBlock ends up being "frozen" into a specific run of
-//! size classes, since we never try to find adjacent freed arrays, but this
-//! shouldn't be a problem since lists of length 1 to 8 are very frequent in
-//! source code and should see high reuse.
-//!
-//! The second type of block is the ChunkBlock which introduces some waste to
-//! reduce fragmentation for moderate-length lists. In the following, the
-//! abbreviation 'CM' stands for ChunkMax:
-//!
-//! For element counts 9 to CM, we split blocks into a series of "chunks" where
-//! each chunk has space for an array within a certain size class, such as 9 to
-//! 12 elements, 13 to 16 elements, and so on. Thus, freeing a list of e.g. 11
-//! elements frees a chunk that can hold any list 9 to 12 elements. This helps
-//! against fragmentation, since lists in these size ranges aren't as common as
-//! the shorter ones, making free-lists for specific element counts impractical.
-//!
-//! A request for a list of e.g. 12 elements could still be fulfilled by chunks
-//! larger than that, if the size class 9-12 is depleted. This avoids causing
-//! too much wasted space if larger lists occur very rarely in source code.
-//!
-//! To perfectly fill a 4 KiB page, which can hold 512 Value (64-bit) elements,
-//! we could combine various chunk sizes and counts (size classes). Currently,
-//! the following is in use:
-//!
-//! 30 * 12 + 8 * 16 + 1 * 24 = 512
-//!
-//! We could increase CM from 24 to e.g. 32, or use blocks that are multiple
-//! pages in size, in which case the chunk split could take various different
-//! forms; we could use empiric testing over large bodies of code to find an
-//! optimum, but it's unlikely to make a difference since list sizes < 9 are
-//! extremely dominant.
-//!
-//! Chunk use within the current (last allocated) block is kept track of via a
-//! bit-map that indicates the used or not status of each chunk regardless of
-//! size class. Finding the first available chunk for a given size class is
-//! then a matter of (optionally) applying a bit-mask to mask out the bits for
-//! the smaller size classes, followed with a bit-counting operation to locate
-//! the index of the first suitable chunk. Since chunks have different sizes,
-//! this index is mapped to an actual byte offset via a static lookup table.
-//!
-//! Upon deallocation, we can use 'MOD block_size' on the heap index of the
-//! list, combined with a small series of less-than checks, to figure out the
-//! size class.
-//!
-//! As an example: If the first group of chunks occupy elements [0, 24*12), and
-//! we're given heap index J for a list to be deallocated, we can check if it's
-//! in that first group by testing: J % 512 < 24*12
-//!
-//! Finally, we have the ArrayBlock, which is simply an N-array of M-element
-//! chunks. Currently, we use N = 64 with M = 32. This is probably not the
-//! most useful optimization, but it's easy to implement anyway.
-//!
-//! Starting from 33 elements, we stop caring and call the underlying allocator
-//! directly, since lists that long in source code are extremely rare.
-//!
-//! The main remaining issue that all of the above cannot solve is that if we
-//! free and allocate new code repeatedly (e.g. of whole modules), each time
-//! there will be bits and pieces that end up in a completely different place
-//! due to the frozen nature of FlexiBlocks. E.g. freeing a module may have
-//! freed 25 arrays of length 6, but the newly loaded code needs 28 arrays of
-//! length 6, so three of them land somewhere far away.
-//!
-//! This shouldn't affect programs that simply load up all their modules, run
-//! forever without dynamic modification, and thus never deallocate code.
-//!
-//! Otherwise, a simple solution is to use separate ListPool instances for
-//! modules, or just forget about all of this and rely on a compacting GC.
-//!
+/// List allocation with optimization for short lists:
+///
+/// The point of this is to ensure that whenever code consists of a bunch of
+/// short lists, which is almost always the case for Lisp code, their elements
+/// are allocated in tight bundles, without any padding, making them share a
+/// cache line, or at least a page, whenever possible.
+///
+/// To this effect, we allocate memory in blocks, and create the next whenever
+/// the current one doesn't have enough space left for a requested allocation.
+/// A block should be the size of a page or a multiple thereof. We have three
+/// types of block; the first is the FlexiBlock which fulfills our goal of no
+/// padding among short lists.
+///
+/// Example of how a series of short list allocations (labeled a to e) may have
+/// their elements allocated within a FlexiBlock:
+///
+/// [ a0 a1 a2 b0 b1 b2 b3 b4 c0 d0 d1 e0 e1 e2 e3 __ __ __ __ ... ]
+/// ^ ^ ^ ^ ^ ^
+/// a[3] b[5] c[1] d[2] e[4] FREE ->
+///
+/// For list element counts of up to 8, each count is its own size class, and
+/// they are allocated serially in a FlexiBlock without padding. Deallocation
+/// pushes the freed heap index onto an intrusive free-list for that list size.
+///
+/// If the array 'b' were to be freed, its heap index (pointing to b0) would be
+/// pushed onto free_lists[4] which points to the last freed 5-element array in
+/// an already allocated block.
+///
+/// Over time, each FlexiBlock ends up being "frozen" into a specific run of
+/// size classes, since we never try to find adjacent freed arrays, but this
+/// shouldn't be a problem since lists of length 1 to 8 are very frequent in
+/// source code and should see high reuse.
+///
+/// The second type of block is the ChunkBlock which introduces some waste to
+/// reduce fragmentation for moderate-length lists. In the following, the
+/// abbreviation 'CM' stands for ChunkMax:
+///
+/// For element counts 9 to CM, we split blocks into a series of "chunks" where
+/// each chunk has space for an array within a certain size class, such as 9 to
+/// 12 elements, 13 to 16 elements, and so on. Thus, freeing a list of e.g. 11
+/// elements frees a chunk that can hold any list 9 to 12 elements. This helps
+/// against fragmentation, since lists in these size ranges aren't as common as
+/// the shorter ones, making free-lists for specific element counts impractical.
+///
+/// A request for a list of e.g. 12 elements could still be fulfilled by chunks
+/// larger than that, if the size class 9-12 is depleted. This avoids causing
+/// too much wasted space if larger lists occur very rarely in source code.
+///
+/// To perfectly fill a 4 KiB page, which can hold 512 Value (64-bit) elements,
+/// we could combine various chunk sizes and counts (size classes). Currently,
+/// the following is in use:
+///
+/// 30 * 12 + 8 * 16 + 1 * 24 = 512
+///
+/// We could increase CM from 24 to e.g. 32, or use blocks that are multiple
+/// pages in size, in which case the chunk split could take various different
+/// forms; we could use empiric testing over large bodies of code to find an
+/// optimum, but it's unlikely to make a difference since list sizes < 9 are
+/// extremely dominant.
+///
+/// Chunk use within the current (last allocated) block is kept track of via a
+/// bit-map that indicates the used or not status of each chunk regardless of
+/// size class. Finding the first available chunk for a given size class is
+/// then a matter of (optionally) applying a bit-mask to mask out the bits for
+/// the smaller size classes, followed with a bit-counting operation to locate
+/// the index of the first suitable chunk. Since chunks have different sizes,
+/// this index is mapped to an actual byte offset via a static lookup table.
+///
+/// Upon deallocation, we can use 'MOD page_size' on a pointer, combined with a
+/// small series of less-than checks, to figure out the size class.
+///
+/// As an example: If the first group of chunks occupy elements [0, 30*12), and
+/// we're given pointer P for a list to be deallocated, we can check if it's in
+/// that first group by testing: P % 4096 < 30 * 12 * 8 (elements are 8 bytes).
+///
+/// Finally, we have the ArrayBlock, which is simply an N-array of M-element
+/// chunks. Currently, we use N = 64 with M = 32. This is probably not the
+/// most useful optimization, but it's easy to implement anyway.
+///
+/// Starting from 33 elements, we stop caring and call the underlying allocator
+/// directly, since lists that long in source code are extremely rare.
+///
+/// The main remaining issue that all of the above cannot solve is that if we
+/// free and allocate new code repeatedly (e.g. of whole modules), each time
+/// there will be bits and pieces that end up in a completely different place
+/// due to the frozen nature of FlexiBlocks. E.g. freeing a module may have
+/// freed 25 arrays of length 6, but the newly loaded code needs 28 arrays of
+/// length 6, so three of them land somewhere far away.
+///
+/// This shouldn't affect programs that simply load up all their modules, run
+/// forever without dynamic modification, and thus never deallocate code.
+///
+/// Otherwise, a simple solution is to use separate ListPool instances for
+/// modules, or just forget about all of this and rely on a compacting GC.
+///
+const Self = @This();
const std = @import("std");
@@ -109,8 +109,6 @@ const SegStack = seg_stack.SegStack;
const i2p = gc.listPtrFromIdx;
const p2i = gc.listIdxFromPtr;
-const Self = @This();
-
// 4 KiB blocks fit 512 Value elements, which should be good.
const block_size = 4096 / @sizeOf(Value);
const Block = [block_size]Value;
@@ -139,6 +137,12 @@ const chunk_empty_big: u64 = (2 ^ 1 - 1) << 38;
const array_block_chunk_size = 32;
const array_block_max_index = block_size / array_block_chunk_size;
+const FreeListNode = packed union(u64) {
+ bits: u64,
+ vals_ptr: [*]Value,
+ prev_ptr: *FreeListNode,
+};
+
alloc: Alloc,
cur_flexi_block: *Block,
@@ -153,9 +157,9 @@ flexi_block_stack: SegStack(*Block, 512),
chunk_block_stack: SegStack(*Block, 256),
array_block_stack: SegStack(*Block, 64),
-flexi_free_lists: [8]SegStack(u32, 256),
-chunk_free_lists: [3]SegStack(u32, 256),
-array_free_list: SegStack(u32, 64),
+flexi_free_lists: [8]FreeListNode,
+chunk_free_lists: [3]FreeListNode,
+array_free_list: FreeListNode,
pub fn init(alloc: Alloc) !Self {
return .{
@@ -166,30 +170,13 @@ pub fn init(alloc: Alloc) !Self {
.flexi_block_stack = try .init(alloc),
.chunk_block_stack = try .init(alloc),
.array_block_stack = try .init(alloc),
- .flexi_free_lists = .{
- try .init(alloc),
- try .init(alloc),
- try .init(alloc),
- try .init(alloc),
- try .init(alloc),
- try .init(alloc),
- try .init(alloc),
- try .init(alloc),
- },
- .chunk_free_lists = .{
- try .init(alloc),
- try .init(alloc),
- try .init(alloc),
- },
- .array_free_list = try .init(alloc),
+ .flexi_free_lists = @splat(.{ .bits = 0 }),
+ .chunk_free_lists = @splat(.{ .bits = 0 }),
+ .array_free_list = .{ .bits = 0 },
};
}
pub fn deinit(self: *Self) void {
- self.array_free_list.deinit(self.alloc);
- for (self.chunk_free_lists) |l| l.deinit(self.alloc);
- for (self.flexi_free_lists) |l| l.deinit(self.alloc);
-
while (self.array_block_stack.pop(self.alloc)) |b| self.alloc.destroy(b);
while (self.chunk_block_stack.pop(self.alloc)) |b| self.alloc.destroy(b);
while (self.flexi_block_stack.pop(self.alloc)) |b| self.alloc.destroy(b);
@@ -203,9 +190,21 @@ pub fn deinit(self: *Self) void {
self.alloc.destroy(self.cur_flexi_block);
}
+fn pushFree(list: *FreeListNode, len: u8, ptr: [*]Value) void {
+ ptr[0].bits = list.bits;
+ list.vals_ptr = ptr;
+}
+
+fn popFree(list: *FreeListNode, len: u8) ?[*]Value {
+ if (list.bits == 0) return null;
+ const ptr = list.vals_ptr;
+ list.* = list.prev_ptr.*;
+ return ptr;
+}
+
fn newFlexiBlock(self: *Self, rem_len: u8) !void {
if (rem_len != 0) {
- self.flexi_free_lists[rem_len - 1].push(self.curFlexiBlockPtr());
+ pushFree(&self.flexi_free_lists[rem_len - 1], self.curFlexiBlockPtr());
}
try self.flexi_block_stack.push(self.alloc, self.cur_flexi_block);
self.cur_flexi_block = try self.alloc.create(Block);
@@ -218,16 +217,13 @@ fn newChunkBlock(self: *Self) !void {
while (empty != 0) {
const idx = @ctz(chunks);
if (idx < 30) {
- const heap_idx = p2i(curChunkBlockPtr(idx));
- self.chunk_free_lists[0].push(self.alloc, heap_idx);
+ pushFree(&self.chunk_free_lists[0], curChunkBlockPtr(idx));
empty &= ~@shlExact(@as(u64, 1), @intCast(idx));
} else if (idx < 38) {
- const heap_idx = p2i(curChunkBlockPtr(idx));
- self.chunk_free_lists[1].push(self.alloc, heap_idx);
+ pushFree(&self.chunk_free_lists[1], curChunkBlockPtr(idx));
empty &= ~@shlExact(@as(u64, 1), @intCast(idx));
} else {
- const heap_idx = p2i(curChunkBlockPtr(38));
- self.chunk_free_lists[2].push(self.alloc, heap_idx);
+ pushFree(&self.chunk_free_lists[2], curChunkBlockPtr(38));
break;
}
}
@@ -266,7 +262,7 @@ pub fn allocVals(self: *Self, len: u8) ![*]Value {
if (len > 24) {
@branchHint(.unlikely);
- if (self.array_free_list.pop(self.alloc)) |i| return i2p(i);
+ if (popFree(&self.array_free_list)) |p| return p;
if (self.cur_array_index == array_block_max_index) {
try self.newArrayBlock();
}
@@ -276,7 +272,7 @@ pub fn allocVals(self: *Self, len: u8) ![*]Value {
if (len > 16) {
@branchHint(.unlikely);
- if (self.chunk_free_lists[2].pop(self.alloc)) |i| return i2p(i);
+ if (popFree(&self.chunk_free_lists[2])) |p| return p;
if ((self.cur_chunk_empty & chunk_empty_big) == 0) {
try self.newChunkBlock();
}
@@ -286,8 +282,8 @@ pub fn allocVals(self: *Self, len: u8) ![*]Value {
if (len > 12) {
@branchHint(.unlikely);
- if (self.chunk_free_lists[1].pop(self.alloc)) |i| return i2p(i);
- if (self.chunk_free_lists[2].pop(self.alloc)) |i| return i2p(i);
+ if (popFree(&self.chunk_free_lists[1])) |p| return p;
+ if (popFree(&self.chunk_free_lists[2])) |p| return p;
const empty = self.cur_chunk_empty & ~chunk_empty_sml;
if (empty != 0) {
const idx: u8 = @ctz(empty);
@@ -301,9 +297,9 @@ pub fn allocVals(self: *Self, len: u8) ![*]Value {
}
if (len > 8) {
- if (self.chunk_free_lists[0].pop(self.alloc)) |i| return i2p(i);
- if (self.chunk_free_lists[1].pop(self.alloc)) |i| return i2p(i);
- if (self.chunk_free_lists[2].pop(self.alloc)) |i| return i2p(i);
+ if (popFree(&self.chunk_free_lists[0])) |p| return p;
+ if (popFree(&self.chunk_free_lists[1])) |p| return p;
+ if (popFree(&self.chunk_free_lists[2])) |p| return p;
const empty = self.cur_chunk_empty;
if (empty != 0) {
const idx: u8 = @ctz(empty);
@@ -316,7 +312,7 @@ pub fn allocVals(self: *Self, len: u8) ![*]Value {
}
}
- if (self.flexi_free_lists[len - 1].pop(self.alloc)) |i| return i2p(i);
+ if (popFree(&self.flexi_free_lists[len - 1])) |p| return p;
const rem_len = self.cur_flexi_block.len - self.cur_flexi_index;
if (len > rem_len) try self.newFlexiBlock(rem_len);
@@ -331,23 +327,27 @@ pub fn freeVals(self: *Self, len: u8, ptr: [*]Value) !void {
@branchHint(.unlikely);
return self.alloc.free(ptr);
}
- const idx = p2i(ptr);
if (len > 24) {
@branchHint(.unlikely);
- return self.array_free_list.push(self.alloc, idx);
+ return pushFree(&self.array_free_list, ptr);
}
if (len > 16) {
@branchHint(.unlikely);
- return self.chunk_free_lists[2].push(self.alloc, idx);
+ return pushFree(&self.chunk_free_lists[2], ptr);
}
- if (len > 12) {
- @branchHint(.unlikely);
- return self.chunk_free_lists[1].push(self.alloc, idx);
+ if (len <= 8) {
+ @branchHint(.likely);
+ return pushFree(&self.flexi_free_lists[len - 1], ptr);
}
- if (len > 8) {
- return self.chunk_free_lists[0].push(self.alloc, idx);
+ // Find real size class, since 9-12 and 13-16 can occupy larger chunks.
+ const pos_in_page = @intFromPtr(ptr) % 4096;
+ if (pos_in_page < 30 * 12 * 8) {
+ return pushFree(&self.chunk_free_lists[0], ptr);
+ } else if (pos_in_page < 38 * 16 * 8) {
+ return pushFree(&self.chunk_free_lists[1], ptr);
+ } else {
+ return pushFree(&self.chunk_free_lists[2], ptr);
}
- return self.flexi_free_lists[len - 1].push(self.alloc, idx);
}
pub fn freeList(self: *Self, list: Value) !void {
diff --git a/src/zisp/value.zig b/src/zisp/value.zig
index 1a63349..f075b35 100644
--- a/src/zisp/value.zig
+++ b/src/zisp/value.zig
@@ -73,12 +73,13 @@ pub const none = Value{ .misc = .{ .value = .none } };
// zig fmt: on
/// A plain (unpacked, untagged, uncompressed) pointer into the main heap.
-pub const HeapPtr = *align(8) anyopaque;
+pub const HeapPtr = *align(16) anyopaque;
/// Values for the 8 type bits on main heap pointers.
pub const HeapType = enum(u8) {
/// Pair (car, cdr)
pair,
+
/// Array of various types: see `ArrayPtr`.
array,
@@ -115,13 +116,11 @@ pub const hi16_list = hi16(0b0000);
pub const hi16_hptr = hi16(0b0001);
pub const hi16_istr = hi16(0b0010);
pub const hi16_sstr = hi16(0b0011);
-pub const hi16_rpos = hi16(0b0100);
-pub const hi16_rneg = hi16(0b0101);
-pub const hi16_ndef = hi16(0b0110);
+pub const hi16_srat = hi16(0b0100);
+pub const hi16_ndf1 = hi16(0b0101);
+pub const hi16_ndf2 = hi16(0b0110);
pub const hi16_rune = hi16(0b0111);
-pub const hi15_srat: u15 = @intCast(hi16(0b0100) >> 1);
-
pub const hi16_const_list = hi16(0b1000);
pub const hi16_const_hptr = hi16(0b1001);
pub const hi16_const_istr = hi16(0b1010);
@@ -195,15 +194,21 @@ pub const Value = packed union {
/// Small rat (rational number)
srat: packed struct {
- q: u24,
- p: i25,
- _hi: u15 = hi15_srat,
+ p: i32,
+ q: u16,
+ _hi: u16 = hi16_srat,
+ },
+
+ /// Undefined 1
+ ndf1: packed struct {
+ _: u48,
+ _hi: u16 = hi16_ndf1,
},
- /// Undefined, for now
- ndef: packed struct {
+ /// Undefined 2
+ ndf2: packed struct {
_: u48,
- _hi: u16 = hi16_ndef,
+ _hi: u16 = hi16_ndf2,
},
/// Rune (6-byte ASCII string)
diff --git a/src/zisp/value/istr.zig b/src/zisp/value/istr.zig
index a581289..528280a 100644
--- a/src/zisp/value/istr.zig
+++ b/src/zisp/value/istr.zig
@@ -1,6 +1,6 @@
//! Intermediate-length String
//!
-//! Prefixed with a 1-byte length. Typically interned in an IstrSet.
+//! Length up to 255, stored in pointer. Typically interned in an IstrPool.
const std = @import("std");
@@ -9,36 +9,13 @@ const Alloc = std.mem.Allocator;
const gc = @import("../gc.zig");
const value = @import("../value.zig");
-const IstrSet = gc.IstrSet;
+const IstrPool = gc.IstrPool;
const Value = value.Value;
pub const max_len = 255;
// Zig API
-/// Pointer to an interned string. First byte is length.
-pub const IstrPtr = *IstrHead;
-
-pub const IstrHead = packed struct(u8) {
- len: u8,
-
- fn bufU8(self: *@This()) [*]u8 {
- return @ptrCast(self);
- }
-
- pub fn bytes(self: *@This()) []const u8 {
- const start = @sizeOf(IstrHead);
- const len: usize = self.len;
- return self.bufU8()[start .. start + len];
- }
-
- pub fn putStr(self: *@This(), s: []const u8) void {
- const start = @sizeOf(IstrHead);
- self.len = @intCast(s.len);
- @memcpy(self.bufU8()[start .. start + s.len], s);
- }
-};
-
pub fn check(v: Value) bool {
return v.isIstr();
}
@@ -70,12 +47,12 @@ pub fn new(alloc: Alloc, s: []const u8) !IstrPtr {
}
pub fn getOrNew(s: []const u8) !IstrPtr {
- return getOrNewInSet(gc.mainIstrSet(), s);
+ return getOrNewInPool(gc.mainIstrPool(), s);
}
-pub fn getOrNewInSet(set: *IstrSet, s: []const u8) !IstrPtr {
+pub fn getOrNewInPool(pool: *IstrPool, s: []const u8) !IstrPtr {
assertValidIstr(s);
- return try set.getOrNew(s);
+ return try pool.getOrNew(s);
}
pub fn pack(istr: IstrPtr) Value {
@@ -104,10 +81,10 @@ pub fn getLen(v: Value) Value {
return value.fixnum.pack(istr.len);
}
-// TODO: Zisp representation of IstrSet & ability to intern in given IstrSet
+// TODO: Zisp representation of IstrPool & ability to intern in given IstrPool
pub fn intern(v: Value) Value {
const istr = assert(v);
- const set = gc.mainIstrSet();
- return pack(set.getOrPut(istr));
+ const pool = gc.mainIstrPool();
+ return pack(pool.getOrPut(istr));
}