Ref/unref all args in mpdm_hset().
[mpdm.git] / mpdm_h.c
blobbcf8a4706f3602109a9895f27551dfa80bb3fa29
1 /*
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
26 #include "config.h"
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <wchar.h>
33 #include "mpdm.h"
35 /** code **/
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 */
46 int c;
48 for (c = 0; *string != L'\0'; string++)
49 c ^= (int) *string;
51 return c % mod;
55 static int null_hash_func(const wchar_t * string, int mod)
57 return *string % 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;
67 else
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)))
77 /* interface */
79 /**
80 * mpdm_hsize - Returns the number of pairs of a hash.
81 * @h: the hash
83 * Returns the number of key-value pairs of a hash.
84 * [Hashes]
86 int mpdm_hsize(const mpdm_t h)
88 int n;
89 int ret = 0;
91 mpdm_ref(h);
93 for (n = 0; n < mpdm_size(h); n++) {
94 mpdm_t b = mpdm_aget(h, n);
95 ret += mpdm_size(b);
98 mpdm_unref(h);
100 return ret / 2;
104 static mpdm_t hget(const mpdm_t h, const mpdm_t k, const wchar_t *ks)
106 mpdm_t b;
107 mpdm_t v = NULL;
108 int n = 0;
110 if (mpdm_size(h)) {
111 /* if hash is not empty... */
112 if (ks) {
113 if ((b = mpdm_aget(h, HASH_BUCKET_S(h, ks))) != NULL)
114 n = mpdm_bseek_s(b, ks, 2, NULL);
116 else {
117 if ((b = mpdm_aget(h, HASH_BUCKET(h, k))) != NULL)
118 n = mpdm_bseek(b, k, 2, NULL);
121 if (n >= 0)
122 v = mpdm_aget(b, n + 1);
125 return v;
130 * mpdm_hget - Gets a value from a hash.
131 * @h: the hash
132 * @k: the key
134 * Gets the value from the hash @h having @k as key, or
135 * NULL if the key does not exist.
136 * [Hashes]
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).
146 * @h: the hash
147 * @k: the key
149 * Gets the value from the hash @h having @k as key, or
150 * NULL if the key does not exist.
151 * [Hashes]
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.
161 * @h: the hash
162 * @k: the key
164 * Returns 1 if @k is defined in @h, or 0 othersize.
165 * [Hashes]
167 int mpdm_exists(const mpdm_t h, const mpdm_t k)
169 mpdm_t b;
170 int n;
171 int ret = 0;
173 mpdm_ref(h);
174 mpdm_ref(k);
176 if (mpdm_size(h)) {
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)
181 ret = 1;
185 mpdm_unref(k);
186 mpdm_unref(h);
188 return ret;
192 static mpdm_t hset(mpdm_t h, mpdm_t k, const wchar_t *ks, mpdm_t v)
194 mpdm_t b, r;
195 int n;
197 mpdm_ref(h);
198 mpdm_ref(k);
199 mpdm_ref(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) {
208 int pos;
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);
213 if (n < 0) {
214 /* the pair does not exist: create it */
215 n = pos;
216 mpdm_expand(b, n, 2);
218 mpdm_aset(b, ks ? MPDM_S(ks) : k, n);
221 else {
222 /* the bucket does not exist; create it */
223 b = MPDM_A(2);
225 /* put the bucket into the hash */
226 mpdm_aset(h, b, n);
228 /* offset 0 */
229 n = 0;
231 /* insert the key */
232 mpdm_aset(b, ks ? MPDM_S(ks) : k, n);
235 r = mpdm_aset(b, v, n + 1);
237 mpdm_unref(v);
238 mpdm_unref(k);
239 mpdm_unref(h);
241 return r;
246 * mpdm_hset - Sets a value in a hash.
247 * @h: the hash
248 * @k: the key
249 * @v: the value
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
253 * value).
254 * [Hashes]
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).
264 * @h: the hash
265 * @k: the key
266 * @v: the value
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
270 * value).
271 * [Hashes]
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.
281 * @h: the hash
282 * @k: the key
284 * Deletes the key @k from the hash @h. Returns NULL
285 * (versions prior to 1.0.10 returned the deleted value).
286 * [Hashes]
288 mpdm_t mpdm_hdel(mpdm_t h, const mpdm_t k)
290 mpdm_t b;
291 int n;
293 mpdm_ref(h);
294 mpdm_ref(k);
296 if ((b = mpdm_aget(h, HASH_BUCKET(h, k))) != NULL) {
297 /* bucket exists */
298 if ((n = mpdm_bseek(b, k, 2, NULL)) >= 0) {
299 /* collapse the bucket */
300 mpdm_collapse(b, n, 2);
304 mpdm_unref(k);
305 mpdm_unref(h);
307 return NULL;
312 * mpdm_keys - Returns the keys of a hash.
313 * @h: the hash
315 * Returns an array containing all the keys of the @h hash.
316 * [Hashes]
317 * [Arrays]
319 mpdm_t mpdm_keys(const mpdm_t h)
321 int n, c;
322 mpdm_t a, k;
324 mpdm_ref(h);
326 /* create an array with the same number of elements */
327 a = MPDM_A(mpdm_hsize(h));
329 c = n = 0;
330 while (mpdm_iterator(h, &c, &k, NULL))
331 mpdm_aset(a, k, n++);
333 mpdm_unref(h);
335 return a;
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.
355 * [Hashes]
356 * [Arrays]
358 int mpdm_iterator(mpdm_t h, int *context, mpdm_t *v1, mpdm_t *v2)
360 int ret = 0;
361 mpdm_t w1, w2;
363 mpdm_ref(h);
365 if (v1 == NULL)
366 v1 = &w1;
368 if (v2 == NULL)
369 v2 = &w2;
371 if (MPDM_IS_HASH(h)) {
372 int bi, ei;
374 if (mpdm_size(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)) {
380 mpdm_t b;
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)) {
384 ei = 0;
385 bi++;
386 continue;
389 /* get pair */
390 *v1 = mpdm_aget(b, ei++);
391 *v2 = mpdm_aget(b, ei++);
393 /* update context */
394 *context = (ei * mpdm_size(h)) + bi;
395 ret = 1;
399 else
400 if (MPDM_IS_ARRAY(h)) {
401 if (*context < mpdm_size(h)) {
402 *v1 = mpdm_aget(h, (*context)++);
403 ret = 1;
407 mpdm_unref(h);
409 return ret;
413 static mpdm_t mpdm_sym(mpdm_t r, mpdm_t k, mpdm_t v, int s)
415 int n;
416 mpdm_t p, w;
418 if (r == NULL)
419 r = mpdm_root();
421 mpdm_ref(r);
422 mpdm_ref(k);
423 mpdm_ref(v);
425 /* splits the path, if needed */
426 if (k->flags & MPDM_MULTIPLE)
427 p = mpdm_ref(k);
428 else
429 p = mpdm_ref(mpdm_split_s(L".", k));
431 w = r;
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);
439 if (MPDM_IS_HASH(w))
440 w = mpdm_hget(w, mpdm_aget(p, n));
441 else
442 if (MPDM_IS_ARRAY(w)) {
443 int i = mpdm_ival(mpdm_aget(p, n));
444 w = mpdm_aget(w, i);
446 else {
447 mpdm_unref(mpdm_ref(w));
448 w = NULL;
451 if (w == NULL)
452 break;
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);
463 else {
464 int i = mpdm_ival(mpdm_aget(p, n));
465 w = mpdm_aset(w, v, i);
469 mpdm_unref(p);
470 mpdm_unref(v);
471 mpdm_unref(k);
472 mpdm_unref(r);
474 return w;
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);