📄 reps-strings.c
字号:
/* reps-strings.c : intepreting representations with respect to strings * * ==================================================================== * Copyright (c) 2000-2004 CollabNet. All rights reserved. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at http://subversion.tigris.org/license-1.html. * If newer versions of this license are posted there, you may use a * newer version instead, at your option. * * This software consists of voluntary contributions made by many * individuals. For exact contribution history, see the revision * history and logs, available at http://subversion.tigris.org/. * ==================================================================== */#include <assert.h>#include <apr_md5.h>#define APU_WANT_DB#include <apu_want.h>#include "svn_fs.h"#include "svn_pools.h"#include "svn_md5.h"#include "fs.h"#include "err.h"#include "trail.h"#include "reps-strings.h"#include "bdb/reps-table.h"#include "bdb/strings-table.h"#include "../libsvn_fs/fs-loader.h"#include "svn_private_config.h"/*** Helper Functions ***//* Return non-zero iff REP is mutable under transaction TXN_ID. */static svn_boolean_t rep_is_mutable(representation_t *rep, const char *txn_id){ if ((! rep->txn_id) || (strcmp(rep->txn_id, txn_id) != 0)) return FALSE; return TRUE;}/* Helper macro that evaluates to an error message indicating that the representation referred to by X has an unknown node kind. */#define UNKNOWN_NODE_KIND(x) \ svn_error_createf \ (SVN_ERR_FS_CORRUPT, NULL, \ _("Unknown node kind for representation '%s'"), x)/* Return a `fulltext' representation, allocated in POOL, which * references the string STR_KEY. * * If TXN_ID is non-zero and non-NULL, make the representation mutable * under that TXN_ID. * * If STR_KEY is non-null, copy it into an allocation from POOL. * * If CHECKSUM is non-null, use it as the checksum for the new rep; * else initialize the rep with an all-zero (i.e., always successful) * checksum. */static representation_t *make_fulltext_rep(const char *str_key, const char *txn_id, const unsigned char *checksum, apr_pool_t *pool){ representation_t *rep = apr_pcalloc(pool, sizeof(*rep)); if (txn_id && *txn_id) rep->txn_id = apr_pstrdup(pool, txn_id); rep->kind = rep_kind_fulltext; if (checksum) memcpy(rep->checksum, checksum, APR_MD5_DIGESTSIZE); else memset(rep->checksum, 0, APR_MD5_DIGESTSIZE); rep->contents.fulltext.string_key = str_key ? apr_pstrdup(pool, str_key) : NULL; return rep;}/* Set *KEYS to an array of string keys gleaned from `delta' representation REP. Allocate *KEYS in POOL. */static svn_error_t *delta_string_keys(apr_array_header_t **keys, const representation_t *rep, apr_pool_t *pool){ const char *key; int i; apr_array_header_t *chunks; if (rep->kind != rep_kind_delta) return svn_error_create (SVN_ERR_FS_GENERAL, NULL, _("Representation is not of type 'delta'")); /* Set up a convenience variable. */ chunks = rep->contents.delta.chunks; /* Initialize *KEYS to an empty array. */ *keys = apr_array_make(pool, chunks->nelts, sizeof(key)); if (! chunks->nelts) return SVN_NO_ERROR; /* Now, push the string keys for each window into *KEYS */ for (i = 0; i < chunks->nelts; i++) { rep_delta_chunk_t *chunk = (((rep_delta_chunk_t **) chunks->elts)[i]); key = apr_pstrdup(pool, chunk->string_key); (*((const char **)(apr_array_push(*keys)))) = key; } return SVN_NO_ERROR;}/* Delete the strings associated with array KEYS in FS as part of TRAIL. */static svn_error_t *delete_strings(apr_array_header_t *keys, svn_fs_t *fs, trail_t *trail, apr_pool_t *pool){ int i; const char *str_key; apr_pool_t *subpool = svn_pool_create(pool); for (i = 0; i < keys->nelts; i++) { svn_pool_clear(subpool); str_key = ((const char **) keys->elts)[i]; SVN_ERR(svn_fs_bdb__string_delete(fs, str_key, trail, subpool)); } svn_pool_destroy(subpool); return SVN_NO_ERROR;}/*** Reading the contents from a representation. ***/struct compose_handler_baton{ /* The combined window, and the pool it's allocated from. */ svn_txdelta_window_t *window; apr_pool_t *window_pool; /* If the incoming window was self-compressed, and the combined WINDOW exists from previous iterations, SOURCE_BUF will point to the expanded self-compressed window. */ char *source_buf; /* The trail for this operation. WINDOW_POOL will be a child of TRAIL->pool. No allocations will be made from TRAIL->pool itself. */ trail_t *trail; /* TRUE when no more windows have to be read/combined. */ svn_boolean_t done; /* TRUE if we've just started reading a new window. We need this because the svndiff handler will push a NULL window at the end of the stream, and we have to ignore that; but we must also know when it's appropriate to push a NULL window at the combiner. */ svn_boolean_t init;};/* Handle one window. If BATON is emtpy, copy the WINDOW into it; otherwise, combine WINDOW with the one in BATON, unless WINDOW is self-compressed (i.e., does not copy from the source view), in which case expand. */static svn_error_t *compose_handler(svn_txdelta_window_t *window, void *baton){ struct compose_handler_baton *cb = baton; assert(!cb->done || window == NULL); assert(cb->trail && cb->trail->pool); if (!cb->init && !window) return SVN_NO_ERROR; /* We should never get here if we've already expanded a self-compressed window. */ assert(!cb->source_buf); if (cb->window) { if (window && (window->sview_len == 0 || window->src_ops == 0)) { /* This is a self-compressed window. Don't combine it with the others, because the combiner may go quadratic. Instead, expand it here and signal that the combination has ended. */ apr_size_t source_len = window->tview_len; assert(cb->window->sview_len == source_len); cb->source_buf = apr_palloc(cb->window_pool, source_len); svn_txdelta_apply_instructions(window, NULL, cb->source_buf, &source_len); cb->done = TRUE; } else { /* Combine the incoming window with whatever's in the baton. */ apr_pool_t *composite_pool = svn_pool_create(cb->trail->pool); svn_txdelta_window_t *composite; composite = svn_txdelta_compose_windows(window, cb->window, composite_pool); svn_pool_destroy(cb->window_pool); cb->window = composite; cb->window_pool = composite_pool; cb->done = (composite->sview_len == 0 || composite->src_ops == 0); } } else if (window) { /* Copy the (first) window into the baton. */ apr_pool_t *window_pool = svn_pool_create(cb->trail->pool); assert(cb->window_pool == NULL); cb->window = svn_txdelta_window_dup(window, window_pool); cb->window_pool = window_pool; cb->done = (window->sview_len == 0 || window->src_ops == 0); } else cb->done = TRUE; cb->init = FALSE; return SVN_NO_ERROR;}/* Read one delta window from REP[CUR_CHUNK] and push it at the composition handler. */static svn_error_t *get_one_window(struct compose_handler_baton *cb, svn_fs_t *fs, representation_t *rep, int cur_chunk){ svn_stream_t *wstream; char diffdata[4096]; /* hunk of svndiff data */ svn_filesize_t off; /* offset into svndiff data */ apr_size_t amt; /* how much svndiff data to/was read */ const char *str_key; apr_array_header_t *chunks = rep->contents.delta.chunks; rep_delta_chunk_t *this_chunk, *first_chunk; cb->init = TRUE; if (chunks->nelts <= cur_chunk) return compose_handler(NULL, cb); /* Set up a window handling stream for the svndiff data. */ wstream = svn_txdelta_parse_svndiff(compose_handler, cb, TRUE, cb->trail->pool); /* First things first: send the "SVN"{version} header through the stream. ### For now, we will just use the version specified in the first chunk, and then verify that no chunks have a different version number than the one used. In the future, we might simply convert chunks that use a different version of the diff format -- or, heck, a different format altogether -- to the format/version of the first chunk. */ first_chunk = APR_ARRAY_IDX(chunks, 0, rep_delta_chunk_t*); diffdata[0] = 'S'; diffdata[1] = 'V'; diffdata[2] = 'N'; diffdata[3] = (char) (first_chunk->version); amt = 4; SVN_ERR(svn_stream_write(wstream, diffdata, &amt)); /* FIXME: The stream write handler is borked; assert (amt == 4); */ /* Get this string key which holds this window's data. ### todo: make sure this is an `svndiff' DIFF skel here. */ this_chunk = APR_ARRAY_IDX(chunks, cur_chunk, rep_delta_chunk_t*); str_key = this_chunk->string_key; /* Run through the svndiff data, at least as far as necessary. */ off = 0; do { amt = sizeof(diffdata); SVN_ERR(svn_fs_bdb__string_read(fs, str_key, diffdata, off, &amt, cb->trail, cb->trail->pool)); off += amt; SVN_ERR(svn_stream_write(wstream, diffdata, &amt)); } while (amt != 0); SVN_ERR(svn_stream_close(wstream)); assert(!cb->init); assert(cb->window != NULL); assert(cb->window_pool != NULL); return SVN_NO_ERROR;}/* Undeltify a range of data. DELTAS is the set of delta windows to combine, FULLTEXT is the source text, CUR_CHUNK is the index of the delta chunk we're starting from. OFFSET is the relative offset of the requested data within the chunk; BUF and LEN are what we're undeltifying to. */static svn_error_t *rep_undeltify_range(svn_fs_t *fs, apr_array_header_t *deltas, representation_t *fulltext, int cur_chunk, char *buf, apr_size_t offset, apr_size_t *len, trail_t *trail, apr_pool_t *pool){ apr_size_t len_read = 0; do { struct compose_handler_baton cb = { 0 }; char *source_buf, *target_buf; apr_size_t target_len; int cur_rep; cb.trail = trail; cb.done = FALSE; for (cur_rep = 0; !cb.done && cur_rep < deltas->nelts; ++cur_rep) { representation_t *const rep = APR_ARRAY_IDX(deltas, cur_rep, representation_t*); SVN_ERR(get_one_window(&cb, fs, rep, cur_chunk)); } if (!cb.window) /* That's it, no more source data is available. */ break; /* The source view length should not be 0 if there are source copy ops in the window. */ assert(cb.window->sview_len > 0 || cb.window->src_ops == 0); /* cb.window is the combined delta window. Read the source text into a buffer. */ if (cb.source_buf) { /* The combiner already created the source text from a self-compressed window. */ source_buf = cb.source_buf; } else if (fulltext && cb.window->sview_len > 0 && cb.window->src_ops > 0) { apr_size_t source_len = cb.window->sview_len; source_buf = apr_palloc(cb.window_pool, source_len); SVN_ERR(svn_fs_bdb__string_read (fs, fulltext->contents.fulltext.string_key, source_buf, cb.window->sview_offset, &source_len, trail, pool)); if (source_len != cb.window->sview_len) return svn_error_create (SVN_ERR_FS_CORRUPT, NULL, _("Svndiff source length inconsistency")); } else { source_buf = NULL; /* Won't read anything from here. */ } if (offset > 0) { target_len = *len - len_read + offset; target_buf = apr_palloc(cb.window_pool, target_len); } else { target_len = *len - len_read; target_buf = buf; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -