Executable values and functions now include a context.
[mpdm.git] / mpdm_a.c
blob0abd4598e79d40df2827b8837669b99116200123
1 /*
3 MPDM - Minimum Profit Data Manager
4 Copyright (C) 2003/2010 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 /** data **/
38 /* sorting callback code */
39 static mpdm_t sort_cb = NULL;
42 /** code **/
44 mpdm_t mpdm_new_a(int flags, int size)
45 /* creates a new array value */
47 mpdm_t v;
49 /* creates and expands */
50 if ((v = mpdm_new(flags | MPDM_MULTIPLE, NULL, 0)) != NULL)
51 mpdm_expand(v, 0, size);
53 return v;
57 static int wrap_offset(const mpdm_t a, int offset)
58 /* manages negative offsets */
60 if (offset < 0)
61 offset = mpdm_size(a) + offset;
63 return offset;
67 mpdm_t mpdm_aclone(const mpdm_t v)
68 /* clones a multiple value */
70 mpdm_t w;
71 int n;
73 mpdm_ref(v);
75 /* creates a similar value */
76 w = mpdm_new_a(v->flags, v->size);
78 mpdm_ref(w);
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 mpdm_unrefnd(w);
86 mpdm_unref(v);
88 return w;
92 /* interface */
94 /**
95 * mpdm_expand - Expands an array.
96 * @a: the array
97 * @offset: insertion offset
98 * @num: number of elements to insert
100 * Expands an array value, inserting @num elements (initialized
101 * to NULL) at the specified @offset.
102 * [Arrays]
104 mpdm_t mpdm_expand(mpdm_t a, int offset, int num)
106 int n;
107 mpdm_t *p;
109 /* sanity checks */
110 if (num > 0) {
111 /* add size */
112 a->size += num;
114 /* expand */
115 p = (mpdm_t *) realloc((mpdm_t *)a->data, a->size * sizeof(mpdm_t));
117 /* moves up from top of the array */
118 for (n = a->size - 1; n >= offset + num; n--)
119 p[n] = p[n - num];
121 /* fills the new space with blanks */
122 for (; n >= offset; n--)
123 p[n] = NULL;
125 a->data = p;
128 return a;
133 * mpdm_collapse - Collapses an array.
134 * @a: the array
135 * @offset: deletion offset
136 * @num: number of elements to collapse
138 * Collapses an array value, deleting @num elements at
139 * the specified @offset.
140 * [Arrays]
142 mpdm_t mpdm_collapse(mpdm_t a, int offset, int num)
144 int n;
145 mpdm_t *p;
147 if (num > 0) {
148 /* don't try to delete beyond the limit */
149 if (offset + num > a->size)
150 num = a->size - offset;
152 p = (mpdm_t *) a->data;
154 /* unrefs the about-to-be-deleted elements */
155 for (n = offset; n < offset + num; n++)
156 mpdm_unref(p[n]);
158 /* array is now shorter */
159 a->size -= num;
161 /* moves down the elements */
162 for (n = offset; n < a->size; n++)
163 p[n] = p[n + num];
165 /* finally shrinks the memory block */
166 a->data = realloc(p, a->size * sizeof(mpdm_t));
169 return a;
174 * mpdm_aset - Sets the value of an array's element.
175 * @a: the array
176 * @e: the element to be assigned
177 * @offset: the subscript of the element
179 * Sets the element of the array @a at @offset to be the @e value.
180 * Returns the new element (versions prior to 1.0.10 returned the
181 * old element).
182 * [Arrays]
184 mpdm_t mpdm_aset(mpdm_t a, mpdm_t e, int offset)
186 mpdm_t *p;
188 mpdm_ref(a);
189 mpdm_ref(e);
191 offset = wrap_offset(a, offset);
193 if (offset >= 0) {
194 /* if the array is shorter than offset, expand to make room for it */
195 if (offset >= mpdm_size(a))
196 mpdm_expand(a, mpdm_size(a), offset - mpdm_size(a) + 1);
198 p = (mpdm_t *) a->data;
200 /* assigns and references */
201 mpdm_ref(e);
202 mpdm_unref(p[offset]);
203 p[offset] = e;
206 mpdm_unref(e);
207 mpdm_unref(a);
209 return e;
214 * mpdm_aget - Gets an element of an array.
215 * @a: the array
216 * @offset: the subscript of the element
218 * Returns the element at @offset of the array @a.
219 * [Arrays]
221 mpdm_t mpdm_aget(const mpdm_t a, int offset)
223 mpdm_t r;
224 mpdm_t *p;
226 offset = wrap_offset(a, offset);
228 /* boundary checks */
229 if (offset >= 0 && offset < mpdm_size(a)) {
230 p = (mpdm_t *) a->data;
231 r = p[offset];
233 else
234 r = NULL;
236 return r;
241 * mpdm_ins - Insert an element in an array.
242 * @a: the array
243 * @e: the element to be inserted
244 * @offset: subscript where the element is going to be inserted
246 * Inserts the @e value in the @a array at @offset.
247 * Further elements are pushed up, so the array increases its size
248 * by one. Returns the inserted element.
249 * [Arrays]
251 mpdm_t mpdm_ins(mpdm_t a, mpdm_t e, int offset)
253 mpdm_ref(a);
254 mpdm_ref(e);
256 offset = wrap_offset(a, offset);
258 /* open room and set value */
259 mpdm_expand(a, offset, 1);
260 mpdm_aset(a, e, offset);
262 mpdm_unref(e);
263 mpdm_unref(a);
265 return e;
270 * mpdm_adel - Deletes an element of an array.
271 * @a: the array
272 * @offset: subscript of the element to be deleted
274 * Deletes the element at @offset of the @a array. The array
275 * is shrinked by one. If @offset is negative, is counted from
276 * the end of the array (so a value of -1 means delete the
277 * last element of the array).
279 * Always returns NULL (version prior to 1.0.10 used to return
280 * the deleted element).
281 * [Arrays]
283 mpdm_t mpdm_adel(mpdm_t a, int offset)
285 mpdm_ref(a);
287 if (mpdm_size(a))
288 mpdm_collapse(a, wrap_offset(a, offset), 1);
290 mpdm_unref(a);
292 return NULL;
297 * mpdm_shift - Extracts the first element of an array.
298 * @a: the array
300 * Extracts the first element of the array. The array
301 * is shrinked by one.
303 * Returns the element.
304 * [Arrays]
306 mpdm_t mpdm_shift(mpdm_t a)
308 mpdm_t r;
310 mpdm_ref(a);
312 r = mpdm_ref(mpdm_aget(a, 0));
313 mpdm_adel(a, 0);
314 mpdm_unrefnd(r);
316 mpdm_unref(a);
318 return r;
323 * mpdm_push - Pushes a value into an array.
324 * @a: the array
325 * @e: the value
327 * Pushes a value into an array (i.e. inserts at the end).
328 * [Arrays]
330 mpdm_t mpdm_push(mpdm_t a, mpdm_t e)
332 mpdm_t r;
334 mpdm_ref(a);
336 /* inserts at the end */
337 r = mpdm_ins(a, e, mpdm_size(a));
339 mpdm_unref(a);
341 return r;
346 * mpdm_pop - Pops a value from an array.
347 * @a: the array
349 * Pops a value from the array (i.e. deletes from the end
350 * and returns it).
351 * [Arrays]
353 mpdm_t mpdm_pop(mpdm_t a)
355 mpdm_t r;
357 mpdm_ref(a);
359 r = mpdm_ref(mpdm_aget(a, -1));
360 mpdm_adel(a, -1);
361 r = mpdm_unrefnd(r);
363 mpdm_unref(a);
365 return r;
370 * mpdm_queue - Implements a queue in an array.
371 * @a: the array
372 * @e: the element to be pushed
373 * @size: maximum size of array
375 * Pushes the @e element into the @a array. If the array already has
376 * @size elements, the first (oldest) element is deleted from the
377 * queue and returned.
379 * Returns the deleted element, or NULL if the array doesn't have
380 * @size elements yet.
381 * [Arrays]
383 mpdm_t mpdm_queue(mpdm_t a, mpdm_t e, int size)
385 mpdm_t v = NULL;
387 mpdm_ref(a);
388 mpdm_ref(e);
390 /* zero size is nonsense */
391 if (size) {
392 /* loop until a has the desired size */
393 while (mpdm_size(a) > size)
394 mpdm_adel(a, 0);
396 if (mpdm_size(a) == size)
397 v = mpdm_shift(a);
399 mpdm_push(a, e);
402 mpdm_unref(e);
403 mpdm_unref(a);
405 return v;
409 static int seek(const mpdm_t a, const mpdm_t k, const wchar_t *ks, int step)
411 int n, o;
413 mpdm_ref(a);
414 mpdm_ref(k);
416 /* avoid stupid steps */
417 if (step <= 0)
418 step = 1;
420 o = -1;
422 for (n = 0; o == -1 && n < mpdm_size(a); n += step) {
423 int r;
425 mpdm_t v = mpdm_aget(a, n);
427 r = ks ? mpdm_cmp_s(v, ks) : mpdm_cmp(v, k);
429 if (r == 0)
430 o = n;
433 mpdm_unref(k);
434 mpdm_unref(a);
436 return o;
441 * mpdm_seek - Seeks a value in an array (sequential).
442 * @a: the array
443 * @k: the key
444 * @step: number of elements to step
446 * Seeks sequentially the value @k in the @a array in
447 * increments of @step. A complete search should use a step of 1.
448 * Returns the offset of the element if found, or -1 otherwise.
449 * [Arrays]
451 int mpdm_seek(const mpdm_t a, const mpdm_t k, int step)
453 return seek(a, k, NULL, step);
458 * mpdm_seek_s - Seeks a value in an array (sequential, string version).
459 * @a: the array
460 * @k: the key
461 * @step: number of elements to step
463 * Seeks sequentially the value @k in the @a array in
464 * increments of @step. A complete search should use a step of 1.
465 * Returns the offset of the element if found, or -1 otherwise.
466 * [Arrays]
468 int mpdm_seek_s(const mpdm_t a, const wchar_t *k, int step)
470 return seek(a, NULL, k, step);
474 static int bseek(const mpdm_t a, const mpdm_t k, const wchar_t *ks, int step, int *pos)
476 int b, t, n, c, o;
478 mpdm_ref(a);
479 mpdm_ref(k);
481 /* avoid stupid steps */
482 if (step <= 0)
483 step = 1;
485 b = 0;
486 t = (mpdm_size(a) - 1) / step;
488 o = -1;
490 while (o == -1 && t >= b) {
491 mpdm_t v;
493 n = (b + t) / 2;
494 if ((v = mpdm_aget(a, n * step)) == NULL)
495 break;
497 c = ks ? mpdm_cmp_s(v, ks) : mpdm_cmp(v, k);
499 if (c == 0)
500 o = n * step;
501 else
502 if (c > 0)
503 t = n - 1;
504 else
505 b = n + 1;
508 if (pos != NULL)
509 *pos = b * step;
511 mpdm_unref(k);
512 mpdm_unref(a);
514 return o;
519 * mpdm_bseek - Seeks a value in an array (binary).
520 * @a: the ordered array
521 * @k: the key
522 * @step: number of elements to step
523 * @pos: the position where the element should be, if it's not found
525 * Seeks the value @k in the @a array in increments of @step.
526 * The array should be sorted to work correctly. A complete search
527 * should use a step of 1.
529 * If the element is found, returns the offset of the element
530 * as a positive number; otherwise, -1 is returned and the position
531 * where the element should be is stored in @pos. You can set @pos
532 * to NULL if you don't mind.
533 * [Arrays]
535 int mpdm_bseek(const mpdm_t a, const mpdm_t k, int step, int *pos)
537 return bseek(a, k, NULL, step, pos);
542 * mpdm_bseek_s - Seeks a value in an array (binary, string version).
543 * @a: the ordered array
544 * @k: the key
545 * @step: number of elements to step
546 * @pos: the position where the element should be, if it's not found
548 * Seeks the value @k in the @a array in increments of @step.
549 * The array should be sorted to work correctly. A complete search
550 * should use a step of 1.
552 * If the element is found, returns the offset of the element
553 * as a positive number; otherwise, -1 is returned and the position
554 * where the element should be is stored in @pos. You can set @pos
555 * to NULL if you don't mind.
556 * [Arrays]
558 int mpdm_bseek_s(const mpdm_t a, const wchar_t *k, int step, int *pos)
560 return bseek(a, NULL, k, step, pos);
564 static int sort_cmp(const void *s1, const void *s2)
565 /* qsort help function */
567 int ret = 0;
569 /* if callback is NULL, use basic value comparisons */
570 if (sort_cb == NULL)
571 ret = mpdm_cmp(*(mpdm_t *) s1, *(mpdm_t *) s2);
572 else {
573 /* executes the callback and converts to integer */
574 ret = mpdm_ival(mpdm_exec_2(sort_cb,
575 (mpdm_t) * ((mpdm_t *) s1),
576 (mpdm_t) * ((mpdm_t *) s2),
577 NULL
582 return ret;
587 * mpdm_sort - Sorts an array.
588 * @a: the array
589 * @step: increment step
591 * Sorts the array. @step is the number of elements to group together.
593 * Returns the same array, sorted (versions prior to 1.0.10 returned
594 * a new array).
595 * [Arrays]
597 mpdm_t mpdm_sort(const mpdm_t a, int step)
599 return mpdm_sort_cb(a, step, NULL);
604 * mpdm_sort_cb - Sorts an array with a special sorting function.
605 * @a: the array
606 * @step: increment step
607 * @asort_cb: sorting function
609 * Sorts the array. @step is the number of elements to group together.
610 * For each pair of elements being sorted, the executable mpdm_t value
611 * @sort_cb is called with an array containing the two elements as
612 * argument. It must return a signed numerical mpdm_t value indicating
613 * the sorting order.
615 * Returns the same array, sorted (versions prior to 1.0.10 returned
616 * a new array).
617 * [Arrays]
619 mpdm_t mpdm_sort_cb(mpdm_t a, int step, mpdm_t cb)
621 if (a != NULL) {
622 sort_cb = cb;
624 /* references the array and the code, as the latter
625 can include anything (including sweeping) */
626 mpdm_ref(a);
627 mpdm_ref(sort_cb);
629 qsort((mpdm_t *)a->data, mpdm_size(a) / step, sizeof(mpdm_t) * step, sort_cmp);
631 /* unreferences */
632 mpdm_unrefnd(sort_cb);
633 mpdm_unrefnd(a);
635 sort_cb = NULL;
638 return a;
643 * mpdm_split_s - Separates a string into an array of pieces (string version).
644 * @s: the separator
645 * @v: the value to be separated
647 * Separates the @v string value into an array of pieces, using @s
648 * as a separator.
650 * If the separator is NULL, the string is splitted by characters.
652 * If the string does not contain the separator, an array holding
653 * the complete string is returned.
654 * [Arrays]
655 * [Strings]
657 mpdm_t mpdm_split_s(const wchar_t *s, const mpdm_t v)
659 mpdm_t w = NULL;
660 const wchar_t *ptr;
662 if (v != NULL) {
663 mpdm_ref(v);
665 w = MPDM_A(0);
666 mpdm_ref(w);
668 /* NULL separator? special case: split string in characters */
669 if (s == NULL) {
670 for (ptr = mpdm_string(v); ptr && *ptr != '\0'; ptr++)
671 mpdm_push(w, MPDM_NS(ptr, 1));
673 else {
674 wchar_t *sptr;
675 int ss;
677 ss = wcslen(s);
679 /* travels the string finding separators and creating new values */
680 for (ptr = v->data;
681 *ptr != L'\0' && (sptr = wcsstr(ptr, s)) != NULL;
682 ptr = sptr + ss)
683 mpdm_push(w, MPDM_NS(ptr, sptr - ptr));
685 /* add last part */
686 mpdm_push(w, MPDM_S(ptr));
689 mpdm_unrefnd(w);
691 mpdm_unref(v);
694 return w;
699 * mpdm_split - Separates a string into an array of pieces.
700 * @s: the separator
701 * @v: the value to be separated
703 * Separates the @v string value into an array of pieces, using @s
704 * as a separator.
706 * If the separator is NULL, the string is splitted by characters.
708 * If the string does not contain the separator, an array holding
709 * the complete string is returned.
710 * [Arrays]
711 * [Strings]
713 mpdm_t mpdm_split(const mpdm_t s, const mpdm_t v)
715 mpdm_t r;
716 wchar_t *ss = NULL;
718 mpdm_ref(s);
720 if (s != NULL)
721 ss = (wchar_t *)s->data;
723 r = mpdm_split_s(ss, v);
725 mpdm_unref(s);
727 return r;
732 * mpdm_join_s - Joins all elements of an array into one (string version).
733 * @s: joiner string
734 * @a: array to be joined
736 * Joins all elements from @a into one string, using @s as a glue.
737 * [Arrays]
738 * [Strings]
740 mpdm_t mpdm_join_s(const wchar_t *s, const mpdm_t a)
742 int n;
743 wchar_t *ptr = NULL;
744 int l = 0;
745 int ss;
746 mpdm_t r = NULL;
748 mpdm_ref(a);
750 if (MPDM_IS_ARRAY(a)) {
751 /* adds the first string */
752 ptr = mpdm_pokev(ptr, &l, mpdm_aget(a, 0));
754 ss = s ? wcslen(s) : 0;
756 for (n = 1; n < mpdm_size(a); n++) {
757 /* add separator */
758 ptr = mpdm_pokewsn(ptr, &l, s, ss);
760 /* add element */
761 ptr = mpdm_pokev(ptr, &l, mpdm_aget(a, n));
764 ptr = mpdm_poke(ptr, &l, L"", 1, sizeof(wchar_t));
765 r = MPDM_ENS(ptr, l - 1);
768 mpdm_unref(a);
770 return r;
775 * mpdm_join - Joins all elements of an array into one.
776 * @s: joiner string
777 * @a: array to be joined
779 * Joins all elements from @a into one string, using @s as a glue.
780 * [Arrays]
781 * [Strings]
783 mpdm_t mpdm_join(const mpdm_t s, const mpdm_t a)
785 mpdm_t r;
787 mpdm_ref(s);
788 r = mpdm_join_s(s ? mpdm_string(s) : NULL, a);
789 mpdm_unref(s);
791 return r;