From: Igor Sysoev Date: Wed, 23 Mar 2016 12:49:46 +0000 (+0300) Subject: Math.random() method. X-Git-Tag: 0.1.0~45 X-Git-Url: http://git.kaiwu.me/postgresql/log/contrib/postgres_fdw/static/gitweb.js?a=commitdiff_plain;h=9ad031416cedbb537703b6416fe9c066d02cd98e;p=njs.git Math.random() method. --- diff --git a/Makefile b/Makefile index 168795b3..cf02682d 100644 --- a/Makefile +++ b/Makefile @@ -34,6 +34,7 @@ $(NXT_BUILDDIR)/libnjs.a: \ $(NXT_BUILDDIR)/nxt_array.o \ $(NXT_BUILDDIR)/nxt_rbtree.o \ $(NXT_BUILDDIR)/nxt_lvlhsh.o \ + $(NXT_BUILDDIR)/nxt_random.o \ $(NXT_BUILDDIR)/nxt_malloc.o \ $(NXT_BUILDDIR)/nxt_mem_cache_pool.o \ @@ -63,6 +64,7 @@ $(NXT_BUILDDIR)/libnjs.a: \ $(NXT_BUILDDIR)/nxt_array.o \ $(NXT_BUILDDIR)/nxt_rbtree.o \ $(NXT_BUILDDIR)/nxt_lvlhsh.o \ + $(NXT_BUILDDIR)/nxt_random.o \ $(NXT_BUILDDIR)/nxt_malloc.o \ $(NXT_BUILDDIR)/nxt_mem_cache_pool.o \ @@ -370,12 +372,14 @@ $(NXT_BUILDDIR)/njs_disassembler.o: \ njs/njs_disassembler.c $(NXT_BUILDDIR)/njs_unit_test: \ + $(NXT_BUILDDIR)/libnxt.a \ $(NXT_BUILDDIR)/libnjs.a \ njs/test/njs_unit_test.c \ $(NXT_CC) -o $(NXT_BUILDDIR)/njs_unit_test $(NXT_CFLAGS) \ -I$(NXT_LIB) -Injs \ njs/test/njs_unit_test.c \ - $(NXT_BUILDDIR)/libnjs.a -lm $(NXT_PCRE_LIB) + $(NXT_BUILDDIR)/libnjs.a \ + -lm $(NXT_PCRE_LIB) include $(NXT_LIB)/Makefile diff --git a/njs/njs_array.c b/njs/njs_array.c index 35d4b01b..8fdf974f 100644 --- a/njs/njs_array.c +++ b/njs/njs_array.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include diff --git a/njs/njs_boolean.c b/njs/njs_boolean.c index 13ca020d..8c89708d 100644 --- a/njs/njs_boolean.c +++ b/njs/njs_boolean.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include diff --git a/njs/njs_builtin.c b/njs/njs_builtin.c index 965d4559..90a43ab5 100644 --- a/njs/njs_builtin.c +++ b/njs/njs_builtin.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include diff --git a/njs/njs_disassembler.c b/njs/njs_disassembler.c index a75608d3..748ec8bf 100644 --- a/njs/njs_disassembler.c +++ b/njs/njs_disassembler.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include diff --git a/njs/njs_extern.c b/njs/njs_extern.c index 11e2358d..fd48abdc 100644 --- a/njs/njs_extern.c +++ b/njs/njs_extern.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include diff --git a/njs/njs_function.c b/njs/njs_function.c index 058da84f..d35a8d02 100644 --- a/njs/njs_function.c +++ b/njs/njs_function.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include diff --git a/njs/njs_generator.c b/njs/njs_generator.c index 2cbfa542..42f28c94 100644 --- a/njs/njs_generator.c +++ b/njs/njs_generator.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include diff --git a/njs/njs_lexer.c b/njs/njs_lexer.c index 3cf5ed4b..d3d5cb66 100644 --- a/njs/njs_lexer.c +++ b/njs/njs_lexer.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include diff --git a/njs/njs_lexer_keyword.c b/njs/njs_lexer_keyword.c index ff9c5c88..dabf10a1 100644 --- a/njs/njs_lexer_keyword.c +++ b/njs/njs_lexer_keyword.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include diff --git a/njs/njs_math.c b/njs/njs_math.c index 7beada50..1473e3d0 100644 --- a/njs/njs_math.c +++ b/njs/njs_math.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -297,6 +298,20 @@ njs_object_math_pow(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, } +static njs_ret_t +njs_object_math_random(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, + njs_index_t unused) +{ + double num; + + num = nxt_random(&vm->random) / 4294967296.0; + + njs_number_set(&vm->retval, num); + + return NXT_OK; +} + + static njs_ret_t njs_object_math_round(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, njs_index_t unused) @@ -518,6 +533,12 @@ static const njs_object_prop_t njs_math_object_properties[] = NJS_SKIP_ARG, NJS_NUMBER_ARG, NJS_NUMBER_ARG), }, + { + .type = NJS_METHOD, + .name = njs_string("random"), + .value = njs_native_function(njs_object_math_random, 0, ), + }, + { .type = NJS_METHOD, .name = njs_string("round"), diff --git a/njs/njs_nonrecursive_parser.c b/njs/njs_nonrecursive_parser.c index 9591a05b..b92c0ba4 100644 --- a/njs/njs_nonrecursive_parser.c +++ b/njs/njs_nonrecursive_parser.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include diff --git a/njs/njs_number.c b/njs/njs_number.c index b7775a77..f5c66422 100644 --- a/njs/njs_number.c +++ b/njs/njs_number.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -163,7 +164,7 @@ njs_number_to_string(njs_vm_t *vm, njs_value_t *string, const njs_value_t *number) { u_char *p; - double num; + double n, num; size_t size; const char *fmt; const njs_value_t *value; @@ -184,10 +185,18 @@ njs_number_to_string(njs_vm_t *vm, njs_value_t *string, } } else { - if (fabs(num) < 1000000) { + n = fabs(num); + + if (n == 0) { + fmt = "%g"; + + } else if (n < 1) { + fmt = "%f"; + + } else if (n < 1000000) { fmt = "%g"; - } else if (fabs(num) < 1e20) { + } else if (n < 1e20) { fmt = "%1.f"; } else { diff --git a/njs/njs_object.c b/njs/njs_object.c index 71f312f7..a4f1d649 100644 --- a/njs/njs_object.c +++ b/njs/njs_object.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include diff --git a/njs/njs_parser.c b/njs/njs_parser.c index 636c0ef3..c858f96a 100644 --- a/njs/njs_parser.c +++ b/njs/njs_parser.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include diff --git a/njs/njs_parser_expression.c b/njs/njs_parser_expression.c index 1a1f7ef1..11bc6d8b 100644 --- a/njs/njs_parser_expression.c +++ b/njs/njs_parser_expression.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include diff --git a/njs/njs_regexp.c b/njs/njs_regexp.c index 608a2f45..71815587 100644 --- a/njs/njs_regexp.c +++ b/njs/njs_regexp.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include diff --git a/njs/njs_string.c b/njs/njs_string.c index 42ab6444..6873907c 100644 --- a/njs/njs_string.c +++ b/njs/njs_string.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include diff --git a/njs/njs_variable.c b/njs/njs_variable.c index 5deb7135..00e34e32 100644 --- a/njs/njs_variable.c +++ b/njs/njs_variable.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include diff --git a/njs/njs_vm.c b/njs/njs_vm.c index 9d1df6df..71d1898a 100644 --- a/njs/njs_vm.c +++ b/njs/njs_vm.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include diff --git a/njs/njs_vm.h b/njs/njs_vm.h index 19a822aa..acb5a396 100644 --- a/njs/njs_vm.h +++ b/njs/njs_vm.h @@ -774,6 +774,8 @@ struct njs_vm_s { njs_regexp_pattern_t *pattern; nxt_array_t *code; /* of njs_vm_code_t */ + + nxt_random_t random; }; diff --git a/njs/njscript.c b/njs/njscript.c index 9d0823d5..ef41b2e7 100644 --- a/njs/njscript.c +++ b/njs/njscript.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include diff --git a/njs/test/njs_unit_test.c b/njs/test/njs_unit_test.c index 2ac4b133..9e3313bd 100644 --- a/njs/test/njs_unit_test.c +++ b/njs/test/njs_unit_test.c @@ -3919,12 +3919,6 @@ static njs_unit_test_t njs_test[] = { nxt_string("Math.PI"), nxt_string("3.14159") }, - { nxt_string("Math.E"), - nxt_string("2.71828") }, - - { nxt_string("Math.SQRT2"), - nxt_string("1.41421") }, - { nxt_string("Math.abs(5)"), nxt_string("5") }, @@ -3970,7 +3964,8 @@ static njs_unit_test_t njs_test[] = { nxt_string("Math.pow()"), nxt_string("NaN") }, - /* ES5: Must be "[object Math]". */ + /* ES5FIX: "[object Math]". */ + { nxt_string("Math"), nxt_string("[object Object]") }, diff --git a/nxt/Makefile b/nxt/Makefile index 19ae2483..7f9fb31d 100644 --- a/nxt/Makefile +++ b/nxt/Makefile @@ -9,6 +9,7 @@ $(NXT_BUILDDIR)/libnxt.a: \ $(NXT_BUILDDIR)/nxt_queue.o \ $(NXT_BUILDDIR)/nxt_rbtree.o \ $(NXT_BUILDDIR)/nxt_lvlhsh.o \ + $(NXT_BUILDDIR)/nxt_random.o \ $(NXT_BUILDDIR)/nxt_malloc.o \ $(NXT_BUILDDIR)/nxt_mem_cache_pool.o \ @@ -19,6 +20,7 @@ $(NXT_BUILDDIR)/libnxt.a: \ $(NXT_BUILDDIR)/nxt_rbtree.o \ $(NXT_BUILDDIR)/nxt_lvlhsh.o \ $(NXT_BUILDDIR)/nxt_malloc.o \ + $(NXT_BUILDDIR)/nxt_random.o \ $(NXT_BUILDDIR)/nxt_mem_cache_pool.o \ $(NXT_BUILDDIR)/nxt_murmur_hash.o: \ @@ -102,6 +104,16 @@ $(NXT_BUILDDIR)/nxt_malloc.o: \ -I$(NXT_LIB) \ $(NXT_LIB)/nxt_malloc.c +$(NXT_BUILDDIR)/nxt_random.o: \ + $(NXT_LIB)/nxt_types.h \ + $(NXT_LIB)/nxt_clang.h \ + $(NXT_LIB)/nxt_random.h \ + $(NXT_LIB)/nxt_random.c \ + + $(NXT_CC) -c -o $(NXT_BUILDDIR)/nxt_random.o $(NXT_CFLAGS) \ + -I$(NXT_LIB) \ + $(NXT_LIB)/nxt_random.c + $(NXT_BUILDDIR)/nxt_mem_cache_pool.o: \ $(NXT_LIB)/nxt_types.h \ $(NXT_LIB)/nxt_clang.h \ diff --git a/nxt/auto/configure b/nxt/auto/configure index 305d0d53..fdb93d03 100755 --- a/nxt/auto/configure +++ b/nxt/auto/configure @@ -52,4 +52,5 @@ END . ${NXT_AUTO}os . ${NXT_AUTO}clang . ${NXT_AUTO}memalign +. ${NXT_AUTO}getrandom . ${NXT_AUTO}pcre diff --git a/nxt/auto/getrandom b/nxt/auto/getrandom new file mode 100644 index 00000000..9f94fbd7 --- /dev/null +++ b/nxt/auto/getrandom @@ -0,0 +1,24 @@ + +# Copyright (C) Igor Sysoev +# Copyright (C) NGINX, Inc. + + +# Linux 3.17 getrandom(). + +nxt_feature="getrandom()" +nxt_feature_name=NXT_HAVE_GETRANDOM +nxt_feature_run= +nxt_feature_incs= +nxt_feature_libs= +nxt_feature_test="#include + #include + #include + + int main() { + char buf[4]; + + (void) syscall(SYS_getrandom, buf, 4, 0); + + return 0; + }" +. ${NXT_AUTO}feature diff --git a/nxt/nxt_random.c b/nxt/nxt_random.c new file mode 100644 index 00000000..8f8bedf5 --- /dev/null +++ b/nxt/nxt_random.c @@ -0,0 +1,179 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) NGINX, Inc. + */ + + +#include +#include +#include +#include +#include +#include +#if (NXT_HAVE_GETRANDOM) +#include +#include +#endif + + +/* + * The pseudorandom generator based on OpenBSD arc4random. Although + * it is usually stated that arc4random uses RC4 pseudorandom generation + * algorithm they are actually different in nxt_random_add(). + */ + + +#define NXT_RANDOM_KEY_SIZE 128 + + +nxt_inline uint8_t nxt_random_byte(nxt_random_t *r); + + +void +nxt_random_init(nxt_random_t *r, nxt_pid_t pid) +{ + nxt_uint_t i; + + r->count = 0; + r->pid = pid; + r->i = 0; + r->j = 0; + + for (i = 0; i < 256; i++) { + r->s[i] = i; + } +} + + +void +nxt_random_stir(nxt_random_t *r, nxt_pid_t pid) +{ + int fd; + ssize_t n; + struct timeval tv; + union { + uint32_t value[3]; + u_char bytes[NXT_RANDOM_KEY_SIZE]; + } key; + + if (r->pid == 0) { + nxt_random_init(r, pid); + } + + r->pid = pid; + + n = 0; + +#if (NXT_HAVE_GETRANDOM) + + /* Linux 3.17 getrandom(), it is not available in Glibc. */ + + n = syscall(SYS_getrandom, key, NXT_RANDOM_KEY_SIZE, 0)); + +#endif + + if (n != NXT_RANDOM_KEY_SIZE) { + fd = open("/dev/urandom", O_RDONLY); + + if (fd >= 0) { + n = read(fd, &key, NXT_RANDOM_KEY_SIZE); + (void) close(fd); + } + } + + if (n != NXT_RANDOM_KEY_SIZE) { + (void) gettimeofday(&tv, NULL); + + /* XOR with stack garbage. */ + + key.value[0] ^= tv.tv_usec; + key.value[1] ^= tv.tv_sec; + key.value[2] ^= getpid(); + } + + nxt_random_add(r, key.bytes, NXT_RANDOM_KEY_SIZE); + + /* Drop the first 3072 bytes. */ + for (n = 3072; n != 0; n--) { + (void) nxt_random_byte(r); + } + + /* Stir again after 1,600,000 bytes. */ + r->count = 400000; +} + + +void +nxt_random_add(nxt_random_t *r, const u_char *key, uint32_t len) +{ + uint8_t val; + uint32_t n; + + for (n = 0; n < 256; n++) { + val = r->s[r->i]; + r->j += val + key[n % len]; + + r->s[r->i] = r->s[r->j]; + r->s[r->j] = val; + + r->i++; + } + + /* This index is not decremented in RC4 algorithm. */ + r->i--; + + r->j = r->i; +} + + +uint32_t +nxt_random(nxt_random_t *r) +{ + uint32_t val; + nxt_pid_t pid; + nxt_bool_t new_pid; + + new_pid = 0; + pid = r->pid; + + if (pid != -1) { + pid = getpid(); + + if (pid != r->pid) { + new_pid = 1; + } + } + + r->count--; + + if (r->count <= 0 || new_pid) { + nxt_random_stir(r, pid); + } + + val = nxt_random_byte(r) << 24; + val |= nxt_random_byte(r) << 16; + val |= nxt_random_byte(r) << 8; + val |= nxt_random_byte(r); + + return val; +} + + +nxt_inline uint8_t +nxt_random_byte(nxt_random_t *r) +{ + uint8_t si, sj; + + r->i++; + si = r->s[r->i]; + r->j += si; + + sj = r->s[r->j]; + r->s[r->i] = sj; + r->s[r->j] = si; + + si += sj; + + return r->s[si]; +} diff --git a/nxt/nxt_random.h b/nxt/nxt_random.h new file mode 100644 index 00000000..caa4ddba --- /dev/null +++ b/nxt/nxt_random.h @@ -0,0 +1,37 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) NGINX, Inc. + */ + +#ifndef _NXT_RANDOM_H_INCLUDED_ +#define _NXT_RANDOM_H_INCLUDED_ + + +typedef struct { + int32_t count; + nxt_pid_t pid; + uint8_t i; + uint8_t j; + uint8_t s[256]; +} nxt_random_t; + + +/* + * The nxt_random_t structure must be either initialized with zeros + * or initialized by nxt_random_init() function. The later is intended + * mainly for unit test. nxt_random() automatically stirs itself if + * process pid changed after fork(). This pid testing can be disabled by + * passing -1 as the pid argument to nxt_random_init() or nxt_random_stir() + * functions. The testing can be later enabled by passing any positive + * number, for example, a real pid number. + */ + +NXT_EXPORT void nxt_random_init(nxt_random_t *r, nxt_pid_t pid); +NXT_EXPORT void nxt_random_stir(nxt_random_t *r, nxt_pid_t pid); +NXT_EXPORT void nxt_random_add(nxt_random_t *r, const u_char *key, + uint32_t len); +NXT_EXPORT uint32_t nxt_random(nxt_random_t *r); + + +#endif /* _NXT_RANDOM_H_INCLUDED_ */ diff --git a/nxt/nxt_types.h b/nxt/nxt_types.h index acf13281..0b28cf4e 100644 --- a/nxt/nxt_types.h +++ b/nxt/nxt_types.h @@ -90,4 +90,7 @@ typedef time_t nxt_time_t; #endif +typedef pid_t nxt_pid_t; + + #endif /* _NXT_TYPES_H_INCLUDED_ */ diff --git a/nxt/test/Makefile b/nxt/test/Makefile index 2a085b14..08288b57 100644 --- a/nxt/test/Makefile +++ b/nxt/test/Makefile @@ -1,9 +1,11 @@ lib_test: \ + $(NXT_BUILDDIR)/random_unit_test \ $(NXT_BUILDDIR)/rbtree_unit_test \ $(NXT_BUILDDIR)/lvlhsh_unit_test \ $(NXT_BUILDDIR)/utf8_unit_test \ + $(NXT_BUILDDIR)/random_unit_test $(NXT_BUILDDIR)/rbtree_unit_test $(NXT_BUILDDIR)/lvlhsh_unit_test $(NXT_BUILDDIR)/utf8_unit_test @@ -43,3 +45,12 @@ $(NXT_BUILDDIR)/lvlhsh_unit_test: \ $(NXT_BUILDDIR)/nxt_murmur_hash.o \ $(NXT_BUILDDIR)/nxt_mem_cache_pool.o \ $(NXT_BUILDDIR)/nxt_malloc.o + +$(NXT_BUILDDIR)/random_unit_test: \ + $(NXT_BUILDDIR)/nxt_random.o \ + $(NXT_LIB)/test/random_unit_test.c \ + + $(NXT_CC) -o $(NXT_BUILDDIR)/random_unit_test $(NXT_CFLAGS) \ + -I$(NXT_LIB) \ + $(NXT_LIB)/test/random_unit_test.c \ + $(NXT_BUILDDIR)/nxt_random.o diff --git a/nxt/test/random_unit_test.c b/nxt/test/random_unit_test.c new file mode 100644 index 00000000..ef721d13 --- /dev/null +++ b/nxt/test/random_unit_test.c @@ -0,0 +1,60 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) NGINX, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include + + +static nxt_int_t +random_unit_test() +{ + nxt_uint_t n; + nxt_random_t r; + + nxt_random_init(&r, -1); + + r.count = 400000; + + nxt_random_add(&r, (u_char *) "arc4random", sizeof("arc4random") - 1); + + /* + * Test arc4random() numbers. + * RC4 pseudorandom numbers would be 0x4642AFC3 and 0xBAF0FFF0. + */ + + if (nxt_random(&r) == 0xD6270B27) { + + for (n = 100000; n != 0; n--) { + (void) nxt_random(&r); + } + + if (nxt_random(&r) == 0x6FCAE186) { + printf("random unit test passed\n"); + + nxt_random_stir(&r, getpid()); + + printf("random unit test: 0x%08X\n", nxt_random(&r)); + + return NXT_OK; + } + } + + printf("random unit test failed\n"); + + return NXT_ERROR; +} + + +int +main(void) +{ + return random_unit_test(); +}