svn_error_t *svn_txdelta_send_stream(svn_stream_t *stream, svn_txdelta_window_handler_t handler, void *handler_baton, unsigned char *digest, apr_pool_t *pool) { svn_txdelta_stream_t *txstream; svn_error_t *err; /* ### this is a hack. we should simply read from the stream, construct ### some windows, and pass those to the handler. there isn't any reason ### to crank up a full "diff" algorithm just to copy a stream. ### ### will fix RSN. */ /* Create a delta stream which converts an *empty* bytestream into the target bytestream. */ svn_txdelta(&txstream, svn_stream_empty(pool), stream, pool); err = svn_txdelta_send_txstream(txstream, handler, handler_baton, pool); if (digest && (! err)) { const unsigned char *result_md5; result_md5 = svn_txdelta_md5_digest(txstream); /* Since err is null, result_md5 "cannot" be null. */ memcpy(digest, result_md5, APR_MD5_DIGESTSIZE); } return err; }
static void do_one_diff(apr_file_t *source_file, apr_file_t *target_file, int *count, apr_off_t *len, int quiet, apr_pool_t *pool, const char *tag, FILE* stream) { svn_txdelta_stream_t *delta_stream = NULL; svn_txdelta_window_t *delta_window = NULL; apr_pool_t *fpool = svn_pool_create(pool); apr_pool_t *wpool = svn_pool_create(pool); *count = 0; *len = 0; svn_txdelta(&delta_stream, svn_stream_from_aprfile(source_file, fpool), svn_stream_from_aprfile(target_file, fpool), fpool); do { svn_error_t *err; err = svn_txdelta_next_window(&delta_window, delta_stream, wpool); if (err) svn_handle_error2(err, stderr, TRUE, "vdelta-test: "); if (delta_window != NULL) { *len += print_delta_window(delta_window, tag, quiet, stream); svn_pool_clear(wpool); ++*count; } } while (delta_window != NULL); fprintf(stream, "%s: (LENGTH %" APR_OFF_T_FMT " +%d)\n", tag, *len, *count); svn_pool_destroy(fpool); svn_pool_destroy(wpool); }
/* Deltifies a node, i.e. generates a svndiff that can be dumped */ static svn_error_t *delta_deltify_node(de_node_baton_t *node) { svn_txdelta_stream_t *stream; svn_txdelta_window_handler_t handler; void *handler_baton; svn_stream_t *source, *target, *dest; apr_file_t *source_file = NULL, *target_file = NULL, *dest_file = NULL; dump_options_t *opts = node->de_baton->opts; apr_pool_t *pool = svn_pool_create(node->pool); svn_error_t *err; DEBUG_MSG("delta_deltify_node(%s): %s -> %s\n", node->path, node->old_filename, node->filename); /* Open source and target */ apr_file_open(&target_file, node->filename, APR_READ, 0600, pool); target = svn_stream_from_aprfile2(target_file, FALSE, pool); if (node->old_filename) { apr_file_open(&source_file, node->old_filename, APR_READ, 0600, pool); source = svn_stream_from_aprfile2(source_file, FALSE, pool); } else { source = svn_stream_empty(pool); } /* Open temporary output file */ node->delta_filename = apr_psprintf(node->pool, "%s/XXXXXX", opts->temp_dir); apr_file_mktemp(&dest_file, node->delta_filename, APR_CREATE | APR_READ | APR_WRITE | APR_EXCL, pool); dest = svn_stream_from_aprfile2(dest_file, FALSE, pool); DEBUG_MSG("delta_deltify_node(%s): writing to %s\n", node->path, node->delta_filename); /* Produce delta in svndiff format */ svn_txdelta(&stream, source, target, pool); svn_txdelta_to_svndiff2(&handler, &handler_baton, dest, 0, pool); err = svn_txdelta_send_txstream(stream, handler, handler_baton, pool); svn_pool_destroy(pool); return err; }
int main(int argc, char **argv) { apr_file_t *source_file_A = NULL; apr_file_t *target_file_A = NULL; int count_A = 0; apr_off_t len_A = 0; apr_file_t *source_file_B = NULL; apr_file_t *target_file_B = NULL; int count_B = 0; apr_off_t len_B = 0; apr_pool_t *pool; int quiet = 0; if (argc > 1 && argv[1][0] == '-' && argv[1][1] == 'q') { quiet = 1; --argc; ++argv; } apr_initialize(); pool = svn_pool_create(NULL); if (argc == 2) { target_file_A = open_binary_read(argv[1], pool); } else if (argc == 3) { source_file_A = open_binary_read(argv[1], pool); target_file_A = open_binary_read(argv[2], pool); } else if (argc == 4) { source_file_A = open_binary_read(argv[1], pool); target_file_A = open_binary_read(argv[2], pool); source_file_B = open_binary_read(argv[2], pool); target_file_B = open_binary_read(argv[3], pool); } else { fprintf(stderr, "Usage: vdelta-test [-q] <target>\n" " or: vdelta-test [-q] <source> <target>\n" " or: vdelta-test [-q] <source> <intermediate> <target>\n"); exit(1); } do_one_diff(source_file_A, target_file_A, &count_A, &len_A, quiet, pool, "A ", stdout); if (source_file_B) { apr_pool_t *fpool = svn_pool_create(pool); apr_pool_t *wpool = svn_pool_create(pool); svn_txdelta_stream_t *stream_A = NULL; svn_txdelta_stream_t *stream_B = NULL; svn_txdelta_window_t *window_A = NULL; svn_txdelta_window_t *window_B = NULL; svn_txdelta_window_t *window_AB = NULL; int count_AB = 0; apr_off_t len_AB = 0; putc('\n', stdout); do_one_diff(source_file_B, target_file_B, &count_B, &len_B, quiet, pool, "B ", stdout); putc('\n', stdout); { apr_off_t offset = 0; apr_file_seek(source_file_A, APR_SET, &offset); apr_file_seek(target_file_A, APR_SET, &offset); apr_file_seek(source_file_B, APR_SET, &offset); apr_file_seek(target_file_B, APR_SET, &offset); } svn_txdelta(&stream_A, svn_stream_from_aprfile(source_file_A, fpool), svn_stream_from_aprfile(target_file_A, fpool), fpool); svn_txdelta(&stream_B, svn_stream_from_aprfile(source_file_B, fpool), svn_stream_from_aprfile(target_file_B, fpool), fpool); for (count_AB = 0; count_AB < count_B; ++count_AB) { svn_error_t *err; err = svn_txdelta_next_window(&window_A, stream_A, wpool); if (err) svn_handle_error2(err, stderr, TRUE, "vdelta-test: "); err = svn_txdelta_next_window(&window_B, stream_B, wpool); if (err) svn_handle_error2(err, stderr, TRUE, "vdelta-test: "); /* Note: It's not possible that window_B is null, we already counted the number of windows in the second delta. */ assert(window_A != NULL || window_B->src_ops == 0); if (window_B->src_ops == 0) { window_AB = window_B; window_AB->sview_len = 0; } else window_AB = svn_txdelta_compose_windows(window_A, window_B, wpool); len_AB += print_delta_window(window_AB, "AB", quiet, stdout); svn_pool_clear(wpool); } fprintf(stdout, "AB: (LENGTH %" APR_OFF_T_FMT " +%d)\n", len_AB, count_AB); } if (source_file_A) apr_file_close(source_file_A); if (target_file_A) apr_file_close(target_file_A); if (source_file_B) apr_file_close(source_file_B); if (target_file_B) apr_file_close(source_file_B); svn_pool_destroy(pool); apr_terminate(); exit(0); }
inline Try<Diff> diff(const std::string& from, const std::string& to) { // Initialize the Apache Portable Runtime subsystem, as necessary // for using the svn library. initialize(); // Note that svn_pool_create wraps apr_pool_create_ex, which is // thread safe, see: http://goo.gl/NX0hps. apr_pool_t* pool = svn_pool_create(NULL); // First we need to produce a text delta stream by diffing 'source' // against 'target'. svn_string_t source; source.data = from.data(); source.len = from.length(); svn_string_t target; target.data = to.data(); target.len = to.length(); svn_txdelta_stream_t* delta; #if SVN_VER_MAJOR >= 1 && SVN_VER_MINOR >= 8 svn_txdelta2( &delta, svn_stream_from_string(&source, pool), svn_stream_from_string(&target, pool), false, pool); #else svn_txdelta( &delta, svn_stream_from_string(&source, pool), svn_stream_from_string(&target, pool), pool); #endif // Now we want to convert this text delta stream into an svndiff // format based diff. Setup the handler that will consume the text // delta and produce the svndiff. svn_txdelta_window_handler_t handler; void* baton = NULL; svn_stringbuf_t* diff = svn_stringbuf_create_ensure(1024, pool); #if SVN_VER_MAJOR >= 1 && SVN_VER_MINOR >= 7 svn_txdelta_to_svndiff3( &handler, &baton, svn_stream_from_stringbuf(diff, pool), 0, SVN_DELTA_COMPRESSION_LEVEL_DEFAULT, pool); #elif SVN_VER_MAJOR >= 1 && SVN_VER_MINOR >= 4 svn_txdelta_to_svndiff2( &handler, &baton, svn_stream_from_stringbuf(diff, pool), 0, pool); #else svn_txdelta_to_svndiff( svn_stream_from_stringbuf(diff, pool), pool, &handler, &baton); #endif // Now feed the text delta to the handler. svn_error_t* error = svn_txdelta_send_txstream(delta, handler, baton, pool); if (error != NULL) { char buffer[1024]; std::string message(svn_err_best_message(error, buffer, 1024)); svn_pool_destroy(pool); return Error(message); } Diff d(std::string(diff->data, diff->len)); svn_pool_destroy(pool); return d; }
int main(int argc, char **argv) { svn_error_t *err; apr_status_t apr_err; apr_file_t *source_file; apr_file_t *target_file; svn_stream_t *stdout_stream; svn_txdelta_stream_t *txdelta_stream; svn_txdelta_window_handler_t svndiff_handler; svn_stream_t *encoder; void *svndiff_baton; apr_pool_t *pool; int version = 0; if (argc < 3) { printf("usage: %s source target [version]\n", argv[0]); exit(0); } apr_initialize(); pool = svn_pool_create(NULL); apr_err = apr_file_open(&source_file, argv[1], (APR_READ | APR_BINARY), APR_OS_DEFAULT, pool); if (apr_err) { fprintf(stderr, "unable to open \"%s\" for reading\n", argv[1]); exit(1); } apr_err = apr_file_open(&target_file, argv[2], (APR_READ | APR_BINARY), APR_OS_DEFAULT, pool); if (apr_err) { fprintf(stderr, "unable to open \"%s\" for reading\n", argv[2]); exit(1); } if (argc == 4) version = atoi(argv[3]); svn_txdelta(&txdelta_stream, svn_stream_from_aprfile(source_file, pool), svn_stream_from_aprfile(target_file, pool), pool); err = svn_stream_for_stdout(&stdout_stream, pool); if (err) svn_handle_error2(err, stdout, TRUE, "svndiff-test: "); #ifdef QUOPRINT_SVNDIFFS encoder = svn_quoprint_encode(stdout_stream, pool); #else encoder = svn_base64_encode(stdout_stream, pool); #endif /* use maximum compression level */ svn_txdelta_to_svndiff3(&svndiff_handler, &svndiff_baton, encoder, version, 9, pool); err = svn_txdelta_send_txstream(txdelta_stream, svndiff_handler, svndiff_baton, pool); if (err) svn_handle_error2(err, stdout, TRUE, "svndiff-test: "); apr_file_close(source_file); apr_file_close(target_file); svn_pool_destroy(pool); apr_terminate(); exit(0); }
/* (Note: *LAST_SEED is an output parameter.) */ static svn_error_t * do_random_combine_test(const char **msg, svn_boolean_t msg_only, apr_pool_t *pool, apr_uint32_t *last_seed) { static char msg_buff[256]; apr_uint32_t seed, bytes_range, maxlen; int i, iterations, dump_files, print_windows; const char *random_bytes; /* Initialize parameters and print out the seed in case we dump core or something. */ init_params(&seed, &maxlen, &iterations, &dump_files, &print_windows, &random_bytes, &bytes_range, pool); sprintf(msg_buff, "random combine delta test, seed = %lu", (unsigned long) seed); *msg = msg_buff; if (msg_only) return SVN_NO_ERROR; else printf("SEED: %s\n", msg_buff); for (i = 0; i < iterations; i++) { /* Generate source and target for the delta and its application. */ apr_uint32_t subseed_base = svn_test_rand((*last_seed = seed, &seed)); apr_file_t *source = generate_random_file(maxlen, subseed_base, &seed, random_bytes, bytes_range, dump_files, pool); apr_file_t *middle = generate_random_file(maxlen, subseed_base, &seed, random_bytes, bytes_range, dump_files, pool); apr_file_t *target = generate_random_file(maxlen, subseed_base, &seed, random_bytes, bytes_range, dump_files, pool); apr_file_t *source_copy = copy_tempfile(source, pool); apr_file_t *middle_copy = copy_tempfile(middle, pool); apr_file_t *target_regen = open_tempfile(NULL, pool); svn_txdelta_stream_t *txdelta_stream_A; svn_txdelta_stream_t *txdelta_stream_B; svn_txdelta_window_handler_t handler; svn_stream_t *stream; void *handler_baton; /* Set up a four-stage pipeline: create two deltas, combine them and convert the result to svndiff format, parse that back into delta format, and apply it to a copy of the source file to see if we get the same target back. */ apr_pool_t *delta_pool = svn_pool_create(pool); /* Make stage 4: apply the text delta. */ svn_txdelta_apply(svn_stream_from_aprfile(source_copy, delta_pool), svn_stream_from_aprfile(target_regen, delta_pool), NULL, NULL, delta_pool, &handler, &handler_baton); /* Make stage 3: reparse the text delta. */ stream = svn_txdelta_parse_svndiff(handler, handler_baton, TRUE, delta_pool); /* Make stage 2: encode the text delta in svndiff format. */ svn_txdelta_to_svndiff2(&handler, &handler_baton, stream, 1, delta_pool); /* Make stage 1: create the text deltas. */ svn_txdelta(&txdelta_stream_A, svn_stream_from_aprfile(source, delta_pool), svn_stream_from_aprfile(middle, delta_pool), delta_pool); svn_txdelta(&txdelta_stream_B, svn_stream_from_aprfile(middle_copy, delta_pool), svn_stream_from_aprfile(target, delta_pool), delta_pool); { svn_txdelta_window_t *window_A; svn_txdelta_window_t *window_B; svn_txdelta_window_t *composite; apr_pool_t *wpool = svn_pool_create(delta_pool); do { SVN_ERR(svn_txdelta_next_window(&window_A, txdelta_stream_A, wpool)); if (print_windows) delta_window_print(window_A, "A ", stdout); SVN_ERR(svn_txdelta_next_window(&window_B, txdelta_stream_B, wpool)); if (print_windows) delta_window_print(window_B, "B ", stdout); if (!window_B) break; assert(window_A != NULL || window_B->src_ops == 0); if (window_B->src_ops == 0) { composite = window_B; composite->sview_len = 0; } else composite = svn_txdelta_compose_windows(window_A, window_B, wpool); if (print_windows) delta_window_print(composite, "AB", stdout); /* The source view length should not be 0 if there are source copy ops in the window. */ if (composite && composite->sview_len == 0 && composite->src_ops > 0) return svn_error_create (SVN_ERR_FS_GENERAL, NULL, "combined delta window is inconsistent"); SVN_ERR(handler(composite, handler_baton)); svn_pool_clear(wpool); } while (composite != NULL); svn_pool_destroy(wpool); } svn_pool_destroy(delta_pool); SVN_ERR(compare_files(target, target_regen, dump_files)); apr_file_close(source); apr_file_close(middle); apr_file_close(target); apr_file_close(source_copy); apr_file_close(middle_copy); apr_file_close(target_regen); } return SVN_NO_ERROR; }
/* Implements svn_test_driver_t. */ static svn_error_t * random_test(const char **msg, svn_boolean_t msg_only, svn_test_opts_t *opts, apr_pool_t *pool) { static char msg_buff[256]; apr_uint32_t seed, bytes_range, maxlen; int i, iterations, dump_files, print_windows; const char *random_bytes; /* Initialize parameters and print out the seed in case we dump core or something. */ init_params(&seed, &maxlen, &iterations, &dump_files, &print_windows, &random_bytes, &bytes_range, pool); sprintf(msg_buff, "random delta test, seed = %lu", (unsigned long) seed); *msg = msg_buff; if (msg_only) return SVN_NO_ERROR; else printf("SEED: %s\n", msg_buff); for (i = 0; i < iterations; i++) { /* Generate source and target for the delta and its application. */ apr_uint32_t subseed_base = svn_test_rand(&seed); apr_file_t *source = generate_random_file(maxlen, subseed_base, &seed, random_bytes, bytes_range, dump_files, pool); apr_file_t *target = generate_random_file(maxlen, subseed_base, &seed, random_bytes, bytes_range, dump_files, pool); apr_file_t *source_copy = copy_tempfile(source, pool); apr_file_t *target_regen = open_tempfile(NULL, pool); svn_txdelta_stream_t *txdelta_stream; svn_txdelta_window_handler_t handler; svn_stream_t *stream; void *handler_baton; /* Set up a four-stage pipeline: create a delta, convert it to svndiff format, parse it back into delta format, and apply it to a copy of the source file to see if we get the same target back. */ apr_pool_t *delta_pool = svn_pool_create(pool); /* Make stage 4: apply the text delta. */ svn_txdelta_apply(svn_stream_from_aprfile(source_copy, delta_pool), svn_stream_from_aprfile(target_regen, delta_pool), NULL, NULL, delta_pool, &handler, &handler_baton); /* Make stage 3: reparse the text delta. */ stream = svn_txdelta_parse_svndiff(handler, handler_baton, TRUE, delta_pool); /* Make stage 2: encode the text delta in svndiff format. */ svn_txdelta_to_svndiff2(&handler, &handler_baton, stream, 1, delta_pool); /* Make stage 1: create the text delta. */ svn_txdelta(&txdelta_stream, svn_stream_from_aprfile(source, delta_pool), svn_stream_from_aprfile(target, delta_pool), delta_pool); SVN_ERR(svn_txdelta_send_txstream(txdelta_stream, handler, handler_baton, delta_pool)); svn_pool_destroy(delta_pool); SVN_ERR(compare_files(target, target_regen, dump_files)); apr_file_close(source); apr_file_close(target); apr_file_close(source_copy); apr_file_close(target_regen); } return SVN_NO_ERROR; }
static svn_error_t * stream_window_test(const char **msg, svn_boolean_t msg_only, svn_test_opts_t *opts, apr_pool_t *pool) { /* Note: put these in data segment, not the stack */ static char source[109001]; static char target[109001]; int i; char *p = &source[9]; svn_checksum_t *expected; svn_checksum_t *actual; svn_string_t source_str; svn_string_t target_str; svn_stream_t *source_stream; svn_stream_t *target_stream; svn_txdelta_stream_t *txstream; *msg = "txdelta stream and windows test"; if (msg_only) return SVN_NO_ERROR; memcpy(source, "a\nb\nc\nd\ne", 9); for (i = 100; i--; ) *p++ = '\n'; for (i = 999; i--; p += 109) memcpy(p, source, 109); source[109000] = '\0'; memcpy(target, source, 109001); for (i = 1000; i--; ) target[i*109 + 4] = 'X'; SVN_ERR(svn_checksum(&expected, svn_checksum_md5, target, 109000, pool)); /* f6fd44565e14c6e44b35292719deb77e */ printf("expected: %s\n", svn_checksum_to_cstring(expected, pool)); source_str.data = source; source_str.len = 109000; source_stream = svn_stream_from_string(&source_str, pool); target_str.data = target; target_str.len = 109000; target_stream = svn_stream_from_string(&target_str, pool); svn_txdelta(&txstream, source_stream, target_stream, pool); while (1) { svn_txdelta_window_t *window; SVN_ERR(svn_txdelta_next_window(&window, txstream, pool)); if (window == NULL) break; /* ### examine the window */ } actual = svn_checksum__from_digest(svn_txdelta_md5_digest(txstream), svn_checksum_md5, pool);; printf(" actual: %s\n", svn_checksum_to_cstring(actual, pool)); if (!svn_checksum_match(expected, actual)) { return svn_error_create(SVN_ERR_TEST_FAILED, NULL, "Checksums did not match."); } return SVN_NO_ERROR; }
svn_error_t * svn_ra__file_revs_from_log(svn_ra_session_t *ra_session, const char *path, svn_revnum_t start, svn_revnum_t end, svn_file_rev_handler_t handler, void *handler_baton, apr_pool_t *pool) { svn_node_kind_t kind; const char *repos_url, *session_url, *fs_path; apr_array_header_t *condensed_targets; struct fr_log_message_baton lmb; struct rev *rev; apr_hash_t *last_props; svn_stream_t *last_stream; apr_pool_t *currpool, *lastpool; /* Fetch the absolute FS path associated with PATH. */ SVN_ERR(get_fs_path(&fs_path, ra_session, path, pool)); /* Check to make sure we're dealing with a file. */ SVN_ERR(svn_ra_check_path(ra_session, path, end, &kind, pool)); if (kind == svn_node_dir) return svn_error_createf(SVN_ERR_FS_NOT_FILE, NULL, _("'%s' is not a file"), fs_path); condensed_targets = apr_array_make(pool, 1, sizeof(const char *)); APR_ARRAY_PUSH(condensed_targets, const char *) = path; lmb.path = fs_path; lmb.eldest = NULL; lmb.pool = pool; /* Accumulate revision metadata by walking the revisions backwards; this allows us to follow moves/copies correctly. */ SVN_ERR(svn_ra_get_log2(ra_session, condensed_targets, end, start, 0, /* no limit */ TRUE, FALSE, FALSE, NULL, fr_log_message_receiver, &lmb, pool)); /* Reparent the session while we go back through the history. */ SVN_ERR(svn_ra_get_session_url(ra_session, &session_url, pool)); SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_url, pool)); SVN_ERR(svn_ra_reparent(ra_session, repos_url, pool)); currpool = svn_pool_create(pool); lastpool = svn_pool_create(pool); /* We want the first txdelta to be against the empty file. */ last_props = apr_hash_make(lastpool); last_stream = svn_stream_empty(lastpool); /* Walk the revision list in chronological order, downloading each fulltext, diffing it with its predecessor, and calling the file_revs handler for each one. Use two iteration pools rather than one, because the diff routines need to look at a sliding window of revisions. Two pools gives us a ring buffer of sorts. */ for (rev = lmb.eldest; rev; rev = rev->next) { const char *temp_path; apr_pool_t *tmppool; apr_hash_t *props; apr_file_t *file; svn_stream_t *stream; apr_array_header_t *prop_diffs; svn_txdelta_stream_t *delta_stream; svn_txdelta_window_handler_t delta_handler = NULL; void *delta_baton = NULL; svn_pool_clear(currpool); /* Get the contents of the file from the repository, and put them in a temporary local file. */ SVN_ERR(svn_stream_open_unique(&stream, &temp_path, NULL, svn_io_file_del_on_pool_cleanup, currpool, currpool)); SVN_ERR(svn_ra_get_file(ra_session, rev->path + 1, rev->revision, stream, NULL, &props, currpool)); SVN_ERR(svn_stream_close(stream)); /* Open up a stream to the local file. */ SVN_ERR(svn_io_file_open(&file, temp_path, APR_READ, APR_OS_DEFAULT, currpool)); stream = svn_stream_from_aprfile2(file, FALSE, currpool); /* Calculate the property diff */ SVN_ERR(svn_prop_diffs(&prop_diffs, props, last_props, lastpool)); /* Call the file_rev handler */ SVN_ERR(handler(handler_baton, rev->path, rev->revision, rev->props, FALSE, /* merged revision */ &delta_handler, &delta_baton, prop_diffs, lastpool)); /* Compute and send delta if client asked for it. */ if (delta_handler) { /* Get the content delta. */ svn_txdelta(&delta_stream, last_stream, stream, lastpool); /* And send. */ SVN_ERR(svn_txdelta_send_txstream(delta_stream, delta_handler, delta_baton, lastpool)); } /* Switch the pools and data for the next iteration */ tmppool = currpool; currpool = lastpool; lastpool = tmppool; SVN_ERR(svn_stream_close(last_stream)); last_stream = stream; last_props = props; } SVN_ERR(svn_stream_close(last_stream)); svn_pool_destroy(currpool); svn_pool_destroy(lastpool); /* Reparent the session back to the original URL. */ return svn_ra_reparent(ra_session, session_url, pool); }