Created
August 7, 2009 16:59
-
-
Save tj/164017 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// array.h - CKit | |
// | |
// (c) 2009 TJ Holowaychuk <[email protected]> (MIT Licensed) | |
// | |
#ifndef __CKIT_ARRAY__ | |
#define __CKIT_ARRAY__ | |
#include "ckit.h" | |
#define CKIT_ARRAY(N, T) \ | |
\ | |
typedef struct { \ | |
int len, max; \ | |
T *vals; \ | |
} N; \ | |
\ | |
/* \ | |
* Allocate new N. \ | |
* \ | |
* @api public \ | |
*/ \ | |
\ | |
N * \ | |
N##_new() { \ | |
N *self = (N *) CKIT_MALLOC(sizeof(N)); \ | |
self->len = 0, self->max = 0; \ | |
self->vals = NULL; \ | |
return self; \ | |
} \ | |
\ | |
/* \ | |
* Push _val_ onto _self_. \ | |
* \ | |
* @api public \ | |
*/ \ | |
\ | |
N * \ | |
N##_push(N *self, T val) { \ | |
if (self->len == self->max) \ | |
self->max = self->max ? self->max << 1 : 2, \ | |
self->vals = (T *) CKIT_REALLOC(self->vals, self->max * sizeof(T)); \ | |
self->vals[self->len++] = val; \ | |
return self; \ | |
} \ | |
\ | |
/* \ | |
* Slice _self_ at _beg_ to _len_. \ | |
* \ | |
* Array_slice(array, 0, 3); \ | |
* Array_slice(array, 5, 10); \ | |
* Array_slice(array, 1, -1); \ | |
* Array_slice(array, -5, -1); \ | |
* \ | |
* @api public \ | |
*/ \ | |
\ | |
N * \ | |
N##_slice(N *self, int beg, int len) { \ | |
N *slice = N##_new(); \ | |
if (beg < 0) beg = self->len + 1 -(-beg+1); \ | |
if (len < 0) len = self->len + 2 -(-len+1); \ | |
if (beg < 0) beg = 0; \ | |
if (len < 0) len = 1; \ | |
if (self->len && len != 0) \ | |
for (; len > 0 && beg < self->len; --len, ++beg) \ | |
if (self->vals[beg]) \ | |
N##_push(slice, self->vals[beg]); \ | |
return slice; \ | |
} \ | |
\ | |
/* \ | |
* Return elements after _i_. \ | |
* \ | |
* @api public \ | |
*/ \ | |
\ | |
N * \ | |
N##_from(N *self, int i) { \ | |
return N##_slice(self, i, -1); \ | |
} \ | |
\ | |
/* \ | |
* Return elements upto _i_. \ | |
* \ | |
* @api public \ | |
*/ \ | |
\ | |
N * \ | |
N##_to(N *self, int i) { \ | |
return N##_slice(self, 0, ++i); \ | |
} \ | |
\ | |
/* \ | |
* Return last _n_ elements. \ | |
* \ | |
* @api public \ | |
*/ \ | |
\ | |
N * \ | |
N##_last_n(N *self, int n) { \ | |
return N##_slice(self, -n, n); \ | |
} \ | |
\ | |
/* \ | |
* Select from _self_ with _callback_. Returns \ | |
* a new array of values which evaluate to 1. \ | |
* \ | |
* @api public \ | |
*/ \ | |
\ | |
N * \ | |
N##_select(N *self, int (* callback)()) { \ | |
N *selected = N##_new(); \ | |
for (int i = 0; i < self->len; ++i) \ | |
if (callback(i, self->vals[i])) \ | |
N##_push(selected, self->vals[i]); \ | |
return selected; \ | |
} \ | |
\ | |
/* \ | |
* Map _self_ with _callback_. Returns a new array \ | |
* of mapped values. \ | |
* \ | |
* @api public \ | |
*/ \ | |
\ | |
N * \ | |
N##_map(N *self, void *(* callback)()) { \ | |
N *map = N##_new(); \ | |
for (int i = 0; i < self->len; ++i) \ | |
N##_push(map, callback(i, self->vals[i])); \ | |
return map; \ | |
} \ | |
\ | |
/* \ | |
* Find an element in _self_ using _callback_. When _callback_ \ | |
* evaluates to 1, the value will be returned, otherwise NULL. \ | |
* \ | |
* @api public \ | |
*/ \ | |
\ | |
T \ | |
N##_find(N *self, int (* callback)()) { \ | |
for (int i = 0; i < self->len; ++i) \ | |
if (callback(i, self->vals[i])) \ | |
return self->vals[i]; \ | |
return NULL; \ | |
} \ | |
\ | |
/* \ | |
* Check if an element in _self_ will evaluate to 1 \ | |
* when passed to _callback_. \ | |
* \ | |
* @api public \ | |
* @see Array_find() \ | |
*/ \ | |
\ | |
int \ | |
N##_any(N *self, int (* callback)()) { \ | |
return !! N##_find(self, callback); \ | |
} \ | |
\ | |
/* \ | |
* Return the last element in _self_. \ | |
*/ \ | |
\ | |
T \ | |
N##_last(N *self) { \ | |
return self->vals[self->len - 1]; \ | |
} \ | |
\ | |
/* \ | |
* Pop an element off the end of _self_'s stack. \ | |
*/ \ | |
\ | |
T \ | |
N##_pop(N *self) { \ | |
return self->len ? \ | |
self->vals[--self->len] : \ | |
NULL; \ | |
} \ | |
\ | |
/* \ | |
* Merge _self_ with _other_ array. \ | |
*/ \ | |
\ | |
N * \ | |
N##_merge(N *self, N *other) { \ | |
for (int i = 0; i < other->len; ++i) \ | |
N##_push(self, other->vals[i]); \ | |
return self; \ | |
} \ | |
\ | |
/* \ | |
* Unshift _val_ onto _self_'s stack, prepending it. \ | |
*/ \ | |
\ | |
N * \ | |
N##_unshift(N *self, T val) { \ | |
*self = *N##_merge(N##_push(N##_new(), val), self); \ | |
return self; \ | |
} \ | |
\ | |
/* \ | |
* Delete element at index _i_ from _self_. \ | |
*/ \ | |
\ | |
T \ | |
N##_delete_at(N *self, int i) { \ | |
if (i < self->len && i > -1) { \ | |
--self->len; \ | |
T val = self->vals[i]; \ | |
for (; i <= self->len; ++i) \ | |
self->vals[i] = i == self->len ? NULL : self->vals[i+1]; \ | |
return val; \ | |
} \ | |
return NULL; \ | |
} \ | |
\ | |
/* \ | |
* Remove duplicate values from _self_. \ | |
*/ \ | |
\ | |
N * \ | |
N##_unique(N *self) { \ | |
for (int i = 0; i < self->len; ++i) \ | |
for (int j = 0; j < self->len; ++j) \ | |
if (self->vals[i] == self->vals[j] && i != j) \ | |
N##_delete_at(self, i); \ | |
return self; \ | |
} \ | |
\ | |
/* \ | |
* Sort _self_ with comparison _callback_. \ | |
* \ | |
* static int \ | |
* compare_string(void *p1, void *p2) { \ | |
* return strcmp(* (char * const *) p1, * (char * const *) p2); \ | |
* } \ | |
* \ | |
* Array_sort(array, compare_string); \ | |
* \ | |
* @api public \ | |
*/ \ | |
\ | |
N * \ | |
N##_sort(N *self, int (* callback)(void *, void *)) { \ | |
qsort(self->vals, self->len - 1, sizeof(T), callback); \ | |
return self; \ | |
} \ | |
\ | |
/* \ | |
* Create a union of _self_ and _other_ array. Merging \ | |
* and removing duplicates. \ | |
*/ \ | |
\ | |
N * \ | |
N##_union(N *self, N *other) { \ | |
return N##_unique(N##_merge(self, other)); \ | |
} \ | |
\ | |
/* \ | |
* Shift an element off the beginning of _self_'s stack. \ | |
*/ \ | |
\ | |
T \ | |
N##_shift(N *self) { \ | |
return N##_delete_at(self, 0); \ | |
} \ | |
\ | |
/* \ | |
* Destroy _self_. \ | |
*/ \ | |
\ | |
void \ | |
N##_destroy(N *self) { \ | |
if (self->vals) free(self->vals); \ | |
if (self) free(self); \ | |
} | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment