In http://svn.apache.org/r1664127 (very similar to the attached patch), I added some array & hash iterators & accessors to the 'move-tracking-2' branch so that I could use more compact iteration code (http://svn.apache.org/r1664285) and get an iteration pool 'for free' (http://svn.apache.org/r1664290).
Is there any interest in using some of this on trunk? Here's a summary. Array: apr_array_header_t of pointers to objects, with - Simpler syntax for operations such as make, get, set, push, pop. - no need to specify the element type in these operations - The get and set and iteration functions avoid the need to use the non-type-safe APR_ARRAY_IDX. (It does not assert that the sizeof(type) matches, let alone (type) itself. Note: When I inserted "assert(sizeof(type)==array->elt_size)" in APR's version, the Subversion test suite still passed, so that's good.) - Shallow and deep duplicators for an array of simple or compound elements. Hash table: same as apr_hash_t but always with C-string keys. Iteration over an array or a hash shares these features: - Convenience prioritized over speed (but still good speed). - Easy access to this key and this value as iterator member variables. - for an array: it->i and it->val - for a hash: it->key and it->val (and it->klen for good measure) - Templated iterator with parameterized element type. - in an iterator declared as "SVN_ITER_T(svn_branch_family_t) *it;", it->val has type "svn_branch_family_t *" - in an iterator declared as "svn_iter_t *it;", it->val has type "void *" - Built-in, managed iterpool as an iterator member variable. - just use it->iterpool; it is automatically created/cleared/destroyed - Iteration in sorted order (optional). - just use "for (SVN_ARRAY_ITER_SORTED(it, array, comparator, pool))" instead of "for (SVN_ARRAY_ITER(it, array, pool))"; same for a hash Usage examples: - outer_family->sub_families = apr_array_make(result_pool, 1, sizeof(void *)); + outer_family->sub_families = svn_array_make(result_pool); - APR_ARRAY_PUSH(outer_family->sub_families, void *) = family; + SVN_ARRAY_PUSH(outer_family->sub_families) = family; { - apr_array_header_t *sub_families - = svn_branch_family_get_children(family, scratch_pool); - int f; + SVN_ITER_T(svn_branch_family_t) *fi; - for (f = 0; f < sub_families->nelts; f++) + for (SVN_ARRAY_ITER(fi, svn_branch_family_get_children( + family, scratch_pool), scratch_pool)) { - svn_branch_family_t *sub_family - = APR_ARRAY_IDX(sub_families, f, svn_branch_family_t *); - - SVN_ERR(family_list_branch_instances(rev_root, sub_family, recursive, + SVN_ERR(family_list_branch_instances(rev_root, fi->val, recursive, - verbose, scratch_pool)); - verbose, fi->iterpool)); } } - Julian
Experimental array and hash functions. Simpler iteration over an array or a hash, sharing these features: - Convenience prioritized over speed. - Easy access to this key and this value as iterator member variables. - Templated iterator with parameterized element type. - Built-in, managed iterpool as an iterator member variable. - Iteration in sorted order (optional). Array of pointers to objects, with - Simpler syntax for operations such as make, get, set, push, pop. - Efficient duplicators for an array of simple or compound elements. - The get and set and iteration functions avoid the need to use the non-type-safe APR_ARRAY_IDX. (It does not assert that the sizeof(type) matches, let alone (type) itself. Note: When I inserted "assert(sizeof(type)==array->elt_size)" in APR's version, the Subversion test suite still passed, so that's good.) Hash table with - C-string keys. TODO: - Implement an efficient 'svn_array_extend' method (what's that?) - Try making a templated array type with parameterized element type. - Add 'svn_hash_this_key/klen/val' and 'svn_hash_this' functions (for compatibility)? * subversion/include/private/svn_sorts_private.h, subversion/libsvn_subr/hash.c (svn_iter_t, SVN_ITER_T, SVN_CONST_ITER_T): New. (svn_array_t, svn_array_make, svn_array_make_n, svn_array_ensure, svn_ptr_array_dup_shallow, SVN_PTR_ARRAY_DUP_SIMPLE, svn_ptr_array_dup_simple, svn_ptr_array_dup_compound, svn_array_get, svn_array_set, svn_array_push, svn_array_pop, SVN_ARRAY_PUSH, SVN_ARRAY_FOR, svn_array__first, svn_array__sorted_first, svn_array__next, SVN_ARRAY_ITER, SVN_ARRAY_ITER_SORTED, SVN_ARRAY_ITER_NO_POOL, SVN_ARRAY_ITER_SORTED_NO_POOL): New. (svn_hash__first, svn_hash__sorted_first, svn_hash__next, SVN_HASH_ITER, SVN_HASH_ITER_SORTED, SVN_HASH_ITER_NO_POOL, SVN_HASH_ITER_SORTED_NO_POOL): New. (test_array, test_hash): New test functions. --This line, and those below, will be ignored-- Index: subversion/include/private/svn_sorts_private.h =================================================================== --- subversion/include/private/svn_sorts_private.h (revision 1663743) +++ subversion/include/private/svn_sorts_private.h (working copy) @@ -146,12 +146,299 @@ svn_sort__array_delete(apr_array_header_ * @note Private. For use by Subversion's own code only. */ void svn_sort__array_reverse(apr_array_header_t *array, apr_pool_t *scratch_pool); + +/* ====================================================================== */ + +/** A hash iterator for iterating over an array or a hash table in + * its natural order or in sorted order. + * + * For an array, the @a i and @a val members provide the index and value + * of the current item. + * + * For a hash table, the @c key, @c klen and @c val members provide the + * key, key length (in bytes) and value of the current item. + * + * The @c iterpool member provides a managed iteration pool. It is cleared + * at the start of each iteration and destroyed at the end of iteration. + */ +typedef struct svn_iter_t +{ + /* private: the original hash for unsorted hash iteration */ + apr_hash_index_t *apr_hi; + + /* private: the original or sorted array for array iteration, or an array + of (svn_sort__item_t) hash items for sorted hash iteration */ + const apr_array_header_t *array; + + /* current element: iteration order index (array only; undefined for hash) */ + int i; + /* current element: key (hash only; undefined for an array) */ + const char *key; + /* current element: key length (hash only; undefined for an array) */ + apr_ssize_t klen; + /* current element: value (array or hash) */ + void *val; + + /* iteration pool */ + apr_pool_t *iterpool; +} svn_iter_t; + +/* Type-templated iterator. + * ### Layout must be the same as svn_iter_t. + */ +#define SVN_ITER_T(element_target_type) \ + struct { \ + apr_hash_index_t *apr_hi; \ + const apr_array_header_t *array; \ + int i; /* the current index into ARRAY */ \ + const char *key; \ + apr_ssize_t klen; \ + element_target_type *val; \ + apr_pool_t *iterpool; \ + } + +/* Type-templated iterator with pointer to 'const' elements. + * ### Layout must be the same as svn_iter_t. + * ### One of the main uses should be to iterate over a const collection, + * but the iteration implementation doesn't currently distinguish const + * from non-const collections. + */ +#define SVN_CONST_ITER_T(element_target_type) \ + SVN_ITER_T(element_target_type const) + +/* ====================================================================== */ + +/* + * An array of pointers to objects. + * + * - (void *) element type + * - an element may not be null + * ### TODO: Allow an element to be null. + */ + +/** An array, assumed to be an array of pointers. */ +typedef apr_array_header_t svn_array_t; + +/** Return a new, empty array, allocated in @a pool. */ +svn_array_t * +svn_array_make(apr_pool_t *pool); + +/** Return a new, empty array, with initial space for @a elements elements, + * allocated in POOL. The current number of elements is set to 0. + * + * @note This is for performance optimization. + */ +svn_array_t * +svn_array_make_n(apr_pool_t *pool, int elements); + +/** Ensure the array has space for at least @a elements elements in total. + * The current number of elements is unchanged. + * + * @note This is for performance optimization. + */ +void +svn_array_ensure(svn_array_t *array, int elements); + +/** Shallow-copy an array of pointers to simple objects. + * + * Return a duplicate in @a pool of the array @a array of pointers. + * Do not duplicate the pointed-to objects. + */ +apr_array_header_t * +svn_array_dup_shallow(const apr_array_header_t *array, + apr_pool_t *pool); + +/** Deep-copy an array of pointers to simple objects. + * + * Return a duplicate in @a pool of the array @a array of pointers to + * objects of size @a object_size bytes. Duplicate each object bytewise. + */ +apr_array_header_t * +svn_array_dup_simple(const apr_array_header_t *array, + size_t object_size, + apr_pool_t *pool); + +/** Deep-copy an array of pointers to simple objects. + * + * Return a duplicate in @a pool of the array @a array of pointers to + * objects of type @a element_type. Duplicate each object bytewise. + */ +#define SVN_ARRAY_DUP_SIMPLE(array, element_type, pool) \ + svn_array_dup_simple(array, sizeof(element_type), pool) + +/** Deep-copy an array of pointers to compound objects. + * + * Return a duplicate in @a pool of the array @a array of pointers to + * compound objects. Use @a element_dup_func to duplicate each element. + * + * ### A more efficient version could be offered, taking a "copy + * constructor" rather than a "dup" function for the elements, + * and using a hybrid of this implementation and the + * svn_array_dup_simple() implementation. (A copy constructor + * constructs a copy at a specified address.) + */ +apr_array_header_t * +svn_array_dup_compound(const apr_array_header_t *array, + void *(*element_dup_func)(const void *, apr_pool_t *), + apr_pool_t *pool); + +/** Get element number @a i in @a array. */ +void * +svn_array_get(const svn_array_t *array, + int i); + +/** Set element number @a i in @a array to @a val. */ +void +svn_array_set(svn_array_t *array, + int i, + const void *val); + +/* ### There is a problem with defining a ARRAY_IDX macro to give a + * read/write reference to an element. Choosing non-const (void *) for + * the type is poor for assigning from a const ptr, while choosing + * (const void *) is poor for assigning to a non-const ptr variable. + */ +/* #define SVN_ARRAY_IDX(array, i) (((void **)(array)->elts)[i]) */ + +/* These pop/push macros are intended to be similar to the APR ones. */ +#define svn_array_pop(array) ((void **)apr_array_pop(array)) +#define svn_array_push(array) ((const void **)apr_array_push(array)) +#define SVN_ARRAY_PUSH(array) (*(svn_array_push(array))) + +/** Loop over @a array, using @a index_ptr as the pointer-to-element-type + * index variable, where 'element-type' is itself a pointer type. + */ +#define SVN_ARRAY_FOR(index_ptr, array) \ + (index_ptr) = APR_ARRAY_IDX(array, 0, void *); \ + (const void *)(index_ptr) \ + < APR_ARRAY_IDX(array, (array)->nelts, const void *); \ + (index_ptr)++ + +/** Start iterating over the array @a array, in arbitrary order. + * + * Return an iterator, or null if there are no items in @a array. + */ +svn_iter_t * +svn_array__first(apr_pool_t *pool, + const svn_array_t *array); + +/** Start iterating over the array @a array, in sorted order according to + * @a comparison_func. Return a pointer to the first element, or NULL if + * there are no elements. + * + * It is permissible to change the original array @a array during the + * iteration. Doing so will not affect the sequence of elements returned + * by svn_array__next(), as svn_array__sorted_first() takes a snapshot of + * pointers to the original keys and values. The memory in which the + * original keys and values of HT are stored must remain available during + * the iteration. + */ +svn_iter_t * +svn_array__sorted_first(apr_pool_t *pool, + const svn_array_t *array, + int (*comparison_func)(const void *, + const void *)); + +/** Return a pointer to the next element of the array being iterated by + * @a i, or NULL if there are no more elements. + */ +svn_iter_t * +svn_array__next(svn_iter_t *i); + +/** Iteration over the array @a array. + * + * To be used in the parentheses of a for() loop. + * @a i is the iterator variable declared with "SVN_[CONST_]ITER_T(type) *i;". + */ +#define SVN_ARRAY_ITER(i, array, pool) \ + i = (void *)svn_array__first(pool, array); \ + i; \ + i = (void *)svn_array__next((void *)i) + +/** Like SVN_ARRAY_ITER but using the array's own pool. */ +#define SVN_ARRAY_ITER_NO_POOL(i, array) \ + SVN_ARRAY_ITER(i, array, (array)->pool) + +/** Like SVN_ARRAY_ITER but iterating over a copy of the array sorted by + * @a comparison_func. */ +#define SVN_ARRAY_ITER_SORTED(i, array, comparison_func, pool) \ + i = (void *)svn_array__sorted_first(pool, array, comparison_func); \ + i; \ + i = (void *)svn_array__next((void *)i) + +/** Like SVN_ARRAY_SORTED but using the array's own pool. */ +#define SVN_ARRAY_ITER_SORTED_NO_POOL(i, array, comparison_func) \ + SVN_ARRAY_ITER_SORTED(i, array, comparison_func, (array)->pool) + +/* ====================================================================== */ + +/* + * A hash table in which: + * - keys are assumed to be null-terminated (const char *) strings + * - iteration in sorted order is possible + * - an iteration pool is provided + */ + +/** Start iterating over the hash table @a ht, in arbitrary order. + * + * Similar to apr_hash_first(). + */ +svn_iter_t * +svn_hash__first(apr_pool_t *pool, + apr_hash_t *ht); + +/** Start iterating over the hash table @a ht, in sorted order according to + * @a comparison_func. Return a pointer to the first element, or NULL if + * there are no elements. + * + * It is permissible to change the original hash table @a ht during the + * iteration. Doing so will not affect the sequence of elements returned + * by svn_hash__next(), as svn_hash__sorted_first() takes a snapshot of + * pointers to the original keys and values. The memory in which the + * original keys and values of HT are stored must remain available during + * the iteration. + */ +svn_iter_t * +svn_hash__sorted_first(apr_pool_t *pool, + apr_hash_t *ht, + int (*comparison_func)(const svn_sort__item_t *, + const svn_sort__item_t *)); + +/** Return a pointer to the next element of the hash table being iterated by + * @a hi, or NULL if there are no more elements. + */ +svn_iter_t * +svn_hash__next(svn_iter_t *hi); + +/* Type-templated iteration. + * To be used in the parentheses of a for() loop. + * I is the iterator variable declared with "SVN_[CONST_]ITER_T(type) *i;". + */ +#define SVN_HASH_ITER(i, pool, ht) \ + i = (void *)svn_hash__first(pool, ht); \ + i; \ + i = (void *)svn_hash__next((void *)i) + +#define SVN_HASH_ITER_NO_POOL(i, ht) \ + SVN_HASH_ITER(i, apr_hash_pool_get(ht), ht) + +#define SVN_HASH_ITER_SORTED(i, ht, comparison_func, pool) \ + i = (void *)svn_hash__sorted_first(pool, ht, comparison_func); \ + i; \ + i = (void *)svn_hash__next((void *)i) + +#define SVN_HASH_ITER_SORTED_NO_POOL(i, ht, comparison_func) \ + SVN_HASH_ITER_SORTED(i, ht, comparison_func, apr_hash_pool_get(ht)) + + +/* ====================================================================== */ + /** Priority queues. * * @defgroup svn_priority_queue__t Priority Queues * @{ */ Index: subversion/libsvn_subr/hash.c =================================================================== --- subversion/libsvn_subr/hash.c (revision 1663743) +++ subversion/libsvn_subr/hash.c (working copy) @@ -646,6 +646,366 @@ hashfunc_compatible(const char *char_key apr_hash_t * svn_hash__make(apr_pool_t *pool) { return apr_hash_make_custom(pool, hashfunc_compatible); } + + +/* ====================================================================== */ + +svn_array_t * +svn_array_make(apr_pool_t *pool) +{ + return apr_array_make(pool, 0, sizeof(void *)); +} + +svn_array_t * +svn_array_make_n(apr_pool_t *pool, int elements) +{ + return apr_array_make(pool, elements, sizeof(void *)); +} + +/* ### inefficient implementation */ +void +svn_array_ensure(svn_array_t *array, int elements) +{ + int old_nelts = array->nelts; + + while (array->nalloc < elements) + apr_array_push(array); + array->nelts = old_nelts; +} + +apr_array_header_t * +svn_array_dup_shallow(const apr_array_header_t *array, + apr_pool_t *pool) +{ + apr_array_header_t *new_array = apr_array_copy(pool, array); + + return new_array; +} + +/* ### This implementation comes from ptr_array_dup(), a local helper + * for svn_rangelist_dup(). */ +apr_array_header_t * +svn_array_dup_simple(const apr_array_header_t *array, + size_t object_size, + apr_pool_t *pool) +{ + apr_array_header_t *new_array = apr_array_make(pool, array->nelts, + sizeof(void *)); + + /* allocate target range buffer with a single operation */ + char *copy = apr_palloc(pool, object_size * array->nelts); + + /* for efficiency, directly address source and target reference buffers */ + void **source = (void **)(array->elts); + void **target = (void **)(new_array->elts); + int i; + + /* copy ranges iteratively and link them into the target range list */ + for (i = 0; i < array->nelts; i++) + { + target[i] = ©[i * object_size]; + memcpy(target[i], source[i], object_size); + } + new_array->nelts = array->nelts; + + return new_array; +} + +apr_array_header_t * +svn_array_dup_compound(const apr_array_header_t *array, + void *(*element_dup_func)(const void *, apr_pool_t *), + apr_pool_t *pool) +{ + apr_array_header_t *new_array = apr_array_make(pool, array->nelts, + sizeof(void *)); + int i; + + for (i = 0; i < array->nelts; i++) + { + svn_array_set(new_array, i, + element_dup_func(svn_array_get(array, i), pool)); + } + new_array->nelts = array->nelts; + + return new_array; +} + +void * +svn_array_get(const svn_array_t *array, + int i) +{ + return APR_ARRAY_IDX(array, i, void *); +} + +void +svn_array_set(svn_array_t *array, + int i, + const void *value) +{ + APR_ARRAY_IDX(array, i, const void *) = value; +} + +/* Clean up internal state of IT. */ +static void +iter_cleanup(svn_iter_t *it) +{ + if (it->iterpool) + svn_pool_destroy(it->iterpool); +#ifdef SVN_DEBUG + memset(it, 0, sizeof(*it)); +#endif +} + +svn_iter_t * +svn_array__first(apr_pool_t *pool, + const svn_array_t *array) +{ + svn_iter_t *it; + + if (! array->nelts) + return NULL; + + it = apr_pcalloc(pool, sizeof(*it)); + it->array = array; + it->i = 0; + it->val = svn_array_get(array, 0); + it->iterpool = svn_pool_create(pool); + return it; +} + +svn_iter_t * +svn_array__sorted_first(apr_pool_t *pool, + const svn_array_t *array, + int (*comparison_func)(const void *, + const void *)) +{ + svn_array_t *new_array = svn_array_dup_shallow(array, pool); + + svn_sort__array(new_array, comparison_func); + return svn_array__first(pool, new_array); +} + +svn_iter_t * +svn_array__next(svn_iter_t *it) +{ + it->i++; + if (it->i >= it->array->nelts) + { + iter_cleanup(it); + return NULL; + } + + it->val = svn_array_get(it->array, it->i); + svn_pool_clear(it->iterpool); + return it; +} + + +/* ====================================================================== */ + +/* Exercise the svn_array API. */ + +static void *test_dup_cstring(const void *s, apr_pool_t *pool) +{ + return apr_pstrdup(pool, s); +} + +static void +test_array(apr_pool_t *pool) +{ + svn_array_t *a = svn_array_make(pool); + const char *str; + const svn_array_t *b; + char *mstr; + + /* get and set and push */ + SVN_ARRAY_PUSH(a) = "hello"; + str = svn_array_get(a, 0); + svn_array_set(a, 1, str); + SVN_ARRAY_PUSH(a) = str; + + /* duplication */ + b = svn_array_dup_shallow(a, pool); + b = SVN_ARRAY_DUP_SIMPLE(b, char *, pool); + b = svn_array_dup_compound(b, test_dup_cstring, pool); + + /* iteration with SVN_ARRAY_FOR; iterator points to const */ + { + const char **str_p; + + for (SVN_ARRAY_FOR(str_p, a)) + printf("%s", *str_p); + for (SVN_ARRAY_FOR(str_p, b)) + printf("%s", *str_p); + } + + mstr = svn_array_get(b, 0); + svn_array_set(a, 1, mstr); + SVN_ARRAY_PUSH(a) = mstr; + + /* iteration with SVN_ARRAY_FOR; iterator points to mutable */ + { + char **mstr_p; + + for (SVN_ARRAY_FOR(mstr_p, a)) + printf("%s", *mstr_p); + for (SVN_ARRAY_FOR(mstr_p, b)) + printf("%s", *mstr_p); + } + + /* iteration with svn_array__[sorted_]first() */ + { + svn_iter_t *i; + + for (i = svn_array__first(pool, a); i; i = svn_array__next(i)) + printf("%s", (char *)i->val); + for (i = svn_array__sorted_first(pool, b, svn_sort_compare_paths); + i; i = svn_array__next(i)) + printf("%s", (char *)i->val); + } + + /* iteration, typed, with SVN_ARRAY_ITER[_SORTED]() */ + { + SVN_ITER_T(char) *ia; + SVN_CONST_ITER_T(char) *ib; + + for (SVN_ARRAY_ITER(ia, a, pool)) + printf("%s", ia->val); + for (SVN_ARRAY_ITER_SORTED(ib, b, svn_sort_compare_paths, pool)) + printf("%s", ib->val); + } +} + + +/* ====================================================================== */ + +svn_iter_t * +svn_hash__first(apr_pool_t *pool, + apr_hash_t *ht) +{ + svn_iter_t *hi = apr_pcalloc(pool, sizeof(*hi)); + + hi->array = NULL; + + hi->apr_hi = apr_hash_first(pool, ht); + if (! hi->apr_hi) + return NULL; + + apr_hash_this(hi->apr_hi, (const void **)&hi->key, &hi->klen, &hi->val); + hi->iterpool = svn_pool_create(pool); + return hi; +} + +svn_iter_t * +svn_hash__sorted_first(apr_pool_t *pool, + apr_hash_t *ht, + int (*comparison_func)(const svn_sort__item_t *, + const svn_sort__item_t *)) +{ + svn_iter_t *hi = apr_palloc(pool, sizeof(*hi)); + + hi->apr_hi = NULL; + + if (apr_hash_count(ht) == 0) + return NULL; + + hi->array = svn_sort__hash(ht, comparison_func, pool); + hi->i = 0; + hi->key = APR_ARRAY_IDX(hi->array, hi->i, svn_sort__item_t).key; + hi->klen = APR_ARRAY_IDX(hi->array, hi->i, svn_sort__item_t).klen; + hi->val = APR_ARRAY_IDX(hi->array, hi->i, svn_sort__item_t).value; + hi->iterpool = svn_pool_create(pool); + return hi; +} + +svn_iter_t * +svn_hash__next(svn_iter_t *hi) +{ + if (hi->apr_hi) /* is an unsorted iterator */ + { + hi->apr_hi = apr_hash_next(hi->apr_hi); + if (! hi->apr_hi) + { + iter_cleanup(hi); + return NULL; + } + apr_hash_this(hi->apr_hi, (const void **)&hi->key, &hi->klen, &hi->val); + } + + if (hi->array) /* is a sorted iterator */ + { + hi->i++; + if (hi->i >= hi->array->nelts) + { + iter_cleanup(hi); + return NULL; + } + hi->key = APR_ARRAY_IDX(hi->array, hi->i, svn_sort__item_t).key; + hi->klen = APR_ARRAY_IDX(hi->array, hi->i, svn_sort__item_t).klen; + hi->val = APR_ARRAY_IDX(hi->array, hi->i, svn_sort__item_t).value; + } + + svn_pool_clear(hi->iterpool); + return hi; +} + + +/* ====================================================================== */ + +/* Exercise the svn_hash API. */ +static void +test_hash(apr_pool_t *pool) +{ + apr_hash_t *hash = apr_hash_make(pool); + svn_iter_t *hi; + SVN_ITER_T(const char) *it; + + svn_hash_sets(hash, "K1", "V1"); + svn_hash_sets(hash, "Key2", "Value2"); + svn_hash_sets(hash, "Key three", "Value three"); + svn_hash_sets(hash, "Fourth key", "Fourth value"); + + printf("Hash iteration, unsorted:"); + for (hi = svn_hash__first(pool, hash); hi; hi = svn_hash__next(hi)) + { + const char *k = apr_psprintf(hi->iterpool, "[%s]", hi->key); + apr_ssize_t l = hi->klen; + const char *v = hi->val; + + printf(" key[%d]: %-20s val: %s\n", (int)l, k, v); + } + + printf("Hash iteration, sorted:"); + for (hi = svn_hash__sorted_first(pool, hash, svn_sort_compare_items_lexically); + hi; hi = svn_hash__next(hi)) + { + const char *k = apr_psprintf(hi->iterpool, "[%s]", hi->key); + apr_ssize_t l = hi->klen; + const char *v = hi->val; + + printf(" key[%d]: %-20s val: %s\n", (int)l, k, v); + } + + printf("Hash iteration, typed, unsorted:"); + for (SVN_HASH_ITER(it, pool, hash)) + { + printf(" key[%d]: %-20s val: %s\n", + (int)it->klen, + apr_psprintf(it->iterpool, "[%s]", it->key), + it->val); + } + + printf("Hash iteration, typed, sorted:"); + for (SVN_HASH_ITER_SORTED(it, hash, svn_sort_compare_items_lexically, pool)) + { + printf(" key[%d]: %-20s val: %s\n", + (int)it->klen, + apr_psprintf(it->iterpool, "[%s]", it->key), + it->val); + } + +} +