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