static rs_result rs_patch_s_copy(rs_job_t *job) { rs_long_t where, len; rs_stats_t *stats; where = job->param1; len = job->param2; rs_trace("COPY(where=" PRINTF_FORMAT_U64 ", len=" PRINTF_FORMAT_U64 ")", PRINTF_CAST_U64(where), PRINTF_CAST_U64(len)); if (len < 0) { rs_log(RS_LOG_ERR, "invalid length=" PRINTF_FORMAT_U64 " on COPY command", PRINTF_CAST_U64(len)); return RS_CORRUPT; } if (where < 0) { rs_log(RS_LOG_ERR, "invalid where=" PRINTF_FORMAT_U64 " on COPY command", PRINTF_CAST_U64(where)); return RS_CORRUPT; } job->basis_pos = where; job->basis_len = len; stats = &job->stats; stats->copy_cmds++; stats->copy_bytes += len; stats->copy_cmdbytes += 1 + job->cmd->len_1 + job->cmd->len_2; job->statefn = rs_patch_s_copying; return RS_RUNNING; }
char * rs_format_stats(rs_stats_t const * stats, char *buf, size_t size) { char const *op = stats->op; int len, sec; double mbps_in, mbps_out; if (!op) op = "noop"; len = snprintf(buf, size, "%s statistics: ", op); if (stats->lit_cmds) { len += snprintf(buf+len, size-len, "literal[%d cmds, " PRINTF_FORMAT_U64 " bytes, " PRINTF_FORMAT_U64 " cmdbytes] ", stats->lit_cmds, PRINTF_CAST_U64(stats->lit_bytes), PRINTF_CAST_U64(stats->lit_cmdbytes)); } if (stats->sig_cmds) { len += snprintf(buf+len, size-len, "in-place-signature[" PRINTF_FORMAT_U64 " cmds, " PRINTF_FORMAT_U64 " bytes] ", PRINTF_CAST_U64(stats->sig_cmds), PRINTF_CAST_U64(stats->sig_bytes)); } if (stats->copy_cmds || stats->false_matches) { len += snprintf(buf+len, size-len, "copy[" PRINTF_FORMAT_U64 " cmds, " PRINTF_FORMAT_U64 " bytes, " PRINTF_FORMAT_U64 " false, " PRINTF_FORMAT_U64 " cmdbytes]", PRINTF_CAST_U64(stats->copy_cmds), PRINTF_CAST_U64(stats->copy_bytes), PRINTF_CAST_U64(stats->false_matches), PRINTF_CAST_U64(stats->copy_cmdbytes)); } if (stats->sig_blocks) { len += snprintf(buf+len, size-len, "signature[" PRINTF_FORMAT_U64 " blocks, " PRINTF_FORMAT_U64 " bytes per block]", PRINTF_CAST_U64(stats->sig_blocks), PRINTF_CAST_U64(stats->block_len)); } sec = (stats->end - stats->start); if (sec == 0) sec = 1; // avoid division by zero mbps_in = stats->in_bytes / 1e6 / sec; mbps_out = stats->out_bytes / 1e6 / sec; len += snprintf(buf+len, size-len, " speed[%.1f MB (%.1f MB/s) in, %.1f MB (%.1f MB/s) out, %d sec]", (stats->in_bytes/1e6), mbps_in, (stats->out_bytes/1e6), mbps_out, sec); return buf; }
/** * Called when we're executing a COPY command and waiting for all the * data to be retrieved from the callback. */ static rs_result rs_patch_s_copying(rs_job_t *job) { rs_result result; size_t desired_len, len; void *ptr; rs_buffers_t *buffs = job->stream; /* copy only as much as will fit in the output buffer, so that we * don't have to block or store the input. */ desired_len = len = (buffs->avail_out < job->basis_len) ? buffs->avail_out : job->basis_len; if (!len) return RS_BLOCKED; rs_trace("copy " PRINTF_FORMAT_U64 " bytes from basis at offset " PRINTF_FORMAT_U64 "", PRINTF_CAST_U64(len), PRINTF_CAST_U64(job->basis_pos)); ptr = buffs->next_out; result = (job->copy_cb)(job->copy_arg, job->basis_pos, &len, &ptr); if (result != RS_DONE) return result; else rs_trace("copy callback returned %s", rs_strerror(result)); rs_trace("got " PRINTF_FORMAT_U64 " bytes back from basis callback", PRINTF_CAST_U64(len)); if (len > desired_len) { rs_trace("warning: copy_cb returned more than the requested length."); len = desired_len; } /* copy back to out buffer only if the callback has used its own buffer */ if (ptr != buffs->next_out) memcpy(buffs->next_out, ptr, len); buffs->next_out += len; buffs->avail_out -= len; job->basis_pos += len; job->basis_len -= len; if (!job->basis_len) { /* Done! */ job->statefn = rs_patch_s_cmdbyte; } return RS_RUNNING; }
/** Write a COPY command for given offset and length. * * There is a choice of variable-length encodings, depending on the * size of representation for the parameters. */ void rs_emit_copy_cmd(rs_job_t *job, rs_long_t where, rs_long_t len) { int cmd; rs_stats_t *stats = &job->stats; const int where_bytes = rs_int_len(where); const int len_bytes = rs_int_len(len); /* Commands ascend (1,1), (1,2), ... (8, 8) */ if (where_bytes == 8) cmd = RS_OP_COPY_N8_N1; else if (where_bytes == 4) cmd = RS_OP_COPY_N4_N1; else if (where_bytes == 2) cmd = RS_OP_COPY_N2_N1; else if (where_bytes == 1) cmd = RS_OP_COPY_N1_N1; else { rs_fatal("can't encode copy command with where_bytes=%d", where_bytes); } if (len_bytes == 1) ; else if (len_bytes == 2) cmd += 1; else if (len_bytes == 4) cmd += 2; else if (len_bytes == 8) cmd += 3; else { rs_fatal("can't encode copy command with len_bytes=%d", len_bytes); } rs_trace("emit COPY_N%d_N%d(where=" PRINTF_FORMAT_U64 ", len=" PRINTF_FORMAT_U64 "), cmd_byte=%#x", where_bytes, len_bytes, PRINTF_CAST_U64(where), PRINTF_CAST_U64(len), cmd); rs_squirt_byte(job, cmd); rs_squirt_netint(job, where, where_bytes); rs_squirt_netint(job, len, len_bytes); stats->copy_cmds++; stats->copy_bytes += len; stats->copy_cmdbytes += 1 + where_bytes + len_bytes; /* TODO: All the stats */ }
/** * Called when we're executing a COPY command and waiting for all the * data to be retrieved from the callback. */ static rs_result rs_patch_s_copying(rs_job_t *job) { rs_result result; size_t len; void *buf, *ptr; rs_buffers_t *buffs = job->stream; len = job->basis_len; /* copy only as much as will fit in the output buffer, so that we * don't have to block or store the input. */ if (len > buffs->avail_out) len = buffs->avail_out; if (!len) return RS_BLOCKED; rs_trace("copy " PRINTF_FORMAT_U64 " bytes from basis at offset " PRINTF_FORMAT_U64 "", PRINTF_CAST_U64(len), PRINTF_CAST_U64(job->basis_pos)); ptr = buf = rs_alloc(len, "basis buffer"); result = (job->copy_cb)(job->copy_arg, job->basis_pos, &len, &ptr); if (result != RS_DONE) return result; else rs_trace("copy callback returned %s", rs_strerror(result)); rs_trace("got " PRINTF_FORMAT_U64 " bytes back from basis callback", PRINTF_CAST_U64(len)); memcpy(buffs->next_out, ptr, len); buffs->next_out += len; buffs->avail_out -= len; job->basis_pos += len; job->basis_len -= len; free(buf); if (!job->basis_len) { /* Done! */ job->statefn = rs_patch_s_cmdbyte; } return RS_RUNNING; }
char * rs_format_stats(rs_stats_t const * stats, char *buf, size_t size) { char const *op = stats->op; int len; if (!op) op = "noop"; len = snprintf(buf, size, "%s statistics: ", op); if (stats->lit_cmds) { len += snprintf(buf+len, size-len, "literal[%d cmds, " PRINTF_FORMAT_U64 " bytes, " PRINTF_FORMAT_U64 " cmdbytes] ", stats->lit_cmds, PRINTF_CAST_U64(stats->lit_bytes), PRINTF_CAST_U64(stats->lit_cmdbytes)); } if (stats->sig_cmds) { len += snprintf(buf+len, size-len, "in-place-signature[" PRINTF_FORMAT_U64 " cmds, " PRINTF_FORMAT_U64 " bytes] ", PRINTF_CAST_U64(stats->sig_cmds), PRINTF_CAST_U64(stats->sig_bytes)); } if (stats->copy_cmds || stats->false_matches) { len += snprintf(buf+len, size-len, "copy[" PRINTF_FORMAT_U64 " cmds, " PRINTF_FORMAT_U64 " bytes, " PRINTF_FORMAT_U64 " false, " PRINTF_FORMAT_U64 " cmdbytes]", PRINTF_CAST_U64(stats->copy_cmds), PRINTF_CAST_U64(stats->copy_bytes), PRINTF_CAST_U64(stats->false_matches), PRINTF_CAST_U64(stats->copy_cmdbytes)); } if (stats->sig_blocks) { len += snprintf(buf+len, size-len, "signature[" PRINTF_FORMAT_U64 " blocks, " PRINTF_FORMAT_U64 " bytes per block]", PRINTF_CAST_U64(stats->sig_blocks), PRINTF_CAST_U64(stats->block_len)); } return buf; }
/** * Flush any accumulating hit or miss, appending it to the delta. */ INLINE rs_result rs_appendflush(rs_job_t *job) { /* if last is a match, emit it and reset last by resetting basis_len */ if (job->basis_len) { rs_trace("matched " PRINTF_FORMAT_U64 " bytes at " PRINTF_FORMAT_U64 "!", PRINTF_CAST_U64(job->basis_len), PRINTF_CAST_U64(job->basis_pos)); rs_emit_copy_cmd(job, job->basis_pos, job->basis_len); job->basis_len=0; return rs_processmatch(job); /* else if last is a miss, emit and process it*/ } else if (job->scoop_pos) { rs_trace("got %ld bytes of literal data", (long) job->scoop_pos); rs_emit_literal_cmd(job, job->scoop_pos); return rs_processmiss(job); } /* otherwise, nothing to flush so we are done */ return RS_DONE; }
rs_result rs_job_iter(rs_job_t *job, rs_buffers_t *buffers) { rs_result result; rs_long_t orig_in, orig_out; orig_in = buffers->avail_in; orig_out = buffers->avail_out; result = rs_job_work(job, buffers); if (result == RS_BLOCKED || result == RS_DONE) if ((orig_in == buffers->avail_in) && (orig_out == buffers->avail_out) && orig_in && orig_out) { rs_log(RS_LOG_ERR, "internal error: job made no progress " "[orig_in=" PRINTF_FORMAT_U64 ", orig_out=" PRINTF_FORMAT_U64 ", final_in=" PRINTF_FORMAT_U64 ", final_out=" PRINTF_FORMAT_U64 "]", PRINTF_CAST_U64(orig_in), PRINTF_CAST_U64(orig_out), PRINTF_CAST_U64(buffers->avail_in), PRINTF_CAST_U64(buffers->avail_out)); return RS_INTERNAL_ERROR; } return result; }
/** * Called when trying to copy through literal data. */ static rs_result rs_patch_s_literal(rs_job_t *job) { rs_long_t len = job->param1; rs_trace("LITERAL(len=" PRINTF_FORMAT_U64 ")", PRINTF_CAST_U64(len)); if (len < 0) { rs_log(RS_LOG_ERR, "invalid length=" PRINTF_FORMAT_U64 " on LITERAL command", PRINTF_CAST_U64(len)); return RS_CORRUPT; } job->stats.lit_cmds++; job->stats.lit_bytes += len; job->stats.lit_cmdbytes += 1 + job->cmd->len_1; rs_tube_copy(job, len); job->statefn = rs_patch_s_cmdbyte; return RS_RUNNING; }
/** * \brief State function that does a slack delta containing only * literal data to recreate the input. */ 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 " PRINTF_FORMAT_U64 " available bytes", PRINTF_CAST_U64(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; } else { return RS_BLOCKED; } } }
/** * State of trying to read the first byte of a command. Once we've * taken that in, we can know how much data to read to get the * arguments. */ static rs_result rs_patch_s_cmdbyte(rs_job_t *job) { rs_result result; if ((result = rs_suck_byte(job, &job->op)) != RS_DONE) return result; job->cmd = &rs_prototab[job->op]; rs_trace("got command byte 0x%02x (%s), len_1=" PRINTF_FORMAT_U64 "", job->op, rs_op_kind_name(job->cmd->kind), PRINTF_CAST_U64(job->cmd->len_1)); if (job->cmd->len_1) job->statefn = rs_patch_s_params; else { job->param1 = job->cmd->immediate; job->statefn = rs_patch_s_run; } return RS_RUNNING; }