📄 text_delta.c
字号:
{ struct tpush_baton *tb = baton; apr_size_t chunk_len, data_len = *len; apr_pool_t *pool = svn_pool_create(tb->pool); svn_txdelta_window_t *window; while (data_len > 0) { svn_pool_clear(pool); /* Make sure we're all full up on source data, if possible. */ if (tb->source_len == 0 && !tb->source_done) { 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 voidsize_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); }}voidsvn_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(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;}/* This is a private interlibrary compatibility wrapper. */voidsvn_txdelta__apply_instructions(svn_txdelta_window_t *window, const char *sbuf, char *tbuf, apr_size_t *tlen);voidsvn_txdelta__apply_instructions(svn_txdelta_window_t *window, const char *sbuf, char *tbuf, apr_size_t *tlen){ svn_txdelta_apply_instructions(window, sbuf, tbuf, tlen);}/* 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);}voidsvn_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 { /* free the window (if any) */ svn_pool_clear(wpool); /* 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)); } while (window != NULL); svn_pool_destroy(wpool); return SVN_NO_ERROR;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -