diff --git a/source/cdcdb/snapshot.d b/source/cdcdb/snapshot.d index 939f4ed..28d0e5d 100644 --- a/source/cdcdb/snapshot.d +++ b/source/cdcdb/snapshot.d @@ -2,49 +2,87 @@ module cdcdb.snapshot; import cdcdb.dblite; +import zstd : uncompress; + +import std.digest.sha : SHA256, digest, SHA256Digest; +import std.datetime : DateTime; import std.exception : enforce; -final class Snapshot { +final class Snapshot +{ private: DBLite _db; DBSnapshot _snapshot; public: - this(DBLite dblite, DBSnapshot dbSnapshot) { + this(DBLite dblite, DBSnapshot dbSnapshot) + { _db = dblite; _snapshot = dbSnapshot; } - this(DBLite dblite, long idSnapshot) { + this(DBLite dblite, long idSnapshot) + { _db = dblite; _snapshot = _db.getSnapshot(idSnapshot); } - ubyte[] data() { - auto dataChunks = _db.getChunks(_snapshot.id); + ubyte[] data() + { + auto chunks = _db.getChunks(_snapshot.id); ubyte[] content; - import zstd : uncompress; - - foreach (chunk; dataChunks) { + foreach (chunk; chunks) + { ubyte[] bytes; - if (chunk.zstd) { + if (chunk.zstd) + { enforce(chunk.zSize == chunk.content.length, "Размер сжатого фрагмента не соответствует ожидаемому"); bytes = cast(ubyte[]) uncompress(chunk.content); - } else { + } + else + { bytes = chunk.content; } enforce(chunk.size == bytes.length, "Оригинальный размер не соответствует ожидаемому"); + enforce(chunk.sha256 == digest!SHA256(bytes), "Хеш-сумма фрагмента не совпадает"); content ~= bytes; } - import std.digest.sha : SHA256, digest; - enforce(_snapshot.sha256 == digest!SHA256(content), "Хеш-сумма файла не совпадает"); return content; } - bool remove() { + void data(void delegate(const(ubyte)[]) sink) + { + auto chunks = _db.getChunks(_snapshot.id); + auto fctx = new SHA256Digest(); + + foreach (chunk; chunks) + { + ubyte[] bytes; + if (chunk.zstd) + { + enforce(chunk.zSize == chunk.content.length, "Размер сжатого фрагмента не соответствует ожидаемому"); + bytes = cast(ubyte[]) uncompress(chunk.content); + } + else + { + bytes = chunk.content; + } + + enforce(chunk.size == bytes.length, "Оригинальный размер не соответствует ожидаемому"); + enforce(chunk.sha256 == digest!SHA256(bytes), "Хеш-сумма фрагмента не совпадает"); + + sink(bytes); + fctx.put(bytes); + } + + enforce(_snapshot.sha256 = fctx.finish(), "Хеш-сумма файла не совпадает"); + } + + bool remove() + { _db.beginImmediate(); bool ok; @@ -65,4 +103,40 @@ public: return _snapshot.id == idDeleted; } + + @property long id() const nothrow @safe + { + return _snapshot.id; + } + + @property string label() const @safe + { + return _snapshot.label; + } + + @property DateTime created() const @safe + { + return _snapshot.createdUtc; + } + + @property long length() const nothrow @safe + { + return _snapshot.sourceLength; + } + + @property ubyte[32] sha256() const nothrow @safe + { + return _snapshot.sha256; + } + + @property string status() const + { + import std.conv : to; + return _snapshot.status.to!string; + } + + @property string description() const nothrow @safe + { + return _snapshot.description; + } } diff --git a/source/cdcdb/storage.d b/source/cdcdb/storage.d index 4438e72..2b1da7c 100644 --- a/source/cdcdb/storage.d +++ b/source/cdcdb/storage.d @@ -4,12 +4,15 @@ import cdcdb.dblite; import cdcdb.core; import cdcdb.snapshot; +import zstd : compress, Level; + final class Storage { private: // Параметры работы с базой данных DBLite _db; bool _zstd; + int _level; // Настройки CDC механизма CDC _cdc; size_t _minSize; @@ -31,10 +34,11 @@ private: } public: - this(string database, bool zstd = false, size_t busyTimeout = 3000, size_t maxRetries = 3) + this(string database, bool zstd = false, int level = Level.base, size_t busyTimeout = 3000, size_t maxRetries = 3) { _db = new DBLite(database, busyTimeout, maxRetries); _zstd = zstd; + _level = level; initCDC(); } @@ -94,8 +98,6 @@ public: // Разбить на фрагменты Chunk[] chunks = _cdc.split(data); - import zstd : compress; - // Запись фрагментов в БД foreach (chunk; chunks) { @@ -105,7 +107,7 @@ public: auto content = data[chunk.offset .. chunk.offset + chunk.size]; if (_zstd) { - ubyte[] zBytes = compress(content, 22); + ubyte[] zBytes = compress(content, _level); size_t zSize = zBytes.length; ubyte[32] zHash = digest!SHA256(zBytes); diff --git a/test/app.d b/test/app.d index fa658d8..0fe107e 100644 --- a/test/app.d +++ b/test/app.d @@ -2,11 +2,14 @@ import std.stdio; import cdcdb; -import std.file : read; +import std.file : read, write; +import std.stdio : File, writeln; +import std.conv : to; + void main() { - auto storage = new Storage("/tmp/base.db", true); + auto storage = new Storage("/tmp/base.db", true, 22); storage.newSnapshot("/tmp/text", cast(ubyte[]) read("/tmp/text")); // if (snapshot !is null) { @@ -14,11 +17,11 @@ void main() // snapshot.remove(); // } - import std.stdio : writeln; - foreach (snapshot; storage.getSnapshots()) { - writeln(cast(string) snapshot.data); + auto file = File("/tmp/restore" ~ snapshot.id.to!string, "wb"); + snapshot.data((const(ubyte)[] content) { + file.rawWrite(content); + }); + file.close(); } - - // writeln(cas.getVersion); }