2 use std
::path
::{Path, PathBuf}
;
5 use crypto
::digest
::Digest
;
6 use crypto
::sha2
::Sha512Trunc256
;
10 pub struct ChunkStore
{
13 hasher
: Sha512Trunc256
,
17 const HEX_CHARS
: &'
static [u8; 16] = b
"0123456789abcdef";
19 fn u256_to_hex(digest
: &[u8; 32]) -> String
{
21 let mut buf
= Vec
::<u8>::with_capacity(64);
24 buf
.push(HEX_CHARS
[(digest
[i
] >> 4) as usize]);
25 buf
.push(HEX_CHARS
[(digest
[i
] & 0xf) as usize]);
28 unsafe { String::from_utf8_unchecked(buf) }
31 fn u256_to_prefix(digest
: &[u8; 32]) -> PathBuf
{
33 let mut buf
= Vec
::<u8>::with_capacity(3+1+2+1);
35 buf
.push(HEX_CHARS
[(digest
[0] as usize) >> 4]);
36 buf
.push(HEX_CHARS
[(digest
[0] as usize) &0xf]);
37 buf
.push(HEX_CHARS
[(digest
[1] as usize) >> 4]);
40 buf
.push(HEX_CHARS
[(digest
[1] as usize) & 0xf]);
41 buf
.push(HEX_CHARS
[(digest
[2] as usize) >> 4]);
44 let path
= unsafe { String::from_utf8_unchecked(buf)}
;
51 fn new
<P
: Into
<PathBuf
>>(path
: P
) -> Self {
52 let base
= path
.into();
53 let mut chunk_dir
= base
.clone();
54 chunk_dir
.push(".chunks");
56 let hasher
= Sha512Trunc256
::new();
58 ChunkStore { base, chunk_dir, hasher, mutex: Mutex::new(false) }
61 // fixme: aquire filesystem lock for directory
63 pub fn create
<P
: Into
<PathBuf
>>(path
: P
) -> Result
<Self, Error
> {
65 let me
= Self::new(path
);
67 std
::fs
::create_dir(&me
.base
)?
;
68 std
::fs
::create_dir(&me
.chunk_dir
)?
;
72 let mut l1path
= me
.base
.clone();
73 l1path
.push(format
!("{:03x}",i
));
74 std
::fs
::create_dir(&l1path
)?
;
80 pub fn open
<P
: Into
<PathBuf
>>(path
: P
) -> Result
<Self, Error
> {
82 let me
= Self::new(path
);
84 let metadata
= std
::fs
::metadata(&me
.chunk_dir
)?
;
86 println
!("{:?}", metadata
.file_type());
91 pub fn insert_chunk(&mut self, chunk
: &[u8]) -> Result
<([u8; 32]), Error
> {
94 self.hasher
.input(chunk
);
96 let mut digest
= [0u8; 32];
97 self.hasher
.result(&mut digest
);
98 println
!("DIGEST {}", u256_to_hex(&digest
));
100 let mut chunk_path
= self.base
.clone();
101 let prefix
= u256_to_prefix(&digest
);
102 chunk_path
.push(&prefix
);
103 let digest_str
= u256_to_hex(&digest
);
104 chunk_path
.push(&digest_str
);
106 let lock
= self.mutex
.lock();
108 if let Ok(metadata
) = std
::fs
::metadata(&chunk_path
) {
109 if metadata
.is_file() {
112 bail
!("Got unexpected file type for chunk {}", digest_str
);
116 let mut chunk_dir
= self.base
.clone();
117 chunk_dir
.push(&prefix
);
119 if let Err(_
) = std
::fs
::create_dir(&chunk_dir
) { /* ignore */ }
121 let mut tmp_path
= chunk_path
.clone();
122 tmp_path
.set_extension("tmp");
123 let mut f
= std
::fs
::File
::create(&tmp_path
)?
;
126 if let Err(err
) = std
::fs
::rename(&tmp_path
, &chunk_path
) {
127 if let Err(_
) = std
::fs
::remove_file(&tmp_path
) { /* ignore */ }
128 bail
!("Atomic rename failed for chunk {} - {}", digest_str
, err
);
131 println
!("PATH {:?}", chunk_path
);
142 fn test_chunk_store1() {
144 if let Err(_e
) = std
::fs
::remove_dir_all(".testdir") { /* ignore */ }
146 let chunk_store
= ChunkStore
::open(".testdir");
147 assert
!(chunk_store
.is_err());
149 let mut chunk_store
= ChunkStore
::create(".testdir").unwrap();
150 chunk_store
.insert_chunk(&[0u8, 1u8]).unwrap();
151 chunk_store
.insert_chunk(&[0u8, 1u8]).unwrap();
154 let chunk_store
= ChunkStore
::create(".testdir");
155 assert
!(chunk_store
.is_err());