diff options
Diffstat (limited to 'subversion/libsvn_fs_x/changes.c')
-rw-r--r-- | subversion/libsvn_fs_x/changes.c | 536 |
1 files changed, 536 insertions, 0 deletions
diff --git a/subversion/libsvn_fs_x/changes.c b/subversion/libsvn_fs_x/changes.c new file mode 100644 index 0000000..a7d5ee2 --- /dev/null +++ b/subversion/libsvn_fs_x/changes.c @@ -0,0 +1,536 @@ +/* changes.h --- FSX changed paths lists container + * + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + */ + +#include "svn_private_config.h" + +#include "private/svn_packed_data.h" + +#include "changes.h" +#include "string_table.h" +#include "temp_serializer.h" + +/* These flags will be used with the FLAGS field in binary_change_t. + */ + +/* the change contains a text modification */ +#define CHANGE_TEXT_MOD 0x00001 + +/* the change contains a property modification */ +#define CHANGE_PROP_MOD 0x00002 + +/* the last part (rev_id) of node revision ID is a transaction ID */ +#define CHANGE_TXN_NODE 0x00004 + +/* (flags & CHANGE_NODE_MASK) >> CHANGE_NODE_SHIFT extracts the node type */ +#define CHANGE_NODE_SHIFT 0x00003 +#define CHANGE_NODE_MASK 0x00018 + +/* node types according to svn_node_kind_t */ +#define CHANGE_NODE_NONE 0x00000 +#define CHANGE_NODE_FILE 0x00008 +#define CHANGE_NODE_DIR 0x00010 +#define CHANGE_NODE_UNKNOWN 0x00018 + +/* (flags & CHANGE_KIND_MASK) >> CHANGE_KIND_SHIFT extracts the change type */ +#define CHANGE_KIND_SHIFT 0x00005 +#define CHANGE_KIND_MASK 0x000E0 + +/* node types according to svn_fs_path_change_kind_t */ +#define CHANGE_KIND_MODIFY 0x00000 +#define CHANGE_KIND_ADD 0x00020 +#define CHANGE_KIND_DELETE 0x00040 +#define CHANGE_KIND_REPLACE 0x00060 +#define CHANGE_KIND_RESET 0x00080 +#define CHANGE_KIND_MOVE 0x000A0 +#define CHANGE_KIND_MOVEREPLACE 0x000C0 + +/* Our internal representation of a change */ +typedef struct binary_change_t +{ + /* define the kind of change and what specific information is present */ + int flags; + + /* Path of the change. */ + apr_size_t path; + + /* copy-from information. + * Not present if COPYFROM_REV is SVN_INVALID_REVNUM. */ + svn_revnum_t copyfrom_rev; + apr_size_t copyfrom_path; + + /* Relevant parts of the node revision ID of the change. + * Empty, if REV_ID is not "used". */ + svn_fs_x__id_t noderev_id; + +} binary_change_t; + +/* The actual container object. Change lists are concatenated into CHANGES + * and and their begins and ends are stored in OFFSETS. + */ +struct svn_fs_x__changes_t +{ + /* The paths - either in 'builder' mode or finalized mode. + * The respective other pointer will be NULL. */ + string_table_builder_t *builder; + string_table_t *paths; + + /* All changes of all change lists concatenated. + * Array elements are binary_change_t.structs (not pointer!) */ + apr_array_header_t *changes; + + /* [Offsets[index] .. Offsets[index+1]) is the range in CHANGES that + * forms the contents of change list INDEX. */ + apr_array_header_t *offsets; +}; + +/* Create and return a new container object, allocated in RESULT_POOL with + * an initial capacity of INITIAL_COUNT changes. The PATH and BUILDER + * members must be initialized by the caller afterwards. + */ +static svn_fs_x__changes_t * +changes_create_body(apr_size_t initial_count, + apr_pool_t *result_pool) +{ + svn_fs_x__changes_t *changes = apr_pcalloc(result_pool, sizeof(*changes)); + + changes->changes = apr_array_make(result_pool, (int)initial_count, + sizeof(binary_change_t)); + changes->offsets = apr_array_make(result_pool, 16, sizeof(int)); + APR_ARRAY_PUSH(changes->offsets, int) = 0; + + return changes; +} + +svn_fs_x__changes_t * +svn_fs_x__changes_create(apr_size_t initial_count, + apr_pool_t *result_pool) +{ + svn_fs_x__changes_t *changes = changes_create_body(initial_count, + result_pool); + changes->builder = svn_fs_x__string_table_builder_create(result_pool); + + return changes; +} + +/* Add CHANGE to the latest change list in CHANGES. + */ +static svn_error_t * +append_change(svn_fs_x__changes_t *changes, + svn_fs_x__change_t *change) +{ + binary_change_t binary_change = { 0 }; + svn_boolean_t is_txn_id; + + /* CHANGE must be sufficiently complete */ + SVN_ERR_ASSERT(change); + SVN_ERR_ASSERT(change->path.data); + + /* Relevant parts of the revision ID of the change. */ + binary_change.noderev_id = change->noderev_id; + + /* define the kind of change and what specific information is present */ + is_txn_id = svn_fs_x__is_txn(binary_change.noderev_id.change_set); + binary_change.flags = (change->text_mod ? CHANGE_TEXT_MOD : 0) + | (change->prop_mod ? CHANGE_PROP_MOD : 0) + | (is_txn_id ? CHANGE_TXN_NODE : 0) + | ((int)change->change_kind << CHANGE_KIND_SHIFT) + | ((int)change->node_kind << CHANGE_NODE_SHIFT); + + /* Path of the change. */ + binary_change.path + = svn_fs_x__string_table_builder_add(changes->builder, + change->path.data, + change->path.len); + + /* copy-from information, if presence is indicated by FLAGS */ + if (SVN_IS_VALID_REVNUM(change->copyfrom_rev)) + { + binary_change.copyfrom_rev = change->copyfrom_rev; + binary_change.copyfrom_path + = svn_fs_x__string_table_builder_add(changes->builder, + change->copyfrom_path, + 0); + } + else + { + binary_change.copyfrom_rev = SVN_INVALID_REVNUM; + binary_change.copyfrom_path = 0; + } + + APR_ARRAY_PUSH(changes->changes, binary_change_t) = binary_change; + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__changes_append_list(apr_size_t *list_index, + svn_fs_x__changes_t *changes, + apr_array_header_t *list) +{ + int i; + + /* CHANGES must be in 'builder' mode */ + SVN_ERR_ASSERT(changes->builder); + SVN_ERR_ASSERT(changes->paths == NULL); + + /* simply append the list and all changes */ + for (i = 0; i < list->nelts; ++i) + append_change(changes, APR_ARRAY_IDX(list, i, svn_fs_x__change_t *)); + + /* terminate the list by storing the next changes offset */ + APR_ARRAY_PUSH(changes->offsets, int) = changes->changes->nelts; + *list_index = (apr_size_t)(changes->offsets->nelts - 2); + + return SVN_NO_ERROR; +} + +apr_size_t +svn_fs_x__changes_estimate_size(const svn_fs_x__changes_t *changes) +{ + /* CHANGES must be in 'builder' mode */ + if (changes->builder == NULL) + return 0; + + /* string table code makes its own prediction, + * changes should be < 10 bytes each, + * some static overhead should be assumed */ + return svn_fs_x__string_table_builder_estimate_size(changes->builder) + + changes->changes->nelts * 10 + + 100; +} + +svn_error_t * +svn_fs_x__changes_get_list(apr_array_header_t **list, + const svn_fs_x__changes_t *changes, + apr_size_t idx, + apr_pool_t *pool) +{ + int first; + int last; + int i; + + /* CHANGES must be in 'finalized' mode */ + SVN_ERR_ASSERT(changes->builder == NULL); + SVN_ERR_ASSERT(changes->paths); + + /* validate index */ + if (idx + 1 >= (apr_size_t)changes->offsets->nelts) + return svn_error_createf(SVN_ERR_FS_CONTAINER_INDEX, NULL, + apr_psprintf(pool, + _("Changes list index %%%s" + " exceeds container size %%d"), + APR_SIZE_T_FMT), + idx, changes->offsets->nelts - 1); + + /* range of changes to return */ + first = APR_ARRAY_IDX(changes->offsets, (int)idx, int); + last = APR_ARRAY_IDX(changes->offsets, (int)idx + 1, int); + + /* construct result */ + *list = apr_array_make(pool, last - first, sizeof(svn_fs_x__change_t*)); + for (i = first; i < last; ++i) + { + const binary_change_t *binary_change + = &APR_ARRAY_IDX(changes->changes, i, binary_change_t); + + /* convert BINARY_CHANGE into a standard FSX svn_fs_x__change_t */ + svn_fs_x__change_t *change = apr_pcalloc(pool, sizeof(*change)); + change->path.data = svn_fs_x__string_table_get(changes->paths, + binary_change->path, + &change->path.len, + pool); + + if (binary_change->noderev_id.change_set != SVN_FS_X__INVALID_CHANGE_SET) + change->noderev_id = binary_change->noderev_id; + + change->change_kind = (svn_fs_path_change_kind_t) + ((binary_change->flags & CHANGE_KIND_MASK) >> CHANGE_KIND_SHIFT); + change->text_mod = (binary_change->flags & CHANGE_TEXT_MOD) != 0; + change->prop_mod = (binary_change->flags & CHANGE_PROP_MOD) != 0; + change->node_kind = (svn_node_kind_t) + ((binary_change->flags & CHANGE_NODE_MASK) >> CHANGE_NODE_SHIFT); + + change->copyfrom_rev = binary_change->copyfrom_rev; + change->copyfrom_known = TRUE; + if (SVN_IS_VALID_REVNUM(binary_change->copyfrom_rev)) + change->copyfrom_path + = svn_fs_x__string_table_get(changes->paths, + binary_change->copyfrom_path, + NULL, + pool); + + /* add it to the result */ + APR_ARRAY_PUSH(*list, svn_fs_x__change_t*) = change; + } + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__write_changes_container(svn_stream_t *stream, + const svn_fs_x__changes_t *changes, + apr_pool_t *scratch_pool) +{ + int i; + + string_table_t *paths = changes->paths + ? changes->paths + : svn_fs_x__string_table_create(changes->builder, + scratch_pool); + + svn_packed__data_root_t *root = svn_packed__data_create_root(scratch_pool); + + /* one top-level stream for each array */ + svn_packed__int_stream_t *offsets_stream + = svn_packed__create_int_stream(root, TRUE, FALSE); + svn_packed__int_stream_t *changes_stream + = svn_packed__create_int_stream(root, FALSE, FALSE); + + /* structure the CHANGES_STREAM such we can extract much of the redundancy + * from the binary_change_t structs */ + svn_packed__create_int_substream(changes_stream, TRUE, FALSE); + svn_packed__create_int_substream(changes_stream, TRUE, FALSE); + svn_packed__create_int_substream(changes_stream, TRUE, TRUE); + svn_packed__create_int_substream(changes_stream, TRUE, FALSE); + svn_packed__create_int_substream(changes_stream, TRUE, TRUE); + svn_packed__create_int_substream(changes_stream, TRUE, FALSE); + + /* serialize offsets array */ + for (i = 0; i < changes->offsets->nelts; ++i) + svn_packed__add_uint(offsets_stream, + APR_ARRAY_IDX(changes->offsets, i, int)); + + /* serialize changes array */ + for (i = 0; i < changes->changes->nelts; ++i) + { + const binary_change_t *change + = &APR_ARRAY_IDX(changes->changes, i, binary_change_t); + + svn_packed__add_uint(changes_stream, change->flags); + svn_packed__add_uint(changes_stream, change->path); + + svn_packed__add_int(changes_stream, change->copyfrom_rev); + svn_packed__add_uint(changes_stream, change->copyfrom_path); + + svn_packed__add_int(changes_stream, change->noderev_id.change_set); + svn_packed__add_uint(changes_stream, change->noderev_id.number); + } + + /* write to disk */ + SVN_ERR(svn_fs_x__write_string_table(stream, paths, scratch_pool)); + SVN_ERR(svn_packed__data_write(stream, root, scratch_pool)); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__read_changes_container(svn_fs_x__changes_t **changes_p, + svn_stream_t *stream, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + apr_size_t i; + apr_size_t count; + + svn_fs_x__changes_t *changes = apr_pcalloc(result_pool, sizeof(*changes)); + + svn_packed__data_root_t *root; + svn_packed__int_stream_t *offsets_stream; + svn_packed__int_stream_t *changes_stream; + + /* read from disk */ + SVN_ERR(svn_fs_x__read_string_table(&changes->paths, stream, + result_pool, scratch_pool)); + + SVN_ERR(svn_packed__data_read(&root, stream, result_pool, scratch_pool)); + offsets_stream = svn_packed__first_int_stream(root); + changes_stream = svn_packed__next_int_stream(offsets_stream); + + /* read offsets array */ + count = svn_packed__int_count(offsets_stream); + changes->offsets = apr_array_make(result_pool, (int)count, sizeof(int)); + for (i = 0; i < count; ++i) + APR_ARRAY_PUSH(changes->offsets, int) + = (int)svn_packed__get_uint(offsets_stream); + + /* read changes array */ + count + = svn_packed__int_count(svn_packed__first_int_substream(changes_stream)); + changes->changes + = apr_array_make(result_pool, (int)count, sizeof(binary_change_t)); + for (i = 0; i < count; ++i) + { + binary_change_t change; + + change.flags = (int)svn_packed__get_uint(changes_stream); + change.path = (apr_size_t)svn_packed__get_uint(changes_stream); + + change.copyfrom_rev = (svn_revnum_t)svn_packed__get_int(changes_stream); + change.copyfrom_path = (apr_size_t)svn_packed__get_uint(changes_stream); + + change.noderev_id.change_set = svn_packed__get_int(changes_stream); + change.noderev_id.number = svn_packed__get_uint(changes_stream); + + APR_ARRAY_PUSH(changes->changes, binary_change_t) = change; + } + + *changes_p = changes; + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__serialize_changes_container(void **data, + apr_size_t *data_len, + void *in, + apr_pool_t *pool) +{ + svn_fs_x__changes_t *changes = in; + svn_stringbuf_t *serialized; + + /* make a guesstimate on the size of the serialized data. Erring on the + * low side will cause the serializer to re-alloc its buffer. */ + apr_size_t size + = changes->changes->elt_size * changes->changes->nelts + + changes->offsets->elt_size * changes->offsets->nelts + + 10 * changes->changes->elt_size + + 100; + + /* serialize array header and all its elements */ + svn_temp_serializer__context_t *context + = svn_temp_serializer__init(changes, sizeof(*changes), size, pool); + + /* serialize sub-structures */ + svn_fs_x__serialize_string_table(context, &changes->paths); + svn_fs_x__serialize_apr_array(context, &changes->changes); + svn_fs_x__serialize_apr_array(context, &changes->offsets); + + /* return the serialized result */ + serialized = svn_temp_serializer__get(context); + + *data = serialized->data; + *data_len = serialized->len; + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__deserialize_changes_container(void **out, + void *data, + apr_size_t data_len, + apr_pool_t *pool) +{ + svn_fs_x__changes_t *changes = (svn_fs_x__changes_t *)data; + + /* de-serialize sub-structures */ + svn_fs_x__deserialize_string_table(changes, &changes->paths); + svn_fs_x__deserialize_apr_array(changes, &changes->changes, pool); + svn_fs_x__deserialize_apr_array(changes, &changes->offsets, pool); + + /* done */ + *out = changes; + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__changes_get_list_func(void **out, + const void *data, + apr_size_t data_len, + void *baton, + apr_pool_t *pool) +{ + int first; + int last; + int i; + apr_array_header_t *list; + + apr_uint32_t idx = *(apr_uint32_t *)baton; + const svn_fs_x__changes_t *container = data; + + /* resolve all the sub-container pointers we need */ + const string_table_t *paths + = svn_temp_deserializer__ptr(container, + (const void *const *)&container->paths); + const apr_array_header_t *serialized_offsets + = svn_temp_deserializer__ptr(container, + (const void *const *)&container->offsets); + const apr_array_header_t *serialized_changes + = svn_temp_deserializer__ptr(container, + (const void *const *)&container->changes); + const int *offsets + = svn_temp_deserializer__ptr(serialized_offsets, + (const void *const *)&serialized_offsets->elts); + const binary_change_t *changes + = svn_temp_deserializer__ptr(serialized_changes, + (const void *const *)&serialized_changes->elts); + + /* validate index */ + if (idx + 1 >= (apr_size_t)serialized_offsets->nelts) + return svn_error_createf(SVN_ERR_FS_CONTAINER_INDEX, NULL, + _("Changes list index %u exceeds container " + "size %d"), + (unsigned)idx, serialized_offsets->nelts - 1); + + /* range of changes to return */ + first = offsets[idx]; + last = offsets[idx+1]; + + /* construct result */ + list = apr_array_make(pool, last - first, sizeof(svn_fs_x__change_t*)); + + for (i = first; i < last; ++i) + { + const binary_change_t *binary_change = &changes[i]; + + /* convert BINARY_CHANGE into a standard FSX svn_fs_x__change_t */ + svn_fs_x__change_t *change = apr_pcalloc(pool, sizeof(*change)); + change->path.data + = svn_fs_x__string_table_get_func(paths, binary_change->path, + &change->path.len, pool); + + change->noderev_id = binary_change->noderev_id; + + change->change_kind = (svn_fs_path_change_kind_t) + ((binary_change->flags & CHANGE_KIND_MASK) >> CHANGE_KIND_SHIFT); + change->text_mod = (binary_change->flags & CHANGE_TEXT_MOD) != 0; + change->prop_mod = (binary_change->flags & CHANGE_PROP_MOD) != 0; + change->node_kind = (svn_node_kind_t) + ((binary_change->flags & CHANGE_NODE_MASK) >> CHANGE_NODE_SHIFT); + + change->copyfrom_rev = binary_change->copyfrom_rev; + change->copyfrom_known = TRUE; + if (SVN_IS_VALID_REVNUM(binary_change->copyfrom_rev)) + change->copyfrom_path + = svn_fs_x__string_table_get_func(paths, + binary_change->copyfrom_path, + NULL, + pool); + + /* add it to the result */ + APR_ARRAY_PUSH(list, svn_fs_x__change_t*) = change; + } + + *out = list; + + return SVN_NO_ERROR; +} |