Fixed mpdm_sregex().
[mpdm.git] / mpdm_h.c
blob1dd5014d48fbded749b7fa0208fd8669cc393ccc
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 mpdm_ref(h);
111 mpdm_ref(k);
113 if (mpdm_size(h)) {
114 /* if hash is not empty... */
115 if (ks) {
116 if ((b = mpdm_aget(h, HASH_BUCKET_S(h, ks))) != NULL)
117 n = mpdm_bseek_s(b, ks, 2, NULL);
119 else {
120 if ((b = mpdm_aget(h, HASH_BUCKET(h, k))) != NULL)
121 n = mpdm_bseek(b, k, 2, NULL);
124 if (n >= 0)
125 v = mpdm_aget(b, n + 1);
128 mpdm_unref(k);
129 mpdm_unref(h);
131 return v;
136 * mpdm_hget - Gets a value from a hash.
137 * @h: the hash
138 * @k: the key
140 * Gets the value from the hash @h having @k as key, or
141 * NULL if the key does not exist.
142 * [Hashes]
144 mpdm_t mpdm_hget(const mpdm_t h, const mpdm_t k)
146 return hget(h, k, NULL);
151 * mpdm_hget_s - Gets the value from a hash (string version).
152 * @h: the hash
153 * @k: the key
155 * Gets the value from the hash @h having @k as key, or
156 * NULL if the key does not exist.
157 * [Hashes]
159 mpdm_t mpdm_hget_s(const mpdm_t h, const wchar_t *k)
161 return hget(h, NULL, k);
166 * mpdm_exists - Tests if a key exists.
167 * @h: the hash
168 * @k: the key
170 * Returns 1 if @k is defined in @h, or 0 othersize.
171 * [Hashes]
173 int mpdm_exists(const mpdm_t h, const mpdm_t k)
175 mpdm_t b;
176 int n;
177 int ret = 0;
179 mpdm_ref(h);
180 mpdm_ref(k);
182 if (mpdm_size(h)) {
183 /* if hash is not empty... */
184 if ((b = mpdm_aget(h, HASH_BUCKET(h, k))) != NULL) {
185 /* if bucket exists, binary-seek it */
186 if ((n = mpdm_bseek(b, k, 2, NULL)) >= 0)
187 ret = 1;
191 mpdm_unref(k);
192 mpdm_unref(h);
194 return ret;
198 static mpdm_t hset(mpdm_t h, mpdm_t k, const wchar_t *ks, mpdm_t v)
200 mpdm_t b, r;
201 int n;
203 mpdm_ref(h);
204 mpdm_ref(k);
205 mpdm_ref(v);
207 /* if hash is empty, create an optimal number of buckets */
208 if (mpdm_size(h) == 0)
209 mpdm_expand(h, 0, mpdm->hash_buckets);
211 n = ks ? HASH_BUCKET_S(h, ks) : HASH_BUCKET(h, k);
213 if ((b = mpdm_aget(h, n)) != NULL) {
214 int pos;
216 /* bucket exists; try to find the key there */
217 n = ks ? mpdm_bseek_s(b, ks, 2, &pos) : mpdm_bseek(b, k, 2, &pos);
219 if (n < 0) {
220 /* the pair does not exist: create it */
221 n = pos;
222 mpdm_expand(b, n, 2);
224 mpdm_aset(b, ks ? MPDM_S(ks) : k, n);
227 else {
228 /* the bucket does not exist; create it */
229 b = MPDM_A(2);
231 /* put the bucket into the hash */
232 mpdm_aset(h, b, n);
234 /* offset 0 */
235 n = 0;
237 /* insert the key */
238 mpdm_aset(b, ks ? MPDM_S(ks) : k, n);
241 r = mpdm_aset(b, v, n + 1);
243 mpdm_unref(v);
244 mpdm_unref(k);
245 mpdm_unref(h);
247 return r;
252 * mpdm_hset - Sets a value in a hash.
253 * @h: the hash
254 * @k: the key
255 * @v: the value
257 * Sets the value @v to the key @k in the hash @h. Returns
258 * the new value (versions prior to 1.0.10 returned the old
259 * value).
260 * [Hashes]
262 mpdm_t mpdm_hset(mpdm_t h, mpdm_t k, mpdm_t v)
264 return hset(h, k, NULL, v);
269 * mpdm_hset_s - Sets a value in a hash (string version).
270 * @h: the hash
271 * @k: the key
272 * @v: the value
274 * Sets the value @v to the key @k in the hash @h. Returns
275 * the new value (versions prior to 1.0.10 returned the old
276 * value).
277 * [Hashes]
279 mpdm_t mpdm_hset_s(mpdm_t h, const wchar_t *k, mpdm_t v)
281 return hset(h, NULL, k, v);
286 * mpdm_hdel - Deletes a key from a hash.
287 * @h: the hash
288 * @k: the key
290 * Deletes the key @k from the hash @h. Returns NULL
291 * (versions prior to 1.0.10 returned the deleted value).
292 * [Hashes]
294 mpdm_t mpdm_hdel(mpdm_t h, const mpdm_t k)
296 mpdm_t b;
297 int n;
299 mpdm_ref(h);
300 mpdm_ref(k);
302 if ((b = mpdm_aget(h, HASH_BUCKET(h, k))) != NULL) {
303 /* bucket exists */
304 if ((n = mpdm_bseek(b, k, 2, NULL)) >= 0) {
305 /* collapse the bucket */
306 mpdm_collapse(b, n, 2);
310 mpdm_unref(k);
311 mpdm_unref(h);
313 return NULL;
318 * mpdm_keys - Returns the keys of a hash.
319 * @h: the hash
321 * Returns an array containing all the keys of the @h hash.
322 * [Hashes]
323 * [Arrays]
325 mpdm_t mpdm_keys(const mpdm_t h)
327 int n, c;
328 mpdm_t a, k;
330 mpdm_ref(h);
332 /* create an array with the same number of elements */
333 a = MPDM_A(mpdm_hsize(h));
335 mpdm_ref(a);
337 c = n = 0;
338 while (mpdm_iterator(h, &c, &k, NULL))
339 mpdm_aset(a, k, n++);
341 mpdm_unrefnd(a);
343 mpdm_unref(h);
345 return a;
350 * mpdm_interator - Iterates through the content of a hash or array.
351 * @h: the hash (or array)
352 * @context: A pointer to an opaque context
353 * @v1: a pointer to a value
354 * @v2: another pointer to a value
356 * Iterates through the content of a hash, filling the @v1 and @v2
357 * pointers with key-value pairs on each call until the hash is
358 * exhausted. If @h is an array, only the @v1 pointer is filled.
359 * @v1 and @v2 pointers can be NULL.
361 * The @context pointer to integer is opaque and should be
362 * initialized to zero on the first call.
364 * Returns 0 if no more data is left in @h.
365 * [Hashes]
366 * [Arrays]
368 int mpdm_iterator(mpdm_t h, int *context, mpdm_t *v1, mpdm_t *v2)
370 int ret = 0;
371 mpdm_t w1, w2;
373 mpdm_ref(h);
375 if (v1 == NULL)
376 v1 = &w1;
378 if (v2 == NULL)
379 v2 = &w2;
381 if (MPDM_IS_HASH(h)) {
382 int bi, ei;
384 if (mpdm_size(h)) {
385 /* get bucket and element index */
386 bi = (*context) % mpdm_size(h);
387 ei = (*context) / mpdm_size(h);
389 while (ret == 0 && bi < mpdm_size(h)) {
390 mpdm_t b;
392 /* if bucket is empty or there is no more elements in it, pick next */
393 if ((b = mpdm_aget(h, bi)) == NULL || ei >= mpdm_size(b)) {
394 ei = 0;
395 bi++;
396 continue;
399 /* get pair */
400 *v1 = mpdm_aget(b, ei++);
401 *v2 = mpdm_aget(b, ei++);
403 /* update context */
404 *context = (ei * mpdm_size(h)) + bi;
405 ret = 1;
409 else
410 if (MPDM_IS_ARRAY(h)) {
411 if (*context < mpdm_size(h)) {
412 *v1 = mpdm_aget(h, (*context)++);
413 ret = 1;
417 mpdm_unref(h);
419 return ret;
423 static mpdm_t mpdm_sym(mpdm_t r, mpdm_t k, mpdm_t v, int s)
425 int n;
426 mpdm_t p, w;
428 if (r == NULL)
429 r = mpdm_root();
431 mpdm_ref(r);
432 mpdm_ref(k);
433 mpdm_ref(v);
435 /* splits the path, if needed */
436 if (k->flags & MPDM_MULTIPLE)
437 p = mpdm_ref(k);
438 else
439 p = mpdm_ref(mpdm_split_s(L".", k));
441 w = r;
443 for (n = 0; n < mpdm_size(p) - s; n++) {
445 /* is executable? run it and take its output */
446 while (MPDM_IS_EXEC(w))
447 w = mpdm_exec(w, NULL, NULL);
449 if (MPDM_IS_HASH(w))
450 w = mpdm_hget(w, mpdm_aget(p, n));
451 else
452 if (MPDM_IS_ARRAY(w)) {
453 int i = mpdm_ival(mpdm_aget(p, n));
454 w = mpdm_aget(w, i);
456 else {
457 mpdm_unref(mpdm_ref(w));
458 w = NULL;
461 if (w == NULL)
462 break;
465 /* if want to set, do it */
466 if (s && w != NULL) {
467 /* resolve executable values again */
468 while (MPDM_IS_EXEC(w))
469 w = mpdm_exec(w, NULL, NULL);
471 if (w->flags & MPDM_HASH)
472 w = mpdm_hset(w, mpdm_aget(p, n), v);
473 else {
474 int i = mpdm_ival(mpdm_aget(p, n));
475 w = mpdm_aset(w, v, i);
479 mpdm_unref(p);
480 mpdm_unref(v);
481 mpdm_unref(k);
482 mpdm_unref(r);
484 return w;
488 mpdm_t mpdm_sget(mpdm_t r, mpdm_t k)
490 return mpdm_sym(r, k, NULL, 0);
494 mpdm_t mpdm_sset(mpdm_t r, mpdm_t k, mpdm_t v)
496 return mpdm_sym(r, k, v, 1);