module xdiff_mmblocks; import std.exception : enforce; import xdiff_init : ensureInit, initMmfile; import xdiff; final class MMBlocks { private: mmfile_t _inner; bool _owned = true; // владеем ли блоками? enum DEFAULT_BSIZE = 8 * 1024; // Глубокая копия: читаем все блоки и записываем в dst static void deepCopyAll(mmfile_t* src, MMBlocks dst) { enforce(xdl_seek_mmfile(src, 0) == 0, "seek(0) failed"); long sz = 0; auto p = xdl_mmfile_first(src, &sz); while (p !is null && sz > 0) { auto wrote = xdl_write_mmfile(dst.mmfilePtr(), p, sz); enforce(wrote == sz, "write failed during deep copy"); p = xdl_mmfile_next(src, &sz); } } public: this() { _inner = initMmfile(DEFAULT_BSIZE); } static MMBlocks fromBytes(const(ubyte)[] data) { auto b = new MMBlocks; if (data.length > 0) { auto wrote = xdl_write_mmfile(&b._inner, data.ptr, cast(long)data.length); enforce(wrote == cast(long)data.length, "xdl_write_mmfile wrote less than requested"); } return b; } ~this() { if (_owned && _inner.head !is null) xdl_free_mmfile(&_inner); // обнулим на всякий случай _inner.head = null; _inner.tail = null; _inner.rcur = null; _inner.wcur = null; _inner.fsize = 0; _inner.bsize = 0; } // доступ к внутреннему mmfile_t* mmfile_t* mmfilePtr() @trusted { return &_inner; } size_t size() { return cast(size_t)xdl_mmfile_size(&_inner); } bool isCompact() const { return xdl_mmfile_iscompact(cast(mmfile_t*)&_inner) != 0; } int writeBuf(const(ubyte)[] buf) { auto wrote = xdl_write_mmfile(&_inner, buf.ptr, cast(long)buf.length); return (wrote == cast(long)buf.length) ? 0 : -1; } /// Глубокая копия (новое владение) MMBlocks clone() const { auto dst = new MMBlocks; // deepCopyAll принимает mmfile_t*, поэтому аккуратно снимаем const с заголовка: deepCopyAll(cast(mmfile_t*)&_inner, dst); return dst; } /// Компактизация через пересборку содержимого void toCompact() { if (isCompact()) return; auto dst = new MMBlocks; deepCopyAll(&_inner, dst); // перехватываем владение содержимым dst if (_owned && _inner.head !is null) xdl_free_mmfile(&_inner); _inner = *dst.mmfilePtr(); dst._owned = false; // dst больше не владеет dst._inner.head = null; // чтобы финалайзер dst ничего не освободил dst._inner.tail = null; dst._inner.rcur = null; dst._inner.wcur = null; dst._inner.fsize = 0; dst._inner.bsize = 0; } /// Снять владение (используется при move в MMFile) void disarm() { _owned = false; _inner.head = null; _inner.tail = null; _inner.rcur = null; _inner.wcur = null; _inner.fsize = 0; _inner.bsize = 0; } }