Compare commits
No commits in common. "manual" and "master" have entirely different histories.
6 changed files with 316 additions and 214 deletions
|
@ -1,6 +0,0 @@
|
||||||
{
|
|
||||||
"fileVersion": 1,
|
|
||||||
"versions": {
|
|
||||||
"libxdiff": {"version":"e2396bc172eba813cdcd1a96c494e35d687f576a","repository":"git+https://git.zhirov.kz/dlang/xdiff.git"}
|
|
||||||
}
|
|
||||||
}
|
|
332
source/app.d
332
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 xdiff;
|
||||||
import std.stdio : writefln, writeln;
|
|
||||||
import std.file : read;
|
|
||||||
|
|
||||||
void main()
|
// ------------------------------
|
||||||
|
// RAII для mmfile_t
|
||||||
|
// ------------------------------
|
||||||
|
struct MmFile
|
||||||
{
|
{
|
||||||
auto file1 = new MMFile(cast(const(ubyte)[]) read("/tmp/diff1.d"));
|
mmfile_t mf;
|
||||||
auto file2 = new MMFile(cast(const(ubyte)[]) read("/tmp/diff2.d"));
|
|
||||||
|
|
||||||
auto patch = file1.computePatch(file2);
|
// фабрика вместо конструктора по умолчанию
|
||||||
|
static MmFile create(long bsize = 8 * 1024, ulong flags = XDL_MMF_ATOMIC)
|
||||||
writeln(patch);
|
{
|
||||||
|
MmFile m;
|
||||||
writefln(
|
enforce(xdl_init_mmfile(&m.mf, bsize, flags) == 0, "xdl_init_mmfile failed");
|
||||||
"file1: %s\nfile2: %s",
|
return m;
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,4 +0,0 @@
|
||||||
module libxdiff;
|
|
||||||
|
|
||||||
public import libxdiff.mmfile;
|
|
||||||
public import libxdiff.mmblocks;
|
|
Loading…
Add table
Add a link
Reference in a new issue