12345678module storage910import compress.zlib11import crypto.blake31213pub const max_file_size = 512 * 1024 * 10241415pub fn hash_hex(data []u8) string {16 return blake3.sum256(data).hex()17}18192021pub fn (s &Store) put_object(data []u8) !string {22 hash := hash_hex(data)23 if s.has_object(hash)! {24 return hash25 }26 packed := zlib.compress(data)!27 mut stmt := s.db.prepare('INSERT OR IGNORE INTO objects (hash, size, data) VALUES (?, ?, ?)')!28 defer {29 stmt.finalize()30 }31 stmt.bind_text(1, hash)!32 stmt.bind_int(2, i64(data.len))!33 stmt.bind_blob(3, packed)!34 stmt.step()!35 return hash36}3738pub fn (s &Store) has_object(hash string) !bool {39 mut stmt := s.db.prepare('SELECT 1 FROM objects WHERE hash = ?')!40 defer {41 stmt.finalize()42 }43 stmt.bind_text(1, hash)!44 return stmt.step()!45}46474849pub fn (s &Store) get_object(hash string) ![]u8 {50 mut stmt := s.db.prepare('SELECT data FROM objects WHERE hash = ?')!51 defer {52 stmt.finalize()53 }54 stmt.bind_text(1, hash)!55 if !stmt.step()! {56 return error('object ${hash} is missing')57 }58 packed := stmt.blob(0)59 data := zlib.decompress(packed)!60 if hash_hex(data) != hash {61 return error('object ${hash} does not match its hash')62 }63 return data64}6566pub fn (s &Store) object_size(hash string) !i64 {67 mut stmt := s.db.prepare('SELECT size FROM objects WHERE hash = ?')!68 defer {69 stmt.finalize()70 }71 stmt.bind_text(1, hash)!72 if !stmt.step()! {73 return error('object ${hash} is missing')74 }75 return stmt.int(0)76}7778pub fn (s &Store) object_count() !i64 {79 mut stmt := s.db.prepare('SELECT COUNT(*) FROM objects')!80 defer {81 stmt.finalize()82 }83 if !stmt.step()! {84 return 085 }86 return stmt.int(0)87}88899091pub fn (s &Store) drop_unreferenced_objects() !i64 {92 before := s.object_count()!93 mut stmt := s.db.prepare('DELETE FROM objects WHERE hash NOT IN (SELECT hash FROM save_files)')!94 defer {95 stmt.finalize()96 }97 stmt.step()!98 after := s.object_count()!99 return before - after100}