3 MPDM - Minimum Profit Data Manager
4 Copyright (C) 2003/2010 Angel Ortega <angel@triptico.com>
6 mpdm_h.c - Hash management
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License
10 as published by the Free Software Foundation; either version 2
11 of the License, or (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 http://www.triptico.com
37 /* prototype for the one-time wrapper hash function */
38 static int switch_hash_func(const wchar_t *, int);
40 /* pointer to the hashing function */
41 static int (*mpdm_hash_func
) (const wchar_t *, int) = switch_hash_func
;
43 static int standard_hash_func(const wchar_t * string
, int mod
)
44 /* computes a hashing function on string */
48 for (c
= 0; *string
!= L
'\0'; string
++)
55 static int null_hash_func(const wchar_t * string
, int mod
)
60 static int switch_hash_func(const wchar_t * string
, int mod
)
61 /* one-time wrapper for hash method autodetection */
63 /* commute the real hashing function on
64 having the MPDM_NULL_HASH environment variable set */
65 if (getenv("MPDM_NULL_HASH") != NULL
)
66 mpdm_hash_func
= null_hash_func
;
68 mpdm_hash_func
= standard_hash_func
;
70 /* and fall back to it */
71 return mpdm_hash_func(string
, mod
);
74 #define HASH_BUCKET_S(h, k) mpdm_hash_func(k, mpdm_size(h))
75 #define HASH_BUCKET(h, k) (mpdm_hash_func(mpdm_string(k), mpdm_size(h)))
80 * mpdm_hsize - Returns the number of pairs of a hash.
83 * Returns the number of key-value pairs of a hash.
86 int mpdm_hsize(const mpdm_t h
)
91 for (n
= 0; n
< mpdm_size(h
); n
++) {
92 mpdm_t b
= mpdm_aget(h
, n
);
100 static mpdm_t
hget(const mpdm_t h
, const mpdm_t k
, const wchar_t *ks
)
107 /* if hash is not empty... */
109 if ((b
= mpdm_aget(h
, HASH_BUCKET_S(h
, ks
))) != NULL
)
110 n
= mpdm_bseek_s(b
, ks
, 2, NULL
);
113 if ((b
= mpdm_aget(h
, HASH_BUCKET(h
, k
))) != NULL
)
114 n
= mpdm_bseek(b
, k
, 2, NULL
);
118 v
= mpdm_aget(b
, n
+ 1);
126 * mpdm_hget - Gets a value from a hash.
130 * Gets the value from the hash @h having @k as key, or
131 * NULL if the key does not exist.
134 mpdm_t
mpdm_hget(const mpdm_t h
, const mpdm_t k
)
136 return hget(h
, k
, NULL
);
141 * mpdm_hget_s - Gets the value from a hash (string version).
145 * Gets the value from the hash @h having @k as key, or
146 * NULL if the key does not exist.
149 mpdm_t
mpdm_hget_s(const mpdm_t h
, const wchar_t *k
)
151 return hget(h
, NULL
, k
);
156 * mpdm_exists - Tests if a key exists.
160 * Returns 1 if @k is defined in @h, or 0 othersize.
163 int mpdm_exists(const mpdm_t h
, const mpdm_t k
)
170 /* if hash is not empty... */
171 if ((b
= mpdm_aget(h
, HASH_BUCKET(h
, k
))) != NULL
) {
172 /* if bucket exists, binary-seek it */
173 if ((n
= mpdm_bseek(b
, k
, 2, NULL
)) >= 0)
182 static mpdm_t
hset(mpdm_t h
, mpdm_t k
, const wchar_t *ks
, mpdm_t v
)
187 /* if hash is empty, create an optimal number of buckets */
188 if (mpdm_size(h
) == 0)
189 mpdm_expand(h
, 0, mpdm
->hash_buckets
);
191 n
= ks
? HASH_BUCKET_S(h
, ks
) : HASH_BUCKET(h
, k
);
193 if ((b
= mpdm_aget(h
, n
)) != NULL
) {
196 /* bucket exists; try to find the key there */
197 n
= ks
? mpdm_bseek_s(b
, ks
, 2, &pos
) : mpdm_bseek(b
, k
, 2, &pos
);
200 /* the pair does not exist: create it */
202 mpdm_expand(b
, n
, 2);
204 mpdm_aset(b
, ks
? MPDM_S(ks
) : k
, n
);
208 /* the bucket does not exist; create it */
211 /* put the bucket into the hash */
218 mpdm_aset(b
, ks
? MPDM_S(ks
) : k
, n
);
221 return mpdm_aset(b
, v
, n
+ 1);
226 * mpdm_hset - Sets a value in a hash.
231 * Sets the value @v to the key @k in the hash @h. Returns
232 * the new value (versions prior to 1.0.10 returned the old
236 mpdm_t
mpdm_hset(mpdm_t h
, mpdm_t k
, mpdm_t v
)
238 return hset(h
, k
, NULL
, v
);
243 * mpdm_hset_s - Sets a value in a hash (string version).
248 * Sets the value @v to the key @k in the hash @h. Returns
249 * the new value (versions prior to 1.0.10 returned the old
253 mpdm_t
mpdm_hset_s(mpdm_t h
, const wchar_t *k
, mpdm_t v
)
255 return hset(h
, NULL
, k
, v
);
260 * mpdm_hdel - Deletes a key from a hash.
264 * Deletes the key @k from the hash @h. Returns NULL
265 * (versions prior to 1.0.10 returned the deleted value).
268 mpdm_t
mpdm_hdel(mpdm_t h
, const mpdm_t k
)
273 if ((b
= mpdm_aget(h
, HASH_BUCKET(h
, k
))) != NULL
) {
275 if ((n
= mpdm_bseek(b
, k
, 2, NULL
)) >= 0) {
276 /* collapse the bucket */
277 mpdm_collapse(b
, n
, 2);
286 * mpdm_keys - Returns the keys of a hash.
289 * Returns an array containing all the keys of the @h hash.
293 mpdm_t
mpdm_keys(const mpdm_t h
)
298 /* create an array with the same number of elements */
299 a
= MPDM_A(mpdm_hsize(h
));
302 while (mpdm_iterator(h
, &c
, &k
, NULL
))
303 mpdm_aset(a
, k
, n
++);
310 * mpdm_interator - Iterates through the content of a hash or array.
311 * @h: the hash (or array)
312 * @context: A pointer to an opaque context
313 * @v1: a pointer to a value
314 * @v2: another pointer to a value
316 * Iterates through the content of a hash, filling the @v1 and @v2
317 * pointers with key-value pairs on each call until the hash is
318 * exhausted. If @h is an array, only the @v1 pointer is filled.
319 * @v1 and @v2 pointers can be NULL.
321 * The @context pointer to integer is opaque and should be
322 * initialized to zero on the first call.
324 * Returns 0 if no more data is left in @h.
328 int mpdm_iterator(mpdm_t h
, int *context
, mpdm_t
*v1
, mpdm_t
*v2
)
338 if (MPDM_IS_HASH(h
)) {
344 /* get bucket and element index */
345 bi
= (*context
) % mpdm_size(h
);
346 ei
= (*context
) / mpdm_size(h
);
348 while (bi
< mpdm_size(h
)) {
351 /* if bucket is empty or there is no more elements in it, pick next */
352 if ((b
= mpdm_aget(h
, bi
)) == NULL
|| ei
>= mpdm_size(b
)) {
359 *v1
= mpdm_aget(b
, ei
++);
360 *v2
= mpdm_aget(b
, ei
++);
363 *context
= (ei
* mpdm_size(h
)) + bi
;
368 if (MPDM_IS_ARRAY(h
)) {
369 if (*context
< mpdm_size(h
)) {
370 *v1
= mpdm_aget(h
, (*context
)++);
379 static mpdm_t
mpdm_sym(mpdm_t r
, mpdm_t k
, mpdm_t v
, int s
)
388 /* splits the path, if needed */
389 if (k
->flags
& MPDM_MULTIPLE
)
392 w
= p
= mpdm_split_s(L
".", k
);
394 for (n
= 0; n
< mpdm_size(p
) - s
; n
++) {
396 /* is executable? run it and take its output */
397 while (MPDM_IS_EXEC(r
)) {
398 mpdm_t t
= mpdm_ref(r
);
399 r
= mpdm_exec(t
, NULL
);
404 r
= mpdm_hget(r
, mpdm_aget(p
, n
));
406 if (MPDM_IS_ARRAY(r
)) {
407 int i
= mpdm_ival(mpdm_aget(p
, n
));
417 /* if want to set, do it */
418 if (s
&& r
!= NULL
) {
419 /* resolve executable values again */
420 while (MPDM_IS_EXEC(r
)) {
421 mpdm_t t
= mpdm_ref(r
);
422 r
= mpdm_exec(t
, NULL
);
426 if (r
->flags
& MPDM_HASH
)
427 r
= mpdm_hset(r
, mpdm_aget(p
, n
), v
);
429 int i
= mpdm_ival(mpdm_aget(p
, n
));
430 r
= mpdm_aset(r
, v
, i
);
440 mpdm_t
mpdm_sget(mpdm_t r
, mpdm_t k
)
442 return mpdm_sym(r
, k
, NULL
, 0);
446 mpdm_t
mpdm_sset(mpdm_t r
, mpdm_t k
, mpdm_t v
)
448 return mpdm_sym(r
, k
, v
, 1);