Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Describe memory management (arena) + more complete parser + std.log #8

Merged
merged 52 commits into from
Mar 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
8ffb155
Add all files to main.zig
malcolmstill Mar 30, 2024
8101ae4
Rename builder convert to toDatalog
malcolmstill Mar 30, 2024
0aedfda
datalog convert rename to remapSymbols
malcolmstill Mar 30, 2024
e224e17
remapSymbols -> remap
malcolmstill Mar 30, 2024
8781af0
Clean up
malcolmstill Mar 30, 2024
526383e
Replace std.mem.starsWith with parser.startsWith
malcolmstill Mar 30, 2024
5c17672
Replace expect / expectString with expect that just takes strings
malcolmstill Mar 30, 2024
a0235d7
More parser clean up
malcolmstill Mar 30, 2024
55d4fce
More parser clean up
malcolmstill Mar 30, 2024
5b40aab
Rename fn expect -> fn consume
malcolmstill Mar 30, 2024
694cc4b
More cleanup
malcolmstill Mar 30, 2024
d2cb81c
More clean up
malcolmstill Mar 30, 2024
bc6ad3b
Split out authorize() functions
malcolmstill Mar 30, 2024
33fd7ef
More clean up
malcolmstill Mar 30, 2024
6900188
Clean up
malcolmstill Mar 30, 2024
f4d2e44
Clean up
malcolmstill Mar 30, 2024
eb47527
Clean up
malcolmstill Mar 30, 2024
2d4e112
Clean up
malcolmstill Mar 30, 2024
7690bdc
More clean up
malcolmstill Mar 30, 2024
95520a0
Parser fixes
malcolmstill Mar 30, 2024
cdd1222
More parser tests
malcolmstill Mar 30, 2024
59e57be
Clean up
malcolmstill Mar 30, 2024
120181b
More parser work
malcolmstill Mar 30, 2024
eae0ad3
Fix number parsing
malcolmstill Mar 30, 2024
d222bae
Number parsing
malcolmstill Mar 30, 2024
8fe6dab
Boolean test
malcolmstill Mar 30, 2024
fb9a013
Parse bytes
malcolmstill Mar 30, 2024
a576a78
Test with all (apart from set) terms
malcolmstill Mar 30, 2024
28c043c
Set parsing
malcolmstill Mar 30, 2024
3835b60
More parser
malcolmstill Mar 30, 2024
19163c8
Spec compliant (?) name parsing
malcolmstill Mar 30, 2024
e3d5e06
Clean up
malcolmstill Mar 30, 2024
9475045
: _ test
malcolmstill Mar 30, 2024
e8bd75b
Fix variable name parsing + rule parsing tests
malcolmstill Mar 30, 2024
5c7dd79
More parser tests
malcolmstill Mar 30, 2024
f28d088
Sets can be empty
malcolmstill Mar 31, 2024
c3f9b13
fn expr -> fn expr0
malcolmstill Mar 31, 2024
8bbb276
Prefer if (!parser.startsWithConsuming(",")) break;
malcolmstill Mar 31, 2024
e01d877
Consistent parsing of at least one of N
malcolmstill Mar 31, 2024
9863950
Consistent parsing of at least one of N
malcolmstill Mar 31, 2024
9404968
Policy parsing
malcolmstill Mar 31, 2024
bdb6ae7
parser.temporary()
malcolmstill Mar 31, 2024
cca2bbd
Expression parsing tests
malcolmstill Mar 31, 2024
bd53fd7
Actually, group the binaryOp functions
malcolmstill Mar 31, 2024
86ad555
Fix precedence, fix chained methods
malcolmstill Mar 31, 2024
0f2ab5e
Clean up
malcolmstill Mar 31, 2024
1385108
Clean up
malcolmstill Mar 31, 2024
ee7973c
Clean up
malcolmstill Mar 31, 2024
1d1dbf3
toDatalog
malcolmstill Mar 31, 2024
faa4405
Reinstates deinit in datalog structures
malcolmstill Mar 31, 2024
c2d833e
FIx scope parsing
malcolmstill Mar 31, 2024
83b2d5f
Rename deinit functions to testDeinit and comment out any deinit call…
malcolmstill Mar 31, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions DESIGN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Design

## Memory Management

Authorizing a biscuit is a short-lived single pass operation. This makes arena allocation
a great candidate for large parts of biscuit memory management:

- It can greatly reduce the complexity of code that would otherwise have to carefully clone
/ move memory to avoid leaks and double frees.
- Potentially code can be faster because we don't do granular deallocation and we can avoid
some copying.
- Again, because we're not necessarily copying, there is the potential for reduced memory usage
in places.

The disadvantage would be that:

- We potentially over allocate, i.e. we are storing more in memory than we technically need to.

We create a toplevel arena and into it allocate all:

- facts
- predicates
- rules
- queries
- policies
- expressions
- ops
- terms

i.e. all the domain level objects are arena allocated. This means we don't have to do
complex reasoning about scope / lifetimes of these objects, they are all valid until
the toplevel arena is deallocated. If we are careful to always copy when modifying
one of these resources we can also share resources.
28 changes: 19 additions & 9 deletions biscuit-builder/src/check.zig
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,35 @@ const Term = @import("term.zig").Term;
const Rule = @import("rule.zig").Rule;

pub const Check = struct {
kind: datalog.Check.Kind,
kind: Kind,
queries: std.ArrayList(Rule),

pub fn deinit(check: Check) void {
for (check.queries.items) |query| {
query.deinit();
}
pub const Kind = enum {
one,
all,
};

check.queries.deinit();
pub fn deinit(_: Check) void {
// for (check.queries.items) |query| {
// query.deinit();
// }

// check.queries.deinit();
}

pub fn convert(check: Check, allocator: std.mem.Allocator, symbols: *datalog.SymbolTable) !datalog.Check {
pub fn toDatalog(check: Check, allocator: std.mem.Allocator, symbols: *datalog.SymbolTable) !datalog.Check {
var queries = std.ArrayList(datalog.Rule).init(allocator);

for (check.queries.items) |query| {
try queries.append(try query.convert(allocator, symbols));
try queries.append(try query.toDatalog(allocator, symbols));
}

return .{ .kind = check.kind, .queries = queries };
const kind: datalog.Check.Kind = switch (check.kind) {
.one => .one,
.all => .all,
};

return .{ .kind = kind, .queries = queries };
}

pub fn format(check: Check, comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void {
Expand Down
38 changes: 19 additions & 19 deletions biscuit-builder/src/expression.zig
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ pub const Expression = union(ExpressionType) {
};

/// convert to datalog fact
pub fn convert(expression: Expression, allocator: std.mem.Allocator, symbols: *datalog.SymbolTable) !datalog.Expression {
pub fn toDatalog(expression: Expression, allocator: std.mem.Allocator, symbols: *datalog.SymbolTable) !datalog.Expression {
var ops = std.ArrayList(datalog.Op).init(allocator);

try expression.toOpcodes(allocator, &ops, symbols);
Expand All @@ -68,7 +68,7 @@ pub const Expression = union(ExpressionType) {

pub fn toOpcodes(expression: Expression, allocator: std.mem.Allocator, ops: *std.ArrayList(datalog.Op), symbols: *datalog.SymbolTable) !void {
switch (expression) {
.value => |v| try ops.append(.{ .value = try v.convert(allocator, symbols) }),
.value => |v| try ops.append(.{ .value = try v.toDatalog(allocator, symbols) }),
.unary => |u| {
try u.expression.toOpcodes(allocator, ops, symbols);

Expand Down Expand Up @@ -113,22 +113,22 @@ pub const Expression = union(ExpressionType) {
}
}

pub fn deinit(expression: *Expression) void {
switch (expression.*) {
.value => |v| v.deinit(),
.unary => |*u| {
u.expression.deinit();

u.allocator.destroy(u.expression);
},
.binary => |*b| {
b.left.deinit();
b.right.deinit();

b.allocator.destroy(b.left);
b.allocator.destroy(b.right);
},
}
pub fn deinit(_: *Expression) void {
// switch (expression.*) {
// .value => |v| v.deinit(),
// .unary => |*u| {
// u.expression.deinit();

// u.allocator.destroy(u.expression);
// },
// .binary => |*b| {
// b.left.deinit();
// b.right.deinit();

// b.allocator.destroy(b.left);
// b.allocator.destroy(b.right);
// },
// }
}

pub fn value(term: Term) !Expression {
Expand Down Expand Up @@ -159,7 +159,7 @@ pub const Expression = union(ExpressionType) {
.value => |v| try writer.print("{any}", .{v}),
.unary => |u| {
switch (u.op) {
.negate => try writer.print("-{any}", .{u.expression}),
.negate => try writer.print("!{any}", .{u.expression}),
.parens => try writer.print("({any})", .{u.expression}),
.length => try writer.print("{any}.length()", .{u.expression}),
}
Expand Down
8 changes: 4 additions & 4 deletions biscuit-builder/src/fact.zig
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ pub const Fact = struct {
predicate: Predicate,
variables: ?std.StringHashMap(?Term),

pub fn deinit(fact: Fact) void {
fact.predicate.deinit();
pub fn deinit(_: Fact) void {
// fact.predicate.deinit();
}

/// convert to datalog fact
pub fn convert(fact: Fact, allocator: std.mem.Allocator, symbols: *datalog.SymbolTable) !datalog.Fact {
return .{ .predicate = try fact.predicate.convert(allocator, symbols) };
pub fn toDatalog(fact: Fact, allocator: std.mem.Allocator, symbols: *datalog.SymbolTable) !datalog.Fact {
return .{ .predicate = try fact.predicate.toDatalog(allocator, symbols) };
}

pub fn format(fact: Fact, comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void {
Expand Down
14 changes: 7 additions & 7 deletions biscuit-builder/src/policy.zig
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,19 @@ pub const Policy = struct {
deny,
};

pub fn deinit(policy: Policy) void {
for (policy.queries.items) |query| {
query.deinit();
}
pub fn deinit(_: Policy) void {
// for (policy.queries.items) |query| {
// query.deinit();
// }

policy.queries.deinit();
// policy.queries.deinit();
}

// pub fn convert(policy: Policy, allocator: std.mem.Allocator, symbols: *datalog.SymbolTable) !Policy {
// pub fn toDatalog(policy: Policy, allocator: std.mem.Allocator, symbols: *datalog.SymbolTable) !Policy {
// var queries = std.ArrayList(Rule).init(allocator);

// for (policy.queries.items) |query| {
// try queries.append(try query.convert(allocator, symbols));
// try queries.append(try query.toDatalog(allocator, symbols));
// }

// return .{ .kind = policy.kind, .queries = queries };
Expand Down
14 changes: 7 additions & 7 deletions biscuit-builder/src/predicate.zig
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,22 @@ pub const Predicate = struct {
name: []const u8,
terms: std.ArrayList(Term),

pub fn deinit(predicate: Predicate) void {
for (predicate.terms.items) |term| {
term.deinit();
}
pub fn deinit(_: Predicate) void {
// for (predicate.terms.items) |term| {
// term.deinit();
// }

predicate.terms.deinit();
// predicate.terms.deinit();
}

/// convert to datalog predicate
pub fn convert(predicate: Predicate, allocator: std.mem.Allocator, symbols: *datalog.SymbolTable) !datalog.Predicate {
pub fn toDatalog(predicate: Predicate, allocator: std.mem.Allocator, symbols: *datalog.SymbolTable) !datalog.Predicate {
const name = try symbols.insert(predicate.name);

var terms = std.ArrayList(datalog.Term).init(allocator);

for (predicate.terms.items) |term| {
try terms.append(try term.convert(allocator, symbols));
try terms.append(try term.toDatalog(allocator, symbols));
}

return .{ .name = name, .terms = terms };
Expand Down
1 change: 1 addition & 0 deletions biscuit-builder/src/root.zig
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ pub const Expression = @import("expression.zig").Expression;
pub const Scope = @import("scope.zig").Scope;
pub const Date = @import("date.zig").Date;
pub const Policy = @import("policy.zig").Policy;
pub const Set = @import("biscuit-datalog").Set;
32 changes: 16 additions & 16 deletions biscuit-builder/src/rule.zig
Original file line number Diff line number Diff line change
Expand Up @@ -12,40 +12,40 @@ pub const Rule = struct {
variables: ?std.StringHashMap(?Term),
scopes: std.ArrayList(Scope),

pub fn deinit(rule: Rule) void {
rule.head.deinit();
pub fn deinit(_: Rule) void {
// rule.head.deinit();

for (rule.body.items) |predicate| {
predicate.deinit();
}
// for (rule.body.items) |predicate| {
// predicate.deinit();
// }

for (rule.expressions.items) |*expression| {
expression.deinit();
}
// for (rule.expressions.items) |*expression| {
// expression.deinit();
// }

rule.body.deinit();
rule.expressions.deinit();
rule.scopes.deinit();
// rule.body.deinit();
// rule.expressions.deinit();
// rule.scopes.deinit();
}

/// convert to datalog predicate
pub fn convert(rule: Rule, allocator: std.mem.Allocator, symbols: *datalog.SymbolTable) !datalog.Rule {
const head = try rule.head.convert(allocator, symbols);
pub fn toDatalog(rule: Rule, allocator: std.mem.Allocator, symbols: *datalog.SymbolTable) !datalog.Rule {
const head = try rule.head.toDatalog(allocator, symbols);

var body = std.ArrayList(datalog.Predicate).init(allocator);
var expressions = std.ArrayList(datalog.Expression).init(allocator);
var scopes = std.ArrayList(datalog.Scope).init(allocator);

for (rule.body.items) |predicate| {
try body.append(try predicate.convert(allocator, symbols));
try body.append(try predicate.toDatalog(allocator, symbols));
}

for (rule.expressions.items) |expression| {
try expressions.append(try expression.convert(allocator, symbols));
try expressions.append(try expression.toDatalog(allocator, symbols));
}

for (rule.scopes.items) |scope| {
try scopes.append(try scope.convert(allocator, symbols));
try scopes.append(try scope.toDatalog(allocator, symbols));
}

return .{
Expand Down
2 changes: 1 addition & 1 deletion biscuit-builder/src/scope.zig
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ pub const Scope = union(ScopeKind) {
parameter: []const u8,

/// convert to datalog fact
pub fn convert(scope: Scope, _: std.mem.Allocator, symbols: *datalog.SymbolTable) !datalog.Scope {
pub fn toDatalog(scope: Scope, _: std.mem.Allocator, symbols: *datalog.SymbolTable) !datalog.Scope {
return switch (scope) {
.authority => .{ .authority = {} },
.previous => .{ .previous = {} },
Expand Down
79 changes: 71 additions & 8 deletions biscuit-builder/src/term.zig
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ const TermTag = enum(u8) {
integer,
bool,
date,
bytes,
set,
};

pub const Term = union(TermTag) {
Expand All @@ -16,17 +18,30 @@ pub const Term = union(TermTag) {
integer: i64,
bool: bool,
date: u64,
bytes: []const u8,
set: datalog.Set(Term),

pub fn deinit(_: Term) void {}

pub fn convert(term: Term, _: std.mem.Allocator, symbols: *datalog.SymbolTable) !datalog.Term {
return switch (term) {
.variable => |s| .{ .variable = @truncate(try symbols.insert(s)) }, // FIXME: assert symbol fits in u32
.string => |s| .{ .string = try symbols.insert(s) },
.integer => |n| .{ .integer = n },
.bool => |b| .{ .bool = b },
.date => |d| .{ .date = d },
};
pub fn toDatalog(term: Term, arena: std.mem.Allocator, symbols: *datalog.SymbolTable) !datalog.Term {
switch (term) {
.variable => |s| return .{ .variable = std.math.cast(u32, try symbols.insert(s)) orelse return error.FailedToCastInt },
.string => |s| return .{ .string = try symbols.insert(s) },
.integer => |n| return .{ .integer = n },
.bool => |b| return .{ .bool = b },
.date => |d| return .{ .date = d },
.bytes => |b| return .{ .bytes = b },
.set => |s| {
var datalog_set = datalog.Set(datalog.Term).init(arena);

var it = s.iterator();
while (it.next()) |t| {
try datalog_set.add(try t.toDatalog(arena, symbols));
}

return .{ .set = datalog_set };
},
}
}

pub fn format(term: Term, comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void {
Expand All @@ -36,6 +51,54 @@ pub const Term = union(TermTag) {
.integer => |n| try writer.print("{}", .{n}),
.bool => |b| if (b) try writer.print("true", .{}) else try writer.print("false", .{}),
.date => |n| try writer.print("{}", .{n}),
.bytes => |b| try writer.print("{x}", .{b}),
.set => |s| {
try writer.print("[", .{});

const count = s.count();

var it = s.iterator();

var i: usize = 0;
while (it.next()) |t| {
defer i += 1;
try writer.print("{any}", .{t});

if (i < count - 1) try writer.print(", ", .{});
}

try writer.print("]", .{});
},
}
}

pub fn eql(term: Term, other_term: Term) bool {
if (std.meta.activeTag(term) != std.meta.activeTag(other_term)) return false;

return switch (term) {
.variable => |v| std.mem.eql(u8, v, other_term.variable),
.integer => |v| v == other_term.integer,
.string => |v| std.mem.eql(u8, v, other_term.string),
.bool => |v| v == other_term.bool,
.date => |v| v == other_term.date,
.bytes => |v| std.mem.eql(u8, v, other_term.bytes),
.set => |v| v.eql(other_term.set),
};
}

pub fn hash(term: Term, hasher: anytype) void {
// Hash the tag type
std.hash.autoHash(hasher, std.meta.activeTag(term));

// Hash the value
switch (term) {
.variable => |v| for (v) |b| std.hash.autoHash(hasher, b),
.integer => |v| std.hash.autoHash(hasher, v),
.string => |v| for (v) |b| std.hash.autoHash(hasher, b),
.bool => |v| std.hash.autoHash(hasher, v),
.date => |v| std.hash.autoHash(hasher, v),
.bytes => |v| for (v) |b| std.hash.autoHash(hasher, b),
.set => |v| v.hash(hasher),
}
}
};
Loading
Loading