diff --git a/dub.selections.json b/dub.selections.json deleted file mode 100644 index c2bab4e..0000000 --- a/dub.selections.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "fileVersion": 1, - "versions": { - "libxdiff": {"version":"e2396bc172eba813cdcd1a96c494e35d687f576a","repository":"git+https://git.zhirov.kz/dlang/xdiff.git"} - } -} diff --git a/source/app.d b/source/app.d index b1e9a4b..7b3a7ed 100644 --- a/source/app.d +++ b/source/app.d @@ -1,20 +1,322 @@ -import libxdiff; +import std.stdio : writeln, stderr, File; +import std.getopt; +import std.file : read, exists; +import std.exception : enforce, collectException; +import core.stdc.stdlib : malloc, free, realloc; +import core.stdc.string : memcpy; +import core.sys.posix.unistd : posix_write = write; -import std.conv : to; -import std.stdio : writefln, writeln; -import std.file : read; +import xdiff; -void main() +// ------------------------------ +// RAII для mmfile_t +// ------------------------------ +struct MmFile { - auto file1 = new MMFile(cast(const(ubyte)[]) read("/tmp/diff1.d")); - auto file2 = new MMFile(cast(const(ubyte)[]) read("/tmp/diff2.d")); + mmfile_t mf; - auto patch = file1.computePatch(file2); + // фабрика вместо конструктора по умолчанию + static MmFile create(long bsize = 8 * 1024, ulong flags = XDL_MMF_ATOMIC) + { + MmFile m; + enforce(xdl_init_mmfile(&m.mf, bsize, flags) == 0, "xdl_init_mmfile failed"); + return m; + } - writeln(patch); - - writefln( - "file1: %s\nfile2: %s", - file1.size, file2.size - ); + void deinit() + { + xdl_free_mmfile(&mf); + } +} + +// ------------------------------ +// Коллбэки вывода +// ------------------------------ +extern (C) int mmfileOut(void* priv, mmbuffer_t* mb, int nbuf) +{ + auto mmfOut = cast(mmfile_t*) priv; + return xdl_writem_mmfile(mmfOut, mb, nbuf) < 0 ? -1 : 0; +} + +extern (C) int fdOut(void* priv, mmbuffer_t* mb, int nbuf) +{ + const fd = cast(int) cast(size_t) priv; // 1=stdout, 2=stderr + foreach (i; 0 .. nbuf) + { + auto ptr = mb[i].ptr; + auto left = cast(size_t) mb[i].size; + while (left) + { + auto n = posix_write(fd, ptr, left); + if (n < 0) + return -1; + ptr += n; + left -= cast(size_t) n; + } + } + return 0; +} + +// ------------------------------ +// Загрузка/выгрузка +// ------------------------------ +void loadMmFile(ref MmFile m, string path) +{ + try + { + enforce(exists(path), "no such file: " ~ path); + auto data = cast(const(char)[]) read(path); // читает весь файл + auto dst = cast(char*) xdl_mmfile_writeallocate(&m.mf, data.length); + enforce(dst !is null, "xdl_mmfile_writeallocate failed (null)"); + if (data.length) + memcpy(dst, data.ptr, data.length); + } + catch (Exception e) + { + // пробрасываем с контекстом пути + throw new Exception("loadMmFile(" ~ path ~ "): " ~ e.msg); + } +} + +void dumpMmFile(const ref MmFile m, string path) +{ + File f = File(path, "wb"); + scope (exit) + f.close(); + long sz = 0; + auto blk = cast(char*) xdl_mmfile_first(cast(mmfile_t*)&m.mf, &sz); + while (blk !is null) + { + f.rawWrite(blk[0 .. cast(size_t) sz]); + blk = cast(char*) xdl_mmfile_next(cast(mmfile_t*)&m.mf, &sz); + } +} + +// ------------------------------ +// Аллокатор +// ------------------------------ +extern (C) void* wrap_malloc(void*, uint size) +{ + return malloc(size); +} + +extern (C) void wrap_free(void*, void* ptr) +{ + free(ptr); +} + +extern (C) void* wrap_realloc(void*, void* ptr, uint size) +{ + return realloc(ptr, size); +} + +void installAllocator() +{ + memallocator_t malt; + malt.priv = null; + malt.malloc = &wrap_malloc; + malt.free = &wrap_free; + malt.realloc = &wrap_realloc; + enforce(xdl_set_allocator(&malt) == 0, "xdl_set_allocator failed"); +} + +// ------------------------------ +// Команды +// ------------------------------ +int doDiff(ref MmFile a, ref MmFile b, long ctxlen) +{ + xpparam_t xpp; + xpp.flags = 0; + xdemitconf_t xecfg; + xecfg.ctxlen = ctxlen; + xdemitcb_t ecb; + ecb.priv = cast(void*) cast(size_t) 1; + ecb.outf = &fdOut; + return xdl_diff(&a.mf, &b.mf, &xpp, &xecfg, &ecb); +} + +int doPatch(ref MmFile orig, ref MmFile patch, int mode) +{ + xdemitcb_t outcb; + outcb.priv = cast(void*) cast(size_t) 1; + outcb.outf = &fdOut; + xdemitcb_t rejcb; + rejcb.priv = cast(void*) cast(size_t) 2; + rejcb.outf = &fdOut; + return xdl_patch(&orig.mf, &patch.mf, mode, &outcb, &rejcb); +} + +int doBDiff(ref MmFile a, ref MmFile b, long bsize) +{ + bdiffparam_t bdp; + bdp.bsize = bsize; + xdemitcb_t ecb; + ecb.priv = cast(void*) cast(size_t) 1; + ecb.outf = &fdOut; + return xdl_bdiff(&a.mf, &b.mf, &bdp, &ecb); +} + +int doRaBDiff(ref MmFile a, ref MmFile b) +{ + xdemitcb_t ecb; + ecb.priv = cast(void*) cast(size_t) 1; + ecb.outf = &fdOut; + return xdl_rabdiff(&a.mf, &b.mf, &ecb); +} + +int doBPatch(ref MmFile base, ref MmFile binPatch) +{ + xdemitcb_t ecb; + ecb.priv = cast(void*) cast(size_t) 1; + ecb.outf = &fdOut; + return xdl_bpatch(&base.mf, &binPatch.mf, &ecb); +} + +// ------------------------------ +// CLI +// ------------------------------ +enum Cmd +{ + diff, + patch, + bdiff, + rabdiff, + bpatch +} + +private Cmd parseCmd(string s) +{ + final switch (s) + { + case "diff": + return Cmd.diff; + case "patch": + return Cmd.patch; + case "bdiff": + return Cmd.bdiff; + case "rabdiff": + return Cmd.rabdiff; + case "bpatch": + return Cmd.bpatch; + } +} + +/// Короткая справка +void printUsage(string prg) +{ + stderr.writefln( + "use: %1$s diff [-C N] FROM TO\n" ~ + " %1$s patch ORIG PATCH\n" ~ + " %1$s bdiff [-B N] FROM TO\n" ~ + " %1$s rabdiff FROM TO\n" ~ + " %1$s bpatch ORIG PATCH\n", + prg); +} + +/// Парсинг подкоманды + её опций. Возвращаем уже «очищенный» список позиционных. +struct Parsed +{ + Cmd cmd; + long ctxlen = 3; // для diff + long bsize = 16; // для bdiff + string[] positional; // оставшиеся позиционные (пути) +} + +Parsed parseArgs(string[] args) +{ + enforce(args.length >= 2, "no subcommand"); + auto sub = args[1]; + auto cmd = parseCmd(sub); + + long ctxlen = 3; + long bsize = 16; + bool helpWanted = false; + + // Опции и позиционные ТОЛЬКО после подкоманды + string[] optArgs = args.length > 2 ? args[2 .. $].dup : []; + + // getopt выкинет опции из optArgs и оставит только позиционные + auto r = getopt(optArgs, + "h|help", &helpWanted, + "C", &ctxlen, // актуально для `diff` + "B", &bsize // актуально для `bdiff` + + + + ); + enforce(!helpWanted, "help"); + + Parsed p; + p.cmd = cmd; + p.ctxlen = ctxlen; + p.bsize = bsize; + p.positional = optArgs; // здесь уже только пути + return p; +} + +int main(string[] args) +{ + // 1) Парсим + Parsed p; + try + { + p = parseArgs(args); + } + catch (Exception) + { + printUsage(args[0]); + return 1; + } + if (p.positional.length != 2) + { + // каждая подкоманда требует ровно 2 пути + printUsage(args[0]); + return 1; + } + auto p1 = p.positional[0]; + auto p2 = p.positional[1]; + + // 2) Аллокатор libxdiff + installAllocator(); + + // 3) Готовим входы + auto mf1 = MmFile.create(); + scope (exit) + mf1.deinit(); + auto mf2 = MmFile.create(); + scope (exit) + mf2.deinit(); + + try + { + loadMmFile(mf1, p1); + loadMmFile(mf2, p2); + } + catch (Exception e) + { + stderr.writeln(e.msg); + return 1; + } + + // 4) Запускаем действие + int rc = 0; + final switch (p.cmd) + { + case Cmd.diff: + rc = doDiff(mf1, mf2, p.ctxlen); + break; + case Cmd.patch: + rc = doPatch(mf1, mf2, XDL_PATCH_NORMAL); + break; + case Cmd.bdiff: + rc = doBDiff(mf1, mf2, p.bsize); + break; + case Cmd.rabdiff: + rc = doRaBDiff(mf1, mf2); + break; + case Cmd.bpatch: + rc = doBPatch(mf1, mf2); + break; + } + return rc < 0 ? 2 : 0; } diff --git a/source/libxdiff/init.d b/source/libxdiff/init.d deleted file mode 100644 index 546679c..0000000 --- a/source/libxdiff/init.d +++ /dev/null @@ -1,70 +0,0 @@ -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 deleted file mode 100644 index 8ec975a..0000000 --- a/source/libxdiff/mmblocks.d +++ /dev/null @@ -1,41 +0,0 @@ -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 deleted file mode 100644 index 1bcbca4..0000000 --- a/source/libxdiff/mmfile.d +++ /dev/null @@ -1,79 +0,0 @@ -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 deleted file mode 100644 index 3e27fe2..0000000 --- a/source/libxdiff/package.d +++ /dev/null @@ -1,4 +0,0 @@ -module libxdiff; - -public import libxdiff.mmfile; -public import libxdiff.mmblocks;