Initial commit

master
Avril 4 years ago
commit 146792b65f
Signed by: flanchan
GPG Key ID: 284488987C31F630

4
.gitignore vendored

@ -0,0 +1,4 @@
target/
output/
*~
#*#

440
Cargo.lock generated

@ -0,0 +1,440 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "async-std"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "538ecb01eb64eecd772087e5b6f7540cbc917f047727339a472dafed2185b267"
dependencies = [
"async-task",
"crossbeam-channel",
"crossbeam-deque",
"crossbeam-utils",
"futures-core",
"futures-io",
"futures-timer",
"kv-log-macro",
"log",
"memchr",
"mio",
"mio-uds",
"num_cpus",
"once_cell",
"pin-project-lite",
"pin-utils",
"slab",
]
[[package]]
name = "async-task"
version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ac2c016b079e771204030951c366db398864f5026f84a44dafb0ff20f02085d"
dependencies = [
"libc",
"winapi 0.3.8",
]
[[package]]
name = "autocfg"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
[[package]]
name = "bitflags"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
[[package]]
name = "block-buffer"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b"
dependencies = [
"block-padding",
"byte-tools",
"byteorder",
"generic-array",
]
[[package]]
name = "block-padding"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5"
dependencies = [
"byte-tools",
]
[[package]]
name = "byte-tools"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7"
[[package]]
name = "byteorder"
version = "1.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
[[package]]
name = "cfg-if"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
[[package]]
name = "crossbeam-channel"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cced8691919c02aac3cb0a1bc2e9b73d89e832bf9a06fc579d4e71b68a2da061"
dependencies = [
"crossbeam-utils",
"maybe-uninit",
]
[[package]]
name = "crossbeam-deque"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f02af974daeee82218205558e51ec8768b48cf524bd01d550abe5573a608285"
dependencies = [
"crossbeam-epoch",
"crossbeam-utils",
"maybe-uninit",
]
[[package]]
name = "crossbeam-epoch"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace"
dependencies = [
"autocfg",
"cfg-if",
"crossbeam-utils",
"lazy_static",
"maybe-uninit",
"memoffset",
"scopeguard",
]
[[package]]
name = "crossbeam-utils"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
dependencies = [
"autocfg",
"cfg-if",
"lazy_static",
]
[[package]]
name = "digest"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5"
dependencies = [
"generic-array",
]
[[package]]
name = "fake-simd"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
[[package]]
name = "fuchsia-zircon"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
dependencies = [
"bitflags",
"fuchsia-zircon-sys",
]
[[package]]
name = "fuchsia-zircon-sys"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
[[package]]
name = "futures-core"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f25592f769825e89b92358db00d26f965761e094951ac44d3663ef25b7ac464a"
[[package]]
name = "futures-io"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a638959aa96152c7a4cddf50fcb1e3fede0583b27157c26e67d6f99904090dc6"
[[package]]
name = "futures-timer"
version = "2.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1de7508b218029b0f01662ed8f61b1c964b3ae99d6f25462d0f55a595109df6"
[[package]]
name = "generic-array"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec"
dependencies = [
"typenum",
]
[[package]]
name = "hashlink"
version = "0.1.0"
dependencies = [
"async-std",
"sha2",
]
[[package]]
name = "hermit-abi"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a0d737e0f947a1864e93d33fdef4af8445a00d1ed8dc0c8ddb73139ea6abf15"
dependencies = [
"libc",
]
[[package]]
name = "iovec"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e"
dependencies = [
"libc",
]
[[package]]
name = "kernel32-sys"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
dependencies = [
"winapi 0.2.8",
"winapi-build",
]
[[package]]
name = "kv-log-macro"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c54d9f465d530a752e6ebdc217e081a7a614b48cb200f6f0aee21ba6bc9aabb"
dependencies = [
"log",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99e85c08494b21a9054e7fe1374a732aeadaff3980b6990b94bfd3a70f690005"
[[package]]
name = "log"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7"
dependencies = [
"cfg-if",
]
[[package]]
name = "maybe-uninit"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00"
[[package]]
name = "memchr"
version = "2.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400"
[[package]]
name = "memoffset"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4fc2c02a7e374099d4ee95a193111f72d2110197fe200272371758f6c3643d8"
dependencies = [
"autocfg",
]
[[package]]
name = "mio"
version = "0.6.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "302dec22bcf6bae6dfb69c647187f4b4d0fb6f535521f7bc022430ce8e12008f"
dependencies = [
"cfg-if",
"fuchsia-zircon",
"fuchsia-zircon-sys",
"iovec",
"kernel32-sys",
"libc",
"log",
"miow",
"net2",
"slab",
"winapi 0.2.8",
]
[[package]]
name = "mio-uds"
version = "0.6.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "966257a94e196b11bb43aca423754d87429960a768de9414f3691d6957abf125"
dependencies = [
"iovec",
"libc",
"mio",
]
[[package]]
name = "miow"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919"
dependencies = [
"kernel32-sys",
"net2",
"winapi 0.2.8",
"ws2_32-sys",
]
[[package]]
name = "net2"
version = "0.2.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88"
dependencies = [
"cfg-if",
"libc",
"winapi 0.3.8",
]
[[package]]
name = "num_cpus"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3"
dependencies = [
"hermit-abi",
"libc",
]
[[package]]
name = "once_cell"
version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1c601810575c99596d4afc46f78a678c80105117c379eb3650cf99b8a21ce5b"
[[package]]
name = "opaque-debug"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
[[package]]
name = "pin-project-lite"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "237844750cfbb86f67afe27eee600dfbbcb6188d734139b534cbfbf4f96792ae"
[[package]]
name = "pin-utils"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "scopeguard"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "sha2"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "27044adfd2e1f077f649f59deb9490d3941d674002f7d062870a60ebe9bd47a0"
dependencies = [
"block-buffer",
"digest",
"fake-simd",
"opaque-debug",
]
[[package]]
name = "slab"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
[[package]]
name = "typenum"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33"
[[package]]
name = "winapi"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
[[package]]
name = "winapi"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-build"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "ws2_32-sys"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"
dependencies = [
"winapi 0.2.8",
"winapi-build",
]

@ -0,0 +1,11 @@
[package]
name = "hashlink"
version = "0.1.0"
authors = ["Avril <flanchan@cumallover.me>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
sha2 = "0.8.1"
async-std = "1.3.0"

@ -0,0 +1,135 @@
extern crate sha2;
extern crate async_std;
use sha2::{Sha256, Digest};
use async_std::io::ReadExt;
pub struct FileHash([u8; 32]);
fn copy_slice<T: Copy>(src: &[T], dst: &mut [T]) -> usize {
let mut c =0;
for (d, s) in dst.iter_mut().zip(src.iter()) {
*d = *s;
c += 1;
}
c
}
const BLOCK_SIZE: usize = 32;
async fn copy_to_hash<F: FnMut(&[u8])>(src: &mut async_std::fs::File, mut do_block: F) -> Result<usize, async_std::io::Error>
{
assert!(BLOCK_SIZE>0);
let mut res: usize =0;
let mut buf: [u8; BLOCK_SIZE] = [0; BLOCK_SIZE];
loop {
let n = src.read(&mut buf).await?;
if n == 0 {
break;
}
do_block(&buf[..n]);
res+=n;
}
Ok(res)
}
impl FileHash {
pub fn create(file: &mut std::fs::File) -> Result<Self, std::io::Error> {
let mut hash = Sha256::default();
std::io::copy(file, &mut hash)?;
let mut u: [u8; 32] = [0; 32];
copy_slice(&hash.result()[..], &mut u);
Ok(Self(u))
}
pub async fn create_async(file: &mut async_std::fs::File) -> Result<Self, async_std::io::Error> {
let mut hash = Sha256::default();
//async_std::io::copy(file, &mut hash).await?;
copy_to_hash(file, |buffer| {
&hash.input(buffer);
}).await?;
let mut u: [u8; 32] = [0; 32];
copy_slice(&hash.result()[..], &mut u);
Ok(Self(u))
}
pub fn iter(&self) -> std::slice::Iter<u8>
{
self.0.iter()
}
pub fn iter_mut(&mut self) -> std::slice::IterMut<u8>
{
self.0.iter_mut()
}
}
impl Clone for FileHash {
fn clone(&self) -> Self {
let mut u: [u8; 32] = [0; 32];
copy_slice(&self.0, &mut u);
Self(u)
}
}
impl AsRef<[u8; 32]> for FileHash {
fn as_ref(&self) -> &[u8; 32] {
&self.0
}
}
impl AsMut<[u8; 32]> for FileHash {
fn as_mut(&mut self) -> &mut [u8; 32] {
&mut self.0
}
}
impl Default for FileHash {
fn default() -> Self {
Self([0; 32])
}
}
fn hex_string(bytes: &[u8]) -> String {
bytes.iter().map(|x| format!("{:02x}", x)).collect::<String>()
}
impl std::fmt::Display for FileHash {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", hex_string(&self.0))
}
}
impl std::cmp::PartialEq for FileHash {
fn eq(&self, other: &Self) -> bool {
self.0.iter()
.zip(other.0
.iter())
.map(|(x, y)| x^y)
.fold(0, |x, y| x|y) == 0
}
}
impl std::cmp::Eq for FileHash{}
impl<'a> IntoIterator for &'a mut FileHash {
type Item = &'a mut u8;
type IntoIter = std::slice::IterMut<'a, u8>;
fn into_iter(self) -> Self::IntoIter {
self.0.iter_mut()
}
}
impl<'a> IntoIterator for &'a FileHash {
type Item = &'a u8;
type IntoIter = std::slice::Iter<'a, u8>;
fn into_iter(self) -> Self::IntoIter
{
self.0.iter()
}
}

@ -0,0 +1,101 @@
#![allow(dead_code)]
#![allow(unused_imports)]
extern crate sha2;
use std::fs::{File};
use std::fs;
use std::path::{Path, PathBuf};
use std::io::prelude::*;
use sha2::{Sha256};
mod hash;
mod traverse;
mod pool;
mod rotating_list;
fn funcall<T: FnOnce()>(w: T)
{
w();
}
const THREADS: usize = 3;
const OUTPUT_DIR: &'static str = "./output";
const INPUT_DIR: &'static str = "./target";
macro_rules! join_path {
($($path:expr),*) => {
{
let mut buf = std::path::PathBuf::new();
$(
buf.push($path);
)+
buf
}
}
}
fn output_path(path: &Path) -> Result<PathBuf, &'static str> {
let mut buf = PathBuf::new();
buf.push(OUTPUT_DIR);
if let Some(path) = path.to_str() {
buf.push(&path[INPUT_DIR.len()+1..]);
Ok(buf)
} else {
Err("Bad pathspec")
}
}
fn main() {
let root = traverse::DirectoryIterator::begin(INPUT_DIR).unwrap();
let mut pools = create_pools!(THREADS);
if !Path::new(OUTPUT_DIR).exists() {
if let Err(err) = fs::create_dir(OUTPUT_DIR) {
println!("Could not create output directory: {}", err);
return;
}
}
let hardlink_dir = join_path!(OUTPUT_DIR, ".hashlink");
if !hardlink_dir.exists() {
if let Err(err) = fs::create_dir(hardlink_dir.as_path()) {
println!("Could not create .hashlink directory: {}", err);
return;
}
}
for dir in root {
let hardlink_dir = hardlink_dir.to_path_buf();
send!(pools.get(), move || {
if let Ok(mut file) = File::open(&dir) {
if let Ok(hash) = hash::FileHash::create(&mut file) {
let hlpath = join_path!(hardlink_dir, hash.to_string());
if let Ok(dst) = output_path(Path::new(&dir)) {
println!("{} -> {}", dir, hash);
if let Some(parent) = dst.parent() {
if !parent.exists() {
if let Err(_) = fs::create_dir(parent) {
return;
}
}
}
if !hlpath.exists() {
if let Err(_) = std::fs::copy(&dir, &hlpath) {
return;
}
}
fs::hard_link(hlpath, dst);
}
}
}
});
}
}

@ -0,0 +1,100 @@
use std::thread;
use std::sync::{Arc, Mutex};
use std::sync::mpsc::{channel, Sender, Receiver, SendError, RecvError};
use std::rc::*;
use std::cell::RefCell;
use std::rc::Rc;
#[macro_export]
macro_rules! create_pools {
($n:expr) => {
{
let mut pools = $crate::rotating_list::RotatingList::new();
for _ in 0..$n {
let mut pool = $crate::pool::Pool::spawn(|x| {
funcall(x);
});
pool.join_on_drop = true;
pools.push(pool);
}
pools
}
}
}
#[macro_export]
macro_rules! send {
($i:expr, $($e:expr),+) => {
$(
$i.send($e).unwrap();
)+
}
}
pub struct Pool<T>
where T:std::marker::Send+ 'static
{
sender: Sender<Option<T>>,
thread: Option<thread::JoinHandle<()>>,
pub join_on_drop: bool,
}
impl<T:std::marker::Send+ 'static> Pool<T>
{
pub fn spawn<F: FnMut(T) + std::marker::Send + 'static>(mut lam: F) -> Self {
let (tx, rx): (Sender<Option<T>>, Receiver<Option<T>>) = channel();
let thread = Some(thread::spawn(move || {
loop {
let value = match rx.recv() {
Ok(v) => v,
Err(_) => break,
};
if let Some(value) = value {
lam(value);
} else {
break;
}
}
}));
Self {
sender: tx,
thread: thread,
join_on_drop: false,
}
}
pub fn join(&mut self)
{
if let Some(thread) = self.thread.take() {
let _ = thread.join();
}
}
pub fn close(&mut self) -> Result<(), SendError<Option<T>>>
{
self.sender.send(None)
}
pub fn send(&mut self, value: T) -> Result<(), SendError<Option<T>>>
{
self.sender.send(Some(value))
}
}
impl<T:std::marker::Send+ 'static> Drop for Pool<T>
{
fn drop(&mut self)
{
let _ = self.close();
if self.join_on_drop {
self.join();
}
}
}

@ -0,0 +1,29 @@
pub struct RotatingList<T>(usize, Vec<T>);
impl<T> RotatingList<T>
{
pub fn new() -> Self {
Self(0, Vec::new())
}
pub fn push(&mut self, value: T)
{
self.1.push(value);
}
pub fn get(&mut self) -> &mut T
{
if self.0 >= self.1.len() {
self.0 = 0;
}
self.0 += 1;
&mut self.1[self.0-1]
}
pub fn iter(&mut self) -> &mut Vec<T> {
&mut self.1
}
}

@ -0,0 +1,67 @@
use std::path::Path;
use std::fs::{ReadDir, DirEntry};
use std::vec::*;
use std::io;
pub struct DirectoryIterator<P>
{
root: P,
dirs: Vec<DirEntry>,
}
fn traverse<P>(path: P, out: &mut Vec<DirEntry>) -> io::Result<()>
where P:AsRef<Path>
{
for entry in std::fs::read_dir(path)? {
match entry {
Ok(entry) => out.push(entry),
Err(_) => (),
}
}
Ok(())
}
impl<P> DirectoryIterator<P>
where P: AsRef<Path>
{
pub fn begin(path: P) -> Result<Self, std::io::Error> {
let mut cache = Vec::new();
traverse(&path, &mut cache)?;
Ok(Self{
root: path,
dirs: cache,
})
}
}
impl<P: AsRef<Path>> Iterator for DirectoryIterator<P>
{
type Item = String;
fn next(&mut self) -> Option<Self::Item>
{
let current = self.dirs.pop();
if let Some(current) = current {
let path = current.path();
if path.is_dir() {
let _ = traverse(&path, &mut self.dirs);
self.next()
} else {
match path.as_path().to_str() {
Some(path) => Some(path.to_string()),
None => self.next(),
}
}
} else {
None
}
}
}
Loading…
Cancel
Save