1251881Speter/* 2251881Speter * hash.c : dumping and reading hash tables to/from files. 3251881Speter * 4251881Speter * ==================================================================== 5251881Speter * Licensed to the Apache Software Foundation (ASF) under one 6251881Speter * or more contributor license agreements. See the NOTICE file 7251881Speter * distributed with this work for additional information 8251881Speter * regarding copyright ownership. The ASF licenses this file 9251881Speter * to you under the Apache License, Version 2.0 (the 10251881Speter * "License"); you may not use this file except in compliance 11251881Speter * with the License. You may obtain a copy of the License at 12251881Speter * 13251881Speter * http://www.apache.org/licenses/LICENSE-2.0 14251881Speter * 15251881Speter * Unless required by applicable law or agreed to in writing, 16251881Speter * software distributed under the License is distributed on an 17251881Speter * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18251881Speter * KIND, either express or implied. See the License for the 19251881Speter * specific language governing permissions and limitations 20251881Speter * under the License. 21251881Speter * ==================================================================== 22251881Speter */ 23251881Speter 24251881Speter 25251881Speter 26251881Speter#include <stdlib.h> 27251881Speter#include <limits.h> 28251881Speter 29251881Speter#include <apr_version.h> 30251881Speter#include <apr_pools.h> 31251881Speter#include <apr_hash.h> 32251881Speter#include <apr_file_io.h> 33251881Speter 34251881Speter#include "svn_types.h" 35251881Speter#include "svn_string.h" 36251881Speter#include "svn_error.h" 37251881Speter#include "svn_hash.h" 38251881Speter#include "svn_sorts.h" 39251881Speter#include "svn_io.h" 40251881Speter#include "svn_pools.h" 41251881Speter 42251881Speter#include "private/svn_dep_compat.h" 43251881Speter#include "private/svn_subr_private.h" 44251881Speter 45251881Speter#include "svn_private_config.h" 46251881Speter 47251881Speter 48251881Speter 49251881Speter 50251881Speter/* 51251881Speter * The format of a dumped hash table is: 52251881Speter * 53251881Speter * K <nlength> 54251881Speter * name (a string of <nlength> bytes, followed by a newline) 55251881Speter * V <vlength> 56251881Speter * val (a string of <vlength> bytes, followed by a newline) 57251881Speter * [... etc, etc ...] 58251881Speter * END 59251881Speter * 60251881Speter * 61251881Speter * (Yes, there is a newline after END.) 62251881Speter * 63251881Speter * For example: 64251881Speter * 65251881Speter * K 5 66251881Speter * color 67251881Speter * V 3 68251881Speter * red 69251881Speter * K 11 70251881Speter * wine review 71251881Speter * V 376 72251881Speter * A forthright entrance, yet coquettish on the tongue, its deceptively 73251881Speter * fruity exterior hides the warm mahagony undercurrent that is the 74251881Speter * hallmark of Chateau Fraisant-Pitre. Connoisseurs of the region will 75251881Speter * be pleased to note the familiar, subtle hints of mulberries and 76251881Speter * carburator fluid. Its confident finish is marred only by a barely 77251881Speter * detectable suggestion of rancid squid ink. 78251881Speter * K 5 79251881Speter * price 80251881Speter * V 8 81251881Speter * US $6.50 82251881Speter * END 83251881Speter * 84251881Speter */ 85251881Speter 86251881Speter 87251881Speter 88251881Speter 89251881Speter/*** Dumping and loading hash files. */ 90251881Speter 91251881Speter/* Implements svn_hash_read2 and svn_hash_read_incremental. */ 92251881Speterstatic svn_error_t * 93251881Speterhash_read(apr_hash_t *hash, svn_stream_t *stream, const char *terminator, 94251881Speter svn_boolean_t incremental, apr_pool_t *pool) 95251881Speter{ 96251881Speter svn_stringbuf_t *buf; 97251881Speter svn_boolean_t eof; 98251881Speter apr_size_t len, keylen, vallen; 99251881Speter char c, *keybuf, *valbuf; 100251881Speter apr_pool_t *iterpool = svn_pool_create(pool); 101251881Speter 102251881Speter while (1) 103251881Speter { 104251881Speter svn_error_t *err; 105251881Speter apr_uint64_t ui64; 106251881Speter 107251881Speter svn_pool_clear(iterpool); 108251881Speter 109251881Speter /* Read a key length line. Might be END, though. */ 110251881Speter SVN_ERR(svn_stream_readline(stream, &buf, "\n", &eof, iterpool)); 111251881Speter 112251881Speter /* Check for the end of the hash. */ 113251881Speter if ((!terminator && eof && buf->len == 0) 114251881Speter || (terminator && (strcmp(buf->data, terminator) == 0))) 115251881Speter break; 116251881Speter 117251881Speter /* Check for unexpected end of stream */ 118251881Speter if (eof) 119251881Speter return svn_error_create(SVN_ERR_MALFORMED_FILE, NULL, 120251881Speter _("Serialized hash missing terminator")); 121251881Speter 122251881Speter if ((buf->len >= 3) && (buf->data[0] == 'K') && (buf->data[1] == ' ')) 123251881Speter { 124251881Speter /* Get the length of the key */ 125251881Speter err = svn_cstring_strtoui64(&ui64, buf->data + 2, 126251881Speter 0, APR_SIZE_MAX, 10); 127251881Speter if (err) 128251881Speter return svn_error_create(SVN_ERR_MALFORMED_FILE, err, 129251881Speter _("Serialized hash malformed")); 130251881Speter keylen = (apr_size_t)ui64; 131251881Speter 132251881Speter /* Now read that much into a buffer. */ 133251881Speter keybuf = apr_palloc(pool, keylen + 1); 134251881Speter SVN_ERR(svn_stream_read(stream, keybuf, &keylen)); 135251881Speter keybuf[keylen] = '\0'; 136251881Speter 137251881Speter /* Suck up extra newline after key data */ 138251881Speter len = 1; 139251881Speter SVN_ERR(svn_stream_read(stream, &c, &len)); 140251881Speter if (c != '\n') 141251881Speter return svn_error_create(SVN_ERR_MALFORMED_FILE, NULL, 142251881Speter _("Serialized hash malformed")); 143251881Speter 144251881Speter /* Read a val length line */ 145251881Speter SVN_ERR(svn_stream_readline(stream, &buf, "\n", &eof, iterpool)); 146251881Speter 147251881Speter if ((buf->data[0] == 'V') && (buf->data[1] == ' ')) 148251881Speter { 149251881Speter err = svn_cstring_strtoui64(&ui64, buf->data + 2, 150251881Speter 0, APR_SIZE_MAX, 10); 151251881Speter if (err) 152251881Speter return svn_error_create(SVN_ERR_MALFORMED_FILE, err, 153251881Speter _("Serialized hash malformed")); 154251881Speter vallen = (apr_size_t)ui64; 155251881Speter 156251881Speter valbuf = apr_palloc(iterpool, vallen + 1); 157251881Speter SVN_ERR(svn_stream_read(stream, valbuf, &vallen)); 158251881Speter valbuf[vallen] = '\0'; 159251881Speter 160251881Speter /* Suck up extra newline after val data */ 161251881Speter len = 1; 162251881Speter SVN_ERR(svn_stream_read(stream, &c, &len)); 163251881Speter if (c != '\n') 164251881Speter return svn_error_create(SVN_ERR_MALFORMED_FILE, NULL, 165251881Speter _("Serialized hash malformed")); 166251881Speter 167251881Speter /* Add a new hash entry. */ 168251881Speter apr_hash_set(hash, keybuf, keylen, 169251881Speter svn_string_ncreate(valbuf, vallen, pool)); 170251881Speter } 171251881Speter else 172251881Speter return svn_error_create(SVN_ERR_MALFORMED_FILE, NULL, 173251881Speter _("Serialized hash malformed")); 174251881Speter } 175251881Speter else if (incremental && (buf->len >= 3) 176251881Speter && (buf->data[0] == 'D') && (buf->data[1] == ' ')) 177251881Speter { 178251881Speter /* Get the length of the key */ 179251881Speter err = svn_cstring_strtoui64(&ui64, buf->data + 2, 180251881Speter 0, APR_SIZE_MAX, 10); 181251881Speter if (err) 182251881Speter return svn_error_create(SVN_ERR_MALFORMED_FILE, err, 183251881Speter _("Serialized hash malformed")); 184251881Speter keylen = (apr_size_t)ui64; 185251881Speter 186251881Speter /* Now read that much into a buffer. */ 187251881Speter keybuf = apr_palloc(iterpool, keylen + 1); 188251881Speter SVN_ERR(svn_stream_read(stream, keybuf, &keylen)); 189251881Speter keybuf[keylen] = '\0'; 190251881Speter 191251881Speter /* Suck up extra newline after key data */ 192251881Speter len = 1; 193251881Speter SVN_ERR(svn_stream_read(stream, &c, &len)); 194251881Speter if (c != '\n') 195251881Speter return svn_error_create(SVN_ERR_MALFORMED_FILE, NULL, 196251881Speter _("Serialized hash malformed")); 197251881Speter 198251881Speter /* Remove this hash entry. */ 199251881Speter apr_hash_set(hash, keybuf, keylen, NULL); 200251881Speter } 201251881Speter else 202251881Speter { 203251881Speter return svn_error_create(SVN_ERR_MALFORMED_FILE, NULL, 204251881Speter _("Serialized hash malformed")); 205251881Speter } 206251881Speter } 207251881Speter 208251881Speter svn_pool_destroy(iterpool); 209251881Speter return SVN_NO_ERROR; 210251881Speter} 211251881Speter 212251881Speter 213251881Speter/* Implements svn_hash_write2 and svn_hash_write_incremental. */ 214251881Speterstatic svn_error_t * 215251881Speterhash_write(apr_hash_t *hash, apr_hash_t *oldhash, svn_stream_t *stream, 216251881Speter const char *terminator, apr_pool_t *pool) 217251881Speter{ 218251881Speter apr_pool_t *subpool; 219251881Speter apr_size_t len; 220251881Speter apr_array_header_t *list; 221251881Speter int i; 222251881Speter 223251881Speter subpool = svn_pool_create(pool); 224251881Speter 225251881Speter list = svn_sort__hash(hash, svn_sort_compare_items_lexically, pool); 226251881Speter for (i = 0; i < list->nelts; i++) 227251881Speter { 228251881Speter svn_sort__item_t *item = &APR_ARRAY_IDX(list, i, svn_sort__item_t); 229251881Speter svn_string_t *valstr = item->value; 230251881Speter 231251881Speter svn_pool_clear(subpool); 232251881Speter 233251881Speter /* Don't output entries equal to the ones in oldhash, if present. */ 234251881Speter if (oldhash) 235251881Speter { 236251881Speter svn_string_t *oldstr = apr_hash_get(oldhash, item->key, item->klen); 237251881Speter 238251881Speter if (oldstr && svn_string_compare(valstr, oldstr)) 239251881Speter continue; 240251881Speter } 241251881Speter 242251881Speter if (item->klen < 0) 243251881Speter return svn_error_create(SVN_ERR_MALFORMED_FILE, NULL, 244251881Speter _("Cannot serialize negative length")); 245251881Speter 246251881Speter /* Write it out. */ 247251881Speter SVN_ERR(svn_stream_printf(stream, subpool, 248251881Speter "K %" APR_SIZE_T_FMT "\n%s\n" 249251881Speter "V %" APR_SIZE_T_FMT "\n", 250251881Speter (apr_size_t) item->klen, 251251881Speter (const char *) item->key, 252251881Speter valstr->len)); 253251881Speter len = valstr->len; 254251881Speter SVN_ERR(svn_stream_write(stream, valstr->data, &len)); 255251881Speter SVN_ERR(svn_stream_puts(stream, "\n")); 256251881Speter } 257251881Speter 258251881Speter if (oldhash) 259251881Speter { 260251881Speter /* Output a deletion entry for each property in oldhash but not hash. */ 261251881Speter list = svn_sort__hash(oldhash, svn_sort_compare_items_lexically, 262251881Speter pool); 263251881Speter for (i = 0; i < list->nelts; i++) 264251881Speter { 265251881Speter svn_sort__item_t *item = &APR_ARRAY_IDX(list, i, svn_sort__item_t); 266251881Speter 267251881Speter svn_pool_clear(subpool); 268251881Speter 269251881Speter /* If it's not present in the new hash, write out a D entry. */ 270251881Speter if (! apr_hash_get(hash, item->key, item->klen)) 271251881Speter SVN_ERR(svn_stream_printf(stream, subpool, 272251881Speter "D %" APR_SSIZE_T_FMT "\n%s\n", 273251881Speter item->klen, (const char *) item->key)); 274251881Speter } 275251881Speter } 276251881Speter 277251881Speter if (terminator) 278251881Speter SVN_ERR(svn_stream_printf(stream, subpool, "%s\n", terminator)); 279251881Speter 280251881Speter svn_pool_destroy(subpool); 281251881Speter return SVN_NO_ERROR; 282251881Speter} 283251881Speter 284251881Speter 285251881Spetersvn_error_t *svn_hash_read2(apr_hash_t *hash, svn_stream_t *stream, 286251881Speter const char *terminator, apr_pool_t *pool) 287251881Speter{ 288251881Speter return hash_read(hash, stream, terminator, FALSE, pool); 289251881Speter} 290251881Speter 291251881Speter 292251881Spetersvn_error_t *svn_hash_read_incremental(apr_hash_t *hash, 293251881Speter svn_stream_t *stream, 294251881Speter const char *terminator, 295251881Speter apr_pool_t *pool) 296251881Speter{ 297251881Speter return hash_read(hash, stream, terminator, TRUE, pool); 298251881Speter} 299251881Speter 300251881Speter 301251881Spetersvn_error_t * 302251881Spetersvn_hash_write2(apr_hash_t *hash, svn_stream_t *stream, 303251881Speter const char *terminator, apr_pool_t *pool) 304251881Speter{ 305251881Speter return hash_write(hash, NULL, stream, terminator, pool); 306251881Speter} 307251881Speter 308251881Speter 309251881Spetersvn_error_t * 310251881Spetersvn_hash_write_incremental(apr_hash_t *hash, apr_hash_t *oldhash, 311251881Speter svn_stream_t *stream, const char *terminator, 312251881Speter apr_pool_t *pool) 313251881Speter{ 314251881Speter SVN_ERR_ASSERT(oldhash != NULL); 315251881Speter return hash_write(hash, oldhash, stream, terminator, pool); 316251881Speter} 317251881Speter 318251881Speter 319251881Spetersvn_error_t * 320251881Spetersvn_hash_write(apr_hash_t *hash, apr_file_t *destfile, apr_pool_t *pool) 321251881Speter{ 322251881Speter return hash_write(hash, NULL, svn_stream_from_aprfile2(destfile, TRUE, pool), 323251881Speter SVN_HASH_TERMINATOR, pool); 324251881Speter} 325251881Speter 326251881Speter 327251881Speter/* There are enough quirks in the deprecated svn_hash_read that we 328251881Speter should just preserve its implementation. */ 329251881Spetersvn_error_t * 330251881Spetersvn_hash_read(apr_hash_t *hash, 331251881Speter apr_file_t *srcfile, 332251881Speter apr_pool_t *pool) 333251881Speter{ 334251881Speter svn_error_t *err; 335251881Speter char buf[SVN_KEYLINE_MAXLEN]; 336251881Speter apr_size_t num_read; 337251881Speter char c; 338251881Speter int first_time = 1; 339251881Speter 340251881Speter 341251881Speter while (1) 342251881Speter { 343251881Speter /* Read a key length line. Might be END, though. */ 344251881Speter apr_size_t len = sizeof(buf); 345251881Speter 346251881Speter err = svn_io_read_length_line(srcfile, buf, &len, pool); 347251881Speter if (err && APR_STATUS_IS_EOF(err->apr_err) && first_time) 348251881Speter { 349251881Speter /* We got an EOF on our very first attempt to read, which 350251881Speter means it's a zero-byte file. No problem, just go home. */ 351251881Speter svn_error_clear(err); 352251881Speter return SVN_NO_ERROR; 353251881Speter } 354251881Speter else if (err) 355251881Speter /* Any other circumstance is a genuine error. */ 356251881Speter return err; 357251881Speter 358251881Speter first_time = 0; 359251881Speter 360251881Speter if (((len == 3) && (buf[0] == 'E') && (buf[1] == 'N') && (buf[2] == 'D')) 361251881Speter || ((len == 9) 362251881Speter && (buf[0] == 'P') 363251881Speter && (buf[1] == 'R') /* We formerly used just "END" to */ 364251881Speter && (buf[2] == 'O') /* end a property hash, but later */ 365251881Speter && (buf[3] == 'P') /* we added "PROPS-END", so that */ 366251881Speter && (buf[4] == 'S') /* the fs dump format would be */ 367251881Speter && (buf[5] == '-') /* more human-readable. That's */ 368251881Speter && (buf[6] == 'E') /* why we accept either way here. */ 369251881Speter && (buf[7] == 'N') 370251881Speter && (buf[8] == 'D'))) 371251881Speter { 372251881Speter /* We've reached the end of the dumped hash table, so leave. */ 373251881Speter return SVN_NO_ERROR; 374251881Speter } 375251881Speter else if ((buf[0] == 'K') && (buf[1] == ' ')) 376251881Speter { 377251881Speter size_t keylen; 378251881Speter int parsed_len; 379251881Speter void *keybuf; 380251881Speter 381251881Speter /* Get the length of the key */ 382251881Speter SVN_ERR(svn_cstring_atoi(&parsed_len, buf + 2)); 383251881Speter keylen = parsed_len; 384251881Speter 385251881Speter /* Now read that much into a buffer, + 1 byte for null terminator */ 386251881Speter keybuf = apr_palloc(pool, keylen + 1); 387251881Speter SVN_ERR(svn_io_file_read_full2(srcfile, 388251881Speter keybuf, keylen, 389251881Speter &num_read, NULL, pool)); 390251881Speter ((char *) keybuf)[keylen] = '\0'; 391251881Speter 392251881Speter /* Suck up extra newline after key data */ 393251881Speter SVN_ERR(svn_io_file_getc(&c, srcfile, pool)); 394251881Speter if (c != '\n') 395251881Speter return svn_error_create(SVN_ERR_MALFORMED_FILE, NULL, NULL); 396251881Speter 397251881Speter /* Read a val length line */ 398251881Speter len = sizeof(buf); 399251881Speter SVN_ERR(svn_io_read_length_line(srcfile, buf, &len, pool)); 400251881Speter 401251881Speter if ((buf[0] == 'V') && (buf[1] == ' ')) 402251881Speter { 403251881Speter svn_string_t *value = apr_palloc(pool, sizeof(*value)); 404251881Speter apr_size_t vallen; 405251881Speter void *valbuf; 406251881Speter 407251881Speter /* Get the length of the value */ 408251881Speter SVN_ERR(svn_cstring_atoi(&parsed_len, buf + 2)); 409251881Speter vallen = parsed_len; 410251881Speter 411251881Speter /* Again, 1 extra byte for the null termination. */ 412251881Speter valbuf = apr_palloc(pool, vallen + 1); 413251881Speter SVN_ERR(svn_io_file_read_full2(srcfile, 414251881Speter valbuf, vallen, 415251881Speter &num_read, NULL, pool)); 416251881Speter ((char *) valbuf)[vallen] = '\0'; 417251881Speter 418251881Speter /* Suck up extra newline after val data */ 419251881Speter SVN_ERR(svn_io_file_getc(&c, srcfile, pool)); 420251881Speter if (c != '\n') 421251881Speter return svn_error_create(SVN_ERR_MALFORMED_FILE, NULL, NULL); 422251881Speter 423251881Speter value->data = valbuf; 424251881Speter value->len = vallen; 425251881Speter 426251881Speter /* The Grand Moment: add a new hash entry! */ 427251881Speter apr_hash_set(hash, keybuf, keylen, value); 428251881Speter } 429251881Speter else 430251881Speter { 431251881Speter return svn_error_create(SVN_ERR_MALFORMED_FILE, NULL, NULL); 432251881Speter } 433251881Speter } 434251881Speter else 435251881Speter { 436251881Speter return svn_error_create(SVN_ERR_MALFORMED_FILE, NULL, NULL); 437251881Speter } 438251881Speter } /* while (1) */ 439251881Speter} 440251881Speter 441251881Speter 442251881Speter 443251881Speter/*** Diffing hashes ***/ 444251881Speter 445251881Spetersvn_error_t * 446251881Spetersvn_hash_diff(apr_hash_t *hash_a, 447251881Speter apr_hash_t *hash_b, 448251881Speter svn_hash_diff_func_t diff_func, 449251881Speter void *diff_func_baton, 450251881Speter apr_pool_t *pool) 451251881Speter{ 452251881Speter apr_hash_index_t *hi; 453251881Speter 454251881Speter if (hash_a) 455251881Speter for (hi = apr_hash_first(pool, hash_a); hi; hi = apr_hash_next(hi)) 456251881Speter { 457251881Speter const void *key; 458251881Speter apr_ssize_t klen; 459251881Speter 460251881Speter apr_hash_this(hi, &key, &klen, NULL); 461251881Speter 462251881Speter if (hash_b && (apr_hash_get(hash_b, key, klen))) 463251881Speter SVN_ERR((*diff_func)(key, klen, svn_hash_diff_key_both, 464251881Speter diff_func_baton)); 465251881Speter else 466251881Speter SVN_ERR((*diff_func)(key, klen, svn_hash_diff_key_a, 467251881Speter diff_func_baton)); 468251881Speter } 469251881Speter 470251881Speter if (hash_b) 471251881Speter for (hi = apr_hash_first(pool, hash_b); hi; hi = apr_hash_next(hi)) 472251881Speter { 473251881Speter const void *key; 474251881Speter apr_ssize_t klen; 475251881Speter 476251881Speter apr_hash_this(hi, &key, &klen, NULL); 477251881Speter 478251881Speter if (! (hash_a && apr_hash_get(hash_a, key, klen))) 479251881Speter SVN_ERR((*diff_func)(key, klen, svn_hash_diff_key_b, 480251881Speter diff_func_baton)); 481251881Speter } 482251881Speter 483251881Speter return SVN_NO_ERROR; 484251881Speter} 485251881Speter 486251881Speter 487251881Speter/*** Misc. hash APIs ***/ 488251881Speter 489251881Spetersvn_error_t * 490251881Spetersvn_hash_keys(apr_array_header_t **array, 491251881Speter apr_hash_t *hash, 492251881Speter apr_pool_t *pool) 493251881Speter{ 494251881Speter apr_hash_index_t *hi; 495251881Speter 496251881Speter *array = apr_array_make(pool, apr_hash_count(hash), sizeof(const char *)); 497251881Speter 498251881Speter for (hi = apr_hash_first(pool, hash); hi; hi = apr_hash_next(hi)) 499251881Speter { 500251881Speter APR_ARRAY_PUSH(*array, const char *) = svn__apr_hash_index_key(hi); 501251881Speter } 502251881Speter 503251881Speter return SVN_NO_ERROR; 504251881Speter} 505251881Speter 506251881Speter 507251881Spetersvn_error_t * 508251881Spetersvn_hash_from_cstring_keys(apr_hash_t **hash_p, 509251881Speter const apr_array_header_t *keys, 510251881Speter apr_pool_t *pool) 511251881Speter{ 512251881Speter int i; 513251881Speter apr_hash_t *hash = svn_hash__make(pool); 514251881Speter for (i = 0; i < keys->nelts; i++) 515251881Speter { 516251881Speter const char *key = 517251881Speter apr_pstrdup(pool, APR_ARRAY_IDX(keys, i, const char *)); 518251881Speter svn_hash_sets(hash, key, key); 519251881Speter } 520251881Speter *hash_p = hash; 521251881Speter return SVN_NO_ERROR; 522251881Speter} 523251881Speter 524251881Speter 525251881Speter#if !APR_VERSION_AT_LEAST(1, 3, 0) 526251881Spetervoid 527251881Spetersvn_hash__clear(apr_hash_t *hash) 528251881Speter{ 529251881Speter apr_hash_index_t *hi; 530251881Speter const void *key; 531251881Speter apr_ssize_t klen; 532251881Speter 533251881Speter for (hi = apr_hash_first(NULL, hash); hi; hi = apr_hash_next(hi)) 534251881Speter { 535251881Speter apr_hash_this(hi, &key, &klen, NULL); 536251881Speter apr_hash_set(hash, key, klen, NULL); 537251881Speter } 538251881Speter} 539251881Speter#endif 540251881Speter 541251881Speter 542251881Speter 543251881Speter/*** Specialized getter APIs ***/ 544251881Speter 545251881Speterconst char * 546251881Spetersvn_hash__get_cstring(apr_hash_t *hash, 547251881Speter const char *key, 548251881Speter const char *default_value) 549251881Speter{ 550251881Speter if (hash) 551251881Speter { 552251881Speter const char *value = svn_hash_gets(hash, key); 553251881Speter return value ? value : default_value; 554251881Speter } 555251881Speter 556251881Speter return default_value; 557251881Speter} 558251881Speter 559251881Speter 560251881Spetersvn_boolean_t 561251881Spetersvn_hash__get_bool(apr_hash_t *hash, const char *key, 562251881Speter svn_boolean_t default_value) 563251881Speter{ 564251881Speter const char *tmp_value = svn_hash__get_cstring(hash, key, NULL); 565251881Speter svn_tristate_t value = svn_tristate__from_word(tmp_value); 566251881Speter 567251881Speter if (value == svn_tristate_true) 568251881Speter return TRUE; 569251881Speter else if (value == svn_tristate_false) 570251881Speter return FALSE; 571251881Speter 572251881Speter return default_value; 573251881Speter} 574251881Speter 575251881Speter 576251881Speter 577251881Speter/*** Optimized hash function ***/ 578251881Speter 579251881Speter/* Optimized version of apr_hashfunc_default in APR 1.4.5 and earlier. 580251881Speter * It assumes that the CPU has 32-bit multiplications with high throughput 581251881Speter * of at least 1 operation every 3 cycles. Latency is not an issue. Another 582251881Speter * optimization is a mildly unrolled main loop and breaking the dependency 583251881Speter * chain within the loop. 584251881Speter * 585251881Speter * Note that most CPUs including Intel Atom, VIA Nano, ARM feature the 586251881Speter * assumed pipelined multiplication circuitry. They can do one MUL every 587251881Speter * or every other cycle. 588251881Speter * 589251881Speter * The performance is ultimately limited by the fact that most CPUs can 590251881Speter * do only one LOAD and only one BRANCH operation per cycle. The best we 591251881Speter * can do is to process one character per cycle - provided the processor 592251881Speter * is wide enough to do 1 LOAD, COMPARE, BRANCH, MUL and ADD per cycle. 593251881Speter */ 594251881Speterstatic unsigned int 595251881Speterhashfunc_compatible(const char *char_key, apr_ssize_t *klen) 596251881Speter{ 597251881Speter unsigned int hash = 0; 598251881Speter const unsigned char *key = (const unsigned char *)char_key; 599251881Speter const unsigned char *p; 600251881Speter apr_ssize_t i; 601251881Speter 602251881Speter if (*klen == APR_HASH_KEY_STRING) 603251881Speter { 604251881Speter for (p = key; ; p+=4) 605251881Speter { 606251881Speter unsigned int new_hash = hash * 33 * 33 * 33 * 33; 607251881Speter if (!p[0]) break; 608251881Speter new_hash += p[0] * 33 * 33 * 33; 609251881Speter if (!p[1]) break; 610251881Speter new_hash += p[1] * 33 * 33; 611251881Speter if (!p[2]) break; 612251881Speter new_hash += p[2] * 33; 613251881Speter if (!p[3]) break; 614251881Speter hash = new_hash + p[3]; 615251881Speter } 616251881Speter for (; *p; p++) 617251881Speter hash = hash * 33 + *p; 618251881Speter 619251881Speter *klen = p - key; 620251881Speter } 621251881Speter else 622251881Speter { 623251881Speter for (p = key, i = *klen; i >= 4; i-=4, p+=4) 624251881Speter { 625251881Speter hash = hash * 33 * 33 * 33 * 33 626251881Speter + p[0] * 33 * 33 * 33 627251881Speter + p[1] * 33 * 33 628251881Speter + p[2] * 33 629251881Speter + p[3]; 630251881Speter } 631251881Speter for (; i; i--, p++) 632251881Speter hash = hash * 33 + *p; 633251881Speter } 634251881Speter 635251881Speter return hash; 636251881Speter} 637251881Speter 638251881Speterapr_hash_t * 639251881Spetersvn_hash__make(apr_pool_t *pool) 640251881Speter{ 641251881Speter return apr_hash_make_custom(pool, hashfunc_compatible); 642251881Speter} 643