📄 compose_delta.c
字号:
NOTE: The range index must be splayed to OFFSET! */static voidinsert_range(apr_size_t offset, apr_size_t limit, apr_size_t target_offset, range_index_t *ndx){ range_index_node_t *node = NULL; if (ndx->tree == NULL) { node = alloc_range_index_node(ndx, offset, limit, target_offset); ndx->tree = node; } else { if (offset == ndx->tree->offset && limit > ndx->tree->limit) { ndx->tree->limit = limit; ndx->tree->target_offset = target_offset; clean_tree(ndx, limit); } else if (offset > ndx->tree->offset && limit > ndx->tree->limit) { /* We have to make the same sort of checks as clean_tree() does for superseded ranges. Have to merge these someday. */ const svn_boolean_t insert_range_p = (!ndx->tree->next || ndx->tree->limit < ndx->tree->next->offset || limit > ndx->tree->next->limit); if (insert_range_p) { /* Again, we have to check if the new node and the one to the left of the root override root's range. */ if (ndx->tree->prev && ndx->tree->prev->limit > offset) { /* Replace the data in the splayed node. */ ndx->tree->offset = offset; ndx->tree->limit = limit; ndx->tree->target_offset = target_offset; } else { /* Insert the range to the right of the splayed node. */ node = alloc_range_index_node(ndx, offset, limit, target_offset); if ((node->next = ndx->tree->next) != NULL) node->next->prev = node; ndx->tree->next = node; node->prev = ndx->tree; node->right = ndx->tree->right; ndx->tree->right = NULL; node->left = ndx->tree; ndx->tree = node; } clean_tree(ndx, limit); } else /* Ignore the range */; } else if (offset < ndx->tree->offset) { assert(ndx->tree->left == NULL); /* Insert the range left of the splayed node */ node = alloc_range_index_node(ndx, offset, limit, target_offset); node->left = node->prev = NULL; node->right = node->next = ndx->tree; ndx->tree = node->next->prev = node; clean_tree(ndx, limit); } else /* Ignore the range */; }}/* ==================================================================== *//* Juggling with lists of ranges. *//* Allocate a node and add it to the range list. LIST is the head of the range list, TAIL is the last node in the list. NDX holds the freelist; OFFSET, LIMIT and KIND are node data. */static range_list_node_t *alloc_range_list(range_list_node_t **list, range_list_node_t **tail, range_index_t *ndx, enum range_kind kind, apr_size_t offset, apr_size_t limit, apr_size_t target_offset){ range_list_node_t *const node = alloc_block(ndx->pool, &ndx->free_list); node->kind = kind; node->offset = offset; node->limit = limit; node->target_offset = target_offset; if (*list == NULL) { node->prev = node->next = NULL; *list = *tail = node; } else { node->prev = *tail; node->next = NULL; (*tail)->next = node; *tail = node; } return *list;}/* Free a range list. LIST is the head of the list, NDX holds the freelist. */static voidfree_range_list(range_list_node_t *list, range_index_t *ndx){ while (list) { range_list_node_t *const node = list; list = node->next; free_block(node, &ndx->free_list); }}/* Based on the data in NDX, build a list of ranges that cover [OFFSET, LIMIT) in the "virtual" source data. NOTE: The range index must be splayed to OFFSET! */static range_list_node_t *build_range_list(apr_size_t offset, apr_size_t limit, range_index_t *ndx){ range_list_node_t *range_list = NULL; range_list_node_t *last_range = NULL; range_index_node_t *node = ndx->tree; while (offset < limit) { if (node == NULL) return alloc_range_list(&range_list, &last_range, ndx, range_from_source, offset, limit, 0); if (offset < node->offset) { if (limit <= node->offset) return alloc_range_list(&range_list, &last_range, ndx, range_from_source, offset, limit, 0); else { alloc_range_list(&range_list, &last_range, ndx, range_from_source, offset, node->offset, 0); offset = node->offset; } } else { /* TODO: (Potential optimization) Investigate if it would make sense to forbid range_from_target lengths shorter than, say, VD_KEY_SIZE (see vdelta.c) */ if (offset >= node->limit) node = node->next; else { const apr_size_t target_offset = offset - node->offset + node->target_offset; if (limit <= node->limit) return alloc_range_list(&range_list, &last_range, ndx, range_from_target, offset, limit, target_offset); else { alloc_range_list(&range_list, &last_range, ndx, range_from_target, offset, node->limit, target_offset); offset = node->limit; node = node->next; } } } } assert(!"A range's offset isn't smaller than its limit? Impossible!"); return range_list;}/* Copy the instructions from WINDOW that define the range [OFFSET, LIMIT) in WINDOW's target stream to TARGET_OFFSET in the window represented by BUILD_BATON. Use NDX to find the instructions in WINDOW. Allocate space in BUILD_BATON from POOL. */static voidcopy_source_ops(apr_size_t offset, apr_size_t limit, apr_size_t target_offset, svn_txdelta__ops_baton_t *build_baton, const svn_txdelta_window_t *window, const offset_index_t *ndx, apr_pool_t *pool){ const int first_op = search_offset_index(ndx, offset); const int last_op = search_offset_index(ndx, limit - 1); int op_ndx; for (op_ndx = first_op; op_ndx <= last_op; ++op_ndx) { const svn_txdelta_op_t *const op = &window->ops[op_ndx]; const apr_size_t *const off = &ndx->offs[op_ndx]; const apr_size_t fix_offset = (offset > off[0] ? offset - off[0] : 0); const apr_size_t fix_limit = (off[1] > limit ? off[1] - limit : 0); /* It would be extremely weird if the fixed-up op had zero length. */ assert(fix_offset + fix_limit < op->length); if (op->action_code != svn_txdelta_target) { /* Delta ops that don't depend on the virtual target can be copied to the composite unchanged. */ const char *const new_data = (op->action_code == svn_txdelta_new ? (window->new_data->data + op->offset + fix_offset) : NULL); svn_txdelta__insert_op(build_baton, op->action_code, op->offset + fix_offset, op->length - fix_offset - fix_limit, new_data, pool); } else { /* The source of a target copy must start before the current offset in the (virtual) target stream. */ assert(op->offset < off[0]); if (op->offset + op->length - fix_limit <= off[0]) { /* The recursion _must_ end, otherwise the delta has circular references, and that is not possible. */ copy_source_ops(op->offset + fix_offset, op->offset + op->length - fix_limit, target_offset, build_baton, window, ndx, pool); } else { /* This is an overlapping target copy. The idea here is to transpose the pattern, then generate another overlapping copy. */ const apr_size_t ptn_length = off[0] - op->offset; const apr_size_t ptn_overlap = fix_offset % ptn_length; apr_size_t fix_off = fix_offset; apr_size_t tgt_off = target_offset; assert(ptn_length > ptn_overlap); /* ### FIXME: ptn_overlap is unsigned, so the if() condition below is always true! Either it should be '> 0', or the code block should be unconditional. See also r2288. */ if (ptn_overlap >= 0) { /* Issue second subrange in the pattern. */ const apr_size_t length = MIN(op->length - fix_off - fix_limit, ptn_length - ptn_overlap); copy_source_ops(op->offset + ptn_overlap, op->offset + ptn_overlap + length, tgt_off, build_baton, window, ndx, pool); fix_off += length; tgt_off += length; } assert(fix_off + fix_limit <= op->length); if (ptn_overlap > 0 && fix_off + fix_limit < op->length) { /* Issue the first subrange in the pattern. */ const apr_size_t length = MIN(op->length - fix_off - fix_limit, ptn_overlap); copy_source_ops(op->offset, op->offset + length, tgt_off, build_baton, window, ndx, pool); fix_off += length; tgt_off += length; } assert(fix_off + fix_limit <= op->length); if (fix_off + fix_limit < op->length) { /* Now multiply the pattern */ svn_txdelta__insert_op(build_baton, svn_txdelta_target, tgt_off - ptn_length, op->length - fix_off - fix_limit, NULL, pool); } } } /* Adjust the target offset for the next op in the list. */ target_offset += op->length - fix_offset - fix_limit; }}/* ==================================================================== *//* Bringing it all together. */svn_txdelta_window_t *svn_txdelta_compose_windows(const svn_txdelta_window_t *window_A, const svn_txdelta_window_t *window_B, apr_pool_t *pool){ svn_txdelta__ops_baton_t build_baton = { 0 }; svn_txdelta_window_t *composite; apr_pool_t *subpool = svn_pool_create(pool); offset_index_t *offset_index = create_offset_index(window_A, subpool); range_index_t *range_index = create_range_index(subpool); apr_size_t target_offset = 0; int i; /* Read the description of the delta composition algorithm in notes/fs-improvements.txt before going any further. You have been warned. */ build_baton.new_data = svn_stringbuf_create("", pool); for (i = 0; i < window_B->num_ops; ++i) { const svn_txdelta_op_t *const op = &window_B->ops[i]; if (op->action_code != svn_txdelta_source) { /* Delta ops that don't depend on the source can be copied to the composite unchanged. */ const char *const new_data = (op->action_code == svn_txdelta_new ? window_B->new_data->data + op->offset : NULL); svn_txdelta__insert_op(&build_baton, op->action_code, op->offset, op->length, new_data, pool); } else { /* NOTE: Remember that `offset' and `limit' refer to positions in window_B's _source_ stream, which is the same as window_A's _target_ stream! */ const apr_size_t offset = op->offset; const apr_size_t limit = op->offset + op->length; range_list_node_t *range_list, *range; apr_size_t tgt_off = target_offset; splay_range_index(offset, range_index); range_list = build_range_list(offset, limit, range_index); for (range = range_list; range; range = range->next) { if (range->kind == range_from_target) svn_txdelta__insert_op(&build_baton, svn_txdelta_target, range->target_offset, range->limit - range->offset, NULL, pool); else copy_source_ops(range->offset, range->limit, tgt_off, &build_baton, window_A, offset_index, pool); tgt_off += range->limit - range->offset; } assert(tgt_off == target_offset + op->length); free_range_list(range_list, range_index); insert_range(offset, limit, target_offset, range_index); } /* Remember the new offset in the would-be target stream. */ target_offset += op->length; } svn_pool_destroy(subpool); composite = svn_txdelta__make_window(&build_baton, pool); composite->sview_offset = window_A->sview_offset; composite->sview_len = window_A->sview_len; composite->tview_len = window_B->tview_len; return composite;}/* This is a private interlibrary compatibility wrapper. */svn_txdelta_window_t *svn_txdelta__compose_windows(const svn_txdelta_window_t *window_A, const svn_txdelta_window_t *window_B, apr_pool_t *pool);svn_txdelta_window_t *svn_txdelta__compose_windows(const svn_txdelta_window_t *window_A, const svn_txdelta_window_t *window_B, apr_pool_t *pool){ return svn_txdelta_compose_windows(window_A, window_B, pool);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -