Zig 0.17.0-dev (Split by item)

This is an example of documentation generated by ZigDoc, an alternative to Zig's built-in Auto Doc feature. See also examples in other modes/formats. The project being documented here (as the example) is the Zig library itself.

Odb

A Git object database backed by a packfile. A packfile index is also used for efficient access to objects in the packfile.

The format of the packfile and its associated index are documented in pack-format.

git.Odb
const Odb = struct

File

Code

const Odb = struct {
    format: Oid.Format,
    pack_file: *Io.File.Reader,
    index_header: IndexHeader,
    index_file: *Io.File.Reader,
    cache: ObjectCache = .{},
    allocator: Allocator,

    /// Initializes the database from open pack and index files.
    fn init(
        odb: *Odb,
        allocator: Allocator,
        format: Oid.Format,
        pack_file: *Io.File.Reader,
        index_file: *Io.File.Reader,
    ) !void {
        try pack_file.seekTo(0);
        try index_file.seekTo(0);
        odb.* = .{
            .format = format,
            .pack_file = pack_file,
            .index_header = undefined,
            .index_file = index_file,
            .allocator = allocator,
        };
        try odb.index_header.read(&index_file.interface);
    }

    fn deinit(odb: *Odb) void {
        odb.cache.deinit(odb.allocator);
        odb.* = undefined;
    }

    /// Reads the object at the current position in the database.
    fn readObject(odb: *Odb) !Object {
        var base_offset = odb.pack_file.logicalPos();
        var base_header: EntryHeader = undefined;
        var delta_offsets: std.ArrayList(u64) = .empty;
        defer delta_offsets.deinit(odb.allocator);
        const base_object = while (true) {
            if (odb.cache.get(base_offset)) |base_object| break base_object;

            base_header = try EntryHeader.read(odb.format, &odb.pack_file.interface);
            switch (base_header) {
                .ofs_delta => |ofs_delta| {
                    try delta_offsets.append(odb.allocator, base_offset);
                    base_offset = std.math.sub(u64, base_offset, ofs_delta.offset) catch return error.InvalidFormat;
                    try odb.pack_file.seekTo(base_offset);
                },
                .ref_delta => |ref_delta| {
                    try delta_offsets.append(odb.allocator, base_offset);
                    try odb.seekOid(ref_delta.base_object);
                    base_offset = odb.pack_file.logicalPos();
                },
                else => {
                    const base_data = try readObjectRaw(odb.allocator, &odb.pack_file.interface, base_header.uncompressedLength());
                    errdefer odb.allocator.free(base_data);
                    const base_object: Object = .{ .type = base_header.objectType(), .data = base_data };
                    try odb.cache.put(odb.allocator, base_offset, base_object);
                    break base_object;
                },
            }
        };

        const base_data = try resolveDeltaChain(
            odb.allocator,
            odb.format,
            odb.pack_file,
            base_object,
            delta_offsets.items,
            &odb.cache,
        );

        return .{ .type = base_object.type, .data = base_data };
    }

    /// Seeks to the beginning of the object with the given ID.
    fn seekOid(odb: *Odb, oid: Oid) !void {
        const oid_length = odb.format.byteLength();
        const key = oid.slice()[0];
        var start_index = if (key > 0) odb.index_header.fan_out_table[key - 1] else 0;
        var end_index = odb.index_header.fan_out_table[key];
        const found_index = while (start_index < end_index) {
            const mid_index = start_index + (end_index - start_index) / 2;
            try odb.index_file.seekTo(IndexHeader.size + mid_index * oid_length);
            const mid_oid = try Oid.readBytes(odb.format, &odb.index_file.interface);
            switch (mem.order(u8, mid_oid.slice(), oid.slice())) {
                .lt => start_index = mid_index + 1,
                .gt => end_index = mid_index,
                .eq => break mid_index,
            }
        } else return error.ObjectNotFound;

        const n_objects = odb.index_header.fan_out_table[255];
        const offset_values_start = IndexHeader.size + n_objects * (oid_length + 4);
        try odb.index_file.seekTo(offset_values_start + found_index * 4);
        const l1_offset: packed struct { value: u31, big: bool } = @bitCast(try odb.index_file.interface.takeInt(u32, .big));
        const pack_offset = pack_offset: {
            if (l1_offset.big) {
                const l2_offset_values_start = offset_values_start + n_objects * 4;
                try odb.index_file.seekTo(l2_offset_values_start + l1_offset.value * 4);
                break :pack_offset try odb.index_file.interface.takeInt(u64, .big);
            } else {
                break :pack_offset l1_offset.value;
            }
        };

        try odb.pack_file.seekTo(pack_offset);
    }
}