diff --git a/ChangeLog b/ChangeLog index a51763db7e..c3329c1e5a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +Thu Oct 20 11:42:23 2011 Nobuyoshi Nakada + + * util.c (mmprepare): fix for fragmental size. + + * util.c (mmswap_, mmrot3_): portability improvement. + Thu Oct 20 05:58:02 2011 Eric Hodel * ext/openssl/ossl_ns_spki.c (Init_ossl_ns_spki): Stub documentation diff --git a/ext/-test-/string/qsort.c b/ext/-test-/string/qsort.c new file mode 100644 index 0000000000..0b34936d38 --- /dev/null +++ b/ext/-test-/string/qsort.c @@ -0,0 +1,61 @@ +#include "ruby.h" +#include "ruby/util.h" +#include "ruby/encoding.h" + +struct sort_data { + rb_encoding *enc; + long elsize; +}; + +static int +cmp_1(const void *ap, const void *bp, void *dummy) +{ + struct sort_data *d = dummy; + VALUE a = rb_enc_str_new(ap, d->elsize, d->enc); + VALUE b = rb_enc_str_new(bp, d->elsize, d->enc); + VALUE retval = rb_yield_values(2, a, b); + return rb_cmpint(retval, a, b); +} + +static int +cmp_2(const void *ap, const void *bp, void *dummy) +{ + int a = *(const unsigned char *)ap; + int b = *(const unsigned char *)bp; + return a - b; +} + +static VALUE +bug_str_qsort_bang(int argc, VALUE *argv, VALUE str) +{ + VALUE beg, len, size; + long l, b = 0, n, s = 1; + struct sort_data d; + + rb_scan_args(argc, argv, "03", &beg, &len, &size); + l = RSTRING_LEN(str); + if (!NIL_P(beg) && (b = NUM2INT(beg)) < 0 && (b += l) < 0) { + rb_raise(rb_eArgError, "out of bounds"); + } + if (!NIL_P(size) && (s = NUM2INT(size)) < 0) { + rb_raise(rb_eArgError, "negative size"); + } + if (NIL_P(len) || + (((n = NUM2INT(len)) < 0) ? + (rb_raise(rb_eArgError, "negative length"), 0) : + (b + n * s > l))) { + n = (l - b) / s; + } + rb_str_modify(str); + d.enc = rb_enc_get(str); + d.elsize = s; + ruby_qsort(RSTRING_PTR(str) + b, n, s, + rb_block_given_p() ? cmp_1 : cmp_2, &d); + return str; +} + +void +Init_qsort(VALUE klass) +{ + rb_define_method(klass, "qsort!", bug_str_qsort_bang, -1); +} diff --git a/test/-ext-/string/test_qsort.rb b/test/-ext-/string/test_qsort.rb new file mode 100644 index 0000000000..3a58523200 --- /dev/null +++ b/test/-ext-/string/test_qsort.rb @@ -0,0 +1,19 @@ +require 'test/unit' +require "-test-/string/string" + +class Test_StringQSort < Test::Unit::TestCase + def test_qsort + s = Bug::String.new("xxozfxx") + s.qsort! + assert_equal("foxxxxz", s) + end + + def test_qsort_slice + s = Bug::String.new("xxofzx1") + s.qsort!(nil, nil, 3) + assert_equal("fzxxxo1", s) + s = Bug::String.new("xxofzx231") + s.qsort!(nil, nil, 3) + assert_equal("231fzxxxo", s) + end +end diff --git a/util.c b/util.c index 45493f2855..37f76c7a7c 100644 --- a/util.c +++ b/util.c @@ -187,66 +187,97 @@ ruby_strtoul(const char *str, char **endptr, int base) /* mm.c */ -#define A ((int*)a) -#define B ((int*)b) -#define C ((int*)c) -#define D ((int*)d) +#define mmtype long +#define mmcount (16 / SIZEOF_LONG) +#define A ((mmtype*)a) +#define B ((mmtype*)b) +#define C ((mmtype*)c) +#define D ((mmtype*)d) +#define mmstep (sizeof(mmtype) * mmcount) #define mmprepare(base, size) do {\ - if (((VALUE)(base) & (0x3)) == 0)\ - if ((size) >= 16) mmkind = 1;\ + if (((VALUE)(base) % sizeof(mmtype)) == 0 && ((size) % sizeof(mmtype)) == 0) \ + if ((size) >= mmstep) mmkind = 1;\ else mmkind = 0;\ else mmkind = -1;\ - high = ((size) & (~0xf));\ - low = ((size) & 0x0c);\ + high = ((size) / mmstep) * mmstep;\ + low = ((size) % mmstep);\ } while (0)\ #define mmarg mmkind, size, high, low +#define mmargdecl int mmkind, size_t size, size_t high, size_t low -static void mmswap_(register char *a, register char *b, int mmkind, size_t size, size_t high, size_t low) +static void mmswap_(register char *a, register char *b, mmargdecl) { - register int s; if (a == b) return; if (mmkind >= 0) { + register mmtype s; +#if mmcount > 1 if (mmkind > 0) { register char *t = a + high; do { s = A[0]; A[0] = B[0]; B[0] = s; s = A[1]; A[1] = B[1]; B[1] = s; +#if mmcount > 2 s = A[2]; A[2] = B[2]; B[2] = s; - s = A[3]; A[3] = B[3]; B[3] = s; a += 16; b += 16; +#if mmcount > 3 + s = A[3]; A[3] = B[3]; B[3] = s; +#endif +#endif + a += mmstep; b += mmstep; } while (a < t); } +#endif if (low != 0) { s = A[0]; A[0] = B[0]; B[0] = s; - if (low >= 8) { s = A[1]; A[1] = B[1]; B[1] = s; - if (low == 12) {s = A[2]; A[2] = B[2]; B[2] = s;}}} +#if mmcount > 2 + if (low >= 2 * sizeof(mmtype)) { s = A[1]; A[1] = B[1]; B[1] = s; +#if mmcount > 3 + if (low >= 3 * sizeof(mmtype)) {s = A[2]; A[2] = B[2]; B[2] = s;} +#endif + } +#endif + } } else { - register char *t = a + size; + register char *t = a + size, s; do {s = *a; *a++ = *b; *b++ = s;} while (a < t); } } #define mmswap(a,b) mmswap_((a),(b),mmarg) -static void mmrot3_(register char *a, register char *b, register char *c, int mmkind, size_t size, size_t high, size_t low) +/* a, b, c = b, c, a */ +static void mmrot3_(register char *a, register char *b, register char *c, mmargdecl) { - register int s; if (mmkind >= 0) { + register mmtype s; +#if mmcount > 1 if (mmkind > 0) { register char *t = a + high; do { s = A[0]; A[0] = B[0]; B[0] = C[0]; C[0] = s; s = A[1]; A[1] = B[1]; B[1] = C[1]; C[1] = s; +#if mmcount > 2 s = A[2]; A[2] = B[2]; B[2] = C[2]; C[2] = s; - s = A[3]; A[3] = B[3]; B[3] = C[3]; C[3] = s; a += 16; b += 16; c += 16; +#if mmcount > 3 + s = A[3]; A[3] = B[3]; B[3] = C[3]; C[3] = s; +#endif +#endif + a += mmstep; b += mmstep; c += mmstep; } while (a < t); } +#endif if (low != 0) { s = A[0]; A[0] = B[0]; B[0] = C[0]; C[0] = s; - if (low >= 8) { s = A[1]; A[1] = B[1]; B[1] = C[1]; C[1] = s; - if (low == 12) {s = A[2]; A[2] = B[2]; B[2] = C[2]; C[2] = s;}}} +#if mmcount > 2 + if (low >= 2 * sizeof(mmtype)) { s = A[1]; A[1] = B[1]; B[1] = C[1]; C[1] = s; +#if mmcount > 3 + if (low == 3 * sizeof(mmtype)) {s = A[2]; A[2] = B[2]; B[2] = C[2]; C[2] = s;} +#endif + } +#endif + } } else { - register char *t = a + size; + register char *t = a + size, s; do {s = *a; *a++ = *b; *b++ = *c; *c++ = s;} while (a < t); } } @@ -269,9 +300,9 @@ typedef struct { char *LL, *RR; } stack_node; /* Stack structure for L,l,R,r */ ((*cmp)((b),(c),d)<0 ? (b) : ((*cmp)((a),(c),d)<0 ? (c) : (a))) : \ ((*cmp)((b),(c),d)>0 ? (b) : ((*cmp)((a),(c),d)<0 ? (a) : (c)))) +typedef int (cmpfunc_t)(const void*, const void*, void*); void -ruby_qsort(void* base, const size_t nel, const size_t size, - int (*cmp)(const void*, const void*, void*), void *d) +ruby_qsort(void* base, const size_t nel, const size_t size, cmpfunc_t *cmp, void *d) { register char *l, *r, *m; /* l,r:left,right group m:median point */ register int t, eq_l, eq_r; /* eq_l: all items in left group are equal to S */