1 
2 /*
3  * Copyright (C) Igor Sysoev
4  * Copyright (C) NGINX, Inc.
5  */
6 
7 #ifndef _NJS_NUMBER_H_INCLUDED_
8 #define _NJS_NUMBER_H_INCLUDED_
9 
10 
11 #define NJS_MAX_LENGTH      (0x1fffffffffffffLL)
12 #define NJS_INT64_DBL_MIN   (-9.223372036854776e+18) /* closest to INT64_MIN */
13 #define NJS_INT64_DBL_MAX   (9.223372036854776e+18) /* closest to INT64_MAX */
14 
15 
16 double njs_key_to_index(const njs_value_t *value);
17 double njs_number_dec_parse(const u_char **start, const u_char *end,
18     njs_bool_t literal);
19 uint64_t njs_number_oct_parse(const u_char **start, const u_char *end);
20 uint64_t njs_number_bin_parse(const u_char **start, const u_char *end);
21 uint64_t njs_number_hex_parse(const u_char **start, const u_char *end,
22     njs_bool_t literal);
23 int64_t njs_number_radix_parse(const u_char **start, const u_char *end,
24     uint8_t radix);
25 njs_int_t njs_number_to_string(njs_vm_t *vm, njs_value_t *string,
26     const njs_value_t *number);
27 njs_int_t njs_number_to_chain(njs_vm_t *vm, njs_chb_t *chain,
28     double number);
29 njs_int_t njs_number_global_is_nan(njs_vm_t *vm, njs_value_t *args,
30     njs_uint_t nargs, njs_index_t unused);
31 njs_int_t njs_number_global_is_finite(njs_vm_t *vm, njs_value_t *args,
32     njs_uint_t nargs, njs_index_t unused);
33 njs_int_t njs_number_parse_int(njs_vm_t *vm, njs_value_t *args,
34     njs_uint_t nargs, njs_index_t unused);
35 njs_int_t njs_number_parse_float(njs_vm_t *vm, njs_value_t *args,
36     njs_uint_t nargs, njs_index_t unused);
37 
38 
39 njs_inline njs_bool_t
njs_number_is_integer_index(double num)40 njs_number_is_integer_index(double num)
41 {
42     uint32_t  u32;
43 
44     u32 = num;
45 
46     return (u32 == num && u32 != 0xffffffff);
47 }
48 
49 
50 njs_inline njs_bool_t
njs_key_is_integer_index(double num,const njs_value_t * value)51 njs_key_is_integer_index(double num, const njs_value_t *value)
52 {
53     return (njs_number_is_integer_index(num))
54             && !(njs_is_string(value) && num == 0 && signbit(num));
55 }
56 
57 
58 njs_inline int64_t
njs_number_to_integer(double num)59 njs_number_to_integer(double num)
60 {
61     if (njs_fast_path(!isnan(num))) {
62         if (num < NJS_INT64_DBL_MIN) {
63             return INT64_MIN;
64 
65         } else if (num > NJS_INT64_DBL_MAX) {
66             return INT64_MAX;
67         }
68 
69         return num;
70     }
71 
72     return 0;
73 }
74 
75 
76 njs_inline int32_t
njs_number_to_int32(double num)77 njs_number_to_int32(double num)
78 {
79     uint32_t          r;
80     uint64_t          v;
81     njs_int_t         exp;
82     njs_diyfp_conv_t  conv;
83 
84     conv.d = num;
85 
86     exp = (conv.u64 & NJS_DBL_EXPONENT_MASK) >> NJS_DBL_SIGNIFICAND_SIZE;
87 
88     if (njs_fast_path(exp < (NJS_DBL_EXPONENT_OFFSET + 31))) {
89         /* |num| < 2**31. */
90         return num;
91     }
92 
93     if (exp < (NJS_DBL_EXPONENT_OFFSET + 31 + 53)) {
94         v = (conv.u64 & NJS_DBL_SIGNIFICAND_MASK) | NJS_DBL_HIDDEN_BIT;
95         v <<= (exp - NJS_DBL_EXPONENT_BIAS + 32);
96         r = v >> 32;
97 
98         if (conv.u64 & NJS_DBL_SIGN_MASK) {
99             r = -r;
100         }
101 
102         return r;
103     }
104 
105     /*
106      * ES5.1: integer must be modulo 2^32.
107      * The distance between larger doubles
108      * (exp >= NJS_DBL_EXPONENT_OFFSET + 31 + 53) is a multiple of 2**32 => 0.
109      * This also handles NaN and Inf.
110      */
111 
112     return 0;
113 }
114 
115 
116 njs_inline uint32_t
njs_number_to_uint32(double num)117 njs_number_to_uint32(double num)
118 {
119     return (uint32_t) njs_number_to_int32(num);
120 }
121 
122 
123 njs_inline uint16_t
njs_number_to_uint16(double num)124 njs_number_to_uint16(double num)
125 {
126     return (uint16_t) njs_number_to_int32(num);
127 }
128 
129 
130 njs_inline uint64_t
njs_number_to_length(double num)131 njs_number_to_length(double num)
132 {
133     if (isnan(num)) {
134         return 0;
135     }
136 
137     if (num > NJS_MAX_LENGTH) {
138         return NJS_MAX_LENGTH;
139 
140     } else if (num < 0.0) {
141         return 0;
142     }
143 
144     return (uint64_t) num;
145 }
146 
147 
148 njs_inline njs_int_t
njs_char_to_hex(u_char c)149 njs_char_to_hex(u_char c)
150 {
151     c |= 0x20;
152 
153     /* Values less than '0' become >= 208. */
154     c = c - '0';
155 
156     if (c > 9) {
157         /* Values less than 'a' become >= 159. */
158         c = c - ('a' - '0');
159 
160         if (njs_slow_path(c > 5)) {
161             return -1;
162         }
163 
164         c += 10;
165     }
166 
167     return c;
168 }
169 
170 
171 njs_inline void
njs_uint32_to_string(njs_value_t * value,uint32_t u32)172 njs_uint32_to_string(njs_value_t *value, uint32_t u32)
173 {
174     u_char  *dst, *p;
175 
176     dst = njs_string_short_start(value);
177     p = njs_sprintf(dst, dst + NJS_STRING_SHORT, "%uD", u32);
178 
179     njs_string_short_set(value, p - dst, p - dst);
180 }
181 
182 
183 extern const njs_object_type_init_t  njs_number_type_init;
184 
185 
186 #endif /* _NJS_NUMBER_H_INCLUDED_ */
187