diff --git a/.github/workflows/CD.yml b/.github/workflows/CD.yml index b8e14b0..a9af10d 100644 --- a/.github/workflows/CD.yml +++ b/.github/workflows/CD.yml @@ -11,8 +11,23 @@ env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SCRIPTS_BASE_URL: https://raw.githubusercontent.com/linksplatform/Scripts/master/MultiProjectRepository + jobs: - testAndDeploy: + rust: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + submodules: true + - name: Rustup nightly + run: | + rustup toolchain install nightly + - name: Rust test + run: | + cd rust + cargo +nightly test --verbose -- --show-output + + csharpTestAndDeploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 @@ -56,18 +71,18 @@ jobs: wget "$SCRIPTS_BASE_URL/publish-release.sh" bash ./publish-release.sh - pushCSharpNuGetToGitHubPackageRegistry: - needs: testAndDeploy - if: github.event_name == 'push' - runs-on: windows-latest - steps: - - uses: actions/checkout@v1 - with: - submodules: true - - uses: nuget/setup-nuget@v1 - - name: Publish CSharp NuGet to GitHub Package Registry - run: | - dotnet build -c Release - dotnet pack -c Release - nuget source Add -Name "GitHub" -Source "https://nuget.pkg.github.com/linksplatform/index.json" -UserName linksplatform -Password ${{ secrets.GITHUB_TOKEN }} - nuget push **/*.nupkg -Source "GitHub" -SkipDuplicate +pushCSharpNuGetToGitHubPackageRegistry: + needs: testAndDeploy + if: github.event_name == 'push' + runs-on: windows-latest + steps: + - uses: actions/checkout@v1 + with: + submodules: true + - uses: nuget/setup-nuget@v1 + - name: Publish CSharp NuGet to GitHub Package Registry + run: | + dotnet build -c Release + dotnet pack -c Release + nuget source Add -Name "GitHub" -Source "https://nuget.pkg.github.com/linksplatform/index.json" -UserName linksplatform -Password ${{ secrets.GITHUB_TOKEN }} + nuget push **/*.nupkg -Source "GitHub" -SkipDuplicate diff --git a/rust/.gitignore b/rust/.gitignore new file mode 100644 index 0000000..96ef6c0 --- /dev/null +++ b/rust/.gitignore @@ -0,0 +1,2 @@ +/target +Cargo.lock diff --git a/rust/Cargo.toml b/rust/Cargo.toml new file mode 100644 index 0000000..fd2ea7c --- /dev/null +++ b/rust/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "triplets" +version = "0.1.0" +authors = ["uselessgoddess"] +edition = "2018" + +build = "build.rs" + +[dependencies] +libc = "0.1" diff --git a/rust/build.rs b/rust/build.rs new file mode 100644 index 0000000..23e51ee --- /dev/null +++ b/rust/build.rs @@ -0,0 +1,4 @@ +fn main() { + println!("cargo:rustc-link-search=native=dylibs"); + println!("cargo:rustc-link-lib=static=Platform.Data.Triplets.Kernel"); +} \ No newline at end of file diff --git a/rust/dylibs/libPlatform.Data.Triplets.Kernel.a b/rust/dylibs/libPlatform.Data.Triplets.Kernel.a new file mode 100644 index 0000000..99d829a Binary files /dev/null and b/rust/dylibs/libPlatform.Data.Triplets.Kernel.a differ diff --git a/rust/src/common.rs b/rust/src/common.rs new file mode 100644 index 0000000..1fd1557 --- /dev/null +++ b/rust/src/common.rs @@ -0,0 +1,65 @@ +#[cfg(windows)] +use std::os::windows::raw::HANDLE; +use libc::c_void; +use libc::int64_t; +use libc::uint64_t; + +pub type SignedInteger = libc::int64_t; +pub type UnsignedInteger = libc::uint64_t; +pub type LinkIndex = UnsignedInteger; + +pub type Visitor = extern "C" fn(LinkIndex); +pub type StoppableVisitor = extern "C" fn(LinkIndex) -> SignedInteger; + +pub const SUCCESS_RESULT: SignedInteger = 0; +pub const ERROR_RESULT: SignedInteger = 1; + +#[repr(C)] +pub struct Link +{ + pub SourceIndex: LinkIndex, + pub TargetIndex: LinkIndex, + pub LinkerIndex: LinkIndex, + pub Timestamp: SignedInteger, + pub BySourceRootIndex: LinkIndex, + pub BySourceLeftIndex: LinkIndex, + pub BySourceRightIndex: LinkIndex, + pub BySourceCount: UnsignedInteger, + pub ByTargetRootIndex: LinkIndex, + pub ByTargetLeftIndex: LinkIndex, + pub ByTargetRightIndex: LinkIndex, + pub ByTargetCount: UnsignedInteger, + pub ByLinkerRootIndex: LinkIndex, + pub ByLinkerLeftIndex: LinkIndex, + pub ByLinkerRightIndex: LinkIndex, + pub ByLinkerCount: UnsignedInteger, +} + +#[repr(C)] +pub struct RawDB +{ + #[cfg(windows)] + pub storageFileHandle: HANDLE, + #[cfg(windows)] + pub storageFileMappingHandle: HANDLE, + #[cfg(unix)] + storageFileHandle: int64_t, + pub storageFileSizeInBytes: int64_t, + pub pointerToMappedRegion: *mut c_void, + + pub currentMemoryPageSizeInBytes: int64_t, + pub serviceBlockSizeInBytes: int64_t, + pub baseLinksSizeInBytes: int64_t, + pub baseBlockSizeInBytes: int64_t, + pub storageFileMinSizeInBytes: int64_t, + + pub pointerToDataSeal: *mut int64_t, + pub pointerToLinkIndexSize: *mut int64_t, + pub pointerToMappingLinksMaxSize: *mut int64_t, + pub lpointerToPointerToMappingLinks: *mut LinkIndex, + pub lpointerToLinksMaxSize: *mut LinkIndex, + pub lpointerToLinksSize: *mut LinkIndex, + pub LpointerToLinks: *mut Link, + + pub pointerToUnusedMarker: *mut Link, +} diff --git a/rust/src/lib.rs b/rust/src/lib.rs new file mode 100644 index 0000000..9bb24ca --- /dev/null +++ b/rust/src/lib.rs @@ -0,0 +1,110 @@ +//#![feature(try_trait)] +#![feature(thread_local)] +#![feature(in_band_lifetimes)] + +use common::{*}; +use link_c::{*}; +//use persistent_memory_manager::{*}; + +pub mod common; +mod link_c; +mod persistent_memory_manager_c; +pub mod persistent_memory_manager; + + +#[cfg(test)] +mod test { + use crate::common::{RawDB, LinkIndex}; + use libc::malloc; + use crate::link_c::{RawDB_new, CreateLink, UpdateLink, DeleteLink}; + use crate::persistent_memory_manager_c::{OpenLinks, CloseLinks, GetLinksCount}; + use std::ffi::CString; + use crate::persistent_memory_manager::Links; + + #[test] + fn it_works() { + std::fs::remove_file("db.links"); + { + let mem = Links::create("db.links"); + println!("{:?}", mem); + // close db file + } + + let mem = Links::open("db.links").unwrap(); + println!("{:?}", mem); + mem.close().unwrap(); + } + + #[test] + fn c_like_CrUD() { + std::fs::remove_file("юникод_data_base.links"); + + unsafe { + let db = RawDB_new(); + + let name = CString::new("юникод_data_base.links").unwrap(); + OpenLinks(db, name.as_ptr()); + + let itself = 0; + + let is_a = CreateLink(db, itself, itself, itself); + let is_not_a = CreateLink(db, itself, itself, is_a); + let link = CreateLink(db, itself, is_a, itself); + let thing = CreateLink(db, itself, is_not_a, link); + + UpdateLink(db, is_a, is_a, is_a, link); + + DeleteLink(db, is_a); + DeleteLink(db, thing); + + println!("Links count: {}", GetLinksCount(db)); + + CloseLinks(db); + + libc::free(db as *mut libc::c_void); + }} + + #[test] + fn CrUD() { + std::fs::remove_file("db.links"); + let mem = Links::create("db.links").unwrap(); + + let itself = 0 as LinkIndex /* optional */; + + let mut is_a = mem.create(itself, itself, itself); + let is_not_a = mem.create(itself, itself, &is_a); + let link = mem.create(itself, &is_a, itself); + let thing = mem.create(itself, &is_not_a, &link); + + let clone = is_a.clone(); + is_a.update(&clone, &clone, &link); + // or + // is_a.update(is_a.index(), is_a.index(), &link); + + is_a.delete(); // delete all + thing.delete(); + + println!("Links count: {:?}", mem.count()); + } + + #[test] + fn multi_file() { + std::fs::remove_file("1.links"); + std::fs::remove_file("2.links"); + + let mem1 = Links::create("1.links").unwrap(); + let mem2 = Links::create("2.links").unwrap(); + + mem1.create(1, 0, 2); + mem2.create(2, 0, 3); + + println!("mem1: {:?}", mem1); + println!("mem1 links count: {}", mem1.count()); + println!("{:-<10}", '-'); + println!("mem2: {:?}", mem2); + println!("mem2 links count: {}", mem1.count()); + println!("{:-<10}", '-'); + println!("mem1 close result: {:?}", mem1.close()); + println!("mem2 close result: {:?}", mem2.close()); + } +} diff --git a/rust/src/link_c.rs b/rust/src/link_c.rs new file mode 100644 index 0000000..a41003b --- /dev/null +++ b/rust/src/link_c.rs @@ -0,0 +1,124 @@ +use crate::common::{*}; + +extern "C" { + pub fn RawDB_new() -> *mut RawDB; + + pub fn GetSourceIndex( + db: *mut RawDB, + index: LinkIndex, + ) -> LinkIndex; + + pub fn GetLinkerIndex( + db: *mut RawDB, + index: LinkIndex, + ) -> LinkIndex; + + pub fn GetTargetIndex( + db: *mut RawDB, + index: LinkIndex, + ) -> LinkIndex; + + pub fn GetTime( + db: *mut RawDB, + index: LinkIndex, + ) -> SignedInteger; + + pub fn CreateLink( + db: *mut RawDB, + source: LinkIndex, + linker: LinkIndex, + target: LinkIndex, + ) -> LinkIndex; + + pub fn SearchLink( + db: *mut RawDB, + source: LinkIndex, + linker: LinkIndex, + target: LinkIndex, + ) -> LinkIndex; + + pub fn ReplaceLink( + db: *mut RawDB, + index: LinkIndex, + replacement: LinkIndex, + ) -> LinkIndex; + + pub fn UpdateLink( + db: *mut RawDB, + index: LinkIndex, + source: LinkIndex, + linker: LinkIndex, + target: LinkIndex, + ) -> LinkIndex; + + pub fn DeleteLink( + db: *mut RawDB, + index: LinkIndex, + ); + + pub fn GetFirstRefererBySourceIndex( + db: *mut RawDB, + index: LinkIndex, + ) -> LinkIndex; + + pub fn GetFirstRefererByLinkerIndex( + db: *mut RawDB, + index: LinkIndex, + ) -> LinkIndex; + + pub fn GetFirstRefererByTargetIndex( + db: *mut RawDB, + index: LinkIndex, + ) -> LinkIndex; + + pub fn GetLinkNumberOfReferersBySource( + db: *mut RawDB, + index: LinkIndex, + ) -> UnsignedInteger; + + pub fn GetLinkNumberOfReferersByLinker( + db: *mut RawDB, + index: LinkIndex, + ) -> UnsignedInteger; + + pub fn GetLinkNumberOfReferersByTarget( + db: *mut RawDB, + index: LinkIndex, + ) -> UnsignedInteger; + + pub fn WalkThroughAllReferersBySource( + db: *mut RawDB, + root: LinkIndex, + visitor: Visitor, + ); + + pub fn WalkThroughReferersBySource( + db: *mut RawDB, + root: LinkIndex, + visitor: StoppableVisitor, + ) -> SignedInteger; + + pub fn WalkThroughAllReferersByLinker( + db: *mut RawDB, + root: LinkIndex, + visitor: Visitor, + ); + + pub fn WalkThroughReferersByLinker( + db: *mut RawDB, + root: LinkIndex, + visitor: StoppableVisitor, + ) -> SignedInteger; + + pub fn WalkThroughAllReferersByTarget( + db: *mut RawDB, + root: LinkIndex, + visitor: Visitor, + ); + + pub fn WalkThroughReferersByTarget( + db: *mut RawDB, + root: LinkIndex, + visitor: StoppableVisitor, + ) -> SignedInteger; +} diff --git a/rust/src/persistent_memory_manager.rs b/rust/src/persistent_memory_manager.rs new file mode 100644 index 0000000..7dac705 --- /dev/null +++ b/rust/src/persistent_memory_manager.rs @@ -0,0 +1,183 @@ +use std::ffi::{CStr, CString, NulError}; +use std::io::{Error, ErrorKind}; +use std::path::Path; + +use libc::{c_char, c_void}; + +use crate::common::{*}; +use crate::link_c::{*}; +use crate::persistent_memory_manager_c::{*}; +use std::ops::Deref; + +pub enum LinkKind { + Source, + Linker, + Target, +} + +pub struct LinkAdapter<'a> { + parent: &'a PersistentMemoryManager, + index: LinkIndex, +} + +impl<'a> Clone for LinkAdapter<'a> { + fn clone(&self) -> Self { + Self { parent: self.parent, index: self.index } + } +} + +pub trait AsIndex { + fn index(&self) -> LinkIndex; +} + +impl AsIndex for LinkIndex { + fn index(&self) -> LinkIndex { + *self + } +} + +impl<'a> AsIndex for LinkAdapter<'a> { + fn index(&self) -> LinkIndex { + self.index + } +} + +impl<'a> AsIndex for &LinkAdapter<'a> { + fn index(&self) -> LinkIndex { + self.index + } +} + +impl<'a> AsIndex for &mut LinkAdapter<'a> { + fn index(&self) -> LinkIndex { + self.index + } +} + +impl<'a> Deref for LinkAdapter<'a> { + type Target = LinkIndex; + + fn deref(&self) -> &Self::Target { + &self.index + } +} + +impl<'a> LinkAdapter<'a> { + // pub fn first_referer_by(&self, kind: LinkKind) -> Self { + // match kind { + // LinkKind::Source => unsafe { + // LinkAdapter { + // parent: self.parent, + // index: GetFirstRefererBySourceIndex(self.index), + // } + // } + // LinkKind::Linker => unsafe { + // LinkAdapter { + // parent: self.parent, + // index: GetFirstRefererByLinkerIndex(self.index), + // } + // } + // LinkKind::Target => unsafe { + // LinkAdapter { + // parent: self.parent, + // index: GetFirstRefererByTargetIndex(self.index), + // } + // } + // } + // } + + pub fn update(&mut self, source: S, linker: L, target: T) + where S: AsIndex, L: AsIndex, T: AsIndex + { + self.index = *self.parent.update(&*self, source, linker, target) + } + + pub fn delete(self) { + self.parent.delete(self) + } +} + +pub struct Links; + +#[derive(Debug)] +pub struct PersistentMemoryManager { + db: *mut RawDB +} + +impl Links { + const ITSELF: LinkIndex = 0; + const CONTINUE: LinkIndex = 0; + const STOP: LinkIndex = 1; + + pub fn open>(path: P) -> std::io::Result { + std::fs::File::open(&path)?; // simulate return an error if the file does not exist + + let raw_path_str = path.as_ref().to_str(); + let path_str; + match raw_path_str { + None => { return Err(Error::from(ErrorKind::NotFound)); } + Some(str) => { path_str = str } + } + + let db = unsafe { RawDB_new() }; + + let c_str = CString::new(path_str)?; + let result = unsafe { OpenLinks(db, c_str.as_ptr()) }; + + match result { + SUCCESS_RESULT => { Ok(PersistentMemoryManager { db }) } + _ => { Err(Error::from_raw_os_error(result as i32)) } + } + } + + pub fn create>(path: P) -> std::io::Result { + let raw_parent = path.as_ref().parent(); + match raw_parent { + None => { Err(Error::from(ErrorKind::NotFound)) } + Some(parent) => { + std::fs::create_dir_all(parent)?; + std::fs::File::create(&path)?; + Self::open(path) + } + } + } +} + +impl PersistentMemoryManager { + pub fn create(&'a self, source: S, linker: L, target: T) -> LinkAdapter<'a> + where S: AsIndex, L: AsIndex, T: AsIndex + { + let index = unsafe { CreateLink(self.db, source.index(), linker.index(), target.index()) }; + LinkAdapter { parent: self, index } + } + + pub fn update(&'a self, index: I, source: S, linker: L, target: T) -> LinkAdapter<'a> + where I: AsIndex, S: AsIndex, L: AsIndex, T: AsIndex + { + let index = unsafe { UpdateLink(self.db, index.index(), source.index(), linker.index(), target.index()) }; + LinkAdapter { parent: self, index } + } + + pub fn delete(&'a self, index: I) { + unsafe { DeleteLink(self.db, index.index()) }; + } + + pub fn count(&self) -> usize { + (unsafe { GetLinksCount(self.db) }) as usize + } + + pub fn close(&self) -> std::io::Result<()> { + let result = unsafe { CloseLinks(self.db) }; + match result { + SUCCESS_RESULT => { Ok(()) } + _ => { Err(Error::from_raw_os_error(result as i32)) } + } + } +} + +impl Drop for PersistentMemoryManager { + fn drop(&mut self) { + self.close(); + unsafe { libc::free(self.db as *mut c_void) } + } +} \ No newline at end of file diff --git a/rust/src/persistent_memory_manager_c.rs b/rust/src/persistent_memory_manager_c.rs new file mode 100644 index 0000000..b12927d --- /dev/null +++ b/rust/src/persistent_memory_manager_c.rs @@ -0,0 +1,18 @@ +use crate::common::{*}; +use libc::c_char; + +extern "C" { + pub fn OpenLinks(db: *mut RawDB, filename: *const c_char) -> SignedInteger; + pub fn CloseLinks(db: *mut RawDB) -> SignedInteger; + + pub fn GetMappedLink(db: *mut RawDB, mapped_index: SignedInteger) -> LinkIndex; + pub fn SetMappedLink(db: *mut RawDB, mapped_index: SignedInteger, link_index: LinkIndex); + + pub fn WalkThroughAllLinks(db: *mut RawDB, visitor: Visitor); + pub fn WalkThroughLinks(db: *mut RawDB, visitor: StoppableVisitor) -> SignedInteger; + + pub fn GetLinksCount(db: *mut RawDB) -> UnsignedInteger; + + pub fn AllocateLink(db: *mut RawDB) -> LinkIndex; + pub fn FreeLink(db: *mut RawDB, link_index: LinkIndex); +} \ No newline at end of file