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
40 /* sorting callback code */
41 static mpdm_t sort_cb
= NULL
;
48 mpdm_t
mpdm_new_a(int flags
, int size
)
49 /* creates a new array value */
53 /* creates and expands */
54 if ((v
= mpdm_new(flags
| MPDM_MULTIPLE
, NULL
, 0)) != NULL
)
55 mpdm_expand(v
, 0, size
);
61 static int wrap_offset(mpdm_t a
, int offset
)
62 /* manages negative offsets */
65 offset
= mpdm_size(a
) + offset
;
71 mpdm_t
mpdm_aclone(mpdm_t v
)
72 /* clones a multiple value */
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
);
91 * mpdm_expand - Expands an 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.
100 mpdm_t
mpdm_expand(mpdm_t a
, int offset
, int num
)
112 /* expand, or fail */
113 if ((p
= (mpdm_t
*) realloc(a
->data
, a
->size
* sizeof(mpdm_t
))) == NULL
)
116 /* moves up from top of the array */
117 for (n
= a
->size
- 1; n
>= offset
+ num
; n
--)
120 /* fills the new space with blanks */
121 for (; n
>= offset
; n
--)
131 * mpdm_collapse - Collapses an 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.
140 mpdm_t
mpdm_collapse(mpdm_t a
, int offset
, int num
)
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
++)
159 /* array is now shorter */
162 /* moves down the elements */
163 for (n
= offset
; n
< a
->size
; n
++)
166 /* finally shrinks the memory block */
167 if ((a
->data
= realloc(p
, a
->size
* sizeof(mpdm_t
))) == NULL
)
175 * mpdm_aset - Sets the value of an array's element.
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.
184 mpdm_t
mpdm_aset(mpdm_t a
, mpdm_t e
, int offset
)
189 offset
= wrap_offset(a
, offset
);
191 /* boundary checks */
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
)
200 p
= (mpdm_t
*) a
->data
;
202 /* assigns and references */
203 v
= mpdm_unref(p
[offset
]);
204 p
[offset
] = mpdm_ref(e
);
211 * mpdm_aget - Gets an element of an array.
213 * @offset: the subscript of the element
215 * Returns the element at @offset of the array @a.
218 mpdm_t
mpdm_aget(mpdm_t a
, int offset
)
222 offset
= wrap_offset(a
, offset
);
224 /* boundary checks */
225 if (offset
< 0 || offset
>= mpdm_size(a
))
228 p
= (mpdm_t
*) a
->data
;
235 * mpdm_ins - Insert an element in an 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.
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
);
258 * mpdm_adel - Deletes an element of an 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.
270 mpdm_t
mpdm_adel(mpdm_t a
, int offset
)
274 if (a
== NULL
|| mpdm_size(a
) == 0)
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);
290 * mpdm_shift - Extracts the first element of an array.
293 * Extracts the first element of the array. The array
294 * is shrinked by one.
296 * Returns the deleted element.
299 mpdm_t
mpdm_shift(mpdm_t a
)
301 return mpdm_adel(a
, 0);
306 * mpdm_push - Pushes a value into an array.
310 * Pushes a value into an array (i.e. inserts at the end).
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.
324 * Pops a value from the array (i.e. deletes from the end
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.
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.
349 mpdm_t
mpdm_queue(mpdm_t a
, mpdm_t e
, int size
)
353 /* zero size is nonsense */
357 /* loop until a has the desired size */
358 while (mpdm_size(a
) > size
- 1)
367 * mpdm_seek - Seeks a value in an array (sequential).
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.
377 int mpdm_seek(mpdm_t a
, mpdm_t k
, int step
)
381 /* avoid stupid steps */
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)
397 * mpdm_seek_s - Seeks a value in an array (sequential, string version).
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.
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
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.
430 int mpdm_bseek(mpdm_t a
, mpdm_t k
, int step
, int *pos
)
434 /* avoid stupid steps */
439 t
= (mpdm_size(a
) - 1) / step
;
445 if ((v
= mpdm_aget(a
, n
* step
)) == NULL
)
466 * mpdm_bseek_s - Seeks a value in an array (binary, string version).
467 * @a: the ordered array
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.
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 */
493 /* if callback is NULL, use basic value comparisons */
495 ret
= mpdm_cmp(*(mpdm_t
*) s1
, *(mpdm_t
*) s2
);
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
)));
508 * mpdm_sort - Sorts an 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).
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.
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
535 * Returns the sorted array (the original one is left untouched).
538 mpdm_t
mpdm_sort_cb(mpdm_t a
, int step
, mpdm_t cb
)
543 /* creates a copy to be sorted */
548 /* references the array and the code, as the latter
549 can include anything (including sweeping) */
553 qsort(a
->data
, mpdm_size(a
) / step
, sizeof(mpdm_t
) * step
, sort_cmp
);
566 * mpdm_split - Separates a string into an array of pieces.
568 * @v: the value to be separated
570 * Separates the @v string value into an array of pieces, using @s
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.
580 mpdm_t
mpdm_split(mpdm_t s
, mpdm_t v
)
586 /* nothing to split? */
592 /* NULL separator? special case: split string in characters */
594 for (ptr
= mpdm_string(v
); ptr
&& *ptr
!= '\0'; ptr
++)
595 mpdm_push(w
, MPDM_NS(ptr
, 1));
600 /* travels the string finding separators and creating new values */
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
));
607 mpdm_push(w
, MPDM_S(ptr
));
614 * mpdm_join - Joins all elements of an array into one.
616 * @a: array to be joined
618 * Joins all elements from @a into one string, using @s as a glue.
622 mpdm_t
mpdm_join(mpdm_t s
, mpdm_t a
)
628 /* adds the first string */
629 ptr
= mpdm_pokev(ptr
, &l
, mpdm_aget(a
, 0));
631 for (n
= 1; n
< mpdm_size(a
); n
++) {
633 ptr
= mpdm_pokev(ptr
, &l
, s
);
636 ptr
= mpdm_pokev(ptr
, &l
, mpdm_aget(a
, n
));
642 ptr
= mpdm_poke(ptr
, &l
, L
"", 1, sizeof(wchar_t));
643 return MPDM_ENS(ptr
, l
- 1);