Skip to content

Commit f5f3653

Browse files
committed
WIP implementation of a sparse hierarchical bitset.
1 parent eaba7bf commit f5f3653

File tree

3 files changed

+130
-53
lines changed

3 files changed

+130
-53
lines changed

Code/Compiler/src/engraph.zig

-53
This file was deleted.

Code/Compiler/src/offsetarray.zig

+2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ pub const OffsetArray = struct {
2020
}
2121

2222
pub fn pushFirst(self: *OffsetArray, index: u32) !void {
23+
// Only call this the very first time.
24+
assert(self.offsets.items.len == 0);
2325
// First is the only case where a zero-index could happen.
2426
// Which typically indicate an extended byte-value.
2527
if (index == 0) {

Code/Compiler/src/sparsebit.zig

+128
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
// A sparse hierarchical bitset.
2+
// Layer 0 - 1 if that segment in subsequent layers is set. i.e. [0 0 1 0 0 0 1 0]
3+
// Would indicate that the range from 0 to 1*width has nothing set,
4+
// and there is something set between 2*width to 3*width and something between [6,7]*width
5+
// That would then subsequently be followed by popcount(layer) number of bitsets indicating the presence in subsequent layers.
6+
// To index into lower levels, we maintain a total offset count.
7+
8+
const std = @import("std");
9+
const assert = std.debug.assert;
10+
const stdbits = std.bit_set;
11+
12+
const LEVEL_WIDTH = 64;
13+
pub const BitSet = stdbits.IntegerBitSet(LEVEL_WIDTH);
14+
15+
pub fn PopCountArray(comptime T: type, comptime D: type) {
16+
return struct {
17+
head: stdbits.IntegerBitSet(@bitSizeOf(T)),
18+
data: []D,
19+
20+
pub fn init(allocator: std.mem.Allocator) !Self {
21+
return Self{
22+
.head = IntegerBitSet(@bitSizeOf(T)).initEmpty(),
23+
.data = &[_]D{},
24+
.allocator = allocator,
25+
};
26+
}
27+
28+
pub fn deinit(self: *Self, allocator: std.mem.Allocator) void {
29+
if(head.count() > 0) {
30+
allocator.free(self.data);
31+
}
32+
}
33+
34+
pub fn set(self: *Self, index: u32) !void {
35+
self.head.set(index);
36+
}
37+
38+
39+
}
40+
41+
}
42+
43+
44+
const SparseLevelBitset = struct {
45+
const Self = @This();
46+
47+
bitlevels: std.ArrayList(BitSet),
48+
// Level 0 starts at 0. Level 1 starts at 1, and extends till popcount of level 0 + 1.
49+
// Level 2 then extends from popcount(lvl0) + 1 till sum of level 1 popcounts.
50+
lvloffsets: std.ArrayList(u32),
51+
52+
pub fn init(allocator: std.mem.Allocator) !Self {
53+
var bitlevels = std.ArrayList(BitSet).init(allocator);
54+
var lvloffsets = std.ArrayList(u32).init(allocator);
55+
try bitlevels.append(BitSet.initEmpty());
56+
try lvloffsets.append(0); // Future optimization: We can skip storing the offsets for level 0, 1 and 2 since that's trivially known.
57+
58+
return Self{
59+
.bitlevels = bitlevels,
60+
.lvloffsets = lvloffsets,
61+
};
62+
}
63+
64+
pub fn deinit(self: *Self) void {
65+
self.bitlevels.deinit();
66+
}
67+
68+
pub fn set(self: *Self, index: u32) !void {
69+
var current_index = index;
70+
var level_index: usize = 0;
71+
while (current_index >= LEVEL_WIDTH) {
72+
assert(level_index < self.lvloffsets.items.len);
73+
// var absolute_index = self.lvloffsets.items[level_index] + level_offset;
74+
// Fast mod & div - as long as our level-sizes are power of two.
75+
const segmentIndex = current_index % LEVEL_WIDTH;
76+
77+
if (self.bitlevels.items[bs_index].isSet(segmentIndex)) {} else {
78+
self.bitlevels.items[bs_index].set(segmentIndex);
79+
if (level_index > 1) {
80+
self.lvloffsets.items[level_index - 1] += 1;
81+
}
82+
}
83+
self.bitlevels.items[bs_index].set();
84+
level_index += 1;
85+
current_index /= LEVEL_WIDTH;
86+
bs_index += 1;
87+
}
88+
if (bs_index >= self.bitlevels.items.len) {
89+
try self.bitlevels.append(BitSet.initEmpty());
90+
}
91+
self.bitlevels.items[bs_index].set(current_index);
92+
}
93+
94+
pub fn isSet(self: *const Self, index: u32) bool {
95+
var current_index = index;
96+
var bs_index: usize = 0;
97+
98+
while (current_index >= LEVEL_WIDTH) {
99+
if (bs_index >= self.bitlevels.items.len) {
100+
return false;
101+
}
102+
103+
// Terminate early if an intermediate layer indicates there's no sparse bit set in subsequent layers.
104+
if (!self.bitlevels.items[bs_index].isSet(current_index % LEVEL_WIDTH)) {
105+
return false;
106+
}
107+
108+
current_index /= LEVEL_WIDTH;
109+
bs_index += 1;
110+
}
111+
112+
if (bs_index >= self.bitlevels.items.len) {
113+
return false;
114+
}
115+
116+
return self.bitlevels.items[bs_index].isSet(current_index);
117+
}
118+
};
119+
120+
const test_allocator = std.testing.allocator;
121+
const expectEqual = std.testing.expectEqual;
122+
const constants = @import("constants.zig");
123+
124+
test {
125+
if (constants.DISABLE_ZIG_LAZY) {
126+
@import("std").testing.refAllDecls(@This());
127+
}
128+
}

0 commit comments

Comments
 (0)