axis-java-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From sam...@apache.org
Subject svn commit: r315082 - in /webservices/axis2/trunk/c: include/axis2_hash.h modules/util/src/axis2_hash.c
Date Thu, 13 Oct 2005 00:29:21 GMT
Author: samisa
Date: Wed Oct 12 17:29:12 2005
New Revision: 315082

URL: http://svn.apache.org/viewcvs?rev=315082&view=rev
Log:
initial version of hash implementation

Added:
    webservices/axis2/trunk/c/include/axis2_hash.h
    webservices/axis2/trunk/c/modules/util/src/axis2_hash.c

Added: webservices/axis2/trunk/c/include/axis2_hash.h
URL: http://svn.apache.org/viewcvs/webservices/axis2/trunk/c/include/axis2_hash.h?rev=315082&view=auto
==============================================================================
--- webservices/axis2/trunk/c/include/axis2_hash.h (added)
+++ webservices/axis2/trunk/c/include/axis2_hash.h Wed Oct 12 17:29:12 2005
@@ -0,0 +1,226 @@
+/*
+ * Copyright 2004,2005 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef AXIS2_HASH_H
+#define AXIS2_HASH_H
+
+/**
+ * @file axis2_hash.h
+ * @brief AXIS2 Hash Tables
+ */
+
+#include <axis2_defines.h>
+#include <axis2_environment.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @defgroup axis2_hash Hash Tables
+ * @ingroup AXIS2 
+ * @{
+ */
+
+/**
+ * When passing a key to axis2_hash_set or axis2_hash_get, this value can be
+ * passed to indicate a string-valued key, and have axis2_hash compute the
+ * length automatically.
+ *
+ * @remark axis2_hash will use strlen(key) for the length. The NUL terminator
+ *         is not included in the hash value (why throw a constant in?).
+ *         Since the hash table merely references the provided key (rather
+ *         than copying it), axis2_hash_this() will return the NUL-term'd key.
+ */
+#define AXIS2_HASH_KEY_STRING     (-1)
+
+/**
+ * Abstract type for hash tables.
+ */
+typedef struct axis2_hash_t axis2_hash_t;
+
+/**
+ * Abstract type for scanning hash tables.
+ */
+typedef struct axis2_hash_index_t axis2_hash_index_t;
+
+/**
+ * Callback functions for calculating hash values.
+ * @param key The key.
+ * @param klen The length of the key, or AXIS2_HASH_KEY_STRING to use the string 
+ *             length. If AXIS2_HASH_KEY_STRING then returns the actual key length.
+ */
+typedef unsigned int (*axis2_hashfunc_t)(const char *key, axis2_ssize_t *klen);
+
+/**
+ * The default hash function.
+ */
+unsigned int axis2_hashfunc_default(const char *key, axis2_ssize_t *klen);
+
+/**
+ * Create a hash table.
+ * @param environment The environment to allocate the hash table out of
+ * @return The hash table just created
+  */
+axis2_hash_t *axis2_hash_make(axis2_environment_t *environment);
+
+/**
+ * Create a hash table with a custom hash function
+ * @param environment The environment to allocate the hash table out of
+ * @param hash_func A custom hash function.
+ * @return The hash table just created
+  */
+axis2_hash_t *axis2_hash_make_custom(axis2_environment_t *environment, 
+                                               axis2_hashfunc_t hash_func);
+
+/**
+ * Make a copy of a hash table
+ * @param environment The environment from which to allocate the new hash table
+ * @param h The hash table to clone
+ * @return The hash table just created
+ * @remark Makes a shallow copy
+ */
+axis2_hash_t *axis2_hash_copy(axis2_environment_t *environment,
+                                        const axis2_hash_t *h);
+
+/**
+ * Associate a value with a key in a hash table.
+ * @param ht The hash table
+ * @param key Pointer to the key
+ * @param klen Length of the key. Can be AXIS2_HASH_KEY_STRING to use the string length.
+ * @param val Value to associate with the key
+ * @remark If the value is NULL the hash entry is deleted.
+ */
+void axis2_hash_set(axis2_hash_t *ht, const void *key,
+                               axis2_ssize_t klen, const void *val);
+
+/**
+ * Look up the value associated with a key in a hash table.
+ * @param ht The hash table
+ * @param key Pointer to the key
+ * @param klen Length of the key. Can be AXIS2_HASH_KEY_STRING to use the string length.
+ * @return Returns NULL if the key is not present.
+ */
+void *axis2_hash_get(axis2_hash_t *ht, const void *key,
+                                 axis2_ssize_t klen);
+
+/**
+ * Start iterating over the entries in a hash table.
+ * @param p The environment to allocate the axis2_hash_index_t iterator. If this
+ *          environment is NULL, then an internal, non-thread-safe iterator is used.
+ * @param ht The hash table
+ * @remark  There is no restriction on adding or deleting hash entries during
+ * an iteration (although the results may be unpredictable unless all you do
+ * is delete the current entry) and multiple iterations can be in
+ * progress at the same time.
+
+ * @example
+ */
+/**
+ * <PRE>
+ * 
+ * int sum_values(axis2_environment_t *environment, axis2_hash_t *ht)
+ * {
+ *     axis2_hash_index_t *hi;
+ *     void *val;
+ *     int sum = 0;
+ *     for (hi = axis2_hash_first(p, ht); hi; hi = axis2_hash_next(hi)) {
+ *         axis2_hash_this(hi, NULL, NULL, &val);
+ *         sum += *(int *)val;
+ *     }
+ *     return sum;
+ * }
+ * </PRE>
+ */
+axis2_hash_index_t *axis2_hash_first(axis2_environment_t *environment, axis2_hash_t *ht);
+
+/**
+ * Continue iterating over the entries in a hash table.
+ * @param hi The iteration state
+ * @return a pointer to the updated iteration state.  NULL if there are no more  
+ *         entries.
+ */
+axis2_hash_index_t *axis2_hash_next(axis2_hash_index_t *hi);
+
+/**
+ * Get the current entry's details from the iteration state.
+ * @param hi The iteration state
+ * @param key Return pointer for the pointer to the key.
+ * @param klen Return pointer for the key length.
+ * @param val Return pointer for the associated value.
+ * @remark The return pointers should point to a variable that will be set to the
+ *         corresponding data, or they may be NULL if the data isn't interesting.
+ */
+void axis2_hash_this(axis2_hash_index_t *hi, const void **key, 
+                                axis2_ssize_t *klen, void **val);
+
+/**
+ * Get the number of key/value pairs in the hash table.
+ * @param ht The hash table
+ * @return The number of key/value pairs in the hash table.
+ */
+unsigned int axis2_hash_count(axis2_hash_t *ht);
+
+/**
+ * Merge two hash tables into one new hash table. The values of the overlay
+ * hash override the values of the base if both have the same key.  Both
+ * hash tables must use the same hash function.
+ * @param p The environment to use for the new hash table
+ * @param overlay The table to add to the initial table
+ * @param base The table that represents the initial values of the new table
+ * @return A new hash table containing all of the data from the two passed in
+ */
+axis2_hash_t *axis2_hash_overlay(axis2_environment_t *environment,
+                                           const axis2_hash_t *overlay, 
+                                           const axis2_hash_t *base);
+
+/**
+ * Merge two hash tables into one new hash table. If the same key
+ * is present in both tables, call the supplied merge function to
+ * produce a merged value for the key in the new table.  Both
+ * hash tables must use the same hash function.
+ * @param p The environment to use for the new hash table
+ * @param h1 The first of the tables to merge
+ * @param h2 The second of the tables to merge
+ * @param merger A callback function to merge values, or NULL to
+ *  make values from h1 override values from h2 (same semantics as
+ *  axis2_hash_overlay())
+ * @param data Client data to pass to the merger function
+ * @return A new hash table containing all of the data from the two passed in
+ */
+axis2_hash_t *axis2_hash_merge(axis2_environment_t *environment,
+                                         const axis2_hash_t *h1,
+                                         const axis2_hash_t *h2,
+                                         void * (*merger)(axis2_environment_t *environment,
+                                                     const void *key,
+                                                     axis2_ssize_t klen,
+                                                     const void *h1_val,
+                                                     const void *h2_val,
+                                                     const void *data),
+                                         const void *data);
+
+/**
+ * Get a pointer to the environment which the hash table was created in
+ */
+/*AXIS2_POOL_DECLARE_ACCESSOR(hash);*/
+
+/** @} */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif	/* !AXIS2_HASH_H */

Added: webservices/axis2/trunk/c/modules/util/src/axis2_hash.c
URL: http://svn.apache.org/viewcvs/webservices/axis2/trunk/c/modules/util/src/axis2_hash.c?rev=315082&view=auto
==============================================================================
--- webservices/axis2/trunk/c/modules/util/src/axis2_hash.c (added)
+++ webservices/axis2/trunk/c/modules/util/src/axis2_hash.c Wed Oct 12 17:29:12 2005
@@ -0,0 +1,457 @@
+/*
+ * Copyright 2004,2005 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "axis2_hash.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+/*
+ * The internal form of a hash table.
+ *
+ * The table is an array indexed by the hash of the key; collisions
+ * are resolved by hanging a linked list of hash entries off each
+ * element of the array. Although this is a really simple design it
+ * isn't too bad given that environments have a low allocation overhead.
+ */
+
+typedef struct axis2_hash_entry_t axis2_hash_entry_t;
+
+struct axis2_hash_entry_t {
+    axis2_hash_entry_t *next;
+    unsigned int      hash;
+    const void       *key;
+    axis2_ssize_t       klen;
+    const void       *val;
+};
+
+/*
+ * Data structure for iterating through a hash table.
+ *
+ * We keep a pointer to the next hash entry here to allow the current
+ * hash entry to be freed or otherwise mangled between calls to
+ * axis2_hash_next().
+ */
+struct axis2_hash_index_t {
+    axis2_hash_t         *ht;
+    axis2_hash_entry_t   *this, *next;
+    unsigned int        index;
+};
+
+/*
+ * The size of the array is always a power of two. We use the maximum
+ * index rather than the size so that we can use bitwise-AND for
+ * modular arithmetic.
+ * The count of hash entries may be greater depending on the chosen
+ * collision rate.
+ */
+struct axis2_hash_t {
+    axis2_environment_t          *environment;
+    axis2_hash_entry_t   **array;
+    axis2_hash_index_t     iterator;  /* For axis2_hash_first(NULL, ...) */
+    unsigned int         count, max;
+    axis2_hashfunc_t       hash_func;
+    axis2_hash_entry_t    *free;  /* List of recycled entries */
+};
+
+#define INITIAL_MAX 15 /* tunable == 2^n - 1 */
+
+
+/*
+ * Hash creation functions.
+ */
+
+static axis2_hash_entry_t **alloc_array(axis2_hash_t *ht, unsigned int max)
+{
+   return memset (axis2_malloc(ht->environment->allocator, 
+                    sizeof(*ht->array) * (max + 1)), 0, sizeof(*ht->array) * (max +
1) );
+}
+
+axis2_hash_t *axis2_hash_make(axis2_environment_t *environment)
+{
+    axis2_hash_t *ht;
+    ht = axis2_malloc(environment->allocator, sizeof(axis2_hash_t));
+    ht->environment = environment;
+    ht->free = NULL;
+    ht->count = 0;
+    ht->max = INITIAL_MAX;
+    ht->array = alloc_array(ht, ht->max);
+    ht->hash_func = axis2_hashfunc_default;
+    return ht;
+}
+
+axis2_hash_t *axis2_hash_make_custom(axis2_environment_t *environment,
+                                               axis2_hashfunc_t hash_func)
+{
+    axis2_hash_t *ht = axis2_hash_make(environment);
+    ht->hash_func = hash_func;
+    return ht;
+}
+
+
+/*
+ * Hash iteration functions.
+ */
+
+axis2_hash_index_t *axis2_hash_next(axis2_hash_index_t *hi)
+{
+    hi->this = hi->next;
+    while (!hi->this) {
+        if (hi->index > hi->ht->max)
+            return NULL;
+
+        hi->this = hi->ht->array[hi->index++];
+    }
+    hi->next = hi->this->next;
+    return hi;
+}
+
+axis2_hash_index_t *axis2_hash_first(axis2_environment_t *environment, axis2_hash_t *ht)
+{
+    axis2_hash_index_t *hi;
+    if (environment)
+        hi = axis2_malloc(environment->allocator, sizeof(*hi));
+    else
+        hi = &ht->iterator;
+
+    hi->ht = ht;
+    hi->index = 0;
+    hi->this = NULL;
+    hi->next = NULL;
+    return axis2_hash_next(hi);
+}
+
+void axis2_hash_this(axis2_hash_index_t *hi,
+                                const void **key,
+                                axis2_ssize_t *klen,
+                                void **val)
+{
+    if (key)  *key  = hi->this->key;
+    if (klen) *klen = hi->this->klen;
+    if (val)  *val  = (void *)hi->this->val;
+}
+
+
+/*
+ * Expanding a hash table
+ */
+
+static void expand_array(axis2_hash_t *ht)
+{
+    axis2_hash_index_t *hi;
+    axis2_hash_entry_t **new_array;
+    unsigned int new_max;
+
+    new_max = ht->max * 2 + 1;
+    new_array = alloc_array(ht, new_max);
+    for (hi = axis2_hash_first(NULL, ht); hi; hi = axis2_hash_next(hi)) {
+        unsigned int i = hi->this->hash & new_max;
+        hi->this->next = new_array[i];
+        new_array[i] = hi->this;
+    }
+    ht->array = new_array;
+    ht->max = new_max;
+}
+
+unsigned int axis2_hashfunc_default(const char *char_key, axis2_ssize_t *klen)
+{
+    unsigned int hash = 0;
+    const unsigned char *key = (const unsigned char *)char_key;
+    const unsigned char *p;
+    axis2_ssize_t i;
+    
+    /*
+     * This is the popular `times 33' hash algorithm which is used by
+     * perl and also appears in Berkeley DB. This is one of the best
+     * known hash functions for strings because it is both computed
+     * very fast and distributes very well.
+     *
+     * The originator may be Dan Bernstein but the code in Berkeley DB
+     * cites Chris Torek as the source. The best citation I have found
+     * is "Chris Torek, Hash function for text in C, Usenet message
+     * <27038@mimsy.umd.edu> in comp.lang.c , October, 1990." in Rich
+     * Salz's USENIX 1992 paper about INN which can be found at
+     * <http://citeseer.nj.nec.com/salz92internetnews.html>.
+     *
+     * The magic of number 33, i.e. why it works better than many other
+     * constants, prime or not, has never been adequately explained by
+     * anyone. So I try an explanation: if one experimentally tests all
+     * multipliers between 1 and 256 (as I did while writing a low-level
+     * data structure library some time ago) one detects that even
+     * numbers are not useable at all. The remaining 128 odd numbers
+     * (except for the number 1) work more or less all equally well.
+     * They all distribute in an acceptable way and this way fill a hash
+     * table with an average percent of approx. 86%.
+     *
+     * If one compares the chi^2 values of the variants (see
+     * Bob Jenkins ``Hashing Frequently Asked Questions'' at
+     * http://burtleburtle.net/bob/hash/hashfaq.html for a description
+     * of chi^2), the number 33 not even has the best value. But the
+     * number 33 and a few other equally good numbers like 17, 31, 63,
+     * 127 and 129 have nevertheless a great advantage to the remaining
+     * numbers in the large set of possible multipliers: their multiply
+     * operation can be replaced by a faster operation based on just one
+     * shift plus either a single addition or subtraction operation. And
+     * because a hash function has to both distribute good _and_ has to
+     * be very fast to compute, those few numbers should be preferred.
+     *
+     *                  -- Ralf S. Engelschall <rse@engelschall.com>
+     */
+     
+    if (*klen == AXIS2_HASH_KEY_STRING) {
+        for (p = key; *p; p++) {
+            hash = hash * 33 + *p;
+        }
+        *klen = p - key;
+    }
+    else {
+        for (p = key, i = *klen; i; i--, p++) {
+            hash = hash * 33 + *p;
+        }
+    }
+
+    return hash;
+}
+
+
+/*
+ * This is where we keep the details of the hash function and control
+ * the maximum collision rate.
+ *
+ * If val is non-NULL it creates and initializes a new hash entry if
+ * there isn't already one there; it returns an updatable pointer so
+ * that hash entries can be removed.
+ */
+
+static axis2_hash_entry_t **find_entry(axis2_hash_t *ht,
+                                     const void *key,
+                                     axis2_ssize_t klen,
+                                     const void *val)
+{
+    axis2_hash_entry_t **hep, *he;
+    unsigned int hash;
+
+    hash = ht->hash_func(key, &klen);
+
+    /* scan linked list */
+    for (hep = &ht->array[hash & ht->max], he = *hep;
+         he; hep = &he->next, he = *hep) {
+        if (he->hash == hash
+            && he->klen == klen
+            && memcmp(he->key, key, klen) == 0)
+            break;
+    }
+    if (he || !val)
+        return hep;
+
+    /* add a new entry for non-NULL values */
+    if ((he = ht->free) != NULL)
+        ht->free = he->next;
+    else
+        he = axis2_malloc(ht->environment->allocator, sizeof(*he));
+    he->next = NULL;
+    he->hash = hash;
+    he->key  = key;
+    he->klen = klen;
+    he->val  = val;
+    *hep = he;
+    ht->count++;
+    return hep;
+}
+
+axis2_hash_t *axis2_hash_copy(axis2_environment_t *environment,
+                                        const axis2_hash_t *orig)
+{
+    axis2_hash_t *ht;
+    axis2_hash_entry_t *new_vals;
+    unsigned int i, j;
+
+    ht = axis2_malloc(environment->allocator, sizeof(axis2_hash_t) +
+                    sizeof(*ht->array) * (orig->max + 1) +
+                    sizeof(axis2_hash_entry_t) * orig->count);
+    ht->environment = environment;
+    ht->free = NULL;
+    ht->count = orig->count;
+    ht->max = orig->max;
+    ht->hash_func = orig->hash_func;
+    ht->array = (axis2_hash_entry_t **)((char *)ht + sizeof(axis2_hash_t));
+
+    new_vals = (axis2_hash_entry_t *)((char *)(ht) + sizeof(axis2_hash_t) +
+                                    sizeof(*ht->array) * (orig->max + 1));
+    j = 0;
+    for (i = 0; i <= ht->max; i++) {
+        axis2_hash_entry_t **new_entry = &(ht->array[i]);
+        axis2_hash_entry_t *orig_entry = orig->array[i];
+        while (orig_entry) {
+            *new_entry = &new_vals[j++];
+            (*new_entry)->hash = orig_entry->hash;
+            (*new_entry)->key = orig_entry->key;
+            (*new_entry)->klen = orig_entry->klen;
+            (*new_entry)->val = orig_entry->val;
+            new_entry = &((*new_entry)->next);
+            orig_entry = orig_entry->next;
+        }
+        *new_entry = NULL;
+    }
+    return ht;
+}
+
+void *axis2_hash_get(axis2_hash_t *ht,
+                                 const void *key,
+                                 axis2_ssize_t klen)
+{
+    axis2_hash_entry_t *he;
+    he = *find_entry(ht, key, klen, NULL);
+    if (he)
+        return (void *)he->val;
+    else
+        return NULL;
+}
+
+void axis2_hash_set(axis2_hash_t *ht,
+                               const void *key,
+                               axis2_ssize_t klen,
+                               const void *val)
+{
+    axis2_hash_entry_t **hep;
+    hep = find_entry(ht, key, klen, val);
+    if (*hep) {
+        if (!val) {
+            /* delete entry */
+            axis2_hash_entry_t *old = *hep;
+            *hep = (*hep)->next;
+            old->next = ht->free;
+            ht->free = old;
+            --ht->count;
+        }
+        else {
+            /* replace entry */
+            (*hep)->val = val;
+            /* check that the collision rate isn't too high */
+            if (ht->count > ht->max) {
+                expand_array(ht);
+            }
+        }
+    }
+    /* else key not present and val==NULL */
+}
+
+unsigned int axis2_hash_count(axis2_hash_t *ht)
+{
+    return ht->count;
+}
+
+axis2_hash_t *axis2_hash_overlay(axis2_environment_t *environment,
+                                          const axis2_hash_t *overlay,
+                                          const axis2_hash_t *base)
+{
+    return axis2_hash_merge(environment, overlay, base, NULL, NULL);
+}
+
+axis2_hash_t *axis2_hash_merge(axis2_environment_t *environment,
+                                         const axis2_hash_t *overlay,
+                                         const axis2_hash_t *base,
+                                         void * (*merger)(axis2_environment_t *environment,
+                                                     const void *key,
+                                                     axis2_ssize_t klen,
+                                                     const void *h1_val,
+                                                     const void *h2_val,
+                                                     const void *data),
+                                         const void *data)
+{
+    axis2_hash_t *res;
+    axis2_hash_entry_t *new_vals = NULL;
+    axis2_hash_entry_t *iter;
+    axis2_hash_entry_t *ent;
+    unsigned int i,j,k;
+
+#if AXIS2_POOL_DEBUG
+    /* we don't copy keys and values, so it's necessary that
+     * overlay->a.environment and base->a.environment have a life span at least
+     * as long as p
+     */
+    if (!axis2_environment_is_ancestor(overlay->environment, p)) {
+        fprintf(stderr,
+                "axis2_hash_merge: overlay's environment is not an ancestor of p\n");
+        abort();
+    }
+    if (!axis2_environment_is_ancestor(base->environment, p)) {
+        fprintf(stderr,
+                "axis2_hash_merge: base's environment is not an ancestor of p\n");
+        abort();
+    }
+#endif
+
+    res = axis2_malloc(environment->allocator, sizeof(axis2_hash_t));
+    res->environment = environment;
+    res->free = NULL;
+    res->hash_func = base->hash_func;
+    res->count = base->count;
+    res->max = (overlay->max > base->max) ? overlay->max : base->max;
+    if (base->count + overlay->count > res->max) {
+        res->max = res->max * 2 + 1;
+    }
+    res->array = alloc_array(res, res->max);
+    if (base->count + overlay->count) {
+        new_vals = axis2_malloc(environment->allocator, sizeof(axis2_hash_entry_t) *
+                              (base->count + overlay->count));
+    }
+    j = 0;
+    for (k = 0; k <= base->max; k++) {
+        for (iter = base->array[k]; iter; iter = iter->next) {
+            i = iter->hash & res->max;
+            new_vals[j].klen = iter->klen;
+            new_vals[j].key = iter->key;
+            new_vals[j].val = iter->val;
+            new_vals[j].hash = iter->hash;
+            new_vals[j].next = res->array[i];
+            res->array[i] = &new_vals[j];
+            j++;
+        }
+    }
+
+    for (k = 0; k <= overlay->max; k++) {
+        for (iter = overlay->array[k]; iter; iter = iter->next) {
+            i = iter->hash & res->max;
+            for (ent = res->array[i]; ent; ent = ent->next) {
+                if ((ent->klen == iter->klen) &&
+                    (memcmp(ent->key, iter->key, iter->klen) == 0)) {
+                    if (merger) {
+                        ent->val = (*merger)(environment, iter->key, iter->klen,
+                                             iter->val, ent->val, data);
+                    }
+                    else {
+                        ent->val = iter->val;
+                    }
+                    break;
+                }
+            }
+            if (!ent) {
+                new_vals[j].klen = iter->klen;
+                new_vals[j].key = iter->key;
+                new_vals[j].val = iter->val;
+                new_vals[j].hash = iter->hash;
+                new_vals[j].next = res->array[i];
+                res->array[i] = &new_vals[j];
+                res->count++;
+                j++;
+            }
+        }
+    }
+    return res;
+}
+



Mime
View raw message