text_delta.c
来自「linux subdivision ying gai ke yi le ba」· C语言 代码 · 共 748 行 · 第 1/2 页
C
748 行
tb->source_len = SVN_DELTA_WINDOW_SIZE;
SVN_ERR (svn_stream_read (tb->source, tb->buf, &tb->source_len));
if (tb->source_len < SVN_DELTA_WINDOW_SIZE)
tb->source_done = TRUE;
}
/* Copy in the target data, up to SVN_DELTA_WINDOW_SIZE. */
chunk_len = SVN_DELTA_WINDOW_SIZE - tb->target_len;
if (chunk_len > data_len)
chunk_len = data_len;
memcpy (tb->buf + tb->source_len + tb->target_len, data, chunk_len);
data += chunk_len;
data_len -= chunk_len;
tb->target_len += chunk_len;
/* If we're full of target data, compute and fire off a window. */
if (tb->target_len == SVN_DELTA_WINDOW_SIZE)
{
window = compute_window (tb->buf, tb->source_len, tb->target_len,
tb->source_offset, pool);
SVN_ERR (tb->wh (window, tb->whb));
tb->source_offset += tb->source_len;
tb->source_len = 0;
tb->target_len = 0;
}
}
svn_pool_destroy(pool);
return SVN_NO_ERROR;
}
/* This is the close handler for a target-push delta stream. It sends
* a final window if there is any buffered target data, and then sends
* a NULL window signifying the end of the window stream. */
static svn_error_t *
tpush_close_handler (void *baton)
{
struct tpush_baton *tb = baton;
svn_txdelta_window_t *window;
/* Send a final window if we have any residual target data. */
if (tb->target_len > 0)
{
window = compute_window(tb->buf, tb->source_len, tb->target_len,
tb->source_offset, tb->pool);
SVN_ERR(tb->wh(window, tb->whb));
}
/* Send a final NULL window signifying the end. */
SVN_ERR(tb->wh(NULL, tb->whb));
return SVN_NO_ERROR;
}
svn_stream_t *
svn_txdelta_target_push (svn_txdelta_window_handler_t handler,
void *handler_baton, svn_stream_t *source,
apr_pool_t *pool)
{
struct tpush_baton *tb;
svn_stream_t *stream;
/* Initialize baton. */
tb = apr_palloc (pool, sizeof(*tb));
tb->source = source;
tb->wh = handler;
tb->whb = handler_baton;
tb->pool = pool;
tb->buf = apr_palloc (pool, 2 * SVN_DELTA_WINDOW_SIZE);
tb->source_offset = 0;
tb->source_len = 0;
tb->source_done = FALSE;
tb->target_len = 0;
/* Create and return writable stream. */
stream = svn_stream_create (tb, pool);
svn_stream_set_write (stream, tpush_write_handler);
svn_stream_set_close (stream, tpush_close_handler);
return stream;
}
/* Functions for applying deltas. */
/* Ensure that BUF has enough space for VIEW_LEN bytes. */
static APR_INLINE void
size_buffer (char **buf, apr_size_t *buf_size,
apr_size_t view_len, apr_pool_t *pool)
{
if (view_len > *buf_size)
{
*buf_size *= 2;
if (*buf_size < view_len)
*buf_size = view_len;
*buf = apr_palloc (pool, *buf_size);
}
}
void
svn_txdelta__apply_instructions (svn_txdelta_window_t *window,
const char *sbuf, char *tbuf,
apr_size_t *tlen)
{
const svn_txdelta_op_t *op;
apr_size_t i, j, tpos = 0;
for (op = window->ops; op < window->ops + window->num_ops; op++)
{
const apr_size_t buf_len = (op->length < *tlen - tpos
? op->length : *tlen - tpos);
/* Check some invariants common to all instructions. */
assert (op->offset >= 0 && op->length >= 0);
assert (tpos + op->length <= window->tview_len);
switch (op->action_code)
{
case svn_txdelta_source:
/* Copy from source area. */
assert (op->offset + op->length <= window->sview_len);
memcpy (tbuf + tpos, sbuf + op->offset, buf_len);
break;
case svn_txdelta_target:
/* Copy from target area. Don't use memcpy() since its
semantics aren't guaranteed for overlapping memory areas,
and target copies are allowed to overlap to generate
repeated data. */
assert (op->offset < tpos);
for (i = op->offset, j = tpos; i < op->offset + buf_len; i++)
tbuf[j++] = tbuf[i];
break;
case svn_txdelta_new:
/* Copy from window new area. */
assert (op->offset + op->length <= window->new_data->len);
memcpy (tbuf + tpos,
window->new_data->data + op->offset,
buf_len);
break;
default:
assert (!"Invalid delta instruction code");
}
tpos += op->length;
if (tpos >= *tlen)
return; /* The buffer is full. */
}
/* Check that we produced the right amount of data. */
assert (tpos == window->tview_len);
*tlen = tpos;
}
/* Apply WINDOW to the streams given by APPL. */
static svn_error_t *
apply_window (svn_txdelta_window_t *window, void *baton)
{
struct apply_baton *ab = (struct apply_baton *) baton;
apr_size_t len;
svn_error_t *err;
if (window == NULL)
{
/* We're done; just clean up. */
if (ab->result_digest)
apr_md5_final (ab->result_digest, &(ab->md5_context));
err = svn_stream_close (ab->target);
svn_pool_destroy (ab->pool);
return err;
}
/* Make sure the source view didn't slide backwards. */
assert (window->sview_len == 0
|| (window->sview_offset >= ab->sbuf_offset
&& (window->sview_offset + window->sview_len
>= ab->sbuf_offset + ab->sbuf_len)));
/* Make sure there's enough room in the target buffer. */
size_buffer (&ab->tbuf, &ab->tbuf_size, window->tview_len, ab->pool);
/* Prepare the source buffer for reading from the input stream. */
if (window->sview_offset != ab->sbuf_offset
|| window->sview_len > ab->sbuf_size)
{
char *old_sbuf = ab->sbuf;
/* Make sure there's enough room. */
size_buffer (&ab->sbuf, &ab->sbuf_size, window->sview_len, ab->pool);
/* If the existing view overlaps with the new view, copy the
* overlap to the beginning of the new buffer. */
if (ab->sbuf_offset + ab->sbuf_len > window->sview_offset)
{
apr_size_t start =
(apr_size_t)(window->sview_offset - ab->sbuf_offset);
memmove (ab->sbuf, old_sbuf + start, ab->sbuf_len - start);
ab->sbuf_len -= start;
}
else
ab->sbuf_len = 0;
ab->sbuf_offset = window->sview_offset;
}
/* Read the remainder of the source view into the buffer. */
if (ab->sbuf_len < window->sview_len)
{
len = window->sview_len - ab->sbuf_len;
err = svn_stream_read (ab->source, ab->sbuf + ab->sbuf_len, &len);
if (err == SVN_NO_ERROR && len != window->sview_len - ab->sbuf_len)
err = svn_error_create (SVN_ERR_INCOMPLETE_DATA, NULL,
"Delta source ended unexpectedly");
if (err != SVN_NO_ERROR)
return err;
ab->sbuf_len = window->sview_len;
}
/* Apply the window instructions to the source view to generate
the target view. */
len = window->tview_len;
svn_txdelta__apply_instructions (window, ab->sbuf, ab->tbuf, &len);
assert (len == window->tview_len);
/* Write out the output. */
/* ### We've also considered just adding two (optionally null)
arguments to svn_stream_create(): read_checksum and
write_checksum. Then instead of every caller updating an md5
context when it calls svn_stream_write() or svn_stream_read(),
streams would do it automatically, and verify the checksum in
svn_stream_closed(). But this might be overkill for issue #689;
so for now we just update the context here. */
if (ab->result_digest)
apr_md5_update(&(ab->md5_context), ab->tbuf, len);
return svn_stream_write (ab->target, ab->tbuf, &len);
}
void
svn_txdelta_apply (svn_stream_t *source,
svn_stream_t *target,
unsigned char *result_digest,
const char *error_info,
apr_pool_t *pool,
svn_txdelta_window_handler_t *handler,
void **handler_baton)
{
apr_pool_t *subpool = svn_pool_create (pool);
struct apply_baton *ab;
ab = apr_palloc (subpool, sizeof (*ab));
ab->source = source;
ab->target = target;
ab->pool = subpool;
ab->sbuf = NULL;
ab->sbuf_size = 0;
ab->sbuf_offset = 0;
ab->sbuf_len = 0;
ab->tbuf = NULL;
ab->tbuf_size = 0;
ab->result_digest = result_digest;
if (result_digest)
apr_md5_init (&(ab->md5_context));
if (error_info)
ab->error_info = apr_pstrdup (subpool, error_info);
else
ab->error_info = NULL;
*handler = apply_window;
*handler_baton = ab;
}
/* Convenience routines */
svn_error_t *
svn_txdelta_send_string (const svn_string_t *string,
svn_txdelta_window_handler_t handler,
void *handler_baton,
apr_pool_t *pool)
{
svn_txdelta_window_t window = { 0 };
svn_txdelta_op_t op;
/* Build a single `new' op */
op.action_code = svn_txdelta_new;
op.offset = 0;
op.length = string->len;
/* Build a single window containing a ptr to the string. */
window.tview_len = string->len;
window.num_ops = 1;
window.ops = &op;
window.new_data = string;
/* Push the one window at the handler. */
SVN_ERR ((*handler) (&window, handler_baton));
/* Push a NULL at the handler, because we're done. */
SVN_ERR ((*handler) (NULL, handler_baton));
return SVN_NO_ERROR;
}
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;
}
svn_error_t *svn_txdelta_send_txstream (svn_txdelta_stream_t *txstream,
svn_txdelta_window_handler_t handler,
void *handler_baton,
apr_pool_t *pool)
{
svn_txdelta_window_t *window;
/* create a pool just for the windows */
apr_pool_t *wpool = svn_pool_create (pool);
do
{
/* read in a single delta window */
SVN_ERR( svn_txdelta_next_window (&window, txstream, wpool));
/* shove it at the handler */
SVN_ERR( (*handler)(window, handler_baton));
/* free the window (if any) */
svn_pool_clear (wpool);
}
while (window != NULL);
svn_pool_destroy (wpool);
return SVN_NO_ERROR;
}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?