Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Miscellaneous improvements #78

Merged
merged 9 commits into from
Dec 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
[workspace]

members = ["omnibor", "gitoid"]
resolver = "2"

6 changes: 3 additions & 3 deletions gitoid/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@ crate-type = ["rlib", "cdylib"]

[dependencies]
hex = "0.4.3"
sha1 = "0.10.4"
sha2 = "0.10.5"
url = "2.2.2"
sha1 = "0.10.6"
sha2 = "0.10.8"
url = "2.4.1"
2 changes: 1 addition & 1 deletion gitoid/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# It is intended to be easy for newcomers to the project to understand.

# Variables for cbindgen.
CBINDGEN_CONFIG="../cbindgen.toml"
CBINDGEN_CONFIG="./cbindgen.toml"

# Variables for the header file.
INCLUDE_DIR="./include"
Expand Down
24 changes: 2 additions & 22 deletions cbindgen.toml → gitoid/cbindgen.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
language = "C"

# Wrap generated file in an include guard.
include_guard = "gitbom_h"
include_guard = "gitoid_h"

# Note the version of cbindgen used to generate the header.
include_version = true
Expand All @@ -20,7 +20,7 @@ style = "both"
header = """
/**
* @file
* @brief "GitBom"
* @brief "GitOID"
*/
"""

Expand All @@ -40,23 +40,3 @@ autogen_warning = "/* Warning, this file is autogenerated by cbindgen. Don't mod

# Prefix enum variants with the name of the overall type.
prefix_with_name = true


#==============================================================================
# Parsing Rules
#------------------------------------------------------------------------------

[parse]

# Parse dependencies.
#parse_deps = true
#include = []

#==============================================================================
# Macro Expansion Rules
#------------------------------------------------------------------------------

#[parse.expand]

# Expand macros for `gitoid`.
#crates = ["gitoid"]
12 changes: 10 additions & 2 deletions gitoid/src/ffi/gitoid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,14 @@ use std::ffi::CStr;
use std::ffi::CString;
use std::fs::File;
use std::io::BufReader;
#[cfg(target_family = "unix")]
use std::os::unix::prelude::FromRawFd;
#[cfg(target_family = "unix")]
use std::os::unix::prelude::RawFd;
#[cfg(target_family = "windows")]
use std::os::windows::io::FromRawHandle;
#[cfg(target_family = "windows")]
use std::os::windows::prelude::RawHandle;
use std::ptr::null;
use std::ptr::null_mut;
use std::slice;
Expand Down Expand Up @@ -64,6 +70,8 @@ pub extern "C" fn gitoid_invalid(gitoid: *const GitOid) -> c_int {

/// Construct a new `GitOid` from a buffer of bytes.
///
/// `content_len` is the number of elements, not the number of bytes.
///
/// `content_len` times 8 (byte size) must be less than or equal to the
/// maximum size representable with an unsigned integer at the size used by
/// the ISA (32-bit or 64-bit usually).
Expand All @@ -74,7 +82,7 @@ pub extern "C" fn gitoid_invalid(gitoid: *const GitOid) -> c_int {
pub extern "C" fn gitoid_new_from_bytes(
hash_algorithm: HashAlgorithm,
object_type: ObjectType,
content: *const u8,
content: *mut u8,
content_len: usize,
) -> GitOid {
let output = catch_panic(|| {
Expand Down Expand Up @@ -277,5 +285,5 @@ pub extern "C" fn gitoid_str_free(s: *mut c_char) {
return;
}

unsafe { CString::from_raw(s) };
let _ = unsafe { CString::from_raw(s) };
}
2 changes: 1 addition & 1 deletion gitoid/src/gitoid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ impl GitOid {
///
/// This construction should _only_ be used for error-handling purposes
/// when using the `gitoid` crate over FFI.
pub fn new_invalid() -> Self {
pub(crate) fn new_invalid() -> Self {
GitOid {
hash_algorithm: HashAlgorithm::Sha1,
object_type: ObjectType::Blob,
Expand Down
2 changes: 2 additions & 0 deletions gitoid/test/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
c_test.exe
c_test
52 changes: 23 additions & 29 deletions gitoid/test/c/test.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,76 +5,70 @@
#include "gitoid.h"

#define LEN(arr) (sizeof(arr) / sizeof(arr[0]));
#define TEST(NAME) {.name = #NAME, .fn = NAME}

void test_gitoid_new_from_str() {
GitOid gitoid = gitoid_new_from_str(HashAlgorithm_Sha1, ObjectType_Blob, "hello world");
assert(!gitoid_invalid(&gitoid));
assert(gitoid.len == 20);
assert(gitoid.value[0] == 149);
}

void test_gitoid_new_from_bytes() {
// Section that creates the byte array was heavily inspired by [1].
//
// Does not do error checking, and is intended solely for test purposes.
// The length of `byte_array` is equal to the length of `string` plus one,
// to make space for the nul-terminator.
//
// [1]: https://stackoverflow.com/a/3409211/2308264
const char *string = "hello_world";
const char *position = string;
unsigned char byte_array[12];
size_t size = LEN(byte_array);
for (size_t count = 0; count < size; ++count) {
sscanf(position, "%2hhx", &byte_array[count]);
position += 2;
}
uint8_t byte_array_length = sizeof byte_array;
unsigned char bytes[] = {0x00, 0x01, 0x02, 0x03,
0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B,
0x0C, 0x0D, 0x0E, 0x0F};
uint8_t bytes_len = LEN(bytes);

GitOid gitoid = gitoid_new_from_bytes(
HashAlgorithm_Sha1,
ObjectType_Blob,
&byte_array_length,
*byte_array
bytes,
bytes_len
);

assert(!gitoid_invalid(&gitoid));
assert(gitoid.len == 20);
assert(gitoid.value[0] == 130);
assert(gitoid.value[0] == 182);
}

void test_gitoid_new_from_url() {
char *url = "gitoid:blob:sha256:fee53a18d32820613c0527aa79be5cb30173c823a9b448fa4817767cc84c6f03";
GitOid gitoid = gitoid_new_from_url(url);
assert(!gitoid_invalid(&gitoid));
assert(gitoid.len == 32);
assert(gitoid.value[0] == 254);
}

void test_gitoid_get_url() {
char *url_in = "gitoid:blob:sha256:fee53a18d32820613c0527aa79be5cb30173c823a9b448fa4817767cc84c6f03";
GitOid gitoid = gitoid_new_from_url(url_in);
assert(!gitoid_invalid(&gitoid));
char *url_out = gitoid_get_url(&gitoid);
assert(strncmp(url_in, url_out, 83) == 0);
gitoid_str_free(url_out);
}

void test_gitoid_hash_algorithm_name() {
GitOid gitoid = gitoid_new_from_str(HashAlgorithm_Sha1, ObjectType_Blob, "hello world");
assert(!gitoid_invalid(&gitoid));
const char *hash_algorithm = gitoid_hash_algorithm_name(gitoid.hash_algorithm);
assert(strncmp(hash_algorithm, "sha1", 4) == 0);
}

void test_gitoid_object_type_name() {
GitOid gitoid = gitoid_new_from_str(HashAlgorithm_Sha1, ObjectType_Blob, "hello world");
assert(!gitoid_invalid(&gitoid));
const char *object_type = gitoid_object_type_name(gitoid.object_type);
assert(strncmp(object_type, "blob", 4) == 0);
}

void test_gitoid_validity() {
// Notice the SHA type is invalid.
char *validity_url = "gitoid:blob:sha000:fee53a18d32820613c0527aa79be5cb30173c823a9b448fa4817767cc84c6f03";
GitOid gitoid = gitoid_new_from_url(validity_url);
assert(gitoid_invalid(&gitoid));

// Also test the error message reporting.
char *expected_msg = "string is not a valid GitOID URL";
char error_msg[256];
gitoid_get_error_message(error_msg, 256);
Expand All @@ -91,14 +85,14 @@ typedef struct test {
int main() {
setvbuf(stdout, NULL, _IONBF, 0);

test_t tests[7] = {
{.name = "gitoid_new_from_str", .fn = test_gitoid_new_from_str},
{.name = "gitoid_new_from_bytes", .fn = test_gitoid_new_from_bytes},
{.name = "gitoid_new_from_url", .fn = test_gitoid_new_from_url},
{.name = "gitoid_get_url", .fn = test_gitoid_get_url},
{.name = "gitoid_hash_algorithm_name", .fn = test_gitoid_hash_algorithm_name},
{.name = "gitoid_object_type_name", .fn = test_gitoid_object_type_name},
{.name = "gitoid_validity", .fn = test_gitoid_validity},
test_t tests[] = {
TEST(test_gitoid_new_from_str),
TEST(test_gitoid_new_from_bytes),
TEST(test_gitoid_new_from_url),
TEST(test_gitoid_get_url),
TEST(test_gitoid_hash_algorithm_name),
TEST(test_gitoid_object_type_name),
TEST(test_gitoid_validity),
};

size_t n_tests = LEN(tests);
Expand Down
4 changes: 0 additions & 4 deletions omnibor/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,3 @@ version = "0.1.7"

[dependencies]
gitoid = "0.1.5"
hex = "0.4.3"
im = "15.1.0"
sha1 = "0.10.4"
sha2 = "0.10.5"
120 changes: 1 addition & 119 deletions omnibor/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,120 +1,2 @@
use gitoid::GitOid;
use im::{HashSet, Vector};

/// A [persistent][wiki] collection of [git oids][git_scm].
///
/// Why persistent? While Rust and the borrow checker is great about ownership and
/// mutation, always knowing that a Ref will not change if passed as a parameter
/// to a function eliminates a class of errors.
///
/// [wiki]: https://en.wikipedia.org/wiki/Persistent_data_structure
/// [git_scm]: https://git-scm.com/book/en/v2/Git-Internals-Git-Objects
#[derive(Clone, PartialOrd, Eq, Ord, Debug, Hash, PartialEq)]
pub struct OmniBor {
git_oids: HashSet<GitOid>,
}

impl FromIterator<GitOid> for OmniBor {
/// Create an OmniBor from many GitOids
fn from_iter<T>(gitoids: T) -> Self
where
T: IntoIterator<Item = GitOid>,
{
let me = OmniBor::new();
me.add_many(gitoids)
}
}

impl OmniBor {
/// Create a new instance
#[allow(clippy::new_without_default)]
pub fn new() -> Self {
Self {
git_oids: HashSet::new(),
}
}

/// Create a OmniBor from many GitOids
pub fn new_from_iterator<I>(gitoids: I) -> Self
where
I: IntoIterator<Item = GitOid>,
{
let me = OmniBor::new();
me.add_many(gitoids)
}

/// Add a `GitOid` hash to the `OmniBor`.
///
/// Note that this creates a new persistent data structure under the hood.
pub fn add(&self, gitoid: GitOid) -> Self {
self.add_many([gitoid])
}

/// Append many `GitOid`s and return a new `OmniBor`
pub fn add_many<I>(&self, gitoids: I) -> Self
where
I: IntoIterator<Item = GitOid>,
{
let mut updated = self.git_oids.clone(); // im::HashSet has O(1) cloning
for gitoid in gitoids {
updated = updated.update(gitoid);
}
Self { git_oids: updated }
}

/// Return the `Vector` of `GitOid`s.
pub fn get_oids(&self) -> HashSet<GitOid> {
self.git_oids.clone() // im::HashSet as O(1) cloning.
}

/// Get a sorted `Vector` of `GitOid`s.
///
/// In some cases, getting a sorted `Vector` of oids is desirable.
/// This function (cost O(n log n)) returns a `Vector` of sorted oids
pub fn get_sorted_oids(&self) -> Vector<GitOid> {
let mut ret = self.git_oids.clone().into_iter().collect::<Vector<_>>();
ret.sort();
ret
}
}

#[cfg(test)]
mod tests {
use gitoid::{GitOid, HashAlgorithm, ObjectType::Blob};
use im::vector;

use super::*;

#[test]
fn test_add() {
let oid = GitOid::new_from_str(HashAlgorithm::Sha256, Blob, "Hello");
assert_eq!(OmniBor::new().add(oid).get_sorted_oids(), vector![oid])
}

#[test]
fn test_add_many() {
let mut oids: Vector<GitOid> = vec!["eee", "Hello", "Cat", "Dog"]
.into_iter()
.map(|s| GitOid::new_from_str(HashAlgorithm::Sha256, Blob, s))
.collect();

let da_bom = OmniBor::new().add_many(oids.clone());
oids.sort();
assert_eq!(da_bom.get_sorted_oids(), oids);
}

#[test]
fn test_add_gitoid_to_gitbom() {
let input = "hello world".as_bytes();

let generated_gitoid = GitOid::new_from_bytes(HashAlgorithm::Sha256, Blob, input);

let new_gitbom = OmniBor::new();
let new_gitbom = new_gitbom.add(generated_gitoid);

assert_eq!(
"fee53a18d32820613c0527aa79be5cb30173c823a9b448fa4817767cc84c6f03",
new_gitbom.get_sorted_oids()[0].hash().as_hex()
)
}
}
// TODO(abrinker): Complete first draft of the rewrite of this crate.
Loading