init
This commit is contained in:
commit
e9952bacf2
6 changed files with 392 additions and 0 deletions
17
.gitignore
vendored
Normal file
17
.gitignore
vendored
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
.dub
|
||||||
|
docs.json
|
||||||
|
__dummy.html
|
||||||
|
docs/
|
||||||
|
/dxdiff
|
||||||
|
dxdiff.so
|
||||||
|
dxdiff.dylib
|
||||||
|
dxdiff.dll
|
||||||
|
dxdiff.a
|
||||||
|
dxdiff.lib
|
||||||
|
dxdiff-test-*
|
||||||
|
*.exe
|
||||||
|
*.pdb
|
||||||
|
*.o
|
||||||
|
*.obj
|
||||||
|
*.lst
|
||||||
|
/bin
|
17
.vscode/launch.json
vendored
Normal file
17
.vscode/launch.json
vendored
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
// Используйте IntelliSense, чтобы узнать о возможных атрибутах.
|
||||||
|
// Наведите указатель мыши, чтобы просмотреть описания существующих атрибутов.
|
||||||
|
// Для получения дополнительной информации посетите: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"type": "code-d",
|
||||||
|
"request": "launch",
|
||||||
|
"dubBuild": true,
|
||||||
|
"name": "Build & Debug DUB project",
|
||||||
|
"cwd": "${command:dubWorkingDirectory}",
|
||||||
|
"program": "bin/${command:dubTarget}",
|
||||||
|
"args": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
5
.vscode/settings.json
vendored
Normal file
5
.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"editor.insertSpaces": false,
|
||||||
|
"editor.tabSize": 4,
|
||||||
|
"editor.detectIndentation": false
|
||||||
|
}
|
11
README.md
Normal file
11
README.md
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
# dxdiff
|
||||||
|
|
||||||
|
Тестовый пример использования библиотеки LibXDiff.
|
||||||
|
|
||||||
|
```
|
||||||
|
use: ./bin/dxdiff diff [-C N] FROM TO
|
||||||
|
./bin/dxdiff patch ORIG PATCH
|
||||||
|
./bin/dxdiff bdiff [-B N] FROM TO
|
||||||
|
./bin/dxdiff rabdiff FROM TO
|
||||||
|
./bin/dxdiff bpatch ORIG PATCH
|
||||||
|
```
|
20
dub.json
Normal file
20
dub.json
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"authors": [
|
||||||
|
"alexander"
|
||||||
|
],
|
||||||
|
"copyright": "Copyright © 2025, alexander",
|
||||||
|
"description": "A minimal D application.",
|
||||||
|
"license": "proprietary",
|
||||||
|
"name": "dxdiff",
|
||||||
|
"targetPath": "bin",
|
||||||
|
"targetType": "executable",
|
||||||
|
"libs": [
|
||||||
|
"xdiff"
|
||||||
|
],
|
||||||
|
"dependencies": {
|
||||||
|
"xdiff": {
|
||||||
|
"repository": "git+https://git.zhirov.kz/dlang/xdiff.git",
|
||||||
|
"version": "e2396bc172eba813cdcd1a96c494e35d687f576a"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
322
source/app.d
Normal file
322
source/app.d
Normal file
|
@ -0,0 +1,322 @@
|
||||||
|
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 xdiff;
|
||||||
|
|
||||||
|
// ------------------------------
|
||||||
|
// RAII для mmfile_t
|
||||||
|
// ------------------------------
|
||||||
|
struct MmFile
|
||||||
|
{
|
||||||
|
mmfile_t mf;
|
||||||
|
|
||||||
|
// фабрика вместо конструктора по умолчанию
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue