1 /* string_table.c : operations on string tables
3 * ====================================================================
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
20 * ====================================================================
25 #include <apr_tables.h>
27 #include "svn_string.h"
28 #include "svn_sorts.h"
29 #include "private/svn_dep_compat.h"
30 #include "private/svn_string_private.h"
31 #include "private/svn_subr_private.h"
32 #include "private/svn_packed_data.h"
33 #include "string_table.h"
37 #define MAX_DATA_SIZE 0xffff
38 #define MAX_SHORT_STRING_LEN (MAX_DATA_SIZE / 4)
39 #define TABLE_SHIFT 13
40 #define MAX_STRINGS_PER_TABLE (1 << (TABLE_SHIFT - 1))
41 #define LONG_STRING_MASK (1 << (TABLE_SHIFT - 1))
42 #define STRING_INDEX_MASK ((1 << (TABLE_SHIFT - 1)) - 1)
43 #define PADDING (sizeof(apr_uint64_t))
46 typedef struct builder_string_t
51 struct builder_string_t *previous;
52 struct builder_string_t *next;
53 apr_size_t previous_match_len;
54 apr_size_t next_match_len;
55 struct builder_string_t *left;
56 struct builder_string_t *right;
59 typedef struct builder_table_t
61 apr_size_t max_data_size;
62 builder_string_t *top;
63 builder_string_t *first;
64 builder_string_t *last;
65 apr_array_header_t *short_strings;
66 apr_array_header_t *long_strings;
67 apr_hash_t *long_string_dict;
68 apr_size_t long_string_size;
71 struct string_table_builder_t
74 apr_array_header_t *tables;
77 typedef struct string_header_t
79 apr_uint16_t head_string;
80 apr_uint16_t head_length;
81 apr_uint16_t tail_start;
82 apr_uint16_t tail_length;
85 typedef struct string_sub_table_t
90 string_header_t *short_strings;
91 apr_size_t short_string_count;
93 svn_string_t *long_strings;
94 apr_size_t long_string_count;
100 string_sub_table_t *sub_tables;
104 /* Accessing ID Pieces. */
106 static builder_table_t *
107 add_table(string_table_builder_t *builder)
109 builder_table_t *table = apr_pcalloc(builder->pool, sizeof(*table));
110 table->max_data_size = MAX_DATA_SIZE - PADDING; /* ensure there remain a few
111 unused bytes at the end */
112 table->short_strings = apr_array_make(builder->pool, 64,
113 sizeof(builder_string_t *));
114 table->long_strings = apr_array_make(builder->pool, 0,
115 sizeof(svn_string_t));
116 table->long_string_dict = svn_hash__make(builder->pool);
118 APR_ARRAY_PUSH(builder->tables, builder_table_t *) = table;
123 string_table_builder_t *
124 svn_fs_x__string_table_builder_create(apr_pool_t *result_pool)
126 string_table_builder_t *result = apr_palloc(result_pool, sizeof(*result));
127 result->pool = result_pool;
128 result->tables = apr_array_make(result_pool, 1, sizeof(builder_table_t *));
136 balance(builder_table_t *table,
137 builder_string_t **parent,
138 builder_string_t *node)
140 apr_size_t left_height = node->left ? node->left->depth + 1 : 0;
141 apr_size_t right_height = node->right ? node->right->depth + 1 : 0;
143 if (left_height > right_height + 1)
145 builder_string_t *temp = node->left->right;
146 node->left->right = node;
147 *parent = node->left;
152 else if (left_height + 1 < right_height)
154 builder_string_t *temp = node->right->left;
155 *parent = node->right;
156 node->right->left = node;
162 node->depth = MAX(left_height, right_height);
166 match_length(const svn_string_t *lhs,
167 const svn_string_t *rhs)
169 apr_size_t len = MIN(lhs->len, rhs->len);
170 return (apr_uint16_t)svn_cstring__match_length(lhs->data, rhs->data, len);
174 insert_string(builder_table_t *table,
175 builder_string_t **parent,
176 builder_string_t *to_insert)
179 builder_string_t *current = *parent;
180 int diff = strcmp(current->string.data, to_insert->string.data);
183 apr_array_pop(table->short_strings);
184 return current->position;
189 if (current->left == NULL)
191 current->left = to_insert;
193 to_insert->previous = current->previous;
194 to_insert->next = current;
196 if (to_insert->previous == NULL)
198 table->first = to_insert;
202 builder_string_t *previous = to_insert->previous;
203 to_insert->previous_match_len
204 = match_length(&previous->string, &to_insert->string);
206 previous->next = to_insert;
207 previous->next_match_len = to_insert->previous_match_len;
210 current->previous = to_insert;
211 to_insert->next_match_len
212 = match_length(¤t->string, &to_insert->string);
213 current->previous_match_len = to_insert->next_match_len;
215 table->max_data_size -= to_insert->string.len;
216 if (to_insert->previous == NULL)
217 table->max_data_size += to_insert->next_match_len;
219 table->max_data_size += MIN(to_insert->previous_match_len,
220 to_insert->next_match_len);
222 return to_insert->position;
225 result = insert_string(table, ¤t->left, to_insert);
229 if (current->right == NULL)
231 current->right = to_insert;
233 to_insert->next = current->next;
234 to_insert->previous = current;
236 if (to_insert->next == NULL)
238 table->last = to_insert;
242 builder_string_t *next = to_insert->next;
243 to_insert->next_match_len
244 = match_length(&next->string, &to_insert->string);
246 next->previous = to_insert;
247 next->previous_match_len = to_insert->next_match_len;
250 current->next = current->right;
251 to_insert->previous_match_len
252 = match_length(¤t->string, &to_insert->string);
253 current->next_match_len = to_insert->previous_match_len;
255 table->max_data_size -= to_insert->string.len;
256 if (to_insert->next == NULL)
257 table->max_data_size += to_insert->previous_match_len;
259 table->max_data_size += MIN(to_insert->previous_match_len,
260 to_insert->next_match_len);
262 return to_insert->position;
265 result = insert_string(table, ¤t->right, to_insert);
268 balance(table, parent, current);
273 svn_fs_x__string_table_builder_add(string_table_builder_t *builder,
278 builder_table_t *table = APR_ARRAY_IDX(builder->tables,
279 builder->tables->nelts - 1,
282 len = strlen(string);
284 string = apr_pstrmemdup(builder->pool, string, len);
285 if (len > MAX_SHORT_STRING_LEN)
292 idx_void = apr_hash_get(table->long_string_dict, string, len);
293 result = (apr_uintptr_t)idx_void;
297 + (((apr_size_t)builder->tables->nelts - 1) << TABLE_SHIFT);
299 if (table->long_strings->nelts == MAX_STRINGS_PER_TABLE)
300 table = add_table(builder);
302 result = table->long_strings->nelts
304 + (((apr_size_t)builder->tables->nelts - 1) << TABLE_SHIFT);
305 APR_ARRAY_PUSH(table->long_strings, svn_string_t) = item;
306 apr_hash_set(table->long_string_dict, string, len,
307 (void*)(apr_uintptr_t)table->long_strings->nelts);
309 table->long_string_size += len;
313 builder_string_t *item = apr_pcalloc(builder->pool, sizeof(*item));
314 item->string.data = string;
315 item->string.len = len;
316 item->previous_match_len = 0;
317 item->next_match_len = 0;
319 if ( table->short_strings->nelts == MAX_STRINGS_PER_TABLE
320 || table->max_data_size < len)
321 table = add_table(builder);
323 item->position = table->short_strings->nelts;
324 APR_ARRAY_PUSH(table->short_strings, builder_string_t *) = item;
326 if (table->top == NULL)
328 table->max_data_size -= len;
333 result = ((apr_size_t)builder->tables->nelts - 1) << TABLE_SHIFT;
337 result = insert_string(table, &table->top, item)
338 + (((apr_size_t)builder->tables->nelts - 1) << TABLE_SHIFT);
346 svn_fs_x__string_table_builder_estimate_size(string_table_builder_t *builder)
348 apr_size_t total = 0;
351 for (i = 0; i < builder->tables->nelts; ++i)
353 builder_table_t *table
354 = APR_ARRAY_IDX(builder->tables, i, builder_table_t*);
356 /* total number of chars to store,
357 * 8 bytes per short string table entry
358 * 4 bytes per long string table entry
359 * some static overhead */
360 apr_size_t table_size
361 = MAX_DATA_SIZE - table->max_data_size
362 + table->long_string_size
363 + table->short_strings->nelts * 8
364 + table->long_strings->nelts * 4
370 /* ZIP compression should give us a 50% reduction.
371 * add some static overhead */
372 return 200 + total / 2;
377 create_table(string_sub_table_t *target,
378 builder_table_t *source,
379 apr_pool_t *result_pool,
380 apr_pool_t *scratch_pool)
383 apr_hash_t *tails = svn_hash__make(scratch_pool);
384 svn_stringbuf_t *data
385 = svn_stringbuf_create_ensure(MAX_DATA_SIZE - source->max_data_size,
388 /* pack sub-strings */
389 target->short_string_count = (apr_size_t)source->short_strings->nelts;
390 target->short_strings = apr_palloc(result_pool,
391 sizeof(*target->short_strings) *
392 target->short_string_count);
393 for (i = 0; i < source->short_strings->nelts; ++i)
395 const builder_string_t *string
396 = APR_ARRAY_IDX(source->short_strings, i, const builder_string_t *);
398 string_header_t *entry = &target->short_strings[i];
399 const char *tail = string->string.data + string->previous_match_len;
400 string_header_t *tail_match;
401 apr_size_t head_length = string->previous_match_len;
403 /* Minimize the number of strings to visit when reconstructing the
404 string head. So, skip all predecessors that don't contribute to
405 first HEAD_LENGTH chars of our string. */
408 const builder_string_t *furthest_prev = string->previous;
409 while (furthest_prev->previous_match_len >= head_length)
410 furthest_prev = furthest_prev->previous;
411 entry->head_string = furthest_prev->position;
414 entry->head_string = 0;
416 /* head & tail length are known */
417 entry->head_length = (apr_uint16_t)head_length;
419 = (apr_uint16_t)(string->string.len - entry->head_length);
421 /* try to reuse an existing tail segment */
422 tail_match = apr_hash_get(tails, tail, entry->tail_length);
425 entry->tail_start = tail_match->tail_start;
429 entry->tail_start = (apr_uint16_t)data->len;
430 svn_stringbuf_appendbytes(data, tail, entry->tail_length);
431 apr_hash_set(tails, tail, entry->tail_length, entry);
435 /* pack long strings */
436 target->long_string_count = (apr_size_t)source->long_strings->nelts;
437 target->long_strings = apr_palloc(result_pool,
438 sizeof(*target->long_strings) *
439 target->long_string_count);
440 for (i = 0; i < source->long_strings->nelts; ++i)
442 svn_string_t *string = &target->long_strings[i];
443 *string = APR_ARRAY_IDX(source->long_strings, i, svn_string_t);
444 string->data = apr_pstrmemdup(result_pool, string->data, string->len);
447 data->len += PADDING; /* add a few extra bytes at the end of the buffer
448 that we want to keep valid for chunky access */
449 assert(data->len < data->blocksize);
450 memset(data->data + data->len - PADDING, 0, PADDING);
452 target->data = apr_pmemdup(result_pool, data->data, data->len);
453 target->data_size = data->len;
457 svn_fs_x__string_table_create(const string_table_builder_t *builder,
458 apr_pool_t *result_pool)
462 string_table_t *result = apr_pcalloc(result_pool, sizeof(*result));
463 result->size = (apr_size_t)builder->tables->nelts;
465 = apr_pcalloc(result_pool, result->size * sizeof(*result->sub_tables));
467 for (i = 0; i < result->size; ++i)
468 create_table(&result->sub_tables[i],
469 APR_ARRAY_IDX(builder->tables, i, builder_table_t*),
476 /* Masks used by table_copy_string. copy_mask[I] is used if the target
477 content to be preserved starts at byte I within the current chunk.
478 This is used to work around alignment issues.
480 #if SVN_UNALIGNED_ACCESS_IS_OK
481 static const char *copy_masks[8] = { "\xff\xff\xff\xff\xff\xff\xff\xff",
482 "\x00\xff\xff\xff\xff\xff\xff\xff",
483 "\x00\x00\xff\xff\xff\xff\xff\xff",
484 "\x00\x00\x00\xff\xff\xff\xff\xff",
485 "\x00\x00\x00\x00\xff\xff\xff\xff",
486 "\x00\x00\x00\x00\x00\xff\xff\xff",
487 "\x00\x00\x00\x00\x00\x00\xff\xff",
488 "\x00\x00\x00\x00\x00\x00\x00\xff" };
492 table_copy_string(char *buffer,
494 const string_sub_table_t *table,
495 string_header_t *header)
500 assert(header->head_length <= len);
502 #if SVN_UNALIGNED_ACCESS_IS_OK
503 /* the sections that we copy tend to be short but we can copy
504 *all* of it chunky because we made sure that source and target
505 buffer have some extra padding to prevent segfaults. */
507 apr_size_t to_copy = len - header->head_length;
508 apr_size_t copied = 0;
510 const char *source = table->data + header->tail_start;
511 char *target = buffer + header->head_length;
512 len = header->head_length;
514 /* copy whole chunks */
515 while (to_copy >= copied + sizeof(apr_uint64_t))
517 *(apr_uint64_t *)(target + copied)
518 = *(const apr_uint64_t *)(source + copied);
519 copied += sizeof(apr_uint64_t);
522 /* copy the remainder assuming that we have up to 8 extra bytes
523 of addressable buffer on the source and target sides.
524 Now, we simply copy 8 bytes and use a mask to filter & merge
525 old with new data. */
526 mask = *(const apr_uint64_t *)copy_masks[to_copy - copied];
527 *(apr_uint64_t *)(target + copied)
528 = (*(apr_uint64_t *)(target + copied) & mask)
529 | (*(const apr_uint64_t *)(source + copied) & ~mask);
531 memcpy(buffer + header->head_length,
532 table->data + header->tail_start,
533 len - header->head_length);
534 len = header->head_length;
538 header = &table->short_strings[header->head_string];
544 svn_fs_x__string_table_get(const string_table_t *table,
547 apr_pool_t *result_pool)
549 apr_size_t table_number = idx >> TABLE_SHIFT;
550 apr_size_t sub_index = idx & STRING_INDEX_MASK;
552 if (table_number < table->size)
554 string_sub_table_t *sub_table = &table->sub_tables[table_number];
555 if (idx & LONG_STRING_MASK)
557 if (sub_index < sub_table->long_string_count)
560 *length = sub_table->long_strings[sub_index].len;
562 return apr_pstrmemdup(result_pool,
563 sub_table->long_strings[sub_index].data,
564 sub_table->long_strings[sub_index].len);
569 if (sub_index < sub_table->short_string_count)
571 string_header_t *header = sub_table->short_strings + sub_index;
572 apr_size_t len = header->head_length + header->tail_length;
573 char *result = apr_palloc(result_pool, len + PADDING);
577 table_copy_string(result, len, sub_table, header);
584 return apr_pstrmemdup(result_pool, "", 0);
588 svn_fs_x__write_string_table(svn_stream_t *stream,
589 const string_table_t *table,
590 apr_pool_t *scratch_pool)
594 svn_packed__data_root_t *root = svn_packed__data_create_root(scratch_pool);
596 svn_packed__int_stream_t *table_sizes
597 = svn_packed__create_int_stream(root, FALSE, FALSE);
598 svn_packed__int_stream_t *small_strings_headers
599 = svn_packed__create_int_stream(root, FALSE, FALSE);
600 svn_packed__byte_stream_t *large_strings
601 = svn_packed__create_bytes_stream(root);
602 svn_packed__byte_stream_t *small_strings_data
603 = svn_packed__create_bytes_stream(root);
605 svn_packed__create_int_substream(small_strings_headers, TRUE, FALSE);
606 svn_packed__create_int_substream(small_strings_headers, FALSE, FALSE);
607 svn_packed__create_int_substream(small_strings_headers, TRUE, FALSE);
608 svn_packed__create_int_substream(small_strings_headers, FALSE, FALSE);
610 /* number of sub-tables */
612 svn_packed__add_uint(table_sizes, table->size);
614 /* all short-string char data sizes */
616 for (i = 0; i < table->size; ++i)
617 svn_packed__add_uint(table_sizes,
618 table->sub_tables[i].short_string_count);
620 for (i = 0; i < table->size; ++i)
621 svn_packed__add_uint(table_sizes,
622 table->sub_tables[i].long_string_count);
626 for (i = 0; i < table->size; ++i)
628 string_sub_table_t *sub_table = &table->sub_tables[i];
629 svn_packed__add_bytes(small_strings_data,
631 sub_table->data_size);
633 for (k = 0; k < sub_table->short_string_count; ++k)
635 string_header_t *string = &sub_table->short_strings[k];
637 svn_packed__add_uint(small_strings_headers, string->head_string);
638 svn_packed__add_uint(small_strings_headers, string->head_length);
639 svn_packed__add_uint(small_strings_headers, string->tail_start);
640 svn_packed__add_uint(small_strings_headers, string->tail_length);
643 for (k = 0; k < sub_table->long_string_count; ++k)
644 svn_packed__add_bytes(large_strings,
645 sub_table->long_strings[k].data,
646 sub_table->long_strings[k].len + 1);
649 /* write to target stream */
651 SVN_ERR(svn_packed__data_write(stream, root, scratch_pool));
657 svn_fs_x__read_string_table(string_table_t **table_p,
658 svn_stream_t *stream,
659 apr_pool_t *result_pool,
660 apr_pool_t *scratch_pool)
664 string_table_t *table = apr_palloc(result_pool, sizeof(*table));
666 svn_packed__data_root_t *root;
667 svn_packed__int_stream_t *table_sizes;
668 svn_packed__byte_stream_t *large_strings;
669 svn_packed__byte_stream_t *small_strings_data;
670 svn_packed__int_stream_t *headers;
672 SVN_ERR(svn_packed__data_read(&root, stream, result_pool, scratch_pool));
673 table_sizes = svn_packed__first_int_stream(root);
674 headers = svn_packed__next_int_stream(table_sizes);
675 large_strings = svn_packed__first_byte_stream(root);
676 small_strings_data = svn_packed__next_byte_stream(large_strings);
678 /* create sub-tables */
680 table->size = (apr_size_t)svn_packed__get_uint(table_sizes);
681 table->sub_tables = apr_pcalloc(result_pool,
682 table->size * sizeof(*table->sub_tables));
684 /* read short strings */
686 for (i = 0; i < table->size; ++i)
688 string_sub_table_t *sub_table = &table->sub_tables[i];
690 sub_table->short_string_count
691 = (apr_size_t)svn_packed__get_uint(table_sizes);
692 if (sub_table->short_string_count)
694 sub_table->short_strings
695 = apr_pcalloc(result_pool, sub_table->short_string_count
696 * sizeof(*sub_table->short_strings));
698 /* read short string headers */
700 for (k = 0; k < sub_table->short_string_count; ++k)
702 string_header_t *string = &sub_table->short_strings[k];
704 string->head_string = (apr_uint16_t)svn_packed__get_uint(headers);
705 string->head_length = (apr_uint16_t)svn_packed__get_uint(headers);
706 string->tail_start = (apr_uint16_t)svn_packed__get_uint(headers);
707 string->tail_length = (apr_uint16_t)svn_packed__get_uint(headers);
711 sub_table->data = svn_packed__get_bytes(small_strings_data,
712 &sub_table->data_size);
715 /* read long strings */
717 for (i = 0; i < table->size; ++i)
719 /* initialize long string table */
720 string_sub_table_t *sub_table = &table->sub_tables[i];
722 sub_table->long_string_count = svn_packed__get_uint(table_sizes);
723 if (sub_table->long_string_count)
725 sub_table->long_strings
726 = apr_pcalloc(result_pool, sub_table->long_string_count
727 * sizeof(*sub_table->long_strings));
729 /* read long strings */
731 for (k = 0; k < sub_table->long_string_count; ++k)
733 svn_string_t *string = &sub_table->long_strings[k];
734 string->data = svn_packed__get_bytes(large_strings,
749 svn_fs_x__serialize_string_table(svn_temp_serializer__context_t *context,
753 string_table_t *string_table = *st;
754 if (string_table == NULL)
757 /* string table struct */
758 svn_temp_serializer__push(context,
759 (const void * const *)st,
760 sizeof(*string_table));
762 /* sub-table array (all structs in a single memory block) */
763 svn_temp_serializer__push(context,
764 (const void * const *)&string_table->sub_tables,
765 sizeof(*string_table->sub_tables) *
768 /* sub-elements of all sub-tables */
769 for (i = 0; i < string_table->size; ++i)
771 string_sub_table_t *sub_table = &string_table->sub_tables[i];
772 svn_temp_serializer__add_leaf(context,
773 (const void * const *)&sub_table->data,
774 sub_table->data_size);
775 svn_temp_serializer__add_leaf(context,
776 (const void * const *)&sub_table->short_strings,
777 sub_table->short_string_count * sizeof(string_header_t));
779 /* all "long string" instances form a single memory block */
780 svn_temp_serializer__push(context,
781 (const void * const *)&sub_table->long_strings,
782 sub_table->long_string_count * sizeof(svn_string_t));
784 /* serialize actual long string contents */
785 for (k = 0; k < sub_table->long_string_count; ++k)
787 svn_string_t *string = &sub_table->long_strings[k];
788 svn_temp_serializer__add_leaf(context,
789 (const void * const *)&string->data,
793 svn_temp_serializer__pop(context);
796 /* back to the caller's nesting level */
797 svn_temp_serializer__pop(context);
798 svn_temp_serializer__pop(context);
802 svn_fs_x__deserialize_string_table(void *buffer,
803 string_table_t **table)
806 string_sub_table_t *sub_tables;
808 svn_temp_deserializer__resolve(buffer, (void **)table);
812 svn_temp_deserializer__resolve(*table, (void **)&(*table)->sub_tables);
813 sub_tables = (*table)->sub_tables;
814 for (i = 0; i < (*table)->size; ++i)
816 string_sub_table_t *sub_table = sub_tables + i;
818 svn_temp_deserializer__resolve(sub_tables,
819 (void **)&sub_table->data);
820 svn_temp_deserializer__resolve(sub_tables,
821 (void **)&sub_table->short_strings);
822 svn_temp_deserializer__resolve(sub_tables,
823 (void **)&sub_table->long_strings);
825 for (k = 0; k < sub_table->long_string_count; ++k)
826 svn_temp_deserializer__resolve(sub_table->long_strings,
827 (void **)&sub_table->long_strings[k].data);
832 svn_fs_x__string_table_get_func(const string_table_t *table,
835 apr_pool_t *result_pool)
837 apr_size_t table_number = idx >> TABLE_SHIFT;
838 apr_size_t sub_index = idx & STRING_INDEX_MASK;
840 if (table_number < table->size)
842 /* resolve TABLE->SUB_TABLES pointer and select sub-table */
843 string_sub_table_t *sub_tables
844 = (string_sub_table_t *)svn_temp_deserializer__ptr(table,
845 (const void *const *)&table->sub_tables);
846 string_sub_table_t *sub_table = sub_tables + table_number;
848 /* pick the right kind of string */
849 if (idx & LONG_STRING_MASK)
851 if (sub_index < sub_table->long_string_count)
853 /* resolve SUB_TABLE->LONG_STRINGS, select the string we want
854 and resolve the pointer to its char data */
855 svn_string_t *long_strings
856 = (svn_string_t *)svn_temp_deserializer__ptr(sub_table,
857 (const void *const *)&sub_table->long_strings);
859 = (const char*)svn_temp_deserializer__ptr(long_strings,
860 (const void *const *)&long_strings[sub_index].data);
862 /* return a copy of the char data */
864 *length = long_strings[sub_index].len;
866 return apr_pstrmemdup(result_pool,
868 long_strings[sub_index].len);
873 if (sub_index < sub_table->short_string_count)
875 string_header_t *header;
879 /* construct a copy of our sub-table struct with SHORT_STRINGS
880 and DATA pointers resolved. Leave all other pointers as
881 they are. This allows us to use the same code for string
882 reconstruction here as in the non-serialized case. */
883 string_sub_table_t table_copy = *sub_table;
885 = (const char *)svn_temp_deserializer__ptr(sub_tables,
886 (const void *const *)&sub_table->data);
887 table_copy.short_strings
888 = (string_header_t *)svn_temp_deserializer__ptr(sub_tables,
889 (const void *const *)&sub_table->short_strings);
891 /* reconstruct the char data and return it */
892 header = table_copy.short_strings + sub_index;
893 len = header->head_length + header->tail_length;
894 result = apr_palloc(result_pool, len + PADDING);
898 table_copy_string(result, len, &table_copy, header);