feat(odb): add more bindings and api methods

This commit is contained in:
Aleksey Kulikov 2021-10-05 17:12:51 +03:00
parent 2ce419d7c4
commit 618b4e7f05
5 changed files with 416 additions and 14 deletions

View file

@ -1,9 +1,53 @@
import 'dart:ffi';
import 'package:ffi/ffi.dart';
import 'oid.dart' as oid_bindings;
import '../error.dart';
import '../oid.dart';
import 'libgit2_bindings.dart';
import '../util.dart';
/// Create a new object database with no backends.
///
/// Before the ODB can be used for read/writing, a custom database backend must be
/// manually added.
///
/// Throws a [LibGit2Error] if error occured.
Pointer<git_odb> create() {
final out = calloc<Pointer<git_odb>>();
final error = libgit2.git_odb_new(out);
if (error < 0) {
throw LibGit2Error(libgit2.git_error_last());
} else {
return out.value;
}
}
/// Add an on-disk alternate to an existing Object DB.
///
/// Note that the added path must point to an `objects`, not to a full repository,
/// to use it as an alternate store.
///
/// Alternate backends are always checked for objects after all the main backends
/// have been exhausted.
///
/// Writing is disabled on alternate backends.
///
/// Throws a [LibGit2Error] if error occured.
void addDiskAlternate({
required Pointer<git_odb> odbPointer,
required String path,
}) {
final pathC = path.toNativeUtf8().cast<Int8>();
final error = libgit2.git_odb_add_disk_alternate(odbPointer, pathC);
calloc.free(pathC);
if (error < 0) {
throw LibGit2Error(libgit2.git_error_last());
}
}
/// Determine if an object can be found in the object database by an abbreviated object ID.
///
/// Throws a [LibGit2Error] if error occured.
@ -27,5 +71,175 @@ Pointer<git_oid> existsPrefix({
}
}
/// Determine if the given object can be found in the object database.
bool exists({
required Pointer<git_odb> odbPointer,
required Pointer<git_oid> oidPointer,
}) {
final result = libgit2.git_odb_exists(odbPointer, oidPointer);
return result == 1 ? true : false;
}
/// List of objects in the database.
var _objects = <Oid>[];
/// List all objects available in the database.
///
/// Throws a [LibGit2Error] if error occured.
List<Oid> objects(Pointer<git_odb> odb) {
const except = -1;
final payload = calloc<Pointer<git_oid>>();
final cb =
Pointer.fromFunction<Int32 Function(Pointer<git_oid>, Pointer<Void>)>(
_forEachCb, except);
final error = libgit2.git_odb_foreach(odb, cb, payload.cast());
if (error < 0) {
_objects.clear();
throw LibGit2Error(libgit2.git_error_last());
}
final result = _objects.toList(growable: false);
_objects.clear();
return result;
}
/// The callback to call for each object.
int _forEachCb(
Pointer<git_oid> oid,
Pointer<Void> payload,
) {
final _oid = oid_bindings.copy(oid);
_objects.add(Oid(_oid));
return 0;
}
/// Read an object from the database.
///
/// This method queries all available ODB backends trying to read the given OID.
///
/// The returned object is reference counted and internally cached, so it should be
/// closed by the user once it's no longer in use.
///
/// Throws a [LibGit2Error] if error occured.
Pointer<git_odb_object> read({
required Pointer<git_odb> odbPointer,
required Pointer<git_oid> oidPointer,
}) {
final out = calloc<Pointer<git_odb_object>>();
final error = libgit2.git_odb_read(out, odbPointer, oidPointer);
if (error < 0) {
throw LibGit2Error(libgit2.git_error_last());
} else {
return out.value;
}
}
/// Return the OID of an ODB object.
///
/// This is the OID from which the object was read from.
Pointer<git_oid> objectId(Pointer<git_odb_object> object) {
return libgit2.git_odb_object_id(object);
}
/// Return the type of an ODB object.
int objectType(Pointer<git_odb_object> object) {
return libgit2.git_odb_object_type(object);
}
/// Return the data of an ODB object.
///
/// This is the uncompressed, raw data as read from the ODB, without the leading header.
String objectData(Pointer<git_odb_object> object) {
final out = libgit2.git_odb_object_data(object);
return out.cast<Utf8>().toDartString();
}
/// Return the size of an ODB object.
///
/// This is the real size of the `data` buffer, not the actual size of the object.
int objectSize(Pointer<git_odb_object> object) {
return libgit2.git_odb_object_size(object);
}
/// Close an ODB object.
///
/// This method must always be called once a odb object is no longer needed,
/// otherwise memory will leak.
void objectFree(Pointer<git_odb_object> object) {
libgit2.git_odb_object_free(object);
}
/// Write raw data to into the object database.
///
/// Throws a [LibGit2Error] if error occured.
Pointer<git_oid> write({
required Pointer<git_odb> odbPointer,
required int type,
required String data,
}) {
final stream = calloc<Pointer<git_odb_stream>>();
final streamError = libgit2.git_odb_open_wstream(
stream,
odbPointer,
data.length,
type,
);
if (streamError < 0) {
throw LibGit2Error(libgit2.git_error_last());
}
final buffer = data.toNativeUtf8().cast<Int8>();
final writeError = libgit2.git_odb_stream_write(
stream.value,
buffer,
data.length,
);
if (writeError < 0) {
libgit2.git_odb_stream_free(stream.value);
throw LibGit2Error(libgit2.git_error_last());
}
final out = calloc<git_oid>();
final finalizeError = libgit2.git_odb_stream_finalize_write(
out,
stream.value,
);
calloc.free(buffer);
libgit2.git_odb_stream_free(stream.value);
if (finalizeError < 0) {
throw LibGit2Error(libgit2.git_error_last());
} else {
return out;
}
}
/// Get the number of ODB backend objects.
int backendsCount(Pointer<git_odb> odb) => libgit2.git_odb_num_backends(odb);
/// Lookup an ODB backend object by index.
///
/// Throws a [LibGit2Error] if error occured.
Pointer<git_odb_backend> getBackend({
required Pointer<git_odb> odbPointer,
required int position,
}) {
final out = calloc<Pointer<git_odb_backend>>();
final error = libgit2.git_odb_get_backend(out, odbPointer, position);
if (error < 0) {
throw LibGit2Error(libgit2.git_error_last());
} else {
return out.value;
}
}
/// Close an open object database.
void free(Pointer<git_odb> db) => libgit2.git_odb_free(db);

View file

@ -84,3 +84,17 @@ int compare({
}) {
return libgit2.git_oid_cmp(aPointer, bPointer);
}
/// Copy an oid from one structure to another.
///
/// Throws a [LibGit2Error] if error occured.
Pointer<git_oid> copy(Pointer<git_oid> src) {
final out = calloc<git_oid>();
final error = libgit2.git_oid_cpy(out, src);
if (error < 0) {
throw LibGit2Error(libgit2.git_error_last());
} else {
return out;
}
}

View file

@ -1,31 +1,138 @@
import 'dart:ffi';
import 'package:libgit2dart/libgit2dart.dart';
import 'package:libgit2dart/src/git_types.dart';
import 'bindings/libgit2_bindings.dart';
import 'bindings/odb.dart' as bindings;
import 'oid.dart';
import 'util.dart';
class Odb {
/// Initializes a new instance of [Odb] class from provided
/// pointer to Odb object in memory.
const Odb(this._odbPointer);
Odb(this._odbPointer);
final Pointer<git_odb> _odbPointer;
/// Initializes a new instance of [Odb] class by creating a new object database with
/// no backends.
///
/// Before the ODB can be used for read/writing, a custom database backend must be
/// manually added.
///
/// Throws a [LibGit2Error] if error occured.
Odb.create() {
libgit2.git_libgit2_init();
_odbPointer = bindings.create();
}
late final Pointer<git_odb> _odbPointer;
/// Pointer to memory address for allocated oid object.
Pointer<git_odb> get pointer => _odbPointer;
/// Determine if an object can be found in the object database by an abbreviated object ID.
/// Adds an on-disk alternate to an existing Object DB.
///
/// Note that the added [path] must point to an `objects`, not to a full repository,
/// to use it as an alternate store.
///
/// Alternate backends are always checked for objects after all the main backends
/// have been exhausted.
///
/// Writing is disabled on alternate backends.
///
/// Throws a [LibGit2Error] if error occured.
Pointer<git_oid> existsPrefix({
required Pointer<git_oid> shortOidPointer,
required int length,
}) {
return bindings.existsPrefix(
void addDiskAlternate(String path) {
bindings.addDiskAlternate(
odbPointer: _odbPointer,
shortOidPointer: shortOidPointer,
length: length,
path: path,
);
}
/// Returns list of all objects available in the database.
///
/// Throws a [LibGit2Error] if error occured.
List<Oid> get objects => bindings.objects(_odbPointer);
/// Checks if the given object can be found in the object database.
bool contains(Oid oid) {
return bindings.exists(odbPointer: _odbPointer, oidPointer: oid.pointer);
}
/// Reads an object from the database.
///
/// This method queries all available ODB backends trying to read the given [oid].
///
/// The returned object should be freed by the user once it's no longer in use.
///
/// Throws a [LibGit2Error] if error occured.
OdbObject read(Oid oid) {
return OdbObject(bindings.read(
odbPointer: _odbPointer,
oidPointer: oid.pointer,
));
}
/// Writes raw [data] to into the object database.
///
/// [type] should be one of [GitObject.blob], [GitObject.commit], [GitObject.tag],
/// [GitObject.tree].
///
/// Throws a [LibGit2Error] if error occured or [ArgumentError] if provided type is invalid.
Oid write({required GitObject type, required String data}) {
if (type == GitObject.any ||
type == GitObject.invalid ||
type == GitObject.offsetDelta ||
type == GitObject.refDelta) {
throw ArgumentError.value('$type is invalid type');
} else {
return Oid(bindings.write(
odbPointer: _odbPointer,
type: type.value,
data: data,
));
}
}
/// Releases memory allocated for odb object.
void free() => bindings.free(_odbPointer);
}
class OdbObject {
/// Initializes a new instance of the [OdbObject] class from
/// provided pointer to odbObject object in memory.
const OdbObject(this._odbObjectPointer);
/// Pointer to memory address for allocated odbObject object.
final Pointer<git_odb_object> _odbObjectPointer;
/// Returns the OID of an ODB object.
///
/// This is the OID from which the object was read from.
Oid get id => Oid(bindings.objectId(_odbObjectPointer));
/// Returns the type of an ODB object.
GitObject get type {
late GitObject result;
final typeInt = bindings.objectType(_odbObjectPointer);
for (var type in GitObject.values) {
if (typeInt == type.value) {
result = type;
break;
}
}
return result;
}
/// Returns the data of an ODB object.
///
/// This is the uncompressed, raw data as read from the ODB, without the leading header.
String get data => bindings.objectData(_odbObjectPointer);
/// Returns the size of an ODB object.
///
/// This is the real size of the `data` buffer, not the actual size of the object.
int get size => bindings.objectSize(_odbObjectPointer);
/// Releases memory allocated for odbObject object.
void free() => bindings.objectFree(_odbObjectPointer);
}

View file

@ -1,6 +1,7 @@
import 'dart:ffi';
import 'bindings/libgit2_bindings.dart';
import 'bindings/oid.dart' as bindings;
import 'bindings/odb.dart' as odb_bindings;
import 'repository.dart';
import 'util.dart';
@ -22,7 +23,8 @@ class Oid {
_oidPointer = bindings.fromSHA(sha);
} else {
final odb = repo.odb;
_oidPointer = odb.existsPrefix(
_oidPointer = odb_bindings.existsPrefix(
odbPointer: odb.pointer,
shortOidPointer: bindings.fromStrN(sha),
length: sha.length,
);