#include "config.h"
#include <assert.h>
#include <stdlib.h>
#include "librsync.h"
#include "job.h"
#include "sumset.h"
#include "checksum.h"
#include "stream.h"
#include "emit.h"
#include "trace.h"
static rs_result rs_delta_s_scan(rs_job_t *job);
static rs_result rs_delta_s_flush(rs_job_t *job);
static rs_result rs_delta_s_end(rs_job_t *job);
static inline void rs_getinput(rs_job_t *job);
static inline int rs_findmatch(rs_job_t *job, rs_long_t *match_pos,
size_t *match_len);
static inline rs_result rs_appendmatch(rs_job_t *job, rs_long_t match_pos,
size_t match_len);
static inline rs_result rs_appendmiss(rs_job_t *job, size_t miss_len);
static inline rs_result rs_appendflush(rs_job_t *job);
static inline rs_result rs_processmatch(rs_job_t *job);
static inline rs_result rs_processmiss(rs_job_t *job);
static rs_result rs_delta_s_scan(rs_job_t *job)
{
const size_t block_len = job->signature->block_len;
rs_long_t match_pos;
size_t match_len;
rs_result result;
rs_job_check(job);
rs_getinput(job);
result = rs_tube_catchup(job);
while ((result == RS_DONE)
&& ((job->scoop_pos + block_len) < job->scoop_avail)) {
if (rs_findmatch(job, &match_pos, &match_len)) {
result = rs_appendmatch(job, match_pos, match_len);
weaksum_reset(&job->weak_sum);
} else {
weaksum_rotate(&job->weak_sum, job->scoop_next[job->scoop_pos],
job->scoop_next[job->scoop_pos + block_len]);
result = rs_appendmiss(job, 1);
}
}
if (result == RS_DONE) {
if (job->stream->eof_in) {
job->statefn = rs_delta_s_flush;
return RS_RUNNING;
} else {
return RS_BLOCKED;
}
}
return result;
}
static rs_result rs_delta_s_flush(rs_job_t *job)
{
rs_long_t match_pos;
size_t match_len;
rs_result result;
rs_job_check(job);
rs_getinput(job);
result = rs_tube_catchup(job);
while ((result == RS_DONE) && (job->scoop_pos < job->scoop_avail)) {
if (rs_findmatch(job, &match_pos, &match_len)) {
result = rs_appendmatch(job, match_pos, match_len);
weaksum_reset(&job->weak_sum);
} else {
weaksum_rollout(&job->weak_sum, job->scoop_next[job->scoop_pos]);
rs_trace("block reduced to " FMT_SIZE "",
weaksum_count(&job->weak_sum));
result = rs_appendmiss(job, 1);
}
}
if (result == RS_DONE) {
result = rs_appendflush(job);
job->statefn = rs_delta_s_end;
}
if (result == RS_DONE) {
return RS_RUNNING;
}
return result;
}
static rs_result rs_delta_s_end(rs_job_t *job)
{
rs_emit_end_cmd(job);
return RS_DONE;
}
static inline void rs_getinput(rs_job_t *job)
{
size_t len;
len = rs_scoop_total_avail(job);
if (job->scoop_avail < len) {
rs_scoop_input(job, len);
}
}
static inline int rs_findmatch(rs_job_t *job, rs_long_t *match_pos,
size_t *match_len)
{
const size_t block_len = job->signature->block_len;
if (weaksum_count(&job->weak_sum) == 0) {
*match_len = job->scoop_avail - job->scoop_pos;
if (*match_len > block_len) {
*match_len = block_len;
}
weaksum_update(&job->weak_sum, job->scoop_next + job->scoop_pos,
*match_len);
rs_trace("calculate weak sum from scratch length " FMT_SIZE "",
weaksum_count(&job->weak_sum));
} else {
*match_len = weaksum_count(&job->weak_sum);
}
*match_pos =
rs_signature_find_match(job->signature, weaksum_digest(&job->weak_sum),
job->scoop_next + job->scoop_pos, *match_len);
return *match_pos != -1;
}
static inline rs_result rs_appendmatch(rs_job_t *job, rs_long_t match_pos,
size_t match_len)
{
rs_result result = RS_DONE;
if (job->basis_len && (job->basis_pos + job->basis_len) == match_pos) {
job->basis_len += match_len;
} else {
result = rs_appendflush(job);
job->basis_pos = match_pos;
job->basis_len = match_len;
}
job->scoop_pos += match_len;
if (result == RS_DONE) {
result = rs_processmatch(job);
}
return result;
}
static inline rs_result rs_appendmiss(rs_job_t *job, size_t miss_len)
{
const size_t max_miss = 32768;
rs_result result = RS_DONE;
if (job->basis_len || (job->scoop_pos >= max_miss)) {
result = rs_appendflush(job);
}
job->scoop_pos += miss_len;
return result;
}
static inline rs_result rs_appendflush(rs_job_t *job)
{
if (job->basis_len) {
rs_trace("matched " FMT_LONG " bytes at " FMT_LONG "!", job->basis_len,
job->basis_pos);
rs_emit_copy_cmd(job, job->basis_pos, job->basis_len);
job->basis_len = 0;
return rs_processmatch(job);
} else if (job->scoop_pos) {
rs_trace("got " FMT_SIZE " bytes of literal data", job->scoop_pos);
rs_emit_literal_cmd(job, job->scoop_pos);
return rs_processmiss(job);
}
return RS_DONE;
}
static inline rs_result rs_processmatch(rs_job_t *job)
{
job->scoop_avail -= job->scoop_pos;
job->scoop_next += job->scoop_pos;
job->scoop_pos = 0;
return rs_tube_catchup(job);
}
static inline rs_result rs_processmiss(rs_job_t *job)
{
rs_tube_copy(job, job->scoop_pos);
job->scoop_pos = 0;
return rs_tube_catchup(job);
}
static rs_result rs_delta_s_slack(rs_job_t *job)
{
rs_buffers_t *const stream = job->stream;
size_t avail = stream->avail_in;
if (avail) {
rs_trace("emit slack delta for " FMT_SIZE " available bytes", avail);
rs_emit_literal_cmd(job, avail);
rs_tube_copy(job, avail);
return RS_RUNNING;
} else if (rs_job_input_is_ending(job)) {
job->statefn = rs_delta_s_end;
return RS_RUNNING;
}
return RS_BLOCKED;
}
static rs_result rs_delta_s_header(rs_job_t *job)
{
rs_emit_delta_header(job);
if (job->signature) {
job->statefn = rs_delta_s_scan;
} else {
rs_trace("no signature provided for delta, using slack deltas");
job->statefn = rs_delta_s_slack;
}
return RS_RUNNING;
}
rs_job_t *rs_delta_begin(rs_signature_t *sig)
{
rs_job_t *job;
job = rs_job_new("delta", rs_delta_s_header);
if (sig && sig->count > 0) {
rs_signature_check(sig);
assert(sig->hashtable);
job->signature = sig;
weaksum_init(&job->weak_sum, rs_signature_weaksum_kind(sig));
}
return job;
}