fuzz_ball 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,276 @@
1
+ // Include the Ruby headers and goodies
2
+ #include <ruby.h>
3
+ #include "uthash.h"
4
+ #include <DupleIndex.h>
5
+
6
+ // The initialization method for this module
7
+ void Init_duple_index() {
8
+ FuzzBall = rb_define_module("FuzzBall");
9
+ DupleIndex = rb_define_class_under(FuzzBall, "DupleIndex", rb_cObject);
10
+
11
+ rb_define_alloc_func(DupleIndex, method_alloc_index);
12
+ rb_define_method(DupleIndex, "add", method_add, 2);
13
+ rb_define_method(DupleIndex, "match", method_match, 1);
14
+ }
15
+
16
+ /* method_alloc_index
17
+ *
18
+ * This method is a custom allocation method where we initialize
19
+ * the hash that will serve as the duple_index and store it in the
20
+ * duples pointer. We then store it within the instantiated ruby
21
+ * object for later use.
22
+ */
23
+ VALUE method_alloc_index(VALUE self) {
24
+ struct duples_hash *duples, *ptr;
25
+
26
+ // Initialize the hash with a single, hidden entry so that
27
+ // the *duples pointer doesn't point to NULL
28
+ ptr = malloc(sizeof(struct duples_hash));
29
+ ptr->id = duple_id(-1, -1);
30
+ ptr->strings = create_duple_pos(-1, -1, NULL, NULL);
31
+
32
+ HASH_ADD_INT(duples, id, ptr);
33
+
34
+ return Data_Wrap_Struct(self, NULL, method_free_index, duples);
35
+ }
36
+
37
+ /* method_free_index
38
+ *
39
+ * called by ruby when it's trying to free the instantiated
40
+ * DupleIndex class. In this method, we start the deallocation
41
+ * of memory by iterating over each hash member and calling
42
+ * free successively.
43
+ */
44
+ static void method_free_index(void *duples) {
45
+ destroy_index( duples );
46
+ }
47
+
48
+ /* destroy_index
49
+ *
50
+ * loops over each key in the duples hash, frees the memory
51
+ * associated with each hash member by deallocating the linked
52
+ * list that stores duple positions.
53
+ */
54
+ void destroy_index(struct duples_hash *duples) {
55
+ struct duples_hash *d, *d_tmp;
56
+
57
+ HASH_ITER(hh, duples, d, d_tmp) {
58
+ destroy_duple_pos(d->strings);
59
+
60
+ HASH_DEL(duples, d);
61
+ free(d);
62
+ }
63
+ }
64
+
65
+ /* destroy_duple_pos
66
+ *
67
+ * frees the memory used to store duple_pos structs
68
+ */
69
+ void destroy_duple_pos(struct duple_pos *head) {
70
+ struct duple_pos *c_pos, *n_pos;
71
+
72
+ c_pos = head;
73
+
74
+ while (1) {
75
+ n_pos = c_pos->next;
76
+ free(c_pos);
77
+
78
+ if (n_pos == NULL) {
79
+ break;
80
+ } else {
81
+ c_pos = n_pos;
82
+ }
83
+ }
84
+ }
85
+
86
+ /* method_add
87
+ *
88
+ * ruby public method that allows one to add strings to the duple index.
89
+ * strings are represented by a unique index (r_str_id) and by an array
90
+ * of ints, that represent each character. The add method will then
91
+ * index where each duple appears, allowing quick recall of what strings
92
+ * and where in those strings a given duple appears.
93
+ */
94
+ VALUE method_add(VALUE self, VALUE r_str_id, VALUE r_str) {
95
+ int i, str_id, str_len, c_a, c_b;
96
+ struct duples_hash *duples;
97
+
98
+ Data_Get_Struct(self, struct duples_hash, duples);
99
+ str_len = (int) RARRAY_LEN(r_str);
100
+ str_id = NUM2INT( r_str_id );
101
+
102
+ for (i=0; i<(str_len-1); i++) {
103
+ c_a = NUM2INT( RARRAY_PTR(r_str)[i] );
104
+ c_b = NUM2INT( RARRAY_PTR(r_str)[i+1] );
105
+ add_duple(duples, c_a, c_b, str_id, i);
106
+ }
107
+
108
+ return Qtrue;
109
+ }
110
+
111
+ /* duple_id
112
+ *
113
+ * method that hashes the two intergers that represent a duple into a
114
+ * unique integer value. Pretty simple algo.
115
+ */
116
+ int duple_id(int c_a, int c_b) {
117
+ return c_b + (c_a % MAX_CHARS) * MAX_CHARS;
118
+ }
119
+
120
+ /* duple_at
121
+ *
122
+ * method that allows one to find which strings (and where in those strings)
123
+ * a given duple appears (represented by two ints, c_a and c_b)
124
+ */
125
+ struct duples_hash *duple_at(struct duples_hash *duples, int c_a, int c_b) {
126
+ int d_id;
127
+ struct duples_hash *d;
128
+
129
+ d_id = duple_id(c_a, c_b);
130
+ HASH_FIND_INT(duples, &d_id, d);
131
+
132
+ return d;
133
+ }
134
+
135
+ /* add_duple
136
+ *
137
+ * Add a duple (represented by c_a and c_b) to the index, given the string (index)
138
+ * and position(pos) it appears.
139
+ */
140
+ void add_duple(struct duples_hash *duples, int c_a, int c_b, int index, int pos) {
141
+ struct duples_hash *ptr;
142
+ struct duple_pos *d_pos;
143
+
144
+ ptr = duple_at(duples, c_a, c_b);
145
+ if (ptr == NULL) {
146
+ ptr = malloc(sizeof(struct duples_hash));
147
+ ptr->id = duple_id(c_a, c_b);
148
+ ptr->strings = create_duple_pos(index, pos, NULL, NULL);
149
+
150
+ HASH_ADD_INT(duples, id, ptr);
151
+
152
+ } else {
153
+ d_pos = create_duple_pos(index, pos, ptr->strings, NULL);
154
+ ptr->strings->prev = d_pos;
155
+ ptr->strings = d_pos;
156
+ }
157
+ }
158
+
159
+ struct duple_pos *create_duple_pos(int index, int pos, struct duple_pos *next, struct duple_pos *prev) {
160
+ struct duple_pos *ptr;
161
+
162
+ ptr = malloc( sizeof(struct duple_pos) );
163
+
164
+ ptr->index = index;
165
+ ptr->pos = pos;
166
+ ptr->next = next;
167
+ ptr->prev = prev;
168
+
169
+ return ptr;
170
+ }
171
+
172
+ /* method_match
173
+ *
174
+ * this is a ruby public method that allows us to query the duple index. For a given
175
+ * needle string (represented as an array of numbers), we want to return a hash that
176
+ * lists which strings matched the needle, by how many times a duple in the needle
177
+ * matched a duple in the hits. To do that, we loop over the duples in the needle
178
+ * string, find which strings match that duple, then keep track of how many times a
179
+ * string matched a duple in the needle.
180
+ * */
181
+ VALUE method_match(VALUE self, VALUE needle) {
182
+
183
+ int i, n_needle, c_a, c_b, match_id;
184
+ struct match *matches, *match, *match_tmp;
185
+ struct duples_hash *duples, *duple;
186
+ struct duple_pos *pos;
187
+ VALUE matches_by_score = rb_hash_new();
188
+ VALUE arr;
189
+
190
+ Data_Get_Struct(self, struct duples_hash, duples);
191
+ matches = NULL;
192
+
193
+ n_needle = (int) RARRAY_LEN(needle);
194
+
195
+ if (n_needle < 2)
196
+ return matches_by_score; // If the needle has fewer than two chars,
197
+ // it's not a duple so return immediately
198
+
199
+ // Loop over each duple in the needle string
200
+ for (i=0; i<(n_needle-1); i++) {
201
+ c_a = NUM2INT( RARRAY_PTR(needle)[i] );
202
+ c_b = NUM2INT( RARRAY_PTR(needle)[i+1] );
203
+
204
+ duple = duple_at(duples, c_a, c_b); // Find the strings and positions
205
+ // where this duple is found
206
+
207
+ if (duple != NULL ) {
208
+ pos = duple->strings;
209
+ while (1) { // Loop over the strings where duple is found
210
+
211
+ // if String not found in matches hash, create new pointer
212
+ match_id = pos->index;
213
+ HASH_FIND_INT(matches, &match_id, match);
214
+
215
+ if (match == NULL) {
216
+ match = create_match(match_id, pos->pos, c_a, c_b);
217
+ HASH_ADD_INT(matches, id, match);
218
+
219
+ } else {
220
+ /* Only update the match count if the next matching duple appears
221
+ * *AFTER* the last matched duple. For instance, with a needle string
222
+ * of 'abc' and a indexed string of 'bcab', we don't want the 'bc'
223
+ * duple to match since it appears before 'ab' in the indexed string,
224
+ * whereas it appears after in the needle string.
225
+ */
226
+ if ((match->last_matched_position < pos->pos) && (match->last_matched_ca != c_a) && (match->last_matched_cb != c_b)) {
227
+ update_match( match, pos->pos, c_a, c_b );
228
+ }
229
+ }
230
+
231
+ if (pos->next == NULL)
232
+ break;
233
+
234
+ pos = pos->next;
235
+ }
236
+ }
237
+ }
238
+
239
+ // Loop over matches and construct the Ruby hash that stores the matching
240
+ // strings by the number of times they matched. Also free up memory as we go.
241
+ HASH_ITER(hh, matches, match, match_tmp) {
242
+ arr = rb_hash_aref(matches_by_score, INT2NUM(match->n_matches));
243
+ if (arr == Qnil)
244
+ arr = rb_ary_new();
245
+
246
+ rb_ary_push(arr, INT2NUM(match->id));
247
+ rb_hash_aset(matches_by_score, INT2NUM(match->n_matches), arr);
248
+
249
+ HASH_DEL(matches, match);
250
+ free(match);
251
+ }
252
+
253
+ return matches_by_score;
254
+ }
255
+
256
+ struct match *create_match(int id, int pos, int c_a, int c_b) {
257
+ struct match *new_match;
258
+
259
+ new_match = malloc( sizeof(struct match) );
260
+
261
+ new_match->id = id;
262
+ new_match->n_matches = 1;
263
+ new_match->last_matched_position = pos;
264
+ new_match->last_matched_ca = c_a;
265
+ new_match->last_matched_cb = c_b;
266
+
267
+ return new_match;
268
+ }
269
+
270
+ void update_match(struct match* match, int pos, int c_a, int c_b) {
271
+ match->n_matches++;
272
+ match->last_matched_position = pos;
273
+ match->last_matched_ca = c_a;
274
+ match->last_matched_cb = c_b;
275
+ }
276
+
@@ -0,0 +1,60 @@
1
+ #define MAX_CHARS 1000
2
+
3
+ /* duples_hash is the struct that is the "core" of the duple index.
4
+ * The id field is a unique integer that represents a unique duple.
5
+ * the duple_pos pointer is a pointer to a linked list that stores
6
+ * all of the strings that a given duple appears in.
7
+ */
8
+ struct duples_hash {
9
+ int id;
10
+ struct duple_pos *strings;
11
+ UT_hash_handle hh;
12
+ };
13
+
14
+ /* duple_pos is a node on a doubly-linked list that stores the index
15
+ * of the string and where inside the string a particular duple appears.
16
+ * The linked list will store all of the locations where a duple appears.
17
+ */
18
+ struct duple_pos {
19
+ int index;
20
+ int pos;
21
+
22
+ struct duple_pos *next;
23
+ struct duple_pos *prev;
24
+ };
25
+
26
+ /* match is a struct that keeps track of how many duples match in a given
27
+ * string. The id field is the id of the string, n_matches is the number
28
+ * of duples that have matched, the last_matched_position is the position
29
+ * of the last matching duple, and last_matched_ca and last_matched_cb
30
+ * records the first and second character of the last matched duple.
31
+ */
32
+ struct match {
33
+ int id;
34
+ int n_matches;
35
+ int last_matched_position;
36
+ int last_matched_ca;
37
+ int last_matched_cb;
38
+
39
+ UT_hash_handle hh;
40
+ };
41
+
42
+ // Ruby-related declarations
43
+ VALUE FuzzBall = Qnil;
44
+ VALUE DupleIndex = Qnil;
45
+
46
+ void Init_duple_index();
47
+ VALUE method_alloc_index(VALUE self);
48
+ static void method_free_index(void *duples);
49
+ VALUE method_add(VALUE self, VALUE r_str_id, VALUE r_str);
50
+ VALUE method_match(VALUE self, VALUE needle);
51
+
52
+ // Internally-used C-declarations (i.e., private methods)
53
+ void add_duple(struct duples_hash *duples, int c_a, int c_b, int index, int pos);
54
+ struct duples_hash *duple_at(struct duples_hash *duples, int c_a, int c_b);
55
+ int duple_id(int c_a, int c_b);
56
+ struct duple_pos *create_duple_pos(int index, int pos, struct duple_pos *next, struct duple_pos *prev);
57
+ void destroy_index(struct duples_hash *duples);
58
+ void destroy_duple_pos(struct duple_pos *head);
59
+ struct match *create_match(int id, int pos, int c_a, int c_b);
60
+ void update_match(struct match* match, int pos, int c_a, int c_b);
@@ -0,0 +1,5 @@
1
+ require 'mkmf'
2
+
3
+ dir_config("fuzz_ball/duple_index")
4
+ create_makefile("fuzz_ball/duple_index")
5
+
@@ -0,0 +1,226 @@
1
+ /*
2
+ Copyright (c) 2008-2011, Troy D. Hanson http://uthash.sourceforge.net
3
+ All rights reserved.
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ * Redistributions of source code must retain the above copyright
9
+ notice, this list of conditions and the following disclaimer.
10
+
11
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
12
+ IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
13
+ TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
14
+ PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
15
+ OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
16
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
17
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
18
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
19
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
20
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
21
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
22
+ */
23
+
24
+ /* a dynamic array implementation using macros
25
+ * see http://uthash.sourceforge.net/utarray
26
+ */
27
+ #ifndef UTARRAY_H
28
+ #define UTARRAY_H
29
+
30
+ #define UTARRAY_VERSION 1.9.4
31
+
32
+ #ifdef __GNUC__
33
+ #define _UNUSED_ __attribute__ ((__unused__))
34
+ #else
35
+ #define _UNUSED_
36
+ #endif
37
+
38
+ #include <stddef.h> /* size_t */
39
+ #include <string.h> /* memset, etc */
40
+ #include <stdlib.h> /* exit */
41
+
42
+ #define oom() exit(-1)
43
+
44
+ typedef void (ctor_f)(void *dst, const void *src);
45
+ typedef void (dtor_f)(void *elt);
46
+ typedef void (init_f)(void *elt);
47
+ typedef struct {
48
+ size_t sz;
49
+ init_f *init;
50
+ ctor_f *copy;
51
+ dtor_f *dtor;
52
+ } UT_icd;
53
+
54
+ typedef struct {
55
+ unsigned i,n;/* i: index of next available slot, n: num slots */
56
+ const UT_icd *icd; /* initializer, copy and destructor functions */
57
+ char *d; /* n slots of size icd->sz*/
58
+ } UT_array;
59
+
60
+ #define utarray_init(a,_icd) do { \
61
+ memset(a,0,sizeof(UT_array)); \
62
+ (a)->icd=_icd; \
63
+ } while(0)
64
+
65
+ #define utarray_done(a) do { \
66
+ if ((a)->n) { \
67
+ if ((a)->icd->dtor) { \
68
+ size_t _ut_i; \
69
+ for(_ut_i=0; _ut_i < (a)->i; _ut_i++) { \
70
+ (a)->icd->dtor(utarray_eltptr(a,_ut_i)); \
71
+ } \
72
+ } \
73
+ free((a)->d); \
74
+ } \
75
+ (a)->n=0; \
76
+ } while(0)
77
+
78
+ #define utarray_new(a,_icd) do { \
79
+ a=(UT_array*)malloc(sizeof(UT_array)); \
80
+ utarray_init(a,_icd); \
81
+ } while(0)
82
+
83
+ #define utarray_free(a) do { \
84
+ utarray_done(a); \
85
+ free(a); \
86
+ } while(0)
87
+
88
+ #define utarray_reserve(a,by) do { \
89
+ if (((a)->i+by) > ((a)->n)) { \
90
+ while(((a)->i+by) > ((a)->n)) { (a)->n = ((a)->n ? (2*(a)->n) : 8); } \
91
+ if ( ((a)->d=(char*)realloc((a)->d, (a)->n*(a)->icd->sz)) == NULL) oom(); \
92
+ } \
93
+ } while(0)
94
+
95
+ #define utarray_push_back(a,p) do { \
96
+ utarray_reserve(a,1); \
97
+ if ((a)->icd->copy) { (a)->icd->copy( _utarray_eltptr(a,(a)->i++), p); } \
98
+ else { memcpy(_utarray_eltptr(a,(a)->i++), p, (a)->icd->sz); }; \
99
+ } while(0)
100
+
101
+ #define utarray_pop_back(a) do { \
102
+ if ((a)->icd->dtor) { (a)->icd->dtor( _utarray_eltptr(a,--((a)->i))); } \
103
+ else { (a)->i--; } \
104
+ } while(0)
105
+
106
+ #define utarray_extend_back(a) do { \
107
+ utarray_reserve(a,1); \
108
+ if ((a)->icd->init) { (a)->icd->init(_utarray_eltptr(a,(a)->i)); } \
109
+ else { memset(_utarray_eltptr(a,(a)->i),0,(a)->icd->sz); } \
110
+ (a)->i++; \
111
+ } while(0)
112
+
113
+ #define utarray_len(a) ((a)->i)
114
+
115
+ #define utarray_eltptr(a,j) (((j) < (a)->i) ? _utarray_eltptr(a,j) : NULL)
116
+ #define _utarray_eltptr(a,j) ((char*)((a)->d + ((a)->icd->sz*(j) )))
117
+
118
+ #define utarray_insert(a,p,j) do { \
119
+ utarray_reserve(a,1); \
120
+ if (j > (a)->i) break; \
121
+ if ((j) < (a)->i) { \
122
+ memmove( _utarray_eltptr(a,(j)+1), _utarray_eltptr(a,j), \
123
+ ((a)->i - (j))*((a)->icd->sz)); \
124
+ } \
125
+ if ((a)->icd->copy) { (a)->icd->copy( _utarray_eltptr(a,j), p); } \
126
+ else { memcpy(_utarray_eltptr(a,j), p, (a)->icd->sz); }; \
127
+ (a)->i++; \
128
+ } while(0)
129
+
130
+ #define utarray_inserta(a,w,j) do { \
131
+ if (utarray_len(w) == 0) break; \
132
+ if (j > (a)->i) break; \
133
+ utarray_reserve(a,utarray_len(w)); \
134
+ if ((j) < (a)->i) { \
135
+ memmove(_utarray_eltptr(a,(j)+utarray_len(w)), \
136
+ _utarray_eltptr(a,j), \
137
+ ((a)->i - (j))*((a)->icd->sz)); \
138
+ } \
139
+ if ((a)->icd->copy) { \
140
+ size_t _ut_i; \
141
+ for(_ut_i=0;_ut_i<(w)->i;_ut_i++) { \
142
+ (a)->icd->copy(_utarray_eltptr(a,j+_ut_i), _utarray_eltptr(w,_ut_i)); \
143
+ } \
144
+ } else { \
145
+ memcpy(_utarray_eltptr(a,j), _utarray_eltptr(w,0), \
146
+ utarray_len(w)*((a)->icd->sz)); \
147
+ } \
148
+ (a)->i += utarray_len(w); \
149
+ } while(0)
150
+
151
+ #define utarray_resize(dst,num) do { \
152
+ size_t _ut_i; \
153
+ if (dst->i > (size_t)(num)) { \
154
+ if ((dst)->icd->dtor) { \
155
+ for(_ut_i=num; _ut_i < dst->i; _ut_i++) { \
156
+ (dst)->icd->dtor(utarray_eltptr(dst,_ut_i)); \
157
+ } \
158
+ } \
159
+ } else if (dst->i < (size_t)(num)) { \
160
+ utarray_reserve(dst,num-dst->i); \
161
+ if ((dst)->icd->init) { \
162
+ for(_ut_i=dst->i; _ut_i < num; _ut_i++) { \
163
+ (dst)->icd->init(utarray_eltptr(dst,_ut_i)); \
164
+ } \
165
+ } else { \
166
+ memset(_utarray_eltptr(dst,dst->i),0,(dst)->icd->sz*(num-dst->i)); \
167
+ } \
168
+ } \
169
+ dst->i = num; \
170
+ } while(0)
171
+
172
+ #define utarray_concat(dst,src) do { \
173
+ utarray_inserta((dst),(src),utarray_len(dst)); \
174
+ } while(0)
175
+
176
+ #define utarray_erase(a,pos,len) do { \
177
+ if ((a)->icd->dtor) { \
178
+ size_t _ut_i; \
179
+ for(_ut_i=0; _ut_i < len; _ut_i++) { \
180
+ (a)->icd->dtor(utarray_eltptr(a,pos+_ut_i)); \
181
+ } \
182
+ } \
183
+ if ((a)->i > (pos+len)) { \
184
+ memmove( _utarray_eltptr(a,pos), _utarray_eltptr(a,pos+len), \
185
+ ((a->i)-(pos+len))*((a)->icd->sz)); \
186
+ } \
187
+ (a)->i -= (len); \
188
+ } while(0)
189
+
190
+ #define utarray_clear(a) do { \
191
+ if ((a)->i > 0) { \
192
+ if ((a)->icd->dtor) { \
193
+ size_t _ut_i; \
194
+ for(_ut_i=0; _ut_i < (a)->i; _ut_i++) { \
195
+ (a)->icd->dtor(utarray_eltptr(a,_ut_i)); \
196
+ } \
197
+ } \
198
+ (a)->i = 0; \
199
+ } \
200
+ } while(0)
201
+
202
+ #define utarray_sort(a,cmp) do { \
203
+ qsort((a)->d, (a)->i, (a)->icd->sz, cmp); \
204
+ } while(0)
205
+
206
+ #define utarray_find(a,v,cmp) bsearch((v),(a)->d,(a)->i,(a)->icd->sz,cmp)
207
+
208
+ #define utarray_front(a) (((a)->i) ? (_utarray_eltptr(a,0)) : NULL)
209
+ #define utarray_next(a,e) (((e)==NULL) ? utarray_front(a) : ((((a)->i) > (utarray_eltidx(a,e)+1)) ? _utarray_eltptr(a,utarray_eltidx(a,e)+1) : NULL))
210
+ #define utarray_back(a) (((a)->i) ? (_utarray_eltptr(a,(a)->i-1)) : NULL)
211
+ #define utarray_eltidx(a,e) (((char*)(e) >= (char*)((a)->d)) ? (((char*)(e) - (char*)((a)->d))/(a)->icd->sz) : -1)
212
+
213
+ /* last we pre-define a few icd for common utarrays of ints and strings */
214
+ static void utarray_str_cpy(void *dst, const void *src) {
215
+ char **_src = (char**)src, **_dst = (char**)dst;
216
+ *_dst = (*_src == NULL) ? NULL : strdup(*_src);
217
+ }
218
+ static void utarray_str_dtor(void *elt) {
219
+ char **eltc = (char**)elt;
220
+ if (*eltc) free(*eltc);
221
+ }
222
+ static const UT_icd ut_str_icd _UNUSED_ = {sizeof(char*),NULL,utarray_str_cpy,utarray_str_dtor};
223
+ static const UT_icd ut_int_icd _UNUSED_ = {sizeof(int),NULL,NULL,NULL};
224
+
225
+
226
+ #endif /* UTARRAY_H */