📄 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_delta/delta.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;
}
/* 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)
{
int i;
const char *str_key;
for (i = 0; i < keys->nelts; i++)
{
str_key = ((const char **) keys->elts)[i];
SVN_ERR (svn_fs_bdb__string_delete (fs, str_key, trail));
}
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;
/* 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. */
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;
if (cb->window)
{
/* 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__copy_window(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));
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_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 (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));
assert (source_len == cb.window->sview_len);
}
else
{
static char empty_buf[] = "";
source_buf = empty_buf; /* 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;
}
svn_txdelta__apply_instructions (cb.window, source_buf,
target_buf, &target_len);
if (offset > 0)
{
assert (target_len > offset);
target_len -= offset;
memcpy (buf, target_buf + offset, target_len);
offset = 0; /* Read from the beginning of the next chunk. */
}
/* Don't need this window any more. */
svn_pool_destroy (cb.window_pool);
len_read += target_len;
buf += target_len;
++cur_chunk;
}
while (len_read < *len);
*len = len_read;
return SVN_NO_ERROR;
}
/* Calculate the index of the chunk in REP that contains REP_OFFSET,
and find the relative CHUNK_OFFSET within the chunk.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -