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
)
93 for (n
= 0; n
< mpdm_size(h
); n
++) {
94 mpdm_t b
= mpdm_aget(h
, n
);
104 static mpdm_t
hget(const mpdm_t h
, const mpdm_t k
, const wchar_t *ks
)
111 /* if hash is not empty... */
113 if ((b
= mpdm_aget(h
, HASH_BUCKET_S(h
, ks
))) != NULL
)
114 n
= mpdm_bseek_s(b
, ks
, 2, NULL
);
117 if ((b
= mpdm_aget(h
, HASH_BUCKET(h
, k
))) != NULL
)
118 n
= mpdm_bseek(b
, k
, 2, NULL
);
122 v
= mpdm_aget(b
, n
+ 1);
130 * mpdm_hget - Gets a value from a hash.
134 * Gets the value from the hash @h having @k as key, or
135 * NULL if the key does not exist.
138 mpdm_t
mpdm_hget(const mpdm_t h
, const mpdm_t k
)
140 return hget(h
, k
, NULL
);
145 * mpdm_hget_s - Gets the value from a hash (string version).
149 * Gets the value from the hash @h having @k as key, or
150 * NULL if the key does not exist.
153 mpdm_t
mpdm_hget_s(const mpdm_t h
, const wchar_t *k
)
155 return hget(h
, NULL
, k
);
160 * mpdm_exists - Tests if a key exists.
164 * Returns 1 if @k is defined in @h, or 0 othersize.
167 int mpdm_exists(const mpdm_t h
, const mpdm_t k
)
177 /* if hash is not empty... */
178 if ((b
= mpdm_aget(h
, HASH_BUCKET(h
, k
))) != NULL
) {
179 /* if bucket exists, binary-seek it */
180 if ((n
= mpdm_bseek(b
, k
, 2, NULL
)) >= 0)
192 static mpdm_t
hset(mpdm_t h
, mpdm_t k
, const wchar_t *ks
, mpdm_t v
)
201 /* if hash is empty, create an optimal number of buckets */
202 if (mpdm_size(h
) == 0)
203 mpdm_expand(h
, 0, mpdm
->hash_buckets
);
205 n
= ks
? HASH_BUCKET_S(h
, ks
) : HASH_BUCKET(h
, k
);
207 if ((b
= mpdm_aget(h
, n
)) != NULL
) {
210 /* bucket exists; try to find the key there */
211 n
= ks
? mpdm_bseek_s(b
, ks
, 2, &pos
) : mpdm_bseek(b
, k
, 2, &pos
);
214 /* the pair does not exist: create it */
216 mpdm_expand(b
, n
, 2);
218 mpdm_aset(b
, ks
? MPDM_S(ks
) : k
, n
);
222 /* the bucket does not exist; create it */
225 /* put the bucket into the hash */
232 mpdm_aset(b
, ks
? MPDM_S(ks
) : k
, n
);
235 r
= mpdm_aset(b
, v
, n
+ 1);
246 * mpdm_hset - Sets a value in a hash.
251 * Sets the value @v to the key @k in the hash @h. Returns
252 * the new value (versions prior to 1.0.10 returned the old
256 mpdm_t
mpdm_hset(mpdm_t h
, mpdm_t k
, mpdm_t v
)
258 return hset(h
, k
, NULL
, v
);
263 * mpdm_hset_s - Sets a value in a hash (string version).
268 * Sets the value @v to the key @k in the hash @h. Returns
269 * the new value (versions prior to 1.0.10 returned the old
273 mpdm_t
mpdm_hset_s(mpdm_t h
, const wchar_t *k
, mpdm_t v
)
275 return hset(h
, NULL
, k
, v
);
280 * mpdm_hdel - Deletes a key from a hash.
284 * Deletes the key @k from the hash @h. Returns NULL
285 * (versions prior to 1.0.10 returned the deleted value).
288 mpdm_t
mpdm_hdel(mpdm_t h
, const mpdm_t k
)
296 if ((b
= mpdm_aget(h
, HASH_BUCKET(h
, k
))) != NULL
) {
298 if ((n
= mpdm_bseek(b
, k
, 2, NULL
)) >= 0) {
299 /* collapse the bucket */
300 mpdm_collapse(b
, n
, 2);
312 * mpdm_keys - Returns the keys of a hash.
315 * Returns an array containing all the keys of the @h hash.
319 mpdm_t
mpdm_keys(const mpdm_t h
)
326 /* create an array with the same number of elements */
327 a
= MPDM_A(mpdm_hsize(h
));
330 while (mpdm_iterator(h
, &c
, &k
, NULL
))
331 mpdm_aset(a
, k
, n
++);
340 * mpdm_interator - Iterates through the content of a hash or array.
341 * @h: the hash (or array)
342 * @context: A pointer to an opaque context
343 * @v1: a pointer to a value
344 * @v2: another pointer to a value
346 * Iterates through the content of a hash, filling the @v1 and @v2
347 * pointers with key-value pairs on each call until the hash is
348 * exhausted. If @h is an array, only the @v1 pointer is filled.
349 * @v1 and @v2 pointers can be NULL.
351 * The @context pointer to integer is opaque and should be
352 * initialized to zero on the first call.
354 * Returns 0 if no more data is left in @h.
358 int mpdm_iterator(mpdm_t h
, int *context
, mpdm_t
*v1
, mpdm_t
*v2
)
371 if (MPDM_IS_HASH(h
)) {
375 /* get bucket and element index */
376 bi
= (*context
) % mpdm_size(h
);
377 ei
= (*context
) / mpdm_size(h
);
379 while (ret
== 0 && bi
< mpdm_size(h
)) {
382 /* if bucket is empty or there is no more elements in it, pick next */
383 if ((b
= mpdm_aget(h
, bi
)) == NULL
|| ei
>= mpdm_size(b
)) {
390 *v1
= mpdm_aget(b
, ei
++);
391 *v2
= mpdm_aget(b
, ei
++);
394 *context
= (ei
* mpdm_size(h
)) + bi
;
400 if (MPDM_IS_ARRAY(h
)) {
401 if (*context
< mpdm_size(h
)) {
402 *v1
= mpdm_aget(h
, (*context
)++);
413 static mpdm_t
mpdm_sym(mpdm_t r
, mpdm_t k
, mpdm_t v
, int s
)
425 /* splits the path, if needed */
426 if (k
->flags
& MPDM_MULTIPLE
)
429 p
= mpdm_ref(mpdm_split_s(L
".", k
));
433 for (n
= 0; n
< mpdm_size(p
) - s
; n
++) {
435 /* is executable? run it and take its output */
436 while (MPDM_IS_EXEC(w
))
437 w
= mpdm_exec(w
, NULL
);
440 w
= mpdm_hget(w
, mpdm_aget(p
, n
));
442 if (MPDM_IS_ARRAY(w
)) {
443 int i
= mpdm_ival(mpdm_aget(p
, n
));
447 mpdm_unref(mpdm_ref(w
));
455 /* if want to set, do it */
456 if (s
&& w
!= NULL
) {
457 /* resolve executable values again */
458 while (MPDM_IS_EXEC(w
))
459 w
= mpdm_exec(w
, NULL
);
461 if (w
->flags
& MPDM_HASH
)
462 w
= mpdm_hset(w
, mpdm_aget(p
, n
), v
);
464 int i
= mpdm_ival(mpdm_aget(p
, n
));
465 w
= mpdm_aset(w
, v
, i
);
478 mpdm_t
mpdm_sget(mpdm_t r
, mpdm_t k
)
480 return mpdm_sym(r
, k
, NULL
, 0);
484 mpdm_t
mpdm_sset(mpdm_t r
, mpdm_t k
, mpdm_t v
)
486 return mpdm_sym(r
, k
, v
, 1);