Lindenii Project Forge
Login

hare-git

Git library for Hare

Warning: Due to various recent migrations, viewing non-HEAD refs may be broken.

/git/loose.ha (raw)

use bytes;
use compress::zlib;
use errors;
use fmt;
use fs;
use io;
use strconv;
use strings;

// Find the path to a loose object with the given ID,
// relative to the repository root.
fn loose_relpath(id: oid) (str | nomem) = {
	const hex = oid_string(id)?;
	defer free(hex);

	const dir = strings::bytesub(hex, 0z, 2z)!;
	const file = strings::bytesub(hex, 2z, strings::end)!;

	return fmt::asprintf("objects/{}/{}", dir, file);
};

// Reads a loose object from the repository by its ID.
export fn read_loose(
	r: repo,
	id: oid,
) (object | fs::error | io::error | errors::invalid | strconv::invalid | strconv::overflow | nomem) = {
	const rel = loose_relpath(id)?;
	defer free(rel);

	const fh = fs::open(r.root, rel)?;
	defer io::close(fh)!;

	let zr = zlib::decompress(fh)?;
	defer io::close(&zr.vtable)!;

	let buf = io::drain(&zr.vtable)?;
	defer free(buf);

	let mnul = bytes::index(buf, 0u8);
	if (mnul is void) {
		return errors::invalid;
	};
	let nul = mnul: size;

	const header = buf[..nul];
	const body = buf[nul + 1z ..];

	let msp = bytes::index(header, ' ');
	if (msp is void) {
		return errors::invalid;
	};
	let sp = msp: size;

	const ty = strings::fromutf8_unsafe(header[..sp]);
	const szs = strings::fromutf8_unsafe(header[sp + 1z ..]);
	const expect = strconv::stoz(szs)?;

	if (expect != len(body)) {
		return errors::invalid;
	};

	if (!verify_oid(buf, id)) {
		return errors::invalid;
	};

	if (ty == "blob") {
		const b = parse_blob(id, body)?;
		return (b: object);
	} else if (ty == "tree") {
		const t = parse_tree(id, body)?;
		return (t: object);
	} else if (ty == "commit") {
		const c = parse_commit(id, body)?;
		return (c: object);
	} else {
		return errors::invalid;
	};
};

// Reads a loose object from the repository by its ID,
// returning its type and raw data.
export fn read_loose_typed(
	r: repo,
	id: oid,
) ((u8, []u8) | fs::error | io::error | errors::invalid | errors::noentry | strconv::invalid | strconv::overflow | nomem) = {
	const rel = loose_relpath(id)?;
	defer free(rel);

	let fh = fs::open(r.root, rel)?;
	defer io::close(fh)!;

	let zr = zlib::decompress(fh)?;
	defer io::close(&zr.vtable)!;

	let buf = io::drain(&zr.vtable)?;
	defer free(buf);

	let mnul = bytes::index(buf, 0u8);
	if (mnul is void) {
		return errors::invalid;
	};
	let nul = mnul: size;

	const header = buf[..nul];
	const body = buf[nul + 1z ..];

	let msp = bytes::index(header, ' ');
	if (msp is void) {
		return errors::invalid;
	};
	let sp = msp: size;

	const ty = strings::fromutf8_unsafe(header[..sp]);
	const szs = strings::fromutf8_unsafe(header[sp + 1z ..]);
	const expect = strconv::stoz(szs)?;
	if (expect != len(body)) {
		return errors::invalid;
	};

	let code: u8 = 0u8;
	if (ty == "blob") {
		code = OBJ_BLOB;
	} else if (ty == "tree") {
		code = OBJ_TREE;
	} else if (ty == "commit") {
		code = OBJ_COMMIT;
	} else {
		return errors::invalid;
	};

	let out = alloc(body...)?;
	return (code, out);
};