From: Dmitry Volyntsev Date: Mon, 25 May 2020 14:21:20 +0000 (+0000) Subject: Introduced quick sort implementation. X-Git-Url: http://git.kaiwu.me/postgresql/log/contrib/postgres_fdw/static/gitweb.js?a=commitdiff_plain;h=737671d56e631a34829660e2c67a5e752e4fe4d8;p=njs.git Introduced quick sort implementation. --- diff --git a/auto/sources b/auto/sources index dbad78c6..a14cd0ca 100644 --- a/auto/sources +++ b/auto/sources @@ -20,6 +20,7 @@ NJS_LIB_SRCS=" \ src/njs_malloc.c \ src/njs_mp.c \ src/njs_sprintf.c \ + src/njs_utils.c \ src/njs_chb.c \ src/njs_value.c \ src/njs_vm.c \ diff --git a/src/njs_array.c b/src/njs_array.c index 3bfcf4c0..265cec86 100644 --- a/src/njs_array.c +++ b/src/njs_array.c @@ -1522,7 +1522,7 @@ njs_array_prototype_join(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, static int -njs_array_indices_handler(const void *first, const void *second) +njs_array_indices_handler(const void *first, const void *second, void *ctx) { double num1, num2; int64_t diff; @@ -1572,8 +1572,8 @@ njs_array_keys(njs_vm_t *vm, njs_value_t *object, njs_bool_t all) return NULL; } - qsort(keys->start, keys->length, sizeof(njs_value_t), - njs_array_indices_handler); + njs_qsort(keys->start, keys->length, sizeof(njs_value_t), + njs_array_indices_handler, NULL); return keys; } diff --git a/src/njs_main.h b/src/njs_main.h index d6af2be7..f03d7f3f 100644 --- a/src/njs_main.h +++ b/src/njs_main.h @@ -32,6 +32,7 @@ #include #include #include +#include #include #include diff --git a/src/njs_utils.c b/src/njs_utils.c new file mode 100644 index 00000000..8628c40c --- /dev/null +++ b/src/njs_utils.c @@ -0,0 +1,330 @@ + +/* + * Copyright (C) Dmitry Volyntsev + * Copyright (C) NGINX, Inc. + */ + + +#include + + +typedef void (*njs_swap_t) (void *a, void *b, size_t size); + + +njs_inline void +njs_swap_u8(void *a, void *b, size_t size) +{ + uint8_t u, *au, *bu; + + au = (uint8_t *) a; + bu = (uint8_t *) b; + + u = au[0]; + au[0] = bu[0]; + bu[0] = u; +} + + +njs_inline void +njs_swap_u16(void *a, void *b, size_t size) +{ + uint16_t u, *au, *bu; + + au = (uint16_t *) a; + bu = (uint16_t *) b; + + u = au[0]; + au[0] = bu[0]; + bu[0] = u; +} + + +njs_inline void +njs_swap_u32(void *a, void *b, size_t size) +{ + uint32_t u, *au, *bu; + + au = (uint32_t *) a; + bu = (uint32_t *) b; + + u = au[0]; + au[0] = bu[0]; + bu[0] = u; +} + + +njs_inline void +njs_swap_u64(void *a, void *b, size_t size) +{ + uint64_t u, *au, *bu; + + au = (uint64_t *) a; + bu = (uint64_t *) b; + + u = au[0]; + au[0] = bu[0]; + bu[0] = u; +} + + +njs_inline void +njs_swap_u128(void *a, void *b, size_t size) +{ + uint64_t u, v, *au, *bu; + + au = (uint64_t *) a; + bu = (uint64_t *) b; + + u = au[0]; + v = au[1]; + au[0] = bu[0]; + au[1] = bu[1]; + bu[0] = u; + bu[1] = v; +} + + +njs_inline void +njs_swap_u128x(void *a, void *b, size_t size) +{ + uint64_t u, v, *au, *bu; + + au = (uint64_t *) a; + bu = (uint64_t *) b; + + do { + u = au[0]; + v = au[1]; + au[0] = bu[0]; + au[1] = bu[1]; + bu[0] = u; + bu[1] = v; + + size -= sizeof(uint64_t) * 2; + + au += 2; + bu += 2; + } while (size != 0); +} + + +njs_inline void +njs_swap_bytes(void *a, void *b, size_t size) +{ + uint8_t u, *au, *bu; + + au = (uint8_t *) a; + bu = (uint8_t *) b; + + while (size-- != 0) { + u = *au; + *au++ = *bu; + *bu++ = u; + } +} + + +njs_inline njs_swap_t +njs_choose_swap(size_t size) +{ + switch (size) { + case 2: + return njs_swap_u16; + case 4: + return njs_swap_u32; + case 8: + return njs_swap_u64; + case 16: + return njs_swap_u128; + default: + if ((size % 16) == 0) { + return njs_swap_u128x; + } + + if (size == 1) { + return njs_swap_u8; + } + } + + return njs_swap_bytes; +} + + +njs_inline void +njs_sift_down(u_char *base, njs_sort_cmp_t cmp, njs_swap_t swap, size_t n, + size_t esize, void *ctx, njs_uint_t i) +{ + njs_uint_t c, m; + + m = i; + + while (1) { + c = 2 * i + esize; + + if (c < n && cmp(base + m, base + c, ctx) < 0) { + m = c; + } + + c += esize; + + if (c < n && cmp(base + m, base + c, ctx) < 0) { + m = c; + } + + if (m == i) { + break; + } + + swap(base + i, base + m, esize); + i = m; + } +} + + +static void +njs_heapsort(u_char *base, size_t n, size_t esize, njs_swap_t swap, + njs_sort_cmp_t cmp, void *ctx) +{ + njs_uint_t i; + + i = (n / 2) * esize; + n = n * esize; + + for ( ;; ) { + njs_sift_down(base, cmp, swap, n, esize, ctx, i); + + if (i == 0) { + break; + } + + i -= esize; + } + + while (n > esize) { + swap(base, base + n - esize, esize); + n -= esize; + + njs_sift_down(base, cmp, swap, n, esize, ctx, 0); + } +} + + +njs_inline void * +njs_pivot(void *a, void *b, void *c, njs_sort_cmp_t cmp, void *ctx) +{ + if (cmp(a, c, ctx) < 0) { + if (cmp(b, c, ctx) < 0) { + return (cmp(a, b, ctx) < 0) ? b : a; + } + + return c; + } + + if (cmp(b, a, ctx) < 0) { + return (cmp(b, c, ctx) < 0) ? c : b; + } + + return a; +} + + +typedef struct { + u_char *base; + njs_uint_t elems; +} njs_qsort_state_t; + + +#define NJS_MAX_DEPTH 16 + + +void +njs_qsort(void *arr, size_t n, size_t esize, njs_sort_cmp_t cmp, void *ctx) +{ + int r; + u_char *base, *lt, *gt, *p, *end; + njs_uint_t m4; + njs_swap_t swap; + njs_qsort_state_t stack[NJS_MAX_DEPTH], *sp; + + if (n < 2) { + return; + } + + swap = njs_choose_swap(esize); + + sp = stack; + sp->base = arr; + sp->elems = n; + sp++; + + while (sp-- > stack) { + base = sp->base; + n = sp->elems; + end = base + n * esize; + + while (n > 6) { + if (njs_slow_path(sp == &stack[NJS_MAX_DEPTH - 1])) { + njs_heapsort(base, n, esize, swap, cmp, ctx); + end = base; + break; + } + + m4 = (n / 4) * esize; + p = njs_pivot(base + m4, base + 2 * m4, base + 3 * m4, cmp, ctx); + swap(base, p, esize); + + /** + * Partition + * < mid | == mid | unprocessed | mid > + * lt p gt + */ + + lt = base; + gt = end; + p = lt + esize; + + while (p < gt) { + r = cmp(p, lt, ctx); + + if (r <= 0) { + if (r < 0) { + swap(lt, p, esize); + lt += esize; + } + + p += esize; + continue; + } + + swap(gt - esize, p, esize); + gt -= esize; + } + + if (lt - base > end - gt) { + sp->base = base; + sp->elems = (lt - base) / esize; + + base = gt; + n = (end - gt) / esize; + + } else { + sp->base = gt; + sp->elems = (end - gt) / esize; + + n = (lt - base) / esize; + } + + end = base + n * esize; + sp++; + } + + /* Insertion sort. */ + + for (p = base + esize; p < end; p += esize) { + while (p > base && cmp(p, p - esize, ctx) < 0) { + swap(p, p - esize, esize); + p -= esize; + } + } + } +} diff --git a/src/njs_utils.h b/src/njs_utils.h new file mode 100644 index 00000000..1cb291aa --- /dev/null +++ b/src/njs_utils.h @@ -0,0 +1,16 @@ + +/* + * Copyright (C) Dmitry Volyntsev + * Copyright (C) NGINX, Inc. + */ + +#ifndef _NJS_UTILS_H_INCLUDED_ +#define _NJS_UTILS_H_INCLUDED_ + + +typedef int (*njs_sort_cmp_t)(const void *, const void *, void *ctx); + +void njs_qsort(void *base, size_t n, size_t size, njs_sort_cmp_t cmp, + void *ctx); + +#endif /* _NJS_UTILS_H_INCLUDED_ */ diff --git a/src/test/njs_unit_test.c b/src/test/njs_unit_test.c index d468f0bc..06682275 100644 --- a/src/test/njs_unit_test.c +++ b/src/test/njs_unit_test.c @@ -17917,6 +17917,134 @@ done: } +typedef struct { + size_t size; + uint32_t array[32]; + size_t esize; + uint32_t expected[32]; +} njs_sort_test_t; + + +static int +njs_sort_cmp(const void *a, const void *b, void *ctx) +{ + njs_sort_test_t *c; + + c = ctx; + + switch (c->esize) { + case 1: + return *((uint8_t *) a) - *((uint8_t *) b); + case 2: + return *((uint16_t *) a) - *((uint16_t *) b); + case 4: + default: + return *((uint32_t *) a) - *((uint32_t *) b); + } +} + + +static njs_int_t +njs_sort_test(njs_vm_t *vm, njs_opts_t *opts, njs_stat_t *stat) +{ + u_char *p; + njs_uint_t i, j, k; + njs_sort_test_t *t; + u_char array[sizeof(t->array)]; + uint32_t sorted[sizeof(t->array) / sizeof(t->array[0])]; + + static const njs_sort_test_t tests[] = { + { 1, { 5 }, 1, { 5 } }, + { 3, { 3, 2, 1 }, 1, { 1, 2, 3 } }, + { 4, { 4, 3, 2, 1 }, 1, { 1, 2, 3, 4 } }, + { 5, { 5, 4, 3, 2, 1 }, 1, { 1, 2, 3, 4, 5 } }, + { 5, { 1, 0, 9, 1, 8 }, 1, { 0, 1, 1, 8, 9 } }, + { 8, { 0, 0, 0, 0, 0, 0, 0, 0 }, 1, { 0, 0, 0, 0, 0, 0, 0, 0 } }, + { 8, { 4, 5, 1, 4, 2, 5, 5, 6 }, 1, { 1, 2, 4, 4, 5, 5, 5, 6 } }, + { 4, { 512, 100, 65535, 0 }, 2, { 0, 100, 512, 65535 } }, + { 3, { 65536, 3, 262141 }, 4, { 3, 65536, 262141 } }, + }; + + for (i = 0; i < njs_nitems(tests); i++) { + t = (njs_sort_test_t *) &tests[i]; + + p = array; + for (k = 0; k < t->size; k++) { + switch (t->esize) { + case 1: + *p = (uint8_t) t->array[k]; + break; + case 2: + *(uint16_t *) p = (uint16_t) t->array[k]; + break; + case 4: + default: + *(uint32_t *) p = (uint32_t) t->array[k]; + break; + } + + p += t->esize; + } + + njs_qsort(array, t->size, t->esize, njs_sort_cmp, t); + + p = array; + for (k = 0; k < t->size; k++) { + switch (t->esize) { + case 1: + sorted[k] = *p; + break; + case 2: + sorted[k] = *(uint16_t *) p; + break; + case 4: + default: + sorted[k] = *(uint32_t *) p; + break; + } + + p += t->esize; + } + + + for (k = 0; k < t->size; k++) { + if (sorted[k] != t->expected[k]) { + goto failed; + } + } + + stat->passed++; + continue; + +failed: + + njs_printf("njs_sort_test(["); + for (j = 0; j < t->size; j++) { + njs_printf("%uD%s", t->array[j], + (j < t->size - 1) ? "," : ""); + } + + njs_printf("]):\nexpected: ["); + for (j = 0; j < t->size; j++) { + njs_printf("%uD%s", t->expected[j], + (j < t->size - 1) ? "," : ""); + } + + njs_printf("]\n got: ["); + for (j = 0; j < t->size; j++) { + njs_printf("%uD%s", sorted[j], + (j < t->size - 1) ? "," : ""); + } + + njs_printf("]\n"); + + stat->failed++; + } + + return NJS_OK; +} + + static njs_int_t njs_string_to_index_test(njs_vm_t *vm, njs_opts_t *opts, njs_stat_t *stat) { @@ -18020,6 +18148,8 @@ njs_api_test(njs_opts_t *opts, njs_stat_t *stat) njs_str("njs_file_dirname_test") }, { njs_chb_test, njs_str("njs_chb_test") }, + { njs_sort_test, + njs_str("njs_sort_test") }, { njs_string_to_index_test, njs_str("njs_string_to_index_test") }, }; diff --git a/src/test/rbtree_unit_test.c b/src/test/rbtree_unit_test.c index 4b527631..5b954ae6 100644 --- a/src/test/rbtree_unit_test.c +++ b/src/test/rbtree_unit_test.c @@ -18,7 +18,7 @@ static intptr_t rbtree_unit_test_comparison(njs_rbtree_node_t *node1, njs_rbtree_node_t *node2); static njs_int_t rbtree_unit_test_compare(uint32_t key1, uint32_t key2); static int njs_cdecl rbtree_unit_test_sort_cmp(const void *one, - const void *two); + const void *two, void *ctx); static njs_int_t @@ -56,7 +56,7 @@ rbtree_unit_test(njs_uint_t n) items[i].key = key; } - qsort(keys, n, sizeof(uint32_t), rbtree_unit_test_sort_cmp); + njs_qsort(keys, n, sizeof(uint32_t), rbtree_unit_test_sort_cmp, NULL); for (i = 0; i < n; i++) { njs_rbtree_insert(&tree, &items[i].node); @@ -161,7 +161,7 @@ rbtree_unit_test_compare(uint32_t key1, uint32_t key2) static int njs_cdecl -rbtree_unit_test_sort_cmp(const void *one, const void *two) +rbtree_unit_test_sort_cmp(const void *one, const void *two, void *ctx) { const uint32_t *first, *second;