diff --git a/.gitignore b/.gitignore index 6704566..cdd45b3 100644 --- a/.gitignore +++ b/.gitignore @@ -36,6 +36,7 @@ bower_components # Compiled binary addons (https://nodejs.org/api/addons.html) build/Release +build/Debug # Dependency directories node_modules/ @@ -102,3 +103,5 @@ dist # TernJS port file .tern-port +.idea +.vscode/ diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..3f3fd4d --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "HDiffPatch"] + path = HDiffPatch + url = https://github.com/sisong/HDiffPatch.git +[submodule "lzma"] + path = lzma + url = https://github.com/sisong/lzma.git diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..08fe6ed --- /dev/null +++ b/.npmignore @@ -0,0 +1,2 @@ +.idea +build diff --git a/HDiffPatch b/HDiffPatch new file mode 160000 index 0000000..eb959d6 --- /dev/null +++ b/HDiffPatch @@ -0,0 +1 @@ +Subproject commit eb959d671cd94281ca763d609f638b5d248df3a3 diff --git a/README.md b/README.md index 0f36e3d..947c255 100644 --- a/README.md +++ b/README.md @@ -1 +1,17 @@ -# node-hdiffpatch \ No newline at end of file +# node-hdiffpatch + +Create patch buffer with origin buffer with [HDiffPatch](https://github.com/sisong/HDiffPatch) + +Patch compatible with HDiffPatch -SD + +## Installation + +```bash +npm install --save node-hdiffpatch +``` + +## Usage + +### diff(originBuf, newBuf) + +Compare two buffers and return a new hdiffpatch patch as return value. diff --git a/binding.gyp b/binding.gyp new file mode 100644 index 0000000..2c014a7 --- /dev/null +++ b/binding.gyp @@ -0,0 +1,34 @@ +{ + "targets": [ + { + "target_name": "hdiffpatch", + "sources": [ + "src/main.cc", + "src/hdiff.cpp", + "HDiffPatch/libHDiffPatch/HPatch/patch.c", + "HDiffPatch/libHDiffPatch/HDiff/diff.cpp", + "HDiffPatch/libHDiffPatch/HDiff/private_diff/bytes_rle.cpp", + "HDiffPatch/libHDiffPatch/HDiff/private_diff/suffix_string.cpp", + "HDiffPatch/libHDiffPatch/HDiff/private_diff/compress_detect.cpp", + "HDiffPatch/libHDiffPatch/HDiff/private_diff/libdivsufsort/divsufsort64.cpp", + "HDiffPatch/libHDiffPatch/HDiff/private_diff/libdivsufsort/divsufsort.cpp", + "HDiffPatch/libHDiffPatch/HDiff/private_diff/limit_mem_diff/digest_matcher.cpp", + "HDiffPatch/libHDiffPatch/HDiff/private_diff/limit_mem_diff/stream_serialize.cpp", + "HDiffPatch/libHDiffPatch/HDiff/private_diff/limit_mem_diff/adler_roll.cpp", + "lzma/C/LzFind.c", + "lzma/C/LzmaDec.c", + "lzma/C/LzmaEnc.c", + "lzma/C/Lzma2Dec.c", + "lzma/C/Lzma2Enc.c" + ], + "defines": [ + "_IS_NEED_DIR_DIFF_PATCH=0", + "_7ZIP_ST", + "_IS_USED_MULTITHREAD=0" + ], + "include_dirs" : [ + " // for std::runtime_error + +#define _CompressPlugin_lzma2 +#define _IsNeedIncludeDefaultCompressHead 0 +#include "../lzma/C/LzmaDec.h" +#include "../lzma/C/Lzma2Dec.h" +#include "../lzma/C/LzmaEnc.h" +#include "../lzma/C/Lzma2Enc.h" +#include "../lzma/C/MtCoder.h" +#include "../HDiffPatch/compress_plugin_demo.h" +#include "../HDiffPatch/decompress_plugin_demo.h" + +void hdiff(const uint8_t* old,size_t oldsize,const uint8_t* _new,size_t newsize, + std::vector& out_codeBuf){ + const int myBestSingleMatchScore=3; + const size_t myBestStepMemSize=kDefaultStepMemSize; + const size_t myBestDictSize=(1<<20)*8; //8MB mem + + hpatch_TDecompress* decompressPlugin=&lzma2DecompressPlugin; + TCompressPlugin_lzma2 compressPlugin=lzma2CompressPlugin; + compressPlugin.compress_level=9; + compressPlugin.dict_size=myBestDictSize; + compressPlugin.thread_num=1; + + create_single_compressed_diff(_new,_new+newsize,old,old+oldsize,out_codeBuf,0, + &compressPlugin.base,myBestSingleMatchScore,myBestStepMemSize); + + if (!check_single_compressed_diff(_new,_new+newsize,old,old+oldsize,out_codeBuf.data(), + out_codeBuf.data()+out_codeBuf.size(),decompressPlugin)){ + throw std::runtime_error("create_single_compressed_diff() out data error!"); + } +} \ No newline at end of file diff --git a/src/hdiff.h b/src/hdiff.h new file mode 100644 index 0000000..a4e4559 --- /dev/null +++ b/src/hdiff.h @@ -0,0 +1,13 @@ +/** + * Created by housisong on 2021.04.07. + */ + +#ifndef HDIFFPATCH_DIFF_H +#define HDIFFPATCH_DIFF_H +#include +#include + +void hdiff(const uint8_t* old,size_t oldsize,const uint8_t* _new,size_t newsize, + std::vector& out_codeBuf); + +#endif diff --git a/src/main.cc b/src/main.cc new file mode 100644 index 0000000..e406d8b --- /dev/null +++ b/src/main.cc @@ -0,0 +1,85 @@ +/** + * Created by housisong on 2021.04.07. + */ +#include +#include +#include +#include +#include "hdiff.h" + +using namespace std; + +namespace hdiffpatchNode +{ + using v8::FunctionCallbackInfo; + using v8::HandleScope; + using v8::Isolate; + using v8::Local; + using v8::Object; + using v8::String; + using v8::Value; + using v8::Function; + using v8::MaybeLocal; + using v8::Null; + using v8::Boolean; + using v8::Exception; + + struct DiffStreamOpaque { + Isolate* isolate; + Local cb; + }; + + static int callback_write(DiffStreamOpaque* opaque, const void* buffer, size_t size) + { + + Local returnObj = node::Buffer::Copy(opaque->isolate, (const char*)buffer, size).ToLocalChecked(); + + Local argv[1] = { returnObj }; + // opaque->cb->Call(Nan::GetCurrentContext()->Global(), Null(opaque->isolate), 1, argv); + + Nan::MakeCallback(Nan::GetCurrentContext()->Global(), opaque->cb, 1, argv); + + return 0; + } + + void diff(const FunctionCallbackInfo& args) { + Isolate* isolate = args.GetIsolate(); + HandleScope scope(isolate); + + if (!node::Buffer::HasInstance(args[0]) || !node::Buffer::HasInstance(args[1]) || !args[2]->IsFunction()) { + Nan::ThrowError("Invalid arguments."); + } + + char* oldData = node::Buffer::Data(args[0]); + size_t oldLength = node::Buffer::Length(args[0]); + char* newData = node::Buffer::Data(args[1]); + size_t newLength = node::Buffer::Length(args[1]); + + DiffStreamOpaque streamOpaque; + streamOpaque.isolate = isolate; + streamOpaque.cb = Local::Cast(args[2]); + + std::vector codeBuf; + try{ + hdiff((const uint8_t*)oldData,oldLength,(const uint8_t*)newData,newLength,codeBuf); + }catch(const std::exception& e){ + Nan::ThrowError("Create hdiff failed."); + } + if (0!=callback_write(&streamOpaque,codeBuf.data(),codeBuf.size())) + Nan::ThrowError("Write DiffStreamOpaque failed."); + +// args.GetReturnValue().Set(returnObj); +// args.GetReturnValue().Set(String::NewFromUtf8(isolate, bufferData, String::kNormalString, bufferLength)); + } + + void init(Local exports) + { + Isolate* isolate = exports->GetIsolate(); + HandleScope scope(isolate); + + NODE_SET_METHOD(exports, "diff", diff); + } + + NODE_MODULE(hdiffpatch, init) + +} // namespace hdiffpatch diff --git a/test/test.js b/test/test.js new file mode 100644 index 0000000..8c393b5 --- /dev/null +++ b/test/test.js @@ -0,0 +1,21 @@ +var hdiffpatch = require("../index"); +var test_hdiffpatch = require("../index_test"); +var crypto = require("crypto"); +var md5 = require("md5"); +var assert = require("assert").strict; + +var cur = crypto.randomBytes(40960); + +var ref = Buffer.concat([ + Buffer.from("fdsa"), + cur.slice(0, 4096), + Buffer.from("asdf"), + cur.slice(4096), + Buffer.from("asdf") +]); + +assert.deepStrictEqual( + md5(hdiffpatch.diff(cur, ref)), + md5(test_hdiffpatch.diff(cur, ref)) +); +console.log("pass"); diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 0000000..0afa0c3 --- /dev/null +++ b/yarn.lock @@ -0,0 +1,49 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +amdefine@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" + integrity sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU= + +charenc@~0.0.1: + version "0.0.2" + resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" + integrity sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc= + +commander@~2.8.1: + version "2.8.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.8.1.tgz#06be367febfda0c330aa1e2a072d3dc9762425d4" + integrity sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ= + dependencies: + graceful-readlink ">= 1.0.0" + +crypt@~0.0.1: + version "0.0.2" + resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" + integrity sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs= + +"graceful-readlink@>= 1.0.0": + version "1.0.1" + resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" + integrity sha1-TK+tdrxi8C+gObL5Tpo906ORpyU= + +is-buffer@~1.1.1: + version "1.1.6" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" + integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== + +md5@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/md5/-/md5-2.2.1.tgz#53ab38d5fe3c8891ba465329ea23fac0540126f9" + integrity sha1-U6s41f48iJG6RlMp6iP6wFQBJvk= + dependencies: + charenc "~0.0.1" + crypt "~0.0.1" + is-buffer "~1.1.1" + +nan@^2.14.0: + version "2.14.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" + integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==