bsdiff和bspatch是用来生成和应用二进制补丁的工具,也就是bsdiff通过新旧文件生成差分包,bspatch通过旧文件和差分包生成新文件,通过差分包的传输,能有效减少网络间传输的流量和时间。bsdiff和bspatch都是基于bzip2,并默认其位置在于/usr/bin。
bsdiff和bspatch在运行时都需要消耗大量的内存空间和时间,假设n是旧文件的大小,m是新文件的大小,那么bsdiff需要的内存空间为max(17n, 9n+m)+O(1)字节;而bspatch需要n+m+O(1)字节。
首先在bsdiff的官网中,可以直接下载bsdiff-4.3这个版本的源码,但是这个版本的源码仅仅支持Linux系统,不支持Windows系统的编译,就需要找到windows版本。
windows下bsdiff的编译
步骤如下:
- 查看main()函数需要什么参数
- 通过Java类,构建JNI头文件
- 将bsdiff源代码导入VS中并修改
- 通过VS将其编译成动态库
1. 查看main()函数需要什么参数
来看下bsdiff windows版本的文件目录:
首先来看下bsdiff.cpp文件中main()函数有这么一行代码:
if(argc!=4) errx(1,"usage: %s oldfile newfile patchfile\n",argv[0]);
表明main()函数需要传入4个参数,而第一个参数任意填,接着继续读main()函数的方法,可以得知剩余的3个参数分别是旧文件路径、新文件路径和差分包路径。
2. 通过Java类,构建JNI头文件
于是我们就可以编写Java代码和JNI代码。
public class BsDiff {
public native static void diff(String oldFile, String newFile, String patchFile);
static {
// LibLoader.loadLib("Bsdiff.dll");
LibLoader.loadLib("Bsdiff.so");
}
}
编译,生成class文件后,通过Idea IDE生成jni头文件。在setting设置中,自定义如下工具
随后调用该工具,就可以生成BsDiff这个类的JNI头文件。
头文件如下:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include
/* Header for class com_ljh_jni_BsDiff */
#ifndef _Included_com_ljh_jni_BsDiff
#define _Included_com_ljh_jni_BsDiff
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_ljh_jni_BsDiff
* Method: diff
* Signature: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
*/
JNIEXPORT void JNICALL Java_com_ljh_jni_BsDiff_diff
(JNIEnv *, jclass, jstring, jstring, jstring);
#ifdef __cplusplus
}
#endif
#endif
3. 将bsdiff源代码导入VS中并修改
在VS中新建空白项目,将bsdiff windows的源代码、jni.h、jni_md.h和刚刚编译出来的JNI头文件导入到项目中。项目目录如下:
随后就要修改源代码,使得整体调用逻辑变成通过JNI方法,调用bsdiff中原有的main()函数,生成差分包。
将JNI头文件(com_ljh_jni_BsDiff.h)中的头文件引用修改为本地:
#include -> #include "jni.h"
在bsdiff.cpp源文件中,添加JNI头文件、修改main()函数名、添加JNI函数的实现。
1. #include "com_ljh_jni_BsDiff.h"
2. int main(int argc,char *argv[]){ ... } -> int bsdiff_main(int argc,char *argv[]){...}
3.
//JNI调用
JNIEXPORT void JNICALL Java_com_ljh_jni_BsDiff_diff
(JNIEnv* env, jclass jcls, jstring oldfile_jstr, jstring newfile_jstr, jstring patchfile_jstr) {
int argc = 4;
char* oldfile = (char*)env->GetStringUTFChars(oldfile_jstr, NULL);
char* newfile = (char*)env->GetStringUTFChars(newfile_jstr, NULL);
char* patchfile = (char*)env->GetStringUTFChars(patchfile_jstr, NULL);
//参数(第一个参数无效)
char* argv[4];
argv[0] = (char *)"bsdiff";
argv[1] = oldfile;
argv[2] = newfile;
argv[3] = patchfile;
bsdiff_main(argc, argv);
env->ReleaseStringUTFChars(oldfile_jstr, oldfile);
env->ReleaseStringUTFChars(newfile_jstr, newfile);
env->ReleaseStringUTFChars(patchfile_jstr, patchfile);
}
4. 通过VS将其编译成动态库
在项目属性中,添加编译的命令行来取消安全性检查之类的异常
-D _CRT_SECURE_NO_WARNINGS -D _CRT_NONSTDC_NO_DEPRECATE
然后在常规设置中,将配置类型配置为动态库(.dll),即可编译。编译成功后,调用JNI中的
Linux下bsdiff的编译
在Linux中编译bsdiff的步骤与在windows中类似:
- 编译环境配置
- 通过Java类,构建JNI头文件
- 修改JNI头文件、源代码
- 编译成.so动态库
1. 编译环境配置
编译需要gcc和bzip2,通过如下指令安装bzip2
sudo apt-get install libbz2-dev
2.通过Java类,构建JNI头文件 3. 修改JNI头文件、源代码 参考windows编译
4. 编译成.so动态库
gcc bsdiff.c -lbz2 -fPIC -shared -o bsdiff.so