Skip to content

Instantly share code, notes, and snippets.

@kdrnic
Created July 31, 2021 04:08
Show Gist options
  • Save kdrnic/5c4b5f2e9e32f7a862a2aaf1f50e1554 to your computer and use it in GitHub Desktop.
Save kdrnic/5c4b5f2e9e32f7a862a2aaf1f50e1554 to your computer and use it in GitHub Desktop.
#ifndef ARR_H
#define ARR_H
#include <assert.h>
//Usage: arr_t(type) a arr_cleanup;
#define arr_cleanup __attribute__ ((__cleanup__(arr_cleanup_func)))
#define arr_t(t) \
struct \
{ \
t *data; \
int len, siz; \
char is_static; \
}
typedef arr_t(void*) arr_void;
typedef arr_t(char*) arr_str;
typedef arr_t(int) arr_int;
typedef arr_t(char) arr_char;
typedef arr_t(float) arr_float;
typedef arr_t(double) arr_double;
void arr_cleanup_func(void *v);
//Note this factor is used divided by 2 - i.e. 3 = 1.5 grow factor
//default: 2
#ifndef arr_grow_factor
extern const int arr_grow_factor;
#endif
#define arr_empty {.len = 0, .siz = 0, .data = 0, .is_static = 0}
//Pre-allocate memory for "max" elements
//the only macro to be used in submacros
#define arr_reserve(___pl, ___max)\
({\
__typeof__(___pl) __pl = (___pl);\
int __max = (___max);\
\
assert(__pl);\
\
if(__max > __pl->siz){\
assert(!__pl->is_static);\
__pl->siz = __max;\
__pl->data = realloc(__pl->data, sizeof(*__pl->data) * ((__pl->siz * arr_grow_factor) / 2));\
}\
\
(void) 0;\
})
//Size down array to fit exactly, freeing memory
#define arr_unreserve(pl)\
({\
__typeof__(pl) _pl = (pl);\
\
assert(_pl);\
\
if(!_pl->is_static){\
_pl->siz = _pl->len;\
_pl->data = realloc(_pl->data, sizeof(*_pl->data) * _pl->siz);\
}\
\
(void) 0;\
})
//out = pl
#define arr_cpy(pl, out)\
({\
const __typeof__(pl) _pl = (pl);\
__typeof__(out) _out = (out);\
\
assert(_pl);\
assert(_out);\
assert(__builtin_types_compatible_p(__typeof__(_pl->data), __typeof__(_out->data)));\
\
_out->len = _pl->len;\
arr_reserve(_out, _out->len);\
memcpy(_out->data, _pl->data, sizeof(*_out->data) * _pl->len);\
\
(void) 0;\
})
//out = a + b
#define arr_join(a, b, out)\
({\
const __typeof__(a) _a = (a);\
const __typeof__(b) _b = (b);\
__typeof__(out) _out = out;\
\
assert(__builtin_types_compatible_p(__typeof__(_a->data), __typeof__(_b->data)));\
assert(__builtin_types_compatible_p(__typeof__(_a->data), __typeof__(_out->data)));\
assert(_a);\
assert(_b);\
assert(_out);\
\
_out->len = _a->len + _b->len;\
arr_reserve(_out, _out->len);\
memcpy(_out->data, _a->data, sizeof(*_out->data) * _a->len);\
memcpy(_out->data + _a->len, _b->data, sizeof(*_out->data) * _b->len);\
\
(void) 0;\
})
//a += b, a and b are arr_ pointers
#define arr_cat(a, b)\
({\
__typeof__(a) _a = (a);\
const __typeof__(b) _b = (b);\
\
assert(__builtin_types_compatible_p(__typeof__(_a->data), __typeof__(_b->data)));\
assert(_a);\
\
if(_b){\
arr_reserve(_a, _a->len + _b->len);\
memcpy(_a->data + _a->len, _b->data, sizeof(*_a->data) * _b->len);\
_a->len += _b->len;\
}\
\
_a->len;\
})
//a += b, b is a regular static array
#define arr_cat_s(a, b)\
({\
__typeof__(a) _a = (a);\
const __typeof__(b[0]) *_b = (b);\
const int _item_count = sizeof(b) / sizeof(b[0]);\
\
assert(__builtin_types_compatible_p(__typeof__(_a->data[0]), __typeof__(_b[0])));\
assert(_a);\
\
arr_reserve(_a, _a->len + _item_count);\
memcpy(_a->data + _a->len, _b, sizeof(*_a->data) * _item_count);\
_a->len += _item_count;\
\
_a->len;\
})
//a += b up to n, b is a regular static or dynamic array, n is a count
#define arr_cat_n(a, b, n)\
({\
__typeof__(a) _a = (a);\
const __typeof__(b[0]) *_b = (b);\
const int _item_count = n;\
\
assert(__builtin_types_compatible_p(__typeof__(_a->data[0]), __typeof__(_b[0])));\
assert(_a);\
\
arr_reserve(_a, _a->len + _item_count);\
memcpy(_a->data + _a->len, _b, sizeof(*_a->data) * _item_count);\
_a->len += _item_count;\
\
_a->len;\
})
//a += {...}
//_va stands for variadic args version
//Note going out of the way to avoid "uninitialized", "braces around scalar", "empty scalar initializer" warnings
#define arr_cat_va(a, items...)\
({\
__typeof__(a) _a = (a);\
const __typeof__(_a->data[0]) _zero[1] = {};\
const __typeof__(_a->data[0]) _items[] = {_zero[0], items};\
const int _item_count = (sizeof(_items) / sizeof(_items[0])) - 1;\
\
assert(_a);\
assert(!_a->is_static);\
\
arr_reserve(_a, _a->len + _item_count);\
memcpy(_a->data + _a->len, _items + 1, sizeof(*_a->data) * _item_count);\
_a->len += _item_count;\
\
_a->len;\
})
//pushes p
#define arr_push(pl, p)\
({\
__typeof__(pl) _pl = (pl);\
__typeof__(p) _p = (p);\
\
assert(__builtin_types_compatible_p(__typeof__(*_pl->data), __typeof__(_p)));\
assert(_pl);\
\
arr_reserve(_pl, _pl->len + 1);\
_pl->data[_pl->len++] = _p;\
\
_pl->len;\
})
//Automatically called when arr_cleanup is used
#define arr_free(pl)\
({\
__typeof__(pl) _pl = (pl);\
\
assert(_pl);\
\
if(!_pl->is_static){\
if(_pl->data) free(_pl->data);\
_pl->data = 0;\
_pl->len = 0;\
_pl->siz = 0;\
}\
\
(void) 0;\
})
//Unlike Js Array.prototype.splice, doesn't return an array of deleted
#define arr_splice(pl, start, del_count, items...)\
({\
__typeof__(pl) _pl = (pl);\
const int _start = (start);\
const int _del_count = (del_count) + 0; /*del_count_ is thus optional*/\
const __typeof__(*_pl->data) _zero;\
const __typeof__(*_pl->data) _items[] = {_zero, items}; /*_items is thus also optional*/\
const int _item_count = (sizeof(_items) / sizeof(_zero)) - 1;\
int _i, _j;\
const int _new_siz = _pl->len - _del_count + _item_count;\
\
assert(_pl);\
assert(_start >= 0);\
assert(_start < _pl->len);\
assert(_start + _del_count <= _pl->len);\
\
arr_reserve(_pl, _new_siz);\
memmove(\
_pl->data + _start + _item_count,\
_pl->data + _start + _del_count,\
(_pl->len - _start - _del_count) * sizeof(_pl->data)\
);\
for(_i = _start, _j = 1; _j <= _item_count; _i++, _j++){\
_pl->data[_i] = _items[_j];\
}\
_pl->len = _new_siz;\
\
_pl->len;\
})
//Remove @idx without keeping order
#define arr_remove(pl, idx)\
({\
__typeof__(pl) _pl = (pl);\
const int _idx = idx;\
\
assert(_pl);\
assert(_idx < _pl->len);\
\
_pl->data[_idx] = _pl->data[--_pl->len];\
\
_pl->len;\
})
#define ARR_ITEMS_FIRST(_a, ...) ((_a))
//Usage: b = *((arr_int *) arr_of(1, 2, 3, 4, 5));
//Uses __builtin_alloca to allocate a temporary, which will waste some stack
#define arr_of(items...)\
({\
arr_void *_arr_tmp = __builtin_alloca(sizeof(arr_void));\
__typeof__(ARR_ITEMS_FIRST(items)) _items[] = {items};\
\
_arr_tmp->is_static = 0;\
_arr_tmp->len = sizeof(_items) / sizeof(_items[0]);\
_arr_tmp->siz = _arr_tmp->len;\
_arr_tmp->data = malloc(sizeof(ARR_ITEMS_FIRST(items)) * _arr_tmp->len);\
memcpy(_arr_tmp->data, _items, _arr_tmp->len * sizeof(ARR_ITEMS_FIRST(items)));\
\
_arr_tmp;\
})
//Fills an array with the value passed
#define arr_fill(pl, val)\
({\
__typeof__(pl) _pl = (pl);\
const __typeof__(val) _val = (val);\
int _i;\
\
assert(_pl);\
\
for(_i = 0; _i < _pl->len; _i++){\
_pl->data[_i] = _val;\
}\
\
(void) 0;\
})
//True if every element passes
//test must be an expression or statement expression, with parameters (el, idx)
#define arr_every(pl, el, idx, test)\
({\
const __typeof__(pl) _pl = (pl);\
int _i;\
\
assert(_pl);\
\
for(_i = 0; _i < _pl->len; _i++){\
if(!({\
__typeof__(*_pl->data) el = _pl->data[_i];\
const int idx = _i;\
test;\
})) break;\
}\
\
_i == _pl->len;\
})
//Filter in-place, doesn't keep order
//test must be an expression or statement expression, with parameters (el, idx)
#define arr_sieve(pl, el, idx, test)\
({\
__typeof__(pl) _pl = (pl);\
int _i;\
\
assert(_pl);\
\
for(_i = 0; _i < _pl->len;){\
if(!({\
__typeof__(*_pl->data) el = _pl->data[_i];\
const int idx = _i;\
test;\
})) _pl->data[_i] = _pl->data[--_pl->len];\
else _i++;\
}\
\
_pl->len;\
})
//Filter in-place, doesn't keep order, pointer version
//test must be an expression or statement expression, with parameters (el, idx)
#define arr_sieve_p(pl, el, idx, test)\
({\
__typeof__(pl) _pl = (pl);\
int _i;\
\
assert(_pl);\
\
for(_i = 0; _i < _pl->len;){\
if(!({\
__typeof__(_pl->data) el = _pl->data + _i;\
const int idx = _i;\
test;\
})) _pl->data[_i] = _pl->data[--_pl->len];\
else _i++;\
}\
\
_pl->len;\
})
//out=filter(pl)
//test must be an expression or statement expression, with parameters (el, idx)
//TODO: turn cache into bitset
#define arr_filter(pl, el, idx, test, out)\
({\
const __typeof__(pl) _pl = (pl);\
__typeof__(out) _out = (out);\
int _i;\
unsigned char *_cache = malloc(_pl->len);\
\
assert(_pl);\
assert(_out);\
\
_out->len = 0;\
for(_i = 0; _i < _pl->len; _i++){\
if((_cache[_i] = ({\
__typeof__(*_pl->data) el = _pl->data[_i];\
const int idx = _i;\
test;\
}))){\
_out->len++;\
}\
}\
arr_reserve(_out, _out->len);\
_out->len = 0;\
for(_i = 0; _i < _pl->len; _i++){\
if(_cache[_i]){\
_out->data[_out->len++] = _pl->data[_i];\
}\
}\
free(_cache);\
\
(void) 0;\
})
//First element passing test
//test must be an expression or statement expression, with parameters (el, idx)
#define arr_find(pl, el, idx, test, default)\
({\
const __typeof__(pl) _pl = (pl);\
int _i;\
\
assert(_pl);\
\
for(_i = 0; _i < _pl->len; _i++){\
if(({\
__typeof__(*_pl->data) el = _pl->data[_i];\
const int idx = _i;\
test;\
})) break;\
}\
\
_i < _pl->len ? _pl->data[_i] : default;\
})
//First element passing test, returned by pointer
//test must be an expression or statement expression, with parameters (el, idx)
#define arr_find_p(pl, el, idx, test)\
({\
const __typeof__(pl) _pl = (pl);\
int _i;\
\
assert(_pl);\
\
for(_i = 0; _i < _pl->len; _i++){\
if(({\
__typeof__(*_pl->data) el = _pl->data[_i];\
const int idx = _i;\
test;\
})) break;\
}\
\
_i < _pl->len ? _pl->data + _i : 0;\
})
//Index of first element passing test
//test must be an expression or statement expression, with parameters (el, idx)
#define arr_findidx(pl, el, idx, test)\
({\
const __typeof__(pl) _pl = (pl);\
int _i;\
\
assert(_pl);\
\
for(_i = 0; _i < _pl->len; _i++){\
if(({\
__typeof__(*_pl->data) el = _pl->data[_i];\
const int idx = _i;\
test;\
})) break;\
}\
\
_i;\
})
//func must be an expression or statement expression, with parameters (el, idx)
#define arr_foreach(pl, el, idx, func)\
({\
__typeof__(pl) _pl = (pl);\
int _i;\
\
assert(_pl);\
\
for(_i = 0; _i < _pl->len; _i++){\
({\
__typeof__(*_pl->data) el = _pl->data[_i];\
const int idx = _i;\
func;\
});\
}\
\
(void) 0;\
})
//func must be an expression or statement expression, with parameters (el, idx)
#define arr_foreach_p(pl, el, idx, func)\
({\
__typeof__(pl) _pl = (pl);\
int _i;\
\
assert(_pl);\
\
for(_i = 0; _i < _pl->len; _i++){\
({\
__typeof__(_pl->data) el = _pl->data + _i;\
const int idx = _i;\
func;\
});\
}\
\
(void) 0;\
})
//True if at least 1 element passes
//test must be an expression or statement expression, with parameters (el, idx)
#define arr_some(pl, el, idx, test)\
({\
const __typeof__(pl) _pl = (pl);\
int _i;\
\
assert(_pl);\
\
for(_i = 0; _i < _pl->len; _i++){\
if(!({\
__typeof__(*_pl->data) el = _pl->data[_i];\
const int idx = _i;\
test;\
})) break;\
}\
\
_i < _pl->len;\
})
//True if pl has val
#define arr_includes(pl, val)\
({\
const __typeof__(pl) _pl = (pl);\
const __typeof__(*_pl->data) _val = (val);\
int _i;\
\
assert(_pl);\
\
for(_i = 0; _i < _pl->len; _i++){\
if(_pl->data[_i] == _val) break;\
}\
\
_i < _pl->len;\
})
//Index of first element equal to
#define arr_idxof(pl, val)\
({\
const __typeof__(pl) _pl = (pl);\
const __typeof__(*_pl->data) _val = (val);\
int _i;\
\
assert(_pl);\
\
for(_i = 0; _i < _pl->len; _i++){\
if(_pl->data[_i] == _val) break;\
}\
\
_i;\
})
//out=map(pl, func)
//func must be an expression or statement expression, with parameters (el, idx)
#define arr_map(pl, el, idx, func, out)\
({\
const __typeof__(pl) _pl = (pl);\
__typeof__(out) _out = (out);\
int _i;\
\
assert(_pl);\
assert(_func);\
assert(_out);\
\
arr_reserve(_out, _pl->len);\
for(_i = 0; _i < _pl->len; _i++){\
_out->data[_i] = ({\
__typeof__(*_pl->data) el = _pl->data[_i];\
const int idx = _i;\
func;\
});\
}\
\
(void) 0;\
})
//Removes last element
#define arr_pop(pl)\
({\
__typeof__(pl) _pl = (pl);\
\
assert(_pl);\
assert(_pl->len);\
\
_pl->data[--_pl->len];\
})
//Reverses in place
#define arr_reverse(pl)\
({\
__typeof__(pl) _pl = (pl);\
int i;\
\
assert(_pl);\
\
for(i = 0; i < _pl->len / 2; i++){\
const __typeof__(*_pl->data) _tmp = _pl->data[i];\
_pl->data[i] = _pl->data[_pl->len - 1 - i];\
_pl->data[_pl->len - 1 - i] = _tmp;\
}\
\
(void) 0;\
})
//Removes first element
#define arr_shift(pl)\
({\
__typeof__(pl) _pl = (pl);\
\
assert(_pl);\
assert(_pl->len);\
\
const __typeof__(*_pl->data) _val = _pl->data[0];\
_pl->len--;\
memmove(_pl->data, _pl->data + 1, sizeof(*_pl->data) * _pl->len);\
\
_val;\
})
//Sorts array in-place
/*
On qsort:
void qsort( void *ptr, size_t count, size_t size,
int (*comp)(const void *, const void *) );
Sorts the given array pointed to by ptr in ascending order. The array contains count
elements of size bytes. Function pointed to by comp is used for object comparison.
If comp indicates two elements as equivalent, their order in the resulting sorted array
is unspecified.
comp - comparison function which returns ​a negative integer value if the first argument
is less than the second, a positive integer value if the first argument is greater than
the second and zero if the arguments are equal. The signature of the comparison function
should be equivalent to the following:
int cmp(const void *a, const void *b);
The function must not modify the objects passed to it and must return consistent results
when called for the same objects, regardless of their positions in the array.
*/
#define arr_sort(pl, func)\
({\
__typeof__(pl) _pl = (pl);\
const int (*_func)(const __typeof__(*_pl->data) *, const __typeof__(*_pl->data) *) = (func);\
\
assert(_pl);\
assert(_func);\
\
qsort(_pl->data, _pl->len, sizeof(*_pl->data), (int (*)(const void *, const void *))_func);\
\
(void) 0;\
})
//pl[idx1] <-> pl[idx2]
#define arr_swap(pl, idx1, idx2)\
({\
__typeof__(pl) _pl = (pl);\
const int _idx1 = idx1;\
const int _idx2 = idx2;\
\
assert(_pl);\
assert(_idx1 < _pl->len);\
assert(_idx2 < _pl->len);\
\
const __typeof__(*_pl->data) _val = _pl->data[_idx1];\
_pl->data[_idx1] = _pl->data[_idx2];\
_pl->data[_idx2] = _val;\
\
(void) 0;\
})
#ifdef arr_sort_stack_static
//Do not use dynamic allocation for sorting stack
#define arr_sort_stack {.siz=256,.data=__builtin_alloca(256),.is_static=1}
#else
#define arr_sort_stack {}
#endif
//Non-recursive quicksort
//Uses middle pivot partition method - avoids worst case for sorted arrays
//Found to perform better than regular qsort generally
//func is a function something like el_a - el_b
#define arr_lampsort(toSort, el_a, el_b, func)\
({\
__typeof__(toSort) _toSort = (toSort);\
\
assert(_toSort);\
\
if(_toSort->len > 1){\
arr_t(int) _intervals = arr_sort_stack;\
arr_reserve(&_intervals, (31 - __builtin_clz(_toSort->len)) * 3);\
arr_push(&_intervals, 0);\
arr_push(&_intervals, _toSort->len - 1);\
do{\
int _hi = arr_pop(&_intervals);\
int _lo = arr_pop(&_intervals);\
int _span = _hi - _lo;\
if(_span >= 2){\
int _i, _j;\
const __typeof__(*_toSort->data) el_b = _toSort->data[_lo + (int)(_span * 0.75)];\
for(_i = _lo, _j = _hi; 1; _i++, _j--){\
while(({ const __typeof__(*_toSort->data) el_a = _toSort->data[_i]; func; }) < 0) _i++;\
while(({ const __typeof__(*_toSort->data) el_a = _toSort->data[_j]; func; }) > 0) _j--;\
if(_i >= _j) break;\
arr_swap(_toSort, _i, _j);\
}\
arr_push(&_intervals, _lo);\
arr_push(&_intervals, _j);\
arr_push(&_intervals, _i);\
arr_push(&_intervals, _hi);\
}\
else if(_span == 1){\
const __typeof__(*_toSort->data) el_a = _toSort->data[_lo];\
const __typeof__(*_toSort->data) el_b = _toSort->data[_hi];\
if(func > 0){\
arr_swap(_toSort, _lo, _hi);\
}\
}\
} while(_intervals.len);\
arr_free(&_intervals);\
}\
\
(void) 0;\
})
//Radix sort
//For arrays of integer-keyable elements
//func gives the key for the element
#define arr_radixsort(toSort, el, func)\
({\
__typeof__(toSort) _toSort = (toSort);\
int _i, _j;\
\
assert(_toSort);\
\
if(_toSort->len > 1){\
arr_t(int) _intervals = arr_sort_stack;\
arr_reserve(&_intervals, (31 - __builtin_clz(_toSort->len)) * 3);\
arr_push(&_intervals, 0);\
arr_push(&_intervals, _toSort->len - 1);\
arr_push(&_intervals, 30);\
while(_intervals.len){\
const int _bit = arr_pop(&_intervals);\
const int _b = arr_pop(&_intervals);\
const int _a = arr_pop(&_intervals);\
\
const int _bitmask = 1 << (_bit);\
\
for(_i = _a, _j = _b; _i <= _j;){\
const __typeof__(*_toSort->data) el = _toSort->data[_i];\
if((func) & _bitmask){\
arr_swap(_toSort, _i, _j);\
_j--;\
}\
else{\
_i++;\
}\
}\
\
if(_bitmask > 1){\
if(_a < _j){\
arr_push(&_intervals, _a);\
arr_push(&_intervals, _j);\
arr_push(&_intervals, _bit - 1);\
}\
if(_i < _b){\
arr_push(&_intervals, _i);\
arr_push(&_intervals, _b);\
arr_push(&_intervals, _bit - 1);\
}\
}\
}\
arr_free(&_intervals);\
}\
\
(void) 0;\
})
//Returns last el
#define arr_last(pl)\
({\
const __typeof__(pl) _pl = (pl);\
__typeof__(*_pl->data) _res;\
\
assert(_pl);\
assert(_pl->len);\
\
_res = _pl->data[_pl->len - 1];\
\
_res;\
})
//Returns 1st el
#define arr_first(pl)\
({\
const __typeof__(pl) _pl = (pl);\
__typeof__(*_pl->data) _res;\
\
assert(_pl);\
assert(_pl->len);\
\
_res = _pl->data[0];\
\
_res;\
})
//Returns last el as ptr
#define arr_lastp(pl)\
({\
const __typeof__(pl) _pl = (pl);\
__typeof__(*_pl->data) _res;\
\
assert(_pl);\
assert(_pl->len);\
\
_pl->data + _pl->len - 1;\
})
//Returns 1st el as ptr
#define arr_firstp(pl)\
({\
const __typeof__(pl) _pl = (pl);\
__typeof__(*_pl->data) _res;\
\
assert(_pl);\
assert(_pl->len);\
\
_pl->data;\
})
//Binary search
//comp must be an expression or statement expression, with parameters (a, b)
//Usage:
//typedef struct { [...] unsigned int hash; [...] } foo;
//arr_t(foo) foos;
//[...]
//int idx = arr_bsearch(&foos, ((int)a.hash) - ((int)b.hash));
#define arr_bsearch(pl, a, b, comp)\
({\
const __typeof__(pl) _pl = (pl);\
const int _ofs = (ofs);\
const __typeof__(val) _val = (val);\
int _l = 0, _r = _pl->len - 1;\
int _idx = _pl->len;\
\
while(_l <= _r){\
const int _m = _l + (_r - _l) / 2;\
const __typeof__(_pl->data) _a = _pl->data + _m;\
__typeof__(val) _val2;\
\
memcpy(&_val2, ((char *) _a) + _ofs, sizeof(_val));\
\
const int _diff = ({\
const __typeof__(_val) a = _val;\
const __typeof__(_val) b = _val2;\
comp;\
});\
\
if(!_diff){\
_idx = _m;\
break;\
}\
else if(_diff > 0) _l = _m + 1;\
else if(_diff < 0) _r = _m - 1;\
}\
\
_idx;\
})
//Binary search, but over a field property
//Implementation avoids use of memcmp due to endianness issue
//Usage:
//typedef struct { [...] unsigned int hash; [...] } foo;
//arr_t(foo) foos;
//[...]
//int idx = arr_bsearch_ex(&foos, offsetof(foo, hash), a_hash);
#define arr_bsearch_ex(pl, ofs, val)\
({\
const __typeof__(pl) _pl = (pl);\
const int _ofs = (ofs);\
const __typeof__(val) _val = (val);\
int _l = 0, _r = _pl->len - 1;\
int _idx = _pl->len;\
\
while(_l <= _r){\
const int _m = _l + (_r - _l) / 2;\
const __typeof__(_pl->data) _a = _pl->data + _m;\
__typeof__(val) _val2;\
\
memcpy(&_val2, ((char *) _a) + _ofs, sizeof(_val));\
if(_val == _val2){\
_idx = _m;\
break;\
}\
else if(_val > _val2) _l = _m + 1;\
else if(_val < _val2) _r = _m - 1;\
}\
\
_idx;\
})
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment