New Makefile target realdistclean.
[mpdm.git] / mpdm_a.c
blob1f23d9da4a72fae351e4dca7d6a7280244f61c73
1 /*
3 MPDM - Minimum Profit Data Manager
4 Copyright (C) 2003/2007 Angel Ortega <angel@triptico.com>
6 mpdm_a.c - Array 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"
36 /*******************
37 Data
38 ********************/
40 /* sorting callback code */
41 static mpdm_t sort_cb = NULL;
44 /*******************
45 Code
46 ********************/
48 mpdm_t mpdm_new_a(int flags, int size)
49 /* creates a new array value */
51 mpdm_t v;
53 /* creates and expands */
54 if ((v = mpdm_new(flags | MPDM_MULTIPLE, NULL, 0)) != NULL)
55 mpdm_expand(v, 0, size);
57 return v;
61 static int wrap_offset(mpdm_t a, int offset)
62 /* manages negative offsets */
64 if (offset < 0)
65 offset = mpdm_size(a) + offset;
67 return offset;
71 mpdm_t mpdm_aclone(mpdm_t v)
72 /* clones a multiple value */
74 mpdm_t w;
75 int n;
77 /* creates a similar value */
78 w = mpdm_new_a(v->flags, v->size);
80 /* fills each element with duplicates of the original */
81 for (n = 0; n < w->size; n++)
82 mpdm_aset(w, mpdm_clone(mpdm_aget(v, n)), n);
84 return w;
88 /* interface */
90 /**
91 * mpdm_expand - Expands an array.
92 * @a: the array
93 * @offset: insertion offset
94 * @num: number of elements to insert
96 * Expands an array value, inserting @num elements (initialized
97 * to NULL) at the specified @offset.
98 * [Arrays]
100 mpdm_t mpdm_expand(mpdm_t a, int offset, int num)
102 int n;
103 mpdm_t *p;
105 /* sanity checks */
106 if (num <= 0)
107 return a;
109 /* add size */
110 a->size += num;
112 /* expand, or fail */
113 if ((p = (mpdm_t *) realloc(a->data, a->size * sizeof(mpdm_t))) == NULL)
114 return NULL;
116 /* moves up from top of the array */
117 for (n = a->size - 1; n >= offset + num; n--)
118 p[n] = p[n - num];
120 /* fills the new space with blanks */
121 for (; n >= offset; n--)
122 p[n] = NULL;
124 a->data = p;
126 return a;
131 * mpdm_collapse - Collapses an array.
132 * @a: the array
133 * @offset: deletion offset
134 * @num: number of elements to collapse
136 * Collapses an array value, deleting @num elements at
137 * the specified @offset.
138 * [Arrays]
140 mpdm_t mpdm_collapse(mpdm_t a, int offset, int num)
142 int n;
143 mpdm_t *p;
145 /* sanity checks */
146 if (num <= 0)
147 return a;
149 /* don't try to delete beyond the limit */
150 if (offset + num > a->size)
151 num = a->size - offset;
153 p = (mpdm_t *) a->data;
155 /* unrefs the about-to-be-deleted elements */
156 for (n = offset; n < offset + num; n++)
157 mpdm_unref(p[n]);
159 /* array is now shorter */
160 a->size -= num;
162 /* moves down the elements */
163 for (n = offset; n < a->size; n++)
164 p[n] = p[n + num];
166 /* finally shrinks the memory block */
167 if ((a->data = realloc(p, a->size * sizeof(mpdm_t))) == NULL)
168 return NULL;
170 return a;
175 * mpdm_aset - Sets the value of an array's element.
176 * @a: the array
177 * @e: the element to be assigned
178 * @offset: the subscript of the element
180 * Sets the element of the array @a at @offset to be the @e value.
181 * Returns the previous element.
182 * [Arrays]
184 mpdm_t mpdm_aset(mpdm_t a, mpdm_t e, int offset)
186 mpdm_t v;
187 mpdm_t *p;
189 offset = wrap_offset(a, offset);
191 /* boundary checks */
192 if (offset < 0)
193 return NULL;
195 /* if the array is shorter than offset, expand to make room for it */
196 if (offset >= mpdm_size(a))
197 if (mpdm_expand(a, mpdm_size(a), offset - mpdm_size(a) + 1) == NULL)
198 return NULL;
200 p = (mpdm_t *) a->data;
202 /* assigns and references */
203 v = mpdm_unref(p[offset]);
204 p[offset] = mpdm_ref(e);
206 return v;
211 * mpdm_aget - Gets an element of an array.
212 * @a: the array
213 * @offset: the subscript of the element
215 * Returns the element at @offset of the array @a.
216 * [Arrays]
218 mpdm_t mpdm_aget(mpdm_t a, int offset)
220 mpdm_t *p;
222 offset = wrap_offset(a, offset);
224 /* boundary checks */
225 if (offset < 0 || offset >= mpdm_size(a))
226 return NULL;
228 p = (mpdm_t *) a->data;
230 return p[offset];
235 * mpdm_ins - Insert an element in an array.
236 * @a: the array
237 * @e: the element to be inserted
238 * @offset: subscript where the element is going to be inserted
240 * Inserts the @e value in the @a array at @offset.
241 * Further elements are pushed up, so the array increases its size
242 * by one. Returns the inserted element.
243 * [Arrays]
245 mpdm_t mpdm_ins(mpdm_t a, mpdm_t e, int offset)
247 offset = wrap_offset(a, offset);
249 /* open room and set value */
250 if (mpdm_expand(a, offset, 1))
251 mpdm_aset(a, e, offset);
253 return e;
258 * mpdm_adel - Deletes an element of an array.
259 * @a: the array
260 * @offset: subscript of the element to be deleted
262 * Deletes the element at @offset of the @a array. The array
263 * is shrinked by one. If @offset is negative, is counted from
264 * the end of the array (so a value of -1 means delete the
265 * last element of the array).
267 * Returns the deleted element.
268 * [Arrays]
270 mpdm_t mpdm_adel(mpdm_t a, int offset)
272 mpdm_t v;
274 if (a == NULL || mpdm_size(a) == 0)
275 return NULL;
277 offset = wrap_offset(a, offset);
279 /* gets current value */
280 v = mpdm_aget(a, offset);
282 /* shrinks the array */
283 mpdm_collapse(a, offset, 1);
285 return v;
290 * mpdm_shift - Extracts the first element of an array.
291 * @a: the array
293 * Extracts the first element of the array. The array
294 * is shrinked by one.
296 * Returns the deleted element.
297 * [Arrays]
299 mpdm_t mpdm_shift(mpdm_t a)
301 return mpdm_adel(a, 0);
306 * mpdm_push - Pushes a value into an array.
307 * @a: the array
308 * @e: the value
310 * Pushes a value into an array (i.e. inserts at the end).
311 * [Arrays]
313 mpdm_t mpdm_push(mpdm_t a, mpdm_t e)
315 /* inserts at the end */
316 return mpdm_ins(a, e, mpdm_size(a));
321 * mpdm_pop - Pops a value from an array.
322 * @a: the array
324 * Pops a value from the array (i.e. deletes from the end
325 * and returns it).
326 * [Arrays]
328 mpdm_t mpdm_pop(mpdm_t a)
330 /* deletes from the end */
331 return mpdm_adel(a, -1);
336 * mpdm_queue - Implements a queue in an array.
337 * @a: the array
338 * @e: the element to be pushed
339 * @size: maximum size of array
341 * Pushes the @e element into the @a array. If the array already has
342 * @size elements, the first (oldest) element is deleted from the
343 * queue and returned.
345 * Returns the deleted element, or NULL if the array doesn't have
346 * @size elements yet.
347 * [Arrays]
349 mpdm_t mpdm_queue(mpdm_t a, mpdm_t e, int size)
351 mpdm_t v = NULL;
353 /* zero size is nonsense */
354 if (size == 0)
355 return NULL;
357 /* loop until a has the desired size */
358 while (mpdm_size(a) > size - 1)
359 v = mpdm_shift(a);
361 mpdm_push(a, e);
362 return v;
367 * mpdm_seek - Seeks a value in an array (sequential).
368 * @a: the array
369 * @k: the key
370 * @step: number of elements to step
372 * Seeks sequentially the value @k in the @a array in
373 * increments of @step. A complete search should use a step of 1.
374 * Returns the offset of the element if found, or -1 otherwise.
375 * [Arrays]
377 int mpdm_seek(mpdm_t a, mpdm_t k, int step)
379 int n;
381 /* avoid stupid steps */
382 if (step <= 0)
383 step = 1;
385 for (n = 0; n < mpdm_size(a); n += step) {
386 mpdm_t v = mpdm_aget(a, n);
388 if (mpdm_cmp(v, k) == 0)
389 return n;
392 return -1;
397 * mpdm_seek_s - Seeks a value in an array (sequential, string version).
398 * @a: the array
399 * @k: the key
400 * @step: number of elements to step
402 * Seeks sequentially the value @k in the @a array in
403 * increments of @step. A complete search should use a step of 1.
404 * Returns the offset of the element if found, or -1 otherwise.
405 * [Arrays]
407 int mpdm_seek_s(mpdm_t a, wchar_t * k, int step)
409 return mpdm_seek(a, MPDM_LS(k), step);
414 * mpdm_bseek - Seeks a value in an array (binary).
415 * @a: the ordered array
416 * @k: the key
417 * @step: number of elements to step
418 * @pos: the position where the element should be, if it's not found
420 * Seeks the value @k in the @a array in increments of @step.
421 * The array should be sorted to work correctly. A complete search
422 * should use a step of 1.
424 * If the element is found, returns the offset of the element
425 * as a positive number; otherwise, -1 is returned and the position
426 * where the element should be is stored in @pos. You can set @pos
427 * to NULL if you don't mind.
428 * [Arrays]
430 int mpdm_bseek(mpdm_t a, mpdm_t k, int step, int *pos)
432 int b, t, n, c;
434 /* avoid stupid steps */
435 if (step <= 0)
436 step = 1;
438 b = 0;
439 t = (mpdm_size(a) - 1) / step;
441 while (t >= b) {
442 mpdm_t v;
444 n = (b + t) / 2;
445 if ((v = mpdm_aget(a, n * step)) == NULL)
446 break;
448 c = mpdm_cmp(k, v);
450 if (c == 0)
451 return n * step;
452 else
453 if (c < 0)
454 t = n - 1;
455 else
456 b = n + 1;
459 if (pos != NULL)
460 *pos = b * step;
461 return -1;
466 * mpdm_bseek_s - Seeks a value in an array (binary, string version).
467 * @a: the ordered array
468 * @k: the key
469 * @step: number of elements to step
470 * @pos: the position where the element should be, if it's not found
472 * Seeks the value @k in the @a array in increments of @step.
473 * The array should be sorted to work correctly. A complete search
474 * should use a step of 1.
476 * If the element is found, returns the offset of the element
477 * as a positive number; otherwise, -1 is returned and the position
478 * where the element should be is stored in @pos. You can set @pos
479 * to NULL if you don't mind.
480 * [Arrays]
482 int mpdm_bseek_s(mpdm_t a, wchar_t * k, int step, int *pos)
484 return mpdm_bseek(a, MPDM_LS(k), step, pos);
488 static int sort_cmp(const void *s1, const void *s2)
489 /* qsort help function */
491 int ret = 0;
493 /* if callback is NULL, use basic value comparisons */
494 if (sort_cb == NULL)
495 ret = mpdm_cmp(*(mpdm_t *) s1, *(mpdm_t *) s2);
496 else {
497 /* executes the callback and converts to integer */
498 ret = mpdm_ival(mpdm_exec_2(sort_cb,
499 (mpdm_t) * ((mpdm_t *) s1),
500 (mpdm_t) * ((mpdm_t *) s2)));
503 return ret;
508 * mpdm_sort - Sorts an array.
509 * @a: the array
510 * @step: increment step
512 * Sorts the array. @step is the number of elements to group together.
514 * Returns the sorted array (the original one is left untouched).
515 * [Arrays]
517 mpdm_t mpdm_sort(mpdm_t a, int step)
519 return mpdm_sort_cb(a, step, NULL);
524 * mpdm_sort_cb - Sorts an array with a special sorting function.
525 * @a: the array
526 * @step: increment step
527 * @asort_cb: sorting function
529 * Sorts the array. @step is the number of elements to group together.
530 * For each pair of elements being sorted, the executable mpdm_t value
531 * @sort_cb is called with an array containing the two elements as
532 * argument. It must return a signed numerical mpdm_t value indicating
533 * the sorting order.
535 * Returns the sorted array (the original one is left untouched).
536 * [Arrays]
538 mpdm_t mpdm_sort_cb(mpdm_t a, int step, mpdm_t cb)
540 if (a == NULL)
541 return NULL;
543 /* creates a copy to be sorted */
544 a = mpdm_clone(a);
546 sort_cb = cb;
548 /* references the array and the code, as the latter
549 can include anything (including sweeping) */
550 mpdm_ref(a);
551 mpdm_ref(sort_cb);
553 qsort(a->data, mpdm_size(a) / step, sizeof(mpdm_t) * step, sort_cmp);
555 /* unreferences */
556 mpdm_unref(sort_cb);
557 mpdm_unref(a);
559 sort_cb = NULL;
561 return a;
566 * mpdm_split - Separates a string into an array of pieces.
567 * @s: the separator
568 * @v: the value to be separated
570 * Separates the @v string value into an array of pieces, using @s
571 * as a separator.
573 * If the separator is NULL, the string is splitted by characters.
575 * If the string does not contain the separator, an array holding
576 * the complete string is returned.
577 * [Arrays]
578 * [Strings]
580 mpdm_t mpdm_split(mpdm_t s, mpdm_t v)
582 mpdm_t w;
583 wchar_t *ptr;
584 wchar_t *sptr;
586 /* nothing to split? */
587 if (v == NULL)
588 return NULL;
590 w = MPDM_A(0);
592 /* NULL separator? special case: split string in characters */
593 if (s == NULL) {
594 for (ptr = mpdm_string(v); ptr && *ptr != '\0'; ptr++)
595 mpdm_push(w, MPDM_NS(ptr, 1));
597 return w;
600 /* travels the string finding separators and creating new values */
601 for (ptr = v->data;
602 *ptr != L'\0' && (sptr = wcsstr(ptr, s->data)) != NULL;
603 ptr = sptr + mpdm_size(s))
604 mpdm_push(w, MPDM_NS(ptr, sptr - ptr));
606 /* add last part */
607 mpdm_push(w, MPDM_S(ptr));
609 return w;
614 * mpdm_join - Joins all elements of an array into one.
615 * @s: joiner string
616 * @a: array to be joined
618 * Joins all elements from @a into one string, using @s as a glue.
619 * [Arrays]
620 * [Strings]
622 mpdm_t mpdm_join(mpdm_t s, mpdm_t a)
624 int n;
625 wchar_t *ptr = NULL;
626 int l = 0;
628 /* adds the first string */
629 ptr = mpdm_pokev(ptr, &l, mpdm_aget(a, 0));
631 for (n = 1; n < mpdm_size(a); n++) {
632 /* add separator */
633 ptr = mpdm_pokev(ptr, &l, s);
635 /* add element */
636 ptr = mpdm_pokev(ptr, &l, mpdm_aget(a, n));
639 if (ptr == NULL)
640 return NULL;
642 ptr = mpdm_poke(ptr, &l, L"", 1, sizeof(wchar_t));
643 return MPDM_ENS(ptr, l - 1);