d02e545d Andy McCall 1 month ago ↶
1
2
3
4
5
6
7
8
9
ff0af1ee unknown 1 month ago
10
5b9c6ad4 Andy McCall 1 month ago ↶
11
use crate :: attest:: Attest;
ff0af1ee unknown 1 month ago
12
use crate :: hash:: { hash_object, header, ObjectRef, ObjectType} ;
1983686f Andy McCall 1 month ago ↶
13
use crate :: identity:: { Identity, Signature} ;
6f8aeb99 Andy McCall 1 month ago ↶
14
use crate :: objects:: { Blob, Change, ObjectError, StateObject} ;
fc1a466a Andy McCall 1 month ago ↶
15
use crate :: receipt:: Receipt;
ff0af1ee unknown 1 month ago
16
use rand:: RngCore;
d02e545d Andy McCall 1 month ago ↶
17
use std:: cell:: RefCell;
ff0af1ee unknown 1 month ago
18
use std:: fs;
d02e545d Andy McCall 1 month ago ↶
19
use std:: io:: { self , Read, Seek, SeekFrom, Write} ;
ff0af1ee unknown 1 month ago
20
use std:: path:: { Path, PathBuf} ;
21
d02e545d Andy McCall 1 month ago ↶
22
const PACK_MAGIC : & [ u8 ; 8 ] = b " OXYPACK1" ;
23
const IDX_MAGIC : & [ u8 ; 8 ] = b " OXYIDX01" ;
24
const IDX_ENTRY : usize = 32 + 8 + 4 + 4 + 1 + 1 ;
25
const FLAG_ZSTD : u8 = 1 ;
a17fc622 Andy McCall 1 month ago ↶
26
27
28
29
const FLAG_DICT : u8 = 2 ;
30
31
32
const DICT_BYTES : usize = 110 < < 10 ;
33
const DICT_MIN_OBJECTS : usize = 1000 ;
34
35
const DICT_OBJECT_MAX : usize = 64 < < 10 ;
d02e545d Andy McCall 1 month ago ↶
36
const ZSTD_LEVEL : i32 = 3 ;
da652d63 Andy McCall 1 month ago ↶
37
38
pub const REPACK_ZSTD_LEVEL : i32 = 9 ;
2b872d07 Andy McCall 1 month ago ↶
39
40
41
const MAX_OBJECT_LEN : u32 = 1 < < 30 ;
d02e545d Andy McCall 1 month ago ↶
42
afaba5f1 Andy McCall 1 month ago ↶
43
44
pub type Object = ( ObjectType, Vec< u8 > ) ;
45
9ae4d15a Andy McCall 1 month ago ↶
46
47
48
pub type Progress < 'a > = & 'a mut dyn FnMut ( & str ) ;
49
50
fn silent ( ) -> impl FnMut( & str ) {
51
| _ | { }
52
}
53
d02e545d Andy McCall 1 month ago ↶
54
# [ derive ( Clone, Copy, Debug ) ]
55
struct PackEntry {
56
offset : u64 ,
57
stored_len : u32 ,
58
raw_len : u32 ,
59
ty : ObjectType,
60
flags : u8 ,
61
}
62
63
64
struct PackIndex {
65
path : PathBuf,
66
refs : Vec< ObjectRef> ,
67
entries : Vec< PackEntry> ,
a17fc622 Andy McCall 1 month ago ↶
68
69
dict : Option< std:: sync:: Arc< zstd:: dict:: DecoderDictionary< 'static > > > ,
d02e545d Andy McCall 1 month ago ↶
70
}
71
a17fc622 Andy McCall 1 month ago ↶
72
impl std:: fmt:: Debug for PackIndex {
73
fn fmt ( & self , f : & mut std:: fmt:: Formatter< '_ > ) -> std:: fmt:: Result {
74
f.debug_struct ( " PackIndex" )
75
.field ( " path" , & self .path)
76
.field ( " objects" , & self .refs.len ( ) )
77
.field ( " dict" , & self .dict.is_some ( ) )
78
.finish ( )
79
}
80
}
81
d02e545d Andy McCall 1 month ago ↶
82
impl PackIndex {
83
fn load ( idx_path : & Path, pack_path : & Path) -> Result< PackIndex> {
84
let data = fs:: read( idx_path) ? ;
75167672 Andy McCall 1 month ago
85
let bad =
86
| m : & str | StoreError:: Corrupt( ObjectRef:: ZERO , format! ( " {} : {m} " , idx_path.display ( ) ) ) ;
d02e545d Andy McCall 1 month ago ↶
87
if data.len ( ) < 12 | | & data[ .. 8 ] ! = IDX_MAGIC {
88
return Err ( bad ( " bad index header" ) ) ;
89
}
90
let count = u32 :: from_le_bytes( data[ 8 .. 12 ] .try_into ( ) .unwrap ( ) ) as usize ;
91
if data.len ( ) ! = 12 + count * IDX_ENTRY {
92
return Err ( bad ( " index length mismatch" ) ) ;
93
}
2b872d07 Andy McCall 1 month ago ↶
94
let pack_len = fs:: metadata( pack_path) ? .len ( ) ;
d02e545d Andy McCall 1 month ago ↶
95
let mut refs = Vec :: with_capacity( count) ;
96
let mut entries = Vec :: with_capacity( count) ;
97
for i in 0 .. count {
98
let e = & data[ 12 + i * IDX_ENTRY .. 12 + ( i + 1 ) * IDX_ENTRY ] ;
99
let r = ObjectRef:: from_slice( & e[ .. 32 ] ) .unwrap ( ) ;
100
if let Some ( prev) = refs.last ( ) {
101
if r < = * prev {
102
return Err ( bad ( " index not sorted" ) ) ;
103
}
104
}
105
refs.push ( r) ;
2b872d07 Andy McCall 1 month ago ↶
106
let entry = PackEntry {
d02e545d Andy McCall 1 month ago ↶
107
offset: u64 :: from_le_bytes( e[ 32 .. 40 ] .try_into ( ) .unwrap ( ) ) ,
108
stored_len: u32 :: from_le_bytes( e[ 40 .. 44 ] .try_into ( ) .unwrap ( ) ) ,
109
raw_len: u32 :: from_le_bytes( e[ 44 .. 48 ] .try_into ( ) .unwrap ( ) ) ,
75167672 Andy McCall 1 month ago
110
ty: ObjectType:: ALL
111
.get ( e[ 48 ] as usize )
112
.copied ( )
113
.ok_or_else ( | | bad ( " unknown object type" ) ) ? ,
d02e545d Andy McCall 1 month ago ↶
114
flags: e[ 49 ] ,
2b872d07 Andy McCall 1 month ago ↶
115
} ;
a9f686ed Andy McCall 1 month ago ↶
116
if entry.stored_len > MAX_OBJECT_LEN | | entry.raw_len > MAX_OBJECT_LEN {
117
return Err ( StoreError:: Corrupt(
118
r,
119
format! (
120
" {} : the index claims an object of {} bytes ({} stored), more than the {MAX_OBJECT_LEN} an object may be" ,
121
idx_path.display ( ) ,
122
entry.raw_len,
123
entry.stored_len
124
) ,
125
) ) ;
126
}
127
if entry.offset.saturating_add ( entry.stored_len as u64 ) > pack_len {
2b872d07 Andy McCall 1 month ago ↶
128
return Err ( bad ( " index entry points outside the pack" ) ) ;
129
}
130
entries.push ( entry) ;
d02e545d Andy McCall 1 month ago ↶
131
}
a17fc622 Andy McCall 1 month ago ↶
132
let dict = match fs:: read( pack_path.with_extension ( " dict" ) ) {
133
Ok ( d) => Some ( std:: sync:: Arc:: new( zstd:: dict:: DecoderDictionary:: copy( & d) ) ) ,
134
Err ( e) if e.kind ( ) = = io:: ErrorKind:: NotFound => None ,
135
Err ( e) => return Err ( e.into ( ) ) ,
136
} ;
75167672 Andy McCall 1 month ago
137
Ok ( PackIndex {
138
path: pack_path.to_path_buf ( ) ,
139
refs,
140
entries,
a17fc622 Andy McCall 1 month ago ↶
141
dict,
75167672 Andy McCall 1 month ago
142
} )
d02e545d Andy McCall 1 month ago ↶
143
}
144
145
fn find ( & self , r : & ObjectRef) -> Option< PackEntry> {
146
self .refs.binary_search ( r) .ok ( ) .map ( | i | self .entries[ i] )
147
}
148
149
fn read ( & self , r : & ObjectRef, e : PackEntry) -> Result< ( ObjectType, Vec< u8 > ) > {
150
let mut f = fs:: File:: open( & self .path) ? ;
151
f.seek ( SeekFrom:: Start( e.offset) ) ? ;
152
let mut stored = vec! [ 0 u8 ; e.stored_len as usize ] ;
153
f.read_exact ( & mut stored) ? ;
a17fc622 Andy McCall 1 month ago ↶
154
let body = if e.flags & FLAG_DICT ! = 0 {
155
let dict = self .dict.as_ref ( ) .ok_or_else ( | | {
156
StoreError:: Corrupt( * r, " packed with a dictionary the pack lacks" .into ( ) )
157
} ) ? ;
158
zstd:: bulk:: Decompressor:: with_prepared_dictionary( dict)
159
.and_then ( | mut d | d.decompress ( & stored, e.raw_len as usize ) )
160
.map_err ( | err | StoreError:: Corrupt( * r, format! ( " zstd: {err} " ) ) ) ?
161
} else if e.flags & FLAG_ZSTD ! = 0 {
75167672 Andy McCall 1 month ago
162
zstd:: bulk:: decompress( & stored, e.raw_len as usize )
163
.map_err ( | err | StoreError:: Corrupt( * r, format! ( " zstd: {err} " ) ) ) ?
d02e545d Andy McCall 1 month ago ↶
164
} else {
165
stored
166
} ;
167
if body.len ( ) ! = e.raw_len as usize | | hash_object ( e.ty, & body) ! = * r {
75167672 Andy McCall 1 month ago
168
return Err ( StoreError:: Corrupt(
169
* r,
170
" packed object does not match its reference" .into ( ) ,
171
) ) ;
d02e545d Andy McCall 1 month ago ↶
172
}
173
Ok ( ( e.ty, body) )
174
}
175
}
176
177
fn type_index ( ty : ObjectType) -> u8 {
178
ObjectType:: ALL .iter ( ) .position ( | t | * t = = ty) .unwrap ( ) as u8
179
}
180
181
182
# [ derive ( Debug, Clone, Default, PartialEq, Eq ) ]
183
pub struct PackReport {
184
pub objects : usize ,
185
pub raw_bytes : u64 ,
186
pub packed_bytes : u64 ,
187
}
188
135212de Andy McCall 1 month ago ↶
189
190
pub const MAX_PACKS : usize = 16 ;
191
ba160f60 Andy McCall 1 month ago ↶
192
193
# [ derive ( Debug, Clone, Default, PartialEq, Eq ) ]
194
pub struct PruneReport {
195
pub objects : usize ,
196
pub bytes : u64 ,
197
198
pub deferred : usize ,
199
}
200
ff0af1ee unknown 1 month ago
201
# [ derive ( Debug, thiserror::Error ) ]
202
pub enum StoreError {
203
# [ error ( " io: {0}" ) ]
204
Io( # [ from ] io:: Error) ,
205
# [ error ( " corrupt object {0}: {1}" ) ]
206
Corrupt( ObjectRef, String ) ,
207
# [ error ( " object {0}" ) ]
208
Object( # [ from ] ObjectError) ,
209
# [ error ( " object {0} is a {1}, expected {2}" ) ]
210
WrongType( ObjectRef, & 'static str , & 'static str ) ,
211
# [ error ( " object {0} not found" ) ]
212
NotFound( ObjectRef) ,
6f8aeb99 Andy McCall 1 month ago ↶
213
# [ error ( " a {kind} object of {size} bytes is larger than the {limit} bytes an object may be; nothing was stored. A change this large adds or touches too many files at once: commit it in parts (a directory at a time)" ) ]
a9f686ed Andy McCall 1 month ago ↶
214
TooLarge {
215
kind: & 'static str ,
216
size: usize ,
217
limit: u32 ,
218
} ,
ff0af1ee unknown 1 month ago
219
}
220
221
pub type Result < T> = std:: result:: Result< T, StoreError> ;
222
d02e545d Andy McCall 1 month ago ↶
223
# [ derive ( Debug ) ]
ff0af1ee unknown 1 month ago
224
pub struct Store {
225
dir : PathBuf,
d02e545d Andy McCall 1 month ago ↶
226
packs : RefCell< Vec< PackIndex> > ,
8f10a07a Andy McCall 1 month ago ↶
227
228
229
pending : RefCell< Option< Pending> > ,
230
batch_depth : std:: cell:: Cell< u32 > ,
231
}
232
233
234
235
236
# [ derive ( Debug ) ]
237
struct Pending {
238
buffered : Vec< ( ObjectType, Vec< u8 > ) > ,
239
buffered_bytes : usize ,
240
lookup : std:: collections:: HashMap< ObjectRef, PendingEntry> ,
241
pack : Option< PendingPack> ,
242
report : PackReport,
ff0af1ee unknown 1 month ago
243
}
244
8f10a07a Andy McCall 1 month ago ↶
245
# [ derive ( Debug, Clone, Copy ) ]
246
enum PendingEntry {
247
Buffered( usize ) ,
248
Packed( PackEntry) ,
249
}
250
251
# [ derive ( Debug ) ]
252
struct PendingPack {
253
id : String,
254
tmp_path : PathBuf,
255
writer : io:: BufWriter< fs:: File> ,
256
offset : u64 ,
257
index : Vec< ( ObjectRef, PackEntry) > ,
258
}
259
260
const SPILL_OBJECTS : usize = 256 ;
261
const SPILL_BYTES : usize = 8 < < 20 ;
262
263
264
pub struct Batch < 'a > {
265
store : & 'a Store,
266
done : bool ,
267
}
268
269
impl Batch < '_ > {
270
pub fn finish ( mut self ) -> Result< ( ) > {
271
self .done = true ;
272
self .store.finish_batch ( )
273
}
274
}
275
276
impl Drop for Batch < '_ > {
277
fn drop ( & mut self ) {
278
if ! self .done {
279
let _ = self .store.finish_batch ( ) ;
280
}
281
}
282
}
283
192fad1e Andy McCall 1 month ago ↶
284
285
fn write_pack_entry (
286
pack : & mut PendingPack,
287
r : & ObjectRef,
288
ty : ObjectType,
289
raw_len : usize ,
290
flags : u8 ,
291
stored : & [u8 ],
292
report : & mut PackReport,
293
) -> Result< PackEntry> {
294
let header_len = 1 + 1 + 4 + 4 + 32 ;
295
pack.writer.write_all ( & [ type_index ( ty) , flags] ) ? ;
296
pack.writer
297
.write_all ( & ( stored.len ( ) as u32 ) .to_le_bytes ( ) ) ? ;
298
pack.writer.write_all ( & ( raw_len as u32 ) .to_le_bytes ( ) ) ? ;
299
pack.writer.write_all ( & r.0 ) ? ;
300
pack.writer.write_all ( stored) ? ;
301
let entry = PackEntry {
302
offset: pack.offset + header_len,
303
stored_len: stored.len ( ) as u32 ,
304
raw_len: raw_len as u32 ,
305
ty,
306
flags,
307
} ;
308
pack.offset + = header_len + stored.len ( ) as u64 ;
309
pack.index.push ( ( * r, entry) ) ;
310
report.objects + = 1 ;
311
report.raw_bytes + = raw_len as u64 ;
312
report.packed_bytes + = stored.len ( ) as u64 ;
313
Ok ( entry)
314
}
315
ff0af1ee unknown 1 month ago
316
impl Store {
317
pub fn open ( dir : impl Into< PathBuf> ) -> io:: Result< Store> {
318
let dir = dir.into ( ) ;
319
fs:: create_dir_all( dir.join ( " loose" ) ) ? ;
75167672 Andy McCall 1 month ago
320
let store = Store {
321
dir,
322
packs: RefCell:: new( Vec :: new( ) ) ,
323
pending: RefCell:: new( None ) ,
324
batch_depth: std:: cell:: Cell:: new( 0 ) ,
325
} ;
326
store
327
.reload_packs ( )
328
.map_err ( | e | io:: Error:: other( e.to_string ( ) ) ) ? ;
d02e545d Andy McCall 1 month ago ↶
329
Ok ( store)
330
}
331
332
fn reload_packs ( & self ) -> Result< ( ) > {
333
let mut packs = Vec :: new( ) ;
334
let mut names: Vec< PathBuf> = fs:: read_dir( & self .dir) ?
335
.filter_map ( | e | e.ok ( ) .map ( | e | e.path ( ) ) )
75167672 Andy McCall 1 month ago
336
.filter ( | p | {
337
p.extension ( ) .is_some_and ( | x | x = = " idx" )
338
& & p.file_name ( )
339
.is_some_and ( | n | n.to_string_lossy ( ) .starts_with ( " pack-" ) )
340
} )
d02e545d Andy McCall 1 month ago ↶
341
.collect ( ) ;
342
names.sort ( ) ;
343
for idx in names {
344
let pack = idx.with_extension ( " pack" ) ;
345
if pack.is_file ( ) {
346
packs.push ( PackIndex:: load( & idx, & pack) ? ) ;
347
}
348
}
349
* self .packs.borrow_mut ( ) = packs;
350
Ok ( ( ) )
351
}
352
353
pub fn pack_count ( & self ) -> usize {
354
self .packs.borrow ( ) .len ( )
355
}
356
357
fn find_packed ( & self , r : & ObjectRef) -> Option< ( usize , PackEntry) > {
75167672 Andy McCall 1 month ago
358
self .packs
359
.borrow ( )
360
.iter ( )
361
.enumerate ( )
362
.find_map ( | ( i , p ) | p.find ( r) .map ( | e | ( i, e) ) )
d02e545d Andy McCall 1 month ago ↶
363
}
364
365
366
fn loose_objects ( & self ) -> io:: Result< Vec< ( ObjectRef, PathBuf) > > {
367
let mut out = Vec :: new( ) ;
368
let loose = self .dir.join ( " loose" ) ;
369
for d in fs:: read_dir( & loose) ? {
370
let d = d? ;
371
if ! d.file_type ( ) ? .is_dir ( ) {
372
continue ;
373
}
374
let prefix = d.file_name ( ) .to_string_lossy ( ) .to_string ( ) ;
375
for f in fs:: read_dir( d.path ( ) ) ? {
376
let f = f? ;
377
let name = f.file_name ( ) .to_string_lossy ( ) .to_string ( ) ;
378
if let Some ( r) = ObjectRef:: from_hex( & format! ( " {prefix} {name} " ) ) {
379
out.push ( ( r, f.path ( ) ) ) ;
380
}
381
}
382
}
383
out.sort ( ) ;
384
Ok ( out)
385
}
386
387
pub fn loose_count ( & self ) -> io:: Result< usize > {
388
Ok ( self .loose_objects ( ) ? .len ( ) )
389
}
390
391
392
393
pub fn pack_loose ( & self ) -> Result< PackReport> {
394
let loose = self .loose_objects ( ) ? ;
75167672 Andy McCall 1 month ago
395
self .write_pack (
396
& loose.iter ( ) .map ( | ( r , _) | * r) .collect:: < Vec< _ > > ( ) ,
397
& loose.iter ( ) .map ( | ( _, p ) | p.clone ( ) ) .collect:: < Vec< _ > > ( ) ,
398
& [ ] ,
da652d63 Andy McCall 1 month ago ↶
399
ZSTD_LEVEL ,
9ae4d15a Andy McCall 1 month ago ↶
400
& mut silent ( ) ,
75167672 Andy McCall 1 month ago
401
)
da652d63 Andy McCall 1 month ago ↶
402
}
403
404
405
406
407
pub fn repack_all ( & self , level : i32 ) -> Result< ( PackReport, usize ) > {
9ae4d15a Andy McCall 1 month ago ↶
408
self .repack_all_with ( level, & mut silent ( ) )
409
}
410
411
412
pub fn repack_all_with ( & self , level : i32 , progress : Progress) -> Result< ( PackReport, usize ) > {
da652d63 Andy McCall 1 month ago ↶
413
let loose = self .loose_objects ( ) ? ;
414
let ( mut refs, paths) : ( Vec< ObjectRef> , Vec< PathBuf> ) = {
415
let packs = self .packs.borrow ( ) ;
416
(
417
packs.iter ( ) .flat_map ( | p | p.refs.iter ( ) .copied ( ) ) .collect ( ) ,
418
packs.iter ( ) .map ( | p | p.path.clone ( ) ) .collect ( ) ,
419
)
420
} ;
421
if loose.is_empty ( ) & & paths.len ( ) < = 1 & & level = = ZSTD_LEVEL {
422
return Ok ( ( PackReport:: default( ) , 0 ) ) ;
423
}
424
refs.extend ( loose.iter ( ) .map ( | ( r , _) | * r) ) ;
425
let loose_paths: Vec< PathBuf> = loose.into_iter ( ) .map ( | ( _, p ) | p ) .collect ( ) ;
426
let n = paths.len ( ) ;
9ae4d15a Andy McCall 1 month ago ↶
427
let report = self .write_pack ( & refs, & loose_paths, & paths, level, progress) ? ;
da652d63 Andy McCall 1 month ago ↶
428
Ok ( ( report, n) )
ba160f60 Andy McCall 1 month ago ↶
429
}
430
135212de Andy McCall 1 month ago ↶
431
432
433
pub fn merge_packs ( & self ) -> Result< usize > {
434
let ( refs, paths) : ( Vec< ObjectRef> , Vec< PathBuf> ) = {
435
let packs = self .packs.borrow ( ) ;
436
if packs.len ( ) < = MAX_PACKS {
437
return Ok ( 0 ) ;
438
}
75167672 Andy McCall 1 month ago
439
(
440
packs.iter ( ) .flat_map ( | p | p.refs.iter ( ) .copied ( ) ) .collect ( ) ,
441
packs.iter ( ) .map ( | p | p.path.clone ( ) ) .collect ( ) ,
442
)
135212de Andy McCall 1 month ago ↶
443
} ;
444
let n = paths.len ( ) ;
9ae4d15a Andy McCall 1 month ago ↶
445
self .write_pack ( & refs, & [ ] , & paths, ZSTD_LEVEL , & mut silent ( ) ) ? ;
135212de Andy McCall 1 month ago ↶
446
Ok ( n)
447
}
448
ba160f60 Andy McCall 1 month ago ↶
449
450
451
452
453
75167672 Andy McCall 1 month ago
454
pub fn prune (
9ae4d15a Andy McCall 1 month ago ↶
455
& self ,
456
keep : & std:: collections:: HashSet< ObjectRef> ,
457
grace : std:: time:: Duration,
458
) -> Result< PruneReport> {
459
self .prune_with ( keep, grace, & mut silent ( ) )
460
}
461
462
463
pub fn prune_with (
75167672 Andy McCall 1 month ago
464
& self ,
465
keep : & std:: collections:: HashSet< ObjectRef> ,
466
grace : std:: time:: Duration,
9ae4d15a Andy McCall 1 month ago ↶
467
progress : Progress,
75167672 Andy McCall 1 month ago
468
) -> Result< PruneReport> {
9ae4d15a Andy McCall 1 month ago ↶
469
progress ( " pruning loose objects" ) ;
ba160f60 Andy McCall 1 month ago ↶
470
let now = std:: time:: SystemTime:: now( ) ;
75167672 Andy McCall 1 month ago
471
let old_enough = | m : & fs:: Metadata| {
472
m.modified ( )
473
.map ( | t | now.duration_since ( t) .unwrap_or_default ( ) > = grace )
474
.unwrap_or ( true )
475
} ;
ba160f60 Andy McCall 1 month ago ↶
476
let mut report = PruneReport:: default( ) ;
477
478
for ( r, path) in self .loose_objects ( ) ? {
479
if keep.contains ( & r) {
480
continue ;
481
}
482
let meta = fs:: metadata( & path) ? ;
483
if ! old_enough ( & meta) {
484
report.deferred + = 1 ;
485
continue ;
486
}
487
report.bytes + = meta.len ( ) ;
488
report.objects + = 1 ;
489
fs:: remove_file( & path) ? ;
490
let _ = fs:: remove_dir( path.parent ( ) .unwrap ( ) ) ;
491
self .remove_sidecars ( & r) ? ;
492
}
493
494
let mut keep_refs: Vec< ObjectRef> = Vec :: new( ) ;
495
let mut old_paths: Vec< PathBuf> = Vec :: new( ) ;
496
let mut rewrite = false ;
9ae4d15a Andy McCall 1 month ago ↶
497
progress ( " pruning packs" ) ;
ba160f60 Andy McCall 1 month ago ↶
498
for p in self .packs.borrow ( ) .iter ( ) {
499
let meta = fs:: metadata( & p.path) ? ;
500
let unreachable = p.refs.iter ( ) .filter ( | r | ! keep.contains ( r) ) .count ( ) ;
501
if unreachable = = 0 {
502
continue ;
503
}
504
if ! old_enough ( & meta) {
505
report.deferred + = unreachable;
506
continue ;
507
}
508
rewrite = true ;
509
old_paths.push ( p.path.clone ( ) ) ;
510
for ( r, e) in p.refs.iter ( ) .zip ( p.entries.iter ( ) ) {
511
if keep.contains ( r) {
512
keep_refs.push ( * r) ;
513
} else {
514
report.objects + = 1 ;
515
report.bytes + = e.stored_len as u64 ;
516
}
517
}
518
}
519
if rewrite {
520
let dropped: Vec< ObjectRef> = {
521
let packs = self .packs.borrow ( ) ;
75167672 Andy McCall 1 month ago
522
packs
523
.iter ( )
524
.filter ( | p | old_paths.contains ( & p.path) )
525
.flat_map ( | p | p.refs.iter ( ) .copied ( ) )
526
.filter ( | r | ! keep.contains ( r) )
527
.collect ( )
ba160f60 Andy McCall 1 month ago ↶
528
} ;
9ae4d15a Andy McCall 1 month ago ↶
529
self .write_pack ( & keep_refs, & [ ] , & old_paths, ZSTD_LEVEL , progress) ? ;
ba160f60 Andy McCall 1 month ago ↶
530
for r in dropped {
531
self .remove_sidecars ( & r) ? ;
532
}
533
}
534
Ok ( report)
535
}
536
537
fn remove_sidecars ( & self , r : & ObjectRef) -> Result< ( ) > {
fc1a466a Andy McCall 1 month ago ↶
538
let sidecars = [
539
self .sig_path ( r) ,
540
self .index_path ( " seals" , r) ,
541
self .index_path ( " receipts" , r) ,
542
] ;
543
for path in sidecars {
ba160f60 Andy McCall 1 month ago ↶
544
match fs:: remove_file( & path) {
545
Ok ( ( ) ) => {
546
let _ = fs:: remove_dir( path.parent ( ) .unwrap ( ) ) ;
547
}
548
Err ( e) if e.kind ( ) = = io:: ErrorKind:: NotFound => { }
549
Err ( e) => return Err ( e.into ( ) ) ,
550
}
551
}
552
Ok ( ( ) )
553
}
554
555
8f10a07a Andy McCall 1 month ago ↶
556
557
75167672 Andy McCall 1 month ago
558
fn finish_pack_files (
559
& self ,
560
id : & str ,
561
tmp_pack : & Path,
562
index : & [( ObjectRef , PackEntry ) ],
563
) -> Result< ( ) > {
8f10a07a Andy McCall 1 month ago ↶
564
let pack_path = self .dir.join ( format! ( " pack-{id} .pack" ) ) ;
565
let idx_path = self .dir.join ( format! ( " pack-{id} .idx" ) ) ;
566
let tmp_idx = self .dir.join ( format! ( " tmp-{id} .idx" ) ) ;
567
{
568
let mut f = io:: BufWriter:: new( fs:: File:: create( & tmp_idx) ? ) ;
569
f.write_all ( IDX_MAGIC ) ? ;
570
f.write_all ( & ( index.len ( ) as u32 ) .to_le_bytes ( ) ) ? ;
571
for ( r, e) in index {
572
f.write_all ( & r.0 ) ? ;
573
f.write_all ( & e.offset.to_le_bytes ( ) ) ? ;
574
f.write_all ( & e.stored_len.to_le_bytes ( ) ) ? ;
575
f.write_all ( & e.raw_len.to_le_bytes ( ) ) ? ;
576
f.write_all ( & [ type_index ( e.ty) , e.flags] ) ? ;
577
}
578
f.flush ( ) ? ;
579
f.get_ref ( ) .sync_all ( ) ? ;
580
}
4dcb8c09 Andy McCall 1 month ago ↶
581
582
crate :: durable:: rename( tmp_pack, & pack_path) ? ;
583
crate :: durable:: rename( & tmp_idx, & idx_path) ? ;
8f10a07a Andy McCall 1 month ago ↶
584
self .reload_packs ( )
585
}
586
75167672 Andy McCall 1 month ago
587
fn write_pack (
588
& self ,
589
refs : & [ObjectRef],
590
loose_paths : & [PathBuf],
591
old_packs : & [PathBuf],
da652d63 Andy McCall 1 month ago ↶
592
level : i32 ,
9ae4d15a Andy McCall 1 month ago ↶
593
progress : Progress,
75167672 Andy McCall 1 month ago
594
) -> Result< PackReport> {
ba160f60 Andy McCall 1 month ago ↶
595
if refs.is_empty ( ) {
596
for p in old_packs {
a17fc622 Andy McCall 1 month ago ↶
597
remove_pack_files ( p) ? ;
ba160f60 Andy McCall 1 month ago ↶
598
}
599
self .reload_packs ( ) ? ;
d02e545d Andy McCall 1 month ago ↶
600
return Ok ( PackReport:: default( ) ) ;
601
}
ba160f60 Andy McCall 1 month ago ↶
602
let mut refs: Vec< ObjectRef> = refs.to_vec ( ) ;
603
refs.sort ( ) ;
604
refs.dedup ( ) ;
d02e545d Andy McCall 1 month ago ↶
605
let mut nonce = [ 0 u8 ; 8 ] ;
606
rand:: rngs:: OsRng.fill_bytes ( & mut nonce) ;
607
let id = hex:: encode( nonce) ;
608
let tmp_pack = self .dir.join ( format! ( " tmp-{id} .pack" ) ) ;
609
let mut report = PackReport:: default( ) ;
ba160f60 Andy McCall 1 month ago ↶
610
let mut index: Vec< ( ObjectRef, PackEntry) > = Vec :: with_capacity( refs.len ( ) ) ;
a17fc622 Andy McCall 1 month ago ↶
611
612
let dict = if level = = REPACK_ZSTD_LEVEL & & refs.len ( ) > = DICT_MIN_OBJECTS {
613
let _t = crate :: timing:: phase( " pack: train a dictionary" ) ;
9ae4d15a Andy McCall 1 month ago ↶
614
progress ( & format! ( " training a dictionary on {} objects" , refs.len ( ) ) ) ;
a17fc622 Andy McCall 1 month ago ↶
615
self .train_dictionary ( & refs)
616
} else {
617
None
618
} ;
619
let encoder = dict
620
.as_ref ( )
621
.map ( | d | zstd:: dict:: EncoderDictionary:: copy( d, level) ) ;
d02e545d Andy McCall 1 month ago ↶
622
{
623
let mut f = io:: BufWriter:: new( fs:: File:: create( & tmp_pack) ? ) ;
624
f.write_all ( PACK_MAGIC ) ? ;
625
let mut offset = PACK_MAGIC .len ( ) as u64 ;
da652d63 Andy McCall 1 month ago ↶
626
627
628
let mut batches = refs.chunks ( 1024 ) ;
629
let mut compressed_batch = Vec :: new( ) ;
9ae4d15a Andy McCall 1 month ago ↶
630
let total = refs.len ( ) ;
631
let mut reported = usize :: MAX ;
da652d63 Andy McCall 1 month ago ↶
632
loop {
633
if compressed_batch.is_empty ( ) {
634
let Some ( batch) = batches.next ( ) else { break } ;
9ae4d15a Andy McCall 1 month ago ↶
635
636
let tenth = report.objects * 10 / total.max ( 1 ) ;
637
if tenth ! = reported {
638
reported = tenth;
639
progress ( & format! ( " packing {} of {total} objects" , report.objects) ) ;
640
}
da652d63 Andy McCall 1 month ago ↶
641
let mut read: Vec< ( ObjectRef, ObjectType, Vec< u8 > , bool ) > =
642
Vec :: with_capacity( batch.len ( ) ) ;
643
for r in batch {
644
let ( ty, body) = self .get ( r) ? ;
645
read.push ( ( * r, ty, body, self .is_nocompress ( r) ) ) ;
75167672 Andy McCall 1 month ago
646
}
da652d63 Andy McCall 1 month ago ↶
647
use rayon:: prelude:: * ;
648
compressed_batch = read
649
.into_par_iter ( )
a17fc622 Andy McCall 1 month ago ↶
650
.map_init (
651
| | {
652
encoder.as_ref ( ) .and_then ( | e | {
653
zstd:: bulk:: Compressor:: with_prepared_dictionary( e) .ok ( )
654
} )
655
} ,
656
| with_dict , ( r , ty , body , raw ) | {
657
let packed = if raw {
658
None
659
} else {
660
661
662
let dicted = with_dict
663
.as_mut ( )
664
.filter ( | _| body.len ( ) < = DICT_OBJECT_MAX )
665
.and_then ( | c | c.compress ( & body) .ok ( ) )
666
.map ( | c | ( FLAG_ZSTD | FLAG_DICT , c) ) ;
667
dicted
668
.or_else ( | | {
669
zstd:: bulk:: compress( & body, level)
670
.ok ( )
671
.map ( | c | ( FLAG_ZSTD , c) )
672
} )
673
.filter ( | ( _, c ) | c.len ( ) < body.len ( ) )
674
} ;
675
( r, ty, body, packed)
676
} ,
677
)
da652d63 Andy McCall 1 month ago ↶
678
.collect:: < Vec< _ > > ( ) ;
679
compressed_batch.reverse ( ) ;
680
}
681
let Some ( ( r, ty, body, packed) ) = compressed_batch.pop ( ) else {
682
continue ;
683
} ;
684
let r = & r;
685
let ( flags, stored) = match packed {
a17fc622 Andy McCall 1 month ago ↶
686
Some ( c) => c,
da652d63 Andy McCall 1 month ago ↶
687
None => ( 0 , body.clone ( ) ) ,
cdb5beba Andy McCall 1 month ago ↶
688
} ;
d02e545d Andy McCall 1 month ago ↶
689
let header_len = 1 + 1 + 4 + 4 + 32 ;
690
f.write_all ( & [ type_index ( ty) , flags] ) ? ;
691
f.write_all ( & ( stored.len ( ) as u32 ) .to_le_bytes ( ) ) ? ;
692
f.write_all ( & ( body.len ( ) as u32 ) .to_le_bytes ( ) ) ? ;
693
f.write_all ( & r.0 ) ? ;
694
f.write_all ( & stored) ? ;
75167672 Andy McCall 1 month ago
695
index.push ( (
696
* r,
697
PackEntry {
698
offset: offset + header_len,
699
stored_len: stored.len ( ) as u32 ,
700
raw_len: body.len ( ) as u32 ,
701
ty,
702
flags,
703
} ,
704
) ) ;
d02e545d Andy McCall 1 month ago ↶
705
offset + = header_len + stored.len ( ) as u64 ;
706
report.objects + = 1 ;
707
report.raw_bytes + = body.len ( ) as u64 ;
708
report.packed_bytes + = stored.len ( ) as u64 ;
709
}
710
f.flush ( ) ? ;
711
f.get_ref ( ) .sync_all ( ) ? ;
a17fc622 Andy McCall 1 month ago ↶
712
}
713
714
if let Some ( d) = & dict {
715
crate :: durable:: write_atomic( & self .dir.join ( format! ( " pack-{id} .dict" ) ) , d) ? ;
d02e545d Andy McCall 1 month ago ↶
716
}
9ae4d15a Andy McCall 1 month ago ↶
717
progress ( & format! ( " writing the index of {} objects" , index.len ( ) ) ) ;
8f10a07a Andy McCall 1 month ago ↶
718
self .finish_pack_files ( & id, & tmp_pack, & index) ? ;
ba160f60 Andy McCall 1 month ago ↶
719
for p in old_packs {
a17fc622 Andy McCall 1 month ago ↶
720
remove_pack_files ( p) ? ;
ba160f60 Andy McCall 1 month ago ↶
721
}
d02e545d Andy McCall 1 month ago ↶
722
self .reload_packs ( ) ? ;
ba160f60 Andy McCall 1 month ago ↶
723
for path in loose_paths {
d02e545d Andy McCall 1 month ago ↶
724
fs:: remove_file( path) ? ;
725
if let Some ( dir) = path.parent ( ) {
726
let _ = fs:: remove_dir( dir) ;
727
}
728
}
729
Ok ( report)
ff0af1ee unknown 1 month ago
730
}
731
a17fc622 Andy McCall 1 month ago ↶
732
733
734
fn train_dictionary ( & self , refs : & [ObjectRef]) -> Option< Vec< u8 > > {
735
let step = ( refs.len ( ) / 10_000 ) .max ( 1 ) ;
736
let mut samples: Vec< Vec< u8 > > = Vec :: new( ) ;
737
let mut total = 0 usize ;
738
for r in refs.iter ( ) .step_by ( step) {
739
if total > 32 < < 20 {
740
break ;
741
}
742
let Ok ( ( ty, body) ) = self .get ( r) else {
743
continue ;
744
} ;
745
if matches! (
746
ty,
747
ObjectType:: Chunk | ObjectType:: Blob | ObjectType:: Change
748
) & & ! body.is_empty ( )
749
& & body.len ( ) < 1 < < 20
750
{
751
total + = body.len ( ) ;
752
samples.push ( body) ;
753
}
754
}
755
zstd:: dict:: from_samples( & samples, DICT_BYTES ) .ok ( )
756
}
757
ff0af1ee unknown 1 month ago
758
pub fn dir ( & self ) -> & Path {
759
& self .dir
760
}
761
387335ff Andy McCall 1 month ago ↶
762
pub ( crate ) fn path_for ( & self , r : & ObjectRef) -> PathBuf {
ff0af1ee unknown 1 month ago
763
let h = r.to_hex ( ) ;
764
self .dir.join ( " loose" ) .join ( & h[ .. 2 ] ) .join ( & h[ 2 .. ] )
765
}
766
767
pub fn has ( & self , r : & ObjectRef) -> bool {
75167672 Andy McCall 1 month ago
768
self .path_for ( r) .is_file ( )
769
| | self .find_packed ( r) .is_some ( )
770
| | self
771
.pending
772
.borrow ( )
773
.as_ref ( )
774
.is_some_and ( | p | p.lookup.contains_key ( r) )
ff0af1ee unknown 1 month ago
775
}
776
8f10a07a Andy McCall 1 month ago ↶
777
778
779
780
781
pub fn begin_batch ( & self ) -> Result< Batch< '_ > > {
782
let depth = self .batch_depth.get ( ) ;
783
if depth = = 0 {
75167672 Andy McCall 1 month ago
784
* self .pending.borrow_mut ( ) = Some ( Pending {
785
buffered: vec! [ ] ,
786
buffered_bytes: 0 ,
787
lookup: Default :: default( ) ,
788
pack: None ,
789
report: PackReport:: default( ) ,
790
} ) ;
8f10a07a Andy McCall 1 month ago ↶
791
}
792
self .batch_depth.set ( depth + 1 ) ;
75167672 Andy McCall 1 month ago
793
Ok ( Batch {
794
store: self ,
795
done: false ,
796
} )
8f10a07a Andy McCall 1 month ago ↶
797
}
798
799
fn finish_batch ( & self ) -> Result< ( ) > {
800
let depth = self .batch_depth.get ( ) ;
801
if depth = = 0 {
802
return Ok ( ( ) ) ;
803
}
804
self .batch_depth.set ( depth - 1 ) ;
805
if depth > 1 {
806
return Ok ( ( ) ) ;
807
}
75167672 Andy McCall 1 month ago
808
let Some ( pending) = self .pending.borrow_mut ( ) .take ( ) else {
809
return Ok ( ( ) ) ;
810
} ;
4dcb8c09 Andy McCall 1 month ago ↶
811
self .write_pending ( pending)
812
}
813
814
815
816
817
pub fn flush ( & self ) -> Result< ( ) > {
818
let Some ( pending) = self .pending.borrow_mut ( ) .take ( ) else {
819
return Ok ( ( ) ) ;
820
} ;
821
self .write_pending ( pending) ? ;
822
* self .pending.borrow_mut ( ) = Some ( Pending {
823
buffered: vec! [ ] ,
824
buffered_bytes: 0 ,
825
lookup: Default :: default( ) ,
826
pack: None ,
827
report: PackReport:: default( ) ,
828
} ) ;
829
Ok ( ( ) )
830
}
831
832
fn write_pending ( & self , pending : Pending) -> Result< ( ) > {
8f10a07a Andy McCall 1 month ago ↶
833
match pending.pack {
834
None => {
835
for ( ty, body) in & pending.buffered {
836
self .put_loose ( * ty, body) ? ;
837
}
838
Ok ( ( ) )
839
}
840
Some ( mut pack) => {
841
pack.writer.flush ( ) ? ;
842
pack.writer.get_ref ( ) .sync_all ( ) ? ;
843
drop ( pack.writer) ;
844
pack.index.sort_by_key ( | ( r , _) | * r) ;
845
self .finish_pack_files ( & pack.id, & pack.tmp_path, & pack.index) ? ;
846
Ok ( ( ) )
847
}
848
}
849
}
850
851
852
fn pending_get ( & self , r : & ObjectRef) -> Option< Result< ( ObjectType, Vec< u8 > ) > > {
853
let mut guard = self .pending.borrow_mut ( ) ;
854
let pending = guard.as_mut ( ) ? ;
855
let entry = * pending.lookup.get ( r) ? ;
856
Some ( match entry {
857
PendingEntry:: Buffered( i) => {
858
let ( ty, body) = & pending.buffered[ i] ;
859
Ok ( ( * ty, body.clone ( ) ) )
860
}
861
PendingEntry:: Packed( e) => {
75167672 Andy McCall 1 month ago
862
let Some ( pack) = pending.pack.as_mut ( ) else {
863
return Some ( Err ( StoreError:: NotFound( * r) ) ) ;
864
} ;
8f10a07a Andy McCall 1 month ago ↶
865
match pack.writer.flush ( ) {
75167672 Andy McCall 1 month ago
866
Ok ( ( ) ) => PackIndex {
867
path: pack.tmp_path.clone ( ) ,
868
refs: vec! [ ] ,
869
entries: vec! [ ] ,
a17fc622 Andy McCall 1 month ago ↶
870
dict: None ,
75167672 Andy McCall 1 month ago
871
}
872
.read ( r, e) ,
8f10a07a Andy McCall 1 month ago ↶
873
Err ( err) => Err ( err.into ( ) ) ,
874
}
875
}
876
} )
877
}
878
879
75167672 Andy McCall 1 month ago
880
fn pending_pack_write (
881
& self ,
882
pack : & mut PendingPack,
883
r : & ObjectRef,
884
ty : ObjectType,
885
body : & [u8 ],
886
report : & mut PackReport,
887
) -> Result< PackEntry> {
8f10a07a Andy McCall 1 month ago ↶
888
let ( flags, stored) = if self .is_nocompress ( r) {
889
( 0 , body.to_vec ( ) )
890
} else {
75167672 Andy McCall 1 month ago
891
let compressed = zstd:: bulk:: compress( body, ZSTD_LEVEL )
892
.map_err ( | e | StoreError:: Corrupt( * r, format! ( " zstd: {e} " ) ) ) ? ;
893
if compressed.len ( ) < body.len ( ) {
894
( FLAG_ZSTD , compressed)
895
} else {
896
( 0 , body.to_vec ( ) )
897
}
8f10a07a Andy McCall 1 month ago ↶
898
} ;
192fad1e Andy McCall 1 month ago ↶
899
write_pack_entry ( pack, r, ty, body.len ( ) , flags, & stored, report)
8f10a07a Andy McCall 1 month ago ↶
900
}
901
902
fn put_pending ( & self , r : ObjectRef, ty : ObjectType, body : & [u8 ]) -> Result< ( ) > {
903
let mut guard = self .pending.borrow_mut ( ) ;
904
let pending = guard.as_mut ( ) .expect ( " batch open" ) ;
905
if pending.lookup.contains_key ( & r) {
906
return Ok ( ( ) ) ;
907
}
75167672 Andy McCall 1 month ago
908
if pending.pack.is_none ( )
909
& & pending.buffered.len ( ) < SPILL_OBJECTS
910
& & pending.buffered_bytes + body.len ( ) < = SPILL_BYTES
911
{
8f10a07a Andy McCall 1 month ago ↶
912
pending.buffered.push ( ( ty, body.to_vec ( ) ) ) ;
913
pending.buffered_bytes + = body.len ( ) ;
75167672 Andy McCall 1 month ago
914
pending
915
.lookup
916
.insert ( r, PendingEntry:: Buffered( pending.buffered.len ( ) - 1 ) ) ;
8f10a07a Andy McCall 1 month ago ↶
917
return Ok ( ( ) ) ;
918
}
192fad1e Andy McCall 1 month ago ↶
919
self .ensure_pack ( pending) ? ;
920
let mut report = std:: mem:: take( & mut pending.report) ;
921
let pack = pending.pack.as_mut ( ) .unwrap ( ) ;
922
let e = self .pending_pack_write ( pack, & r, ty, body, & mut report) ? ;
923
pending.report = report;
924
pending.lookup.insert ( r, PendingEntry:: Packed( e) ) ;
925
Ok ( ( ) )
926
}
927
928
929
930
fn ensure_pack ( & self , pending : & mut Pending) -> Result< ( ) > {
8f10a07a Andy McCall 1 month ago ↶
931
if pending.pack.is_none ( ) {
932
933
let mut nonce = [ 0 u8 ; 8 ] ;
934
rand:: rngs:: OsRng.fill_bytes ( & mut nonce) ;
935
let id = hex:: encode( nonce) ;
936
let tmp_path = self .dir.join ( format! ( " tmp-{id} .pack" ) ) ;
937
let mut writer = io:: BufWriter:: new( fs:: File:: create( & tmp_path) ? ) ;
938
writer.write_all ( PACK_MAGIC ) ? ;
75167672 Andy McCall 1 month ago
939
let mut pack = PendingPack {
940
id,
941
tmp_path,
942
writer,
943
offset: PACK_MAGIC .len ( ) as u64 ,
944
index: vec! [ ] ,
945
} ;
8f10a07a Andy McCall 1 month ago ↶
946
let buffered = std:: mem:: take( & mut pending.buffered) ;
947
pending.buffered_bytes = 0 ;
948
let mut report = std:: mem:: take( & mut pending.report) ;
949
for ( bty, bbody) in & buffered {
950
let br = hash_object ( * bty, bbody) ;
951
let e = self .pending_pack_write ( & mut pack, & br, * bty, bbody, & mut report) ? ;
952
pending.lookup.insert ( br, PendingEntry:: Packed( e) ) ;
953
}
954
pending.report = report;
955
pending.pack = Some ( pack) ;
192fad1e Andy McCall 1 month ago ↶
956
}
957
Ok ( ( ) )
958
}
959
960
961
962
963
pub fn put_many ( & self , objects : & [( ObjectType , &[u8 ]) ]) -> Result< Vec< ObjectRef> > {
964
use rayon:: prelude:: * ;
965
let total: usize = objects.iter ( ) .map ( | ( _, b ) | b.len ( ) ) .sum ( ) ;
966
if self .pending.borrow ( ) .is_none ( ) | | total < SPILL_BYTES {
967
return objects.iter ( ) .map ( | ( ty , b ) | self .put ( * ty, b) ) .collect ( ) ;
968
}
969
for ( ty, b) in objects {
970
if b.len ( ) > MAX_OBJECT_LEN as usize {
971
return Err ( StoreError:: TooLarge {
972
kind: ty.name ( ) ,
973
size: b.len ( ) ,
974
limit: MAX_OBJECT_LEN ,
975
} ) ;
976
}
8f10a07a Andy McCall 1 month ago ↶
977
}
192fad1e Andy McCall 1 month ago ↶
978
let refs: Vec< ObjectRef> = objects
979
.par_iter ( )
980
.map ( | ( ty , b ) | hash_object ( * ty, b) )
981
.collect ( ) ;
982
983
let mut seen: std:: collections:: HashSet< ObjectRef> = std:: collections:: HashSet:: new( ) ;
984
let mut todo: Vec< usize > = Vec :: new( ) ;
985
for ( i, r) in refs.iter ( ) .enumerate ( ) {
986
let known = self
987
.pending
988
.borrow ( )
989
.as_ref ( )
990
.is_some_and ( | p | p.lookup.contains_key ( r) ) ;
991
if seen.insert ( * r)
992
& & ! known
993
& & ! self .path_for ( r) .is_file ( )
994
& & self .find_packed ( r) .is_none ( )
995
{
996
todo.push ( i) ;
997
}
998
}
999
let raw: std:: collections:: HashSet< ObjectRef> =
1000
match fs:: read_dir( self .dir.join ( " nocompress" ) ) {
1001
Ok ( rd) => rd
1002
.flatten ( )
1003
.filter_map ( | e | ObjectRef:: from_hex( & e.file_name ( ) .to_string_lossy ( ) ) )
1004
.collect ( ) ,
1005
Err ( _ ) => std:: collections:: HashSet:: new( ) ,
1006
} ;
1007
let packed: Vec< ( u8 , Vec< u8 > ) > = todo
1008
.par_iter ( )
1009
.map ( | i | {
1010
let body = objects[ * i] .1 ;
1011
if raw.contains ( & refs[ * i] ) {
1012
return ( 0 , body.to_vec ( ) ) ;
1013
}
1014
match zstd:: bulk:: compress( body, ZSTD_LEVEL ) {
1015
Ok ( c) if c.len ( ) < body.len ( ) => ( FLAG_ZSTD , c) ,
1016
_ => ( 0 , body.to_vec ( ) ) ,
1017
}
1018
} )
1019
.collect ( ) ;
1020
let mut guard = self .pending.borrow_mut ( ) ;
1021
let pending = guard.as_mut ( ) .expect ( " batch open" ) ;
1022
self .ensure_pack ( pending) ? ;
8f10a07a Andy McCall 1 month ago ↶
1023
let mut report = std:: mem:: take( & mut pending.report) ;
1024
let pack = pending.pack.as_mut ( ) .unwrap ( ) ;
192fad1e Andy McCall 1 month ago ↶
1025
for ( i, ( flags, stored) ) in todo.iter ( ) .zip ( packed) {
1026
let ( ty, body) = objects[ * i] ;
1027
let e = write_pack_entry ( pack, & refs[ * i] , ty, body.len ( ) , flags, & stored, & mut report) ? ;
1028
pending.lookup.insert ( refs[ * i] , PendingEntry:: Packed( e) ) ;
1029
}
8f10a07a Andy McCall 1 month ago ↶
1030
pending.report = report;
192fad1e Andy McCall 1 month ago ↶
1031
Ok ( refs)
8f10a07a Andy McCall 1 month ago ↶
1032
}
1033
ff0af1ee unknown 1 month ago
1034
1035
pub fn put ( & self , ty : ObjectType, body : & [u8 ]) -> Result< ObjectRef> {
a9f686ed Andy McCall 1 month ago ↶
1036
1037
1038
1039
if body.len ( ) > MAX_OBJECT_LEN as usize {
1040
return Err ( StoreError:: TooLarge {
1041
kind: ty.name ( ) ,
1042
size: body.len ( ) ,
1043
limit: MAX_OBJECT_LEN ,
1044
} ) ;
1045
}
ff0af1ee unknown 1 month ago
1046
let r = hash_object ( ty, body) ;
1047
let path = self .path_for ( & r) ;
d02e545d Andy McCall 1 month ago ↶
1048
if path.is_file ( ) | | self .find_packed ( & r) .is_some ( ) {
ff0af1ee unknown 1 month ago
1049
return Ok ( r) ;
1050
}
8f10a07a Andy McCall 1 month ago ↶
1051
if self .pending.borrow ( ) .is_some ( ) {
1052
self .put_pending ( r, ty, body) ? ;
1053
return Ok ( r) ;
1054
}
1055
self .put_loose ( ty, body) ? ;
1056
Ok ( r)
1057
}
1058
1059
1060
fn put_loose ( & self , ty : ObjectType, body : & [u8 ]) -> Result< ObjectRef> {
1061
let r = hash_object ( ty, body) ;
1062
let path = self .path_for ( & r) ;
1063
if path.is_file ( ) {
1064
return Ok ( r) ;
1065
}
4dcb8c09 Andy McCall 1 month ago ↶
1066
let fanout = path.parent ( ) .unwrap ( ) ;
1067
if ! fanout.is_dir ( ) {
1068
fs:: create_dir_all( fanout) ? ;
1069
crate :: durable:: sync_dir( & self .dir.join ( " loose" ) ) ? ;
1070
}
ff0af1ee unknown 1 month ago
1071
let mut nonce = [ 0 u8 ; 8 ] ;
1072
rand:: rngs:: OsRng.fill_bytes ( & mut nonce) ;
75167672 Andy McCall 1 month ago
1073
let tmp = self
1074
.dir
1075
.join ( " loose" )
1076
.join ( format! ( " tmp-{} " , hex:: encode( nonce) ) ) ;
4dcb8c09 Andy McCall 1 month ago ↶
1077
crate :: durable:: step( " write loose object" ) ? ;
ff0af1ee unknown 1 month ago
1078
{
1079
let mut f = fs:: File:: create( & tmp) ? ;
1080
f.write_all ( & header ( ty, body.len ( ) ) ) ? ;
1081
f.write_all ( body) ? ;
1082
f.sync_all ( ) ? ;
1083
}
4dcb8c09 Andy McCall 1 month ago ↶
1084
crate :: durable:: rename( & tmp, & path) ? ;
ff0af1ee unknown 1 month ago
1085
Ok ( r)
1086
}
1087
8f10a07a Andy McCall 1 month ago ↶
1088
ff0af1ee unknown 1 month ago
1089
pub fn get ( & self , r : & ObjectRef) -> Result< ( ObjectType, Vec< u8 > ) > {
afaba5f1 Andy McCall 1 month ago ↶
1090
match read_loose_at ( & self .path_for ( r) , r) {
1091
Some ( found) => found,
1092
None => match self .find_packed ( r) {
1093
Some ( ( i, e) ) => self .packs.borrow ( ) [ i] .read ( r, e) ,
1094
None => self .pending_get ( r) .unwrap_or ( Err ( StoreError:: NotFound( * r) ) ) ,
1095
} ,
1096
}
1097
}
1098
1099
1100
1101
1102
pub fn get_many ( & self , refs : & [ObjectRef]) -> Vec< Result< Object> > {
1103
use rayon:: prelude:: * ;
1104
let paths: Vec< ( ObjectRef, PathBuf) > =
1105
refs.iter ( ) .map ( | r | ( * r, self .path_for ( r) ) ) .collect ( ) ;
1106
let mut found: Vec< Option< Result< Object> > > =
1107
paths.par_iter ( ) .map ( | ( r , p ) | read_loose_at ( p, r) ) .collect ( ) ;
1108
1109
let packs = self .packs.borrow ( ) ;
1110
let packed: Vec< ( usize , usize , PackEntry) > = found
75167672 Andy McCall 1 month ago
1111
.iter ( )
afaba5f1 Andy McCall 1 month ago ↶
1112
.enumerate ( )
1113
.filter ( | ( _, f ) | f.is_none ( ) )
1114
.filter_map ( | ( i , _) | {
1115
packs
1116
.iter ( )
1117
.enumerate ( )
1118
.find_map ( | ( p , pack ) | pack.find ( & refs[ i] ) .map ( | e | ( i, p, e) ) )
1119
} )
1120
.collect ( ) ;
1121
let indexes: & [ PackIndex] = & packs;
1122
let read: Vec< ( usize , Result< Object> ) > = packed
1123
.par_iter ( )
1124
.map ( | ( i , p , e ) | ( * i, indexes[ * p] .read ( & refs[ * i] , * e) ) )
1125
.collect ( ) ;
1126
drop ( packs) ;
1127
for ( i, r) in read {
1128
found[ i] = Some ( r) ;
ff0af1ee unknown 1 month ago
1129
}
afaba5f1 Andy McCall 1 month ago ↶
1130
found
1131
.into_iter ( )
1132
.zip ( refs)
1133
.map ( | ( f , r ) | match f {
1134
Some ( f) => f,
1135
None => self .get ( r) ,
1136
} )
1137
.collect ( )
ff0af1ee unknown 1 month ago
1138
}
1139
1140
pub fn get_typed ( & self , r : & ObjectRef, want : ObjectType) -> Result< Vec< u8 > > {
1141
let ( ty, body) = self .get ( r) ? ;
1142
if ty ! = want {
1143
return Err ( StoreError:: WrongType( * r, ty.name ( ) , want.name ( ) ) ) ;
1144
}
1145
Ok ( body)
1146
}
1147
6f8aeb99 Andy McCall 1 month ago ↶
1148
1149
ff0af1ee unknown 1 month ago
1150
pub fn put_change ( & self , c : & Change) -> Result< ObjectRef> {
6f8aeb99 Andy McCall 1 month ago ↶
1151
let ( bytes, texts) = c.encode_parts ( ) ;
192fad1e Andy McCall 1 month ago ↶
1152
self .put_change_encoded ( & bytes, & texts)
6f8aeb99 Andy McCall 1 month ago ↶
1153
}
1154
192fad1e Andy McCall 1 month ago ↶
1155
1156
pub fn put_change_encoded (
1157
& self ,
1158
bytes : & [u8 ],
1159
texts : & [crate:: objects:: StoredText],
1160
) -> Result< ObjectRef> {
1161
1162
1163
let mut chunks: Vec< ( ObjectType, & [u8 ]) > = Vec :: new( ) ;
1164
for t in texts {
1165
if ! self .has ( & t.blob.hash ( ) ) {
1166
chunks.extend (
1167
crate :: chunk:: chunks( & t.content)
1168
.into_iter ( )
1169
.map ( | c | ( ObjectType:: Chunk, c) ) ,
1170
) ;
1171
}
6f8aeb99 Andy McCall 1 month ago ↶
1172
}
192fad1e Andy McCall 1 month ago ↶
1173
self .put_many ( & chunks) ? ;
1174
for t in texts {
1175
self .put ( ObjectType:: Blob, & t.blob.encode ( ) ) ? ;
6f8aeb99 Andy McCall 1 month ago ↶
1176
}
192fad1e Andy McCall 1 month ago ↶
1177
self .put ( ObjectType:: Change, bytes)
ff0af1ee unknown 1 month ago
1178
}
1179
1180
pub fn get_change ( & self , r : & ObjectRef) -> Result< Change> {
6f8aeb99 Andy McCall 1 month ago ↶
1181
Ok ( self .get_change_parts ( r) ? .0 )
1182
}
1183
1184
1185
1186
pub fn get_change_parts ( & self , r : & ObjectRef) -> Result< ( Change, Vec< ObjectRef> ) > {
1187
let bytes = self .get_typed ( r, ObjectType:: Change) ? ;
1188
let mut parts = Vec :: new( ) ;
1189
let mut failed = None ;
1190
let decoded = Change:: decode_with( & bytes, & mut | text, count| match self .read_text ( text) {
1191
Ok ( ( blob, content) ) => {
1192
parts.push ( * text) ;
1193
parts.extend ( blob.chunks) ;
1194
crate :: objects:: text_lines( & content, count)
1195
}
1196
Err ( e) => {
1197
let why = e.to_string ( ) ;
1198
failed = Some ( e) ;
1199
Err ( ObjectError:: Schema( format! (
1200
" text blob {} : {why} " ,
1201
text.short ( 12 )
1202
) ) )
1203
}
1204
} ) ;
1205
match ( decoded, failed) {
1206
( Ok ( c) , _ ) => Ok ( ( c, parts) ) ,
1207
( Err ( _ ) , Some ( StoreError:: NotFound( t) ) ) => Err ( StoreError:: Corrupt(
1208
* r,
1209
format! ( " its text blob {} is missing" , t.short ( 12 ) ) ,
1210
) ) ,
1211
( Err ( e) , _ ) => Err ( StoreError:: Corrupt( * r, e.to_string ( ) ) ) ,
1212
}
1213
}
1214
a4fad5b0 Andy McCall 1 month ago ↶
1215
1216
1217
1218
1219
pub fn get_change_lazy ( & self , r : & ObjectRef) -> Result< ( Change, Vec< crate:: objects:: TextRef> ) > {
86e1824f Andy McCall 1 month ago ↶
1220
let bytes = self .get_typed ( r, ObjectType:: Change) ? ;
a4fad5b0 Andy McCall 1 month ago ↶
1221
Change:: decode_skeleton( & bytes, * r) .map_err ( | e | StoreError:: Corrupt( * r, e.to_string ( ) ) )
86e1824f Andy McCall 1 month ago ↶
1222
}
1223
69816d68 Andy McCall 1 month ago ↶
1224
1225
1226
1227
pub fn get_change_meta ( & self , r : & ObjectRef) -> Result< crate:: state:: ChangeMeta> {
1228
Ok ( crate :: state:: ChangeMeta:: of( & self .get_change_lazy ( r) ? .0 ) )
1229
}
1230
6f8aeb99 Andy McCall 1 month ago ↶
1231
fn read_text ( & self , r : & ObjectRef) -> Result< ( Blob, Vec< u8 > ) > {
1232
let blob = Blob:: decode( & self .get_typed ( r, ObjectType:: Blob) ? ) ? ;
1233
let mut content = Vec :: with_capacity( blob.length as usize ) ;
1234
for c in & blob.chunks {
1235
content.extend ( self .get_typed ( c, ObjectType:: Chunk) ? ) ;
1236
}
1237
Ok ( ( blob, content) )
ff0af1ee unknown 1 month ago
1238
}
1239
1240
pub fn put_chunk ( & self , bytes : & [u8 ]) -> Result< ObjectRef> {
1241
self .put ( ObjectType:: Chunk, bytes)
1242
}
1243
cdb5beba Andy McCall 1 month ago ↶
1244
1245
pub fn put_chunk_nocompress ( & self , bytes : & [u8 ]) -> Result< ObjectRef> {
8f10a07a Andy McCall 1 month ago ↶
1246
let r = hash_object ( ObjectType:: Chunk, bytes) ;
cdb5beba Andy McCall 1 month ago ↶
1247
let dir = self .dir.join ( " nocompress" ) ;
1248
fs:: create_dir_all( & dir) ? ;
1249
let _ = fs:: File:: create( dir.join ( r.to_hex ( ) ) ) ;
8f10a07a Andy McCall 1 month ago ↶
1250
self .put ( ObjectType:: Chunk, bytes)
cdb5beba Andy McCall 1 month ago ↶
1251
}
1252
1253
fn is_nocompress ( & self , r : & ObjectRef) -> bool {
1254
self .dir.join ( " nocompress" ) .join ( r.to_hex ( ) ) .exists ( )
1255
}
1256
86e682c2 Andy McCall 1 month ago ↶
1257
pub fn put_state ( & self , st : & StateObject) -> Result< ObjectRef> {
1258
self .put ( ObjectType:: State, & st.encode ( ) )
1259
}
1260
1261
pub fn get_state ( & self , r : & ObjectRef) -> Result< StateObject> {
1262
Ok ( StateObject:: decode( & self .get_typed ( r, ObjectType:: State) ? ) ? )
1263
}
1264
5b9c6ad4 Andy McCall 1 month ago ↶
1265
pub fn put_attest ( & self , a : & Attest) -> Result< ObjectRef> {
1266
self .put ( ObjectType:: Attest, & a.encode ( ) )
1267
}
1268
1269
pub fn get_attest ( & self , r : & ObjectRef) -> Result< Attest> {
1270
Ok ( Attest:: decode( & self .get_typed ( r, ObjectType:: Attest) ? ) ? )
fc1a466a Andy McCall 1 month ago ↶
1271
}
1272
1273
pub fn put_receipt ( & self , r : & Receipt) -> Result< ObjectRef> {
1274
self .put ( ObjectType:: Receipt, & r.encode ( ) )
5b9c6ad4 Andy McCall 1 month ago ↶
1275
}
1276
fc1a466a Andy McCall 1 month ago ↶
1277
pub fn get_receipt ( & self , r : & ObjectRef) -> Result< Receipt> {
1278
Ok ( Receipt:: decode( & self .get_typed ( r, ObjectType:: Receipt) ? ) ? )
5b9c6ad4 Andy McCall 1 month ago ↶
1279
}
1280
fc1a466a Andy McCall 1 month ago ↶
1281
fn index_path ( & self , index : & str , key : & ObjectRef) -> PathBuf {
1282
let h = key.to_hex ( ) ;
1283
self .dir.join ( index) .join ( & h[ .. 2 ] ) .join ( & h[ 2 .. ] )
1284
}
1285
1286
1287
fn index_add ( & self , index : & str , key : & ObjectRef, value : & ObjectRef) -> Result< ( ) > {
1288
let mut values = self .index_get ( index, key) ? ;
1289
if values.contains ( value) {
5b9c6ad4 Andy McCall 1 month ago ↶
1290
return Ok ( ( ) ) ;
1291
}
fc1a466a Andy McCall 1 month ago ↶
1292
values.push ( * value) ;
1293
values.sort ( ) ;
1294
let path = self .index_path ( index, key) ;
5b9c6ad4 Andy McCall 1 month ago ↶
1295
fs:: create_dir_all( path.parent ( ) .unwrap ( ) ) ? ;
fc1a466a Andy McCall 1 month ago ↶
1296
let text: String = values.iter ( ) .map ( | s | format! ( " {} \n " , s.to_hex ( ) ) ) .collect ( ) ;
4dcb8c09 Andy McCall 1 month ago ↶
1297
crate :: durable:: write_atomic( & path, text.as_bytes ( ) ) ? ;
5b9c6ad4 Andy McCall 1 month ago ↶
1298
Ok ( ( ) )
1299
}
1300
fc1a466a Andy McCall 1 month ago ↶
1301
fn index_get ( & self , index : & str , key : & ObjectRef) -> Result< Vec< ObjectRef> > {
1302
match fs:: read_to_string( self .index_path ( index, key) ) {
5b9c6ad4 Andy McCall 1 month ago ↶
1303
Ok ( t) => Ok ( t.lines ( ) .filter_map ( ObjectRef:: from_hex) .collect ( ) ) ,
1304
Err ( e) if e.kind ( ) = = io:: ErrorKind:: NotFound => Ok ( vec! [ ] ) ,
1305
Err ( e) => Err ( e.into ( ) ) ,
1306
}
fc1a466a Andy McCall 1 month ago ↶
1307
}
1308
1309
1310
pub fn index_seal ( & self , change : & ObjectRef, attest : & ObjectRef) -> Result< ( ) > {
1311
self .index_add ( " seals" , change, attest)
1312
}
1313
1314
1315
pub fn seals_for ( & self , change : & ObjectRef) -> Result< Vec< ObjectRef> > {
1316
self .index_get ( " seals" , change)
1317
}
1318
1319
1320
pub fn index_receipt ( & self , state : & ObjectRef, attest : & ObjectRef) -> Result< ( ) > {
1321
self .index_add ( " receipts" , state, attest)
1322
}
1323
1324
1325
pub fn receipts_for ( & self , state : & ObjectRef) -> Result< Vec< ObjectRef> > {
1326
self .index_get ( " receipts" , state)
1327
}
1328
1329
1330
pub fn receipt_states ( & self ) -> Result< Vec< ObjectRef> > {
1331
let mut out = Vec :: new( ) ;
1332
let dir = self .dir.join ( " receipts" ) ;
1333
let top = match fs:: read_dir( & dir) {
1334
Ok ( d) => d,
1335
Err ( e) if e.kind ( ) = = io:: ErrorKind:: NotFound => return Ok ( out) ,
1336
Err ( e) => return Err ( e.into ( ) ) ,
1337
} ;
1338
for d in top {
1339
let d = d? ;
1340
let prefix = d.file_name ( ) .to_string_lossy ( ) .into_owned ( ) ;
1341
for f in fs:: read_dir( d.path ( ) ) ? {
1342
let name = f? .file_name ( ) .to_string_lossy ( ) .into_owned ( ) ;
1343
if let Some ( r) = ObjectRef:: from_hex( & format! ( " {prefix} {name} " ) ) {
1344
out.push ( r) ;
1345
}
1346
}
1347
}
1348
out.sort ( ) ;
1349
Ok ( out)
5b9c6ad4 Andy McCall 1 month ago ↶
1350
}
1351
1983686f Andy McCall 1 month ago ↶
1352
pub fn put_identity ( & self , id : & Identity) -> Result< ObjectRef> {
1353
self .put ( ObjectType:: Identity, & id.encode ( ) )
1354
}
1355
1356
pub fn get_identity ( & self , r : & ObjectRef) -> Result< Identity> {
1357
Ok ( Identity:: decode( & self .get_typed ( r, ObjectType:: Identity) ? ) ? )
1358
}
1359
1360
fn sig_path ( & self , r : & ObjectRef) -> PathBuf {
1361
let h = r.to_hex ( ) ;
1362
self .dir.join ( " sigs" ) .join ( & h[ .. 2 ] ) .join ( & h[ 2 .. ] )
1363
}
1364
1365
1366
pub fn signatures ( & self , r : & ObjectRef) -> Result< Vec< Signature> > {
1367
match fs:: read( self .sig_path ( r) ) {
1368
Ok ( b) => Ok ( Signature:: decode_list( & b) ? ) ,
1369
Err ( e) if e.kind ( ) = = io:: ErrorKind:: NotFound => Ok ( vec! [ ] ) ,
1370
Err ( e) => Err ( e.into ( ) ) ,
1371
}
1372
}
1373
1374
1375
pub fn put_signature ( & self , r : & ObjectRef, sig : & Signature) -> Result< ( ) > {
1376
let mut sigs = self .signatures ( r) ? ;
1377
sigs.retain ( | s | s.key ! = sig.key ) ;
1378
sigs.push ( sig.clone ( ) ) ;
5b1114a0 Andy McCall 1 month ago ↶
1379
sigs.sort_by_key ( | s | s.key ) ;
1983686f Andy McCall 1 month ago ↶
1380
let path = self .sig_path ( r) ;
1381
fs:: create_dir_all( path.parent ( ) .unwrap ( ) ) ? ;
1382
let mut nonce = [ 0 u8 ; 8 ] ;
1383
rand:: rngs:: OsRng.fill_bytes ( & mut nonce) ;
1384
let tmp = self .dir.join ( format! ( " sigs-tmp-{} " , hex:: encode( nonce) ) ) ;
1385
{
1386
let mut f = fs:: File:: create( & tmp) ? ;
1387
f.write_all ( & Signature:: encode_list( & sigs) ) ? ;
1388
f.sync_all ( ) ? ;
1389
}
4dcb8c09 Andy McCall 1 month ago ↶
1390
crate :: durable:: rename( & tmp, & path) ? ;
1983686f Andy McCall 1 month ago ↶
1391
Ok ( ( ) )
1392
}
1393
d02e545d Andy McCall 1 month ago ↶
1394
ff0af1ee unknown 1 month ago
1395
pub fn list ( & self ) -> io:: Result< Vec< ObjectRef> > {
d02e545d Andy McCall 1 month ago ↶
1396
let mut out: Vec< ObjectRef> = self .loose_objects ( ) ? .into_iter ( ) .map ( | ( r , _) | r ) .collect ( ) ;
1397
for p in self .packs.borrow ( ) .iter ( ) {
1398
out.extend ( p.refs.iter ( ) .copied ( ) ) ;
ff0af1ee unknown 1 month ago
1399
}
1400
out.sort ( ) ;
d02e545d Andy McCall 1 month ago ↶
1401
out.dedup ( ) ;
ff0af1ee unknown 1 month ago
1402
Ok ( out)
1403
}
afaba5f1 Andy McCall 1 month ago ↶
1404
}
1405
1406
1407
1408
fn read_loose_at ( path : & Path, r : & ObjectRef) -> Option< Result< Object> > {
1409
let data = match fs:: read( path) {
1410
Ok ( d) => d,
1411
Err ( e) if e.kind ( ) = = io:: ErrorKind:: NotFound => return None ,
1412
Err ( e) => return Some ( Err ( e.into ( ) ) ) ,
1413
} ;
1414
Some ( ( | | {
1415
let nul = data
1416
.iter ( )
1417
.position ( | & b | b = = 0 )
1418
.ok_or_else ( | | StoreError:: Corrupt( * r, " no header" .into ( ) ) ) ? ;
1419
let head = std:: str:: from_utf8( & data[ .. nul] )
1420
.map_err ( | _| StoreError:: Corrupt( * r, " header not UTF-8" .into ( ) ) ) ? ;
1421
let mut parts = head.split ( ' ' ) ;
1422
let ( Some ( " oxy1" ) , Some ( ty) , Some ( len) , None ) =
1423
( parts.next ( ) , parts.next ( ) , parts.next ( ) , parts.next ( ) )
1424
else {
1425
return Err ( StoreError:: Corrupt( * r, format! ( " bad header {head:?} " ) ) ) ;
1426
} ;
1427
let ty = ObjectType:: from_name( ty)
1428
.ok_or_else ( | | StoreError:: Corrupt( * r, format! ( " unknown type {ty} " ) ) ) ? ;
1429
let len: usize = len
1430
.parse ( )
1431
.map_err ( | _| StoreError:: Corrupt( * r, " bad length" .into ( ) ) ) ? ;
1432
let body = & data[ nul + 1 .. ] ;
1433
if body.len ( ) ! = len {
1434
return Err ( StoreError:: Corrupt(
1435
* r,
1436
format! ( " length {} != header {len} " , body.len ( ) ) ,
1437
) ) ;
1438
}
1439
if hash_object ( ty, body) ! = * r {
1440
return Err ( StoreError:: Corrupt( * r, " hash mismatch" .into ( ) ) ) ;
1441
}
1442
Ok ( ( ty, body.to_vec ( ) ) )
1443
} ) ( ) )
ff0af1ee unknown 1 month ago
1444
}
1445
1446
# [ cfg ( test ) ]
1447
mod tests {
1448
use super :: * ;
1449
1450
# [ test ]
1451
fn put_get_verify ( ) {
1452
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
1453
let s = Store:: open( dir.path ( ) .join ( " objects" ) ) .unwrap ( ) ;
1454
let r = s.put ( ObjectType:: Chunk, b " hello" ) .unwrap ( ) ;
1455
assert! ( s.has ( & r) ) ;
1456
assert_eq! ( s.get ( & r) .unwrap ( ) , ( ObjectType:: Chunk, b " hello" .to_vec ( ) ) ) ;
1457
assert_eq! ( s.put ( ObjectType:: Chunk, b " hello" ) .unwrap ( ) , r) ;
1458
assert_eq! ( s.list ( ) .unwrap ( ) , vec! [ r] ) ;
75167672 Andy McCall 1 month ago
1459
assert! ( matches! (
1460
s.get_typed ( & r, ObjectType:: Blob) ,
1461
Err ( StoreError:: WrongType( .. ) )
1462
) ) ;
1463
assert! ( matches! (
1464
s.get ( & ObjectRef( [ 9 ; 32 ] ) ) ,
1465
Err ( StoreError:: NotFound( _ ) )
1466
) ) ;
ff0af1ee unknown 1 month ago
1467
1468
1469
let path = s.path_for ( & r) ;
1470
let mut data = fs:: read( & path) .unwrap ( ) ;
1471
let last = data.len ( ) - 1 ;
1472
data[ last] ^ = 1 ;
1473
fs:: write( & path, data) .unwrap ( ) ;
1474
assert! ( matches! ( s.get ( & r) , Err ( StoreError:: Corrupt( .. ) ) ) ) ;
1475
}
1476
1477
# [ test ]
d02e545d Andy McCall 1 month ago ↶
1478
fn packing_keeps_every_object_readable ( ) {
1479
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
1480
let s = Store:: open( dir.path ( ) ) .unwrap ( ) ;
1481
let mut refs = Vec :: new( ) ;
1482
for i in 0 .. 300 u32 {
1483
let body = format! ( " object {i} " ) .repeat ( 1 + ( i % 7 ) as usize ) ;
1484
refs.push ( ( s.put ( ObjectType:: Chunk, body.as_bytes ( ) ) .unwrap ( ) , body) ) ;
1485
}
1486
let c = crate :: objects:: tests:: sample_change( ) ;
1487
let cr = s.put_change ( & c) .unwrap ( ) ;
1488
assert_eq! ( s.loose_count ( ) .unwrap ( ) , 301 ) ;
1489
let report = s.pack_loose ( ) .unwrap ( ) ;
1490
assert_eq! ( report.objects, 301 ) ;
1491
assert! ( report.packed_bytes < report.raw_bytes, " {report:?}" ) ;
1492
assert_eq! ( s.loose_count ( ) .unwrap ( ) , 0 ) ;
1493
assert_eq! ( s.pack_count ( ) , 1 ) ;
1494
for ( r, body) in & refs {
1495
assert! ( s.has ( r) ) ;
75167672 Andy McCall 1 month ago
1496
assert_eq! (
1497
s.get ( r) .unwrap ( ) ,
1498
( ObjectType:: Chunk, body.as_bytes ( ) .to_vec ( ) )
1499
) ;
d02e545d Andy McCall 1 month ago ↶
1500
}
1501
assert_eq! ( s.get_change ( & cr) .unwrap ( ) , c) ;
1502
assert_eq! ( s.list ( ) .unwrap ( ) .len ( ) , 301 ) ;
1503
1504
75167672 Andy McCall 1 month ago
1505
assert_eq! (
1506
s.put ( ObjectType:: Chunk, refs[ 0 ] .1. as_bytes ( ) ) .unwrap ( ) ,
1507
refs[ 0 ] .0
1508
) ;
d02e545d Andy McCall 1 month ago ↶
1509
assert_eq! ( s.loose_count ( ) .unwrap ( ) , 0 ) ;
1510
let extra = s.put ( ObjectType:: Chunk, b " after packing" ) .unwrap ( ) ;
1511
s.pack_loose ( ) .unwrap ( ) ;
1512
assert_eq! ( s.pack_count ( ) , 2 ) ;
1513
assert_eq! ( s.get ( & extra) .unwrap ( ) .1 , b " after packing" ) ;
1514
1515
let s2 = Store:: open( dir.path ( ) ) .unwrap ( ) ;
1516
assert_eq! ( s2.pack_count ( ) , 2 ) ;
1517
assert_eq! ( s2.get ( & refs[ 5 ] .0 ) .unwrap ( ) .1 , refs[ 5 ] .1. as_bytes ( ) ) ;
75167672 Andy McCall 1 month ago
1518
assert! ( matches! (
1519
s2.get ( & ObjectRef( [ 9 ; 32 ] ) ) ,
1520
Err ( StoreError:: NotFound( _ ) )
1521
) ) ;
d02e545d Andy McCall 1 month ago ↶
1522
1523
let target = refs[ 5 ] .0 ;
1524
let ( pack_path, offset) = {
1525
let packs = s2.packs.borrow ( ) ;
1526
let ( i, e) = s2.find_packed ( & target) .unwrap ( ) ;
1527
( packs[ i] .path.clone ( ) , e.offset as usize )
1528
} ;
1529
let mut data = fs:: read( & pack_path) .unwrap ( ) ;
1530
data[ offset] ^ = 0xff ;
1531
fs:: write( & pack_path, data) .unwrap ( ) ;
1532
let s3 = Store:: open( dir.path ( ) ) .unwrap ( ) ;
1533
assert! ( matches! ( s3.get ( & target) , Err ( StoreError:: Corrupt( .. ) ) ) ) ;
75167672 Andy McCall 1 month ago
1534
assert_eq! (
1535
s3.get ( & refs[ 6 ] .0 ) .unwrap ( ) .1 ,
1536
refs[ 6 ] .1. as_bytes ( ) ,
1537
" other objects are unaffected"
1538
) ;
a17fc622 Andy McCall 1 month ago ↶
1539
}
1540
1541
# [ test ]
1542
fn a_full_repack_trains_a_dictionary_and_reads_back ( ) {
1543
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
1544
let s = Store:: open( dir.path ( ) ) .unwrap ( ) ;
1545
1546
let bodies: Vec< Vec< u8 > > = ( 0 .. 1500 )
1547
.map ( | i | {
1548
format! (
1549
" // SPDX-License-Identifier: GPL-2.0\n #include <linux/module.h>\n \
1550
static int probe_{i} (struct device *dev)\n {{ \n \t return {i} ;\n }} \n "
1551
)
1552
.into_bytes ( )
1553
} )
1554
.collect ( ) ;
1555
let refs: Vec< ObjectRef> = bodies
1556
.iter ( )
1557
.map ( | b | s.put ( ObjectType:: Chunk, b) .unwrap ( ) )
1558
.collect ( ) ;
1559
s.repack_all ( REPACK_ZSTD_LEVEL ) .unwrap ( ) ;
1560
let dicts = fs:: read_dir( dir.path ( ) )
1561
.unwrap ( )
1562
.flatten ( )
1563
.filter ( | e | e.path ( ) .extension ( ) .is_some_and ( | x | x = = " dict" ) )
1564
.count ( ) ;
1565
assert_eq! ( dicts, 1 , " the pack has a dictionary" ) ;
1566
let with_dict = refs
1567
.iter ( )
1568
.filter ( | r | s.find_packed ( r) .unwrap ( ) .1. flags & FLAG_DICT ! = 0 )
1569
.count ( ) ;
1570
assert! ( with_dict > 1000 , " most use it: {with_dict}" ) ;
1571
let again = Store:: open( dir.path ( ) ) .unwrap ( ) ;
1572
for ( r, b) in refs.iter ( ) .zip ( & bodies) {
1573
assert_eq! ( & again.get ( r) .unwrap ( ) .1 , b) ;
1574
}
1575
1576
again.repack_all ( REPACK_ZSTD_LEVEL ) .unwrap ( ) ;
1577
let files: Vec< String> = fs:: read_dir( dir.path ( ) )
1578
.unwrap ( )
1579
.flatten ( )
1580
.map ( | e | e.file_name ( ) .to_string_lossy ( ) .into_owned ( ) )
1581
.filter ( | n | n.starts_with ( " pack-" ) )
1582
.collect ( ) ;
1583
assert_eq! ( files.len ( ) , 3 , " one pack, index and dictionary: {files:?}" ) ;
1584
assert_eq! ( & again.get ( & refs[ 7 ] ) .unwrap ( ) .1 , & bodies[ 7 ] ) ;
d02e545d Andy McCall 1 month ago ↶
1585
}
1586
1587
# [ test ]
cdb5beba Andy McCall 1 month ago ↶
1588
fn nocompress_chunks_pack_raw ( ) {
1589
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
1590
let s = Store:: open( dir.path ( ) ) .unwrap ( ) ;
1591
let body = vec! [ b ' a' ; 10_000 ] ;
1592
let raw = s.put_chunk_nocompress ( & body) .unwrap ( ) ;
1593
let compressible = s.put ( ObjectType:: Chunk, & vec! [ b ' b' ; 10_000 ] ) .unwrap ( ) ;
1594
s.pack_loose ( ) .unwrap ( ) ;
1595
let packs = s.packs.borrow ( ) ;
1596
let ( i, e) = s.find_packed ( & raw) .unwrap ( ) ;
1597
assert_eq! ( ( e.flags, e.stored_len as usize ) , ( 0 , 10_000 ) ) ;
1598
let ( _ , c) = s.find_packed ( & compressible) .unwrap ( ) ;
1599
assert_eq! ( c.flags, FLAG_ZSTD ) ;
1600
assert! ( c.stored_len < 10_000 ) ;
1601
assert_eq! ( packs[ i] .read ( & raw, e) .unwrap ( ) .1 , body) ;
1602
}
1603
1604
# [ test ]
135212de Andy McCall 1 month ago ↶
1605
fn many_packs_merge_into_one ( ) {
1606
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
1607
let s = Store:: open( dir.path ( ) ) .unwrap ( ) ;
1608
let mut refs = Vec :: new( ) ;
1609
for i in 0 .. ( MAX_PACKS + 2 ) {
75167672 Andy McCall 1 month ago
1610
refs.push (
1611
s.put ( ObjectType:: Chunk, format! ( " pack {i} " ) .as_bytes ( ) )
1612
.unwrap ( ) ,
1613
) ;
135212de Andy McCall 1 month ago ↶
1614
s.pack_loose ( ) .unwrap ( ) ;
1615
}
1616
assert_eq! ( s.pack_count ( ) , MAX_PACKS + 2 ) ;
1617
assert_eq! ( s.merge_packs ( ) .unwrap ( ) , MAX_PACKS + 2 ) ;
1618
assert_eq! ( s.pack_count ( ) , 1 ) ;
1619
assert_eq! ( s.merge_packs ( ) .unwrap ( ) , 0 ) ;
1620
for ( i, r) in refs.iter ( ) .enumerate ( ) {
1621
assert_eq! ( s.get ( r) .unwrap ( ) .1 , format! ( " pack {i} " ) .as_bytes ( ) ) ;
1622
}
1623
assert_eq! ( Store:: open( dir.path ( ) ) .unwrap ( ) .pack_count ( ) , 1 ) ;
1624
}
1625
1626
# [ test ]
ba160f60 Andy McCall 1 month ago ↶
1627
fn prune_removes_unreachable_loose_and_packed_objects ( ) {
1628
use std:: collections:: HashSet;
1629
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
1630
let s = Store:: open( dir.path ( ) ) .unwrap ( ) ;
1631
let keep_a = s.put ( ObjectType:: Chunk, b " keep a" ) .unwrap ( ) ;
1632
let drop_a = s.put ( ObjectType:: Chunk, b " drop a" ) .unwrap ( ) ;
1633
s.pack_loose ( ) .unwrap ( ) ;
1634
let keep_b = s.put ( ObjectType:: Chunk, b " keep b" ) .unwrap ( ) ;
1635
let drop_b = s.put ( ObjectType:: Chunk, b " drop b" ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
1636
let sk = crate :: identity:: SecretKey {
1637
ed_seed: [ 1 ; 32 ] ,
1638
ml_seed: [ 2 ; 32 ] ,
1639
} ;
ba160f60 Andy McCall 1 month ago ↶
1640
let id = sk.identity ( " a" , " b" , 0 ) ;
1641
let key = s.put_identity ( & id) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
1642
s.put_signature (
1643
& drop_b,
1644
& sk.sign ( crate :: identity:: Domain:: Change, & key, & drop_b) ,
1645
)
1646
.unwrap ( ) ;
ba160f60 Andy McCall 1 month ago ↶
1647
s.index_seal ( & drop_a, & ObjectRef( [ 3 ; 32 ] ) ) .unwrap ( ) ;
1648
let keep: HashSet< ObjectRef> = [ keep_a, keep_b, key] .into_iter ( ) .collect ( ) ;
1649
75167672 Andy McCall 1 month ago
1650
let r = s
1651
.prune ( & keep, std:: time:: Duration:: from_secs( 3600 ) )
1652
.unwrap ( ) ;
ba160f60 Andy McCall 1 month ago ↶
1653
assert_eq! ( ( r.objects, r.deferred) , ( 0 , 2 ) ) ;
1654
let r = s.prune ( & keep, std:: time:: Duration:: ZERO ) .unwrap ( ) ;
1655
assert_eq! ( r.objects, 2 , " {r:?}" ) ;
1656
assert! ( s.has ( & keep_a) & & s.has ( & keep_b) & & s.has ( & key) ) ;
1657
assert! ( ! s.has ( & drop_a) & & ! s.has ( & drop_b) ) ;
1658
assert! ( s.signatures ( & drop_b) .unwrap ( ) .is_empty ( ) ) ;
1659
assert! ( s.seals_for ( & drop_a) .unwrap ( ) .is_empty ( ) ) ;
1660
assert_eq! ( s.pack_count ( ) , 1 ) ;
1661
assert_eq! ( s.get ( & keep_a) .unwrap ( ) .1 , b " keep a" ) ;
1662
assert_eq! ( s.list ( ) .unwrap ( ) .len ( ) , 3 ) ;
1663
}
1664
1665
# [ test ]
1983686f Andy McCall 1 month ago ↶
1666
fn signatures_are_stored_beside_objects ( ) {
1667
use crate :: identity:: { Domain, SecretKey} ;
1668
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
1669
let s = Store:: open( dir.path ( ) ) .unwrap ( ) ;
1670
let r = s.put ( ObjectType:: Chunk, b " x" ) .unwrap ( ) ;
1671
assert! ( s.signatures ( & r) .unwrap ( ) .is_empty ( ) ) ;
75167672 Andy McCall 1 month ago
1672
let sk = SecretKey {
1673
ed_seed: [ 1 ; 32 ] ,
1674
ml_seed: [ 2 ; 32 ] ,
1675
} ;
1983686f Andy McCall 1 month ago ↶
1676
let id = sk.identity ( " a" , " b" , 0 ) ;
1677
let key = s.put_identity ( & id) .unwrap ( ) ;
1678
assert_eq! ( s.get_identity ( & key) .unwrap ( ) , id) ;
1679
let sig = sk.sign ( Domain:: Change, & key, & r) ;
1680
s.put_signature ( & r, & sig) .unwrap ( ) ;
1681
s.put_signature ( & r, & sig) .unwrap ( ) ;
1682
assert_eq! ( s.signatures ( & r) .unwrap ( ) , vec! [ sig] ) ;
1683
1684
assert_eq! ( s.get ( & r) .unwrap ( ) .1 , b " x" ) ;
1685
}
1686
1687
# [ test ]
ff0af1ee unknown 1 month ago
1688
fn change_round_trip_through_store ( ) {
1689
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
1690
let s = Store:: open( dir.path ( ) ) .unwrap ( ) ;
1691
let c = crate :: objects:: tests:: sample_change( ) ;
1692
let r = s.put_change ( & c) .unwrap ( ) ;
1693
assert_eq! ( r, c.hash ( ) ) ;
1694
assert_eq! ( s.get_change ( & r) .unwrap ( ) , c) ;
1695
}
2b872d07 Andy McCall 1 month ago ↶
1696
1697
fn xorshift ( state : & mut u64 ) -> u64 {
1698
* state ^ = * state < < 13 ;
1699
* state ^ = * state > > 7 ;
1700
* state ^ = * state < < 17 ;
1701
* state
1702
}
1703
1704
1705
1706
1707
# [ test ]
1708
fn corrupted_packs_and_indexes_fail_cleanly ( ) {
1709
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
1710
let objects = dir.path ( ) .join ( " objects" ) ;
1711
let s = Store:: open( & objects) .unwrap ( ) ;
1712
let mut refs = Vec :: new( ) ;
1713
let mut seed = 0x9e3779b97f4a7c15 u64 ;
1714
for i in 0 .. 40 u32 {
1715
let len = ( xorshift ( & mut seed) % 5000 ) as usize ;
1716
let mut body = vec! [ 0 u8 ; len] ;
1717
for b in body.iter_mut ( ) {
75167672 Andy McCall 1 month ago
1718
* b = if i % 2 = = 0 {
1719
( xorshift ( & mut seed) & 0xff ) as u8
1720
} else {
1721
b ' a' + ( i % 26 ) as u8
1722
} ;
2b872d07 Andy McCall 1 month ago ↶
1723
}
1724
refs.push ( s.put ( ObjectType:: Chunk, & body) .unwrap ( ) ) ;
1725
}
1726
s.pack_loose ( ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
1727
let pack = fs:: read_dir( & objects)
1728
.unwrap ( )
1729
.filter_map ( | e | e.ok ( ) .map ( | e | e.path ( ) ) )
1730
.find ( | p | p.extension ( ) .is_some_and ( | x | x = = " pack" ) )
1731
.unwrap ( ) ;
2b872d07 Andy McCall 1 month ago ↶
1732
let idx = pack.with_extension ( " idx" ) ;
1733
let pristine_pack = fs:: read( & pack) .unwrap ( ) ;
1734
let pristine_idx = fs:: read( & idx) .unwrap ( ) ;
1735
1736
for round in 0 .. 300 u32 {
75167672 Andy McCall 1 month ago
1737
let ( path, pristine) = if round % 2 = = 0 {
1738
( & pack, & pristine_pack)
1739
} else {
1740
( & idx, & pristine_idx)
1741
} ;
2b872d07 Andy McCall 1 month ago ↶
1742
let mut data = pristine.clone ( ) ;
1743
match xorshift ( & mut seed) % 4 {
1744
0 => {
1745
let at = ( xorshift ( & mut seed) as usize ) % data.len ( ) ;
1746
data[ at] ^ = 1 < < ( xorshift ( & mut seed) % 8 ) ;
1747
}
1748
1 => {
1749
let at = ( xorshift ( & mut seed) as usize ) % data.len ( ) ;
1750
data[ at] = ( xorshift ( & mut seed) & 0xff ) as u8 ;
1751
}
1752
2 => {
1753
let keep = ( xorshift ( & mut seed) as usize ) % data.len ( ) ;
1754
data.truncate ( keep) ;
1755
}
1756
_ => {
1757
let at = ( xorshift ( & mut seed) as usize ) % data.len ( ) ;
1758
let n = ( ( xorshift ( & mut seed) % 8 ) + 1 ) as usize ;
1759
for k in 0 .. n.min ( data.len ( ) - at) {
1760
data[ at + k] = 0xff ;
1761
}
1762
}
1763
}
1764
fs:: write( path, & data) .unwrap ( ) ;
1765
1766
1767
if let Ok ( s2) = Store:: open( & objects) {
1768
for r in & refs {
1769
match s2.get ( r) {
75167672 Andy McCall 1 month ago
1770
Ok ( ( ty, body) ) => assert_eq! (
1771
hash_object ( ty, & body) ,
1772
* r,
1773
" a returned object always matches its reference"
1774
) ,
1775
Err ( StoreError:: Corrupt( .. ) )
1776
| Err ( StoreError:: NotFound( _ ) )
1777
| Err ( StoreError:: Io( _ ) ) => { }
2b872d07 Andy McCall 1 month ago ↶
1778
Err ( e) => panic! ( " unexpected error {e}" ) ,
1779
}
1780
}
1781
}
1782
fs:: write( path, pristine) .unwrap ( ) ;
1783
}
1784
1785
let s3 = Store:: open( & objects) .unwrap ( ) ;
1786
for r in & refs {
1787
let ( ty, body) = s3.get ( r) .unwrap ( ) ;
1788
assert_eq! ( hash_object ( ty, & body) , * r) ;
1789
}
1790
}
8f10a07a Andy McCall 1 month ago ↶
1791
1792
1793
1794
# [ test ]
1795
fn batches_write_loose_or_one_pack ( ) {
1796
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
1797
let s = Store:: open( dir.path ( ) .join ( " objects" ) ) .unwrap ( ) ;
1798
let outer = s.begin_batch ( ) .unwrap ( ) ;
1799
let a = s.put ( ObjectType:: Chunk, b " small a" ) .unwrap ( ) ;
1800
{
1801
let inner = s.begin_batch ( ) .unwrap ( ) ;
1802
let b = s.put ( ObjectType:: Chunk, b " small b" ) .unwrap ( ) ;
1803
assert! ( s.has ( & b) ) ;
1804
assert_eq! ( s.get ( & b) .unwrap ( ) .1 , b " small b" ) ;
1805
inner.finish ( ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
1806
assert_eq! (
1807
s.loose_count ( ) .unwrap ( ) ,
1808
0 ,
1809
" the inner batch defers to the outer one"
1810
) ;
8f10a07a Andy McCall 1 month ago ↶
1811
}
1812
assert_eq! ( s.get ( & a) .unwrap ( ) .1 , b " small a" ) ;
75167672 Andy McCall 1 month ago
1813
assert_eq! (
1814
s.put ( ObjectType:: Chunk, b " small a" ) .unwrap ( ) ,
1815
a,
1816
" idempotent while pending"
1817
) ;
8f10a07a Andy McCall 1 month ago ↶
1818
outer.finish ( ) .unwrap ( ) ;
1819
assert_eq! ( s.loose_count ( ) .unwrap ( ) , 2 ) ;
1820
assert_eq! ( s.pack_count ( ) , 0 ) ;
1821
assert_eq! ( s.get ( & a) .unwrap ( ) .1 , b " small a" ) ;
1822
1823
1824
let big = s.begin_batch ( ) .unwrap ( ) ;
1825
let mut refs = Vec :: new( ) ;
1826
let mut seed = 7 u64 ;
1827
for i in 0 .. 300 u32 {
1828
let mut body = vec! [ b ' z' ; 40_000 ] ;
1829
if i % 3 = = 0 {
1830
for b in body.iter_mut ( ) {
1831
* b = ( xorshift ( & mut seed) & 0xff ) as u8 ;
1832
}
1833
}
1834
body[ .. 4 ] .copy_from_slice ( & i.to_le_bytes ( ) ) ;
75167672 Andy McCall 1 month ago
1835
let r = if i % 5 = = 0 {
1836
s.put_chunk_nocompress ( & body) .unwrap ( )
1837
} else {
1838
s.put ( ObjectType:: Chunk, & body) .unwrap ( )
1839
} ;
1840
assert_eq! (
1841
s.get ( & r) .unwrap ( ) .1 ,
1842
body,
1843
" readable while pending (object {i})"
1844
) ;
8f10a07a Andy McCall 1 month ago ↶
1845
refs.push ( ( r, body) ) ;
1846
}
1847
assert! ( s.has ( & refs[ 0 ] .0 ) ) ;
1848
big.finish ( ) .unwrap ( ) ;
1849
assert_eq! ( s.pack_count ( ) , 1 ) ;
75167672 Andy McCall 1 month ago
1850
assert_eq! (
1851
s.loose_count ( ) .unwrap ( ) ,
1852
2 ,
1853
" nothing from the big batch is loose"
1854
) ;
1855
assert! ( fs:: read_dir( dir.path ( ) .join ( " objects" ) )
1856
.unwrap ( )
1857
.flatten ( )
1858
.all ( | e | ! e.file_name ( ) .to_string_lossy ( ) .starts_with ( " tmp-" ) ) ) ;
8f10a07a Andy McCall 1 month ago ↶
1859
let s2 = Store:: open( dir.path ( ) .join ( " objects" ) ) .unwrap ( ) ;
1860
for ( r, body) in & refs {
1861
assert_eq! ( s2.get ( r) .unwrap ( ) .1 , * body) ;
1862
}
1863
1864
{
1865
let _b = s2.begin_batch ( ) .unwrap ( ) ;
1866
s2.put ( ObjectType:: Chunk, b " dropped" ) .unwrap ( ) ;
1867
}
75167672 Andy McCall 1 month ago
1868
assert_eq! (
1869
s2.get ( & hash_object ( ObjectType:: Chunk, b " dropped" ) )
1870
.unwrap ( )
1871
.1 ,
1872
b " dropped"
1873
) ;
afaba5f1 Andy McCall 1 month ago ↶
1874
}
1875
1876
# [ test ]
1877
fn get_many_reads_loose_and_packed_objects_in_order ( ) {
1878
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
1879
let s = Store:: open( dir.path ( ) .join ( " objects" ) ) .unwrap ( ) ;
1880
let packed: Vec< ObjectRef> = ( 0 .. 20 )
1881
.map ( | i | {
1882
s.put ( ObjectType:: Chunk, format! ( " packed {i} " ) .as_bytes ( ) )
1883
.unwrap ( )
1884
} )
1885
.collect ( ) ;
1886
s.pack_loose ( ) .unwrap ( ) ;
1887
let loose: Vec< ObjectRef> = ( 0 .. 20 )
1888
.map ( | i | {
1889
s.put ( ObjectType:: Chunk, format! ( " loose {i} " ) .as_bytes ( ) )
1890
.unwrap ( )
1891
} )
1892
.collect ( ) ;
1893
let missing = hash_object ( ObjectType:: Chunk, b " never stored" ) ;
1894
let mut refs: Vec< ObjectRef> = Vec :: new( ) ;
1895
for i in 0 .. 20 {
1896
refs.push ( loose[ i] ) ;
1897
refs.push ( packed[ i] ) ;
1898
}
1899
refs.push ( missing) ;
1900
let got = s.get_many ( & refs) ;
1901
assert_eq! ( got.len ( ) , refs.len ( ) ) ;
1902
for ( i, r) in refs.iter ( ) .enumerate ( ) .take ( 40 ) {
1903
let want = if i % 2 = = 0 {
1904
format! ( " loose {} " , i / 2 )
1905
} else {
1906
format! ( " packed {} " , i / 2 )
1907
} ;
1908
let ( ty, body) = got[ i] .as_ref ( ) .unwrap ( ) ;
1909
assert_eq! (
1910
( * ty, body.as_slice ( ) ) ,
1911
( ObjectType:: Chunk, want.as_bytes ( ) ) ,
1912
" {r:?}"
1913
) ;
1914
}
1915
assert! ( matches! ( got[ 40 ] , Err ( StoreError:: NotFound( _ ) ) ) ) ;
a9f686ed Andy McCall 1 month ago ↶
1916
}
1917
}
1918
1919
# [ cfg ( test ) ]
1920
mod batch_tests {
1921
use super :: * ;
1922
1923
# [ test ]
1924
fn a_large_batch_packs_every_object_and_indexes_them_all ( ) {
1925
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
1926
let store = Store:: open( dir.path ( ) ) .unwrap ( ) ;
1927
let mut refs = Vec :: new( ) ;
1928
{
1929
let batch = store.begin_batch ( ) .unwrap ( ) ;
1930
for i in 0 .. 1000 u32 {
1931
let body = format! ( " object {i} {} " , " x" .repeat ( 20_000 + i as usize ) ) .into_bytes ( ) ;
1932
refs.push ( store.put ( ObjectType:: Blob, & body) .unwrap ( ) ) ;
1933
}
1934
batch.finish ( ) .unwrap ( ) ;
1935
}
1936
let again = Store:: open( dir.path ( ) ) .unwrap ( ) ;
1937
for r in & refs {
1938
again
1939
.get ( r)
1940
.unwrap_or_else ( | e | panic! ( " {}: {e}" , r.short ( 12 ) ) ) ;
1941
}
1942
}
1943
}
1944
1945
# [ cfg ( test ) ]
1946
mod size_tests {
1947
use super :: * ;
1948
1949
# [ test ]
1950
fn an_object_over_the_limit_is_refused_and_nothing_is_written ( ) {
1951
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
1952
let store = Store:: open( dir.path ( ) ) .unwrap ( ) ;
1953
let big = vec! [ b ' x' ; MAX_OBJECT_LEN as usize + 1 ] ;
1954
{
1955
let batch = store.begin_batch ( ) .unwrap ( ) ;
1956
store.put ( ObjectType:: Blob, b " small" ) .unwrap ( ) ;
1957
let e = store.put ( ObjectType:: Change, & big) .unwrap_err ( ) ;
1958
assert! (
1959
matches! ( e, StoreError:: TooLarge { kind: " change" , .. } ) ,
1960
" {e}"
1961
) ;
1962
assert! ( e.to_string ( ) .contains ( " commit it in parts" ) , " {e}" ) ;
1963
batch.finish ( ) .unwrap ( ) ;
1964
}
1965
1966
let again = Store:: open( dir.path ( ) ) .unwrap ( ) ;
1967
again.get ( & hash_object ( ObjectType:: Blob, b " small" ) ) .unwrap ( ) ;
6f8aeb99 Andy McCall 1 month ago ↶
1968
}
1969
}
1970
1971
# [ cfg ( test ) ]
1972
mod text_tests {
1973
use super :: * ;
1974
use crate :: objects:: { AtomRef, Edit, CHANGE_VERSION } ;
1975
1976
fn large_change ( ) -> Change {
1977
let mut c = crate :: objects:: tests:: sample_change( ) ;
1978
assert_eq! ( c.version, CHANGE_VERSION ) ;
1979
c.edits[ 0 ] = Edit:: Insert {
1980
node: crate :: ids:: NodeId( [ 7 ; 16 ] ) ,
1981
after: AtomRef:: Start,
1982
before: AtomRef:: End,
1983
lines: ( 0 .. 2000 )
1984
.map ( | i | format! ( " line {i} \n " ) .into_bytes ( ) )
1985
.collect ( ) ,
1986
} ;
1987
c
1988
}
1989
1990
# [ test ]
1991
fn a_large_insert_is_stored_beside_its_change_and_read_back ( ) {
1992
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
1993
let s = Store:: open( dir.path ( ) ) .unwrap ( ) ;
1994
let c = large_change ( ) ;
1995
let r = s.put_change ( & c) .unwrap ( ) ;
1996
assert_eq! ( r, c.hash ( ) ) ;
1997
let ( _ , body) = s.get ( & r) .unwrap ( ) ;
1998
assert! (
1999
body.len ( ) < 1024 ,
2000
" the change holds a reference, not {} bytes of lines" ,
2001
body.len ( )
2002
) ;
2003
let ( back, parts) = s.get_change_parts ( & r) .unwrap ( ) ;
2004
assert_eq! ( back, c) ;
2005
2006
assert_eq! ( parts.len ( ) , 2 ) ;
2007
for p in & parts {
2008
assert! ( s.has ( p) ) ;
2009
}
2010
2011
let n = s.list ( ) .unwrap ( ) .len ( ) ;
2012
s.put_change ( & c) .unwrap ( ) ;
2013
assert_eq! ( s.list ( ) .unwrap ( ) .len ( ) , n) ;
2014
}
2015
2016
# [ test ]
2017
fn a_change_whose_text_blob_is_missing_is_reported_not_misread ( ) {
2018
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
2019
let s = Store:: open( dir.path ( ) ) .unwrap ( ) ;
2020
let c = large_change ( ) ;
2021
let r = s.put_change ( & c) .unwrap ( ) ;
2022
let ( _ , parts) = s.get_change_parts ( & r) .unwrap ( ) ;
2023
fs:: remove_file( s.path_for ( & parts[ 0 ] ) ) .unwrap ( ) ;
2024
let e = s.get_change ( & r) .unwrap_err ( ) ;
2025
assert! (
2026
matches! ( & e, StoreError:: Corrupt( h, why) if * h = = r & & why.contains ( " missing" ) ) ,
2027
" {e}"
2028
) ;
192fad1e Andy McCall 1 month ago ↶
2029
}
2030
}
2031
2032
# [ cfg ( test ) ]
2033
mod put_many_tests {
2034
use super :: * ;
2035
2036
# [ test ]
2037
fn many_objects_in_a_batch_are_compressed_together_and_all_read_back ( ) {
2038
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
2039
let s = Store:: open( dir.path ( ) ) .unwrap ( ) ;
2040
let before = s.put ( ObjectType:: Chunk, b " already here" ) .unwrap ( ) ;
2041
let bodies: Vec< Vec< u8 > > = ( 0 .. 200 u32 )
2042
.map ( | i | format! ( " chunk {i} " ) .repeat ( 6000 ) .into_bytes ( ) )
2043
.collect ( ) ;
2044
let mut objects: Vec< ( ObjectType, & [u8 ]) > = bodies
2045
.iter ( )
2046
.map ( | b | ( ObjectType:: Chunk, b.as_slice ( ) ) )
2047
.collect ( ) ;
2048
objects.push ( ( ObjectType:: Chunk, bodies[ 0 ] .as_slice ( ) ) ) ;
2049
objects.push ( ( ObjectType:: Chunk, b " already here" ) ) ;
2050
let refs = {
2051
let batch = s.begin_batch ( ) .unwrap ( ) ;
2052
let refs = s.put_many ( & objects) .unwrap ( ) ;
2053
batch.finish ( ) .unwrap ( ) ;
2054
refs
2055
} ;
2056
assert_eq! ( refs.len ( ) , objects.len ( ) ) ;
2057
assert_eq! ( refs[ 200 ] , refs[ 0 ] , " a repeat is the same object" ) ;
2058
assert_eq! ( refs[ 201 ] , before) ;
2059
let again = Store:: open( dir.path ( ) ) .unwrap ( ) ;
2060
for ( r, b) in refs.iter ( ) .zip ( & objects) {
2061
assert_eq! ( again.get ( r) .unwrap ( ) .1 , b.1 ) ;
2062
}
a17fc622 Andy McCall 1 month ago ↶
2063
}
2064
}
2065
2066
2067
fn remove_pack_files ( pack : & Path) -> io:: Result< ( ) > {
2068
fs:: remove_file( pack) ? ;
2069
fs:: remove_file( pack.with_extension ( " idx" ) ) ? ;
2070
match fs:: remove_file( pack.with_extension ( " dict" ) ) {
2071
Err ( e) if e.kind ( ) ! = io:: ErrorKind:: NotFound => Err ( e) ,
2072
_ => Ok ( ( ) ) ,
8f10a07a Andy McCall 1 month ago ↶
2073
}
ff0af1ee unknown 1 month ago
2074
}