diff --git a/dub.json b/dub.json index 33c9c1e..d9c5233 100644 --- a/dub.json +++ b/dub.json @@ -12,9 +12,9 @@ "xdiff" ], "dependencies": { - "libxdiff": { - "repository": "git+https://git.zhirov.kz/dlang/libxdiff.git", - "version": "143f39005c0e89cc57d564365e6ea61427928bc3" + "xdiff": { + "repository": "git+https://git.zhirov.kz/dlang/xdiff.git", + "version": "e2396bc172eba813cdcd1a96c494e35d687f576a" } } } \ No newline at end of file diff --git a/dub.selections.json b/dub.selections.json index c3c6475..c2bab4e 100644 --- a/dub.selections.json +++ b/dub.selections.json @@ -1,6 +1,6 @@ { "fileVersion": 1, "versions": { - "libxdiff": {"version":"143f39005c0e89cc57d564365e6ea61427928bc3","repository":"git+https://git.zhirov.kz/dlang/libxdiff.git"} + "libxdiff": {"version":"e2396bc172eba813cdcd1a96c494e35d687f576a","repository":"git+https://git.zhirov.kz/dlang/xdiff.git"} } } diff --git a/source/app.d b/source/app.d index 9101d29..b1e9a4b 100644 --- a/source/app.d +++ b/source/app.d @@ -1,17 +1,20 @@ -import std.stdio; -import libxdiff : MMFile; +import libxdiff; + +import std.conv : to; +import std.stdio : writefln, writeln; +import std.file : read; void main() { - auto a = MMFile.fromBytes(cast(ubyte[]) "hello world\n"); - auto b = MMFile.fromBytes(cast(ubyte[]) "hello world!\n"); + auto file1 = new MMFile(cast(const(ubyte)[]) read("/tmp/diff1.d")); + auto file2 = new MMFile(cast(const(ubyte)[]) read("/tmp/diff2.d")); - auto patch = a.computePatch(b); - writeln("patch size: ", patch.size()); + auto patch = file1.computePatch(file2); - auto res = a.applyPatch(patch); - writeln("apply success: ", res.success); - writeln(res.patched.asSlice()); // печатаем результат - // печатаем как есть (включая заголовок @@ и строки с '-'/'+' и '\n') - write(cast(string) MMFile.fromBlocksMoved(patch).asSlice()); + writeln(patch); + + writefln( + "file1: %s\nfile2: %s", + file1.size, file2.size + ); } diff --git a/source/libxdiff/init.d b/source/libxdiff/init.d new file mode 100644 index 0000000..546679c --- /dev/null +++ b/source/libxdiff/init.d @@ -0,0 +1,70 @@ +module libxdiff.init; + +import xdiff; + +import core.stdc.stdlib : malloc, free, realloc; +import std.exception : enforce; +import core.stdc.errno : errno; +import core.stdc.string : strerror; +import std.string : fromStringz; +import std.conv : to; + +private __gshared bool _allocatorReady = false; +package(libxdiff) alias ByteArray = const(ubyte)[]; + +extern (C) +{ + private void* wrap_malloc(void*, uint size) + { + return malloc(size); + } + + private void wrap_free(void*, void* p) + { + free(p); + } + + private void* wrap_realloc(void*, void* p, uint size) + { + return realloc(p, size); + } +} + +private void setAllocator() +{ + if (_allocatorReady) + return; + + memallocator_t m = {null, &wrap_malloc, &wrap_free, &wrap_realloc}; + + enforce(xdl_set_allocator(&m) == 0, + "xdl_set_allocator failed: " ~ strerror(errno).fromStringz); + _allocatorReady = true; +} + +mmfile_t initMmfile(size_t len) +{ + setAllocator(); + mmfile_t mf; + enforce(xdl_init_mmfile(&mf, len.to!long, XDL_MMF_ATOMIC) == 0, + "xdl_init_mmfile failed: " ~ strerror(errno).fromStringz); + return mf; +} + +abstract class MM +{ +protected: + mmfile_t _inner; +public: + final @property size_t size() + { + return xdl_mmfile_size(&_inner).to!size_t; + } + + // Проверка «компактности» — содержимое лежит в одном блоке + // (важно для бинарных функций, которым нужно сплошное непрерывное представление). + final bool isCompact() const + { + return xdl_mmfile_iscompact(cast(mmfile_t*)&_inner) != 0; + } +} diff --git a/source/libxdiff/mmblocks.d b/source/libxdiff/mmblocks.d new file mode 100644 index 0000000..8ec975a --- /dev/null +++ b/source/libxdiff/mmblocks.d @@ -0,0 +1,41 @@ +module libxdiff.mmblocks; + +import xdiff; + +import libxdiff.init; + +import std.exception : enforce; +import core.stdc.errno : errno; +import core.stdc.string : strerror; +import std.string : fromStringz; +import std.conv : to; + +private enum DEFAULT_BSIZE = 8 * 1024; + +final class MMBlocks : MM +{ +public: + this() + { + _inner = initMmfile(DEFAULT_BSIZE); + } + + package(libxdiff) mmfile_t* ptr() @trusted + { + return &_inner; + } + + const(ubyte)[] asSlice() @trusted + { + enforce(isCompact(), "MMFile must be compact for asSliceConst"); + auto h = _inner.head; + return h is null || h.size <= 0 ? [] : (cast(const(ubyte)*) h.ptr)[0 .. h.size.to!size_t]; + } + + override string toString() const @trusted + { + enforce(isCompact(), "MMFile must be compact for asSliceConst"); + auto h = _inner.head; + return h is null || h.size <= 0 ? [] : fromStringz(cast(const char*) h.ptr).idup; + } +} diff --git a/source/libxdiff/mmfile.d b/source/libxdiff/mmfile.d new file mode 100644 index 0000000..1bcbca4 --- /dev/null +++ b/source/libxdiff/mmfile.d @@ -0,0 +1,79 @@ +module libxdiff.mmfile; + +import xdiff; + +import libxdiff.init; +import libxdiff.mmblocks; + +import std.exception : enforce; +import core.stdc.errno : errno; +import core.stdc.string : strerror; +import std.string : fromStringz; +import std.conv : to; +import std.string : join, representation; + +final class MMFile : MM +{ +private: + void _write(ByteArray data) + { + _inner = initMmfile(0); + + if (data.length > 0) + { + auto length = data.length.to!long; + auto wrote = xdl_write_mmfile(&_inner, data.ptr, length); + enforce(wrote == length, + "xdl_write_mmfile wrote less than requested: " ~ strerror(errno).fromStringz); + } + } + +public: + this(ByteArray data) + { + _write(data); + } + + this(string data) + { + _write(data.representation); + } + + this(string[] data) + { + _write(data.join('\n').representation); + } + + ~this() + { + if (_inner.head !is null) + xdl_free_mmfile(&_inner); + } + + MMBlocks computePatch(ref MMFile other, long ctxlen = 3) + { + auto patch = new MMBlocks(); + + extern (C) int emitToPatch(void* priv, mmbuffer_t* bufs, int num) + { + auto target = cast(MMBlocks) priv; + foreach (i; 0 .. num) + { + mmbuffer_t* buf = &bufs[i]; + long written = xdl_writem_mmfile(target.ptr(), buf, 1); + if (written < 0 || written != buf.size) + return -1; + } + return 0; + } + + xpparam_t xpp = {0}; + xdemitconf_t xec = {ctxlen}; + xdemitcb_t ecb = {cast(void*) patch, &emitToPatch}; + + enforce(xdl_diff(&_inner, &other._inner, &xpp, &xec, &ecb) == 0, + "xdl_diff failed: " ~ strerror(errno).fromStringz); + + return patch; + } +} diff --git a/source/libxdiff/package.d b/source/libxdiff/package.d new file mode 100644 index 0000000..3e27fe2 --- /dev/null +++ b/source/libxdiff/package.d @@ -0,0 +1,4 @@ +module libxdiff; + +public import libxdiff.mmfile; +public import libxdiff.mmblocks;