Lindenii Project Forge
Login

hare-git

Git library for Hare

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

/git/object.ha (raw)

use bytes;
use crypto::sha256;
use errors;
use fmt;
use fs;
use hash;
use io;
use strconv;
use strings;

// Object/pack type tags.
//
// These are not typically used as we could represent objects with tagged
// unions. However, they may be useful in scenarios where a full object is
// undesirable or unavailable.
export type objtype = enum u8 {
	OBJ_INVALID   = 0u8,
	OBJ_COMMIT    = 1u8,
	OBJ_TREE      = 2u8,
	OBJ_BLOB      = 3u8,
	OBJ_TAG       = 4u8,
	OBJ_FUTURE    = 5u8,
	OBJ_OFS_DELTA = 6u8,
	OBJ_REF_DELTA = 7u8,
};

// Any Git object.
export type object = (blob | tree | commit | tag);

// Frees resources associated with any Git object.
export fn object_finish(o: object) void = {
	match (o) {
	case let b: blob =>
		blob_finish(b);
	case let t: tree =>
		tree_finish(t);
	case let c: commit =>
		commit_finish(c);
	case let g: tag =>
		tag_finish(g);
	case =>
		abort("Unknown object type being freed...");
	};
};

// Serializes an object into its on-disk representation.
export fn object_serialize(o: object)
	([]u8 | errors::invalid | nomem) = {
	match (o) {
	case let b: blob =>
		return blob_serialize(b);
	case let t: tree =>
		return tree_serialize(t);
	case let c: commit =>
		return commit_serialize(c);
	case let g: tag =>
		return tag_serialize(g);
	case =>
		abort("Unknown object type being serialized...");
	};
};

// Verifies that the given buffer (which must be the exact on-disk format
// structured as "type size\0body") matches the given object ID.
export fn object_verify_oid(buf: []u8, want: oid) bool = {
	let st = sha256::sha256();
	hash::write(&st, buf);

	let got: oid = [0...];
	hash::sum(&st, got);
	hash::close(&st);

	return bytes::equal(got[..], want[..]);
};

// Verifies that the given typed body matches the given object ID.
export fn object_verify_typed(ty: objtype, body: []u8, want: oid) bool = {
	let st = sha256::sha256();
	defer hash::close(&st);

	switch (ty) {
	case objtype::OBJ_BLOB =>
		hash::write(&st, strings::toutf8("blob"));
	case objtype::OBJ_TREE =>
		hash::write(&st, strings::toutf8("tree"));
	case objtype::OBJ_COMMIT =>
		hash::write(&st, strings::toutf8("commit"));
	case objtype::OBJ_TAG =>
		hash::write(&st, strings::toutf8("tag"));
	case =>
		return false;
	};

	hash::write(&st, strings::toutf8(" "));
	let szs = strconv::ztos(len(body));
	hash::write(&st, strings::toutf8(szs));
	hash::write(&st, strings::toutf8("\x00"));

	hash::write(&st, body);

	let got: oid = [0...];
	hash::sum(&st, got);

	return bytes::equal(got[..], want[..]);
};

// Reads a Git object from the repository by its ID.
export fn object_read(
	r: repo,
	id: oid,
) (object | fs::error | io::error | errors::invalid | strconv::invalid | strconv::overflow | nomem) = {
	match (loose_read(r, id)) {
	case let o: object =>
		return o;
	case let fe: fs::error =>
		if (fe is errors::noentry) {
			void;
		} else {
			return fe;
		};
	case let e: (io::error | errors::invalid | strconv::invalid | strconv::overflow | nomem) =>
		return e;
	};

	match (pack_read(r, id)) {
	case let o: object =>
		return o;
	case let fe: fs::error =>
		if (fe is errors::noentry) {
			return errors::invalid;
		} else {
			return fe;
		};
	case let e: (io::error | errors::invalid | strconv::invalid | strconv::overflow | nomem) =>
		return e;
	};
};