String-based API for consensus diffs.

Also, add very strict split/join functions, and totally forbid
nonempty files that end with somethig besides a newline.  This
change is necessary to ensure that diff/apply are actually reliable
inverse operations.
This commit is contained in:
Nick Mathewson 2017-03-07 13:45:32 -05:00
parent eff9fbd17d
commit 6a36e5ff3b
2 changed files with 130 additions and 14 deletions

View File

@ -39,9 +39,12 @@
static const char* ns_diff_version = "network-status-diff-version 1";
static const char* hash_token = "hash";
STATIC int
consensus_compute_digest(const char *cons,
consensus_digest_t *digest_out)
static char *consensus_join_lines(const smartlist_t *inp);
/** DOCDOC */
MOCK_IMPL(STATIC int,
consensus_compute_digest,(const char *cons,
consensus_digest_t *digest_out))
{
int r = crypto_digest256((char*)digest_out->sha3_256,
cons, strlen(cons), DIGEST_SHA3_256);
@ -987,7 +990,7 @@ consdiff_apply_diff(const smartlist_t *cons1,
goto error_cleanup;
}
cons2_str = smartlist_join_strings(cons2, "\n", 1, NULL);
cons2_str = consensus_join_lines(cons2);
consensus_digest_t cons2_digests;
if (consensus_compute_digest(cons2_str, &cons2_digests) < 0) {
@ -1029,3 +1032,110 @@ consdiff_apply_diff(const smartlist_t *cons1,
return cons2_str;
}
/**DOCDOC*/
static int
consensus_split_lines(smartlist_t *out, const char *s)
{
/* XXXX If we used string slices, we could avoid a bunch of copies here. */
while (*s) {
const char *eol = strchr(s, '\n');
if (!eol) {
/* File doesn't end with newline. */
return -1;
}
smartlist_add(out, tor_strndup(s, eol-s));
s = eol+1;
}
return 0;
}
/** DOCDOC */
static char *
consensus_join_lines(const smartlist_t *inp)
{
size_t n = 0;
SMARTLIST_FOREACH(inp, const char *, cp, n += strlen(cp) + 1);
n += 1;
char *result = tor_malloc(n);
char *out = result;
SMARTLIST_FOREACH_BEGIN(inp, const char *, cp) {
size_t len = strlen(cp);
memcpy(out, cp, len);
out += len;
*out++ = '\n';
} SMARTLIST_FOREACH_END(cp);
*out++ = '\0';
tor_assert(out == result+n);
return result;
}
/**DOCDOC */
char *
consensus_diff_generate(const char *cons1,
const char *cons2)
{
consensus_digest_t d1, d2;
smartlist_t *lines1 = NULL, *lines2 = NULL, *result_lines = NULL;
int r1, r2;
char *result = NULL;
r1 = consensus_compute_digest(cons1, &d1);
r2 = consensus_compute_digest(cons2, &d2);
if (BUG(r1 < 0 || r2 < 0))
return NULL; // LCOV_EXCL_LINE
lines1 = smartlist_new();
lines2 = smartlist_new();
if (consensus_split_lines(lines1, cons1) < 0)
goto done;
if (consensus_split_lines(lines2, cons2) < 0)
goto done;
result_lines = consdiff_gen_diff(lines1, lines2, &d1, &d2);
done:
SMARTLIST_FOREACH(lines1, char *, cp, tor_free(cp));
smartlist_free(lines1);
SMARTLIST_FOREACH(lines2, char *, cp, tor_free(cp));
smartlist_free(lines2);
if (result_lines) {
result = consensus_join_lines(result_lines);
SMARTLIST_FOREACH(result_lines, char *, cp, tor_free(cp));
smartlist_free(result_lines);
}
return result;
}
/** DOCDOC */
char *
consensus_diff_apply(const char *consensus,
const char *diff)
{
consensus_digest_t d1;
smartlist_t *lines1 = NULL, *lines2 = NULL;
int r1;
char *result = NULL;
r1 = consensus_compute_digest(consensus, &d1);
if (BUG(r1 < 0))
return NULL; // LCOV_EXCL_LINE
lines1 = smartlist_new();
lines2 = smartlist_new();
if (consensus_split_lines(lines1, consensus) < 0)
goto done;
if (consensus_split_lines(lines2, diff) < 0)
goto done;
result = consdiff_apply_diff(lines1, lines2, &d1);
done:
SMARTLIST_FOREACH(lines1, char *, cp, tor_free(cp));
smartlist_free(lines1);
SMARTLIST_FOREACH(lines2, char *, cp, tor_free(cp));
smartlist_free(lines2);
return result;
}

View File

@ -7,21 +7,27 @@
#include "or.h"
char *consensus_diff_generate(const char *cons1,
const char *cons2);
char *consensus_diff_apply(const char *consensus,
const char *diff);
#ifdef CONSDIFF_PRIVATE
typedef struct consensus_digest_t {
uint8_t sha3_256[DIGEST256_LEN];
} consensus_digest_t;
smartlist_t *consdiff_gen_diff(const smartlist_t *cons1,
const smartlist_t *cons2,
const consensus_digest_t *digests1,
const consensus_digest_t *digests2);
char *consdiff_apply_diff(const smartlist_t *cons1, const smartlist_t *diff,
const consensus_digest_t *digests1);
int consdiff_get_digests(const smartlist_t *diff,
char *digest1_out,
char *digest2_out);
STATIC smartlist_t *consdiff_gen_diff(const smartlist_t *cons1,
const smartlist_t *cons2,
const consensus_digest_t *digests1,
const consensus_digest_t *digests2);
STATIC char *consdiff_apply_diff(const smartlist_t *cons1,
const smartlist_t *diff,
const consensus_digest_t *digests1);
STATIC int consdiff_get_digests(const smartlist_t *diff,
char *digest1_out,
char *digest2_out);
#ifdef CONSDIFF_PRIVATE
/** Data structure to define a slice of a smarltist. */
typedef struct smartlist_slice_t {
/**