From ba81e091aac2efe014111f64164d54ec92e8e549 Mon Sep 17 00:00:00 2001 From: Kevin Chabowski Date: Wed, 27 Mar 2013 14:18:35 +0100 Subject: Implemented patching --- librsync/librsync.go | 67 ++++++++++++++++++++++++++++++++++++++++++++++ librsync/signature_test.go | 20 +++++++++++++- 2 files changed, 86 insertions(+), 1 deletion(-) diff --git a/librsync/librsync.go b/librsync/librsync.go index 5d51c10..e154da6 100644 --- a/librsync/librsync.go +++ b/librsync/librsync.go @@ -108,7 +108,25 @@ func (job *Job) Close() error { return nil } +// For errors in callbacks +type jobInternalPanic struct { + err error +} + +func (jp jobInternalPanic) Error() string { return jp.err.Error() } + func jobIter(job *C.rs_job_t, rsbufs *C.rs_buffers_t) (running bool, err error) { + defer func() { + r := recover() + jp, ok := r.(jobInternalPanic) + if !ok { + panic(r) + } + + running = false + err = jp.err + }() + switch res := C.rs_job_iter(job, rsbufs); res { case C.RS_DONE: case C.RS_BLOCKED: @@ -243,3 +261,52 @@ func NewDeltaGen(sig Signature, input io.Reader) (job *Job, err error) { return } + +// Patcher is a job with additional hidden data for patching. +// +// IMPORTANT: You still need to Close() this! +type Patcher struct { + *Job + base io.ReaderAt + buf []byte +} + +func _patch_callback(_patcher unsafe.Pointer, pos C.rs_long_t, len *C.size_t, _buf *unsafe.Pointer) C.rs_result { + patcher := (*Patcher)(_patcher) + + patcher.buf = make([]byte, int(*len)) + n, err := patcher.base.ReadAt(patcher.buf, int64(pos)) + if n < int(*len) { + if err != io.EOF { + panic(jobInternalPanic{err}) + } else { + return C.RS_INPUT_ENDED + } + } + *len = C.size_t(n) + *_buf = unsafe.Pointer(&(patcher.buf[0])) + + return C.RS_DONE +} + +var patch_callback = _patch_callback // So we can use the `&` operator in NewPatcher + +func NewPatcher(delta io.Reader, base io.ReaderAt) (job *Patcher, err error) { + _job, e := newJob(delta) + if e != nil { + err = e + return + } + + job = &Patcher{ + Job: _job, + base: base} + + job.job = C.rs_patch_begin((*C.rs_copy_cb)(unsafe.Pointer(&patch_callback)), unsafe.Pointer(job)) + if job.job == nil { + job.Close() + return nil, errors.New("rs_patch_begin failed") + } + + return +} diff --git a/librsync/signature_test.go b/librsync/signature_test.go index 152fd12..e0fd50f 100644 --- a/librsync/signature_test.go +++ b/librsync/signature_test.go @@ -76,5 +76,23 @@ func TestSignatureDeltaPatch(t *testing.T) { } } - // TODO: Test patching + // Apply Patch + patchres := new(bytes.Buffer) + patcher, err := NewPatcher(deltabuf, orig) + if err != nil { + t.Fatalf("could not create a patcher: %s", err) + } + defer patcher.Close() + + if _, err = io.Copy(patchres, patcher); err != nil { + t.Fatalf("Applying the patch failed: %s", err) + } + + if !bytes.Equal(patchres.Bytes(), testdata.Mutation()) { + if path, err := dump(patchres); err == nil { + t.Fatalf("patch result and mutation are not equal. Result dumped to %s", path) + } else { + t.Fatalf("patch result and mutation are not equal. Could not dump result: %s", err) + } + } } -- cgit v1.2.3-54-g00ecf