Set CFLAGS to "-g -Wall" only if CC is gcc.
[mpdm.git] / mpdm_a.c
blob2823ff350eb8bf2b856858e74449b6969a604b6b
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) offset = mpdm_size(a) + offset;
66 return(offset);
70 mpdm_t mpdm_aclone(mpdm_t v)
71 /* clones a multiple value */
73 mpdm_t w;
74 int n;
76 /* creates a similar value */
77 w = mpdm_new_a(v->flags, v->size);
79 /* fills each element with duplicates of the original */
80 for(n=0;n < w->size;n++)
81 mpdm_aset(w, mpdm_clone(mpdm_aget(v, n)), n);
83 return(w);
87 /* interface */
89 /**
90 * mpdm_expand - Expands an array.
91 * @a: the array
92 * @offset: insertion offset
93 * @num: number of elements to insert
95 * Expands an array value, inserting @num elements (initialized
96 * to NULL) at the specified @offset.
97 * [Arrays]
99 mpdm_t mpdm_expand(mpdm_t a, int offset, int num)
101 int n;
102 mpdm_t * p;
104 /* sanity checks */
105 if(num <= 0) return(a);
107 /* add size */
108 a->size += num;
110 /* expand, or fail */
111 if((p = (mpdm_t *) realloc(a->data, a->size * sizeof(mpdm_t))) == NULL)
112 return(NULL);
114 /* moves up from top of the array */
115 for(n = a->size - 1;n >= offset + num;n--)
116 p[n] = p[n - num];
118 /* fills the new space with blanks */
119 for(;n >= offset;n--)
120 p[n] = NULL;
122 a->data = p;
124 return(a);
129 * mpdm_collapse - Collapses an array.
130 * @a: the array
131 * @offset: deletion offset
132 * @num: number of elements to collapse
134 * Collapses an array value, deleting @num elements at
135 * the specified @offset.
136 * [Arrays]
138 mpdm_t mpdm_collapse(mpdm_t a, int offset, int num)
140 int n;
141 mpdm_t * p;
143 /* sanity checks */
144 if(num <= 0) return(a);
146 /* don't try to delete beyond the limit */
147 if(offset + num > a->size)
148 num = a->size - offset;
150 p = (mpdm_t *) a->data;
152 /* unrefs the about-to-be-deleted elements */
153 for(n = offset;n < offset + num;n++)
154 mpdm_unref(p[n]);
156 /* array is now shorter */
157 a->size -= num;
159 /* moves down the elements */
160 for(n = offset;n < a->size;n++)
161 p[n] = p[n + num];
163 /* finally shrinks the memory block */
164 if((a->data = realloc(p, a->size * sizeof(mpdm_t))) == NULL)
165 return(NULL);
167 return(a);
172 * mpdm_aset - Sets the value of an array's element.
173 * @a: the array
174 * @e: the element to be assigned
175 * @offset: the subscript of the element
177 * Sets the element of the array @a at @offset to be the @e value.
178 * Returns the previous element.
179 * [Arrays]
181 mpdm_t mpdm_aset(mpdm_t a, mpdm_t e, int offset)
183 mpdm_t v;
184 mpdm_t * p;
186 offset = wrap_offset(a, offset);
188 /* boundary checks */
189 if(offset < 0)
190 return(NULL);
192 /* if the array is shorter than offset, expand to make room for it */
193 if(offset >= mpdm_size(a))
194 if(mpdm_expand(a, mpdm_size(a), offset - mpdm_size(a) + 1) == NULL)
195 return(NULL);
197 p = (mpdm_t *)a->data;
199 /* assigns and references */
200 v = mpdm_unref(p[offset]);
201 p[offset] = mpdm_ref(e);
203 return(v);
208 * mpdm_aget - Gets an element of an array.
209 * @a: the array
210 * @offset: the subscript of the element
212 * Returns the element at @offset of the array @a.
213 * [Arrays]
215 mpdm_t mpdm_aget(mpdm_t a, int offset)
217 mpdm_t * p;
219 offset = wrap_offset(a, offset);
221 /* boundary checks */
222 if(offset < 0 || offset >= mpdm_size(a))
223 return(NULL);
225 p = (mpdm_t *) a->data;
227 return(p[offset]);
232 * mpdm_ins - Insert an element in an array.
233 * @a: the array
234 * @e: the element to be inserted
235 * @offset: subscript where the element is going to be inserted
237 * Inserts the @e value in the @a array at @offset.
238 * Further elements are pushed up, so the array increases its size
239 * by one. Returns the inserted element.
240 * [Arrays]
242 mpdm_t mpdm_ins(mpdm_t a, mpdm_t e, int offset)
244 offset = wrap_offset(a, offset);
246 /* open room and set value */
247 if(mpdm_expand(a, offset, 1))
248 mpdm_aset(a, e, offset);
250 return(e);
255 * mpdm_adel - Deletes an element of an array.
256 * @a: the array
257 * @offset: subscript of the element to be deleted
259 * Deletes the element at @offset of the @a array. The array
260 * is shrinked by one. If @offset is negative, is counted from
261 * the end of the array (so a value of -1 means delete the
262 * last element of the array).
264 * Returns the deleted element.
265 * [Arrays]
267 mpdm_t mpdm_adel(mpdm_t a, int offset)
269 mpdm_t v;
271 if(a == NULL || mpdm_size(a) == 0)
272 return(NULL);
274 offset = wrap_offset(a, offset);
276 /* gets current value */
277 v = mpdm_aget(a, offset);
279 /* shrinks the array */
280 mpdm_collapse(a, offset, 1);
282 return(v);
287 * mpdm_shift - Extracts the first element of an array.
288 * @a: the array
290 * Extracts the first element of the array. The array
291 * is shrinked by one.
293 * Returns the deleted element.
294 * [Arrays]
296 mpdm_t mpdm_shift(mpdm_t a)
298 return(mpdm_adel(a, 0));
303 * mpdm_push - Pushes a value into an array.
304 * @a: the array
305 * @e: the value
307 * Pushes a value into an array (i.e. inserts at the end).
308 * [Arrays]
310 mpdm_t mpdm_push(mpdm_t a, mpdm_t e)
312 /* inserts at the end */
313 return(mpdm_ins(a, e, mpdm_size(a)));
318 * mpdm_pop - Pops a value from an array.
319 * @a: the array
321 * Pops a value from the array (i.e. deletes from the end
322 * and returns it).
323 * [Arrays]
325 mpdm_t mpdm_pop(mpdm_t a)
327 /* deletes from the end */
328 return(mpdm_adel(a, -1));
333 * mpdm_queue - Implements a queue in an array.
334 * @a: the array
335 * @e: the element to be pushed
336 * @size: maximum size of array
338 * Pushes the @e element into the @a array. If the array already has
339 * @size elements, the first (oldest) element is deleted from the
340 * queue and returned.
342 * Returns the deleted element, or NULL if the array doesn't have
343 * @size elements yet.
344 * [Arrays]
346 mpdm_t mpdm_queue(mpdm_t a, mpdm_t e, int size)
348 mpdm_t v=NULL;
350 /* zero size is nonsense */
351 if(size == 0)
352 return(NULL);
354 /* loop until a has the desired size */
355 while(mpdm_size(a) > size - 1)
356 v = mpdm_shift(a);
358 mpdm_push(a, e);
359 return(v);
364 * mpdm_seek - Seeks a value in an array (sequential).
365 * @a: the array
366 * @k: the key
367 * @step: number of elements to step
369 * Seeks sequentially the value @k in the @a array in
370 * increments of @step. A complete search should use a step of 1.
371 * Returns the offset of the element if found, or -1 otherwise.
372 * [Arrays]
374 int mpdm_seek(mpdm_t a, mpdm_t k, int step)
376 int n;
378 /* avoid stupid steps */
379 if(step <= 0) step = 1;
381 for(n=0;n < mpdm_size(a);n += step)
383 mpdm_t v = mpdm_aget(a, n);
385 if(mpdm_cmp(v, k) == 0)
386 return(n);
389 return(-1);
394 * mpdm_seek_s - Seeks a value in an array (sequential, string version).
395 * @a: the array
396 * @k: the key
397 * @step: number of elements to step
399 * Seeks sequentially the value @k in the @a array in
400 * increments of @step. A complete search should use a step of 1.
401 * Returns the offset of the element if found, or -1 otherwise.
402 * [Arrays]
404 int mpdm_seek_s(mpdm_t a, wchar_t * k, int step)
406 return(mpdm_seek(a, MPDM_LS(k), step));
411 * mpdm_bseek - Seeks a value in an array (binary).
412 * @a: the ordered array
413 * @k: the key
414 * @step: number of elements to step
415 * @pos: the position where the element should be, if it's not found
417 * Seeks the value @k in the @a array in increments of @step.
418 * The array should be sorted to work correctly. A complete search
419 * should use a step of 1.
421 * If the element is found, returns the offset of the element
422 * as a positive number; otherwise, -1 is returned and the position
423 * where the element should be is stored in @pos. You can set @pos
424 * to NULL if you don't mind.
425 * [Arrays]
427 int mpdm_bseek(mpdm_t a, mpdm_t k, int step, int * pos)
429 int b, t, n, c;
431 /* avoid stupid steps */
432 if(step <= 0) step = 1;
434 b=0; t = (mpdm_size(a) - 1) / step;
436 while(t >= b)
438 mpdm_t v;
440 n = (b + t) / 2;
441 if((v = mpdm_aget(a, n * step)) == NULL)
442 break;
444 c = mpdm_cmp(k, v);
446 if(c == 0)
447 return(n * step);
448 else
449 if(c < 0)
450 t = n - 1;
451 else
452 b = n + 1;
455 if(pos != NULL) *pos = b * step;
456 return(-1);
461 * mpdm_bseek_s - Seeks a value in an array (binary, string version).
462 * @a: the ordered array
463 * @k: the key
464 * @step: number of elements to step
465 * @pos: the position where the element should be, if it's not found
467 * Seeks the value @k in the @a array in increments of @step.
468 * The array should be sorted to work correctly. A complete search
469 * should use a step of 1.
471 * If the element is found, returns the offset of the element
472 * as a positive number; otherwise, -1 is returned and the position
473 * where the element should be is stored in @pos. You can set @pos
474 * to NULL if you don't mind.
475 * [Arrays]
477 int mpdm_bseek_s(mpdm_t a, wchar_t * k, int step, int * pos)
479 return(mpdm_bseek(a, MPDM_LS(k), step, pos));
483 static int sort_cmp(const void * s1, const void * s2)
484 /* qsort help function */
486 int ret = 0;
488 /* if callback is NULL, use basic value comparisons */
489 if(sort_cb == NULL)
490 ret = mpdm_cmp(*(mpdm_t *)s1, *(mpdm_t *)s2);
491 else
493 /* executes the callback and converts to integer */
494 ret = mpdm_ival(mpdm_exec_2(sort_cb,
495 (mpdm_t) * ((mpdm_t *)s1),
496 (mpdm_t) * ((mpdm_t *)s2)));
499 return(ret);
504 * mpdm_sort - Sorts an array.
505 * @a: the array
506 * @step: increment step
508 * Sorts the array. @step is the number of elements to group together.
510 * Returns the sorted array (the original one is left untouched).
511 * [Arrays]
513 mpdm_t mpdm_sort(mpdm_t a, int step)
515 return(mpdm_sort_cb(a, step, NULL));
520 * mpdm_sort_cb - Sorts an array with a special sorting function.
521 * @a: the array
522 * @step: increment step
523 * @asort_cb: sorting function
525 * Sorts the array. @step is the number of elements to group together.
526 * For each pair of elements being sorted, the executable mpdm_t value
527 * @sort_cb is called with an array containing the two elements as
528 * argument. It must return a signed numerical mpdm_t value indicating
529 * the sorting order.
531 * Returns the sorted array (the original one is left untouched).
532 * [Arrays]
534 mpdm_t mpdm_sort_cb(mpdm_t a, int step, mpdm_t cb)
536 if(a == NULL) return(NULL);
538 /* creates a copy to be sorted */
539 a = mpdm_clone(a);
541 sort_cb = cb;
543 /* references the array and the code, as the latter
544 can include anything (including sweeping) */
545 mpdm_ref(a);
546 mpdm_ref(sort_cb);
548 qsort(a->data, mpdm_size(a) / step,
549 sizeof(mpdm_t) * step, sort_cmp);
551 /* unreferences */
552 mpdm_unref(sort_cb);
553 mpdm_unref(a);
555 sort_cb = NULL;
557 return(a);
562 * mpdm_split - Separates a string into an array of pieces.
563 * @s: the separator
564 * @v: the value to be separated
566 * Separates the @v string value into an array of pieces, using @s
567 * as a separator.
569 * If the separator is NULL, the string is splitted by characters.
571 * If the string does not contain the separator, an array holding
572 * the complete string is returned.
573 * [Arrays]
574 * [Strings]
576 mpdm_t mpdm_split(mpdm_t s, mpdm_t v)
578 mpdm_t w;
579 wchar_t * ptr;
580 wchar_t * sptr;
582 /* nothing to split? */
583 if(v == NULL) return(NULL);
585 w = MPDM_A(0);
587 /* NULL separator? special case: split string in characters */
588 if(s == NULL)
590 for(ptr = mpdm_string(v);ptr && *ptr != '\0';ptr++)
591 mpdm_push(w, MPDM_NS(ptr, 1));
593 return(w);
596 /* travels the string finding separators and creating new values */
597 for(ptr = v->data;
598 *ptr != L'\0' && (sptr = wcsstr(ptr, s->data)) != NULL;
599 ptr = sptr + mpdm_size(s))
600 mpdm_push(w, MPDM_NS(ptr, sptr - ptr));
602 /* add last part */
603 mpdm_push(w, MPDM_S(ptr));
605 return(w);
610 * mpdm_join - Joins all elements of an array into one.
611 * @s: joiner string
612 * @a: array to be joined
614 * Joins all elements from @a into one string, using @s as a glue.
615 * [Arrays]
616 * [Strings]
618 mpdm_t mpdm_join(mpdm_t s, mpdm_t a)
620 int n;
621 wchar_t * ptr = NULL;
622 int l = 0;
624 /* adds the first string */
625 ptr = mpdm_pokev(ptr, &l, mpdm_aget(a, 0));
627 for(n = 1;n < mpdm_size(a);n++)
629 /* add separator */
630 ptr = mpdm_pokev(ptr, &l, s);
632 /* add element */
633 ptr = mpdm_pokev(ptr, &l, mpdm_aget(a, n));
636 if(ptr == NULL)
637 return(NULL);
639 ptr = mpdm_poke(ptr, &l, L"", 1, sizeof(wchar_t));
640 return(MPDM_ENS(ptr, l - 1));