From ca42b9a996f9046ba3876ad8a81cda1d935b39cf Mon Sep 17 00:00:00 2001 From: Mark Pulford Date: Thu, 29 Dec 2011 23:03:58 +1030 Subject: Use internal dtoa/strtod for double conversion The internal Lua CJSON dtoa/strtod routines have locale support disabled. This avoids problems under locales with comma decimal separators. Build changes: - CMake: Check for big endian architectures - Makefile: Provide option to build with dtoa.c Modifications to dtoa.c: - Include locale dtoa_config.h configuration - Rename Infinity/NaN to inf/nan to match common C libraries - Rename strtod() -> internal_strtod() to prevent conflict with libc function Modifications to g_fmt.c: - Return output string length (instead of original buffer pointer) - Provide precision as an argument to g_fmt() - Silence compilations warnings from vendor source - while(a = b) - Unused label "done:" - Only swap to scientific notation when once the number of decimal digits required exceeds the precision available. This matches standard printf format %g. - Display a "0" in front of numbers < 1. --- CMakeLists.txt | 6 ++++++ Makefile | 21 ++++++++++++++++++- dtoa.c | 10 +++++---- dtoa_config.h | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ fpconv.h | 10 ++++++++- g_fmt.c | 26 ++++++++++++++--------- 6 files changed, 123 insertions(+), 16 deletions(-) create mode 100644 dtoa_config.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 8d8a420..ffc718b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,6 +11,12 @@ set(CMAKE_BUILD_TYPE Release) find_package(Lua51 REQUIRED) include_directories(${LUA_INCLUDE_DIR}) +include(TestBigEndian) +TEST_BIG_ENDIAN(BIG_ENDIAN) +if(HAVE_BIG_ENDIAN) + add_definitions(-DIEEE_BIG_ENDIAN) +endif() + # Handle platforms missing isinf() macro (Eg, some Solaris systems). include(CheckSymbolExists) CHECK_SYMBOL_EXISTS(isinf math.h HAVE_ISINF) diff --git a/Makefile b/Makefile index 57f2e1b..b283634 100644 --- a/Makefile +++ b/Makefile @@ -4,6 +4,10 @@ ## DISABLE_CJSON_GLOBAL: Do not store module is "cjson" global. ## DISABLE_INVALID_NUMBERS: Permanently disable invalid JSON numbers: ## NaN, Infinity, hex. +## +## Optional built-in number conversion uses the following defines: +## USE_INTERNAL_DTOA: Use builtin strtod/dtoa for numeric conversions. +## IEEE_BIG_ENDIAN: Required on big endian architectures. ##### Build defaults ##### LUA_VERSION = 5.1 @@ -42,10 +46,25 @@ INSTALL_CMD = install #CJSON_CFLAGS = -DDISABLE_INVALID_NUMBERS #CJSON_LDFLAGS = -shared -L$(PREFIX)/lib -llua51 +##### Use built in number conversion (optional) ##### + +## Enable built in number conversion +#FPCONV_OBJS = g_fmt.o dtoa.o +#CJSON_CFLAGS += -DUSE_INTERNAL_DTOA + +## Compile built in number conversion for big endian architectures +#CJSON_CFLAGS += -DIEEE_BIG_ENDIAN + +## Compile built in number conversion to support multi-threaded +## applications (recommended) +#CJSON_CFLAGS += -pthread -DMULTIPLE_THREADS +#CJSON_LDFLAGS += -pthread + ##### End customisable sections ##### BUILD_CFLAGS = -I$(LUA_INCLUDE_DIR) $(CJSON_CFLAGS) -OBJS := lua_cjson.o strbuf.o fpconv.o +FPCONV_OBJS ?= fpconv.o +OBJS := lua_cjson.o strbuf.o $(FPCONV_OBJS) .PHONY: all clean install package doc diff --git a/dtoa.c b/dtoa.c index 4a458a4..520926c 100644 --- a/dtoa.c +++ b/dtoa.c @@ -185,6 +185,8 @@ * used for input more than STRTOD_DIGLIM digits long (default 40). */ +#include "dtoa_config.h" + #ifndef Long #define Long long #endif @@ -523,7 +525,7 @@ BCinfo { int dp0, dp1, dplen, dsign, e0, inexact, nd, nd0, rounding, scale, uflc #define Kmax 7 #ifdef __cplusplus -extern "C" double strtod(const char *s00, char **se); +extern "C" double fpconv_strtod(const char *s00, char **se); extern "C" char *dtoa(double d, int mode, int ndigits, int *decpt, int *sign, char **rve); #endif @@ -2471,7 +2473,7 @@ retlow1: #endif /* NO_STRTOD_BIGCOMP */ double -strtod +fpconv_strtod #ifdef KR_headers (s00, se) CONST char *s00; char **se; #else @@ -3746,9 +3748,9 @@ dtoa *decpt = 9999; #ifdef IEEE_Arith if (!word1(&u) && !(word0(&u) & 0xfffff)) - return nrv_alloc("Infinity", rve, 8); + return nrv_alloc("inf", rve, 8); #endif - return nrv_alloc("NaN", rve, 3); + return nrv_alloc("nan", rve, 3); } #endif #ifdef IBM diff --git a/dtoa_config.h b/dtoa_config.h new file mode 100644 index 0000000..294351d --- /dev/null +++ b/dtoa_config.h @@ -0,0 +1,66 @@ +#ifndef _DTOA_CONFIG_H +#define _DTOA_CONFIG_H + +#include +#include +#include + +/* Ensure dtoa.c does not USE_LOCALE. Lua CJSON must not use locale + * aware conversion routines. */ +#undef USE_LOCALE + +/* dtoa.c should not touch errno, Lua CJSON does not use it, and it + * may not be threadsafe */ +#define NO_ERRNO + +#define Long int32_t +#define ULong uint32_t +#define Llong int64_t +#define ULLong uint64_t + +#ifdef IEEE_BIG_ENDIAN +#define IEEE_MC68k +#else +#define IEEE_8087 +#endif + +#define MALLOC(n) xmalloc(n) + +static void *xmalloc(size_t size) +{ + void *p; + + p = malloc(size); + if (!p) { + fprintf(stderr, "Out of memory"); + abort(); + } + + return p; +} + +#ifdef MULTIPLE_THREADS + +/* Enable locking to support multi-threaded applications */ + +#include + +static pthread_mutex_t private_dtoa_lock[2] = { + PTHREAD_MUTEX_INITIALIZER, + PTHREAD_MUTEX_INITIALIZER +}; + +#define ACQUIRE_DTOA_LOCK(n) do { \ + pthread_mutex_lock(&private_dtoa_lock[n]); \ +} while (0) + +#define FREE_DTOA_LOCK(n) do { \ + pthread_mutex_unlock(&private_dtoa_lock[n]); \ +} while (0) + +#endif /* MULTIPLE_THREADS */ + +#endif /* _DTOA_CONFIG_H */ + +/* vi:ai et sw=4 ts=4: + */ diff --git a/fpconv.h b/fpconv.h index ea875c0..ac2f2c4 100644 --- a/fpconv.h +++ b/fpconv.h @@ -6,7 +6,15 @@ * -1.7976931348623e+308 */ # define FPCONV_G_FMT_BUFSIZE 32 -extern void fpconv_init(); +#ifdef USE_INTERNAL_DTOA +static inline void fpconv_init() +{ + /* Do nothing - not required */ +} +#else +extern inline void fpconv_init(); +#endif + extern int fpconv_g_fmt(char*, double, int); extern double fpconv_strtod(const char*, char**); diff --git a/g_fmt.c b/g_fmt.c index 543430a..130dcd4 100644 --- a/g_fmt.c +++ b/g_fmt.c @@ -26,14 +26,14 @@ extern "C" { #endif extern char *dtoa(double, int, int, int *, int *, char **); - extern char *g_fmt(char *, double); + extern int g_fmt(char *, double, int); extern void freedtoa(char*); #ifdef __cplusplus } #endif - char * -g_fmt(register char *b, double x) +int +fpconv_g_fmt(char *b, double x, int precision) { register int i, k; register char *s; @@ -48,18 +48,21 @@ g_fmt(register char *b, double x) goto done; } #endif - s = s0 = dtoa(x, 0, 0, &decpt, &sign, &se); + s = s0 = dtoa(x, 2, precision, &decpt, &sign, &se); if (sign) *b++ = '-'; if (decpt == 9999) /* Infinity or Nan */ { - while(*b++ = *s++); + while((*b++ = *s++)); + /* "b" is used to calculate the return length. Decrement to exclude the + * Null terminator from the length */ + b--; goto done0; } - if (decpt <= -4 || decpt > se - s + 5) { + if (decpt <= -4 || decpt > precision) { *b++ = *s++; if (*s) { *b++ = '.'; - while(*b = *s++) + while((*b = *s++)) b++; } *b++ = 'e'; @@ -82,13 +85,14 @@ g_fmt(register char *b, double x) *b = 0; } else if (decpt <= 0) { + *b++ = '0'; *b++ = '.'; for(; decpt < 0; decpt++) *b++ = '0'; - while(*b++ = *s++); + while((*b++ = *s++)); } else { - while(*b = *s++) { + while((*b = *s++)) { b++; if (--decpt == 0 && *s) *b++ = '.'; @@ -99,6 +103,8 @@ g_fmt(register char *b, double x) } done0: freedtoa(s0); +#ifdef IGNORE_ZERO_SIGN done: - return b0; +#endif + return b - b0; } -- cgit v1.2.3-55-g6feb