diff --git a/Cargo.lock b/Cargo.lock index 1c1ed48..e2dc5d9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -61,6 +61,15 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + [[package]] name = "bsv" version = "0.1.0" @@ -92,6 +101,21 @@ dependencies = [ "vec_map", ] +[[package]] +name = "cpuid-bool" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634" + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + [[package]] name = "error-chain" version = "0.12.4" @@ -102,6 +126,16 @@ dependencies = [ "version_check", ] +[[package]] +name = "generic-array" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" +dependencies = [ + "typenum", + "version_check", +] + [[package]] name = "getrandom" version = "0.1.15" @@ -143,8 +177,10 @@ dependencies = [ name = "libbsv" version = "0.1.0" dependencies = [ + "digest", "error-chain", "serde", + "sha2", "tempfile", "toml", "uuid", @@ -178,6 +214,12 @@ version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ab52be62400ca80aa00285d25253d7f7c437b7375c4de678f5405d3afe82ca5" +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + [[package]] name = "ppv-lite86" version = "0.2.9" @@ -284,6 +326,19 @@ dependencies = [ "syn", ] +[[package]] +name = "sha2" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2933378ddfeda7ea26f48c555bdad8bb446bf8a3d17832dc83e380d444cfb8c1" +dependencies = [ + "block-buffer", + "cfg-if", + "cpuid-bool", + "digest", + "opaque-debug", +] + [[package]] name = "strsim" version = "0.8.0" @@ -333,6 +388,12 @@ dependencies = [ "serde", ] +[[package]] +name = "typenum" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" + [[package]] name = "unicode-width" version = "0.1.8" diff --git a/bsv/src_bak/core/error.rs b/bsv/src_bak/core/error.rs new file mode 100644 index 0000000..662574f --- /dev/null +++ b/bsv/src_bak/core/error.rs @@ -0,0 +1,23 @@ +use std::result; + + +#[derive(Debug, PartialEq, Eq)] +pub enum Error { + InvalidObjectIdSize, + InvalidObjectIdCharacter, +} + + +impl Error { + fn message(&self) -> String { + match self { + Error::InvalidObjectIdSize => + "invalid size for object id string".to_string(), + Error::InvalidObjectIdCharacter => + "invalid character in object id string".to_string(), + } + } +} + + +pub type Result = result::Result; diff --git a/bsv/src_bak/core/mod.rs b/bsv/src_bak/core/mod.rs new file mode 100644 index 0000000..644a358 --- /dev/null +++ b/bsv/src_bak/core/mod.rs @@ -0,0 +1,5 @@ +pub mod error; +pub mod object_id; +pub mod object_type; +pub mod object; +pub mod object_db; diff --git a/bsv/src_bak/core/object.rs b/bsv/src_bak/core/object.rs new file mode 100644 index 0000000..48aa2e0 --- /dev/null +++ b/bsv/src_bak/core/object.rs @@ -0,0 +1,21 @@ +use super::object_id::ObjectId; +use super::object_type::ObjectType; + + +/// An object in an ObjectDb. +/// +/// An Objects is fundamentaly an arbitrary blob of data. It has a unique identifier, a size and +/// a type. +#[derive(Debug)] +pub struct Object { + pub id: ObjectId, + pub otype: ObjectType, + pub size: usize, +} + + +impl Object { + fn new(object_id: ObjectId, otype: ObjectType, size: usize) -> Object { + Object { id: object_id, otype, size } + } +} diff --git a/bsv/src_bak/core/object_db.rs b/bsv/src_bak/core/object_db.rs new file mode 100644 index 0000000..0b3fdc1 --- /dev/null +++ b/bsv/src_bak/core/object_db.rs @@ -0,0 +1,17 @@ +use std::io; + +use super::error::Result; +use super::object_id::ObjectId; +use super::object_type::ObjectType; +use super::object::Object; + + +pub trait ObjectDb { + fn object_exists(&self, oid: &ObjectId) -> Result; + fn get_object(&self, oid: &ObjectId) -> Result>; + fn open_object(&self, oid: &ObjectId) -> Result>; + fn compute_object_id(&self, otype: ObjectType, reader: &dyn io::Read) -> Result; + + fn add_object(&mut self, otype: ObjectType, reader: &dyn io::Read) -> Result; + fn delete_object(&mut self, oid: &ObjectId) -> Result<()>; +} diff --git a/bsv/src_bak/core/object_type.rs b/bsv/src_bak/core/object_type.rs new file mode 100644 index 0000000..200ecec --- /dev/null +++ b/bsv/src_bak/core/object_type.rs @@ -0,0 +1,16 @@ + + +/// Describe the type of an Object. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct ObjectType { + name: String, +} + + +impl ObjectType { + fn new(name: &str) -> ObjectType { + ObjectType { + name: name.to_string(), + } + } +} diff --git a/bsv/src_bak/main.rs b/bsv/src_bak/main.rs new file mode 100644 index 0000000..169cdee --- /dev/null +++ b/bsv/src_bak/main.rs @@ -0,0 +1,7 @@ + +mod core; +mod object_db; + +fn main() { + println!("Hello, world!"); +} diff --git a/libbsv/Cargo.toml b/libbsv/Cargo.toml index e12d902..6df74ab 100644 --- a/libbsv/Cargo.toml +++ b/libbsv/Cargo.toml @@ -11,3 +11,6 @@ serde = "1.0.106" toml = "0.5.6" uuid = { version = "0.8.1", features = ["serde", "v4"] } tempfile = "3.1.0" +digest = "0.9.0" +sha2 = "0.9.1" +flate2 = "1.0.17" diff --git a/libbsv/src/core/error.rs b/libbsv/src/core/error.rs index 28aef9b..9de5112 100644 --- a/libbsv/src/core/error.rs +++ b/libbsv/src/core/error.rs @@ -42,5 +42,10 @@ error_chain! { description("Object id contains invalid character") display("Object id contains invalid character") } + + InvalidObjectType(otype: [u8; 4]) { + description("Object has an invalid object type") + display("Object has an invalid object type {:?}", otype) + } } } diff --git a/libbsv/src/core/object_id.rs b/libbsv/src/core/object_id.rs index 4f13b25..759ab94 100644 --- a/libbsv/src/core/object_id.rs +++ b/libbsv/src/core/object_id.rs @@ -30,9 +30,9 @@ pub struct ObjectId { impl ObjectId { - pub fn new(id: Vec) -> ObjectId { + pub fn new(id: &[u8]) -> ObjectId { ObjectId { - id + id: id.into(), } } @@ -82,7 +82,7 @@ mod tests { #[test] fn object_id_display() { - let obj = ObjectId::new(vec![ + let obj = ObjectId::new(&[ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, ]); @@ -91,7 +91,7 @@ mod tests { #[test] fn object_id_debug() { - let obj = ObjectId::new(vec![ + let obj = ObjectId::new(&[ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, ]); @@ -101,7 +101,7 @@ mod tests { #[test] fn str_to_object_id() { let obj = ObjectId::from_str("0001020304050607").unwrap(); - let obj_ref = ObjectId::new(vec![ + let obj_ref = ObjectId::new(&[ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, ]); diff --git a/libbsv/src/simple_db/mod.rs b/libbsv/src/simple_db/mod.rs index 1502e9a..040f716 100644 --- a/libbsv/src/simple_db/mod.rs +++ b/libbsv/src/simple_db/mod.rs @@ -14,63 +14,8 @@ // along with cdb. If not, see . -use std::path::{Path, PathBuf}; +pub mod simple_db; +pub mod object; -use crate::core::error::*; -use crate::core::object_id::ObjectId; - -const OBJECTS_FOLDER: &str = "objects"; - - -#[derive(Debug)] -pub struct SimpleDb { - path: PathBuf, -} - - -impl SimpleDb { - pub fn new(path: &Path) -> Result { - Ok(SimpleDb { - path: path.into(), - }) - } - - - pub fn has_object(&self, oid: &ObjectId) -> bool { - let obj_path = self.path_from_id(oid); - if let Ok(metadata) = std::fs::metadata(obj_path) { - metadata.is_file() - } - else { - false - } - } - - fn path_from_id(&self, oid: &ObjectId) -> PathBuf { - let oid_str = oid.to_string(); - let mut path = self.path.clone(); - - path.push(OBJECTS_FOLDER); - path.push(oid_str.get(..4).unwrap()); // unwrap is ok here because we know oid_str is a - path.push(oid_str.get(4..).unwrap()); // hexadecimal number (i.e. ascii only). - - path - } -} - - -#[cfg(test)] -mod tests { - use super::ObjectId; - use super::SimpleDb; - - #[test] - fn simple_db_path_from_id() { - let db = SimpleDb::new(std::path::Path::new(".bsv")).unwrap(); - let oid = ObjectId::from_str("0001020304050607").unwrap(); - - let path = db.path_from_id(&oid); - assert_eq!(path, std::path::PathBuf::from(".bsv/objects/0001/020304050607")); - } -} +pub use simple_db::SimpleDb; diff --git a/libbsv/src/simple_db/object.rs b/libbsv/src/simple_db/object.rs new file mode 100644 index 0000000..6ab96d7 --- /dev/null +++ b/libbsv/src/simple_db/object.rs @@ -0,0 +1,161 @@ +// This file is part of bsv. +// +// bsv is free software: you can redistribute it and/or modify it under the +// terms of the GNU Affero General Public License as published by the Free +// Software Foundation, either version 3 of the License, or (at your option) +// any later version. +// +// cdb is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for +// more details. +// +// You should have received a copy of the Affero GNU General Public License +// along with cdb. If not, see . + + +extern crate digest; +extern crate sha2; +extern crate flate2; + +use std::io::{Read, Write, copy}; + +use digest::Digest; + +use crate::core::error::*; +use crate::core::ObjectId; + + +type ObjectType = [u8; 4]; + +pub const TYPE_BLOB: &ObjectType = b"blob"; +pub const TYPE_TREE: &ObjectType = b"tree"; + + +pub struct ObjectWriter<'a, W: Write, D: Digest> { + writer: &'a mut W, + digest: D, +} + + +impl<'a, W: Write, D: Digest> ObjectWriter<'a, W, D> { + pub fn new(writer: &'a mut W, otype: &ObjectType, size: u64) + -> Result> { + + let mut digest = D::new(); + + digest.update(otype); + digest.update(&size.to_be_bytes()); + + writer.write_all(otype)?; + writer.write_all(&size.to_be_bytes())?; + + Ok(ObjectWriter { + writer, + digest, + }) + } + + pub fn close(self) -> Result { + Ok(ObjectId::new(&self.digest.finalize())) + } +} + + +impl<'a, W: Write, D: Digest> Write for ObjectWriter<'a, W, D> { + fn write(&mut self, buf: &[u8]) -> std::io::Result { + let size = self.writer.write(buf)?; + self.digest.update(&buf[..size]); + Ok(size) + } + + fn flush(&mut self) -> std::io::Result<()> { + self.writer.flush() + } +} + + +pub fn write_object<'a, R, W>(reader: &mut R, writer: &'a mut W, + otype: &ObjectType, size: u64) + -> Result where + R: Read, + W: Write { + let mut owriter: ObjectWriter + = ObjectWriter::new(writer, otype, size)?; + + copy(reader, &mut owriter)?; + + owriter.close() +} + + +pub struct ObjectReader { + reader: R, +} + + +impl ObjectReader { + pub fn new(mut reader: R) + -> Result<(ObjectType, u64, ObjectReader)> { + + let mut buffer = [0u8; 12]; + reader.read_exact(&mut buffer)?; + + let otype = { + let mut otype = [0; 4]; + otype.copy_from_slice(&buffer[0..4]); + otype + }; + let size = { + let mut size_bytes = [0; 8]; + size_bytes.copy_from_slice(&buffer[4..12]); + u64::from_be_bytes(size_bytes) + }; + + Ok(( + otype, + size, + ObjectReader { + reader, + } + )) + } + + pub fn close(self) -> Result<()> { + Ok(()) + } +} + + +impl Read for ObjectReader { + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + self.reader.read(buf) + } +} + + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn object_read_write() { + use std::io::{Cursor, Seek, SeekFrom}; + + let payload = b"Hello World!"; + let mut source = Cursor::new(payload); + let mut fake_file = Cursor::new(vec![]); + let mut output = vec![]; + + write_object(&mut source, &mut fake_file, TYPE_BLOB, payload.len() as u64).unwrap(); + fake_file.seek(SeekFrom::Start(0)).unwrap(); + + let (otype, size, mut reader) = ObjectReader::new(fake_file).unwrap(); + let read_size = reader.read_to_end(&mut output).unwrap(); + + assert_eq!(otype, *TYPE_BLOB); + assert_eq!(size, payload.len() as u64); + assert_eq!(read_size, payload.len()); + assert_eq!(output, payload); + } +} diff --git a/libbsv/src/simple_db/simple_db.rs b/libbsv/src/simple_db/simple_db.rs new file mode 100644 index 0000000..a394609 --- /dev/null +++ b/libbsv/src/simple_db/simple_db.rs @@ -0,0 +1,76 @@ +// This file is part of bsv. +// +// bsv is free software: you can redistribute it and/or modify it under the +// terms of the GNU Affero General Public License as published by the Free +// Software Foundation, either version 3 of the License, or (at your option) +// any later version. +// +// cdb is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for +// more details. +// +// You should have received a copy of the Affero GNU General Public License +// along with cdb. If not, see . + + +use std::path::{Path, PathBuf}; + +use crate::core::error::*; +use crate::core::ObjectId; + + +const OBJECTS_FOLDER: &str = "objects"; + + +#[derive(Debug)] +pub struct SimpleDb { + path: PathBuf, +} + + +impl SimpleDb { + pub fn new(path: &Path) -> Result { + Ok(SimpleDb { + path: path.into(), + }) + } + + + pub fn has_object(&self, oid: &ObjectId) -> bool { + let obj_path = self.path_from_id(oid); + if let Ok(metadata) = std::fs::metadata(obj_path) { + metadata.is_file() + } + else { + false + } + } + + fn path_from_id(&self, oid: &ObjectId) -> PathBuf { + let oid_str = oid.to_string(); + let mut path = self.path.clone(); + + path.push(OBJECTS_FOLDER); + path.push(oid_str.get(..4).unwrap()); // unwrap is ok here because we know oid_str is a + path.push(oid_str.get(4..).unwrap()); // hexadecimal number (i.e. ascii only). + + path + } +} + + +#[cfg(test)] +mod tests { + use super::ObjectId; + use super::SimpleDb; + + #[test] + fn simple_db_path_from_id() { + let db = SimpleDb::new(std::path::Path::new(".bsv")).unwrap(); + let oid = ObjectId::from_str("0001020304050607").unwrap(); + + let path = db.path_from_id(&oid); + assert_eq!(path, std::path::PathBuf::from(".bsv/objects/0001/020304050607")); + } +}