aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Pulford <mark@kyne.com.au>2012-01-01 15:24:30 +1030
committerMark Pulford <mark@kyne.com.au>2012-01-01 15:24:30 +1030
commit929c814b12e3575859fa0d5a8ea9950ae2187c56 (patch)
treec953eadc2fe8415ca85bf15175b6c3114fbc0622
parent4ce40cdccf28551f4b091cb8f9a735c2cc9f5513 (diff)
downloadlua-cjson-929c814b12e3575859fa0d5a8ea9950ae2187c56.tar.gz
lua-cjson-929c814b12e3575859fa0d5a8ea9950ae2187c56.tar.bz2
lua-cjson-929c814b12e3575859fa0d5a8ea9950ae2187c56.zip
Sanitise locale code, comments and documentation
-rw-r--r--fpconv.c70
-rw-r--r--fpconv.h7
-rw-r--r--lua_cjson.c7
-rw-r--r--manual.txt28
4 files changed, 71 insertions, 41 deletions
diff --git a/fpconv.c b/fpconv.c
index fd4deb0..3bcef41 100644
--- a/fpconv.c
+++ b/fpconv.c
@@ -1,3 +1,9 @@
1/* JSON uses a '.' decimal separator. strtod() / sprintf() under C libraries
2 * with locale support will break when the decimal separator is a comma.
3 *
4 * fpconv_* will around these issues with a translation buffer if required.
5 */
6
1#include <stdio.h> 7#include <stdio.h>
2#include <stdlib.h> 8#include <stdlib.h>
3#include <assert.h> 9#include <assert.h>
@@ -5,15 +11,21 @@
5 11
6#include "fpconv.h" 12#include "fpconv.h"
7 13
14/* Lua CJSON assumes the locale is the same for all threads within a
15 * process and doesn't change after initialisation.
16 *
17 * This avoids the need for per thread storage or expensive checks
18 * for call. */
8static char locale_decimal_point = '.'; 19static char locale_decimal_point = '.';
9 20
10/* In theory multibyte decimal_points are possible, but 21/* In theory multibyte decimal_points are possible, but
11 * Lua CJSON only supports UTF-8 and known locales only have 22 * Lua CJSON only supports UTF-8 and known locales only have
12 * single byte decimal points ([.,]). 23 * single byte decimal points ([.,]).
13 * 24 *
14 * localconv() may not be thread safe, and nl_langinfo() is not 25 * localconv() may not be thread safe (=>crash), and nl_langinfo() is
15 * supported on some platforms. Use sprintf() instead. */ 26 * not supported on some platforms. Use sprintf() instead - if the
16void fpconv_update_locale() 27 * locale does change, at least Lua CJSON won't crash. */
28static void fpconv_update_locale()
17{ 29{
18 char buf[8]; 30 char buf[8];
19 31
@@ -29,11 +41,14 @@ void fpconv_update_locale()
29 locale_decimal_point = buf[1]; 41 locale_decimal_point = buf[1];
30} 42}
31 43
32/* Check for a valid number character: [-+0-9a-fA-FpPxX.] 44/* Check for a valid number character: [-+0-9a-yA-Y.]
33 * It doesn't matter if actual invalid characters are counted - strtod() 45 * Eg: -0.6e+5, infinity, 0xF0.F0pF0
34 * will find the valid number if it exists. The risk is that slightly more 46 *
35 * memory might be allocated before a parse error occurs. */ 47 * Used to find the probable end of a number. It doesn't matter if
36static int valid_number_character(char ch) 48 * invalid characters are counted - strtod() will find the valid
49 * number if it exists. The risk is that slightly more memory might
50 * be allocated before a parse error occurs. */
51static inline int valid_number_character(char ch)
37{ 52{
38 char lower_ch; 53 char lower_ch;
39 54
@@ -42,9 +57,7 @@ static int valid_number_character(char ch)
42 if (ch == '-' || ch == '+' || ch == '.') 57 if (ch == '-' || ch == '+' || ch == '.')
43 return 1; 58 return 1;
44 59
45 /* Hex digits, exponent (e), base (p), "infinity",.. 60 /* Hex digits, exponent (e), base (p), "infinity",.. */
46 * The main purpose is to not include a "comma". If any other invalid
47 * characters are included, the will only generate a parse error later. */
48 lower_ch = ch | 0x20; 61 lower_ch = ch | 0x20;
49 if ('a' <= lower_ch && lower_ch <= 'y') 62 if ('a' <= lower_ch && lower_ch <= 'y')
50 return 1; 63 return 1;
@@ -52,8 +65,8 @@ static int valid_number_character(char ch)
52 return 0; 65 return 0;
53} 66}
54 67
55/* Calculate the size of the buffer required for a locale 68/* Calculate the size of the buffer required for a strtod locale
56 * conversion. Returns 0 if conversion is not required */ 69 * conversion. */
57static int strtod_buffer_size(const char *s) 70static int strtod_buffer_size(const char *s)
58{ 71{
59 const char *p = s; 72 const char *p = s;
@@ -68,38 +81,38 @@ static int strtod_buffer_size(const char *s)
68 * character. Guaranteed to be called at the start of any valid number in a string */ 81 * character. Guaranteed to be called at the start of any valid number in a string */
69double fpconv_strtod(const char *nptr, char **endptr) 82double fpconv_strtod(const char *nptr, char **endptr)
70{ 83{
71 char *num, *endnum, *dp; 84 char *buf, *endbuf, *dp;
72 int numlen; 85 int buflen;
73 double value; 86 double value;
74 87
75 /* System strtod() is fine when decimal point is '.' */ 88 /* System strtod() is fine when decimal point is '.' */
76 if (locale_decimal_point == '.') 89 if (locale_decimal_point == '.')
77 return strtod(nptr, endptr); 90 return strtod(nptr, endptr);
78 91
79 numlen = strtod_buffer_size(nptr); 92 buflen = strtod_buffer_size(nptr);
80 if (!numlen) { 93 if (!buflen) {
81 /* No valid characters found, standard strtod() return */ 94 /* No valid characters found, standard strtod() return */
82 *endptr = (char *)nptr; 95 *endptr = (char *)nptr;
83 return 0; 96 return 0;
84 } 97 }
85 98
86 /* Duplicate number into buffer */ 99 /* Duplicate number into buffer */
87 num = malloc(numlen + 1); 100 buf = malloc(buflen + 1);
88 if (!num) { 101 if (!buf) {
89 fprintf(stderr, "Out of memory"); 102 fprintf(stderr, "Out of memory");
90 abort(); 103 abort();
91 } 104 }
92 memcpy(num, nptr, numlen); 105 memcpy(buf, nptr, buflen);
93 num[numlen] = 0; 106 buf[buflen] = 0;
94 107
95 /* Update decimal point character if found */ 108 /* Update decimal point character if found */
96 dp = strchr(num, '.'); 109 dp = strchr(buf, '.');
97 if (dp) 110 if (dp)
98 *dp = locale_decimal_point; 111 *dp = locale_decimal_point;
99 112
100 value = strtod(num, &endnum); 113 value = strtod(buf, &endbuf);
101 *endptr = (char *)&nptr[endnum - num]; 114 *endptr = (char *)&nptr[endbuf - buf];
102 free(num); 115 free(buf);
103 116
104 return value; 117 return value;
105} 118}
@@ -142,7 +155,7 @@ int fpconv_g_fmt(char *str, double num, int precision)
142 /* snprintf() to a buffer then translate for other decimal point characters */ 155 /* snprintf() to a buffer then translate for other decimal point characters */
143 len = snprintf(buf, FPCONV_G_FMT_BUFSIZE, fmt, num); 156 len = snprintf(buf, FPCONV_G_FMT_BUFSIZE, fmt, num);
144 157
145 /* Returned 'len' includes the null terminator */ 158 /* Copy into target location. Translate decimal point if required */
146 b = buf; 159 b = buf;
147 do { 160 do {
148 *str++ = (*b == locale_decimal_point ? '.' : *b); 161 *str++ = (*b == locale_decimal_point ? '.' : *b);
@@ -151,5 +164,10 @@ int fpconv_g_fmt(char *str, double num, int precision)
151 return len; 164 return len;
152} 165}
153 166
167void fpconv_init()
168{
169 fpconv_update_locale();
170}
171
154/* vi:ai et sw=4 ts=4: 172/* vi:ai et sw=4 ts=4:
155 */ 173 */
diff --git a/fpconv.h b/fpconv.h
index b8a6469..ea875c0 100644
--- a/fpconv.h
+++ b/fpconv.h
@@ -1,9 +1,12 @@
1/* Lua CJSON floating point conversion routines */ 1/* Lua CJSON floating point conversion routines */
2 2
3/* Buffer larger than required to store the largest %.14g number */ 3/* Buffer required to store the largest string representation of a double.
4 *
5 * Longest double printed with %.14g is 21 characters long:
6 * -1.7976931348623e+308 */
4# define FPCONV_G_FMT_BUFSIZE 32 7# define FPCONV_G_FMT_BUFSIZE 32
5 8
6extern void fpconv_update_locale(); 9extern void fpconv_init();
7extern int fpconv_g_fmt(char*, double, int); 10extern int fpconv_g_fmt(char*, double, int);
8extern double fpconv_strtod(const char*, char**); 11extern double fpconv_strtod(const char*, char**);
9 12
diff --git a/lua_cjson.c b/lua_cjson.c
index fe6d41a..175433a 100644
--- a/lua_cjson.c
+++ b/lua_cjson.c
@@ -571,8 +571,6 @@ static void json_append_number(lua_State *l, json_config_t *cfg,
571 /* Some platforms may print -nan, just hard code it */ 571 /* Some platforms may print -nan, just hard code it */
572 strbuf_append_mem(json, "nan", 3); 572 strbuf_append_mem(json, "nan", 3);
573 } else { 573 } else {
574 /* Longest double printed with %.14g is 21 characters long:
575 * -1.7976931348623e+308 */
576 strbuf_ensure_empty_length(json, FPCONV_G_FMT_BUFSIZE); 574 strbuf_ensure_empty_length(json, FPCONV_G_FMT_BUFSIZE);
577 len = fpconv_g_fmt(strbuf_empty_ptr(json), num, cfg->encode_number_precision); 575 len = fpconv_g_fmt(strbuf_empty_ptr(json), num, cfg->encode_number_precision);
578 strbuf_extend_length(json, len); 576 strbuf_extend_length(json, len);
@@ -1283,9 +1281,8 @@ static int lua_cjson_new(lua_State *l)
1283 { NULL, NULL } 1281 { NULL, NULL }
1284 }; 1282 };
1285 1283
1286 /* Update the current locale for g_fmt/strtod. 1284 /* Initialise number conversions */
1287 * Using different locales per-thread is not supported. */ 1285 fpconv_init();
1288 fpconv_update_locale();
1289 1286
1290 /* cjson module table */ 1287 /* cjson module table */
1291 lua_newtable(l); 1288 lua_newtable(l);
diff --git a/manual.txt b/manual.txt
index 810d3d1..3ccbce3 100644
--- a/manual.txt
+++ b/manual.txt
@@ -48,7 +48,7 @@ Build Options (#define)
48[horizontal] 48[horizontal]
49USE_INTERNAL_ISINF:: Workaround for Solaris platforms missing isinf(). 49USE_INTERNAL_ISINF:: Workaround for Solaris platforms missing isinf().
50DISABLE_CJSON_GLOBAL:: Do not store module table in global "cjson" 50DISABLE_CJSON_GLOBAL:: Do not store module table in global "cjson"
51 variable. 51 variable. Redundant from Lua 5.2 onwards.
52 52
53 53
54Make 54Make
@@ -150,10 +150,10 @@ local cjson = require "cjson"
150local cjson2 = cjson.new() 150local cjson2 = cjson.new()
151------------ 151------------
152 152
153Lua CJSON can be loaded via +require+. A global +cjson+ table is 153Lua CJSON can be loaded via the Lua +require+ function. A global
154registered under Lua 5.1 to maintain backward compatibility. Lua CJSON 154+cjson+ module table is registered under Lua 5.1 to maintain backward
155does not register a global table under Lua 5.2 since this practice is 155compatibility. Lua CJSON does not register a global table under Lua
156discouraged. 1565.2 since this practice is discouraged.
157 157
158+cjson.new+ can be used to instantiate an independent copy of the Lua 158+cjson.new+ can be used to instantiate an independent copy of the Lua
159CJSON module. The new module has a separate persistent encoding 159CJSON module. The new module has a separate persistent encoding
@@ -164,9 +164,21 @@ threads within a single Lua state provided the persistent encoding
164buffer is not shared. This can be achieved by one of the following 164buffer is not shared. This can be achieved by one of the following
165methods: 165methods:
166 166
167- Disabling the persistent encoding buffer with +encode_keep_buffer+. 167- Disabling the persistent encoding buffer with
168- Ensuring only a single thread calls +encode+ at a time. 168 +cjson.encode_keep_buffer+.
169- Using a separate +cjson+ instantiation per pre-emptive thread. 169- Ensuring each thread calls +cjson.encode+ at a time.
170- Using a separate +cjson+ module table per pre-emptive thread
171 (+cjson.new+).
172
173[NOTE]
174Lua CJSON uses ++strtod++(3) and ++snprintf++(3) to perform numeric
175conversion as they are usually well supported, fast and bug free.
176However, these functions require a workaround for JSON
177encoding/parsing under locales using a comma decimal separator. Lua
178CJSON detects the current locale during instantiation to determine
179whether a workaround is required. CJSON should be reinitialised via
180+cjson.new+ if the locale of the current process changes. Different
181locales per thread are not supported.
170 182
171 183
172decode 184decode