diff --git a/inst/include/S4Vectors_interface.h b/inst/include/S4Vectors_interface.h index 45a1ce2..1ab272d 100644 --- a/inst/include/S4Vectors_interface.h +++ b/inst/include/S4Vectors_interface.h @@ -159,6 +159,18 @@ void get_matches_of_ordered_int_quads( int out_shift ); +int sort_void_array( + int *base, + int base_len, + const void *x, + size_t elem_size, + int desc, + int use_double_compar, + int use_radix, + unsigned short int *rxbuf1, + int *rxbuf2 +); + /* * Hash table management. * (see hash_utils.c) diff --git a/inst/include/_S4Vectors_stubs.c b/inst/include/_S4Vectors_stubs.c index ce2e8ef..8599975 100644 --- a/inst/include/_S4Vectors_stubs.c +++ b/inst/include/_S4Vectors_stubs.c @@ -115,6 +115,11 @@ DEFINE_NOVALUE_CCALLABLE_STUB(get_matches_of_ordered_int_quads, ( a1, b1, c1, d1, o1, nelt1, a2, b2, c2, d2, o2, nelt2, nomatch, out, out_shift) ) +DEFINE_CCALLABLE_STUB(int, sort_void_array, + (int *base, int base_len, const void *x, size_t elem_size, int desc, int use_double_compar, int use_radix, unsigned short int *rxbuf1, int *rxbuf2), + ( base, base_len, x, elem_size, desc, use_double_compar, use_radix, rxbuf1, rxbuf2) +) + /* * Stubs for callables defined in hash_utils.c */ diff --git a/src/R_init_S4Vectors.c b/src/R_init_S4Vectors.c index b7fc6ec..c42a923 100644 --- a/src/R_init_S4Vectors.c +++ b/src/R_init_S4Vectors.c @@ -129,6 +129,7 @@ void R_init_S4Vectors(DllInfo *info) REGISTER_CCALLABLE(_get_matches_of_ordered_int_pairs); REGISTER_CCALLABLE(_get_order_of_int_quads); REGISTER_CCALLABLE(_get_matches_of_ordered_int_quads); + REGISTER_CCALLABLE(_sort_void_array); /* hash_utils.c */ REGISTER_CCALLABLE(_new_htab); diff --git a/src/S4Vectors.h b/src/S4Vectors.h index 02d7fc4..2d0e577 100644 --- a/src/S4Vectors.h +++ b/src/S4Vectors.h @@ -195,6 +195,18 @@ void _get_matches_of_ordered_int_quads( int out_shift ); +int _sort_void_array( + int *base, + int base_len, + const void *x, + size_t elem_size, + int desc, + int use_double_compar, + int use_radix, + unsigned short int *rxbuf1, + int *rxbuf2 +); + /* hash_utils.c */ diff --git a/src/sort_utils.c b/src/sort_utils.c index d2a3af8..e1db74a 100644 --- a/src/sort_utils.c +++ b/src/sort_utils.c @@ -13,6 +13,8 @@ static const int *aa, *bb, *cc, *dd; static int aa_desc, bb_desc, cc_desc, dd_desc; +static size_t byte_elem_size; +static const unsigned char *global_byte_array; #define COMPARE_TARGET_INTS(target, i1, i2, desc) \ ((desc) ? (target)[(i2)] - (target)[(i1)] \ @@ -86,6 +88,27 @@ static int compar4_stable(const void *p1, const void *p2) return i1 - i2; } +static int comparbyte_stable(const void *p1, const void *p2){ + int i1, i2, ret; + i1 = *((const int *) p1); + i2 = *((const int *) p2); + ret = memcmp(&(global_byte_array[i1*byte_elem_size]), &(global_byte_array[i2*byte_elem_size]), byte_elem_size); + if(aa_desc) ret *= -1; + return ret == 0 ? i1 - i2 : ret; +} + +static int compardouble_stable(const void *p1, const void *p2){ + // can't use memcmp for doubles unfortunately + int i1, i2, ret; + i1 = *((const int *) p1); + i2 = *((const int *) p2); + double v1 = ((double*)global_byte_array)[i1]; + double v2 = ((double*)global_byte_array)[i2]; + ret = (v1 > v2) - (v1 < v2); + if(aa_desc) ret *= -1; + return ret == 0 ? i1 - i2 : ret; +} + static void qsort1(int *base, int base_len, const int *a, int a_desc) { aa = a; @@ -93,6 +116,18 @@ static void qsort1(int *base, int base_len, const int *a, int a_desc) qsort(base, base_len, sizeof(int), compar1_stable); } +static void qsort_void(int *base, int base_len, const void *a, size_t a_size, + int a_desc, int use_double_compar){ + // have to cast to bytes so we can index + global_byte_array = (const unsigned char*)a; + aa_desc = a_desc; + byte_elem_size = a_size; + if(!use_double_compar) + qsort(base, base_len, sizeof(int), comparbyte_stable); + else + qsort(base, base_len, sizeof(int), compardouble_stable); +} + static void qsort2(int *base, int base_len, const int *a, const int *b, int a_desc, int b_desc) @@ -1350,3 +1385,60 @@ void _get_matches_of_ordered_int_quads( return; } +/**************************************************************************** + * Getting the order of a void* array using memcmp + * + */ + +/* base: 0-based indices into 'x'. + rxbuf1, rxbuf2: NULL or user-allocated buffers of length 'base_len'. + Returns 0 if nothing to sort, that is, if 'base' is already sorted with + respect to 'x'. Otherwise returns 1, or a negative value if an error + occurred. Has a dedicated switch for sorting float/double...it's clunky, + but I couldn't think of a better way. Primary use-case is XRaw anyway, + so it shouldn't be a huge deal. */ +int _sort_void_array(int *base, int base_len, + const void *x, size_t elem_size, + int desc, int use_double_compar, + int use_radix, unsigned short int *rxbuf1, int *rxbuf2) +{ + // will add this in eventually, not critical for functionality + // Below section is copied from _sort_int_array, will need to be adapted + // Until this is incorporated, use_radix, rxbuf1, and rxbuf2 are not used + /* + int qsort_cutoff, ret, auto_rxbuf1, auto_rxbuf2; + + rxtargets[0] = x; + rxdescs[0] = desc; + + qsort_cutoff = (use_radix && can_use_rxsort()) ? 1024 : base_len; + ret = lucky_sort_targets(base, base_len, rxtargets, rxdescs, 1, qsort_cutoff); + if (ret != 0) + return ret != 1; + + auto_rxbuf1 = rxbuf1 == NULL; + if (auto_rxbuf1) { + rxbuf1 = alloc_rxbuf1(base_len); + if (rxbuf1 == NULL) + return -1; + } + auto_rxbuf2 = rxbuf2 == NULL; + if (auto_rxbuf2) { + rxbuf2 = alloc_rxbuf2(base_len, rxbuf1, auto_rxbuf1); + if (rxbuf2 == NULL) + return -2; + } + + last_rxlevel = 1; + base_uidx_buf = rxbuf1; + rxsort_rec(base, base_len, rxbuf2, 0, 0); + + if (auto_rxbuf2) + free(rxbuf2); + if (auto_rxbuf1) + free(rxbuf1); + */ + + qsort_void(base, base_len, x, elem_size, desc, use_double_compar); + return 1; +}