Initial commit.
commit
b210f39f07
|
@ -0,0 +1,3 @@
|
||||||
|
[submodule "bsdiff"]
|
||||||
|
path = bsdiff
|
||||||
|
url = https://github.com/mendsley/bsdiff.git
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 7d70d8f4ff48345bc76e314c9d98da91f78873fa
|
|
@ -0,0 +1,31 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/icedream/go-bsdiff"
|
||||||
|
)
|
||||||
|
|
||||||
|
func must(err error) {
|
||||||
|
if err == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
oldFile, err := os.Open("test.txt")
|
||||||
|
must(err)
|
||||||
|
|
||||||
|
newFile, err := os.Open("test_new.txt")
|
||||||
|
must(err)
|
||||||
|
|
||||||
|
patchFile, err := os.Create("test.patch")
|
||||||
|
must(err)
|
||||||
|
|
||||||
|
must(bsdiff.Diff(oldFile, newFile, patchFile))
|
||||||
|
|
||||||
|
log.Println("Done.")
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
package bsdiff
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
|
||||||
|
"github.com/dsnet/compress/bzip2"
|
||||||
|
"github.com/icedream/go-bsdiff/internal/native"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Diff(oldReader, newReader io.Reader, patchWriter io.Writer) (err error) {
|
||||||
|
oldBytes, err := ioutil.ReadAll(oldReader)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
newBytes, err := ioutil.ReadAll(newReader)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = writeHeader(patchWriter, uint64(len(newBytes))); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compression
|
||||||
|
bz2Writer, err := bzip2.NewWriter(patchWriter, nil)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer bz2Writer.Close()
|
||||||
|
|
||||||
|
return native.Diff(oldBytes, newBytes, bz2Writer)
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
#include "cgo.h"
|
||||||
|
|
||||||
|
#include "bsdiff.h"
|
||||||
|
#include "bspatch.h"
|
||||||
|
|
||||||
|
extern int cgo_write_buffer(int bufferIndex, void* buf, int size);
|
||||||
|
|
||||||
|
int cgo_write(struct bsdiff_stream* stream,
|
||||||
|
const void* buf, int size) {
|
||||||
|
struct buffer_table_index* bufferEntry;
|
||||||
|
|
||||||
|
bufferEntry = (struct buffer_table_index*)stream->opaque;
|
||||||
|
|
||||||
|
return cgo_write_buffer(bufferEntry->index, (void*)buf, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern int cgo_read_buffer(int bufferIndex, void* buf, int size);
|
||||||
|
|
||||||
|
int cgo_read(const struct bspatch_stream* stream,
|
||||||
|
void* buf, int size) {
|
||||||
|
struct buffer_table_index* bufferEntry;
|
||||||
|
|
||||||
|
bufferEntry = (struct buffer_table_index*)stream->opaque;
|
||||||
|
|
||||||
|
return cgo_read_buffer(bufferEntry->index, buf, size) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bsdiff_cgo(uint8_t* oldptr, int64_t oldsize,
|
||||||
|
uint8_t* newptr, int64_t newsize,
|
||||||
|
int bufferIndex)
|
||||||
|
{
|
||||||
|
struct bsdiff_stream stream;
|
||||||
|
stream.malloc = malloc;
|
||||||
|
stream.free = free;
|
||||||
|
stream.write = cgo_write;
|
||||||
|
|
||||||
|
struct buffer_table_index bufferEntry;
|
||||||
|
bufferEntry.index = bufferIndex;
|
||||||
|
stream.opaque = &bufferEntry;
|
||||||
|
|
||||||
|
return bsdiff(oldptr, oldsize, newptr, newsize, &stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
int bspatch_cgo(uint8_t* oldptr, int64_t oldsize,
|
||||||
|
uint8_t* newptr, int64_t newsize,
|
||||||
|
int bufferIndex)
|
||||||
|
{
|
||||||
|
struct bspatch_stream stream;
|
||||||
|
stream.read = cgo_read;
|
||||||
|
|
||||||
|
struct buffer_table_index bufferEntry;
|
||||||
|
bufferEntry.index = bufferIndex;
|
||||||
|
stream.opaque = &bufferEntry;
|
||||||
|
|
||||||
|
return bspatch(oldptr, oldsize, newptr, newsize, &stream);
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "stdint.h"
|
||||||
|
|
||||||
|
struct buffer_table_index
|
||||||
|
{
|
||||||
|
int index;
|
||||||
|
};
|
||||||
|
|
||||||
|
int bsdiff_cgo(uint8_t* oldptr, int64_t oldsize,
|
||||||
|
uint8_t* newptr, int64_t newsize,
|
||||||
|
int bufferIndex);
|
||||||
|
|
||||||
|
int bspatch_cgo(uint8_t* oldptr, int64_t oldsize,
|
||||||
|
uint8_t* newptr, int64_t newsize,
|
||||||
|
int bufferIndex);
|
|
@ -0,0 +1,43 @@
|
||||||
|
package native
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include "bspatch.h"
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
//export cgo_read_buffer
|
||||||
|
func cgo_read_buffer(bufferIndex C.int,
|
||||||
|
bufPtr unsafe.Pointer, length C.int) C.int {
|
||||||
|
goLength := int(length)
|
||||||
|
|
||||||
|
if goLength == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceBuffer := readers.Get(int(bufferIndex))
|
||||||
|
targetBuffer := cPtrToSlice(bufPtr, goLength)
|
||||||
|
|
||||||
|
errCode := 0
|
||||||
|
offset := 0
|
||||||
|
for offset < goLength {
|
||||||
|
n, err := sourceBuffer.Read(targetBuffer)
|
||||||
|
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
} else if err != nil {
|
||||||
|
log.Println("cgo_read_buffer failed:", err)
|
||||||
|
errCode = 1
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
offset += n
|
||||||
|
targetBuffer = targetBuffer[n:]
|
||||||
|
}
|
||||||
|
|
||||||
|
return C.int(errCode)
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
package native
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include "bsdiff.h"
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
import "unsafe"
|
||||||
|
|
||||||
|
//export cgo_write_buffer
|
||||||
|
func cgo_write_buffer(bufferIndex C.int,
|
||||||
|
dataPtr unsafe.Pointer, size C.int) C.int {
|
||||||
|
buffer := writers.Get(int(bufferIndex))
|
||||||
|
errCode := 0
|
||||||
|
if _, err := buffer.Write(cPtrToSlice(dataPtr, int(size))); err != nil {
|
||||||
|
errCode = 1
|
||||||
|
}
|
||||||
|
return C.int(errCode)
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
package native
|
||||||
|
|
||||||
|
/*
|
||||||
|
#cgo CFLAGS: -I../../bsdiff
|
||||||
|
|
||||||
|
#include "bsdiff.h"
|
||||||
|
#include "cgo.h"
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Diff(oldbytes, newbytes []byte, patch io.Writer) (err error) {
|
||||||
|
oldptr, oldsize := bytesToUint8PtrAndSize(oldbytes)
|
||||||
|
newptr, newsize := bytesToUint8PtrAndSize(newbytes)
|
||||||
|
|
||||||
|
bufferIndex := writers.Add(patch)
|
||||||
|
|
||||||
|
errCode := int(C.bsdiff_cgo(oldptr, oldsize, newptr, newsize, C.int(bufferIndex)))
|
||||||
|
if errCode != 0 {
|
||||||
|
err = errors.New("bsdiff failed")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
#include "bsdiff.c"
|
||||||
|
#include "bspatch.c"
|
|
@ -0,0 +1,31 @@
|
||||||
|
package native
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include <stdint.h>
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
writers = writerTable{}
|
||||||
|
readers = readerTable{}
|
||||||
|
)
|
||||||
|
|
||||||
|
func bytesToUint8PtrAndSize(bytes []byte) (ptr *C.uint8_t, size C.int64_t) {
|
||||||
|
ptr = (*C.uint8_t)(unsafe.Pointer(&bytes[0]))
|
||||||
|
size = C.int64_t(int64(len(bytes)))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func cPtrToSlice(ptr unsafe.Pointer, size int) []byte {
|
||||||
|
var slice []byte
|
||||||
|
sliceHeader := (*reflect.SliceHeader)(unsafe.Pointer(&slice))
|
||||||
|
sliceHeader.Cap = size
|
||||||
|
sliceHeader.Len = size
|
||||||
|
sliceHeader.Data = uintptr(ptr)
|
||||||
|
|
||||||
|
return slice
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
package native
|
||||||
|
|
||||||
|
/*
|
||||||
|
#cgo CFLAGS: -I../../bsdiff
|
||||||
|
|
||||||
|
#include "bspatch.h"
|
||||||
|
#include "cgo.h"
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Patch(oldbytes, newbytes []byte, patch io.Reader) (err error) {
|
||||||
|
oldptr, oldsize := bytesToUint8PtrAndSize(oldbytes)
|
||||||
|
newptr, newsize := bytesToUint8PtrAndSize(newbytes)
|
||||||
|
|
||||||
|
bufferIndex := readers.Add(patch)
|
||||||
|
|
||||||
|
errCode := int(C.bspatch_cgo(oldptr, oldsize, newptr, newsize, C.int(bufferIndex)))
|
||||||
|
if errCode != 0 {
|
||||||
|
err = errors.New("bspatch failed with code " + strconv.Itoa(errCode))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
package native
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
type readerTable struct {
|
||||||
|
nextIndex int
|
||||||
|
table map[int]io.Reader
|
||||||
|
mutex sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bt *readerTable) Add(reader io.Reader) (index int) {
|
||||||
|
bt.mutex.Lock()
|
||||||
|
defer bt.mutex.Unlock()
|
||||||
|
|
||||||
|
if bt.table == nil {
|
||||||
|
bt.table = map[int]io.Reader{}
|
||||||
|
}
|
||||||
|
|
||||||
|
index = bt.nextIndex
|
||||||
|
bt.table[index] = reader
|
||||||
|
|
||||||
|
// TODO - Handle int overflow
|
||||||
|
|
||||||
|
bt.nextIndex++
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bt *readerTable) Get(index int) io.Reader {
|
||||||
|
bt.mutex.Lock()
|
||||||
|
defer bt.mutex.Unlock()
|
||||||
|
|
||||||
|
return bt.table[index]
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
package native
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
type writerTable struct {
|
||||||
|
nextIndex int
|
||||||
|
table map[int]io.Writer
|
||||||
|
mutex sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bt *writerTable) Add(writer io.Writer) (index int) {
|
||||||
|
bt.mutex.Lock()
|
||||||
|
defer bt.mutex.Unlock()
|
||||||
|
|
||||||
|
if bt.table == nil {
|
||||||
|
bt.table = map[int]io.Writer{}
|
||||||
|
}
|
||||||
|
|
||||||
|
index = bt.nextIndex
|
||||||
|
bt.table[index] = writer
|
||||||
|
|
||||||
|
// TODO - Handle int overflow
|
||||||
|
|
||||||
|
bt.nextIndex++
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bt *writerTable) Get(index int) io.Writer {
|
||||||
|
bt.mutex.Lock()
|
||||||
|
defer bt.mutex.Unlock()
|
||||||
|
|
||||||
|
return bt.table[index]
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
package bsdiff
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrInvalidMagic = errors.New("Invalid magic")
|
||||||
|
|
||||||
|
sizeEncoding = binary.BigEndian
|
||||||
|
|
||||||
|
magicText = []byte("ENDSLEY/BSDIFF43")
|
||||||
|
)
|
||||||
|
|
||||||
|
func writeHeader(w io.Writer, size uint64) (err error) {
|
||||||
|
if _, err = w.Write(magicText); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = binary.Write(w, sizeEncoding, size)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func readHeader(r io.Reader) (size uint64, err error) {
|
||||||
|
magicBuf := make([]byte, len(magicText))
|
||||||
|
n, err := r.Read(magicBuf)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if n < len(magicText) {
|
||||||
|
err = ErrInvalidMagic
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = binary.Read(r, sizeEncoding, &size)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
package bsdiff
|
||||||
|
|
||||||
|
import (
|
||||||
|
"compress/bzip2"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/icedream/go-bsdiff/internal/native"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Patch(oldReader io.Reader, newWriter io.Writer, patchReader io.Reader) (err error) {
|
||||||
|
oldBytes, err := ioutil.ReadAll(oldReader)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
newLen, err := readHeader(patchReader)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
newBytes := make([]byte, newLen)
|
||||||
|
|
||||||
|
log.Printf("Going to create a file of %d bytes.", newLen)
|
||||||
|
|
||||||
|
// Decompression
|
||||||
|
bz2Reader := bzip2.NewReader(patchReader)
|
||||||
|
|
||||||
|
err = native.Patch(oldBytes, newBytes, bz2Reader)
|
||||||
|
|
||||||
|
newWriter.Write(newBytes)
|
||||||
|
return
|
||||||
|
}
|
Loading…
Reference in New Issue