inline Try<std::string> patch(const std::string& s, const Diff& diff) { // 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); // We want to apply the svndiff format diff to the source trying to // produce a result. First setup a handler for applying a text delta // to the source stream. svn_string_t source; source.data = s.data(); source.len = s.length(); svn_txdelta_window_handler_t handler; void* baton = NULL; svn_stringbuf_t* patched = svn_stringbuf_create_ensure(s.length(), pool); svn_txdelta_apply( svn_stream_from_string(&source, pool), svn_stream_from_stringbuf(patched, pool), NULL, NULL, pool, &handler, &baton); // Setup a stream that converts an svndiff format diff to a text // delta, so that we can use our handler to patch the source string. svn_stream_t* stream = svn_txdelta_parse_svndiff( handler, baton, TRUE, pool); // Now feed the diff into the stream to compute the patched result. const char* data = diff.data.data(); apr_size_t length = diff.data.length(); svn_error_t* error = svn_stream_write(stream, data, &length); if (error != NULL) { char buffer[1024]; std::string message(svn_err_best_message(error, buffer, 1024)); svn_pool_destroy(pool); return Error(message); } std::string result(patched->data, patched->len); svn_pool_destroy(pool); return result; }
/* Conforms to svn_ra_serf__xml_opened_t */ static svn_error_t * blame_opened(svn_ra_serf__xml_estate_t *xes, void *baton, int entered_state, const svn_ra_serf__dav_props_t *tag, apr_pool_t *scratch_pool) { blame_context_t *blame_ctx = baton; if (entered_state == FILE_REV) { apr_pool_t *state_pool = svn_ra_serf__xml_state_pool(xes); /* Child elements will store properties in these structures. */ blame_ctx->rev_props = apr_hash_make(state_pool); blame_ctx->prop_diffs = apr_array_make(state_pool, 5, sizeof(svn_prop_t)); blame_ctx->state_pool = state_pool; /* Clear this, so we can detect the absence of a TXDELTA. */ blame_ctx->stream = NULL; } else if (entered_state == TXDELTA) { apr_pool_t *state_pool = svn_ra_serf__xml_state_pool(xes); apr_hash_t *gathered = svn_ra_serf__xml_gather_since(xes, FILE_REV); const char *path; const char *rev_str; const char *merged_revision; svn_txdelta_window_handler_t txdelta; void *txdelta_baton; apr_int64_t rev; path = svn_hash_gets(gathered, "path"); rev_str = svn_hash_gets(gathered, "rev"); SVN_ERR(svn_cstring_atoi64(&rev, rev_str)); merged_revision = svn_hash_gets(gathered, "merged-revision"); SVN_ERR(blame_ctx->file_rev(blame_ctx->file_rev_baton, path, (svn_revnum_t)rev, blame_ctx->rev_props, merged_revision != NULL, &txdelta, &txdelta_baton, blame_ctx->prop_diffs, state_pool)); blame_ctx->stream = svn_base64_decode(svn_txdelta_parse_svndiff( txdelta, txdelta_baton, TRUE /* error_on_early_close */, state_pool), state_pool); } return SVN_NO_ERROR; }
static svn_error_t *ra_svn_handle_apply_textdelta(svn_ra_svn_conn_t *conn, apr_pool_t *pool, const apr_array_header_t *params, ra_svn_driver_state_t *ds) { const char *token; ra_svn_token_entry_t *entry; svn_txdelta_window_handler_t wh; void *wh_baton; char *base_checksum; /* Parse arguments and look up the token. */ SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?c)", &token, &base_checksum)); SVN_ERR(lookup_token(ds, token, TRUE, &entry)); if (entry->dstream) return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, _("Apply-textdelta already active")); entry->pool = svn_pool_create(ds->file_pool); SVN_CMD_ERR(ds->editor->apply_textdelta(entry->baton, base_checksum, entry->pool, &wh, &wh_baton)); entry->dstream = svn_txdelta_parse_svndiff(wh, wh_baton, TRUE, entry->pool); return SVN_NO_ERROR; }
/* Read CONTENT_LENGTH bytes from STREAM, and use PARSE_FNS->set_fulltext to push those bytes as replace fulltext for a node. Use BUFFER/BUFLEN to push the fulltext in "chunks". Use POOL for all allocations. */ static svn_error_t * parse_text_block(svn_stream_t *stream, svn_filesize_t content_length, svn_boolean_t is_delta, const svn_repos_parse_fns3_t *parse_fns, void *record_baton, char *buffer, apr_size_t buflen, apr_pool_t *pool) { svn_stream_t *text_stream = NULL; apr_size_t num_to_read, rlen, wlen; if (is_delta) { svn_txdelta_window_handler_t wh; void *whb; SVN_ERR(parse_fns->apply_textdelta(&wh, &whb, record_baton)); if (wh) text_stream = svn_txdelta_parse_svndiff(wh, whb, TRUE, pool); } else { /* Get a stream to which we can push the data. */ SVN_ERR(parse_fns->set_fulltext(&text_stream, record_baton)); } /* If there are no contents to read, just write an empty buffer through our callback. */ if (content_length == 0) { wlen = 0; if (text_stream) SVN_ERR(svn_stream_write(text_stream, "", &wlen)); } /* Regardless of whether or not we have a sink for our data, we need to read it. */ while (content_length) { if (content_length >= (svn_filesize_t)buflen) rlen = buflen; else rlen = (apr_size_t) content_length; num_to_read = rlen; SVN_ERR(svn_stream_read(stream, buffer, &rlen)); content_length -= rlen; if (rlen != num_to_read) return stream_ran_dry(); if (text_stream) { /* write however many bytes you read. */ wlen = rlen; SVN_ERR(svn_stream_write(text_stream, buffer, &wlen)); if (wlen != rlen) { /* Uh oh, didn't write as many bytes as we read. */ return svn_error_create(SVN_ERR_STREAM_UNEXPECTED_EOF, NULL, _("Unexpected EOF writing contents")); } } } /* If we opened a stream, we must close it. */ if (text_stream) SVN_ERR(svn_stream_close(text_stream)); return SVN_NO_ERROR; }
static svn_error_t * start_element(int *elem, void *baton, int parent_state, const char *nspace, const char *elt_name, const char **atts) { replay_baton_t *rb = baton; const svn_ra_neon__xml_elm_t *elm = svn_ra_neon__lookup_xml_elem(editor_report_elements, nspace, elt_name); if (! elm) { *elem = NE_XML_DECLINE; return SVN_NO_ERROR; } if (parent_state == ELEM_root) { /* If we're at the root of the tree, the element has to be the editor * report itself. */ if (elm->id != ELEM_editor_report) return UNEXPECTED_ELEMENT(nspace, elt_name); } else if (parent_state != ELEM_editor_report) { /* If we're not at the root, our parent has to be the editor report, * since we don't actually nest any elements. */ return UNEXPECTED_ELEMENT(nspace, elt_name); } switch (elm->id) { case ELEM_target_revision: { const char *crev = svn_xml_get_attr_value("rev", atts); if (! crev) return MISSING_ATTR(nspace, elt_name, "rev"); else return rb->editor->set_target_revision(rb->edit_baton, SVN_STR_TO_REV(crev), rb->pool); } break; case ELEM_open_root: { const char *crev = svn_xml_get_attr_value("rev", atts); if (! crev) return MISSING_ATTR(nspace, elt_name, "rev"); else { apr_pool_t *subpool = svn_pool_create(rb->pool); void *dir_baton; SVN_ERR(rb->editor->open_root(rb->edit_baton, SVN_STR_TO_REV(crev), subpool, &dir_baton)); push_dir(rb, dir_baton, "", subpool); } } break; case ELEM_delete_entry: { const char *path = svn_xml_get_attr_value("name", atts); const char *crev = svn_xml_get_attr_value("rev", atts); if (! path) return MISSING_ATTR(nspace, elt_name, "name"); else if (! crev) return MISSING_ATTR(nspace, elt_name, "rev"); else { dir_item_t *di = &TOP_DIR(rb); SVN_ERR(rb->editor->delete_entry(path, SVN_STR_TO_REV(crev), di->baton, di->pool)); } } break; case ELEM_open_directory: case ELEM_add_directory: { const char *crev = svn_xml_get_attr_value("rev", atts); const char *name = svn_xml_get_attr_value("name", atts); if (! name) return MISSING_ATTR(nspace, elt_name, "name"); else { dir_item_t *parent = &TOP_DIR(rb); apr_pool_t *subpool = svn_pool_create(parent->pool); svn_revnum_t rev; void *dir_baton; if (crev) rev = SVN_STR_TO_REV(crev); else rev = SVN_INVALID_REVNUM; if (elm->id == ELEM_open_directory) SVN_ERR(rb->editor->open_directory(name, parent->baton, rev, subpool, &dir_baton)); else if (elm->id == ELEM_add_directory) { const char *cpath = svn_xml_get_attr_value("copyfrom-path", atts); crev = svn_xml_get_attr_value("copyfrom-rev", atts); if (crev) rev = SVN_STR_TO_REV(crev); else rev = SVN_INVALID_REVNUM; SVN_ERR(rb->editor->add_directory(name, parent->baton, cpath, rev, subpool, &dir_baton)); } else SVN_ERR_MALFUNCTION(); push_dir(rb, dir_baton, name, subpool); } } break; case ELEM_open_file: case ELEM_add_file: { const char *path = svn_xml_get_attr_value("name", atts); svn_revnum_t rev; dir_item_t *parent = &TOP_DIR(rb); if (! path) return MISSING_ATTR(nspace, elt_name, "name"); svn_pool_clear(parent->file_pool); if (elm->id == ELEM_add_file) { const char *cpath = svn_xml_get_attr_value("copyfrom-path", atts); const char *crev = svn_xml_get_attr_value("copyfrom-rev", atts); if (crev) rev = SVN_STR_TO_REV(crev); else rev = SVN_INVALID_REVNUM; SVN_ERR(rb->editor->add_file(path, parent->baton, cpath, rev, parent->file_pool, &rb->file_baton)); } else { const char *crev = svn_xml_get_attr_value("rev", atts); if (crev) rev = SVN_STR_TO_REV(crev); else rev = SVN_INVALID_REVNUM; SVN_ERR(rb->editor->open_file(path, parent->baton, rev, parent->file_pool, &rb->file_baton)); } } break; case ELEM_apply_textdelta: if (! rb->file_baton) return svn_error_create (SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, _("Got apply-textdelta element without preceding " "add-file or open-file")); else { const char *checksum = svn_xml_get_attr_value("checksum", atts); SVN_ERR(rb->editor->apply_textdelta(rb->file_baton, checksum, TOP_DIR(rb).file_pool, &rb->whandler, &rb->whandler_baton)); rb->svndiff_decoder = svn_txdelta_parse_svndiff (rb->whandler, rb->whandler_baton, TRUE, TOP_DIR(rb).file_pool); rb->base64_decoder = svn_base64_decode(rb->svndiff_decoder, TOP_DIR(rb).file_pool); } break; case ELEM_close_file: if (! rb->file_baton) return svn_error_create (SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, _("Got close-file element without preceding " "add-file or open-file")); else { const char *checksum = svn_xml_get_attr_value("checksum", atts); SVN_ERR(rb->editor->close_file(rb->file_baton, checksum, TOP_DIR(rb).file_pool)); rb->file_baton = NULL; } break; case ELEM_close_directory: if (rb->dirs->nelts == 0) return svn_error_create (SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, _("Got close-directory element without ever opening " "a directory")); else { dir_item_t *di = &TOP_DIR(rb); SVN_ERR(rb->editor->close_directory(di->baton, di->pool)); svn_pool_destroy(di->pool); apr_array_pop(rb->dirs); } break; case ELEM_change_file_prop: case ELEM_change_dir_prop: { const char *name = svn_xml_get_attr_value("name", atts); if (! name) return MISSING_ATTR(nspace, elt_name, "name"); else { svn_pool_clear(rb->prop_pool); if (svn_xml_get_attr_value("del", atts)) rb->prop_accum = NULL; else rb->prop_accum = svn_stringbuf_create("", rb->prop_pool); rb->prop_name = apr_pstrdup(rb->prop_pool, name); } } break; } *elem = elm->id; return SVN_NO_ERROR; }
/* (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; }
/* Implements svn_test_driver_t. */ static svn_error_t * random_test(apr_pool_t *pool) { apr_uint32_t seed, maxlen; apr_size_t bytes_range; 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); 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 using varying compression levels. */ svn_txdelta_to_svndiff3(&handler, &handler_baton, stream, 1, i % 10, delta_pool); /* Make stage 1: create the text delta. */ svn_txdelta2(&txdelta_stream, svn_stream_from_aprfile(source, delta_pool), svn_stream_from_aprfile(target, delta_pool), FALSE, 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; }