1 /* string_table.c : operations on string tables
2 *
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
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
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
19 * under the License.
20 * ====================================================================
21 */
22
23 #include <assert.h>
24 #include <string.h>
25 #include <apr_tables.h>
26
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"
34
35
36
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))
44
45
46 typedef struct builder_string_t
47 {
48 svn_string_t string;
49 int position;
50 apr_size_t depth;
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;
57 } builder_string_t;
58
59 typedef struct builder_table_t
60 {
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;
69 } builder_table_t;
70
71 struct string_table_builder_t
72 {
73 apr_pool_t *pool;
74 apr_array_header_t *tables;
75 };
76
77 typedef struct string_header_t
78 {
79 apr_uint16_t head_string;
80 apr_uint16_t head_length;
81 apr_uint16_t tail_start;
82 apr_uint16_t tail_length;
83 } string_header_t;
84
85 typedef struct string_sub_table_t
86 {
87 const char *data;
88 apr_size_t data_size;
89
90 string_header_t *short_strings;
91 apr_size_t short_string_count;
92
93 svn_string_t *long_strings;
94 apr_size_t long_string_count;
95 } string_sub_table_t;
96
97 struct string_table_t
98 {
99 apr_size_t size;
100 string_sub_table_t *sub_tables;
101 };
102
103
104 /* Accessing ID Pieces. */
105
106 static builder_table_t *
add_table(string_table_builder_t * builder)107 add_table(string_table_builder_t *builder)
108 {
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);
117
118 APR_ARRAY_PUSH(builder->tables, builder_table_t *) = table;
119
120 return table;
121 }
122
123 string_table_builder_t *
svn_fs_x__string_table_builder_create(apr_pool_t * result_pool)124 svn_fs_x__string_table_builder_create(apr_pool_t *result_pool)
125 {
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 *));
129
130 add_table(result);
131
132 return result;
133 }
134
135 static void
balance(builder_table_t * table,builder_string_t ** parent,builder_string_t * node)136 balance(builder_table_t *table,
137 builder_string_t **parent,
138 builder_string_t *node)
139 {
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;
142
143 if (left_height > right_height + 1)
144 {
145 builder_string_t *temp = node->left->right;
146 node->left->right = node;
147 *parent = node->left;
148 node->left = temp;
149
150 --left_height;
151 }
152 else if (left_height + 1 < right_height)
153 {
154 builder_string_t *temp = node->right->left;
155 *parent = node->right;
156 node->right->left = node;
157 node->right = temp;
158
159 --right_height;
160 }
161
162 node->depth = MAX(left_height, right_height);
163 }
164
165 static apr_uint16_t
match_length(const svn_string_t * lhs,const svn_string_t * rhs)166 match_length(const svn_string_t *lhs,
167 const svn_string_t *rhs)
168 {
169 apr_size_t len = MIN(lhs->len, rhs->len);
170 return (apr_uint16_t)svn_cstring__match_length(lhs->data, rhs->data, len);
171 }
172
173 static apr_uint16_t
insert_string(builder_table_t * table,builder_string_t ** parent,builder_string_t * to_insert)174 insert_string(builder_table_t *table,
175 builder_string_t **parent,
176 builder_string_t *to_insert)
177 {
178 apr_uint16_t result;
179 builder_string_t *current = *parent;
180 int diff = strcmp(current->string.data, to_insert->string.data);
181 if (diff == 0)
182 {
183 apr_array_pop(table->short_strings);
184 return current->position;
185 }
186
187 if (diff < 0)
188 {
189 if (current->left == NULL)
190 {
191 current->left = to_insert;
192
193 to_insert->previous = current->previous;
194 to_insert->next = current;
195
196 if (to_insert->previous == NULL)
197 {
198 table->first = to_insert;
199 }
200 else
201 {
202 builder_string_t *previous = to_insert->previous;
203 to_insert->previous_match_len
204 = match_length(&previous->string, &to_insert->string);
205
206 previous->next = to_insert;
207 previous->next_match_len = to_insert->previous_match_len;
208 }
209
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;
214
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;
218 else
219 table->max_data_size += MIN(to_insert->previous_match_len,
220 to_insert->next_match_len);
221
222 return to_insert->position;
223 }
224 else
225 result = insert_string(table, ¤t->left, to_insert);
226 }
227 else
228 {
229 if (current->right == NULL)
230 {
231 current->right = to_insert;
232
233 to_insert->next = current->next;
234 to_insert->previous = current;
235
236 if (to_insert->next == NULL)
237 {
238 table->last = to_insert;
239 }
240 else
241 {
242 builder_string_t *next = to_insert->next;
243 to_insert->next_match_len
244 = match_length(&next->string, &to_insert->string);
245
246 next->previous = to_insert;
247 next->previous_match_len = to_insert->next_match_len;
248 }
249
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;
254
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;
258 else
259 table->max_data_size += MIN(to_insert->previous_match_len,
260 to_insert->next_match_len);
261
262 return to_insert->position;
263 }
264 else
265 result = insert_string(table, ¤t->right, to_insert);
266 }
267
268 balance(table, parent, current);
269 return result;
270 }
271
272 apr_size_t
svn_fs_x__string_table_builder_add(string_table_builder_t * builder,const char * string,apr_size_t len)273 svn_fs_x__string_table_builder_add(string_table_builder_t *builder,
274 const char *string,
275 apr_size_t len)
276 {
277 apr_size_t result;
278 builder_table_t *table = APR_ARRAY_IDX(builder->tables,
279 builder->tables->nelts - 1,
280 builder_table_t *);
281 if (len == 0)
282 len = strlen(string);
283
284 string = apr_pstrmemdup(builder->pool, string, len);
285 if (len > MAX_SHORT_STRING_LEN)
286 {
287 void *idx_void;
288 svn_string_t item;
289 item.data = string;
290 item.len = len;
291
292 idx_void = apr_hash_get(table->long_string_dict, string, len);
293 result = (apr_uintptr_t)idx_void;
294 if (result)
295 return result - 1
296 + LONG_STRING_MASK
297 + (((apr_size_t)builder->tables->nelts - 1) << TABLE_SHIFT);
298
299 if (table->long_strings->nelts == MAX_STRINGS_PER_TABLE)
300 table = add_table(builder);
301
302 result = table->long_strings->nelts
303 + LONG_STRING_MASK
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);
308
309 table->long_string_size += len;
310 }
311 else
312 {
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;
318
319 if ( table->short_strings->nelts == MAX_STRINGS_PER_TABLE
320 || table->max_data_size < len)
321 table = add_table(builder);
322
323 item->position = table->short_strings->nelts;
324 APR_ARRAY_PUSH(table->short_strings, builder_string_t *) = item;
325
326 if (table->top == NULL)
327 {
328 table->max_data_size -= len;
329 table->top = item;
330 table->first = item;
331 table->last = item;
332
333 result = ((apr_size_t)builder->tables->nelts - 1) << TABLE_SHIFT;
334 }
335 else
336 {
337 result = insert_string(table, &table->top, item)
338 + (((apr_size_t)builder->tables->nelts - 1) << TABLE_SHIFT);
339 }
340 }
341
342 return result;
343 }
344
345 apr_size_t
svn_fs_x__string_table_builder_estimate_size(string_table_builder_t * builder)346 svn_fs_x__string_table_builder_estimate_size(string_table_builder_t *builder)
347 {
348 apr_size_t total = 0;
349 int i;
350
351 for (i = 0; i < builder->tables->nelts; ++i)
352 {
353 builder_table_t *table
354 = APR_ARRAY_IDX(builder->tables, i, builder_table_t*);
355
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
365 + 10;
366
367 total += table_size;
368 }
369
370 /* ZIP compression should give us a 50% reduction.
371 * add some static overhead */
372 return 200 + total / 2;
373
374 }
375
376 static void
create_table(string_sub_table_t * target,builder_table_t * source,apr_pool_t * result_pool,apr_pool_t * scratch_pool)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)
381 {
382 int i = 0;
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,
386 scratch_pool);
387
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)
394 {
395 const builder_string_t *string
396 = APR_ARRAY_IDX(source->short_strings, i, const builder_string_t *);
397
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;
402
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. */
406 if (head_length)
407 {
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;
412 }
413 else
414 entry->head_string = 0;
415
416 /* head & tail length are known */
417 entry->head_length = (apr_uint16_t)head_length;
418 entry->tail_length
419 = (apr_uint16_t)(string->string.len - entry->head_length);
420
421 /* try to reuse an existing tail segment */
422 tail_match = apr_hash_get(tails, tail, entry->tail_length);
423 if (tail_match)
424 {
425 entry->tail_start = tail_match->tail_start;
426 }
427 else
428 {
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);
432 }
433 }
434
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)
441 {
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);
445 }
446
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);
451
452 target->data = apr_pmemdup(result_pool, data->data, data->len);
453 target->data_size = data->len;
454 }
455
456 string_table_t *
svn_fs_x__string_table_create(const string_table_builder_t * builder,apr_pool_t * result_pool)457 svn_fs_x__string_table_create(const string_table_builder_t *builder,
458 apr_pool_t *result_pool)
459 {
460 apr_size_t i;
461
462 string_table_t *result = apr_pcalloc(result_pool, sizeof(*result));
463 result->size = (apr_size_t)builder->tables->nelts;
464 result->sub_tables
465 = apr_pcalloc(result_pool, result->size * sizeof(*result->sub_tables));
466
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*),
470 result_pool,
471 builder->pool);
472
473 return result;
474 }
475
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.
479 */
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" };
489 #endif
490
491 static void
table_copy_string(char * buffer,apr_size_t len,const string_sub_table_t * table,string_header_t * header)492 table_copy_string(char *buffer,
493 apr_size_t len,
494 const string_sub_table_t *table,
495 string_header_t *header)
496 {
497 buffer[len] = '\0';
498 do
499 {
500 assert(header->head_length <= len);
501 {
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. */
506 apr_uint64_t mask;
507 apr_size_t to_copy = len - header->head_length;
508 apr_size_t copied = 0;
509
510 const char *source = table->data + header->tail_start;
511 char *target = buffer + header->head_length;
512 len = header->head_length;
513
514 /* copy whole chunks */
515 while (to_copy >= copied + sizeof(apr_uint64_t))
516 {
517 *(apr_uint64_t *)(target + copied)
518 = *(const apr_uint64_t *)(source + copied);
519 copied += sizeof(apr_uint64_t);
520 }
521
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);
530 #else
531 memcpy(buffer + header->head_length,
532 table->data + header->tail_start,
533 len - header->head_length);
534 len = header->head_length;
535 #endif
536 }
537
538 header = &table->short_strings[header->head_string];
539 }
540 while (len);
541 }
542
543 const char*
svn_fs_x__string_table_get(const string_table_t * table,apr_size_t idx,apr_size_t * length,apr_pool_t * result_pool)544 svn_fs_x__string_table_get(const string_table_t *table,
545 apr_size_t idx,
546 apr_size_t *length,
547 apr_pool_t *result_pool)
548 {
549 apr_size_t table_number = idx >> TABLE_SHIFT;
550 apr_size_t sub_index = idx & STRING_INDEX_MASK;
551
552 if (table_number < table->size)
553 {
554 string_sub_table_t *sub_table = &table->sub_tables[table_number];
555 if (idx & LONG_STRING_MASK)
556 {
557 if (sub_index < sub_table->long_string_count)
558 {
559 if (length)
560 *length = sub_table->long_strings[sub_index].len;
561
562 return apr_pstrmemdup(result_pool,
563 sub_table->long_strings[sub_index].data,
564 sub_table->long_strings[sub_index].len);
565 }
566 }
567 else
568 {
569 if (sub_index < sub_table->short_string_count)
570 {
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);
574
575 if (length)
576 *length = len;
577 table_copy_string(result, len, sub_table, header);
578
579 return result;
580 }
581 }
582 }
583
584 return apr_pstrmemdup(result_pool, "", 0);
585 }
586
587 svn_error_t *
svn_fs_x__write_string_table(svn_stream_t * stream,const string_table_t * table,apr_pool_t * scratch_pool)588 svn_fs_x__write_string_table(svn_stream_t *stream,
589 const string_table_t *table,
590 apr_pool_t *scratch_pool)
591 {
592 apr_size_t i, k;
593
594 svn_packed__data_root_t *root = svn_packed__data_create_root(scratch_pool);
595
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);
604
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);
609
610 /* number of sub-tables */
611
612 svn_packed__add_uint(table_sizes, table->size);
613
614 /* all short-string char data sizes */
615
616 for (i = 0; i < table->size; ++i)
617 svn_packed__add_uint(table_sizes,
618 table->sub_tables[i].short_string_count);
619
620 for (i = 0; i < table->size; ++i)
621 svn_packed__add_uint(table_sizes,
622 table->sub_tables[i].long_string_count);
623
624 /* all strings */
625
626 for (i = 0; i < table->size; ++i)
627 {
628 string_sub_table_t *sub_table = &table->sub_tables[i];
629 svn_packed__add_bytes(small_strings_data,
630 sub_table->data,
631 sub_table->data_size);
632
633 for (k = 0; k < sub_table->short_string_count; ++k)
634 {
635 string_header_t *string = &sub_table->short_strings[k];
636
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);
641 }
642
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);
647 }
648
649 /* write to target stream */
650
651 SVN_ERR(svn_packed__data_write(stream, root, scratch_pool));
652
653 return SVN_NO_ERROR;
654 }
655
656 svn_error_t *
svn_fs_x__read_string_table(string_table_t ** table_p,svn_stream_t * stream,apr_pool_t * result_pool,apr_pool_t * 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)
661 {
662 apr_size_t i, k;
663
664 string_table_t *table = apr_palloc(result_pool, sizeof(*table));
665
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;
671
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);
677
678 /* create sub-tables */
679
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));
683
684 /* read short strings */
685
686 for (i = 0; i < table->size; ++i)
687 {
688 string_sub_table_t *sub_table = &table->sub_tables[i];
689
690 sub_table->short_string_count
691 = (apr_size_t)svn_packed__get_uint(table_sizes);
692 if (sub_table->short_string_count)
693 {
694 sub_table->short_strings
695 = apr_pcalloc(result_pool, sub_table->short_string_count
696 * sizeof(*sub_table->short_strings));
697
698 /* read short string headers */
699
700 for (k = 0; k < sub_table->short_string_count; ++k)
701 {
702 string_header_t *string = &sub_table->short_strings[k];
703
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);
708 }
709 }
710
711 sub_table->data = svn_packed__get_bytes(small_strings_data,
712 &sub_table->data_size);
713 }
714
715 /* read long strings */
716
717 for (i = 0; i < table->size; ++i)
718 {
719 /* initialize long string table */
720 string_sub_table_t *sub_table = &table->sub_tables[i];
721
722 sub_table->long_string_count = svn_packed__get_uint(table_sizes);
723 if (sub_table->long_string_count)
724 {
725 sub_table->long_strings
726 = apr_pcalloc(result_pool, sub_table->long_string_count
727 * sizeof(*sub_table->long_strings));
728
729 /* read long strings */
730
731 for (k = 0; k < sub_table->long_string_count; ++k)
732 {
733 svn_string_t *string = &sub_table->long_strings[k];
734 string->data = svn_packed__get_bytes(large_strings,
735 &string->len);
736 string->len--;
737 }
738 }
739 }
740
741 /* done */
742
743 *table_p = table;
744
745 return SVN_NO_ERROR;
746 }
747
748 void
svn_fs_x__serialize_string_table(svn_temp_serializer__context_t * context,string_table_t ** st)749 svn_fs_x__serialize_string_table(svn_temp_serializer__context_t *context,
750 string_table_t **st)
751 {
752 apr_size_t i, k;
753 string_table_t *string_table = *st;
754 if (string_table == NULL)
755 return;
756
757 /* string table struct */
758 svn_temp_serializer__push(context,
759 (const void * const *)st,
760 sizeof(*string_table));
761
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) *
766 string_table->size);
767
768 /* sub-elements of all sub-tables */
769 for (i = 0; i < string_table->size; ++i)
770 {
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));
778
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));
783
784 /* serialize actual long string contents */
785 for (k = 0; k < sub_table->long_string_count; ++k)
786 {
787 svn_string_t *string = &sub_table->long_strings[k];
788 svn_temp_serializer__add_leaf(context,
789 (const void * const *)&string->data,
790 string->len + 1);
791 }
792
793 svn_temp_serializer__pop(context);
794 }
795
796 /* back to the caller's nesting level */
797 svn_temp_serializer__pop(context);
798 svn_temp_serializer__pop(context);
799 }
800
801 void
svn_fs_x__deserialize_string_table(void * buffer,string_table_t ** table)802 svn_fs_x__deserialize_string_table(void *buffer,
803 string_table_t **table)
804 {
805 apr_size_t i, k;
806 string_sub_table_t *sub_tables;
807
808 svn_temp_deserializer__resolve(buffer, (void **)table);
809 if (*table == NULL)
810 return;
811
812 svn_temp_deserializer__resolve(*table, (void **)&(*table)->sub_tables);
813 sub_tables = (*table)->sub_tables;
814 for (i = 0; i < (*table)->size; ++i)
815 {
816 string_sub_table_t *sub_table = sub_tables + i;
817
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);
824
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);
828 }
829 }
830
831 const char*
svn_fs_x__string_table_get_func(const string_table_t * table,apr_size_t idx,apr_size_t * length,apr_pool_t * result_pool)832 svn_fs_x__string_table_get_func(const string_table_t *table,
833 apr_size_t idx,
834 apr_size_t *length,
835 apr_pool_t *result_pool)
836 {
837 apr_size_t table_number = idx >> TABLE_SHIFT;
838 apr_size_t sub_index = idx & STRING_INDEX_MASK;
839
840 if (table_number < table->size)
841 {
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;
847
848 /* pick the right kind of string */
849 if (idx & LONG_STRING_MASK)
850 {
851 if (sub_index < sub_table->long_string_count)
852 {
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);
858 const char *str_data
859 = (const char*)svn_temp_deserializer__ptr(long_strings,
860 (const void *const *)&long_strings[sub_index].data);
861
862 /* return a copy of the char data */
863 if (length)
864 *length = long_strings[sub_index].len;
865
866 return apr_pstrmemdup(result_pool,
867 str_data,
868 long_strings[sub_index].len);
869 }
870 }
871 else
872 {
873 if (sub_index < sub_table->short_string_count)
874 {
875 string_header_t *header;
876 apr_size_t len;
877 char *result;
878
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;
884 table_copy.data
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);
890
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);
895 if (length)
896 *length = len;
897
898 table_copy_string(result, len, &table_copy, header);
899
900 return result;
901 }
902 }
903 }
904
905 return "";
906 }
907