Skip to content

Commit

Permalink
Describe memory management (arena) + more complete parser + std.log (#8)
Browse files Browse the repository at this point in the history
  • Loading branch information
malcolmstill authored Mar 31, 2024
1 parent 432d4b6 commit 9aaff35
Show file tree
Hide file tree
Showing 36 changed files with 1,623 additions and 1,131 deletions.
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

0 comments on commit 9aaff35

Please sign in to comment.