diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-08-05 16:22:51 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-08-05 16:22:51 +0000 |
commit | cf46733632c7279a9fd0fe6ce26f9185a4ae82a9 (patch) | |
tree | da27775a2161723ef342e91af41a8b51fedef405 /subversion/libsvn_subr/packed_data.c | |
parent | bb0ef45f7c46b0ae221b26265ef98a768c33f820 (diff) | |
download | subversion-tarball-master.tar.gz |
subversion-1.9.7HEADsubversion-1.9.7master
Diffstat (limited to 'subversion/libsvn_subr/packed_data.c')
-rw-r--r-- | subversion/libsvn_subr/packed_data.c | 1099 |
1 files changed, 1099 insertions, 0 deletions
diff --git a/subversion/libsvn_subr/packed_data.c b/subversion/libsvn_subr/packed_data.c new file mode 100644 index 0000000..27651ff --- /dev/null +++ b/subversion/libsvn_subr/packed_data.c @@ -0,0 +1,1099 @@ +/* packed_data.c : implement the packed binary stream data structure + * + * ==================================================================== + * 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 <apr_tables.h> + +#include "svn_string.h" +#include "svn_sorts.h" +#include "private/svn_string_private.h" +#include "private/svn_subr_private.h" +#include "private/svn_delta_private.h" +#include "private/svn_packed_data.h" + +#include "svn_private_config.h" + + + +/* Private int stream data referenced by svn_packed__int_stream_t. + */ +typedef struct packed_int_private_t +{ + /* First sub-stream, if any. NULL otherwise. */ + svn_packed__int_stream_t *first_substream; + + /* Last sub-stream, if any. NULL otherwise. */ + svn_packed__int_stream_t *last_substream; + + /* Current sub-stream to read from / write to, if any. NULL otherwise. + This will be initialized to FIRST_SUBSTREAM and then advanced in a + round-robin scheme after each number being read. */ + svn_packed__int_stream_t *current_substream; + + /* Number of sub-streams. */ + apr_size_t substream_count; + + /* Next (sibling) integer stream. If this is the last one, points to + the first in the list (i.e. this forms a ring list). Never NULL. */ + svn_packed__int_stream_t *next; + + /* 7b/8b encoded integer values (previously diff'ed and sign-handled, + if indicated by the flags below). The contents are disjoint from + the unparsed number buffer. May be NULL while not written to. */ + svn_stringbuf_t *packed; + + /* Initialized to 0. Latest value written to / read from PACKED. + Undefined if DIFF is FALSE. */ + apr_uint64_t last_value; + + /* Deltify data before storing it in PACKED. */ + svn_boolean_t diff; + + /* Numbers are likely to contain negative values with small absolutes. + If TRUE, store the signed bit in LSB before encoding. */ + svn_boolean_t is_signed; + + /* Number of integers in this stream. */ + apr_size_t item_count; + + /* TRUE for the last stream in a list of siblings. */ + svn_boolean_t is_last; + + /* Pool to use for allocations. */ + apr_pool_t *pool; +} packed_int_private_t; + +/* A byte sequence stream. Please note that NEXT is defined different + * from the NEXT member in integer streams. + */ +struct svn_packed__byte_stream_t +{ + /* First sub-stream, if any. NULL otherwise. */ + svn_packed__byte_stream_t *first_substream; + + /* Last sub-stream, if any. NULL otherwise. */ + svn_packed__byte_stream_t *last_substream; + + /* Next (sibling) byte sequence stream, if any. NULL otherwise. */ + svn_packed__byte_stream_t *next; + + /* Stream to store the sequence lengths. */ + svn_packed__int_stream_t *lengths_stream; + + /* It's index (relative to its parent). */ + apr_size_t lengths_stream_index; + + /* Concatenated byte sequences. */ + svn_stringbuf_t *packed; + + /* Pool to use for allocations. */ + apr_pool_t *pool; +}; + +/* The serialization root object. It references the top-level streams. + */ +struct svn_packed__data_root_t +{ + /* First top-level integer stream, if any. NULL otherwise. */ + svn_packed__int_stream_t *first_int_stream; + + /* Last top-level integer stream, if any. NULL otherwise. */ + svn_packed__int_stream_t *last_int_stream; + + /* Number of top-level integer streams. */ + apr_size_t int_stream_count; + + /* First top-level byte sequence stream, if any. NULL otherwise. */ + svn_packed__byte_stream_t *first_byte_stream; + + /* Last top-level byte sequence stream, if any. NULL otherwise. */ + svn_packed__byte_stream_t *last_byte_stream; + + /* Number of top-level byte sequence streams. */ + apr_size_t byte_stream_count; + + /* Pool to use for allocations. */ + apr_pool_t *pool; +}; + +/* Write access. */ + +svn_packed__data_root_t * +svn_packed__data_create_root(apr_pool_t *pool) +{ + svn_packed__data_root_t *root = apr_pcalloc(pool, sizeof(*root)); + root->pool = pool; + + return root; +} + +svn_packed__int_stream_t * +svn_packed__create_int_stream(svn_packed__data_root_t *root, + svn_boolean_t diff, + svn_boolean_t signed_ints) +{ + /* allocate and initialize the stream node */ + packed_int_private_t *private_data + = apr_pcalloc(root->pool, sizeof(*private_data)); + svn_packed__int_stream_t *stream + = apr_palloc(root->pool, sizeof(*stream)); + + private_data->diff = diff; + private_data->is_signed = signed_ints; + private_data->is_last = TRUE; + private_data->pool = root->pool; + + stream->buffer_used = 0; + stream->private_data = private_data; + + /* maintain the ring list */ + if (root->last_int_stream) + { + packed_int_private_t *previous_private_data + = root->last_int_stream->private_data; + previous_private_data->next = stream; + previous_private_data->is_last = FALSE; + } + else + { + root->first_int_stream = stream; + } + + root->last_int_stream = stream; + root->int_stream_count++; + + return stream; +} + +svn_packed__int_stream_t * +svn_packed__create_int_substream(svn_packed__int_stream_t *parent, + svn_boolean_t diff, + svn_boolean_t signed_ints) +{ + packed_int_private_t *parent_private = parent->private_data; + + /* allocate and initialize the stream node */ + packed_int_private_t *private_data + = apr_pcalloc(parent_private->pool, sizeof(*private_data)); + svn_packed__int_stream_t *stream + = apr_palloc(parent_private->pool, sizeof(*stream)); + + private_data->diff = diff; + private_data->is_signed = signed_ints; + private_data->is_last = TRUE; + private_data->pool = parent_private->pool; + + stream->buffer_used = 0; + stream->private_data = private_data; + + /* maintain the ring list */ + if (parent_private->last_substream) + { + packed_int_private_t *previous_private_data + = parent_private->last_substream->private_data; + previous_private_data->next = stream; + previous_private_data->is_last = FALSE; + } + else + { + parent_private->first_substream = stream; + parent_private->current_substream = stream; + } + + parent_private->last_substream = stream; + parent_private->substream_count++; + private_data->next = parent_private->first_substream; + + return stream; +} + +/* Returns a new top-level byte sequence stream for ROOT but does not + * initialize the LENGTH_STREAM member. + */ +static svn_packed__byte_stream_t * +create_bytes_stream_body(svn_packed__data_root_t *root) +{ + svn_packed__byte_stream_t *stream + = apr_pcalloc(root->pool, sizeof(*stream)); + + stream->packed = svn_stringbuf_create_empty(root->pool); + + if (root->last_byte_stream) + root->last_byte_stream->next = stream; + else + root->first_byte_stream = stream; + + root->last_byte_stream = stream; + root->byte_stream_count++; + + return stream; +} + +svn_packed__byte_stream_t * +svn_packed__create_bytes_stream(svn_packed__data_root_t *root) +{ + svn_packed__byte_stream_t *stream + = create_bytes_stream_body(root); + + stream->lengths_stream_index = root->int_stream_count; + stream->lengths_stream = svn_packed__create_int_stream(root, FALSE, FALSE); + + return stream; +} + +/* Write the 7b/8b representation of VALUE into BUFFER. BUFFER must + * provide at least 10 bytes space. + * Returns the first position behind the written data. + */ +static unsigned char * +write_packed_uint_body(unsigned char *buffer, apr_uint64_t value) +{ + while (value >= 0x80) + { + *(buffer++) = (unsigned char)((value % 0x80) + 0x80); + value /= 0x80; + } + + *(buffer++) = (unsigned char)value; + return buffer; +} + +/* Return remapped VALUE. + * + * Due to sign conversion and diff underflow, values close to UINT64_MAX + * are almost as frequent as those close to 0. Remap them such that the + * MSB is stored in the LSB and the remainder stores the absolute distance + * to 0. + * + * This minimizes the absolute value to store in many scenarios. + * Hence, the variable-length representation on disk is shorter, too. + */ +static apr_uint64_t +remap_uint(apr_uint64_t value) +{ + return value & APR_UINT64_C(0x8000000000000000) + ? APR_UINT64_MAX - (2 * value) + : 2 * value; +} + +/* Invert remap_uint. */ +static apr_uint64_t +unmap_uint(apr_uint64_t value) +{ + return value & 1 + ? (APR_UINT64_MAX - value / 2) + : value / 2; +} + +/* Empty the unprocessed integer buffer in STREAM by either pushing the + * data to the sub-streams or writing to the packed data (in case there + * are no sub-streams). + */ +static void +svn_packed__data_flush_buffer(svn_packed__int_stream_t *stream) +{ + packed_int_private_t *private_data = stream->private_data; + apr_size_t i; + + /* if we have sub-streams, push the data down to them */ + if (private_data->current_substream) + for (i = 0; i < stream->buffer_used; ++i) + { + packed_int_private_t *current_private_data + = private_data->current_substream->private_data; + + svn_packed__add_uint(private_data->current_substream, + stream->buffer[i]); + private_data->current_substream = current_private_data->next; + } + else + { + /* pack the numbers into our local PACKED buffer */ + + /* temporary buffer, max 10 bytes required per 7b/8b encoded number */ + unsigned char local_buffer[10 * SVN__PACKED_DATA_BUFFER_SIZE]; + unsigned char *p = local_buffer; + + /* if configured, deltify numbers before packing them. + Since delta may be negative, always use the 'signed' encoding. */ + if (private_data->diff) + { + apr_uint64_t last_value = private_data->last_value; + for (i = 0; i < stream->buffer_used; ++i) + { + apr_uint64_t temp = stream->buffer[i]; + stream->buffer[i] = remap_uint(temp - last_value); + last_value = temp; + } + + private_data->last_value = last_value; + } + + /* if configured and not already done by the deltification above, + transform to 'signed' encoding. Store the sign in the LSB and + the absolute value (-1 for negative values) in the remaining + 63 bits. */ + if (!private_data->diff && private_data->is_signed) + for (i = 0; i < stream->buffer_used; ++i) + stream->buffer[i] = remap_uint(stream->buffer[i]); + + /* auto-create packed data buffer. Give it some reasonable initial + size - just enough for a few tens of values. */ + if (private_data->packed == NULL) + private_data->packed + = svn_stringbuf_create_ensure(256, private_data->pool); + + /* encode numbers into our temp buffer. */ + for (i = 0; i < stream->buffer_used; ++i) + p = write_packed_uint_body(p, stream->buffer[i]); + + /* append them to the final packed data */ + svn_stringbuf_appendbytes(private_data->packed, + (char *)local_buffer, + p - local_buffer); + } + + /* maintain counters */ + private_data->item_count += stream->buffer_used; + stream->buffer_used = 0; +} + +void +svn_packed__add_uint(svn_packed__int_stream_t *stream, + apr_uint64_t value) +{ + stream->buffer[stream->buffer_used] = value; + if (++stream->buffer_used == SVN__PACKED_DATA_BUFFER_SIZE) + svn_packed__data_flush_buffer(stream); +} + +void +svn_packed__add_int(svn_packed__int_stream_t *stream, + apr_int64_t value) +{ + svn_packed__add_uint(stream, (apr_uint64_t)value); +} + +void +svn_packed__add_bytes(svn_packed__byte_stream_t *stream, + const char *data, + apr_size_t len) +{ + svn_packed__add_uint(stream->lengths_stream, len); + svn_stringbuf_appendbytes(stream->packed, data, len); +} + +/* Append the 7b/8b encoded representation of VALUE to PACKED. + */ +static void +write_packed_uint(svn_stringbuf_t* packed, apr_uint64_t value) +{ + if (value < 0x80) + { + svn_stringbuf_appendbyte(packed, (char)value); + } + else + { + unsigned char buffer[10]; + unsigned char *p = write_packed_uint_body(buffer, value); + + svn_stringbuf_appendbytes(packed, (char *)buffer, p - buffer); + } +} + +/* Recursively write the structure (config parameters, sub-streams, data + * sizes) of the STREAM and all its siblings to the TREE_STRUCT buffer. + */ +static void +write_int_stream_structure(svn_stringbuf_t* tree_struct, + svn_packed__int_stream_t* stream) +{ + while (stream) + { + /* store config parameters and number of sub-streams in 1 number */ + packed_int_private_t *private_data = stream->private_data; + write_packed_uint(tree_struct, (private_data->substream_count << 2) + + (private_data->diff ? 1 : 0) + + (private_data->is_signed ? 2 : 0)); + + /* store item count and length their of packed representation */ + svn_packed__data_flush_buffer(stream); + + write_packed_uint(tree_struct, private_data->item_count); + write_packed_uint(tree_struct, private_data->packed + ? private_data->packed->len + : 0); + + /* append all sub-stream structures */ + write_int_stream_structure(tree_struct, private_data->first_substream); + + /* continue with next sibling */ + stream = private_data->is_last ? NULL : private_data->next; + } +} + +/* Recursively write the structure (sub-streams, data sizes) of the STREAM + * and all its siblings to the TREE_STRUCT buffer. + */ +static void +write_byte_stream_structure(svn_stringbuf_t* tree_struct, + svn_packed__byte_stream_t* stream) +{ + /* for this and all siblings */ + for (; stream; stream = stream->next) + { + /* this stream's structure and size */ + write_packed_uint(tree_struct, 0); + write_packed_uint(tree_struct, stream->lengths_stream_index); + write_packed_uint(tree_struct, stream->packed->len); + + /* followed by all its sub-streams */ + write_byte_stream_structure(tree_struct, stream->first_substream); + } +} + +/* Write the 7b/8b encoded representation of VALUE to STREAM. + */ +static svn_error_t * +write_stream_uint(svn_stream_t *stream, + apr_uint64_t value) +{ + unsigned char buffer[10]; + apr_size_t count = write_packed_uint_body(buffer, value) - buffer; + + SVN_ERR(svn_stream_write(stream, (char *)buffer, &count)); + + return SVN_NO_ERROR; +} + +/* Return the total size of all packed data in STREAM, its siblings and + * all sub-streams. To get an accurate value, flush all buffers prior to + * calling this function. + */ +static apr_size_t +packed_int_stream_length(svn_packed__int_stream_t *stream) +{ + packed_int_private_t *private_data = stream->private_data; + apr_size_t result = private_data->packed ? private_data->packed->len : 0; + + stream = private_data->first_substream; + while (stream) + { + private_data = stream->private_data; + result += packed_int_stream_length(stream); + stream = private_data->is_last ? NULL : private_data->next; + } + + return result; +} + +/* Return the total size of all byte sequences data in STREAM, its siblings + * and all sub-streams. + */ +static apr_size_t +packed_byte_stream_length(svn_packed__byte_stream_t *stream) +{ + apr_size_t result = stream->packed->len; + + for (stream = stream->first_substream; stream; stream = stream->next) + result += packed_byte_stream_length(stream); + + return result; +} + +/* Append all packed data in STREAM, its siblings and all sub-streams to + * COMBINED. + */ +static void +append_int_stream(svn_packed__int_stream_t *stream, + svn_stringbuf_t *combined) +{ + packed_int_private_t *private_data = stream->private_data; + if (private_data->packed) + svn_stringbuf_appendstr(combined, private_data->packed); + + stream = private_data->first_substream; + while (stream) + { + private_data = stream->private_data; + append_int_stream(stream, combined); + stream = private_data->is_last ? NULL : private_data->next; + } +} + +/* Append all byte sequences in STREAM, its siblings and all sub-streams + * to COMBINED. + */ +static void +append_byte_stream(svn_packed__byte_stream_t *stream, + svn_stringbuf_t *combined) +{ + svn_stringbuf_appendstr(combined, stream->packed); + + for (stream = stream->first_substream; stream; stream = stream->next) + append_byte_stream(stream, combined); +} + +/* Take the binary data in UNCOMPRESSED, zip it into COMPRESSED and write + * it to STREAM. COMPRESSED simply acts as a re-usable memory buffer. + * Clear all buffers (COMPRESSED, UNCOMPRESSED) at the end of the function. + */ +static svn_error_t * +write_stream_data(svn_stream_t *stream, + svn_stringbuf_t *uncompressed, + svn_stringbuf_t *compressed) +{ + SVN_ERR(svn__compress(uncompressed, + compressed, + SVN_DELTA_COMPRESSION_LEVEL_DEFAULT)); + + SVN_ERR(write_stream_uint(stream, compressed->len)); + SVN_ERR(svn_stream_write(stream, compressed->data, &compressed->len)); + + svn_stringbuf_setempty(uncompressed); + svn_stringbuf_setempty(compressed); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_packed__data_write(svn_stream_t *stream, + svn_packed__data_root_t *root, + apr_pool_t *scratch_pool) +{ + svn_packed__int_stream_t *int_stream; + svn_packed__byte_stream_t *byte_stream; + + /* re-usable data buffers */ + svn_stringbuf_t *compressed + = svn_stringbuf_create_ensure(1024, scratch_pool); + svn_stringbuf_t *uncompressed + = svn_stringbuf_create_ensure(1024, scratch_pool); + + /* write tree structure */ + svn_stringbuf_t *tree_struct + = svn_stringbuf_create_ensure(127, scratch_pool); + + write_packed_uint(tree_struct, root->int_stream_count); + write_int_stream_structure(tree_struct, root->first_int_stream); + + write_packed_uint(tree_struct, root->byte_stream_count); + write_byte_stream_structure(tree_struct, root->first_byte_stream); + + SVN_ERR(write_stream_uint(stream, tree_struct->len)); + SVN_ERR(svn_stream_write(stream, tree_struct->data, &tree_struct->len)); + + /* flatten sub-streams, zip them and write them to disk */ + + for (int_stream = root->first_int_stream; + int_stream; + int_stream = ((packed_int_private_t*)int_stream->private_data)->next) + { + apr_size_t len = packed_int_stream_length(int_stream); + svn_stringbuf_ensure(uncompressed, len); + + append_int_stream(int_stream, uncompressed); + SVN_ERR(write_stream_data(stream, uncompressed, compressed)); + } + + for (byte_stream = root->first_byte_stream; + byte_stream; + byte_stream = byte_stream->next) + { + apr_size_t len = packed_byte_stream_length(byte_stream); + svn_stringbuf_ensure(uncompressed, len); + + append_byte_stream(byte_stream, uncompressed); + SVN_ERR(write_stream_data(stream, uncompressed, compressed)); + } + + return SVN_NO_ERROR; +} + + +/* Read access. */ + +svn_packed__int_stream_t * +svn_packed__first_int_stream(svn_packed__data_root_t *root) +{ + return root->first_int_stream; +} + +svn_packed__byte_stream_t * +svn_packed__first_byte_stream(svn_packed__data_root_t *root) +{ + return root->first_byte_stream; +} + +svn_packed__int_stream_t * +svn_packed__next_int_stream(svn_packed__int_stream_t *stream) +{ + packed_int_private_t *private_data = stream->private_data; + return private_data->is_last ? NULL : private_data->next; +} + +svn_packed__byte_stream_t * +svn_packed__next_byte_stream(svn_packed__byte_stream_t *stream) +{ + return stream->next; +} + +svn_packed__int_stream_t * +svn_packed__first_int_substream(svn_packed__int_stream_t *stream) +{ + packed_int_private_t *private_data = stream->private_data; + return private_data->first_substream; +} + +apr_size_t +svn_packed__int_count(svn_packed__int_stream_t *stream) +{ + packed_int_private_t *private_data = stream->private_data; + return private_data->item_count + stream->buffer_used; +} + +apr_size_t +svn_packed__byte_count(svn_packed__byte_stream_t *stream) +{ + return stream->packed->len; +} + +/* Read one 7b/8b encoded value from *P and return it in *RESULT. Returns + * the first position after the parsed data. + * + * Overflows will be detected in the sense that it will end parsing the + * input but the result is undefined. + */ +static unsigned char * +read_packed_uint_body(unsigned char *p, apr_uint64_t *result) +{ + if (*p < 0x80) + { + *result = *p; + } + else + { + apr_uint64_t shift = 0; + apr_uint64_t value = 0; + while (*p >= 0x80) + { + value += (apr_uint64_t)(*p & 0x7f) << shift; + ++p; + + shift += 7; + if (shift > 64) + { + /* a definite overflow. Note, that numbers of 65 .. 70 + bits will not be detected as an overflow as they don't + threaten to exceed the input buffer. */ + *result = 0; + return p; + } + } + + *result = value + ((apr_uint64_t)*p << shift); + } + + return ++p; +} + +/* Read one 7b/8b encoded value from STREAM and return it in *RESULT. + * + * Overflows will be detected in the sense that it will end parsing the + * input but the result is undefined. + */ +static svn_error_t * +read_stream_uint(svn_stream_t *stream, apr_uint64_t *result) +{ + apr_uint64_t shift = 0; + apr_uint64_t value = 0; + unsigned char c; + + do + { + apr_size_t len = 1; + SVN_ERR(svn_stream_read_full(stream, (char *)&c, &len)); + if (len != 1) + return svn_error_create(SVN_ERR_CORRUPT_PACKED_DATA, NULL, + _("Unexpected end of stream")); + + value += (apr_uint64_t)(c & 0x7f) << shift; + shift += 7; + if (shift > 64) + return svn_error_create(SVN_ERR_CORRUPT_PACKED_DATA, NULL, + _("Integer representation too long")); + } + while (c >= 0x80); + + *result = value; + return SVN_NO_ERROR; +} + +/* Extract and return the next integer from PACKED and make PACKED point + * to the next integer. + */ +static apr_uint64_t +read_packed_uint(svn_stringbuf_t *packed) +{ + apr_uint64_t result = 0; + unsigned char *p = (unsigned char *)packed->data; + apr_size_t read = read_packed_uint_body(p, &result) - p; + + if (read > packed->len) + read = packed->len; + + packed->data += read; + packed->blocksize -= read; + packed->len -= read; + + return result; +} + +/* Ensure that STREAM contains at least one item in its buffer. + */ +static void +svn_packed__data_fill_buffer(svn_packed__int_stream_t *stream) +{ + packed_int_private_t *private_data = stream->private_data; + apr_size_t i; + apr_size_t end = MIN(SVN__PACKED_DATA_BUFFER_SIZE, + private_data->item_count); + + /* in case, some user calls us explicitly without a good reason ... */ + if (stream->buffer_used) + return; + + /* can we get data from the sub-streams or do we have to decode it from + our local packed container? */ + if (private_data->current_substream) + for (i = end; i > 0; --i) + { + packed_int_private_t *current_private_data + = private_data->current_substream->private_data; + stream->buffer[i-1] + = svn_packed__get_uint(private_data->current_substream); + private_data->current_substream = current_private_data->next; + } + else + { + /* use this local buffer only if the packed data is shorter than this. + The goal is that read_packed_uint_body doesn't need check for + overflows. */ + unsigned char local_buffer[10 * SVN__PACKED_DATA_BUFFER_SIZE]; + unsigned char *p; + unsigned char *start; + apr_size_t packed_read; + + if (private_data->packed->len < sizeof(local_buffer)) + { + apr_size_t trail = sizeof(local_buffer) - private_data->packed->len; + memcpy(local_buffer, + private_data->packed->data, + private_data->packed->len); + memset(local_buffer + private_data->packed->len, 0, MIN(trail, end)); + + p = local_buffer; + } + else + p = (unsigned char *)private_data->packed->data; + + /* unpack numbers */ + start = p; + for (i = end; i > 0; --i) + p = read_packed_uint_body(p, &stream->buffer[i-1]); + + /* adjust remaining packed data buffer */ + packed_read = p - start; + private_data->packed->data += packed_read; + private_data->packed->len -= packed_read; + private_data->packed->blocksize -= packed_read; + + /* undeltify numbers, if configured */ + if (private_data->diff) + { + apr_uint64_t last_value = private_data->last_value; + for (i = end; i > 0; --i) + { + last_value += unmap_uint(stream->buffer[i-1]); + stream->buffer[i-1] = last_value; + } + + private_data->last_value = last_value; + } + + /* handle signed values, if configured and not handled already */ + if (!private_data->diff && private_data->is_signed) + for (i = 0; i < end; ++i) + stream->buffer[i] = unmap_uint(stream->buffer[i]); + } + + stream->buffer_used = end; + private_data->item_count -= end; +} + +apr_uint64_t +svn_packed__get_uint(svn_packed__int_stream_t *stream) +{ + if (stream->buffer_used == 0) + svn_packed__data_fill_buffer(stream); + + return stream->buffer_used ? stream->buffer[--stream->buffer_used] : 0; +} + +apr_int64_t +svn_packed__get_int(svn_packed__int_stream_t *stream) +{ + return (apr_int64_t)svn_packed__get_uint(stream); +} + +const char * +svn_packed__get_bytes(svn_packed__byte_stream_t *stream, + apr_size_t *len) +{ + const char *result = stream->packed->data; + apr_size_t count = (apr_size_t)svn_packed__get_uint(stream->lengths_stream); + + if (count > stream->packed->len) + count = stream->packed->len; + + /* advance packed buffer */ + stream->packed->data += count; + stream->packed->len -= count; + stream->packed->blocksize -= count; + + *len = count; + return result; +} + +/* Read the integer stream structure and recreate it in STREAM, including + * sub-streams, from TREE_STRUCT. + */ +static void +read_int_stream_structure(svn_stringbuf_t *tree_struct, + svn_packed__int_stream_t *stream) +{ + packed_int_private_t *private_data = stream->private_data; + apr_uint64_t value = read_packed_uint(tree_struct); + apr_size_t substream_count; + apr_size_t i; + + /* extract local parameters */ + private_data->diff = (value & 1) != 0; + private_data->is_signed = (value & 2) != 0; + substream_count = (apr_size_t)(value >> 2); + + /* read item count & packed size; allocate packed data buffer */ + private_data->item_count = (apr_size_t)read_packed_uint(tree_struct); + value = read_packed_uint(tree_struct); + if (value) + { + private_data->packed = svn_stringbuf_create_ensure((apr_size_t)value, + private_data->pool); + private_data->packed->len = (apr_size_t)value; + } + + /* add sub-streams and read their config, too */ + for (i = 0; i < substream_count; ++i) + read_int_stream_structure(tree_struct, + svn_packed__create_int_substream(stream, + FALSE, + FALSE)); +} + +/* Read the integer stream structure and recreate it in STREAM, including + * sub-streams, from TREE_STRUCT. FIRST_INT_STREAM is the integer stream + * that would correspond to lengths_stream_index 0. + */ +static void +read_byte_stream_structure(svn_stringbuf_t *tree_struct, + svn_packed__byte_stream_t *stream, + svn_packed__int_stream_t *first_int_stream) +{ + apr_size_t lengths_stream_index; + apr_size_t packed_size; + apr_size_t i; + + /* read parameters from the TREE_STRUCT buffer */ + (void) (apr_size_t)read_packed_uint(tree_struct); /* discard first uint */ + lengths_stream_index = (apr_size_t)read_packed_uint(tree_struct); + packed_size = (apr_size_t)read_packed_uint(tree_struct); + + /* allocate byte sequence buffer size */ + svn_stringbuf_ensure(stream->packed, packed_size); + stream->packed->len = packed_size; + + /* navigate to the (already existing) lengths_stream */ + stream->lengths_stream_index = lengths_stream_index; + stream->lengths_stream = first_int_stream; + for (i = 0; i < lengths_stream_index; ++i) + { + packed_int_private_t *length_private + = stream->lengths_stream->private_data; + stream->lengths_stream = length_private->next; + } +} + +/* Read a compressed block from STREAM and uncompress it into UNCOMPRESSED. + * UNCOMPRESSED_LEN is the expected size of the stream. COMPRESSED is a + * re-used buffer for temporary data. + */ +static svn_error_t * +read_stream_data(svn_stream_t *stream, + apr_size_t uncompressed_len, + svn_stringbuf_t *uncompressed, + svn_stringbuf_t *compressed) +{ + apr_uint64_t len; + apr_size_t compressed_len; + + SVN_ERR(read_stream_uint(stream, &len)); + compressed_len = (apr_size_t)len; + + svn_stringbuf_ensure(compressed, compressed_len); + compressed->len = compressed_len; + SVN_ERR(svn_stream_read_full(stream, compressed->data, &compressed->len)); + compressed->data[compressed_len] = '\0'; + + SVN_ERR(svn__decompress(compressed, uncompressed, uncompressed_len)); + + return SVN_NO_ERROR; +} + +/* Read the packed contents from COMBINED, starting at *OFFSET and store + * it in STREAM. Update *OFFSET to point to the next stream's data and + * continue with the sub-streams. + */ +static void +unflatten_int_stream(svn_packed__int_stream_t *stream, + svn_stringbuf_t *combined, + apr_size_t *offset) +{ + packed_int_private_t *private_data = stream->private_data; + if (private_data->packed) + { + memcpy(private_data->packed->data, + combined->data + *offset, + private_data->packed->len); + + private_data->packed->data[private_data->packed->len] = '\0'; + *offset += private_data->packed->len; + } + + stream = private_data->first_substream; + while (stream) + { + private_data = stream->private_data; + unflatten_int_stream(stream, combined, offset); + stream = private_data->is_last ? NULL : private_data->next; + } +} + +/* Read the packed contents from COMBINED, starting at *OFFSET and store + * it in STREAM. Update *OFFSET to point to the next stream's data and + * continue with the sub-streams. + */ +static void +unflatten_byte_stream(svn_packed__byte_stream_t *stream, + svn_stringbuf_t *combined, + apr_size_t *offset) +{ + memcpy(stream->packed->data, + combined->data + *offset, + stream->packed->len); + stream->packed->data[stream->packed->len] = '\0'; + + *offset += stream->packed->len; + for (stream = stream->first_substream; stream; stream = stream->next) + unflatten_byte_stream(stream, combined, offset); +} + +svn_error_t * +svn_packed__data_read(svn_packed__data_root_t **root_p, + svn_stream_t *stream, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + apr_uint64_t i; + apr_uint64_t count; + + svn_packed__int_stream_t *int_stream; + svn_packed__byte_stream_t *byte_stream; + svn_packed__data_root_t *root = svn_packed__data_create_root(result_pool); + + svn_stringbuf_t *compressed + = svn_stringbuf_create_ensure(1024, scratch_pool); + svn_stringbuf_t *uncompressed + = svn_stringbuf_create_ensure(1024, scratch_pool); + + /* read tree structure */ + + apr_uint64_t tree_struct_size; + svn_stringbuf_t *tree_struct; + + SVN_ERR(read_stream_uint(stream, &tree_struct_size)); + tree_struct + = svn_stringbuf_create_ensure((apr_size_t)tree_struct_size, scratch_pool); + tree_struct->len = (apr_size_t)tree_struct_size; + + SVN_ERR(svn_stream_read_full(stream, tree_struct->data, &tree_struct->len)); + tree_struct->data[tree_struct->len] = '\0'; + + /* reconstruct tree structure */ + + count = read_packed_uint(tree_struct); + for (i = 0; i < count; ++i) + read_int_stream_structure(tree_struct, + svn_packed__create_int_stream(root, FALSE, + FALSE)); + + count = read_packed_uint(tree_struct); + for (i = 0; i < count; ++i) + read_byte_stream_structure(tree_struct, + create_bytes_stream_body(root), + root->first_int_stream); + + /* read sub-stream data from disk, unzip it and buffer it */ + + for (int_stream = root->first_int_stream; + int_stream; + int_stream = ((packed_int_private_t*)int_stream->private_data)->next) + { + apr_size_t offset = 0; + SVN_ERR(read_stream_data(stream, + packed_int_stream_length(int_stream), + uncompressed, compressed)); + unflatten_int_stream(int_stream, uncompressed, &offset); + } + + for (byte_stream = root->first_byte_stream; + byte_stream; + byte_stream = byte_stream->next) + { + apr_size_t offset = 0; + SVN_ERR(read_stream_data(stream, + packed_byte_stream_length(byte_stream), + uncompressed, compressed)); + unflatten_byte_stream(byte_stream, uncompressed, &offset); + } + + *root_p = root; + return SVN_NO_ERROR; +} |