lib/choir/src/product/entity.zig

daab053ee43316e1809a84551d573ddd1e5bf3d2

  1 const std = @import("std");
  2 const revision = @import("root.zig").revision;
  3 const bytecode = @import("../bytecode/root.zig");
  4 
  5 /// A retained view owns only immutable bytes and bytecode lookup metadata.
  6 pub const Image = opaque {
  7     pub fn create(
  8         allocator: std.mem.Allocator,
  9         published: *const revision.Revision,
 10         limits: bytecode.image.Limits,
 11         expected_version: u32,
 12     ) !*Image {
 13         const retained = try published.retain();
 14         errdefer retained.release();
 15         const state = try allocator.create(ImageData);
 16         errdefer allocator.destroy(state);
 17         var reader = try revision.record.Reader.init(published.view().exact.image);
 18         if (try reader.readInt(u32) != expected_version) return error.UnknownSchema;
 19         const count = try reader.readCount();
 20         if (count > limits.entities) return error.ImageLimit;
 21         const roots = try allocator.alloc(*bytecode.image.Index, count);
 22         errdefer allocator.free(roots);
 23         var initialized: usize = 0;
 24         errdefer for (roots[0..initialized]) |index| index.destroy();
 25         for (roots) |*index| {
 26             index.* = try bytecode.image.Index.create(allocator, try reader.readBlob(), limits);
 27             initialized += 1;
 28         }
 29         state.* = .{
 30             .allocator = allocator,
 31             .revision = retained,
 32             .roots = roots,
 33             .stage = try reader.readBlob(),
 34         };
 35         if (!reader.atEnd()) return error.InvalidRecord;
 36         return @ptrCast(state);
 37     }
 38 
 39     pub fn destroy(self: *Image) void {
 40         const state = imageData(self);
 41         for (state.roots) |index| index.destroy();
 42         state.allocator.free(state.roots);
 43         state.revision.release();
 44         state.allocator.destroy(state);
 45     }
 46 
 47     pub fn root(self: *const Image, ordinal: u32) ?bytecode.image.View {
 48         const state: *const ImageData = @ptrCast(@alignCast(self));
 49         if (ordinal >= state.roots.len) return null;
 50         return state.roots[ordinal].view();
 51     }
 52 
 53     pub fn stage(self: *const Image) []const u8 {
 54         const state: *const ImageData = @ptrCast(@alignCast(self));
 55         return state.stage;
 56     }
 57 
 58     pub fn operation(
 59         self: *const Image,
 60         ref: revision.store.Entity,
 61     ) !Located(bytecode.image.Operation) {
 62         return self.locate(ref, .operation, "operations", bytecode.image.Operation);
 63     }
 64 
 65     pub fn block(self: *const Image, ref: revision.store.Entity) !Located(bytecode.image.Block) {
 66         return self.locate(ref, .block, "blocks", bytecode.image.Block);
 67     }
 68 
 69     pub fn value(self: *const Image, ref: revision.store.Entity) !Located(bytecode.image.Value) {
 70         return self.locate(ref, .value, "values", bytecode.image.Value);
 71     }
 72 
 73     pub fn region(self: *const Image, ref: revision.store.Entity) !Located(bytecode.image.Region) {
 74         return self.locate(ref, .region, "regions", bytecode.image.Region);
 75     }
 76 
 77     pub fn attribute(self: *const Image, ref: revision.store.Entity) !Located([]const u8) {
 78         return self.locate(ref, .attribute, "attributes", []const u8);
 79     }
 80 
 81     fn locate(
 82         self: *const Image,
 83         ref: revision.store.Entity,
 84         comptime namespace: revision.record.Namespace,
 85         comptime field: []const u8,
 86         comptime T: type,
 87     ) !Located(T) {
 88         const state: *const ImageData = @ptrCast(@alignCast(self));
 89         if (!ref.revision.eql(state.revision)) return error.StaleEntity;
 90         if (ref.namespace != namespace) return error.InvalidEntity;
 91         var ordinal = ref.ordinal;
 92         for (state.roots, 0..) |index, root_ordinal| {
 93             const items = @field(index.view(), field);
 94             if (ordinal < items.len) return .{
 95                 .root = @intCast(root_ordinal),
 96                 .value = items[ordinal],
 97             };
 98             ordinal -= @intCast(items.len);
 99         }
100         return error.InvalidEntity;
101     }
102 };
103 
104 pub fn Located(comptime T: type) type {
105     return struct { root: u32, value: T };
106 }
107 
108 const ImageData = struct {
109     allocator: std.mem.Allocator,
110     revision: *const revision.Revision,
111     roots: []*bytecode.image.Index,
112     stage: []const u8,
113 };
114 
115 fn imageData(image: *Image) *ImageData {
116     return @ptrCast(@alignCast(image));
117 }