From a287020096393dea6f88b4e2c90b3eeb243a3aad Mon Sep 17 00:00:00 2001 From: devsaurus Date: Wed, 18 Feb 2015 23:57:34 +0100 Subject: [PATCH 01/40] implement drawXBM --- app/modules/u8g.c | 21 ++++++++++++ lua_examples/u8g_bitmaps.lua | 62 +++++++++++++++++++++++++++++++++++ lua_examples/u8glib_logo.xbm | Bin 0 -> 120 bytes 3 files changed, 83 insertions(+) create mode 100644 lua_examples/u8g_bitmaps.lua create mode 100644 lua_examples/u8glib_logo.xbm diff --git a/app/modules/u8g.c b/app/modules/u8g.c index 9876616f..e91b4f5b 100644 --- a/app/modules/u8g.c +++ b/app/modules/u8g.c @@ -576,6 +576,26 @@ static int lu8g_drawVLine( lua_State *L ) return 0; } +// Lua: u8g.drawXBM( self, x, y, width, height, data ) +static int lu8g_drawXBM( lua_State *L ) +{ + lu8g_userdata_t *lud; + + if ((lud = get_lud( L )) == NULL) + return 0; + + u8g_uint_t args[4]; + lu8g_get_int_args( L, 2, 4, args ); + + const char *xbm_data = luaL_checkstring( L, (1+4) + 1 ); + if (xbm_data == NULL) + return 0; + + u8g_DrawXBM( lud, args[0], args[1], args[2], args[3], (const uint8_t *)xbm_data ); + + return 0; +} + // Lua: u8g.setScale2x2( self ) static int lu8g_setScale2x2( lua_State *L ) { @@ -931,6 +951,7 @@ static const LUA_REG_TYPE lu8g_display_map[] = { LSTRKEY( "drawPixel" ), LFUNCVAL( lu8g_drawPixel ) }, { LSTRKEY( "drawHLine" ), LFUNCVAL( lu8g_drawHLine ) }, { LSTRKEY( "drawVLine" ), LFUNCVAL( lu8g_drawVLine ) }, + { LSTRKEY( "drawXBM" ), LFUNCVAL( lu8g_drawXBM ) }, { LSTRKEY( "setScale2x2" ), LFUNCVAL( lu8g_setScale2x2 ) }, { LSTRKEY( "undoScale" ), LFUNCVAL( lu8g_undoScale ) }, { LSTRKEY( "firstPage" ), LFUNCVAL( lu8g_firstPage ) }, diff --git a/lua_examples/u8g_bitmaps.lua b/lua_examples/u8g_bitmaps.lua new file mode 100644 index 00000000..43f49d8c --- /dev/null +++ b/lua_examples/u8g_bitmaps.lua @@ -0,0 +1,62 @@ + +-- setup I2c and connect display +function init_i2c_display() + sda = 5 + scl = 6 + sla = 0x3c + i2c.setup(0, sda, scl, i2c.SLOW) + disp = u8g.ssd1306_128x64_i2c(sla) +end + +function xbm_picture() + disp:setFont(u8g.font_6x10) + disp:drawStr( 0, 10, "XBM picture") + + disp:drawXBM( 0, 20, 38, 24, xbm_data ) +end + +function bitmap_picture() + disp:setFont(u8g.font_6x10) + disp:drawStr( 0, 10, "Bitmap picture") + + --disp:drawXBM( 0, 20, 38, 24, bitmap_data ) +end + +-- the draw() routine +function draw(draw_state) + local component = bit.rshift(draw_state, 3) + + if (component == 0) then + xbm_picture(bit.band(draw_state, 7)) + elseif (component == 1) then + bitmap_picture(bit.band(draw_state, 7)) + end +end + + +function bitmap_test() + init_i2c_display() + + -- read XBM picture + file.open("u8glib_logo.xbm", "r") + xbm_data = file.read() + file.close() + + print("--- Starting Bitmap Test ---") + dir = 0 + next_rotation = 0 + + local draw_state + for draw_state = 1, 7 + 0*8, 1 do + disp:firstPage() + repeat + draw(draw_state) + until disp:nextPage() == false + + tmr.wdclr() + end + + print("--- Bitmap Test done ---") +end + +bitmap_test() diff --git a/lua_examples/u8glib_logo.xbm b/lua_examples/u8glib_logo.xbm new file mode 100644 index 0000000000000000000000000000000000000000..876c4b7ca11b4f3da9e39271fc9facbf40c5491d GIT binary patch literal 120 zcmezW|Nnn`ApURv-~o_${18Zd{s<=i?Kl4{C?jw1nUSMM;WHz{0n5*g#}8V5K6w13 z#pi>EC2byeGS09+F2TTI$8S?0V_^UPPrKp&|ND Date: Thu, 19 Feb 2015 12:12:14 +0100 Subject: [PATCH 02/40] implement drawBitmap --- README.md | 6 +++--- app/modules/u8g.c | 21 +++++++++++++++++++++ lua_examples/u8g_bitmaps.lua | 11 ++++++++--- lua_examples/u8g_rook.bm | Bin 0 -> 8 bytes 4 files changed, 32 insertions(+), 6 deletions(-) create mode 100644 lua_examples/u8g_rook.bm diff --git a/README.md b/README.md index f4cf9c85..42712dc1 100644 --- a/README.md +++ b/README.md @@ -368,9 +368,9 @@ They'll be available as `u8g.` in Lua. - [ ] setCursorFont() - [ ] setCursorPos() - [ ] setCursorStyle() -- [ ] Bitmaps - - [ ] drawBitmap() - - [ ] drawXBM() +- [x] Bitmaps + - [x] drawBitmap() + - [x] drawXBM() - [ ] General functions - [x] begin() - [ ] print() diff --git a/app/modules/u8g.c b/app/modules/u8g.c index e91b4f5b..316ccddd 100644 --- a/app/modules/u8g.c +++ b/app/modules/u8g.c @@ -596,6 +596,26 @@ static int lu8g_drawXBM( lua_State *L ) return 0; } +// Lua: u8g.drawBitmap( self, x, y, count, height, data ) +static int lu8g_drawBitmap( lua_State *L ) +{ + lu8g_userdata_t *lud; + + if ((lud = get_lud( L )) == NULL) + return 0; + + u8g_uint_t args[4]; + lu8g_get_int_args( L, 2, 4, args ); + + const char *bm_data = luaL_checkstring( L, (1+4) + 1 ); + if (bm_data == NULL) + return 0; + + u8g_DrawBitmap( lud, args[0], args[1], args[2], args[3], (const uint8_t *)bm_data ); + + return 0; +} + // Lua: u8g.setScale2x2( self ) static int lu8g_setScale2x2( lua_State *L ) { @@ -951,6 +971,7 @@ static const LUA_REG_TYPE lu8g_display_map[] = { LSTRKEY( "drawPixel" ), LFUNCVAL( lu8g_drawPixel ) }, { LSTRKEY( "drawHLine" ), LFUNCVAL( lu8g_drawHLine ) }, { LSTRKEY( "drawVLine" ), LFUNCVAL( lu8g_drawVLine ) }, + { LSTRKEY( "drawBitmap" ), LFUNCVAL( lu8g_drawBitmap ) }, { LSTRKEY( "drawXBM" ), LFUNCVAL( lu8g_drawXBM ) }, { LSTRKEY( "setScale2x2" ), LFUNCVAL( lu8g_setScale2x2 ) }, { LSTRKEY( "undoScale" ), LFUNCVAL( lu8g_undoScale ) }, diff --git a/lua_examples/u8g_bitmaps.lua b/lua_examples/u8g_bitmaps.lua index 43f49d8c..0a8ac8e7 100644 --- a/lua_examples/u8g_bitmaps.lua +++ b/lua_examples/u8g_bitmaps.lua @@ -15,11 +15,11 @@ function xbm_picture() disp:drawXBM( 0, 20, 38, 24, xbm_data ) end -function bitmap_picture() +function bitmap_picture(state) disp:setFont(u8g.font_6x10) disp:drawStr( 0, 10, "Bitmap picture") - --disp:drawXBM( 0, 20, 38, 24, bitmap_data ) + disp:drawBitmap( 0 + (state * 10), 20 + (state * 4), 1, 8, bm_data ) end -- the draw() routine @@ -42,12 +42,17 @@ function bitmap_test() xbm_data = file.read() file.close() + -- read Bitmap picture + file.open("u8g_rook.bm", "r") + bm_data = file.read() + file.close() + print("--- Starting Bitmap Test ---") dir = 0 next_rotation = 0 local draw_state - for draw_state = 1, 7 + 0*8, 1 do + for draw_state = 1, 7 + 1*8, 1 do disp:firstPage() repeat draw(draw_state) diff --git a/lua_examples/u8g_rook.bm b/lua_examples/u8g_rook.bm new file mode 100644 index 0000000000000000000000000000000000000000..9dd93966f264609e124d1438e97c63b29a56c805 GIT binary patch literal 8 NcmZPwt+xY$dH@MH0!#n^ literal 0 HcmV?d00001 From 81be529d68dad4cf576b4307e4e2ced74537e81d Mon Sep 17 00:00:00 2001 From: devsaurus Date: Thu, 19 Feb 2015 16:47:41 +0100 Subject: [PATCH 03/40] preparation for adding more display devices: allocate and construct page buffer dynamically to save heap for unused device types --- app/modules/u8g.c | 157 ++++++++++++++++++++++++++++++---------------- 1 file changed, 104 insertions(+), 53 deletions(-) diff --git a/app/modules/u8g.c b/app/modules/u8g.c index 316ccddd..caf487bc 100644 --- a/app/modules/u8g.c +++ b/app/modules/u8g.c @@ -8,11 +8,21 @@ #include "lrotable.h" //#include "c_string.h" -//#include "c_stdlib.h" +#include "c_stdlib.h" #include "u8g.h" -typedef u8g_t lu8g_userdata_t; +struct _lu8g_userdata_t +{ + u8g_t u8g; + u8g_pb_t pb; + u8g_dev_t dev; +}; + +typedef struct _lu8g_userdata_t lu8g_userdata_t; + +// shorthand macro for the u8g structure inside the userdata +#define LU8G (&(lud->u8g)) // Font look-up array @@ -79,7 +89,7 @@ static int lu8g_begin( lua_State *L ) if ((lud = get_lud( L )) == NULL) return 0; - u8g_Begin( lud ); + u8g_Begin( LU8G ); return 0; } @@ -94,7 +104,7 @@ static int lu8g_setFont( lua_State *L ) lua_Integer fontnr = luaL_checkinteger( L, 2 ); if ((fontnr >= 0) && (fontnr < (sizeof( font_array ) / sizeof( u8g_fntpgm_uint8_t )))) - u8g_SetFont( lud, font_array[fontnr] ); + u8g_SetFont( LU8G, font_array[fontnr] ); return 0; } @@ -107,7 +117,7 @@ static int lu8g_setFontRefHeightAll( lua_State *L ) if ((lud = get_lud( L )) == NULL) return 0; - u8g_SetFontRefHeightAll( lud ); + u8g_SetFontRefHeightAll( LU8G ); return 0; } @@ -120,7 +130,7 @@ static int lu8g_setFontRefHeightExtendedText( lua_State *L ) if ((lud = get_lud( L )) == NULL) return 0; - u8g_SetFontRefHeightExtendedText( lud ); + u8g_SetFontRefHeightExtendedText( LU8G ); return 0; } @@ -133,7 +143,7 @@ static int lu8g_setFontRefHeightText( lua_State *L ) if ((lud = get_lud( L )) == NULL) return 0; - u8g_SetFontRefHeightText( lud ); + u8g_SetFontRefHeightText( LU8G ); return 0; } @@ -146,7 +156,7 @@ static int lu8g_setDefaultBackgroundColor( lua_State *L ) if ((lud = get_lud( L )) == NULL) return 0; - u8g_SetDefaultBackgroundColor( lud ); + u8g_SetDefaultBackgroundColor( LU8G ); return 0; } @@ -159,7 +169,7 @@ static int lu8g_setDefaultForegroundColor( lua_State *L ) if ((lud = get_lud( L )) == NULL) return 0; - u8g_SetDefaultForegroundColor( lud ); + u8g_SetDefaultForegroundColor( LU8G ); return 0; } @@ -172,7 +182,7 @@ static int lu8g_setFontPosBaseline( lua_State *L ) if ((lud = get_lud( L )) == NULL) return 0; - u8g_SetFontPosBaseline( lud ); + u8g_SetFontPosBaseline( LU8G ); return 0; } @@ -185,7 +195,7 @@ static int lu8g_setFontPosBottom( lua_State *L ) if ((lud = get_lud( L )) == NULL) return 0; - u8g_SetFontPosBottom( lud ); + u8g_SetFontPosBottom( LU8G ); return 0; } @@ -198,7 +208,7 @@ static int lu8g_setFontPosCenter( lua_State *L ) if ((lud = get_lud( L )) == NULL) return 0; - u8g_SetFontPosCenter( lud ); + u8g_SetFontPosCenter( LU8G ); return 0; } @@ -211,7 +221,7 @@ static int lu8g_setFontPosTop( lua_State *L ) if ((lud = get_lud( L )) == NULL) return 0; - u8g_SetFontPosTop( lud ); + u8g_SetFontPosTop( LU8G ); return 0; } @@ -224,7 +234,7 @@ static int lu8g_getFontAscent( lua_State *L ) if ((lud = get_lud( L )) == NULL) return 0; - lua_pushinteger( L, u8g_GetFontAscent( lud ) ); + lua_pushinteger( L, u8g_GetFontAscent( LU8G ) ); return 1; } @@ -237,7 +247,7 @@ static int lu8g_getFontDescent( lua_State *L ) if ((lud = get_lud( L )) == NULL) return 0; - lua_pushinteger( L, u8g_GetFontDescent( lud ) ); + lua_pushinteger( L, u8g_GetFontDescent( LU8G ) ); return 1; } @@ -250,7 +260,7 @@ static int lu8g_getFontLineSpacing( lua_State *L ) if ((lud = get_lud( L )) == NULL) return 0; - lua_pushinteger( L, u8g_GetFontLineSpacing( lud ) ); + lua_pushinteger( L, u8g_GetFontLineSpacing( LU8G ) ); return 1; } @@ -263,7 +273,7 @@ static int lu8g_getMode( lua_State *L ) if ((lud = get_lud( L )) == NULL) return 0; - lua_pushinteger( L, u8g_GetMode( lud ) ); + lua_pushinteger( L, u8g_GetMode( LU8G ) ); return 1; } @@ -276,7 +286,7 @@ static int lu8g_setColorIndex( lua_State *L ) if ((lud = get_lud( L )) == NULL) return 0; - u8g_SetColorIndex( lud, luaL_checkinteger( L, 2 ) ); + u8g_SetColorIndex( LU8G, luaL_checkinteger( L, 2 ) ); return 0; } @@ -289,7 +299,7 @@ static int lu8g_getColorIndex( lua_State *L ) if ((lud = get_lud( L )) == NULL) return 0; - lua_pushinteger( L, u8g_GetColorIndex( lud ) ); + lua_pushinteger( L, u8g_GetColorIndex( LU8G ) ); return 1; } @@ -311,16 +321,16 @@ static int lu8g_generic_drawStr( lua_State *L, uint8_t rot ) switch (rot) { case 1: - lua_pushinteger( L, u8g_DrawStr90( lud, args[0], args[1], s ) ); + lua_pushinteger( L, u8g_DrawStr90( LU8G, args[0], args[1], s ) ); break; case 2: - lua_pushinteger( L, u8g_DrawStr180( lud, args[0], args[1], s ) ); + lua_pushinteger( L, u8g_DrawStr180( LU8G, args[0], args[1], s ) ); break; case 3: - lua_pushinteger( L, u8g_DrawStr270( lud, args[0], args[1], s ) ); + lua_pushinteger( L, u8g_DrawStr270( LU8G, args[0], args[1], s ) ); break; default: - lua_pushinteger( L, u8g_DrawStr( lud, args[0], args[1], s ) ); + lua_pushinteger( L, u8g_DrawStr( LU8G, args[0], args[1], s ) ); break; } @@ -371,7 +381,7 @@ static int lu8g_drawLine( lua_State *L ) u8g_uint_t args[4]; lu8g_get_int_args( L, 2, 4, args ); - u8g_DrawLine( lud, args[0], args[1], args[2], args[3] ); + u8g_DrawLine( LU8G, args[0], args[1], args[2], args[3] ); return 0; } @@ -387,7 +397,7 @@ static int lu8g_drawTriangle( lua_State *L ) u8g_uint_t args[6]; lu8g_get_int_args( L, 2, 6, args ); - u8g_DrawTriangle( lud, args[0], args[1], args[2], args[3], args[4], args[5] ); + u8g_DrawTriangle( LU8G, args[0], args[1], args[2], args[3], args[4], args[5] ); return 0; } @@ -403,7 +413,7 @@ static int lu8g_drawBox( lua_State *L ) u8g_uint_t args[4]; lu8g_get_int_args( L, 2, 4, args ); - u8g_DrawBox( lud, args[0], args[1], args[2], args[3] ); + u8g_DrawBox( LU8G, args[0], args[1], args[2], args[3] ); return 0; } @@ -419,7 +429,7 @@ static int lu8g_drawRBox( lua_State *L ) u8g_uint_t args[5]; lu8g_get_int_args( L, 2, 5, args ); - u8g_DrawRBox( lud, args[0], args[1], args[2], args[3], args[4] ); + u8g_DrawRBox( LU8G, args[0], args[1], args[2], args[3], args[4] ); return 0; } @@ -435,7 +445,7 @@ static int lu8g_drawFrame( lua_State *L ) u8g_uint_t args[4]; lu8g_get_int_args( L, 2, 4, args ); - u8g_DrawFrame( lud, args[0], args[1], args[2], args[3] ); + u8g_DrawFrame( LU8G, args[0], args[1], args[2], args[3] ); return 0; } @@ -451,7 +461,7 @@ static int lu8g_drawRFrame( lua_State *L ) u8g_uint_t args[5]; lu8g_get_int_args( L, 2, 5, args ); - u8g_DrawRFrame( lud, args[0], args[1], args[2], args[3], args[4] ); + u8g_DrawRFrame( LU8G, args[0], args[1], args[2], args[3], args[4] ); return 0; } @@ -469,7 +479,7 @@ static int lu8g_drawDisc( lua_State *L ) u8g_uint_t opt = luaL_optinteger( L, (1+3) + 1, U8G_DRAW_ALL ); - u8g_DrawDisc( lud, args[0], args[1], args[2], opt ); + u8g_DrawDisc( LU8G, args[0], args[1], args[2], opt ); return 0; } @@ -487,7 +497,7 @@ static int lu8g_drawCircle( lua_State *L ) u8g_uint_t opt = luaL_optinteger( L, (1+3) + 1, U8G_DRAW_ALL ); - u8g_DrawCircle( lud, args[0], args[1], args[2], opt ); + u8g_DrawCircle( LU8G, args[0], args[1], args[2], opt ); return 0; } @@ -505,7 +515,7 @@ static int lu8g_drawEllipse( lua_State *L ) u8g_uint_t opt = luaL_optinteger( L, (1+4) + 1, U8G_DRAW_ALL ); - u8g_DrawEllipse( lud, args[0], args[1], args[2], args[3], opt ); + u8g_DrawEllipse( LU8G, args[0], args[1], args[2], args[3], opt ); return 0; } @@ -523,7 +533,7 @@ static int lu8g_drawFilledEllipse( lua_State *L ) u8g_uint_t opt = luaL_optinteger( L, (1+4) + 1, U8G_DRAW_ALL ); - u8g_DrawFilledEllipse( lud, args[0], args[1], args[2], args[3], opt ); + u8g_DrawFilledEllipse( LU8G, args[0], args[1], args[2], args[3], opt ); return 0; } @@ -539,7 +549,7 @@ static int lu8g_drawPixel( lua_State *L ) u8g_uint_t args[2]; lu8g_get_int_args( L, 2, 2, args ); - u8g_DrawPixel( lud, args[0], args[1] ); + u8g_DrawPixel( LU8G, args[0], args[1] ); return 0; } @@ -555,7 +565,7 @@ static int lu8g_drawHLine( lua_State *L ) u8g_uint_t args[3]; lu8g_get_int_args( L, 2, 3, args ); - u8g_DrawHLine( lud, args[0], args[1], args[2] ); + u8g_DrawHLine( LU8G, args[0], args[1], args[2] ); return 0; } @@ -571,7 +581,7 @@ static int lu8g_drawVLine( lua_State *L ) u8g_uint_t args[3]; lu8g_get_int_args( L, 2, 3, args ); - u8g_DrawVLine( lud, args[0], args[1], args[2] ); + u8g_DrawVLine( LU8G, args[0], args[1], args[2] ); return 0; } @@ -591,7 +601,7 @@ static int lu8g_drawXBM( lua_State *L ) if (xbm_data == NULL) return 0; - u8g_DrawXBM( lud, args[0], args[1], args[2], args[3], (const uint8_t *)xbm_data ); + u8g_DrawXBM( LU8G, args[0], args[1], args[2], args[3], (const uint8_t *)xbm_data ); return 0; } @@ -611,7 +621,7 @@ static int lu8g_drawBitmap( lua_State *L ) if (bm_data == NULL) return 0; - u8g_DrawBitmap( lud, args[0], args[1], args[2], args[3], (const uint8_t *)bm_data ); + u8g_DrawBitmap( LU8G, args[0], args[1], args[2], args[3], (const uint8_t *)bm_data ); return 0; } @@ -624,7 +634,7 @@ static int lu8g_setScale2x2( lua_State *L ) if ((lud = get_lud( L )) == NULL) return 0; - u8g_SetScale2x2( lud ); + u8g_SetScale2x2( LU8G ); return 0; } @@ -637,7 +647,7 @@ static int lu8g_undoScale( lua_State *L ) if ((lud = get_lud( L )) == NULL) return 0; - u8g_UndoScale( lud ); + u8g_UndoScale( LU8G ); return 0; } @@ -650,7 +660,7 @@ static int lu8g_firstPage( lua_State *L ) if ((lud = get_lud( L )) == NULL) return 0; - u8g_FirstPage( lud ); + u8g_FirstPage( LU8G ); return 0; } @@ -663,7 +673,7 @@ static int lu8g_nextPage( lua_State *L ) if ((lud = get_lud( L )) == NULL) return 0; - lua_pushboolean( L, u8g_NextPage( lud ) ); + lua_pushboolean( L, u8g_NextPage( LU8G ) ); return 1; } @@ -676,7 +686,7 @@ static int lu8g_sleepOn( lua_State *L ) if ((lud = get_lud( L )) == NULL) return 0; - u8g_SleepOn( lud ); + u8g_SleepOn( LU8G ); return 0; } @@ -689,7 +699,7 @@ static int lu8g_sleepOff( lua_State *L ) if ((lud = get_lud( L )) == NULL) return 0; - u8g_SleepOff( lud ); + u8g_SleepOff( LU8G ); return 0; } @@ -702,7 +712,7 @@ static int lu8g_setRot90( lua_State *L ) if ((lud = get_lud( L )) == NULL) return 0; - u8g_SetRot90( lud ); + u8g_SetRot90( LU8G ); return 0; } @@ -715,7 +725,7 @@ static int lu8g_setRot180( lua_State *L ) if ((lud = get_lud( L )) == NULL) return 0; - u8g_SetRot180( lud ); + u8g_SetRot180( LU8G ); return 0; } @@ -728,7 +738,7 @@ static int lu8g_setRot270( lua_State *L ) if ((lud = get_lud( L )) == NULL) return 0; - u8g_SetRot270( lud ); + u8g_SetRot270( LU8G ); return 0; } @@ -741,7 +751,7 @@ static int lu8g_undoRotation( lua_State *L ) if ((lud = get_lud( L )) == NULL) return 0; - u8g_UndoRotation( lud ); + u8g_UndoRotation( LU8G ); return 0; } @@ -754,7 +764,7 @@ static int lu8g_getWidth( lua_State *L ) if ((lud = get_lud( L )) == NULL) return 0; - lua_pushinteger( L, u8g_GetWidth( lud ) ); + lua_pushinteger( L, u8g_GetWidth( LU8G ) ); return 1; } @@ -767,7 +777,7 @@ static int lu8g_getHeight( lua_State *L ) if ((lud = get_lud( L )) == NULL) return 0; - lua_pushinteger( L, u8g_GetHeight( lud ) ); + lua_pushinteger( L, u8g_GetHeight( LU8G ) ); return 1; } @@ -906,9 +916,26 @@ uint8_t u8g_com_esp8266_ssd_i2c_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, voi +// device destructor +static int lu8g_close_display( lua_State *L ) +{ + lu8g_userdata_t *lud; + + if ((lud = get_lud( L )) == NULL) + return 0; + + // free up allocated page buffer + if (lud->pb.buf != NULL) + c_free( lud->pb.buf ); + + return 0; +} + + // device constructors -// Lua: speed = u8g.ssd1306_128x64_i2c( i2c_addr ) +uint8_t u8g_dev_ssd1306_128x64_fn(u8g_t *u8g, u8g_dev_t *dev, uint8_t msg, void *arg); +// Lua: object = u8g.ssd1306_128x64_i2c( i2c_addr ) static int lu8g_ssd1306_128x64_i2c( lua_State *L ) { unsigned addr = luaL_checkinteger( L, 1 ); @@ -918,9 +945,32 @@ static int lu8g_ssd1306_128x64_i2c( lua_State *L ) lu8g_userdata_t *lud = (lu8g_userdata_t *) lua_newuserdata( L, sizeof( lu8g_userdata_t ) ); - lud->i2c_addr = (uint8_t)addr; + lud->u8g.i2c_addr = (uint8_t)addr; - u8g_InitI2C( lud, &u8g_dev_ssd1306_128x64_i2c, U8G_I2C_OPT_NONE); + // We don't use the pre-defined device structure for u8g_dev_ssd1306_128x64_i2c here + // Reason: linking the pre-defined structures allocates RAM for the device/comm structure + // *before* the display is constructed (especially the page buffers) + // this consumes heap even when the device is not used at all +#if 1 + // build device entry + lud->dev.dev_fn = u8g_dev_ssd1306_128x64_fn; + lud->dev.dev_mem = &(lud->pb); + lud->dev.com_fn = u8g_com_esp8266_ssd_i2c_fn; + // then allocate and populate page buffer + lud->pb.width = 128; // WIDTH in u8g_dev_ssd1306_128x64.c + if ((lud->pb.buf = (void *)c_zalloc(lud->pb.width)) == NULL) + return luaL_error( L, "out of memory" ); + + lud->pb.p.page_height = 8; // PAGE_HEIGHT in u8g_dev_ssd1306_128x64.c + lud->pb.p.total_height = 64; // HEIGHT in u8g_dev_ssd1306_128x64.c + lud->pb.p.page_y0 = 0; + lud->pb.p.page_y1 = 0; + lud->pb.p.page = 0; + + u8g_InitI2C( LU8G, &(lud->dev), U8G_I2C_OPT_NONE); +#else + u8g_InitI2C( LU8G, u8g_dev_ssd1306_128x64_i2c, U8G_I2C_OPT_NONE); +#endif // set its metatable @@ -985,6 +1035,7 @@ static const LUA_REG_TYPE lu8g_display_map[] = { LSTRKEY( "undoRotation" ), LFUNCVAL( lu8g_undoRotation ) }, { LSTRKEY( "getWidth" ), LFUNCVAL( lu8g_getWidth ) }, { LSTRKEY( "getHeight" ), LFUNCVAL( lu8g_getHeight ) }, + { LSTRKEY( "__gc" ), LFUNCVAL( lu8g_close_display ) }, #if LUA_OPTIMIZE_MEMORY > 0 { LSTRKEY( "__index" ), LROVAL ( lu8g_display_map ) }, #endif From 6c0adbf980a14cd3c6002c92b86f8a2107100bf4 Mon Sep 17 00:00:00 2001 From: devsaurus Date: Fri, 20 Feb 2015 11:07:52 +0100 Subject: [PATCH 04/40] SPI comm function, untested --- app/modules/u8g.c | 80 +++++++++++++++++++++++++++++++++++++++++++++-- app/u8glib/u8g.h | 5 +++ 2 files changed, 83 insertions(+), 2 deletions(-) diff --git a/app/modules/u8g.c b/app/modules/u8g.c index caf487bc..b1e3f0a8 100644 --- a/app/modules/u8g.c +++ b/app/modules/u8g.c @@ -826,6 +826,83 @@ static uint8_t u8g_com_esp8266_ssd_start_sequence(u8g_t *u8g) } +static void lu8g_digital_write( u8g_t *u8g, uint8_t pin_index, uint8_t value ) +{ + uint8_t pin; + + pin = u8g->pin_list[pin_index]; + if ( pin != U8G_PIN_NONE ) + platform_gpio_write( pin, value ); +} + +uint8_t u8g_com_esp8266_hw_spi_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr) +{ + switch(msg) + { + case U8G_COM_MSG_STOP: + break; + + case U8G_COM_MSG_INIT: + // we assume that the SPI interface was already initialized + // just care for the /CS and D/C pins + lu8g_digital_write( u8g, U8G_PI_CS, PLATFORM_GPIO_HIGH ); + platform_gpio_mode( u8g->pin_list[U8G_PI_CS], PLATFORM_GPIO_OUTPUT, PLATFORM_GPIO_FLOAT ); + platform_gpio_mode( u8g->pin_list[U8G_PI_A0], PLATFORM_GPIO_OUTPUT, PLATFORM_GPIO_FLOAT ); + break; + + case U8G_COM_MSG_ADDRESS: /* define cmd (arg_val = 0) or data mode (arg_val = 1) */ + lu8g_digital_write( u8g, U8G_PI_A0, arg_val == 0 ? PLATFORM_GPIO_LOW : PLATFORM_GPIO_HIGH ); + break; + + case U8G_COM_MSG_CHIP_SELECT: + if (arg_val == 0) + { + /* disable */ + lu8g_digital_write( u8g, U8G_PI_CS, PLATFORM_GPIO_HIGH ); + } + else + { + /* enable */ + //u8g_com_arduino_digital_write(u8g, U8G_PI_SCK, LOW); + lu8g_digital_write( u8g, U8G_PI_CS, PLATFORM_GPIO_LOW ); + } + break; + + case U8G_COM_MSG_RESET: + if ( u8g->pin_list[U8G_PI_RESET] != U8G_PIN_NONE ) + lu8g_digital_write( u8g, U8G_PI_RESET, arg_val == 0 ? PLATFORM_GPIO_LOW : PLATFORM_GPIO_HIGH ); + break; + + case U8G_COM_MSG_WRITE_BYTE: + platform_spi_send_recv( 1, arg_val ); + break; + + case U8G_COM_MSG_WRITE_SEQ: + { + register uint8_t *ptr = arg_ptr; + while( arg_val > 0 ) + { + platform_spi_send_recv( 1, *ptr++ ); + arg_val--; + } + } + break; + case U8G_COM_MSG_WRITE_SEQ_P: + { + register uint8_t *ptr = arg_ptr; + while( arg_val > 0 ) + { + platform_spi_send_recv( 1, u8g_pgm_read(ptr) ); + ptr++; + arg_val--; + } + } + break; + } + return 1; +} + + uint8_t u8g_com_esp8266_ssd_i2c_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr) { switch(msg) @@ -915,7 +992,6 @@ uint8_t u8g_com_esp8266_ssd_i2c_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, voi - // device destructor static int lu8g_close_display( lua_State *L ) { @@ -955,7 +1031,7 @@ static int lu8g_ssd1306_128x64_i2c( lua_State *L ) // build device entry lud->dev.dev_fn = u8g_dev_ssd1306_128x64_fn; lud->dev.dev_mem = &(lud->pb); - lud->dev.com_fn = u8g_com_esp8266_ssd_i2c_fn; + lud->dev.com_fn = U8G_COM_SSD_I2C; // then allocate and populate page buffer lud->pb.width = 128; // WIDTH in u8g_dev_ssd1306_128x64.c if ((lud->pb.buf = (void *)c_zalloc(lud->pb.width)) == NULL) diff --git a/app/u8glib/u8g.h b/app/u8glib/u8g.h index 3c590df3..7aa2845a 100644 --- a/app/u8glib/u8g.h +++ b/app/u8glib/u8g.h @@ -654,6 +654,7 @@ uint8_t u8g_com_arduino_port_d_wr_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, v uint8_t u8g_com_arduino_no_en_parallel_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr); /* u8g_com_arduino_no_en_parallel.c */ uint8_t u8g_com_arduino_ssd_i2c_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr); /* u8g_com_arduino_ssd_i2c.c */ uint8_t u8g_com_esp8266_ssd_i2c_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr); /* u8g.c */ +uint8_t u8g_com_esp8266_hw_spi_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr); /* u8g.c */ uint8_t u8g_com_arduino_uc_i2c_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr); uint8_t u8g_com_arduino_t6963_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr); /* u8g_com_arduino_t6963.c */ @@ -723,6 +724,10 @@ defined(__18CXX) || defined(__PIC32MX) #define U8G_COM_HW_SPI u8g_com_atmega_hw_spi_fn #define U8G_COM_ST7920_HW_SPI u8g_com_atmega_st7920_hw_spi_fn #endif +#if defined(__XTENSA__) +#define U8G_COM_HW_SPI u8g_com_esp8266_hw_spi_fn +#define U8G_COM_ST7920_HW_SPI u8g_com_null_fn +#endif #endif #ifndef U8G_COM_HW_SPI #define U8G_COM_HW_SPI u8g_com_null_fn From e3ed4fc0da6b0da74476575b4576750309326370 Mon Sep 17 00:00:00 2001 From: devsaurus Date: Fri, 20 Feb 2015 18:24:53 +0100 Subject: [PATCH 05/40] fix typo --- app/modules/u8g.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/modules/u8g.c b/app/modules/u8g.c index b1e3f0a8..44fb2232 100644 --- a/app/modules/u8g.c +++ b/app/modules/u8g.c @@ -1045,7 +1045,7 @@ static int lu8g_ssd1306_128x64_i2c( lua_State *L ) u8g_InitI2C( LU8G, &(lud->dev), U8G_I2C_OPT_NONE); #else - u8g_InitI2C( LU8G, u8g_dev_ssd1306_128x64_i2c, U8G_I2C_OPT_NONE); + u8g_InitI2C( LU8G, &u8g_dev_ssd1306_128x64_i2c, U8G_I2C_OPT_NONE); #endif From 2cfb86b5b53e68526c557309a31674312391b51c Mon Sep 17 00:00:00 2001 From: devsaurus Date: Fri, 20 Feb 2015 21:15:38 +0100 Subject: [PATCH 06/40] improve device and page buffer initialization --- app/modules/u8g.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/app/modules/u8g.c b/app/modules/u8g.c index 44fb2232..c5dfccd6 100644 --- a/app/modules/u8g.c +++ b/app/modules/u8g.c @@ -1029,20 +1029,20 @@ static int lu8g_ssd1306_128x64_i2c( lua_State *L ) // this consumes heap even when the device is not used at all #if 1 // build device entry - lud->dev.dev_fn = u8g_dev_ssd1306_128x64_fn; - lud->dev.dev_mem = &(lud->pb); - lud->dev.com_fn = U8G_COM_SSD_I2C; - // then allocate and populate page buffer - lud->pb.width = 128; // WIDTH in u8g_dev_ssd1306_128x64.c + lud->dev = (u8g_dev_t){ u8g_dev_ssd1306_128x64_fn, &(lud->pb), U8G_COM_SSD_I2C }; + + // populate and allocate page buffer + // constants taken from u8g_dev_ssd1306_128x64.c: + // PAGE_HEIGHT + // | Height + // | | WIDTH + // | | | + lud->pb = (u8g_pb_t){ { 8, 64, 0, 0, 0 }, 128, NULL }; + // if ((lud->pb.buf = (void *)c_zalloc(lud->pb.width)) == NULL) return luaL_error( L, "out of memory" ); - lud->pb.p.page_height = 8; // PAGE_HEIGHT in u8g_dev_ssd1306_128x64.c - lud->pb.p.total_height = 64; // HEIGHT in u8g_dev_ssd1306_128x64.c - lud->pb.p.page_y0 = 0; - lud->pb.p.page_y1 = 0; - lud->pb.p.page = 0; - + // and finally init device using specific interface init function u8g_InitI2C( LU8G, &(lud->dev), U8G_I2C_OPT_NONE); #else u8g_InitI2C( LU8G, &u8g_dev_ssd1306_128x64_i2c, U8G_I2C_OPT_NONE); From 7ae293d5664dfef4cfdaaa641beec38dda9bd547 Mon Sep 17 00:00:00 2001 From: devsaurus Date: Wed, 25 Feb 2015 23:17:08 +0100 Subject: [PATCH 07/40] set free'd pointer to NULL --- app/modules/u8g.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/app/modules/u8g.c b/app/modules/u8g.c index c5dfccd6..0fb26fd3 100644 --- a/app/modules/u8g.c +++ b/app/modules/u8g.c @@ -1002,7 +1002,10 @@ static int lu8g_close_display( lua_State *L ) // free up allocated page buffer if (lud->pb.buf != NULL) + { c_free( lud->pb.buf ); + lud->pb.buf = NULL; + } return 0; } @@ -1033,10 +1036,10 @@ static int lu8g_ssd1306_128x64_i2c( lua_State *L ) // populate and allocate page buffer // constants taken from u8g_dev_ssd1306_128x64.c: - // PAGE_HEIGHT - // | Height - // | | WIDTH - // | | | + // PAGE_HEIGHT + // | Height + // | | WIDTH + // | | | lud->pb = (u8g_pb_t){ { 8, 64, 0, 0, 0 }, 128, NULL }; // if ((lud->pb.buf = (void *)c_zalloc(lud->pb.width)) == NULL) From 32774d428cabdbd54ae47dea551b1eb5080a1c57 Mon Sep 17 00:00:00 2001 From: devsaurus Date: Sun, 1 Mar 2015 23:11:24 +0100 Subject: [PATCH 08/40] handle font data as light userdata --- app/lua/lrodefs.h | 1 + app/lua/lrotable.h | 3 +++ app/modules/auxmods.h | 4 ++++ app/modules/u8g.c | 24 ++++++++++++------------ 4 files changed, 20 insertions(+), 12 deletions(-) diff --git a/app/lua/lrodefs.h b/app/lua/lrodefs.h index fb6fabfb..384be3ee 100644 --- a/app/lua/lrodefs.h +++ b/app/lua/lrodefs.h @@ -21,6 +21,7 @@ #define LNUMKEY LRO_NUMKEY #define LNILKEY LRO_NILKEY #define LFUNCVAL LRO_FUNCVAL +#define LUDATA LRO_LUDATA #define LNUMVAL LRO_NUMVAL #define LROVAL LRO_ROVAL #define LNILVAL LRO_NILVAL diff --git a/app/lua/lrotable.h b/app/lua/lrotable.h index 27de84a0..e8963e3b 100644 --- a/app/lua/lrotable.h +++ b/app/lua/lrotable.h @@ -11,6 +11,7 @@ /* Macros one can use to define rotable entries */ #ifndef LUA_PACK_VALUE #define LRO_FUNCVAL(v) {{.p = v}, LUA_TLIGHTFUNCTION} +#define LRO_LUDATA(v) {{.p = v}, LUA_TLIGHTUSERDATA} #define LRO_NUMVAL(v) {{.n = v}, LUA_TNUMBER} #define LRO_ROVAL(v) {{.p = (void*)v}, LUA_TROTABLE} #define LRO_NILVAL {{.p = NULL}, LUA_TNIL} @@ -18,10 +19,12 @@ #define LRO_NUMVAL(v) {.value.n = v} #ifdef ELUA_ENDIAN_LITTLE #define LRO_FUNCVAL(v) {{(int)v, add_sig(LUA_TLIGHTFUNCTION)}} +#define LRO_LUDATA(v) {{(int)v, add_sig(LUA_TLIGHTUSERDATA)}} #define LRO_ROVAL(v) {{(int)v, add_sig(LUA_TROTABLE)}} #define LRO_NILVAL {{0, add_sig(LUA_TNIL)}} #else // #ifdef ELUA_ENDIAN_LITTLE #define LRO_FUNCVAL(v) {{add_sig(LUA_TLIGHTFUNCTION), (int)v}} +#define LRO_LUDATA(v) {{add_sig(LUA_TLIGHTUSERDATA), (int)v}} #define LRO_ROVAL(v) {{add_sig(LUA_TROTABLE), (int)v}} #define LRO_NILVAL {{add_sig(LUA_TNIL), 0}} #endif // #ifdef ELUA_ENDIAN_LITTLE diff --git a/app/modules/auxmods.h b/app/modules/auxmods.h index 1823d300..175a1f2b 100644 --- a/app/modules/auxmods.h +++ b/app/modules/auxmods.h @@ -95,5 +95,9 @@ LUALIB_API int ( luaopen_ow )( lua_State *L ); lua_pushnumber( L, val );\ lua_setfield( L, -2, name ) +#define MOD_REG_LUDATA( L, name, val )\ + lua_pushlightuserdata( L, val );\ + lua_setfield( L, -2, name ) + #endif diff --git a/app/modules/u8g.c b/app/modules/u8g.c index 0fb26fd3..3595bf18 100644 --- a/app/modules/u8g.c +++ b/app/modules/u8g.c @@ -26,13 +26,13 @@ typedef struct _lu8g_userdata_t lu8g_userdata_t; // Font look-up array -static const u8g_fntpgm_uint8_t *font_array[] = -{ -#undef U8G_FONT_TABLE_ENTRY -#define U8G_FONT_TABLE_ENTRY(font) u8g_ ## font , - U8G_FONT_TABLE - NULL -}; +//static const u8g_fntpgm_uint8_t *font_array[] = +//{ +//#undef U8G_FONT_TABLE_ENTRY +//#define U8G_FONT_TABLE_ENTRY(font) u8g_ ## font , +// U8G_FONT_TABLE +// NULL +//}; static uint32_t *u8g_pgm_cached_iadr = NULL; @@ -102,9 +102,9 @@ static int lu8g_setFont( lua_State *L ) if ((lud = get_lud( L )) == NULL) return 0; - lua_Integer fontnr = luaL_checkinteger( L, 2 ); - if ((fontnr >= 0) && (fontnr < (sizeof( font_array ) / sizeof( u8g_fntpgm_uint8_t )))) - u8g_SetFont( LU8G, font_array[fontnr] ); + u8g_fntpgm_uint8_t *font = (u8g_fntpgm_uint8_t *)lua_touserdata( L, 2 ); + if (font != NULL) + u8g_SetFont( LU8G, font ); return 0; } @@ -1128,7 +1128,7 @@ const LUA_REG_TYPE lu8g_map[] = // Register fonts #undef U8G_FONT_TABLE_ENTRY -#define U8G_FONT_TABLE_ENTRY(font) { LSTRKEY( #font ), LNUMVAL( __COUNTER__ ) }, +#define U8G_FONT_TABLE_ENTRY(font) { LSTRKEY( #font ), LUDATA( (void *)(u8g_ ## font) ) }, U8G_FONT_TABLE // Options for circle/ ellipse drwing @@ -1164,7 +1164,7 @@ LUALIB_API int luaopen_u8g( lua_State *L ) // Register fonts #undef U8G_FONT_TABLE_ENTRY -#define U8G_FONT_TABLE_ENTRY(font) MOD_REG_NUMBER( L, #font, __COUNTER__ ); +#define U8G_FONT_TABLE_ENTRY(font) MOD_REG_LUDATA( L, #font, (void *)(u8g_ ## font) ); U8G_FONT_TABLE // Options for circle/ ellipse drawing From 5f1d32405985f58f26f36b871c16551682d080f8 Mon Sep 17 00:00:00 2001 From: devsaurus Date: Sun, 1 Mar 2015 23:45:25 +0100 Subject: [PATCH 09/40] remove caching --- app/modules/u8g.c | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/app/modules/u8g.c b/app/modules/u8g.c index 3595bf18..e882ae4e 100644 --- a/app/modules/u8g.c +++ b/app/modules/u8g.c @@ -35,28 +35,15 @@ typedef struct _lu8g_userdata_t lu8g_userdata_t; //}; -static uint32_t *u8g_pgm_cached_iadr = NULL; -static uint32_t u8g_pgm_cached_data; - // function to read 4-byte aligned from program memory AKA irom0 u8g_pgm_uint8_t ICACHE_FLASH_ATTR u8g_pgm_read(const u8g_pgm_uint8_t *adr) { uint32_t iadr = (uint32_t)adr; // set up pointer to 4-byte aligned memory location uint32_t *ptr = (uint32_t *)(iadr & ~0x3); - uint32_t pgm_data; - if (ptr == u8g_pgm_cached_iadr) - { - pgm_data = u8g_pgm_cached_data; - } - else - { - // read 4-byte aligned - pgm_data = *ptr; - u8g_pgm_cached_iadr = ptr; - u8g_pgm_cached_data = pgm_data; - } + // read 4-byte aligned + uint32_t pgm_data = *ptr; // return the correct byte within the retrieved 32bit word return pgm_data >> ((iadr % 4) * 8); From 36d42010c2dfd86ab5eed59b4a3a665c8e4a2950 Mon Sep 17 00:00:00 2001 From: AllAboutEE Date: Wed, 11 Mar 2015 08:32:00 -0500 Subject: [PATCH 10/40] Added IMAP read email example. The example is working but the output sections are not 100% correct. Parsing the output and waiting for OK message from server is needed. --- lua_examples/email/read_email_imap.lua | 88 ++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 lua_examples/email/read_email_imap.lua diff --git a/lua_examples/email/read_email_imap.lua b/lua_examples/email/read_email_imap.lua new file mode 100644 index 00000000..fe96ed4a --- /dev/null +++ b/lua_examples/email/read_email_imap.lua @@ -0,0 +1,88 @@ +--- +-- @author Miguel (AllAboutEE.com) +-- @description Reads email via IMAP + +-- this could be your email (and it is in most cases even in you have a "username") +local IMAP_USERNAME = "myemail@xxx.rr.com" +local IMAP_PASSWORD = "mypassword" + +local IMAP_SERVER = "mail.twc.com" +local IMAP_PORT = "143" +local IMAP_TAG = "t1" + +local SSID = "ssid" +local SSID_PASSWORD = "password" + +wifi.setmode(wifi.STATION) +wifi.sta.config(SSID,SSID_PASSWORD) +wifi.sta.autoconnect(1) + + +function print_logout(sck,logout_reply) + print("=========== LOGOUT REPLY ==========\r\n") + print(logout_reply) +end + +function logout(sck) + print("Logging out...\r\n") + sck:on("receive",print_logout) + sck:send(IMAP_TAG .. " LOGOUT\r\n") +end + +function print_body(sck,email_body) + print("========== EMAIL BODY =========== \r\n") + print(email_body) + print("\r\n") + logout(sck) +end + +function fetch_body(sck) + print("\r\nFetching first mail body...\r\n") + sck:on("receive",print_body) + sck:send(IMAP_TAG .. " FETCH 1 BODY[TEXT]\r\n") +end + + +function print_header(sck, email_header) + print("============== EMAIL HEADERS ==========\r\n") + print(email_header) -- prints the email headers + print("\r\n") + fetch_body(sck) +end + +function fetch_header(sck) + print("\r\nFetching first mail headers...\r\n") + sck:on("receive",print_header) + sck:send(IMAP_TAG .. " FETCH 1 BODY[HEADER]\r\n") +end + +function print_login(sck,login_reply) + print("========== LOGIN REPLY ==========\r\n") + print(login_reply) + select(sck,"INBOX") + +end + +function print_select(sck,response) + print(response) + fetch_header(sck) +end + +function select(sck,mailbox) + print("Selecting inbox...\r\n") + sck:on("receive",print_select) + sck:send(IMAP_TAG .. " SELECT "..mailbox.."\r\n") +end + + + +function login(sck) + print("Logging in...\r\n") + sck:on("receive", print_login) + sck:send(IMAP_TAG .. " LOGIN " .. IMAP_USERNAME .. " ".. IMAP_PASSWORD.."\r\n") +end + + +local socket = net.createConnection(net.TCP,0) +socket:on("connection",login) +socket:connect(IMAP_PORT,IMAP_SERVER) From 4f55ee4e34e32dc77e06202006b07cd45fa5a645 Mon Sep 17 00:00:00 2001 From: devsaurus Date: Wed, 11 Mar 2015 21:53:45 +0100 Subject: [PATCH 11/40] validate SPI comm interface with ssd1306_128x64_spi --- app/modules/u8g.c | 46 ++++++++++++++++++++++++++++++ lua_examples/u8g_bitmaps.lua | 28 ++++++++++++++---- lua_examples/u8g_graphics_test.lua | 27 ++++++++++++++---- lua_examples/u8g_rotation.lua | 25 +++++++++++++--- 4 files changed, 110 insertions(+), 16 deletions(-) diff --git a/app/modules/u8g.c b/app/modules/u8g.c index e882ae4e..66e75dfe 100644 --- a/app/modules/u8g.c +++ b/app/modules/u8g.c @@ -1046,6 +1046,51 @@ static int lu8g_ssd1306_128x64_i2c( lua_State *L ) return 1; } +// Lua: object = u8g.ssd1306_128x64_spi( cs, dc ) +static int lu8g_ssd1306_128x64_spi( lua_State *L ) +{ + unsigned cs = luaL_checkinteger( L, 1 ); + if (cs == 0) + return luaL_error( L, "CS pin required" ); + unsigned dc = luaL_checkinteger( L, 2 ); + if (dc == 0) + return luaL_error( L, "D/C pin required" ); + + lu8g_userdata_t *lud = (lu8g_userdata_t *) lua_newuserdata( L, sizeof( lu8g_userdata_t ) ); + + // We don't use the pre-defined device structure for u8g_dev_ssd1306_128x64_i2c here + // Reason: linking the pre-defined structures allocates RAM for the device/comm structure + // *before* the display is constructed (especially the page buffers) + // this consumes heap even when the device is not used at all +#if 1 + // build device entry + lud->dev = (u8g_dev_t){ u8g_dev_ssd1306_128x64_fn, &(lud->pb), U8G_COM_HW_SPI }; + + // populate and allocate page buffer + // constants taken from u8g_dev_ssd1306_128x64.c: + // PAGE_HEIGHT + // | Height + // | | WIDTH + // | | | + lud->pb = (u8g_pb_t){ { 8, 64, 0, 0, 0 }, 128, NULL }; + // + if ((lud->pb.buf = (void *)c_zalloc(lud->pb.width)) == NULL) + return luaL_error( L, "out of memory" ); + + // and finally init device using specific interface init function + u8g_InitHWSPI( LU8G, &(lud->dev), cs, dc, U8G_PIN_NONE ); +#else + u8g_InitHWSPI( LU8G, &u8g_dev_ssd1306_128x64_spi, cs, dc, U8G_PIN_NONE ); +#endif + + + // set its metatable + luaL_getmetatable(L, "u8g.display"); + lua_setmetatable(L, -2); + + return 1; +} + // Module function map #define MIN_OPT_LEVEL 2 @@ -1111,6 +1156,7 @@ static const LUA_REG_TYPE lu8g_display_map[] = const LUA_REG_TYPE lu8g_map[] = { { LSTRKEY( "ssd1306_128x64_i2c" ), LFUNCVAL ( lu8g_ssd1306_128x64_i2c ) }, + { LSTRKEY( "ssd1306_128x64_spi" ), LFUNCVAL ( lu8g_ssd1306_128x64_spi ) }, #if LUA_OPTIMIZE_MEMORY > 0 // Register fonts diff --git a/lua_examples/u8g_bitmaps.lua b/lua_examples/u8g_bitmaps.lua index 0a8ac8e7..92b5f7a0 100644 --- a/lua_examples/u8g_bitmaps.lua +++ b/lua_examples/u8g_bitmaps.lua @@ -1,13 +1,28 @@ -- setup I2c and connect display function init_i2c_display() - sda = 5 - scl = 6 + -- SDA and SCL can be assigned freely to available GPIOs + sda = 5 -- GPIO14 + scl = 6 -- GPIO12 sla = 0x3c i2c.setup(0, sda, scl, i2c.SLOW) disp = u8g.ssd1306_128x64_i2c(sla) end +-- setup SPI and connect display +function init_spi_display() + -- Hardware SPI CLK = GPIO14 + -- Hardware SPI MOSI = GPIO13 + -- Hardware SPI MISO = GPIO12 (not used) + -- CS and D/C can be assigned freely to available GPIOs + cs = 8 -- GPIO15, pull-down 10k to GND + dc = 4 -- GPIO2 + + spi.setup(1, spi.MASTER, spi.CPOL_LOW, spi.CPHA_LOW, spi.DATABITS_8, 0) + disp = u8g.ssd1306_128x64_spi(cs, dc) +end + + function xbm_picture() disp:setFont(u8g.font_6x10) disp:drawStr( 0, 10, "XBM picture") @@ -34,9 +49,7 @@ function draw(draw_state) end -function bitmap_test() - init_i2c_display() - +function bitmap_test(delay) -- read XBM picture file.open("u8glib_logo.xbm", "r") xbm_data = file.read() @@ -58,10 +71,13 @@ function bitmap_test() draw(draw_state) until disp:nextPage() == false + tmr.delay(delay) tmr.wdclr() end print("--- Bitmap Test done ---") end -bitmap_test() +--init_i2c_display() +init_spi_display() +bitmap_test(50000) diff --git a/lua_examples/u8g_graphics_test.lua b/lua_examples/u8g_graphics_test.lua index edadf872..8a721e99 100644 --- a/lua_examples/u8g_graphics_test.lua +++ b/lua_examples/u8g_graphics_test.lua @@ -1,13 +1,27 @@ -- setup I2c and connect display function init_i2c_display() - sda = 5 - scl = 6 + -- SDA and SCL can be assigned freely to available GPIOs + sda = 5 -- GPIO14 + scl = 6 -- GPIO12 sla = 0x3c i2c.setup(0, sda, scl, i2c.SLOW) disp = u8g.ssd1306_128x64_i2c(sla) end +-- setup SPI and connect display +function init_spi_display() + -- Hardware SPI CLK = GPIO14 + -- Hardware SPI MOSI = GPIO13 + -- Hardware SPI MISO = GPIO12 (not used) + -- CS and D/C can be assigned freely to available GPIOs + cs = 8 -- GPIO15, pull-down 10k to GND + dc = 4 -- GPIO2 + + spi.setup(1, spi.MASTER, spi.CPOL_LOW, spi.CPHA_LOW, spi.DATABITS_8, 0) + disp = u8g.ssd1306_128x64_spi(cs, dc) +end + -- graphic test components function prepare() @@ -122,9 +136,7 @@ function draw(draw_state) end end -function graphics_test() - init_i2c_display() - +function graphics_test(delay) print("--- Starting Graphics Test ---") -- cycle through all components @@ -135,6 +147,7 @@ function graphics_test() draw(draw_state) until disp:nextPage() == false --print(node.heap()) + tmr.delay(delay) -- re-trigger Watchdog! tmr.wdclr() end @@ -142,4 +155,6 @@ function graphics_test() print("--- Graphics Test done ---") end -graphics_test() +--init_i2c_display() +init_spi_display() +graphics_test(50000) diff --git a/lua_examples/u8g_rotation.lua b/lua_examples/u8g_rotation.lua index 0d900831..5b20fca8 100644 --- a/lua_examples/u8g_rotation.lua +++ b/lua_examples/u8g_rotation.lua @@ -1,13 +1,28 @@ -- setup I2c and connect display function init_i2c_display() - sda = 5 - scl = 6 + -- SDA and SCL can be assigned freely to available GPIOs + sda = 5 -- GPIO14 + scl = 6 -- GPIO12 sla = 0x3c i2c.setup(0, sda, scl, i2c.SLOW) disp = u8g.ssd1306_128x64_i2c(sla) end +-- setup SPI and connect display +function init_spi_display() + -- Hardware SPI CLK = GPIO14 + -- Hardware SPI MOSI = GPIO13 + -- Hardware SPI MISO = GPIO12 (not used) + -- CS and D/C can be assigned freely to available GPIOs + cs = 8 -- GPIO15, pull-down 10k to GND + dc = 4 -- GPIO2 + + spi.setup(1, spi.MASTER, spi.CPOL_LOW, spi.CPHA_LOW, spi.DATABITS_8, 0) + disp = u8g.ssd1306_128x64_spi(cs, dc) +end + + -- the draw() routine function draw() disp:setFont(u8g.font_6x10) @@ -35,13 +50,12 @@ function rotate() dir = dir + 1 dir = bit.band(dir, 3) + -- schedule next rotation step in 1000ms next_rotation = tmr.now() / 1000 + 1000 end end function rotation_test() - init_i2c_display() - print("--- Starting Rotation Test ---") dir = 0 next_rotation = 0 @@ -55,10 +69,13 @@ function rotation_test() draw(draw_state) until disp:nextPage() == false + tmr.delay(100000) tmr.wdclr() end print("--- Rotation Test done ---") end +--init_i2c_display() +init_spi_display() rotation_test() From 88815012d4dbd036226854faaf772a41facc0f47 Mon Sep 17 00:00:00 2001 From: devsaurus Date: Wed, 11 Mar 2015 21:56:20 +0100 Subject: [PATCH 12/40] move u8glib examples to subfolder --- lua_examples/{ => u8glib}/u8g_bitmaps.lua | 0 lua_examples/{ => u8glib}/u8g_graphics_test.lua | 0 lua_examples/{ => u8glib}/u8g_rook.bm | Bin lua_examples/{ => u8glib}/u8g_rotation.lua | 0 lua_examples/{ => u8glib}/u8glib_logo.xbm | Bin 5 files changed, 0 insertions(+), 0 deletions(-) rename lua_examples/{ => u8glib}/u8g_bitmaps.lua (100%) rename lua_examples/{ => u8glib}/u8g_graphics_test.lua (100%) rename lua_examples/{ => u8glib}/u8g_rook.bm (100%) rename lua_examples/{ => u8glib}/u8g_rotation.lua (100%) rename lua_examples/{ => u8glib}/u8glib_logo.xbm (100%) diff --git a/lua_examples/u8g_bitmaps.lua b/lua_examples/u8glib/u8g_bitmaps.lua similarity index 100% rename from lua_examples/u8g_bitmaps.lua rename to lua_examples/u8glib/u8g_bitmaps.lua diff --git a/lua_examples/u8g_graphics_test.lua b/lua_examples/u8glib/u8g_graphics_test.lua similarity index 100% rename from lua_examples/u8g_graphics_test.lua rename to lua_examples/u8glib/u8g_graphics_test.lua diff --git a/lua_examples/u8g_rook.bm b/lua_examples/u8glib/u8g_rook.bm similarity index 100% rename from lua_examples/u8g_rook.bm rename to lua_examples/u8glib/u8g_rook.bm diff --git a/lua_examples/u8g_rotation.lua b/lua_examples/u8glib/u8g_rotation.lua similarity index 100% rename from lua_examples/u8g_rotation.lua rename to lua_examples/u8glib/u8g_rotation.lua diff --git a/lua_examples/u8glib_logo.xbm b/lua_examples/u8glib/u8glib_logo.xbm similarity index 100% rename from lua_examples/u8glib_logo.xbm rename to lua_examples/u8glib/u8glib_logo.xbm From b172f628b5c621b3d790c0c22f3f32159daf36ea Mon Sep 17 00:00:00 2001 From: devsaurus Date: Wed, 11 Mar 2015 22:20:28 +0100 Subject: [PATCH 13/40] document SPI and bitmap --- README.md | 42 +++++++++++++++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index fb55a684..9ffec70a 100644 --- a/README.md +++ b/README.md @@ -346,24 +346,50 @@ The integration in nodemcu is developed for SSD1306 based display attached via t U8glib v1.17 #####I2C connection -Hook up SDA and SCL to any free GPIOs. Eg. [lua_examples/graphics_test.lua](https://github.com/devsaurus/nodemcu-firmware/blob/dev/lua_examples/graphics_test.lua) expects SDA=5 (GPIO14) and SCL=6 (GPIO12). They are used to set up nodemcu's I2C driver before accessing the display: +Hook up SDA and SCL to any free GPIOs. Eg. `lua_examples/u8glib/graphics_test.lua` expects SDA=5 (GPIO14) and SCL=6 (GPIO12). They are used to set up nodemcu's I2C driver before accessing the display: ```lua sda = 5 scl = 6 i2c.setup(0, sda, scl, i2c.SLOW) ``` +#####SPI connection +The HSPI module is used, so certain pins are fixed: +* HSPI CLK = GPIO14 +* HSPI MOSI = GPIO13 +* HPSI MISO = GPIO12 (not used) + +All other pins can be assigned to any available GPIO: +* CS +* D/C +* RES (optional) + +Also refer to the initialization sequence eg in `lua_examples/u8glib/graphics_test.lua`: +```lua +spi.setup(1, spi.MASTER, spi.CPOL_LOW, spi.CPHA_LOW, spi.DATABITS_8, 0) +``` + + #####Library usage -The Lua bindings for this library closely follow u8glib's object oriented C++ API. Based on the u8g class, you create an object for your display type: +The Lua bindings for this library closely follow u8glib's object oriented C++ API. Based on the u8g class, you create an object for your display type. + +SSD1306 via I2C: ```lua sla = 0x3c disp = u8g.ssd1306_128x64_i2c(sla) ``` +SSD1306 via SPI: +```lua +cs = 8 -- GPIO15, pull-down 10k to GND +dc = 4 -- GPIO2 +disp = u8g.ssd1306_128x64_spi(cs, dc) +``` + This object provides all of u8glib's methods to control the display. -Again, refer to [lua_examples/graphics_test.lua](https://github.com/devsaurus/nodemcu-firmware/blob/dev/lua_examples/u8g_graphics_test.lua) to get an impression how this is achieved with Lua code. Visit the [u8glib homepage](https://code.google.com/p/u8glib/) for technical details. +Again, refer to `lua_examples/u8glib/graphics_test.lua` to get an impression how this is achieved with Lua code. Visit the [u8glib homepage](https://code.google.com/p/u8glib/) for technical details. #####Fonts -u8glib comes with a wide range of fonts for small displays. Since they need to be compiled into the firmware image, you'd need to include them in [app/include/user_config.h](https://github.com/devsaurus/nodemcu-firmware/blob/dev/app/include/user_config.h) and recompile. Simply add the desired fonts to the font table: +u8glib comes with a wide range of fonts for small displays. Since they need to be compiled into the firmware image, you'd need to include them in `app/include/user_config.h` and recompile. Simply add the desired fonts to the font table: ```c #define U8G_FONT_TABLE \ U8G_FONT_TABLE_ENTRY(font_6x10) \ @@ -371,6 +397,9 @@ u8glib comes with a wide range of fonts for small displays. Since they need to b ``` They'll be available as `u8g.` in Lua. +#####Bitmaps +Bitmaps and XBMs are supplied as strings to `drawBitmap()` and `drawXBM()`. This off-loads all data handling from the u8g module to generic methods for binary files. See `lua_examples/u8glib/u8g_bitmaps.lua`. Binary files can be uploaded with [nodemcu-uploader.py](https://github.com/kmpm/nodemcu-uploader). + #####Unimplemented functions - [ ] Cursor handling - [ ] disableCursor() @@ -379,12 +408,7 @@ They'll be available as `u8g.` in Lua. - [ ] setCursorFont() - [ ] setCursorPos() - [ ] setCursorStyle() -- [x] Bitmaps - - [x] drawBitmap() - - [x] drawXBM() - [ ] General functions - - [x] begin() - - [ ] print() - [ ] setContrast() - [ ] setPrintPos() - [ ] setHardwareBackup() From 56aa416535ab2b59a4dd520cb3679965f2e88300 Mon Sep 17 00:00:00 2001 From: AllAboutEE Date: Thu, 12 Mar 2015 01:16:02 -0500 Subject: [PATCH 14/40] Added imap lua module and updated read_email_imap.lua example to use module. There is a bug where Examine is not sent sometimes. --- lua_examples/email/read_email_imap.lua | 119 +++++++++++-------------- lua_modules/email/imap.lua | 79 ++++++++++++++++ 2 files changed, 130 insertions(+), 68 deletions(-) create mode 100644 lua_modules/email/imap.lua diff --git a/lua_examples/email/read_email_imap.lua b/lua_examples/email/read_email_imap.lua index fe96ed4a..bbd22b6b 100644 --- a/lua_examples/email/read_email_imap.lua +++ b/lua_examples/email/read_email_imap.lua @@ -3,86 +3,69 @@ -- @description Reads email via IMAP -- this could be your email (and it is in most cases even in you have a "username") -local IMAP_USERNAME = "myemail@xxx.rr.com" -local IMAP_PASSWORD = "mypassword" +require("imap") -local IMAP_SERVER = "mail.twc.com" +local IMAP_USERNAME = "myemail@domain.com" +local IMAP_PASSWORD = "myemailpassword" + +local IMAP_SERVER = "imap.domain.com" local IMAP_PORT = "143" local IMAP_TAG = "t1" local SSID = "ssid" -local SSID_PASSWORD = "password" +local SSID_PASSWORD = "ssidpassword" + + +local count = 0 wifi.setmode(wifi.STATION) wifi.sta.config(SSID,SSID_PASSWORD) wifi.sta.autoconnect(1) +local imap_socket = net.createConnection(net.TCP,0) -function print_logout(sck,logout_reply) - print("=========== LOGOUT REPLY ==========\r\n") - print(logout_reply) + +function setup(sck) + imap.config(IMAP_USERNAME, + IMAP_PASSWORD, + IMAP_TAG, + sck) + + imap.login(imap_socket) end -function logout(sck) - print("Logging out...\r\n") - sck:on("receive",print_logout) - sck:send(IMAP_TAG .. " LOGOUT\r\n") +imap_socket:on("connection",setup) +imap_socket:connect(IMAP_PORT,IMAP_SERVER) + +function do_next() + + if(imap.receive_complete() == true) then + print("receive complete") + + if (count == 0) then + print("Examine:\r\n") + imap.examine(imap_socket,"INBOX") + count = count + 1 + elseif (count == 1) then + imap.fetch_header(imap_socket,1,"SUBJECT") + count = count + 1 + elseif (count == 2) then + imap.fetch_header(imap_socket,1,"FROM") + count = count + 1 + elseif (count == 3) then + imap.fetch_body_plain_text(imap_socket,1) + count = count + 1 + elseif (count == 4) then + imap.logout(imap_socket) + count = count + 1 + else + print("The body is: ".. imap.get_body()) + tmr.stop(0) + imap_socket:close() + collectgarbage() + end + end + end -function print_body(sck,email_body) - print("========== EMAIL BODY =========== \r\n") - print(email_body) - print("\r\n") - logout(sck) -end - -function fetch_body(sck) - print("\r\nFetching first mail body...\r\n") - sck:on("receive",print_body) - sck:send(IMAP_TAG .. " FETCH 1 BODY[TEXT]\r\n") -end - - -function print_header(sck, email_header) - print("============== EMAIL HEADERS ==========\r\n") - print(email_header) -- prints the email headers - print("\r\n") - fetch_body(sck) -end - -function fetch_header(sck) - print("\r\nFetching first mail headers...\r\n") - sck:on("receive",print_header) - sck:send(IMAP_TAG .. " FETCH 1 BODY[HEADER]\r\n") -end - -function print_login(sck,login_reply) - print("========== LOGIN REPLY ==========\r\n") - print(login_reply) - select(sck,"INBOX") - -end - -function print_select(sck,response) - print(response) - fetch_header(sck) -end - -function select(sck,mailbox) - print("Selecting inbox...\r\n") - sck:on("receive",print_select) - sck:send(IMAP_TAG .. " SELECT "..mailbox.."\r\n") -end - - - -function login(sck) - print("Logging in...\r\n") - sck:on("receive", print_login) - sck:send(IMAP_TAG .. " LOGIN " .. IMAP_USERNAME .. " ".. IMAP_PASSWORD.."\r\n") -end - - -local socket = net.createConnection(net.TCP,0) -socket:on("connection",login) -socket:connect(IMAP_PORT,IMAP_SERVER) +tmr.alarm(0,1000,1, do_next) diff --git a/lua_modules/email/imap.lua b/lua_modules/email/imap.lua new file mode 100644 index 00000000..589481e4 --- /dev/null +++ b/lua_modules/email/imap.lua @@ -0,0 +1,79 @@ +local moduleName = ... +local M = {} +_G[moduleName] = M + +local USERNAME = "" +local PASSWORD = "" + +local SERVER = "" +local PORT = "" +local TAG = "" + +local body = "" + + +local receive_complete = false + +function M.receive_complete() + return receive_complete +end + + +local function display(socket, response) + print(response) + if(string.match(response,'complete') ~= nil) then + receive_complete = true + end +end + +function M.config(username,password,tag,sk) + USERNAME = username + PASSWORD = password + TAG = tag +end + +function M.login(socket) + receive_complete = false + socket:send(TAG .. " LOGIN " .. USERNAME .. " " .. PASSWORD .. "\r\n") + socket:on("receive",display) +end + +function M.examine(socket,mailbox) + + receive_complete = false + socket:send(TAG .. " EXAMINE " .. mailbox .. "\r\n") + socket:on("receive",display) +end + +function M.fetch_header(socket,msg_number,field) + + receive_complete = false + socket:send(TAG .. " FETCH " .. msg_number .. " BODY[HEADER.FIELDS (" .. field .. ")]\r\n") + socket:on("receive",display) +end + +function M.get_body() + return body +end + + +local function set_body(socket,response) + print(response) + body = body .. response + if(string.match(response,'complete') ~= nil) then + receive_complete = true + end +end + +function M.fetch_body_plain_text(socket,msg_number) +receive_complete = false + body = "" + socket:send(TAG .. " FETCH " .. msg_number .. " BODY[1]\r\n") + socket:on("receive",set_body) +end + +function M.logout(socket) + receive_complete = false + socket:send(TAG .. " LOGOUT\r\n") + socket:on("receive",display) +end From 72f767445fec755566de1ea91d5689af94faec3e Mon Sep 17 00:00:00 2001 From: AllAboutEE Date: Thu, 12 Mar 2015 11:27:01 -0500 Subject: [PATCH 15/40] Working example of IMAP read email module. --- lua_examples/email/read_email_imap.lua | 94 ++++++++++---- lua_modules/email/imap.lua | 167 +++++++++++++++++++++---- 2 files changed, 217 insertions(+), 44 deletions(-) diff --git a/lua_examples/email/read_email_imap.lua b/lua_examples/email/read_email_imap.lua index bbd22b6b..02289aec 100644 --- a/lua_examples/email/read_email_imap.lua +++ b/lua_examples/email/read_email_imap.lua @@ -1,71 +1,119 @@ --- -- @author Miguel (AllAboutEE.com) --- @description Reads email via IMAP +-- @description This example will read the first email in your inbox using IMAP and +-- display it through serial. The email server must provided unecrypted access. The code +-- was tested with an AOL and Time Waraner cable email ccounts (GMail and other services who do +-- not support no SSL access will not work). --- this could be your email (and it is in most cases even in you have a "username") require("imap") -local IMAP_USERNAME = "myemail@domain.com" -local IMAP_PASSWORD = "myemailpassword" +local IMAP_USERNAME = "email@domain.com" +local IMAP_PASSWORD = "password" -local IMAP_SERVER = "imap.domain.com" +-- find out your unencrypted imap server and port +-- from your email provided i.e. google "[my email service] imap settings" for example +local IMAP_SERVER = "imap.service.com" local IMAP_PORT = "143" -local IMAP_TAG = "t1" + +local IMAP_TAG = "t1" -- You do not need to change this +local IMAP_DEBUG = false -- change to true if you would like to see the entire conversation between + -- the ESP8266 and IMAP server local SSID = "ssid" -local SSID_PASSWORD = "ssidpassword" +local SSID_PASSWORD = "password" -local count = 0 +local count = 0 -- we will send several IMAP commands/requests, this variable helps keep track of which one to send +-- configure the ESP8266 as a station wifi.setmode(wifi.STATION) wifi.sta.config(SSID,SSID_PASSWORD) wifi.sta.autoconnect(1) +-- create an unencrypted connection local imap_socket = net.createConnection(net.TCP,0) +--- +-- @name setup +-- @description A call back function used to begin reading email +-- upon sucessfull connection to the IMAP server function setup(sck) + -- Set the email user name and password, IMAP tag, and if debugging output is needed imap.config(IMAP_USERNAME, IMAP_PASSWORD, IMAP_TAG, - sck) + IMAP_DEBUG) - imap.login(imap_socket) + imap.login(sck) end -imap_socket:on("connection",setup) -imap_socket:connect(IMAP_PORT,IMAP_SERVER) +imap_socket:on("connection",setup) -- call setup() upon connection +imap_socket:connect(IMAP_PORT,IMAP_SERVER) -- connect to the IMAP server +local subject = "" +local from = "" +local message = "" + +--- +-- @name do_next +-- @description A call back function for a timer alarm used to check if the previous +-- IMAP command reply has been processed. If the IMAP reply has been processed +-- this function will call the next IMAP command function necessary to read the email function do_next() - if(imap.receive_complete() == true) then - print("receive complete") + -- Check if the IMAP reply was processed + if(imap.response_processed() == true) then + + -- The IMAP reply was processed if (count == 0) then - print("Examine:\r\n") + -- After logging in we need to select the email folder from which we wish to read + -- in this case the INBOX folder imap.examine(imap_socket,"INBOX") count = count + 1 elseif (count == 1) then - imap.fetch_header(imap_socket,1,"SUBJECT") + -- After examining/selecting the INBOX folder we can begin to retrieve emails. + imap.fetch_header(imap_socket,imap.get_most_recent_num(),"SUBJECT") -- Retrieve the SUBJECT of the first/newest email count = count + 1 elseif (count == 2) then - imap.fetch_header(imap_socket,1,"FROM") + subject = imap.get_header() -- store the SUBJECT response in subject + imap.fetch_header(imap_socket,imap.get_most_recent_num(),"FROM") -- Retrieve the FROM of the first/newest email count = count + 1 elseif (count == 3) then - imap.fetch_body_plain_text(imap_socket,1) + from = imap.get_header() -- store the FROM response in from + imap.fetch_body_plain_text(imap_socket,imap.get_most_recent_num()) -- Retrieve the BODY of the first/newest email count = count + 1 elseif (count == 4) then - imap.logout(imap_socket) + body = imap.get_body() -- store the BODY response in body + imap.logout(imap_socket) -- Logout of the email account count = count + 1 else - print("The body is: ".. imap.get_body()) - tmr.stop(0) - imap_socket:close() - collectgarbage() + -- display the email contents + + -- create patterns to strip away IMAP protocl text from actual message + pattern1 = "(\*.+\}\r\n)" -- to remove "* n command (BODY[n] {n}" + pattern2 = "(\r\n.+)" -- to remove ") t1 OK command completed" + + from = string.gsub(from,pattern1,"") + from = string.gsub(from,pattern2,"") + print(from) + + subject = string.gsub(subject,pattern1,"") + subject = string.gsub(subject,pattern2,"") + print(subject) + + body = string.gsub(body,pattern1,"") + body = string.gsub(body,pattern2,"") + print("Message: " .. body) + + tmr.stop(0) -- Stop the timer alarm + imap_socket:close() -- close the IMAP socket + collectgarbage() -- clean up end end end +-- A timer alarm is sued to check if an IMAP reply has been processed tmr.alarm(0,1000,1, do_next) diff --git a/lua_modules/email/imap.lua b/lua_modules/email/imap.lua index 589481e4..4b2031ab 100644 --- a/lua_modules/email/imap.lua +++ b/lua_modules/email/imap.lua @@ -1,3 +1,19 @@ +--- +-- IMPORTANT: run node.compile("imap.lua") after uploading this script +-- to create a compiled module. Then run file.remove("imap.lua") +-- @name imap +-- @description An IMAP 4rev1 module that can be used to read email. +-- Tested on NodeMCU 0.9.5 build 20150213. +-- @date March 12, 2015 +-- @author Miguel +-- GitHub: https://github.com/AllAboutEE +-- YouTube: https://www.youtube.com/user/AllAboutEE +-- Website: http://AllAboutEE.com +-- +-- Visit the following URLs to learn more about IMAP: +-- "How to test an IMAP server by using telnet" http://www.anta.net/misc/telnet-troubleshooting/imap.shtml +-- "RFC 2060 - Internet Message Access Protocol - Version 4rev1" http://www.faqs.org/rfcs/rfc2060.html +------------------------------------------------------------------------------------------------------------- local moduleName = ... local M = {} _G[moduleName] = M @@ -9,71 +25,180 @@ local SERVER = "" local PORT = "" local TAG = "" -local body = "" +local DEBUG = false + +local body = "" -- used to store an email's body / main text +local header = "" -- used to store an email's last requested header field e.g. SUBJECT, FROM, DATA etc. +local most_recent_num = 1 -- used to store the latest/newest email number/id -local receive_complete = false +local response_processed = false -- used to know if the last IMAP response has been processed -function M.receive_complete() - return receive_complete +--- +-- @name response_processed +-- @returns The response process status of the last IMAP command sent +function M.response_processed() + return response_processed end - +--- +-- @name display +-- @description A generic IMAP response processing function. +-- Can disply the IMAP response if DEBUG is set to true. +-- Sets the reponse processed variable to true when the string "complete" +-- is found in the IMAP reply/response local function display(socket, response) - print(response) - if(string.match(response,'complete') ~= nil) then - receive_complete = true + + -- If debuggins is enabled print the IMAP response + if(DEBUG) then + print(response) end + + -- Some IMAP responses are long enough that they will cause the display + -- function to be called several times. One thing is certain, IMAP will replay with + -- " OK complete" when it's done sending data back. + if(string.match(response,'complete') ~= nil) then + response_processed = true + end + end -function M.config(username,password,tag,sk) +--- +-- @name config +-- @description Initiates the IMAP settings +function M.config(username,password,tag,debug) USERNAME = username PASSWORD = password TAG = tag + DEBUG = debug end +--- +-- @name login +-- @descrpiton Logs into a new email session function M.login(socket) - receive_complete = false + response_processed = false -- we are sending a new command + -- which means that the response for it has not been processed socket:send(TAG .. " LOGIN " .. USERNAME .. " " .. PASSWORD .. "\r\n") socket:on("receive",display) end +--- +-- @name get_most_recent_num +-- @returns The most recent email number. Should only be called after examine() +function M.get_most_recent_num() + return most_recent_num +end + +--- +-- @name set_most_recent_num +-- @description Gets the most recent email number from the EXAMINE command. +-- i.e. if EXAMINE returns "* 4 EXISTS" this means that there are 4 emails, +-- so the latest/newest will be identified by the number 4 +local function set_most_recent_num(socket,response) + + if(DEBUG) then + print(response) + end + + local _, _, num = string.find(response,"([0-9]+) EXISTS(\.)") -- the _ and _ keep the index of the string found + -- but we don't care about that. + + if(num~=nil) then + most_recent_num = num + end + + if(string.match(response,'complete') ~= nil) then + response_processed = true + end +end + +--- +-- @name examine +-- @description IMAP examines the given mailbox/folder. Sends the IMAP EXAMINE command function M.examine(socket,mailbox) - receive_complete = false + response_processed = false socket:send(TAG .. " EXAMINE " .. mailbox .. "\r\n") - socket:on("receive",display) + socket:on("receive",set_most_recent_num) end +--- +-- @name get_header +-- @returns The last fetched header field +function M.get_header() + return header +end + +--- +-- @name set_header +-- @description Records the IMAP header field response in a variable +-- so that it may be read later +local function set_header(socket,response) + if(DEBUG) then + print(response) + end + + header = header .. response + if(string.match(response,'complete') ~= nil) then + response_processed = true + end +end + +--- +-- @name fetch_header +-- @description Fetches an emails header field e.g. SUBJECT, FROM, DATE +-- @param socket The IMAP socket to use +-- @param msg_number The email number to read e.g. 1 will read fetch the latest/newest email +-- @param field A header field such as SUBJECT, FROM, or DATE function M.fetch_header(socket,msg_number,field) - - receive_complete = false + header = "" -- we are getting a new header so clear this variable + response_processed = false socket:send(TAG .. " FETCH " .. msg_number .. " BODY[HEADER.FIELDS (" .. field .. ")]\r\n") - socket:on("receive",display) + socket:on("receive",set_header) end + +--- +-- @name get_body +-- @return The last email read's body function M.get_body() return body end - +--- +-- @name set_body +-- @description Records the IMAP body response in a variable +-- so that it may be read later local function set_body(socket,response) - print(response) + + if(DEBUG) then + print(response) + end + body = body .. response if(string.match(response,'complete') ~= nil) then - receive_complete = true + response_processed = true end end +--- +-- @name fetch_body_plain_text +-- @description Sends the IMAP command to fetch a plain text version of the email's body +-- @param socket The IMAP socket to use +-- @param msg_number The email number to obtain e.g. 1 will obtain the latest email function M.fetch_body_plain_text(socket,msg_number) -receive_complete = false - body = "" + response_processed = false + body = "" -- clear the body variable since we'll be fetching a new email socket:send(TAG .. " FETCH " .. msg_number .. " BODY[1]\r\n") socket:on("receive",set_body) end +--- +-- @name logout +-- @description Sends the IMAP command to logout of the email session function M.logout(socket) - receive_complete = false + response_processed = false socket:send(TAG .. " LOGOUT\r\n") socket:on("receive",display) end From b387ba93ecf6061fb62fed2c6b568f425d489f18 Mon Sep 17 00:00:00 2001 From: devsaurus Date: Thu, 12 Mar 2015 21:50:57 +0100 Subject: [PATCH 16/40] introduce u8g_config.h --- app/include/u8g_config.h | 14 ++++++++++++++ app/include/user_config.h | 8 -------- app/modules/u8g.c | 13 ++----------- 3 files changed, 16 insertions(+), 19 deletions(-) create mode 100644 app/include/u8g_config.h diff --git a/app/include/u8g_config.h b/app/include/u8g_config.h new file mode 100644 index 00000000..d0055167 --- /dev/null +++ b/app/include/u8g_config.h @@ -0,0 +1,14 @@ +#ifndef __U8G_CONFIG_H__ +#define __U8G_CONFIG_H__ + + + +// Configure U8glib fonts +// add a U8G_FONT_TABLE_ENTRY for each font you want to compile into the image +#define U8G_FONT_TABLE_ENTRY(font) +#define U8G_FONT_TABLE \ + U8G_FONT_TABLE_ENTRY(font_6x10) \ + U8G_FONT_TABLE_ENTRY(font_chikita) +#undef U8G_FONT_TABLE_ENTRY + +#endif /* __U8G_CONFIG_H__ */ diff --git a/app/include/user_config.h b/app/include/user_config.h index c1a9805c..26af62cc 100644 --- a/app/include/user_config.h +++ b/app/include/user_config.h @@ -65,12 +65,4 @@ #define LED_LOW_COUNT_DEFAULT 0 #endif -// Configure U8glib fonts -// add a U8G_FONT_TABLE_ENTRY for each font you want to compile into the image -#define U8G_FONT_TABLE_ENTRY(font) -#define U8G_FONT_TABLE \ - U8G_FONT_TABLE_ENTRY(font_6x10) \ - U8G_FONT_TABLE_ENTRY(font_chikita) -#undef U8G_FONT_TABLE_ENTRY - #endif /* __USER_CONFIG_H__ */ diff --git a/app/modules/u8g.c b/app/modules/u8g.c index 66e75dfe..79366c3c 100644 --- a/app/modules/u8g.c +++ b/app/modules/u8g.c @@ -1,6 +1,5 @@ // Module for U8glib -//#include "lua.h" #include "lualib.h" #include "lauxlib.h" #include "platform.h" @@ -12,6 +11,8 @@ #include "u8g.h" +#include "u8g_config.h" + struct _lu8g_userdata_t { u8g_t u8g; @@ -25,16 +26,6 @@ typedef struct _lu8g_userdata_t lu8g_userdata_t; #define LU8G (&(lud->u8g)) -// Font look-up array -//static const u8g_fntpgm_uint8_t *font_array[] = -//{ -//#undef U8G_FONT_TABLE_ENTRY -//#define U8G_FONT_TABLE_ENTRY(font) u8g_ ## font , -// U8G_FONT_TABLE -// NULL -//}; - - // function to read 4-byte aligned from program memory AKA irom0 u8g_pgm_uint8_t ICACHE_FLASH_ATTR u8g_pgm_read(const u8g_pgm_uint8_t *adr) { From 429bf57d67ce1a281c31e80fd506225a4ea49c81 Mon Sep 17 00:00:00 2001 From: devsaurus Date: Thu, 12 Mar 2015 23:09:41 +0100 Subject: [PATCH 17/40] add pcd8544 --- app/include/u8g_config.h | 8 +++++- app/modules/u8g.c | 61 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 66 insertions(+), 3 deletions(-) diff --git a/app/include/u8g_config.h b/app/include/u8g_config.h index d0055167..08885fef 100644 --- a/app/include/u8g_config.h +++ b/app/include/u8g_config.h @@ -2,7 +2,6 @@ #define __U8G_CONFIG_H__ - // Configure U8glib fonts // add a U8G_FONT_TABLE_ENTRY for each font you want to compile into the image #define U8G_FONT_TABLE_ENTRY(font) @@ -11,4 +10,11 @@ U8G_FONT_TABLE_ENTRY(font_chikita) #undef U8G_FONT_TABLE_ENTRY + +// Enable display drivers +#define U8G_SSD1306_128x64_I2C +#define U8G_SSD1306_128x64_SPI +#define U8G_PCD8544_84x48 + + #endif /* __U8G_CONFIG_H__ */ diff --git a/app/modules/u8g.c b/app/modules/u8g.c index 79366c3c..e2aff544 100644 --- a/app/modules/u8g.c +++ b/app/modules/u8g.c @@ -1004,7 +1004,7 @@ static int lu8g_ssd1306_128x64_i2c( lua_State *L ) lud->u8g.i2c_addr = (uint8_t)addr; - // We don't use the pre-defined device structure for u8g_dev_ssd1306_128x64_i2c here + // Don't use the pre-defined device structure for u8g_dev_ssd1306_128x64_i2c here // Reason: linking the pre-defined structures allocates RAM for the device/comm structure // *before* the display is constructed (especially the page buffers) // this consumes heap even when the device is not used at all @@ -1049,7 +1049,7 @@ static int lu8g_ssd1306_128x64_spi( lua_State *L ) lu8g_userdata_t *lud = (lu8g_userdata_t *) lua_newuserdata( L, sizeof( lu8g_userdata_t ) ); - // We don't use the pre-defined device structure for u8g_dev_ssd1306_128x64_i2c here + // Don't use the pre-defined device structure for u8g_dev_ssd1306_128x64_spi here // Reason: linking the pre-defined structures allocates RAM for the device/comm structure // *before* the display is constructed (especially the page buffers) // this consumes heap even when the device is not used at all @@ -1082,6 +1082,55 @@ static int lu8g_ssd1306_128x64_spi( lua_State *L ) return 1; } +uint8_t u8g_dev_pcd8544_fn(u8g_t *u8g, u8g_dev_t *dev, uint8_t msg, void *arg); +// Lua: object = u8g.pcd8544_84x48( sce, dc, res ) +static int lu8g_pcd8544_84x48( lua_State *L ) +{ + unsigned sce = luaL_checkinteger( L, 1 ); + if (sce == 0) + return luaL_error( L, "SCE pin required" ); + unsigned dc = luaL_checkinteger( L, 2 ); + if (dc == 0) + return luaL_error( L, "D/C pin required" ); + unsigned res = luaL_checkinteger( L, 3 ); + if (res == 0) + return luaL_error( L, "RES pin required" ); + + lu8g_userdata_t *lud = (lu8g_userdata_t *) lua_newuserdata( L, sizeof( lu8g_userdata_t ) ); + + // Don't use the pre-defined device structure for u8g_dev_pcd8544_84x48_hw_spi here + // Reason: linking the pre-defined structures allocates RAM for the device/comm structure + // *before* the display is constructed (especially the page buffers) + // this consumes heap even when the device is not used at all +#if 1 + // build device entry + lud->dev = (u8g_dev_t){ u8g_dev_pcd8544_fn, &(lud->pb), U8G_COM_HW_SPI }; + + // populate and allocate page buffer + // constants taken from u8g_dev_pcd8544_84x48.c: + // PAGE_HEIGHT + // | Height + // | | WIDTH + // | | | + lud->pb = (u8g_pb_t){ { 8, 48, 0, 0, 0 }, 84, NULL }; + // + if ((lud->pb.buf = (void *)c_zalloc(lud->pb.width)) == NULL) + return luaL_error( L, "out of memory" ); + + // and finally init device using specific interface init function + u8g_InitHWSPI( LU8G, &(lud->dev), sce, dc, res ); +#else + u8g_InitHWSPI( LU8G, &u8g_dev_pcd8544_84x48_hw_spi, sce, dc, res ); +#endif + + + // set its metatable + luaL_getmetatable(L, "u8g.display"); + lua_setmetatable(L, -2); + + return 1; +} + // Module function map #define MIN_OPT_LEVEL 2 @@ -1146,8 +1195,16 @@ static const LUA_REG_TYPE lu8g_display_map[] = const LUA_REG_TYPE lu8g_map[] = { +#ifdef U8G_SSD1306_128x64_I2C { LSTRKEY( "ssd1306_128x64_i2c" ), LFUNCVAL ( lu8g_ssd1306_128x64_i2c ) }, +#endif +#ifdef U8G_SSD1306_128x64_I2C { LSTRKEY( "ssd1306_128x64_spi" ), LFUNCVAL ( lu8g_ssd1306_128x64_spi ) }, +#endif +#ifdef U8G_PCD8544_84x48 + { LSTRKEY( "pcd8544_84x48" ), LFUNCVAL ( lu8g_pcd8544_84x48 ) }, +#endif + #if LUA_OPTIMIZE_MEMORY > 0 // Register fonts From 998f96a4d6b0b0a86eeca434d7a7875629628c59 Mon Sep 17 00:00:00 2001 From: AllAboutEE Date: Fri, 13 Mar 2015 14:39:14 -0500 Subject: [PATCH 18/40] Added base64 module needed to encode username and password. Added SMTP send email example. --- lua_examples/email/send_email_smtp.lua | 96 ++++++++++++++++++++++++++ lua_modules/base64/base64.lua | 41 +++++++++++ 2 files changed, 137 insertions(+) create mode 100644 lua_examples/email/send_email_smtp.lua create mode 100644 lua_modules/base64/base64.lua diff --git a/lua_examples/email/send_email_smtp.lua b/lua_examples/email/send_email_smtp.lua new file mode 100644 index 00000000..637cd638 --- /dev/null +++ b/lua_examples/email/send_email_smtp.lua @@ -0,0 +1,96 @@ +require("base64") + +local MY_EMAIL = "esp8266@domain.com" +local EMAIL_PASSWORD = "123456" + +local SMTP_SERVER = "smtp.server.com" +local SMTP_PORT = "587" + +local mail_to = "to_email@domain.com" + +local SSID = "ssid" +local SSID_PASSWORD = "password" + +wifi.setmode(wifi.STATION) +wifi.sta.config(SSID,SSID_PASSWORD) +wifi.sta.autoconnect(1) + +local email_subject = "" +local email_body = "" +local receive_processed = false +local count = 0 + +local smtp_socket = net.createConnection(net.TCP,0) + +function display(sck,response) + print(response) +end + +function do_next() + if(count == 0)then + count = count+1 + local IP_ADDRESS = wifi.sta.getip() + smtp_socket:send("HELO "..IP_ADDRESS.."\r\n") + elseif(count==1) then + count = count+1 + smtp_socket:send("AUTH LOGIN\r\n") + elseif(count == 2) then + count = count + 1 + smtp_socket:send(base64.enc(MY_EMAIL).."\r\n") + elseif(count == 3) then + count = count + 1 + smtp_socket:send(base64.enc(EMAIL_PASSWORD).."\r\n") + elseif(count==4) then + count = count+1 + smtp_socket:send("MAIL FROM:<" .. MY_EMAIL .. ">\r\n") + elseif(count==5) then + count = count+1 + smtp_socket:send("RCPT TO:<" .. mail_to ..">\r\n") + elseif(count==6) then + count = count+1 + smtp_socket:send("DATA\r\n") + elseif(count==7) then + count = count+1 + local message = string.gsub( + "From: \"".. MY_EMAIL .."\"<"..MY_EMAIL..">\r\n" .. + "To: \"".. mail_to .. "\"<".. mail_to..">\r\n".. + "Subject: ".. email_subject .. "\r\n\r\n" .. + email_body,"\r\n.\r\n","") + + smtp_socket:send(message.."\r\n.\r\n") + elseif(count==8) then + count = count+1 + tmr.stop(0) + smtp_socket:send("QUIT\r\n") + else + smtp_socket:close() + end +end + +function connected(sck) + tmr.alarm(0,5000,1,do_next) +end + +function send_email(subject,body) + email_subject = subject + email_body = body + smtp_socket:on("connection",connected) + smtp_socket:on("receive",display) + smtp_socket:connect(SMTP_PORT,SMTP_SERVER) +end + +send_email( + "ESP8266", +[[Hi, +How are your IoT projects coming along? +Best Wishes, +ESP8266]]) + + + + + + + + + diff --git a/lua_modules/base64/base64.lua b/lua_modules/base64/base64.lua new file mode 100644 index 00000000..e3523a31 --- /dev/null +++ b/lua_modules/base64/base64.lua @@ -0,0 +1,41 @@ +-- Lua 5.1+ base64 v3.0 (c) 2009 by Alex Kloss +-- licensed under the terms of the LGPL2 + +local moduleName = ... +local M = {} +_G[moduleName] = M + +-- character table string +local b='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' + +-- encoding +function M.enc(data) + return ((data:gsub('.', function(x) + local r,b='',x:byte() + for i=8,1,-1 do r=r..(b%2^i-b%2^(i-1)>0 and '1' or '0') end + return r; + end)..'0000'):gsub('%d%d%d?%d?%d?%d?', function(x) + if (#x < 6) then return '' end + local c=0 + for i=1,6 do c=c+(x:sub(i,i)=='1' and 2^(6-i) or 0) end + return b:sub(c+1,c+1) + end)..({ '', '==', '=' })[#data%3+1]) +end + +-- decoding +function M.dec(data) + data = string.gsub(data, '[^'..b..'=]', '') + return (data:gsub('.', function(x) + if (x == '=') then return '' end + local r,f='',(b:find(x)-1) + for i=6,1,-1 do r=r..(f%2^i-f%2^(i-1)>0 and '1' or '0') end + return r; + end):gsub('%d%d%d?%d?%d?%d?%d?%d?', function(x) + if (#x ~= 8) then return '' end + local c=0 + for i=1,8 do c=c+(x:sub(i,i)=='1' and 2^(7-i) or 0) end + return string.char(c) + end)) +end + +return M From 3b37f0920cb41e19958dffe7eb6f218644d4709f Mon Sep 17 00:00:00 2001 From: devsaurus Date: Fri, 13 Mar 2015 22:22:57 +0100 Subject: [PATCH 19/40] res optional --- app/modules/u8g.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/modules/u8g.c b/app/modules/u8g.c index e2aff544..59cb20d9 100644 --- a/app/modules/u8g.c +++ b/app/modules/u8g.c @@ -1037,7 +1037,7 @@ static int lu8g_ssd1306_128x64_i2c( lua_State *L ) return 1; } -// Lua: object = u8g.ssd1306_128x64_spi( cs, dc ) +// Lua: object = u8g.ssd1306_128x64_spi( cs, dc, [res] ) static int lu8g_ssd1306_128x64_spi( lua_State *L ) { unsigned cs = luaL_checkinteger( L, 1 ); @@ -1046,6 +1046,7 @@ static int lu8g_ssd1306_128x64_spi( lua_State *L ) unsigned dc = luaL_checkinteger( L, 2 ); if (dc == 0) return luaL_error( L, "D/C pin required" ); + unsigned res = luaL_optinteger( L, 3, U8G_PIN_NONE ); lu8g_userdata_t *lud = (lu8g_userdata_t *) lua_newuserdata( L, sizeof( lu8g_userdata_t ) ); @@ -1069,9 +1070,9 @@ static int lu8g_ssd1306_128x64_spi( lua_State *L ) return luaL_error( L, "out of memory" ); // and finally init device using specific interface init function - u8g_InitHWSPI( LU8G, &(lud->dev), cs, dc, U8G_PIN_NONE ); + u8g_InitHWSPI( LU8G, &(lud->dev), cs, dc, res ); #else - u8g_InitHWSPI( LU8G, &u8g_dev_ssd1306_128x64_spi, cs, dc, U8G_PIN_NONE ); + u8g_InitHWSPI( LU8G, &u8g_dev_ssd1306_128x64_spi, cs, dc, res ); #endif From be4bb14e7c4e472565ce507ac412035903dd0fde Mon Sep 17 00:00:00 2001 From: AllAboutEE Date: Fri, 13 Mar 2015 20:51:19 -0500 Subject: [PATCH 20/40] Added comments to send_email_smtp and removed an unecessary variable. Fixed mispelling in comment in read_email_imap.lua --- lua_examples/email/read_email_imap.lua | 2 +- lua_examples/email/send_email_smtp.lua | 22 +++++++++++++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/lua_examples/email/read_email_imap.lua b/lua_examples/email/read_email_imap.lua index 02289aec..13aa7db6 100644 --- a/lua_examples/email/read_email_imap.lua +++ b/lua_examples/email/read_email_imap.lua @@ -2,7 +2,7 @@ -- @author Miguel (AllAboutEE.com) -- @description This example will read the first email in your inbox using IMAP and -- display it through serial. The email server must provided unecrypted access. The code --- was tested with an AOL and Time Waraner cable email ccounts (GMail and other services who do +-- was tested with an AOL and Time Warner cable email accounts (GMail and other services who do -- not support no SSL access will not work). require("imap") diff --git a/lua_examples/email/send_email_smtp.lua b/lua_examples/email/send_email_smtp.lua index 637cd638..1c79b6e5 100644 --- a/lua_examples/email/send_email_smtp.lua +++ b/lua_examples/email/send_email_smtp.lua @@ -1,31 +1,42 @@ require("base64") +-- The email and password from the account you want to send emails from local MY_EMAIL = "esp8266@domain.com" local EMAIL_PASSWORD = "123456" +-- The SMTP server and port of your email provider. +-- If you don't know it google [my email provider] SMTP settings local SMTP_SERVER = "smtp.server.com" local SMTP_PORT = "587" +-- The account you want to send email to local mail_to = "to_email@domain.com" +-- Your access point's SSID and password local SSID = "ssid" local SSID_PASSWORD = "password" +-- configure ESP as a station wifi.setmode(wifi.STATION) wifi.sta.config(SSID,SSID_PASSWORD) wifi.sta.autoconnect(1) +-- These are global variables. Don't change their values +-- they will be changed in the functions below local email_subject = "" local email_body = "" -local receive_processed = false local count = 0 +-- create a socket to the SMTP server local smtp_socket = net.createConnection(net.TCP,0) +-- The display() function will be used to print the SMTP server's response function display(sck,response) print(response) end +-- The do_next() function is used to send the SMTP commands to the SMTP server in the required sequence. +-- I was going to use socket callbacks but the code would not run callbacks after the first 3. function do_next() if(count == 0)then count = count+1 @@ -67,10 +78,18 @@ function do_next() end end +-- The connectted() function is executed when the SMTP socket is connected to the SMTP server. +-- This function will create a timer to call the do_next function which will send the SMTP commands +-- in sequence, one by one, every 5000 seconds. +-- You can change the time to be smaller if that works for you, I used 5000ms just because. function connected(sck) tmr.alarm(0,5000,1,do_next) end +-- @name send_email +-- @description Will initiated a socket connection to the SMTP server and trigger the connected() function +-- @param subject The email's subject +-- @param body The email's body function send_email(subject,body) email_subject = subject email_body = body @@ -79,6 +98,7 @@ function send_email(subject,body) smtp_socket:connect(SMTP_PORT,SMTP_SERVER) end +-- Send an email send_email( "ESP8266", [[Hi, From 8e7e5310224ea3a00bdfd4654d34bca0c1f8bbb8 Mon Sep 17 00:00:00 2001 From: devsaurus Date: Sun, 15 Mar 2015 22:08:35 +0100 Subject: [PATCH 21/40] update readme --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index ab78ed81..987c2891 100644 --- a/README.md +++ b/README.md @@ -375,12 +375,12 @@ i2c.setup(0, sda, scl, i2c.SLOW) The HSPI module is used, so certain pins are fixed: * HSPI CLK = GPIO14 * HSPI MOSI = GPIO13 -* HPSI MISO = GPIO12 (not used) +* HSPI MISO = GPIO12 (not used) All other pins can be assigned to any available GPIO: * CS * D/C -* RES (optional) +* RES (optional for some displays) Also refer to the initialization sequence eg in `lua_examples/u8glib/graphics_test.lua`: ```lua @@ -407,7 +407,7 @@ This object provides all of u8glib's methods to control the display. Again, refer to `lua_examples/u8glib/graphics_test.lua` to get an impression how this is achieved with Lua code. Visit the [u8glib homepage](https://code.google.com/p/u8glib/) for technical details. #####Fonts -u8glib comes with a wide range of fonts for small displays. Since they need to be compiled into the firmware image, you'd need to include them in `app/include/user_config.h` and recompile. Simply add the desired fonts to the font table: +u8glib comes with a wide range of fonts for small displays. Since they need to be compiled into the firmware image, you'd need to include them in `app/include/u8g_config.h` and recompile. Simply add the desired fonts to the font table: ```c #define U8G_FONT_TABLE \ U8G_FONT_TABLE_ENTRY(font_6x10) \ @@ -463,4 +463,4 @@ cs:func("myfun") -- post coap://192.168.18.103:5683/v1/f/myfun will call myfun cc = coap.Client() cc:get(coap.CON, "coap://192.168.18.100:5683/.well-known/core") cc:post(coap.NON, "coap://192.168.18.100:5683/", "Hello") -``` \ No newline at end of file +``` From 9c0c960e848c2e4c3765ea35f63523be0c55f3a2 Mon Sep 17 00:00:00 2001 From: funshine Date: Mon, 16 Mar 2015 09:00:33 +0800 Subject: [PATCH 22/40] json port from cjson. not work yet --- app/Makefile | 4 +- app/cjson | 1 + app/include/user_modules.h | 3 +- app/modules/Makefile | 1 + app/modules/auxmods.h | 3 + app/modules/cjson.c | 1530 ++++++++++++++++++++++++++++++++++++ app/modules/file.c | 2 +- app/modules/modules.h | 10 +- 8 files changed, 1550 insertions(+), 4 deletions(-) create mode 160000 app/cjson create mode 100644 app/modules/cjson.c diff --git a/app/Makefile b/app/Makefile index 78f95518..4d04d7f5 100644 --- a/app/Makefile +++ b/app/Makefile @@ -36,7 +36,8 @@ SUBDIRS= \ smart \ wofs \ modules \ - spiffs + spiffs \ + cjson endif # } PDIR @@ -84,6 +85,7 @@ COMPONENTS_eagle.app.v6 = \ smart/smart.a \ wofs/wofs.a \ spiffs/spiffs.a \ + cjson/libcjson.a \ modules/libmodules.a LINKFLAGS_eagle.app.v6 = \ diff --git a/app/cjson b/app/cjson new file mode 160000 index 00000000..d59326b2 --- /dev/null +++ b/app/cjson @@ -0,0 +1 @@ +Subproject commit d59326b2d718e1a140b9b396ffe0a557b2d93fe0 diff --git a/app/include/user_modules.h b/app/include/user_modules.h index 103b004f..e72d6587 100644 --- a/app/include/user_modules.h +++ b/app/include/user_modules.h @@ -4,7 +4,7 @@ #define LUA_USE_BUILTIN_STRING // for string.xxx() #define LUA_USE_BUILTIN_TABLE // for table.xxx() #define LUA_USE_BUILTIN_COROUTINE // for coroutine.xxx() -#define LUA_USE_BUILTIN_MATH // for math.xxx(), partially work +// #define LUA_USE_BUILTIN_MATH // for math.xxx(), partially work // #define LUA_USE_BUILTIN_IO // for io.xxx(), partially work // #define LUA_USE_BUILTIN_OS // for os.xxx(), not work @@ -30,6 +30,7 @@ #define LUA_USE_MODULES_COAP #define LUA_USE_MODULES_U8G #define LUA_USE_MODULES_WS2812 +#define LUA_USE_MODULES_CJSON #endif /* LUA_USE_MODULES */ #endif /* __USER_MODULES_H__ */ diff --git a/app/modules/Makefile b/app/modules/Makefile index 7d2ae1cc..499b9314 100644 --- a/app/modules/Makefile +++ b/app/modules/Makefile @@ -47,6 +47,7 @@ INCLUDES += -I ../platform INCLUDES += -I ../wofs INCLUDES += -I ../spiffs INCLUDES += -I ../smart +INCLUDES += -I ../cjson PDIR := ../$(PDIR) sinclude $(PDIR)Makefile diff --git a/app/modules/auxmods.h b/app/modules/auxmods.h index b64a05a4..b251ddac 100644 --- a/app/modules/auxmods.h +++ b/app/modules/auxmods.h @@ -79,6 +79,9 @@ LUALIB_API int ( luaopen_file )( lua_State *L ); #define AUXLIB_OW "ow" LUALIB_API int ( luaopen_ow )( lua_State *L ); +#define AUXLIB_CJSON "cjson" +LUALIB_API int ( luaopen_ow )( lua_State *L ); + // Helper macros #define MOD_CHECK_ID( mod, id )\ if( !platform_ ## mod ## _exists( id ) )\ diff --git a/app/modules/cjson.c b/app/modules/cjson.c new file mode 100644 index 00000000..78bf2e33 --- /dev/null +++ b/app/modules/cjson.c @@ -0,0 +1,1530 @@ +/* Lua CJSON - JSON support for Lua + * + * Copyright (c) 2010-2012 Mark Pulford + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* Caveats: + * - JSON "null" values are represented as lightuserdata since Lua + * tables cannot contain "nil". Compare with cjson.null. + * - Invalid UTF-8 characters are not detected and will be passed + * untouched. If required, UTF-8 error checking should be done + * outside this library. + * - Javascript comments are not part of the JSON spec, and are not + * currently supported. + * + * Note: Decoding is slower than encoding. Lua spends significant + * time (30%) managing tables when parsing JSON since it is + * difficult to know object/array sizes ahead of time. + */ + +#include +#include "c_string.h" +#include "c_math.h" +#include "c_limits.h" +#include "lua.h" +#include "lauxlib.h" + +#include "strbuf.h" +#include "fpconv.h" + +#ifndef CJSON_MODNAME +#define CJSON_MODNAME "cjson" +#endif + +#ifndef CJSON_VERSION +#define CJSON_VERSION "2.1devel" +#endif + +/* Workaround for Solaris platforms missing isinf() */ +#if !defined(isinf) && (defined(USE_INTERNAL_ISINF) || defined(MISSING_ISINF)) +#define isinf(x) (!isnan(x) && isnan((x) - (x))) +#endif + +#define DEFAULT_SPARSE_CONVERT 0 +#define DEFAULT_SPARSE_RATIO 2 +#define DEFAULT_SPARSE_SAFE 10 +#define DEFAULT_ENCODE_MAX_DEPTH 1000 +#define DEFAULT_DECODE_MAX_DEPTH 1000 +#define DEFAULT_ENCODE_INVALID_NUMBERS 0 +#define DEFAULT_DECODE_INVALID_NUMBERS 1 +#define DEFAULT_ENCODE_KEEP_BUFFER 1 +#define DEFAULT_ENCODE_NUMBER_PRECISION 14 + +#ifdef DISABLE_INVALID_NUMBERS +#undef DEFAULT_DECODE_INVALID_NUMBERS +#define DEFAULT_DECODE_INVALID_NUMBERS 0 +#endif + +typedef enum { + T_OBJ_BEGIN, + T_OBJ_END, + T_ARR_BEGIN, + T_ARR_END, + T_STRING, + T_NUMBER, + T_BOOLEAN, + T_NULL, + T_COLON, + T_COMMA, + T_END, + T_WHITESPACE, + T_ERROR, + T_UNKNOWN +} json_token_type_t; + +static const char *json_token_type_name[] = { + "T_OBJ_BEGIN", + "T_OBJ_END", + "T_ARR_BEGIN", + "T_ARR_END", + "T_STRING", + "T_NUMBER", + "T_BOOLEAN", + "T_NULL", + "T_COLON", + "T_COMMA", + "T_END", + "T_WHITESPACE", + "T_ERROR", + "T_UNKNOWN", + NULL +}; + +typedef struct { + json_token_type_t ch2token[256]; + char escape2char[256]; /* Decoding */ + + /* encode_buf is only allocated and used when + * encode_keep_buffer is set */ + strbuf_t encode_buf; + + int encode_sparse_convert; + int encode_sparse_ratio; + int encode_sparse_safe; + int encode_max_depth; + int encode_invalid_numbers; /* 2 => Encode as "null" */ + int encode_number_precision; + int encode_keep_buffer; + + int decode_invalid_numbers; + int decode_max_depth; +} json_config_t; + +typedef struct { + const char *data; + const char *ptr; + strbuf_t *tmp; /* Temporary storage for strings */ + json_config_t *cfg; + int current_depth; +} json_parse_t; + +typedef struct { + json_token_type_t type; + int index; + union { + const char *string; + double number; + int boolean; + } value; + int string_len; +} json_token_t; + +static const char *char2escape[256] = { + "\\u0000", "\\u0001", "\\u0002", "\\u0003", + "\\u0004", "\\u0005", "\\u0006", "\\u0007", + "\\b", "\\t", "\\n", "\\u000b", + "\\f", "\\r", "\\u000e", "\\u000f", + "\\u0010", "\\u0011", "\\u0012", "\\u0013", + "\\u0014", "\\u0015", "\\u0016", "\\u0017", + "\\u0018", "\\u0019", "\\u001a", "\\u001b", + "\\u001c", "\\u001d", "\\u001e", "\\u001f", + NULL, NULL, "\\\"", NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, "\\/", + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, "\\\\", NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, "\\u007f", + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +}; + +/* ===== CONFIGURATION ===== */ +#if 0 +static json_config_t *json_fetch_config(lua_State *l) +{ + json_config_t *cfg; + + cfg = lua_touserdata(l, lua_upvalueindex(1)); + if (!cfg) + luaL_error(l, "BUG: Unable to fetch CJSON configuration"); + + return cfg; +} + +/* Ensure the correct number of arguments have been provided. + * Pad with nil to allow other functions to simply check arg[i] + * to find whether an argument was provided */ +static json_config_t *json_arg_init(lua_State *l, int args) +{ + luaL_argcheck(l, lua_gettop(l) <= args, args + 1, + "found too many arguments"); + + while (lua_gettop(l) < args) + lua_pushnil(l); + + return json_fetch_config(l); +} + +/* Process integer options for configuration functions */ +static int json_integer_option(lua_State *l, int optindex, int *setting, + int min, int max) +{ + char errmsg[64]; + int value; + + if (!lua_isnil(l, optindex)) { + value = luaL_checkinteger(l, optindex); + c_sprintf(errmsg, "expected integer between %d and %d", min, max); + luaL_argcheck(l, min <= value && value <= max, 1, errmsg); + *setting = value; + } + + lua_pushinteger(l, *setting); + + return 1; +} + +/* Process enumerated arguments for a configuration function */ +static int json_enum_option(lua_State *l, int optindex, int *setting, + const char **options, int bool_true) +{ + static const char *bool_options[] = { "off", "on", NULL }; + + if (!options) { + options = bool_options; + bool_true = 1; + } + + if (!lua_isnil(l, optindex)) { + if (bool_true && lua_isboolean(l, optindex)) + *setting = lua_toboolean(l, optindex) * bool_true; + else + *setting = luaL_checkoption(l, optindex, NULL, options); + } + + if (bool_true && (*setting == 0 || *setting == bool_true)) + lua_pushboolean(l, *setting); + else + lua_pushstring(l, options[*setting]); + + return 1; +} + +/* Configures handling of extremely sparse arrays: + * convert: Convert extremely sparse arrays into objects? Otherwise error. + * ratio: 0: always allow sparse; 1: never allow sparse; >1: use ratio + * safe: Always use an array when the max index <= safe */ +static int json_cfg_encode_sparse_array(lua_State *l) +{ + json_config_t *cfg = json_arg_init(l, 3); + + json_enum_option(l, 1, &cfg->encode_sparse_convert, NULL, 1); + json_integer_option(l, 2, &cfg->encode_sparse_ratio, 0, INT_MAX); + json_integer_option(l, 3, &cfg->encode_sparse_safe, 0, INT_MAX); + + return 3; +} + +/* Configures the maximum number of nested arrays/objects allowed when + * encoding */ +static int json_cfg_encode_max_depth(lua_State *l) +{ + json_config_t *cfg = json_arg_init(l, 1); + + return json_integer_option(l, 1, &cfg->encode_max_depth, 1, INT_MAX); +} + +/* Configures the maximum number of nested arrays/objects allowed when + * encoding */ +static int json_cfg_decode_max_depth(lua_State *l) +{ + json_config_t *cfg = json_arg_init(l, 1); + + return json_integer_option(l, 1, &cfg->decode_max_depth, 1, INT_MAX); +} + +/* Configures number precision when converting doubles to text */ +static int json_cfg_encode_number_precision(lua_State *l) +{ + json_config_t *cfg = json_arg_init(l, 1); + + return json_integer_option(l, 1, &cfg->encode_number_precision, 1, 14); +} + +/* Configures JSON encoding buffer persistence */ +static int json_cfg_encode_keep_buffer(lua_State *l) +{ + json_config_t *cfg = json_arg_init(l, 1); + int old_value; + + old_value = cfg->encode_keep_buffer; + + json_enum_option(l, 1, &cfg->encode_keep_buffer, NULL, 1); + + /* Init / free the buffer if the setting has changed */ + if (old_value ^ cfg->encode_keep_buffer) { + if (cfg->encode_keep_buffer) + strbuf_init(&cfg->encode_buf, 0); + else + strbuf_free(&cfg->encode_buf); + } + + return 1; +} + +#if defined(DISABLE_INVALID_NUMBERS) && !defined(USE_INTERNAL_FPCONV) +void json_verify_invalid_number_setting(lua_State *l, int *setting) +{ + if (*setting == 1) { + *setting = 0; + luaL_error(l, "Infinity, NaN, and/or hexadecimal numbers are not supported."); + } +} +#else +#define json_verify_invalid_number_setting(l, s) do { } while(0) +#endif + +static int json_cfg_encode_invalid_numbers(lua_State *l) +{ + static const char *options[] = { "off", "on", "null", NULL }; + json_config_t *cfg = json_arg_init(l, 1); + + json_enum_option(l, 1, &cfg->encode_invalid_numbers, options, 1); + + json_verify_invalid_number_setting(l, &cfg->encode_invalid_numbers); + + return 1; +} + +static int json_cfg_decode_invalid_numbers(lua_State *l) +{ + json_config_t *cfg = json_arg_init(l, 1); + + json_enum_option(l, 1, &cfg->decode_invalid_numbers, NULL, 1); + + json_verify_invalid_number_setting(l, &cfg->encode_invalid_numbers); + + return 1; +} + +static int json_destroy_config(lua_State *l) +{ + json_config_t *cfg; + + cfg = lua_touserdata(l, 1); + if (cfg) + strbuf_free(&cfg->encode_buf); + cfg = NULL; + + return 0; +} + +static void json_create_config(lua_State *l) +{ + json_config_t *cfg; + int i; + + cfg = lua_newuserdata(l, sizeof(*cfg)); + + /* Create GC method to clean up strbuf */ + lua_newtable(l); + lua_pushcfunction(l, json_destroy_config); + lua_setfield(l, -2, "__gc"); + lua_setmetatable(l, -2); + + cfg->encode_sparse_convert = DEFAULT_SPARSE_CONVERT; + cfg->encode_sparse_ratio = DEFAULT_SPARSE_RATIO; + cfg->encode_sparse_safe = DEFAULT_SPARSE_SAFE; + cfg->encode_max_depth = DEFAULT_ENCODE_MAX_DEPTH; + cfg->decode_max_depth = DEFAULT_DECODE_MAX_DEPTH; + cfg->encode_invalid_numbers = DEFAULT_ENCODE_INVALID_NUMBERS; + cfg->decode_invalid_numbers = DEFAULT_DECODE_INVALID_NUMBERS; + cfg->encode_keep_buffer = DEFAULT_ENCODE_KEEP_BUFFER; + cfg->encode_number_precision = DEFAULT_ENCODE_NUMBER_PRECISION; + +#if DEFAULT_ENCODE_KEEP_BUFFER > 0 + strbuf_init(&cfg->encode_buf, 0); +#endif + + /* Decoding init */ + + /* Tag all characters as an error */ + for (i = 0; i < 256; i++) + cfg->ch2token[i] = T_ERROR; + + /* Set tokens that require no further processing */ + cfg->ch2token['{'] = T_OBJ_BEGIN; + cfg->ch2token['}'] = T_OBJ_END; + cfg->ch2token['['] = T_ARR_BEGIN; + cfg->ch2token[']'] = T_ARR_END; + cfg->ch2token[','] = T_COMMA; + cfg->ch2token[':'] = T_COLON; + cfg->ch2token['\0'] = T_END; + cfg->ch2token[' '] = T_WHITESPACE; + cfg->ch2token['\t'] = T_WHITESPACE; + cfg->ch2token['\n'] = T_WHITESPACE; + cfg->ch2token['\r'] = T_WHITESPACE; + + /* Update characters that require further processing */ + cfg->ch2token['f'] = T_UNKNOWN; /* false? */ + cfg->ch2token['i'] = T_UNKNOWN; /* inf, ininity? */ + cfg->ch2token['I'] = T_UNKNOWN; + cfg->ch2token['n'] = T_UNKNOWN; /* null, nan? */ + cfg->ch2token['N'] = T_UNKNOWN; + cfg->ch2token['t'] = T_UNKNOWN; /* true? */ + cfg->ch2token['"'] = T_UNKNOWN; /* string? */ + cfg->ch2token['+'] = T_UNKNOWN; /* number? */ + cfg->ch2token['-'] = T_UNKNOWN; + for (i = 0; i < 10; i++) + cfg->ch2token['0' + i] = T_UNKNOWN; + + /* Lookup table for parsing escape characters */ + for (i = 0; i < 256; i++) + cfg->escape2char[i] = 0; /* String error */ + cfg->escape2char['"'] = '"'; + cfg->escape2char['\\'] = '\\'; + cfg->escape2char['/'] = '/'; + cfg->escape2char['b'] = '\b'; + cfg->escape2char['t'] = '\t'; + cfg->escape2char['n'] = '\n'; + cfg->escape2char['f'] = '\f'; + cfg->escape2char['r'] = '\r'; + cfg->escape2char['u'] = 'u'; /* Unicode parsing required */ +} +#endif + +json_config_t _cfg; + +static json_config_t *json_fetch_config(lua_State *l) +{ + return &_cfg; +} + +static void cfg_init(json_config_t *cfg){ + int i; + + cfg->encode_sparse_convert = DEFAULT_SPARSE_CONVERT; + cfg->encode_sparse_ratio = DEFAULT_SPARSE_RATIO; + cfg->encode_sparse_safe = DEFAULT_SPARSE_SAFE; + cfg->encode_max_depth = DEFAULT_ENCODE_MAX_DEPTH; + cfg->decode_max_depth = DEFAULT_DECODE_MAX_DEPTH; + cfg->encode_invalid_numbers = DEFAULT_ENCODE_INVALID_NUMBERS; + cfg->decode_invalid_numbers = DEFAULT_DECODE_INVALID_NUMBERS; + cfg->encode_keep_buffer = DEFAULT_ENCODE_KEEP_BUFFER; + cfg->encode_number_precision = DEFAULT_ENCODE_NUMBER_PRECISION; + +#if DEFAULT_ENCODE_KEEP_BUFFER > 0 + strbuf_init(&cfg->encode_buf, 0); +#endif + + /* Decoding init */ + + /* Tag all characters as an error */ + for (i = 0; i < 256; i++) + cfg->ch2token[i] = T_ERROR; + + /* Set tokens that require no further processing */ + cfg->ch2token['{'] = T_OBJ_BEGIN; + cfg->ch2token['}'] = T_OBJ_END; + cfg->ch2token['['] = T_ARR_BEGIN; + cfg->ch2token[']'] = T_ARR_END; + cfg->ch2token[','] = T_COMMA; + cfg->ch2token[':'] = T_COLON; + cfg->ch2token['\0'] = T_END; + cfg->ch2token[' '] = T_WHITESPACE; + cfg->ch2token['\t'] = T_WHITESPACE; + cfg->ch2token['\n'] = T_WHITESPACE; + cfg->ch2token['\r'] = T_WHITESPACE; + + /* Update characters that require further processing */ + cfg->ch2token['f'] = T_UNKNOWN; /* false? */ + cfg->ch2token['i'] = T_UNKNOWN; /* inf, ininity? */ + cfg->ch2token['I'] = T_UNKNOWN; + cfg->ch2token['n'] = T_UNKNOWN; /* null, nan? */ + cfg->ch2token['N'] = T_UNKNOWN; + cfg->ch2token['t'] = T_UNKNOWN; /* true? */ + cfg->ch2token['"'] = T_UNKNOWN; /* string? */ + cfg->ch2token['+'] = T_UNKNOWN; /* number? */ + cfg->ch2token['-'] = T_UNKNOWN; + for (i = 0; i < 10; i++) + cfg->ch2token['0' + i] = T_UNKNOWN; + + /* Lookup table for parsing escape characters */ + for (i = 0; i < 256; i++) + cfg->escape2char[i] = 0; /* String error */ + cfg->escape2char['"'] = '"'; + cfg->escape2char['\\'] = '\\'; + cfg->escape2char['/'] = '/'; + cfg->escape2char['b'] = '\b'; + cfg->escape2char['t'] = '\t'; + cfg->escape2char['n'] = '\n'; + cfg->escape2char['f'] = '\f'; + cfg->escape2char['r'] = '\r'; + cfg->escape2char['u'] = 'u'; /* Unicode parsing required */ +} +/* ===== ENCODING ===== */ + +static void json_encode_exception(lua_State *l, json_config_t *cfg, strbuf_t *json, int lindex, + const char *reason) +{ + if (!cfg->encode_keep_buffer) + strbuf_free(json); + luaL_error(l, "Cannot serialise %s: %s", + lua_typename(l, lua_type(l, lindex)), reason); +} + +/* json_append_string args: + * - lua_State + * - JSON strbuf + * - String (Lua stack index) + * + * Returns nothing. Doesn't remove string from Lua stack */ +static void json_append_string(lua_State *l, strbuf_t *json, int lindex) +{ + const char *escstr; + int i; + const char *str; + size_t len; + + str = lua_tolstring(l, lindex, &len); + + /* Worst case is len * 6 (all unicode escapes). + * This buffer is reused constantly for small strings + * If there are any excess pages, they won't be hit anyway. + * This gains ~5% speedup. */ + strbuf_ensure_empty_length(json, len * 6 + 2); + + strbuf_append_char_unsafe(json, '\"'); + for (i = 0; i < len; i++) { + escstr = char2escape[(unsigned char)str[i]]; + if (escstr) + strbuf_append_string(json, escstr); + else + strbuf_append_char_unsafe(json, str[i]); + } + strbuf_append_char_unsafe(json, '\"'); +} + +/* Find the size of the array on the top of the Lua stack + * -1 object (not a pure array) + * >=0 elements in array + */ +static int lua_array_length(lua_State *l, json_config_t *cfg, strbuf_t *json) +{ + double k; + int max; + int items; + + max = 0; + items = 0; + + lua_pushnil(l); + /* table, startkey */ + while (lua_next(l, -2) != 0) { + /* table, key, value */ + if (lua_type(l, -2) == LUA_TNUMBER && + (k = lua_tonumber(l, -2))) { + /* Integer >= 1 ? */ + if (floor(k) == k && k >= 1) { + if (k > max) + max = k; + items++; + lua_pop(l, 1); + continue; + } + } + + /* Must not be an array (non integer key) */ + lua_pop(l, 2); + return -1; + } + + /* Encode excessively sparse arrays as objects (if enabled) */ + if (cfg->encode_sparse_ratio > 0 && + max > items * cfg->encode_sparse_ratio && + max > cfg->encode_sparse_safe) { + if (!cfg->encode_sparse_convert) + json_encode_exception(l, cfg, json, -1, "excessively sparse array"); + + return -1; + } + + return max; +} + +static void json_check_encode_depth(lua_State *l, json_config_t *cfg, + int current_depth, strbuf_t *json) +{ + /* Ensure there are enough slots free to traverse a table (key, + * value) and push a string for a potential error message. + * + * Unlike "decode", the key and value are still on the stack when + * lua_checkstack() is called. Hence an extra slot for luaL_error() + * below is required just in case the next check to lua_checkstack() + * fails. + * + * While this won't cause a crash due to the EXTRA_STACK reserve + * slots, it would still be an improper use of the API. */ + if (current_depth <= cfg->encode_max_depth && lua_checkstack(l, 3)) + return; + + if (!cfg->encode_keep_buffer) + strbuf_free(json); + + luaL_error(l, "Cannot serialise, excessive nesting (%d)", + current_depth); +} + +static void json_append_data(lua_State *l, json_config_t *cfg, + int current_depth, strbuf_t *json); + +/* json_append_array args: + * - lua_State + * - JSON strbuf + * - Size of passwd Lua array (top of stack) */ +static void json_append_array(lua_State *l, json_config_t *cfg, int current_depth, + strbuf_t *json, int array_length) +{ + int comma, i; + + strbuf_append_char(json, '['); + + comma = 0; + for (i = 1; i <= array_length; i++) { + if (comma) + strbuf_append_char(json, ','); + else + comma = 1; + + lua_rawgeti(l, -1, i); + json_append_data(l, cfg, current_depth, json); + lua_pop(l, 1); + } + + strbuf_append_char(json, ']'); +} + +static void json_append_number(lua_State *l, json_config_t *cfg, + strbuf_t *json, int lindex) +{ + double num = lua_tonumber(l, lindex); + int len; + + if (cfg->encode_invalid_numbers == 0) { + /* Prevent encoding invalid numbers */ + if (isinf(num) || isnan(num)) + json_encode_exception(l, cfg, json, lindex, + "must not be NaN or Infinity"); + } else if (cfg->encode_invalid_numbers == 1) { + /* Encode NaN/Infinity separately to ensure Javascript compatible + * values are used. */ + if (isnan(num)) { + strbuf_append_mem(json, "NaN", 3); + return; + } + if (isinf(num)) { + if (num < 0) + strbuf_append_mem(json, "-Infinity", 9); + else + strbuf_append_mem(json, "Infinity", 8); + return; + } + } else { + /* Encode invalid numbers as "null" */ + if (isinf(num) || isnan(num)) { + strbuf_append_mem(json, "null", 4); + return; + } + } + + strbuf_ensure_empty_length(json, FPCONV_G_FMT_BUFSIZE); + len = fpconv_g_fmt(strbuf_empty_ptr(json), num, cfg->encode_number_precision); + strbuf_extend_length(json, len); +} + +static void json_append_object(lua_State *l, json_config_t *cfg, + int current_depth, strbuf_t *json) +{ + int comma, keytype; + + /* Object */ + strbuf_append_char(json, '{'); + + lua_pushnil(l); + /* table, startkey */ + comma = 0; + while (lua_next(l, -2) != 0) { + if (comma) + strbuf_append_char(json, ','); + else + comma = 1; + + /* table, key, value */ + keytype = lua_type(l, -2); + if (keytype == LUA_TNUMBER) { + strbuf_append_char(json, '"'); + json_append_number(l, cfg, json, -2); + strbuf_append_mem(json, "\":", 2); + } else if (keytype == LUA_TSTRING) { + json_append_string(l, json, -2); + strbuf_append_char(json, ':'); + } else { + json_encode_exception(l, cfg, json, -2, + "table key must be a number or string"); + /* never returns */ + } + + /* table, key, value */ + json_append_data(l, cfg, current_depth, json); + lua_pop(l, 1); + /* table, key */ + } + + strbuf_append_char(json, '}'); +} + +/* Serialise Lua data into JSON string. */ +static void json_append_data(lua_State *l, json_config_t *cfg, + int current_depth, strbuf_t *json) +{ + int len; + + switch (lua_type(l, -1)) { + case LUA_TSTRING: + json_append_string(l, json, -1); + break; + case LUA_TNUMBER: + json_append_number(l, cfg, json, -1); + break; + case LUA_TBOOLEAN: + if (lua_toboolean(l, -1)) + strbuf_append_mem(json, "true", 4); + else + strbuf_append_mem(json, "false", 5); + break; + case LUA_TTABLE: + current_depth++; + json_check_encode_depth(l, cfg, current_depth, json); + len = lua_array_length(l, cfg, json); + if (len > 0) + json_append_array(l, cfg, current_depth, json, len); + else + json_append_object(l, cfg, current_depth, json); + break; + case LUA_TNIL: + strbuf_append_mem(json, "null", 4); + break; + case LUA_TLIGHTUSERDATA: + if (lua_touserdata(l, -1) == NULL) { + strbuf_append_mem(json, "null", 4); + break; + } + default: + /* Remaining types (LUA_TFUNCTION, LUA_TUSERDATA, LUA_TTHREAD, + * and LUA_TLIGHTUSERDATA) cannot be serialised */ + json_encode_exception(l, cfg, json, -1, "type not supported"); + /* never returns */ + } +} + +static int json_encode(lua_State *l) +{ + json_config_t *cfg = json_fetch_config(l); + strbuf_t local_encode_buf; + strbuf_t *encode_buf; + char *json; + int len; + + luaL_argcheck(l, lua_gettop(l) == 1, 1, "expected 1 argument"); + + if (!cfg->encode_keep_buffer) { + /* Use private buffer */ + encode_buf = &local_encode_buf; + strbuf_init(encode_buf, 0); + } else { + /* Reuse existing buffer */ + encode_buf = &cfg->encode_buf; + strbuf_reset(encode_buf); + } + + json_append_data(l, cfg, 0, encode_buf); + json = strbuf_string(encode_buf, &len); + + lua_pushlstring(l, json, len); + + if (!cfg->encode_keep_buffer) + strbuf_free(encode_buf); + + return 1; +} + +/* ===== DECODING ===== */ + +static void json_process_value(lua_State *l, json_parse_t *json, + json_token_t *token); + +static int hexdigit2int(char hex) +{ + if ('0' <= hex && hex <= '9') + return hex - '0'; + + /* Force lowercase */ + hex |= 0x20; + if ('a' <= hex && hex <= 'f') + return 10 + hex - 'a'; + + return -1; +} + +static int decode_hex4(const char *hex) +{ + int digit[4]; + int i; + + /* Convert ASCII hex digit to numeric digit + * Note: this returns an error for invalid hex digits, including + * NULL */ + for (i = 0; i < 4; i++) { + digit[i] = hexdigit2int(hex[i]); + if (digit[i] < 0) { + return -1; + } + } + + return (digit[0] << 12) + + (digit[1] << 8) + + (digit[2] << 4) + + digit[3]; +} + +/* Converts a Unicode codepoint to UTF-8. + * Returns UTF-8 string length, and up to 4 bytes in *utf8 */ +static int codepoint_to_utf8(char *utf8, int codepoint) +{ + /* 0xxxxxxx */ + if (codepoint <= 0x7F) { + utf8[0] = codepoint; + return 1; + } + + /* 110xxxxx 10xxxxxx */ + if (codepoint <= 0x7FF) { + utf8[0] = (codepoint >> 6) | 0xC0; + utf8[1] = (codepoint & 0x3F) | 0x80; + return 2; + } + + /* 1110xxxx 10xxxxxx 10xxxxxx */ + if (codepoint <= 0xFFFF) { + utf8[0] = (codepoint >> 12) | 0xE0; + utf8[1] = ((codepoint >> 6) & 0x3F) | 0x80; + utf8[2] = (codepoint & 0x3F) | 0x80; + return 3; + } + + /* 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ + if (codepoint <= 0x1FFFFF) { + utf8[0] = (codepoint >> 18) | 0xF0; + utf8[1] = ((codepoint >> 12) & 0x3F) | 0x80; + utf8[2] = ((codepoint >> 6) & 0x3F) | 0x80; + utf8[3] = (codepoint & 0x3F) | 0x80; + return 4; + } + + return 0; +} + + +/* Called when index pointing to beginning of UTF-16 code escape: \uXXXX + * \u is guaranteed to exist, but the remaining hex characters may be + * missing. + * Translate to UTF-8 and append to temporary token string. + * Must advance index to the next character to be processed. + * Returns: 0 success + * -1 error + */ +static int json_append_unicode_escape(json_parse_t *json) +{ + char utf8[4]; /* Surrogate pairs require 4 UTF-8 bytes */ + int codepoint; + int surrogate_low; + int len; + int escape_len = 6; + + /* Fetch UTF-16 code unit */ + codepoint = decode_hex4(json->ptr + 2); + if (codepoint < 0) + return -1; + + /* UTF-16 surrogate pairs take the following 2 byte form: + * 11011 x yyyyyyyyyy + * When x = 0: y is the high 10 bits of the codepoint + * x = 1: y is the low 10 bits of the codepoint + * + * Check for a surrogate pair (high or low) */ + if ((codepoint & 0xF800) == 0xD800) { + /* Error if the 1st surrogate is not high */ + if (codepoint & 0x400) + return -1; + + /* Ensure the next code is a unicode escape */ + if (*(json->ptr + escape_len) != '\\' || + *(json->ptr + escape_len + 1) != 'u') { + return -1; + } + + /* Fetch the next codepoint */ + surrogate_low = decode_hex4(json->ptr + 2 + escape_len); + if (surrogate_low < 0) + return -1; + + /* Error if the 2nd code is not a low surrogate */ + if ((surrogate_low & 0xFC00) != 0xDC00) + return -1; + + /* Calculate Unicode codepoint */ + codepoint = (codepoint & 0x3FF) << 10; + surrogate_low &= 0x3FF; + codepoint = (codepoint | surrogate_low) + 0x10000; + escape_len = 12; + } + + /* Convert codepoint to UTF-8 */ + len = codepoint_to_utf8(utf8, codepoint); + if (!len) + return -1; + + /* Append bytes and advance parse index */ + strbuf_append_mem_unsafe(json->tmp, utf8, len); + json->ptr += escape_len; + + return 0; +} + +static void json_set_token_error(json_token_t *token, json_parse_t *json, + const char *errtype) +{ + token->type = T_ERROR; + token->index = json->ptr - json->data; + token->value.string = errtype; +} + +static void json_next_string_token(json_parse_t *json, json_token_t *token) +{ + char *escape2char = json->cfg->escape2char; + char ch; + + /* Caller must ensure a string is next */ + assert(*json->ptr == '"'); + + /* Skip " */ + json->ptr++; + + /* json->tmp is the temporary strbuf used to accumulate the + * decoded string value. + * json->tmp is sized to handle JSON containing only a string value. + */ + strbuf_reset(json->tmp); + + while ((ch = *json->ptr) != '"') { + if (!ch) { + /* Premature end of the string */ + json_set_token_error(token, json, "unexpected end of string"); + return; + } + + /* Handle escapes */ + if (ch == '\\') { + /* Fetch escape character */ + ch = *(json->ptr + 1); + + /* Translate escape code and append to tmp string */ + ch = escape2char[(unsigned char)ch]; + if (ch == 'u') { + if (json_append_unicode_escape(json) == 0) + continue; + + json_set_token_error(token, json, + "invalid unicode escape code"); + return; + } + if (!ch) { + json_set_token_error(token, json, "invalid escape code"); + return; + } + + /* Skip '\' */ + json->ptr++; + } + /* Append normal character or translated single character + * Unicode escapes are handled above */ + strbuf_append_char_unsafe(json->tmp, ch); + json->ptr++; + } + json->ptr++; /* Eat final quote (") */ + + strbuf_ensure_null(json->tmp); + + token->type = T_STRING; + token->value.string = strbuf_string(json->tmp, &token->string_len); +} + +/* JSON numbers should take the following form: + * -?(0|[1-9]|[1-9][0-9]+)(.[0-9]+)?([eE][-+]?[0-9]+)? + * + * json_next_number_token() uses strtod() which allows other forms: + * - numbers starting with '+' + * - NaN, -NaN, infinity, -infinity + * - hexadecimal numbers + * - numbers with leading zeros + * + * json_is_invalid_number() detects "numbers" which may pass strtod()'s + * error checking, but should not be allowed with strict JSON. + * + * json_is_invalid_number() may pass numbers which cause strtod() + * to generate an error. + */ +static int json_is_invalid_number(json_parse_t *json) +{ + const char *p = json->ptr; + + /* Reject numbers starting with + */ + if (*p == '+') + return 1; + + /* Skip minus sign if it exists */ + if (*p == '-') + p++; + + /* Reject numbers starting with 0x, or leading zeros */ + if (*p == '0') { + int ch2 = *(p + 1); + + if ((ch2 | 0x20) == 'x' || /* Hex */ + ('0' <= ch2 && ch2 <= '9')) /* Leading zero */ + return 1; + + return 0; + } else if (*p <= '9') { + return 0; /* Ordinary number */ + } + + /* Reject inf/nan */ + if (!strncasecmp(p, "inf", 3)) + return 1; + if (!strncasecmp(p, "nan", 3)) + return 1; + + /* Pass all other numbers which may still be invalid, but + * strtod() will catch them. */ + return 0; +} + +static void json_next_number_token(json_parse_t *json, json_token_t *token) +{ + char *endptr; + + token->type = T_NUMBER; + token->value.number = fpconv_strtod(json->ptr, &endptr); + if (json->ptr == endptr) + json_set_token_error(token, json, "invalid number"); + else + json->ptr = endptr; /* Skip the processed number */ + + return; +} + +/* Fills in the token struct. + * T_STRING will return a pointer to the json_parse_t temporary string + * T_ERROR will leave the json->ptr pointer at the error. + */ +static void json_next_token(json_parse_t *json, json_token_t *token) +{ + const json_token_type_t *ch2token = json->cfg->ch2token; + int ch; + + /* Eat whitespace. */ + while (1) { + ch = (unsigned char)*(json->ptr); + token->type = ch2token[ch]; + if (token->type != T_WHITESPACE) + break; + json->ptr++; + } + + /* Store location of new token. Required when throwing errors + * for unexpected tokens (syntax errors). */ + token->index = json->ptr - json->data; + + /* Don't advance the pointer for an error or the end */ + if (token->type == T_ERROR) { + json_set_token_error(token, json, "invalid token"); + return; + } + + if (token->type == T_END) { + return; + } + + /* Found a known single character token, advance index and return */ + if (token->type != T_UNKNOWN) { + json->ptr++; + return; + } + + /* Process characters which triggered T_UNKNOWN + * + * Must use strncmp() to match the front of the JSON string. + * JSON identifier must be lowercase. + * When strict_numbers if disabled, either case is allowed for + * Infinity/NaN (since we are no longer following the spec..) */ + if (ch == '"') { + json_next_string_token(json, token); + return; + } else if (ch == '-' || ('0' <= ch && ch <= '9')) { + if (!json->cfg->decode_invalid_numbers && json_is_invalid_number(json)) { + json_set_token_error(token, json, "invalid number"); + return; + } + json_next_number_token(json, token); + return; + } else if (!strncmp(json->ptr, "true", 4)) { + token->type = T_BOOLEAN; + token->value.boolean = 1; + json->ptr += 4; + return; + } else if (!strncmp(json->ptr, "false", 5)) { + token->type = T_BOOLEAN; + token->value.boolean = 0; + json->ptr += 5; + return; + } else if (!strncmp(json->ptr, "null", 4)) { + token->type = T_NULL; + json->ptr += 4; + return; + } else if (json->cfg->decode_invalid_numbers && + json_is_invalid_number(json)) { + /* When decode_invalid_numbers is enabled, only attempt to process + * numbers we know are invalid JSON (Inf, NaN, hex) + * This is required to generate an appropriate token error, + * otherwise all bad tokens will register as "invalid number" + */ + json_next_number_token(json, token); + return; + } + + /* Token starts with t/f/n but isn't recognised above. */ + json_set_token_error(token, json, "invalid token"); +} + +/* This function does not return. + * DO NOT CALL WITH DYNAMIC MEMORY ALLOCATED. + * The only supported exception is the temporary parser string + * json->tmp struct. + * json and token should exist on the stack somewhere. + * luaL_error() will long_jmp and release the stack */ +static void json_throw_parse_error(lua_State *l, json_parse_t *json, + const char *exp, json_token_t *token) +{ + const char *found; + + strbuf_free(json->tmp); + + if (token->type == T_ERROR) + found = token->value.string; + else + found = json_token_type_name[token->type]; + + /* Note: token->index is 0 based, display starting from 1 */ + luaL_error(l, "Expected %s but found %s at character %d", + exp, found, token->index + 1); +} + +static inline void json_decode_ascend(json_parse_t *json) +{ + json->current_depth--; +} + +static void json_decode_descend(lua_State *l, json_parse_t *json, int slots) +{ + json->current_depth++; + + if (json->current_depth <= json->cfg->decode_max_depth && + lua_checkstack(l, slots)) { + return; + } + + strbuf_free(json->tmp); + luaL_error(l, "Found too many nested data structures (%d) at character %d", + json->current_depth, json->ptr - json->data); +} + +static void json_parse_object_context(lua_State *l, json_parse_t *json) +{ + json_token_t token; + + /* 3 slots required: + * .., table, key, value */ + json_decode_descend(l, json, 3); + + lua_newtable(l); + + json_next_token(json, &token); + + /* Handle empty objects */ + if (token.type == T_OBJ_END) { + json_decode_ascend(json); + return; + } + + while (1) { + if (token.type != T_STRING) + json_throw_parse_error(l, json, "object key string", &token); + + /* Push key */ + lua_pushlstring(l, token.value.string, token.string_len); + + json_next_token(json, &token); + if (token.type != T_COLON) + json_throw_parse_error(l, json, "colon", &token); + + /* Fetch value */ + json_next_token(json, &token); + json_process_value(l, json, &token); + + /* Set key = value */ + lua_rawset(l, -3); + + json_next_token(json, &token); + + if (token.type == T_OBJ_END) { + json_decode_ascend(json); + return; + } + + if (token.type != T_COMMA) + json_throw_parse_error(l, json, "comma or object end", &token); + + json_next_token(json, &token); + } +} + +/* Handle the array context */ +static void json_parse_array_context(lua_State *l, json_parse_t *json) +{ + json_token_t token; + int i; + + /* 2 slots required: + * .., table, value */ + json_decode_descend(l, json, 2); + + lua_newtable(l); + + json_next_token(json, &token); + + /* Handle empty arrays */ + if (token.type == T_ARR_END) { + json_decode_ascend(json); + return; + } + + for (i = 1; ; i++) { + json_process_value(l, json, &token); + lua_rawseti(l, -2, i); /* arr[i] = value */ + + json_next_token(json, &token); + + if (token.type == T_ARR_END) { + json_decode_ascend(json); + return; + } + + if (token.type != T_COMMA) + json_throw_parse_error(l, json, "comma or array end", &token); + + json_next_token(json, &token); + } +} + +/* Handle the "value" context */ +static void json_process_value(lua_State *l, json_parse_t *json, + json_token_t *token) +{ + switch (token->type) { + case T_STRING: + lua_pushlstring(l, token->value.string, token->string_len); + break;; + case T_NUMBER: + lua_pushnumber(l, token->value.number); + break;; + case T_BOOLEAN: + lua_pushboolean(l, token->value.boolean); + break;; + case T_OBJ_BEGIN: + json_parse_object_context(l, json); + break;; + case T_ARR_BEGIN: + json_parse_array_context(l, json); + break;; + case T_NULL: + /* In Lua, setting "t[k] = nil" will delete k from the table. + * Hence a NULL pointer lightuserdata object is used instead */ + lua_pushlightuserdata(l, NULL); + break;; + default: + json_throw_parse_error(l, json, "value", token); + } +} + +static int json_decode(lua_State *l) +{ + json_parse_t json; + json_token_t token; + size_t json_len; + + luaL_argcheck(l, lua_gettop(l) == 1, 1, "expected 1 argument"); + + json.cfg = json_fetch_config(l); + json.data = luaL_checklstring(l, 1, &json_len); + json.current_depth = 0; + json.ptr = json.data; + + /* Detect Unicode other than UTF-8 (see RFC 4627, Sec 3) + * + * CJSON can support any simple data type, hence only the first + * character is guaranteed to be ASCII (at worst: '"'). This is + * still enough to detect whether the wrong encoding is in use. */ + if (json_len >= 2 && (!json.data[0] || !json.data[1])) + luaL_error(l, "JSON parser does not support UTF-16 or UTF-32"); + + /* Ensure the temporary buffer can hold the entire string. + * This means we no longer need to do length checks since the decoded + * string must be smaller than the entire json string */ + json.tmp = strbuf_new(json_len); + + json_next_token(&json, &token); + json_process_value(l, &json, &token); + + /* Ensure there is no more input left */ + json_next_token(&json, &token); + + if (token.type != T_END) + json_throw_parse_error(l, &json, "the end", &token); + + strbuf_free(json.tmp); + + return 1; +} + +/* ===== INITIALISATION ===== */ +#if 0 +#if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 502 +/* Compatibility for Lua 5.1. + * + * luaL_setfuncs() is used to create a module table where the functions have + * json_config_t as their first upvalue. Code borrowed from Lua 5.2 source. */ +static void luaL_setfuncs (lua_State *l, const luaL_Reg *reg, int nup) +{ + int i; + + luaL_checkstack(l, nup, "too many upvalues"); + for (; reg->name != NULL; reg++) { /* fill the table with given functions */ + for (i = 0; i < nup; i++) /* copy upvalues to the top */ + lua_pushvalue(l, -nup); + lua_pushcclosure(l, reg->func, nup); /* closure with those upvalues */ + lua_setfield(l, -(nup + 2), reg->name); + } + lua_pop(l, nup); /* remove upvalues */ +} +#endif + +/* Call target function in protected mode with all supplied args. + * Assumes target function only returns a single non-nil value. + * Convert and return thrown errors as: nil, "error message" */ +static int json_protect_conversion(lua_State *l) +{ + int err; + + /* Deliberately throw an error for invalid arguments */ + luaL_argcheck(l, lua_gettop(l) == 1, 1, "expected 1 argument"); + + /* pcall() the function stored as upvalue(1) */ + lua_pushvalue(l, lua_upvalueindex(1)); + lua_insert(l, 1); + err = lua_pcall(l, 1, 1, 0); + if (!err) + return 1; + + if (err == LUA_ERRRUN) { + lua_pushnil(l); + lua_insert(l, -2); + return 2; + } + + /* Since we are not using a custom error handler, the only remaining + * errors are memory related */ + return luaL_error(l, "Memory allocation error in CJSON protected call"); +} +#endif +// Module function map +#define MIN_OPT_LEVEL 2 +#include "lrodefs.h" +const LUA_REG_TYPE cjson_map[] = +{ + { LSTRKEY( "encode" ), LFUNCVAL( json_encode ) }, + { LSTRKEY( "decode" ), LFUNCVAL( json_decode ) }, + // { LSTRKEY( "encode_sparse_array" ), LFUNCVAL( json_cfg_encode_sparse_array ) }, + // { LSTRKEY( "encode_max_depth" ), LFUNCVAL( json_cfg_encode_max_depth ) }, + // { LSTRKEY( "decode_max_depth" ), LFUNCVAL( json_cfg_decode_max_depth ) }, + // { LSTRKEY( "encode_number_precision" ), LFUNCVAL( json_cfg_encode_number_precision ) }, + // { LSTRKEY( "encode_keep_buffer" ), LFUNCVAL( json_cfg_encode_keep_buffer ) }, + // { LSTRKEY( "encode_invalid_numbers" ), LFUNCVAL( json_cfg_encode_invalid_numbers ) }, + // { LSTRKEY( "decode_invalid_numbers" ), LFUNCVAL( json_cfg_decode_invalid_numbers ) }, + // { LSTRKEY( "new" ), LFUNCVAL( lua_cjson_new ) }, +#if LUA_OPTIMIZE_MEMORY > 0 + +#endif + { LNILKEY, LNILVAL } +}; + +LUALIB_API int luaopen_cjson( lua_State *L ) +{ + /* Initialise number conversions */ + fpconv_init(); + cfg_init(&_cfg); +#if LUA_OPTIMIZE_MEMORY > 0 + return 0; +#else // #if LUA_OPTIMIZE_MEMORY > 0 + luaL_register( L, AUXLIB_CJSON, cjson_map ); + // Add constants + /* Set cjson.null */ + lua_pushlightuserdata(l, NULL); + lua_setfield(l, -2, "null"); + + /* Return cjson table */ + return 1; +#endif // #if LUA_OPTIMIZE_MEMORY > 0 +} + +#if 0 +/* Return cjson module table */ +static int lua_cjson_new(lua_State *l) +{ + /* Initialise number conversions */ + fpconv_init(); + + /* cjson module table */ + lua_newtable(l); + + /* Register functions with config data as upvalue */ + json_create_config(l); + luaL_setfuncs(l, reg, 1); + + /* Set cjson.null */ + lua_pushlightuserdata(l, NULL); + lua_setfield(l, -2, "null"); + + /* Set module name / version fields */ + lua_pushliteral(l, CJSON_MODNAME); + lua_setfield(l, -2, "_NAME"); + lua_pushliteral(l, CJSON_VERSION); + lua_setfield(l, -2, "_VERSION"); + + return 1; +} + +/* Return cjson.safe module table */ +static int lua_cjson_safe_new(lua_State *l) +{ + const char *func[] = { "decode", "encode", NULL }; + int i; + + lua_cjson_new(l); + + /* Fix new() method */ + lua_pushcfunction(l, lua_cjson_safe_new); + lua_setfield(l, -2, "new"); + + for (i = 0; func[i]; i++) { + lua_getfield(l, -1, func[i]); + lua_pushcclosure(l, json_protect_conversion, 1); + lua_setfield(l, -2, func[i]); + } + + return 1; +} + +int luaopen_cjson(lua_State *l) +{ + lua_cjson_new(l); + +#ifdef ENABLE_CJSON_GLOBAL + /* Register a global "cjson" table. */ + lua_pushvalue(l, -1); + lua_setglobal(l, CJSON_MODNAME); +#endif + + /* Return cjson table */ + return 1; +} + +int luaopen_cjson_safe(lua_State *l) +{ + lua_cjson_safe_new(l); + + /* Return cjson.safe table */ + return 1; +} +#endif +/* vi:ai et sw=4 ts=4: + */ diff --git a/app/modules/file.c b/app/modules/file.c index 117ea128..19487d13 100644 --- a/app/modules/file.c +++ b/app/modules/file.c @@ -338,7 +338,7 @@ LUALIB_API int luaopen_file( lua_State *L ) #if LUA_OPTIMIZE_MEMORY > 0 return 0; #else // #if LUA_OPTIMIZE_MEMORY > 0 - luaL_register( L, AUXLIB_NODE, file_map ); + luaL_register( L, AUXLIB_FILE, file_map ); // Add constants return 1; diff --git a/app/modules/modules.h b/app/modules/modules.h index 3f077efd..fddc45cc 100644 --- a/app/modules/modules.h +++ b/app/modules/modules.h @@ -141,6 +141,13 @@ #define ROM_MODULES_WS2812 #endif +#if defined(LUA_USE_MODULES_CJSON) +#define MODULES_CJSON "cjson" +#define ROM_MODULES_CJSON \ + _ROM(MODULES_CJSON, luaopen_cjson, cjson_map) +#else +#define ROM_MODULES_CJSON +#endif #define LUA_MODULES_ROM \ ROM_MODULES_GPIO \ @@ -159,7 +166,8 @@ ROM_MODULES_UART \ ROM_MODULES_OW \ ROM_MODULES_BIT \ - ROM_MODULES_WS2812 + ROM_MODULES_WS2812 \ + ROM_MODULES_CJSON #endif From c9912f1d825ecd5e0761b702f1ca1404f6f8b3e9 Mon Sep 17 00:00:00 2001 From: funshine Date: Mon, 16 Mar 2015 09:17:21 +0800 Subject: [PATCH 23/40] json port from cjson. not work yet, delete folder --- app/cjson | 1 - 1 file changed, 1 deletion(-) delete mode 160000 app/cjson diff --git a/app/cjson b/app/cjson deleted file mode 160000 index d59326b2..00000000 --- a/app/cjson +++ /dev/null @@ -1 +0,0 @@ -Subproject commit d59326b2d718e1a140b9b396ffe0a557b2d93fe0 From 83f53c23c151781b2bc62d7e68a5985e01cdb601 Mon Sep 17 00:00:00 2001 From: funshine Date: Mon, 16 Mar 2015 09:19:06 +0800 Subject: [PATCH 24/40] json port from cjson. not work yet, add cjson folder --- app/cjson/CMakeLists.txt | 76 + app/cjson/LICENSE | 21 + app/cjson/Makefile | 47 + app/cjson/NEWS | 44 + app/cjson/THANKS | 9 + app/cjson/devel/json_parser_outline.txt | 50 + app/cjson/dtoa.c.bak | 4358 +++++++++++++++++++++++ app/cjson/dtoa_config.h.bak | 74 + app/cjson/fpconv.c | 208 ++ app/cjson/fpconv.h | 22 + app/cjson/g_fmt.c | 111 + app/cjson/lua-cjson-2.1devel-1.rockspec | 56 + app/cjson/lua-cjson.spec | 80 + app/cjson/lua/cjson/util.lua | 271 ++ app/cjson/lua/json2lua.lua | 14 + app/cjson/lua/lua2json.lua | 20 + app/cjson/manual.txt | 612 ++++ app/cjson/performance.txt | 89 + app/cjson/rfc4627.txt | 563 +++ app/cjson/strbuf.c | 261 ++ app/cjson/strbuf.h | 154 + app/cjson/tests/README | 4 + app/cjson/tests/bench.lua | 131 + app/cjson/tests/example1.json | 22 + app/cjson/tests/example2.json | 11 + app/cjson/tests/example3.json | 26 + app/cjson/tests/example4.json | 88 + app/cjson/tests/example5.json | 27 + app/cjson/tests/genutf8.pl | 23 + app/cjson/tests/numbers.json | 7 + app/cjson/tests/octets-escaped.dat | 1 + app/cjson/tests/rfc-example1.json | 13 + app/cjson/tests/rfc-example2.json | 22 + app/cjson/tests/test.lua | 425 +++ app/cjson/tests/types.json | 1 + 35 files changed, 7941 insertions(+) create mode 100644 app/cjson/CMakeLists.txt create mode 100644 app/cjson/LICENSE create mode 100644 app/cjson/Makefile create mode 100644 app/cjson/NEWS create mode 100644 app/cjson/THANKS create mode 100644 app/cjson/devel/json_parser_outline.txt create mode 100644 app/cjson/dtoa.c.bak create mode 100644 app/cjson/dtoa_config.h.bak create mode 100644 app/cjson/fpconv.c create mode 100644 app/cjson/fpconv.h create mode 100644 app/cjson/g_fmt.c create mode 100644 app/cjson/lua-cjson-2.1devel-1.rockspec create mode 100644 app/cjson/lua-cjson.spec create mode 100644 app/cjson/lua/cjson/util.lua create mode 100644 app/cjson/lua/json2lua.lua create mode 100644 app/cjson/lua/lua2json.lua create mode 100644 app/cjson/manual.txt create mode 100644 app/cjson/performance.txt create mode 100644 app/cjson/rfc4627.txt create mode 100644 app/cjson/strbuf.c create mode 100644 app/cjson/strbuf.h create mode 100644 app/cjson/tests/README create mode 100644 app/cjson/tests/bench.lua create mode 100644 app/cjson/tests/example1.json create mode 100644 app/cjson/tests/example2.json create mode 100644 app/cjson/tests/example3.json create mode 100644 app/cjson/tests/example4.json create mode 100644 app/cjson/tests/example5.json create mode 100644 app/cjson/tests/genutf8.pl create mode 100644 app/cjson/tests/numbers.json create mode 100644 app/cjson/tests/octets-escaped.dat create mode 100644 app/cjson/tests/rfc-example1.json create mode 100644 app/cjson/tests/rfc-example2.json create mode 100644 app/cjson/tests/test.lua create mode 100644 app/cjson/tests/types.json diff --git a/app/cjson/CMakeLists.txt b/app/cjson/CMakeLists.txt new file mode 100644 index 00000000..c17239b2 --- /dev/null +++ b/app/cjson/CMakeLists.txt @@ -0,0 +1,76 @@ +# If Lua is installed in a non-standard location, please set the LUA_DIR +# environment variable to point to prefix for the install. Eg: +# Unix: export LUA_DIR=/home/user/pkg +# Windows: set LUA_DIR=c:\lua51 + +project(lua-cjson C) +cmake_minimum_required(VERSION 2.6) + +option(USE_INTERNAL_FPCONV "Use internal strtod() / g_fmt() code for performance") +option(MULTIPLE_THREADS "Support multi-threaded apps with internal fpconv - recommended" ON) + +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release CACHE STRING + "Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel." + FORCE) +endif() + +find_package(Lua51 REQUIRED) +include_directories(${LUA_INCLUDE_DIR}) + +if(NOT USE_INTERNAL_FPCONV) + # Use libc number conversion routines (strtod(), sprintf()) + set(FPCONV_SOURCES fpconv.c) +else() + # Use internal number conversion routines + add_definitions(-DUSE_INTERNAL_FPCONV) + set(FPCONV_SOURCES g_fmt.c dtoa.c) + + include(TestBigEndian) + TEST_BIG_ENDIAN(IEEE_BIG_ENDIAN) + if(IEEE_BIG_ENDIAN) + add_definitions(-DIEEE_BIG_ENDIAN) + endif() + + if(MULTIPLE_THREADS) + set(CMAKE_THREAD_PREFER_PTHREAD TRUE) + find_package(Threads REQUIRED) + if(NOT CMAKE_USE_PTHREADS_INIT) + message(FATAL_ERROR + "Pthreads not found - required by MULTIPLE_THREADS option") + endif() + add_definitions(-DMULTIPLE_THREADS) + endif() +endif() + +# Handle platforms missing isinf() macro (Eg, some Solaris systems). +include(CheckSymbolExists) +CHECK_SYMBOL_EXISTS(isinf math.h HAVE_ISINF) +if(NOT HAVE_ISINF) + add_definitions(-DUSE_INTERNAL_ISINF) +endif() + +set(_MODULE_LINK "${CMAKE_THREAD_LIBS_INIT}") +get_filename_component(_lua_lib_dir ${LUA_LIBRARY} PATH) + +if(APPLE) + set(CMAKE_SHARED_MODULE_CREATE_C_FLAGS + "${CMAKE_SHARED_MODULE_CREATE_C_FLAGS} -undefined dynamic_lookup") +endif() + +if(WIN32) + # Win32 modules need to be linked to the Lua library. + set(_MODULE_LINK ${LUA_LIBRARY} ${_MODULE_LINK}) + set(_lua_module_dir "${_lua_lib_dir}") + # Windows sprintf()/strtod() handle NaN/inf differently. Not supported. + add_definitions(-DDISABLE_INVALID_NUMBERS) +else() + set(_lua_module_dir "${_lua_lib_dir}/lua/5.1") +endif() + +add_library(cjson MODULE lua_cjson.c strbuf.c ${FPCONV_SOURCES}) +set_target_properties(cjson PROPERTIES PREFIX "") +target_link_libraries(cjson ${_MODULE_LINK}) +install(TARGETS cjson DESTINATION "${_lua_module_dir}") + +# vi:ai et sw=4 ts=4: diff --git a/app/cjson/LICENSE b/app/cjson/LICENSE new file mode 100644 index 00000000..7f37af3b --- /dev/null +++ b/app/cjson/LICENSE @@ -0,0 +1,21 @@ +Copyright (c) 2010-2012 Mark Pulford + 2015 Zeroday Hong nodemcu.com + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/app/cjson/Makefile b/app/cjson/Makefile new file mode 100644 index 00000000..c81eb098 --- /dev/null +++ b/app/cjson/Makefile @@ -0,0 +1,47 @@ + +############################################################# +# Required variables for each makefile +# Discard this section from all parent makefiles +# Expected variables (with automatic defaults): +# CSRCS (all "C" files in the dir) +# SUBDIRS (all subdirs with a Makefile) +# GEN_LIBS - list of libs to be generated () +# GEN_IMAGES - list of images to be generated () +# COMPONENTS_xxx - a list of libs/objs in the form +# subdir/lib to be extracted and rolled up into +# a generated lib/image xxx.a () +# +ifndef PDIR + +GEN_LIBS = libcjson.a + +endif + + +############################################################# +# Configuration i.e. compile options etc. +# Target specific stuff (defines etc.) goes in here! +# Generally values applying to a tree are captured in the +# makefile at its root level - these are then overridden +# for a subtree within the makefile rooted therein +# +#DEFINES += + +############################################################# +# Recursion Magic - Don't touch this!! +# +# Each subtree potentially has an include directory +# corresponding to the common APIs applicable to modules +# rooted at that subtree. Accordingly, the INCLUDE PATH +# of a module can only contain the include directories up +# its parent path, and not its siblings +# +# Required for each makefile to inherit from the parent +# + +INCLUDES := $(INCLUDES) -I $(PDIR)include +INCLUDES += -I ./ +INCLUDES += -I ../libc +PDIR := ../$(PDIR) +sinclude $(PDIR)Makefile + diff --git a/app/cjson/NEWS b/app/cjson/NEWS new file mode 100644 index 00000000..8927d6e5 --- /dev/null +++ b/app/cjson/NEWS @@ -0,0 +1,44 @@ +Version 2.1.0 (Mar 1 2012) +* Added cjson.safe module interface which returns nil after an error +* Improved Makefile compatibility with Solaris make + +Version 2.0.0 (Jan 22 2012) +* Improved platform compatibility for strtod/sprintf locale workaround +* Added option to build with David Gay's dtoa.c for improved performance +* Added support for Lua 5.2 +* Added option to encode infinity/NaN as JSON null +* Fixed encode bug with a raised default limit and deeply nested tables +* Updated Makefile for compatibility with non-GNU make implementations +* Added CMake build support +* Added HTML manual +* Increased default nesting limit to 1000 +* Added support for re-entrant use of encode and decode +* Added support for installing lua2json and json2lua utilities +* Added encode_invalid_numbers() and decode_invalid_numbers() +* Added decode_max_depth() +* Removed registration of global cjson module table +* Removed refuse_invalid_numbers() + +Version 1.0.4 (Nov 30 2011) +* Fixed numeric conversion under locales with a comma decimal separator + +Version 1.0.3 (Sep 15 2011) +* Fixed detection of objects with numeric string keys +* Provided work around for missing isinf() on Solaris + +Version 1.0.2 (May 30 2011) +* Portability improvements for Windows + - No longer links with -lm + - Use "socket" instead of "posix" for sub-second timing +* Removed UTF-8 test dependency on Perl Text::Iconv +* Added simple CLI commands for testing Lua <-> JSON conversions +* Added cjson.encode_number_precision() + +Version 1.0.1 (May 10 2011) +* Added build support for OSX +* Removed unnecessary whitespace from JSON output +* Added cjson.encode_keep_buffer() +* Fixed memory leak on Lua stack overflow exception + +Version 1.0 (May 9 2011) +* Initial release diff --git a/app/cjson/THANKS b/app/cjson/THANKS new file mode 100644 index 00000000..4aade136 --- /dev/null +++ b/app/cjson/THANKS @@ -0,0 +1,9 @@ +The following people have helped with bug reports, testing and/or +suggestions: + +- Louis-Philippe Perron (@loopole) +- Ondřej Jirman +- Steve Donovan +- Zhang "agentzh" Yichun + +Thanks! diff --git a/app/cjson/devel/json_parser_outline.txt b/app/cjson/devel/json_parser_outline.txt new file mode 100644 index 00000000..01db78d3 --- /dev/null +++ b/app/cjson/devel/json_parser_outline.txt @@ -0,0 +1,50 @@ +parser: + - call parse_value + - next_token + ? nop. + +parse_value: + - next_token + ? call parse_object. + ? call parse_array. + ? push. return. + ? push. return. + ? push. return. + ? push. return. + +parse_object: + - push table + - next_token + ? push. + - next_token + ? nop. + - call parse_value + - set table + - next_token + ? return. + ? loop parse_object. + +parse_array: + - push table + - call parse_value + - table append + - next_token + ? loop parse_array. + ? ] return. + +next_token: + - check next character + ? { return + ? } return + ? [ return + ? ] return + ? , return + ? : return + ? [-0-9] gobble number. return + ? " gobble string. return + ? [ \t\n] eat whitespace. + ? n Check "null". return or + ? t Check "true". return or + ? f Check "false". return or + ? . return + ? \0 return diff --git a/app/cjson/dtoa.c.bak b/app/cjson/dtoa.c.bak new file mode 100644 index 00000000..56398ba7 --- /dev/null +++ b/app/cjson/dtoa.c.bak @@ -0,0 +1,4358 @@ +/**************************************************************** + * + * The author of this software is David M. Gay. + * + * Copyright (c) 1991, 2000, 2001 by Lucent Technologies. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHOR NOR LUCENT MAKES ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + * + ***************************************************************/ + +/* Please send bug reports to David M. Gay (dmg at acm dot org, + * with " at " changed at "@" and " dot " changed to "."). */ + +/* On a machine with IEEE extended-precision registers, it is + * necessary to specify double-precision (53-bit) rounding precision + * before invoking strtod or dtoa. If the machine uses (the equivalent + * of) Intel 80x87 arithmetic, the call + * _control87(PC_53, MCW_PC); + * does this with many compilers. Whether this or another call is + * appropriate depends on the compiler; for this to work, it may be + * necessary to #include "float.h" or another system-dependent header + * file. + */ + +/* strtod for IEEE-, VAX-, and IBM-arithmetic machines. + * + * This strtod returns a nearest machine number to the input decimal + * string (or sets errno to ERANGE). With IEEE arithmetic, ties are + * broken by the IEEE round-even rule. Otherwise ties are broken by + * biased rounding (add half and chop). + * + * Inspired loosely by William D. Clinger's paper "How to Read Floating + * Point Numbers Accurately" [Proc. ACM SIGPLAN '90, pp. 92-101]. + * + * Modifications: + * + * 1. We only require IEEE, IBM, or VAX double-precision + * arithmetic (not IEEE double-extended). + * 2. We get by with floating-point arithmetic in a case that + * Clinger missed -- when we're computing d * 10^n + * for a small integer d and the integer n is not too + * much larger than 22 (the maximum integer k for which + * we can represent 10^k exactly), we may be able to + * compute (d*10^k) * 10^(e-k) with just one roundoff. + * 3. Rather than a bit-at-a-time adjustment of the binary + * result in the hard case, we use floating-point + * arithmetic to determine the adjustment to within + * one bit; only in really hard cases do we need to + * compute a second residual. + * 4. Because of 3., we don't need a large table of powers of 10 + * for ten-to-e (just some small tables, e.g. of 10^k + * for 0 <= k <= 22). + */ + +/* + * #define IEEE_8087 for IEEE-arithmetic machines where the least + * significant byte has the lowest address. + * #define IEEE_MC68k for IEEE-arithmetic machines where the most + * significant byte has the lowest address. + * #define Long int on machines with 32-bit ints and 64-bit longs. + * #define IBM for IBM mainframe-style floating-point arithmetic. + * #define VAX for VAX-style floating-point arithmetic (D_floating). + * #define No_leftright to omit left-right logic in fast floating-point + * computation of dtoa. This will cause dtoa modes 4 and 5 to be + * treated the same as modes 2 and 3 for some inputs. + * #define Honor_FLT_ROUNDS if FLT_ROUNDS can assume the values 2 or 3 + * and strtod and dtoa should round accordingly. Unless Trust_FLT_ROUNDS + * is also #defined, fegetround() will be queried for the rounding mode. + * Note that both FLT_ROUNDS and fegetround() are specified by the C99 + * standard (and are specified to be consistent, with fesetround() + * affecting the value of FLT_ROUNDS), but that some (Linux) systems + * do not work correctly in this regard, so using fegetround() is more + * portable than using FLT_ROUNDS directly. + * #define Check_FLT_ROUNDS if FLT_ROUNDS can assume the values 2 or 3 + * and Honor_FLT_ROUNDS is not #defined. + * #define RND_PRODQUOT to use rnd_prod and rnd_quot (assembly routines + * that use extended-precision instructions to compute rounded + * products and quotients) with IBM. + * #define ROUND_BIASED for IEEE-format with biased rounding and arithmetic + * that rounds toward +Infinity. + * #define ROUND_BIASED_without_Round_Up for IEEE-format with biased + * rounding when the underlying floating-point arithmetic uses + * unbiased rounding. This prevent using ordinary floating-point + * arithmetic when the result could be computed with one rounding error. + * #define Inaccurate_Divide for IEEE-format with correctly rounded + * products but inaccurate quotients, e.g., for Intel i860. + * #define NO_LONG_LONG on machines that do not have a "long long" + * integer type (of >= 64 bits). On such machines, you can + * #define Just_16 to store 16 bits per 32-bit Long when doing + * high-precision integer arithmetic. Whether this speeds things + * up or slows things down depends on the machine and the number + * being converted. If long long is available and the name is + * something other than "long long", #define Llong to be the name, + * and if "unsigned Llong" does not work as an unsigned version of + * Llong, #define #ULLong to be the corresponding unsigned type. + * #define KR_headers for old-style C function headers. + * #define Bad_float_h if your system lacks a float.h or if it does not + * define some or all of DBL_DIG, DBL_MAX_10_EXP, DBL_MAX_EXP, + * FLT_RADIX, FLT_ROUNDS, and DBL_MAX. + * #define MALLOC your_malloc, where your_malloc(n) acts like malloc(n) + * if memory is available and otherwise does something you deem + * appropriate. If MALLOC is undefined, malloc will be invoked + * directly -- and assumed always to succeed. Similarly, if you + * want something other than the system's free() to be called to + * recycle memory acquired from MALLOC, #define FREE to be the + * name of the alternate routine. (FREE or free is only called in + * pathological cases, e.g., in a dtoa call after a dtoa return in + * mode 3 with thousands of digits requested.) + * #define Omit_Private_Memory to omit logic (added Jan. 1998) for making + * memory allocations from a private pool of memory when possible. + * When used, the private pool is PRIVATE_MEM bytes long: 2304 bytes, + * unless #defined to be a different length. This default length + * suffices to get rid of MALLOC calls except for unusual cases, + * such as decimal-to-binary conversion of a very long string of + * digits. The longest string dtoa can return is about 751 bytes + * long. For conversions by strtod of strings of 800 digits and + * all dtoa conversions in single-threaded executions with 8-byte + * pointers, PRIVATE_MEM >= 7400 appears to suffice; with 4-byte + * pointers, PRIVATE_MEM >= 7112 appears adequate. + * #define NO_INFNAN_CHECK if you do not wish to have INFNAN_CHECK + * #defined automatically on IEEE systems. On such systems, + * when INFNAN_CHECK is #defined, strtod checks + * for Infinity and NaN (case insensitively). On some systems + * (e.g., some HP systems), it may be necessary to #define NAN_WORD0 + * appropriately -- to the most significant word of a quiet NaN. + * (On HP Series 700/800 machines, -DNAN_WORD0=0x7ff40000 works.) + * When INFNAN_CHECK is #defined and No_Hex_NaN is not #defined, + * strtod also accepts (case insensitively) strings of the form + * NaN(x), where x is a string of hexadecimal digits and spaces; + * if there is only one string of hexadecimal digits, it is taken + * for the 52 fraction bits of the resulting NaN; if there are two + * or more strings of hex digits, the first is for the high 20 bits, + * the second and subsequent for the low 32 bits, with intervening + * white space ignored; but if this results in none of the 52 + * fraction bits being on (an IEEE Infinity symbol), then NAN_WORD0 + * and NAN_WORD1 are used instead. + * #define MULTIPLE_THREADS if the system offers preemptively scheduled + * multiple threads. In this case, you must provide (or suitably + * #define) two locks, acquired by ACQUIRE_DTOA_LOCK(n) and freed + * by FREE_DTOA_LOCK(n) for n = 0 or 1. (The second lock, accessed + * in pow5mult, ensures lazy evaluation of only one copy of high + * powers of 5; omitting this lock would introduce a small + * probability of wasting memory, but would otherwise be harmless.) + * You must also invoke freedtoa(s) to free the value s returned by + * dtoa. You may do so whether or not MULTIPLE_THREADS is #defined. + * #define NO_IEEE_Scale to disable new (Feb. 1997) logic in strtod that + * avoids underflows on inputs whose result does not underflow. + * If you #define NO_IEEE_Scale on a machine that uses IEEE-format + * floating-point numbers and flushes underflows to zero rather + * than implementing gradual underflow, then you must also #define + * Sudden_Underflow. + * #define USE_LOCALE to use the current locale's decimal_point value. + * #define SET_INEXACT if IEEE arithmetic is being used and extra + * computation should be done to set the inexact flag when the + * result is inexact and avoid setting inexact when the result + * is exact. In this case, dtoa.c must be compiled in + * an environment, perhaps provided by #include "dtoa.c" in a + * suitable wrapper, that defines two functions, + * int get_inexact(void); + * void clear_inexact(void); + * such that get_inexact() returns a nonzero value if the + * inexact bit is already set, and clear_inexact() sets the + * inexact bit to 0. When SET_INEXACT is #defined, strtod + * also does extra computations to set the underflow and overflow + * flags when appropriate (i.e., when the result is tiny and + * inexact or when it is a numeric value rounded to +-infinity). + * #define NO_ERRNO if strtod should not assign errno = ERANGE when + * the result overflows to +-Infinity or underflows to 0. + * #define NO_HEX_FP to omit recognition of hexadecimal floating-point + * values by strtod. + * #define NO_STRTOD_BIGCOMP (on IEEE-arithmetic systems only for now) + * to disable logic for "fast" testing of very long input strings + * to strtod. This testing proceeds by initially truncating the + * input string, then if necessary comparing the whole string with + * a decimal expansion to decide close cases. This logic is only + * used for input more than STRTOD_DIGLIM digits long (default 40). + */ + +#include "dtoa_config.h" + +#ifndef Long +#define Long long +#endif +#ifndef ULong +typedef unsigned Long ULong; +#endif + +#ifdef DEBUG +#include "stdio.h" +#define Bug(x) {fprintf(stderr, "%s\n", x); exit(1);} +#endif + +#include "stdlib.h" +#include "string.h" + +#ifdef USE_LOCALE +#include "locale.h" +#endif + +#ifdef Honor_FLT_ROUNDS +#ifndef Trust_FLT_ROUNDS +#include +#endif +#endif + +#ifdef MALLOC +#ifdef KR_headers +extern char *MALLOC(); +#else +extern void *MALLOC(size_t); +#endif +#else +#define MALLOC malloc +#endif + +#ifndef Omit_Private_Memory +#ifndef PRIVATE_MEM +#define PRIVATE_MEM 2304 +#endif +#define PRIVATE_mem ((PRIVATE_MEM+sizeof(double)-1)/sizeof(double)) +static double private_mem[PRIVATE_mem], *pmem_next = private_mem; +#endif + +#undef IEEE_Arith +#undef Avoid_Underflow +#ifdef IEEE_MC68k +#define IEEE_Arith +#endif +#ifdef IEEE_8087 +#define IEEE_Arith +#endif + +#ifdef IEEE_Arith +#ifndef NO_INFNAN_CHECK +#undef INFNAN_CHECK +#define INFNAN_CHECK +#endif +#else +#undef INFNAN_CHECK +#define NO_STRTOD_BIGCOMP +#endif + +#include "errno.h" + +#ifdef Bad_float_h + +#ifdef IEEE_Arith +#define DBL_DIG 15 +#define DBL_MAX_10_EXP 308 +#define DBL_MAX_EXP 1024 +#define FLT_RADIX 2 +#endif /*IEEE_Arith*/ + +#ifdef IBM +#define DBL_DIG 16 +#define DBL_MAX_10_EXP 75 +#define DBL_MAX_EXP 63 +#define FLT_RADIX 16 +#define DBL_MAX 7.2370055773322621e+75 +#endif + +#ifdef VAX +#define DBL_DIG 16 +#define DBL_MAX_10_EXP 38 +#define DBL_MAX_EXP 127 +#define FLT_RADIX 2 +#define DBL_MAX 1.7014118346046923e+38 +#endif + +#ifndef LONG_MAX +#define LONG_MAX 2147483647 +#endif + +#else /* ifndef Bad_float_h */ +#include "float.h" +#endif /* Bad_float_h */ + +#ifndef __MATH_H__ +#include "math.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef CONST +#ifdef KR_headers +#define CONST /* blank */ +#else +#define CONST const +#endif +#endif + +#if defined(IEEE_8087) + defined(IEEE_MC68k) + defined(VAX) + defined(IBM) != 1 +Exactly one of IEEE_8087, IEEE_MC68k, VAX, or IBM should be defined. +#endif + +typedef union { double d; ULong L[2]; } U; + +#ifdef IEEE_8087 +#define word0(x) (x)->L[1] +#define word1(x) (x)->L[0] +#else +#define word0(x) (x)->L[0] +#define word1(x) (x)->L[1] +#endif +#define dval(x) (x)->d + +#ifndef STRTOD_DIGLIM +#define STRTOD_DIGLIM 40 +#endif + +#ifdef DIGLIM_DEBUG +extern int strtod_diglim; +#else +#define strtod_diglim STRTOD_DIGLIM +#endif + +/* The following definition of Storeinc is appropriate for MIPS processors. + * An alternative that might be better on some machines is + * #define Storeinc(a,b,c) (*a++ = b << 16 | c & 0xffff) + */ +#if defined(IEEE_8087) + defined(VAX) +#define Storeinc(a,b,c) (((unsigned short *)a)[1] = (unsigned short)b, \ +((unsigned short *)a)[0] = (unsigned short)c, a++) +#else +#define Storeinc(a,b,c) (((unsigned short *)a)[0] = (unsigned short)b, \ +((unsigned short *)a)[1] = (unsigned short)c, a++) +#endif + +/* #define P DBL_MANT_DIG */ +/* Ten_pmax = floor(P*log(2)/log(5)) */ +/* Bletch = (highest power of 2 < DBL_MAX_10_EXP) / 16 */ +/* Quick_max = floor((P-1)*log(FLT_RADIX)/log(10) - 1) */ +/* Int_max = floor(P*log(FLT_RADIX)/log(10) - 1) */ + +#ifdef IEEE_Arith +#define Exp_shift 20 +#define Exp_shift1 20 +#define Exp_msk1 0x100000 +#define Exp_msk11 0x100000 +#define Exp_mask 0x7ff00000 +#define P 53 +#define Nbits 53 +#define Bias 1023 +#define Emax 1023 +#define Emin (-1022) +#define Exp_1 0x3ff00000 +#define Exp_11 0x3ff00000 +#define Ebits 11 +#define Frac_mask 0xfffff +#define Frac_mask1 0xfffff +#define Ten_pmax 22 +#define Bletch 0x10 +#define Bndry_mask 0xfffff +#define Bndry_mask1 0xfffff +#define LSB 1 +#define Sign_bit 0x80000000 +#define Log2P 1 +#define Tiny0 0 +#define Tiny1 1 +#define Quick_max 14 +#define Int_max 14 +#ifndef NO_IEEE_Scale +#define Avoid_Underflow +#ifdef Flush_Denorm /* debugging option */ +#undef Sudden_Underflow +#endif +#endif + +#ifndef Flt_Rounds +#ifdef FLT_ROUNDS +#define Flt_Rounds FLT_ROUNDS +#else +#define Flt_Rounds 1 +#endif +#endif /*Flt_Rounds*/ + +#ifdef Honor_FLT_ROUNDS +#undef Check_FLT_ROUNDS +#define Check_FLT_ROUNDS +#else +#define Rounding Flt_Rounds +#endif + +#else /* ifndef IEEE_Arith */ +#undef Check_FLT_ROUNDS +#undef Honor_FLT_ROUNDS +#undef SET_INEXACT +#undef Sudden_Underflow +#define Sudden_Underflow +#ifdef IBM +#undef Flt_Rounds +#define Flt_Rounds 0 +#define Exp_shift 24 +#define Exp_shift1 24 +#define Exp_msk1 0x1000000 +#define Exp_msk11 0x1000000 +#define Exp_mask 0x7f000000 +#define P 14 +#define Nbits 56 +#define Bias 65 +#define Emax 248 +#define Emin (-260) +#define Exp_1 0x41000000 +#define Exp_11 0x41000000 +#define Ebits 8 /* exponent has 7 bits, but 8 is the right value in b2d */ +#define Frac_mask 0xffffff +#define Frac_mask1 0xffffff +#define Bletch 4 +#define Ten_pmax 22 +#define Bndry_mask 0xefffff +#define Bndry_mask1 0xffffff +#define LSB 1 +#define Sign_bit 0x80000000 +#define Log2P 4 +#define Tiny0 0x100000 +#define Tiny1 0 +#define Quick_max 14 +#define Int_max 15 +#else /* VAX */ +#undef Flt_Rounds +#define Flt_Rounds 1 +#define Exp_shift 23 +#define Exp_shift1 7 +#define Exp_msk1 0x80 +#define Exp_msk11 0x800000 +#define Exp_mask 0x7f80 +#define P 56 +#define Nbits 56 +#define Bias 129 +#define Emax 126 +#define Emin (-129) +#define Exp_1 0x40800000 +#define Exp_11 0x4080 +#define Ebits 8 +#define Frac_mask 0x7fffff +#define Frac_mask1 0xffff007f +#define Ten_pmax 24 +#define Bletch 2 +#define Bndry_mask 0xffff007f +#define Bndry_mask1 0xffff007f +#define LSB 0x10000 +#define Sign_bit 0x8000 +#define Log2P 1 +#define Tiny0 0x80 +#define Tiny1 0 +#define Quick_max 15 +#define Int_max 15 +#endif /* IBM, VAX */ +#endif /* IEEE_Arith */ + +#ifndef IEEE_Arith +#define ROUND_BIASED +#else +#ifdef ROUND_BIASED_without_Round_Up +#undef ROUND_BIASED +#define ROUND_BIASED +#endif +#endif + +#ifdef RND_PRODQUOT +#define rounded_product(a,b) a = rnd_prod(a, b) +#define rounded_quotient(a,b) a = rnd_quot(a, b) +#ifdef KR_headers +extern double rnd_prod(), rnd_quot(); +#else +extern double rnd_prod(double, double), rnd_quot(double, double); +#endif +#else +#define rounded_product(a,b) a *= b +#define rounded_quotient(a,b) a /= b +#endif + +#define Big0 (Frac_mask1 | Exp_msk1*(DBL_MAX_EXP+Bias-1)) +#define Big1 0xffffffff + +#ifndef Pack_32 +#define Pack_32 +#endif + +typedef struct BCinfo BCinfo; + struct +BCinfo { int dp0, dp1, dplen, dsign, e0, inexact, nd, nd0, rounding, scale, uflchk; }; + +#ifdef KR_headers +#define FFFFFFFF ((((unsigned long)0xffff)<<16)|(unsigned long)0xffff) +#else +#define FFFFFFFF 0xffffffffUL +#endif + +#ifdef NO_LONG_LONG +#undef ULLong +#ifdef Just_16 +#undef Pack_32 +/* When Pack_32 is not defined, we store 16 bits per 32-bit Long. + * This makes some inner loops simpler and sometimes saves work + * during multiplications, but it often seems to make things slightly + * slower. Hence the default is now to store 32 bits per Long. + */ +#endif +#else /* long long available */ +#ifndef Llong +#define Llong long long +#endif +#ifndef ULLong +#define ULLong unsigned Llong +#endif +#endif /* NO_LONG_LONG */ + +#ifndef MULTIPLE_THREADS +#define ACQUIRE_DTOA_LOCK(n) /*nothing*/ +#define FREE_DTOA_LOCK(n) /*nothing*/ +#endif + +#define Kmax 7 + +#ifdef __cplusplus +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 + + struct +Bigint { + struct Bigint *next; + int k, maxwds, sign, wds; + ULong x[1]; + }; + + typedef struct Bigint Bigint; + + static Bigint *freelist[Kmax+1]; + + static Bigint * +Balloc +#ifdef KR_headers + (k) int k; +#else + (int k) +#endif +{ + int x; + Bigint *rv; +#ifndef Omit_Private_Memory + unsigned int len; +#endif + + ACQUIRE_DTOA_LOCK(0); + /* The k > Kmax case does not need ACQUIRE_DTOA_LOCK(0), */ + /* but this case seems very unlikely. */ + if (k <= Kmax && (rv = freelist[k])) + freelist[k] = rv->next; + else { + x = 1 << k; +#ifdef Omit_Private_Memory + rv = (Bigint *)MALLOC(sizeof(Bigint) + (x-1)*sizeof(ULong)); +#else + len = (sizeof(Bigint) + (x-1)*sizeof(ULong) + sizeof(double) - 1) + /sizeof(double); + if (k <= Kmax && pmem_next - private_mem + len <= PRIVATE_mem) { + rv = (Bigint*)pmem_next; + pmem_next += len; + } + else + rv = (Bigint*)MALLOC(len*sizeof(double)); +#endif + rv->k = k; + rv->maxwds = x; + } + FREE_DTOA_LOCK(0); + rv->sign = rv->wds = 0; + return rv; + } + + static void +Bfree +#ifdef KR_headers + (v) Bigint *v; +#else + (Bigint *v) +#endif +{ + if (v) { + if (v->k > Kmax) +#ifdef FREE + FREE((void*)v); +#else + free((void*)v); +#endif + else { + ACQUIRE_DTOA_LOCK(0); + v->next = freelist[v->k]; + freelist[v->k] = v; + FREE_DTOA_LOCK(0); + } + } + } + +#define Bcopy(x,y) memcpy((char *)&x->sign, (char *)&y->sign, \ +y->wds*sizeof(Long) + 2*sizeof(int)) + + static Bigint * +multadd +#ifdef KR_headers + (b, m, a) Bigint *b; int m, a; +#else + (Bigint *b, int m, int a) /* multiply by m and add a */ +#endif +{ + int i, wds; +#ifdef ULLong + ULong *x; + ULLong carry, y; +#else + ULong carry, *x, y; +#ifdef Pack_32 + ULong xi, z; +#endif +#endif + Bigint *b1; + + wds = b->wds; + x = b->x; + i = 0; + carry = a; + do { +#ifdef ULLong + y = *x * (ULLong)m + carry; + carry = y >> 32; + *x++ = y & FFFFFFFF; +#else +#ifdef Pack_32 + xi = *x; + y = (xi & 0xffff) * m + carry; + z = (xi >> 16) * m + (y >> 16); + carry = z >> 16; + *x++ = (z << 16) + (y & 0xffff); +#else + y = *x * m + carry; + carry = y >> 16; + *x++ = y & 0xffff; +#endif +#endif + } + while(++i < wds); + if (carry) { + if (wds >= b->maxwds) { + b1 = Balloc(b->k+1); + Bcopy(b1, b); + Bfree(b); + b = b1; + } + b->x[wds++] = carry; + b->wds = wds; + } + return b; + } + + static Bigint * +s2b +#ifdef KR_headers + (s, nd0, nd, y9, dplen) CONST char *s; int nd0, nd, dplen; ULong y9; +#else + (const char *s, int nd0, int nd, ULong y9, int dplen) +#endif +{ + Bigint *b; + int i, k; + Long x, y; + + x = (nd + 8) / 9; + for(k = 0, y = 1; x > y; y <<= 1, k++) ; +#ifdef Pack_32 + b = Balloc(k); + b->x[0] = y9; + b->wds = 1; +#else + b = Balloc(k+1); + b->x[0] = y9 & 0xffff; + b->wds = (b->x[1] = y9 >> 16) ? 2 : 1; +#endif + + i = 9; + if (9 < nd0) { + s += 9; + do b = multadd(b, 10, *s++ - '0'); + while(++i < nd0); + s += dplen; + } + else + s += dplen + 9; + for(; i < nd; i++) + b = multadd(b, 10, *s++ - '0'); + return b; + } + + static int +hi0bits +#ifdef KR_headers + (x) ULong x; +#else + (ULong x) +#endif +{ + int k = 0; + + if (!(x & 0xffff0000)) { + k = 16; + x <<= 16; + } + if (!(x & 0xff000000)) { + k += 8; + x <<= 8; + } + if (!(x & 0xf0000000)) { + k += 4; + x <<= 4; + } + if (!(x & 0xc0000000)) { + k += 2; + x <<= 2; + } + if (!(x & 0x80000000)) { + k++; + if (!(x & 0x40000000)) + return 32; + } + return k; + } + + static int +lo0bits +#ifdef KR_headers + (y) ULong *y; +#else + (ULong *y) +#endif +{ + int k; + ULong x = *y; + + if (x & 7) { + if (x & 1) + return 0; + if (x & 2) { + *y = x >> 1; + return 1; + } + *y = x >> 2; + return 2; + } + k = 0; + if (!(x & 0xffff)) { + k = 16; + x >>= 16; + } + if (!(x & 0xff)) { + k += 8; + x >>= 8; + } + if (!(x & 0xf)) { + k += 4; + x >>= 4; + } + if (!(x & 0x3)) { + k += 2; + x >>= 2; + } + if (!(x & 1)) { + k++; + x >>= 1; + if (!x) + return 32; + } + *y = x; + return k; + } + + static Bigint * +i2b +#ifdef KR_headers + (i) int i; +#else + (int i) +#endif +{ + Bigint *b; + + b = Balloc(1); + b->x[0] = i; + b->wds = 1; + return b; + } + + static Bigint * +mult +#ifdef KR_headers + (a, b) Bigint *a, *b; +#else + (Bigint *a, Bigint *b) +#endif +{ + Bigint *c; + int k, wa, wb, wc; + ULong *x, *xa, *xae, *xb, *xbe, *xc, *xc0; + ULong y; +#ifdef ULLong + ULLong carry, z; +#else + ULong carry, z; +#ifdef Pack_32 + ULong z2; +#endif +#endif + + if (a->wds < b->wds) { + c = a; + a = b; + b = c; + } + k = a->k; + wa = a->wds; + wb = b->wds; + wc = wa + wb; + if (wc > a->maxwds) + k++; + c = Balloc(k); + for(x = c->x, xa = x + wc; x < xa; x++) + *x = 0; + xa = a->x; + xae = xa + wa; + xb = b->x; + xbe = xb + wb; + xc0 = c->x; +#ifdef ULLong + for(; xb < xbe; xc0++) { + if ((y = *xb++)) { + x = xa; + xc = xc0; + carry = 0; + do { + z = *x++ * (ULLong)y + *xc + carry; + carry = z >> 32; + *xc++ = z & FFFFFFFF; + } + while(x < xae); + *xc = carry; + } + } +#else +#ifdef Pack_32 + for(; xb < xbe; xb++, xc0++) { + if (y = *xb & 0xffff) { + x = xa; + xc = xc0; + carry = 0; + do { + z = (*x & 0xffff) * y + (*xc & 0xffff) + carry; + carry = z >> 16; + z2 = (*x++ >> 16) * y + (*xc >> 16) + carry; + carry = z2 >> 16; + Storeinc(xc, z2, z); + } + while(x < xae); + *xc = carry; + } + if (y = *xb >> 16) { + x = xa; + xc = xc0; + carry = 0; + z2 = *xc; + do { + z = (*x & 0xffff) * y + (*xc >> 16) + carry; + carry = z >> 16; + Storeinc(xc, z, z2); + z2 = (*x++ >> 16) * y + (*xc & 0xffff) + carry; + carry = z2 >> 16; + } + while(x < xae); + *xc = z2; + } + } +#else + for(; xb < xbe; xc0++) { + if (y = *xb++) { + x = xa; + xc = xc0; + carry = 0; + do { + z = *x++ * y + *xc + carry; + carry = z >> 16; + *xc++ = z & 0xffff; + } + while(x < xae); + *xc = carry; + } + } +#endif +#endif + for(xc0 = c->x, xc = xc0 + wc; wc > 0 && !*--xc; --wc) ; + c->wds = wc; + return c; + } + + static Bigint *p5s; + + static Bigint * +pow5mult +#ifdef KR_headers + (b, k) Bigint *b; int k; +#else + (Bigint *b, int k) +#endif +{ + Bigint *b1, *p5, *p51; + int i; + static int p05[3] = { 5, 25, 125 }; + + if ((i = k & 3)) + b = multadd(b, p05[i-1], 0); + + if (!(k >>= 2)) + return b; + if (!(p5 = p5s)) { + /* first time */ +#ifdef MULTIPLE_THREADS + ACQUIRE_DTOA_LOCK(1); + if (!(p5 = p5s)) { + p5 = p5s = i2b(625); + p5->next = 0; + } + FREE_DTOA_LOCK(1); +#else + p5 = p5s = i2b(625); + p5->next = 0; +#endif + } + for(;;) { + if (k & 1) { + b1 = mult(b, p5); + Bfree(b); + b = b1; + } + if (!(k >>= 1)) + break; + if (!(p51 = p5->next)) { +#ifdef MULTIPLE_THREADS + ACQUIRE_DTOA_LOCK(1); + if (!(p51 = p5->next)) { + p51 = p5->next = mult(p5,p5); + p51->next = 0; + } + FREE_DTOA_LOCK(1); +#else + p51 = p5->next = mult(p5,p5); + p51->next = 0; +#endif + } + p5 = p51; + } + return b; + } + + static Bigint * +lshift +#ifdef KR_headers + (b, k) Bigint *b; int k; +#else + (Bigint *b, int k) +#endif +{ + int i, k1, n, n1; + Bigint *b1; + ULong *x, *x1, *xe, z; + +#ifdef Pack_32 + n = k >> 5; +#else + n = k >> 4; +#endif + k1 = b->k; + n1 = n + b->wds + 1; + for(i = b->maxwds; n1 > i; i <<= 1) + k1++; + b1 = Balloc(k1); + x1 = b1->x; + for(i = 0; i < n; i++) + *x1++ = 0; + x = b->x; + xe = x + b->wds; +#ifdef Pack_32 + if (k &= 0x1f) { + k1 = 32 - k; + z = 0; + do { + *x1++ = *x << k | z; + z = *x++ >> k1; + } + while(x < xe); + if ((*x1 = z)) + ++n1; + } +#else + if (k &= 0xf) { + k1 = 16 - k; + z = 0; + do { + *x1++ = *x << k & 0xffff | z; + z = *x++ >> k1; + } + while(x < xe); + if (*x1 = z) + ++n1; + } +#endif + else do + *x1++ = *x++; + while(x < xe); + b1->wds = n1 - 1; + Bfree(b); + return b1; + } + + static int +cmp +#ifdef KR_headers + (a, b) Bigint *a, *b; +#else + (Bigint *a, Bigint *b) +#endif +{ + ULong *xa, *xa0, *xb, *xb0; + int i, j; + + i = a->wds; + j = b->wds; +#ifdef DEBUG + if (i > 1 && !a->x[i-1]) + Bug("cmp called with a->x[a->wds-1] == 0"); + if (j > 1 && !b->x[j-1]) + Bug("cmp called with b->x[b->wds-1] == 0"); +#endif + if (i -= j) + return i; + xa0 = a->x; + xa = xa0 + j; + xb0 = b->x; + xb = xb0 + j; + for(;;) { + if (*--xa != *--xb) + return *xa < *xb ? -1 : 1; + if (xa <= xa0) + break; + } + return 0; + } + + static Bigint * +diff +#ifdef KR_headers + (a, b) Bigint *a, *b; +#else + (Bigint *a, Bigint *b) +#endif +{ + Bigint *c; + int i, wa, wb; + ULong *xa, *xae, *xb, *xbe, *xc; +#ifdef ULLong + ULLong borrow, y; +#else + ULong borrow, y; +#ifdef Pack_32 + ULong z; +#endif +#endif + + i = cmp(a,b); + if (!i) { + c = Balloc(0); + c->wds = 1; + c->x[0] = 0; + return c; + } + if (i < 0) { + c = a; + a = b; + b = c; + i = 1; + } + else + i = 0; + c = Balloc(a->k); + c->sign = i; + wa = a->wds; + xa = a->x; + xae = xa + wa; + wb = b->wds; + xb = b->x; + xbe = xb + wb; + xc = c->x; + borrow = 0; +#ifdef ULLong + do { + y = (ULLong)*xa++ - *xb++ - borrow; + borrow = y >> 32 & (ULong)1; + *xc++ = y & FFFFFFFF; + } + while(xb < xbe); + while(xa < xae) { + y = *xa++ - borrow; + borrow = y >> 32 & (ULong)1; + *xc++ = y & FFFFFFFF; + } +#else +#ifdef Pack_32 + do { + y = (*xa & 0xffff) - (*xb & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + z = (*xa++ >> 16) - (*xb++ >> 16) - borrow; + borrow = (z & 0x10000) >> 16; + Storeinc(xc, z, y); + } + while(xb < xbe); + while(xa < xae) { + y = (*xa & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + z = (*xa++ >> 16) - borrow; + borrow = (z & 0x10000) >> 16; + Storeinc(xc, z, y); + } +#else + do { + y = *xa++ - *xb++ - borrow; + borrow = (y & 0x10000) >> 16; + *xc++ = y & 0xffff; + } + while(xb < xbe); + while(xa < xae) { + y = *xa++ - borrow; + borrow = (y & 0x10000) >> 16; + *xc++ = y & 0xffff; + } +#endif +#endif + while(!*--xc) + wa--; + c->wds = wa; + return c; + } + + static double +ulp +#ifdef KR_headers + (x) U *x; +#else + (U *x) +#endif +{ + Long L; + U u; + + L = (word0(x) & Exp_mask) - (P-1)*Exp_msk1; +#ifndef Avoid_Underflow +#ifndef Sudden_Underflow + if (L > 0) { +#endif +#endif +#ifdef IBM + L |= Exp_msk1 >> 4; +#endif + word0(&u) = L; + word1(&u) = 0; +#ifndef Avoid_Underflow +#ifndef Sudden_Underflow + } + else { + L = -L >> Exp_shift; + if (L < Exp_shift) { + word0(&u) = 0x80000 >> L; + word1(&u) = 0; + } + else { + word0(&u) = 0; + L -= Exp_shift; + word1(&u) = L >= 31 ? 1 : 1 << 31 - L; + } + } +#endif +#endif + return dval(&u); + } + + static double +b2d +#ifdef KR_headers + (a, e) Bigint *a; int *e; +#else + (Bigint *a, int *e) +#endif +{ + ULong *xa, *xa0, w, y, z; + int k; + U d; +#ifdef VAX + ULong d0, d1; +#else +#define d0 word0(&d) +#define d1 word1(&d) +#endif + + xa0 = a->x; + xa = xa0 + a->wds; + y = *--xa; +#ifdef DEBUG + if (!y) Bug("zero y in b2d"); +#endif + k = hi0bits(y); + *e = 32 - k; +#ifdef Pack_32 + if (k < Ebits) { + d0 = Exp_1 | y >> (Ebits - k); + w = xa > xa0 ? *--xa : 0; + d1 = y << ((32-Ebits) + k) | w >> (Ebits - k); + goto ret_d; + } + z = xa > xa0 ? *--xa : 0; + if (k -= Ebits) { + d0 = Exp_1 | y << k | z >> (32 - k); + y = xa > xa0 ? *--xa : 0; + d1 = z << k | y >> (32 - k); + } + else { + d0 = Exp_1 | y; + d1 = z; + } +#else + if (k < Ebits + 16) { + z = xa > xa0 ? *--xa : 0; + d0 = Exp_1 | y << k - Ebits | z >> Ebits + 16 - k; + w = xa > xa0 ? *--xa : 0; + y = xa > xa0 ? *--xa : 0; + d1 = z << k + 16 - Ebits | w << k - Ebits | y >> 16 + Ebits - k; + goto ret_d; + } + z = xa > xa0 ? *--xa : 0; + w = xa > xa0 ? *--xa : 0; + k -= Ebits + 16; + d0 = Exp_1 | y << k + 16 | z << k | w >> 16 - k; + y = xa > xa0 ? *--xa : 0; + d1 = w << k + 16 | y << k; +#endif + ret_d: +#ifdef VAX + word0(&d) = d0 >> 16 | d0 << 16; + word1(&d) = d1 >> 16 | d1 << 16; +#else +#undef d0 +#undef d1 +#endif + return dval(&d); + } + + static Bigint * +d2b +#ifdef KR_headers + (d, e, bits) U *d; int *e, *bits; +#else + (U *d, int *e, int *bits) +#endif +{ + Bigint *b; + int de, k; + ULong *x, y, z; +#ifndef Sudden_Underflow + int i; +#endif +#ifdef VAX + ULong d0, d1; + d0 = word0(d) >> 16 | word0(d) << 16; + d1 = word1(d) >> 16 | word1(d) << 16; +#else +#define d0 word0(d) +#define d1 word1(d) +#endif + +#ifdef Pack_32 + b = Balloc(1); +#else + b = Balloc(2); +#endif + x = b->x; + + z = d0 & Frac_mask; + d0 &= 0x7fffffff; /* clear sign bit, which we ignore */ +#ifdef Sudden_Underflow + de = (int)(d0 >> Exp_shift); +#ifndef IBM + z |= Exp_msk11; +#endif +#else + if ((de = (int)(d0 >> Exp_shift))) + z |= Exp_msk1; +#endif +#ifdef Pack_32 + if ((y = d1)) { + if ((k = lo0bits(&y))) { + x[0] = y | z << (32 - k); + z >>= k; + } + else + x[0] = y; +#ifndef Sudden_Underflow + i = +#endif + b->wds = (x[1] = z) ? 2 : 1; + } + else { + k = lo0bits(&z); + x[0] = z; +#ifndef Sudden_Underflow + i = +#endif + b->wds = 1; + k += 32; + } +#else + if (y = d1) { + if (k = lo0bits(&y)) + if (k >= 16) { + x[0] = y | z << 32 - k & 0xffff; + x[1] = z >> k - 16 & 0xffff; + x[2] = z >> k; + i = 2; + } + else { + x[0] = y & 0xffff; + x[1] = y >> 16 | z << 16 - k & 0xffff; + x[2] = z >> k & 0xffff; + x[3] = z >> k+16; + i = 3; + } + else { + x[0] = y & 0xffff; + x[1] = y >> 16; + x[2] = z & 0xffff; + x[3] = z >> 16; + i = 3; + } + } + else { +#ifdef DEBUG + if (!z) + Bug("Zero passed to d2b"); +#endif + k = lo0bits(&z); + if (k >= 16) { + x[0] = z; + i = 0; + } + else { + x[0] = z & 0xffff; + x[1] = z >> 16; + i = 1; + } + k += 32; + } + while(!x[i]) + --i; + b->wds = i + 1; +#endif +#ifndef Sudden_Underflow + if (de) { +#endif +#ifdef IBM + *e = (de - Bias - (P-1) << 2) + k; + *bits = 4*P + 8 - k - hi0bits(word0(d) & Frac_mask); +#else + *e = de - Bias - (P-1) + k; + *bits = P - k; +#endif +#ifndef Sudden_Underflow + } + else { + *e = de - Bias - (P-1) + 1 + k; +#ifdef Pack_32 + *bits = 32*i - hi0bits(x[i-1]); +#else + *bits = (i+2)*16 - hi0bits(x[i]); +#endif + } +#endif + return b; + } +#undef d0 +#undef d1 + + static double +ratio +#ifdef KR_headers + (a, b) Bigint *a, *b; +#else + (Bigint *a, Bigint *b) +#endif +{ + U da, db; + int k, ka, kb; + + dval(&da) = b2d(a, &ka); + dval(&db) = b2d(b, &kb); +#ifdef Pack_32 + k = ka - kb + 32*(a->wds - b->wds); +#else + k = ka - kb + 16*(a->wds - b->wds); +#endif +#ifdef IBM + if (k > 0) { + word0(&da) += (k >> 2)*Exp_msk1; + if (k &= 3) + dval(&da) *= 1 << k; + } + else { + k = -k; + word0(&db) += (k >> 2)*Exp_msk1; + if (k &= 3) + dval(&db) *= 1 << k; + } +#else + if (k > 0) + word0(&da) += k*Exp_msk1; + else { + k = -k; + word0(&db) += k*Exp_msk1; + } +#endif + return dval(&da) / dval(&db); + } + + static CONST double +tens[] = { + 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, + 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, + 1e20, 1e21, 1e22 +#ifdef VAX + , 1e23, 1e24 +#endif + }; + + static CONST double +#ifdef IEEE_Arith +bigtens[] = { 1e16, 1e32, 1e64, 1e128, 1e256 }; +static CONST double tinytens[] = { 1e-16, 1e-32, 1e-64, 1e-128, +#ifdef Avoid_Underflow + 9007199254740992.*9007199254740992.e-256 + /* = 2^106 * 1e-256 */ +#else + 1e-256 +#endif + }; +/* The factor of 2^53 in tinytens[4] helps us avoid setting the underflow */ +/* flag unnecessarily. It leads to a song and dance at the end of strtod. */ +#define Scale_Bit 0x10 +#define n_bigtens 5 +#else +#ifdef IBM +bigtens[] = { 1e16, 1e32, 1e64 }; +static CONST double tinytens[] = { 1e-16, 1e-32, 1e-64 }; +#define n_bigtens 3 +#else +bigtens[] = { 1e16, 1e32 }; +static CONST double tinytens[] = { 1e-16, 1e-32 }; +#define n_bigtens 2 +#endif +#endif + +#undef Need_Hexdig +#ifdef INFNAN_CHECK +#ifndef No_Hex_NaN +#define Need_Hexdig +#endif +#endif + +#ifndef Need_Hexdig +#ifndef NO_HEX_FP +#define Need_Hexdig +#endif +#endif + +#ifdef Need_Hexdig /*{*/ +static unsigned char hexdig[256]; + + static void +#ifdef KR_headers +htinit(h, s, inc) unsigned char *h; unsigned char *s; int inc; +#else +htinit(unsigned char *h, unsigned char *s, int inc) +#endif +{ + int i, j; + for(i = 0; (j = s[i]) !=0; i++) + h[j] = i + inc; + } + + static void +#ifdef KR_headers +hexdig_init() +#else +hexdig_init(void) +#endif +{ +#define USC (unsigned char *) + htinit(hexdig, USC "0123456789", 0x10); + htinit(hexdig, USC "abcdef", 0x10 + 10); + htinit(hexdig, USC "ABCDEF", 0x10 + 10); + } +#endif /* } Need_Hexdig */ + +#ifdef INFNAN_CHECK + +#ifndef NAN_WORD0 +#define NAN_WORD0 0x7ff80000 +#endif + +#ifndef NAN_WORD1 +#define NAN_WORD1 0 +#endif + + static int +match +#ifdef KR_headers + (sp, t) char **sp, *t; +#else + (const char **sp, const char *t) +#endif +{ + int c, d; + CONST char *s = *sp; + + while((d = *t++)) { + if ((c = *++s) >= 'A' && c <= 'Z') + c += 'a' - 'A'; + if (c != d) + return 0; + } + *sp = s + 1; + return 1; + } + +#ifndef No_Hex_NaN + static void +hexnan +#ifdef KR_headers + (rvp, sp) U *rvp; CONST char **sp; +#else + (U *rvp, const char **sp) +#endif +{ + ULong c, x[2]; + CONST char *s; + int c1, havedig, udx0, xshift; + + if (!hexdig['0']) + hexdig_init(); + x[0] = x[1] = 0; + havedig = xshift = 0; + udx0 = 1; + s = *sp; + /* allow optional initial 0x or 0X */ + while((c = *(CONST unsigned char*)(s+1)) && c <= ' ') + ++s; + if (s[1] == '0' && (s[2] == 'x' || s[2] == 'X')) + s += 2; + while((c = *(CONST unsigned char*)++s)) { + if ((c1 = hexdig[c])) + c = c1 & 0xf; + else if (c <= ' ') { + if (udx0 && havedig) { + udx0 = 0; + xshift = 1; + } + continue; + } +#ifdef GDTOA_NON_PEDANTIC_NANCHECK + else if (/*(*/ c == ')' && havedig) { + *sp = s + 1; + break; + } + else + return; /* invalid form: don't change *sp */ +#else + else { + do { + if (/*(*/ c == ')') { + *sp = s + 1; + break; + } + } while((c = *++s)); + break; + } +#endif + havedig = 1; + if (xshift) { + xshift = 0; + x[0] = x[1]; + x[1] = 0; + } + if (udx0) + x[0] = (x[0] << 4) | (x[1] >> 28); + x[1] = (x[1] << 4) | c; + } + if ((x[0] &= 0xfffff) || x[1]) { + word0(rvp) = Exp_mask | x[0]; + word1(rvp) = x[1]; + } + } +#endif /*No_Hex_NaN*/ +#endif /* INFNAN_CHECK */ + +#ifdef Pack_32 +#define ULbits 32 +#define kshift 5 +#define kmask 31 +#else +#define ULbits 16 +#define kshift 4 +#define kmask 15 +#endif + +#if !defined(NO_HEX_FP) || defined(Honor_FLT_ROUNDS) /*{*/ + static Bigint * +#ifdef KR_headers +increment(b) Bigint *b; +#else +increment(Bigint *b) +#endif +{ + ULong *x, *xe; + Bigint *b1; + + x = b->x; + xe = x + b->wds; + do { + if (*x < (ULong)0xffffffffL) { + ++*x; + return b; + } + *x++ = 0; + } while(x < xe); + { + if (b->wds >= b->maxwds) { + b1 = Balloc(b->k+1); + Bcopy(b1,b); + Bfree(b); + b = b1; + } + b->x[b->wds++] = 1; + } + return b; + } + +#endif /*}*/ + +#ifndef NO_HEX_FP /*{*/ + + static void +#ifdef KR_headers +rshift(b, k) Bigint *b; int k; +#else +rshift(Bigint *b, int k) +#endif +{ + ULong *x, *x1, *xe, y; + int n; + + x = x1 = b->x; + n = k >> kshift; + if (n < b->wds) { + xe = x + b->wds; + x += n; + if (k &= kmask) { + n = 32 - k; + y = *x++ >> k; + while(x < xe) { + *x1++ = (y | (*x << n)) & 0xffffffff; + y = *x++ >> k; + } + if ((*x1 = y) !=0) + x1++; + } + else + while(x < xe) + *x1++ = *x++; + } + if ((b->wds = x1 - b->x) == 0) + b->x[0] = 0; + } + + static ULong +#ifdef KR_headers +any_on(b, k) Bigint *b; int k; +#else +any_on(Bigint *b, int k) +#endif +{ + int n, nwds; + ULong *x, *x0, x1, x2; + + x = b->x; + nwds = b->wds; + n = k >> kshift; + if (n > nwds) + n = nwds; + else if (n < nwds && (k &= kmask)) { + x1 = x2 = x[n]; + x1 >>= k; + x1 <<= k; + if (x1 != x2) + return 1; + } + x0 = x; + x += n; + while(x > x0) + if (*--x) + return 1; + return 0; + } + +enum { /* rounding values: same as FLT_ROUNDS */ + Round_zero = 0, + Round_near = 1, + Round_up = 2, + Round_down = 3 + }; + + void +#ifdef KR_headers +gethex(sp, rvp, rounding, sign) + CONST char **sp; U *rvp; int rounding, sign; +#else +gethex( CONST char **sp, U *rvp, int rounding, int sign) +#endif +{ + Bigint *b; + CONST unsigned char *decpt, *s0, *s, *s1; + Long e, e1; + ULong L, lostbits, *x; + int big, denorm, esign, havedig, k, n, nbits, up, zret; +#ifdef IBM + int j; +#endif + enum { +#ifdef IEEE_Arith /*{{*/ + emax = 0x7fe - Bias - P + 1, + emin = Emin - P + 1 +#else /*}{*/ + emin = Emin - P, +#ifdef VAX + emax = 0x7ff - Bias - P + 1 +#endif +#ifdef IBM + emax = 0x7f - Bias - P +#endif +#endif /*}}*/ + }; +#ifdef USE_LOCALE + int i; +#ifdef NO_LOCALE_CACHE + const unsigned char *decimalpoint = (unsigned char*) + localeconv()->decimal_point; +#else + const unsigned char *decimalpoint; + static unsigned char *decimalpoint_cache; + if (!(s0 = decimalpoint_cache)) { + s0 = (unsigned char*)localeconv()->decimal_point; + if ((decimalpoint_cache = (unsigned char*) + MALLOC(strlen((CONST char*)s0) + 1))) { + strcpy((char*)decimalpoint_cache, (CONST char*)s0); + s0 = decimalpoint_cache; + } + } + decimalpoint = s0; +#endif +#endif + + if (!hexdig['0']) + hexdig_init(); + havedig = 0; + s0 = *(CONST unsigned char **)sp + 2; + while(s0[havedig] == '0') + havedig++; + s0 += havedig; + s = s0; + decpt = 0; + zret = 0; + e = 0; + if (hexdig[*s]) + havedig++; + else { + zret = 1; +#ifdef USE_LOCALE + for(i = 0; decimalpoint[i]; ++i) { + if (s[i] != decimalpoint[i]) + goto pcheck; + } + decpt = s += i; +#else + if (*s != '.') + goto pcheck; + decpt = ++s; +#endif + if (!hexdig[*s]) + goto pcheck; + while(*s == '0') + s++; + if (hexdig[*s]) + zret = 0; + havedig = 1; + s0 = s; + } + while(hexdig[*s]) + s++; +#ifdef USE_LOCALE + if (*s == *decimalpoint && !decpt) { + for(i = 1; decimalpoint[i]; ++i) { + if (s[i] != decimalpoint[i]) + goto pcheck; + } + decpt = s += i; +#else + if (*s == '.' && !decpt) { + decpt = ++s; +#endif + while(hexdig[*s]) + s++; + }/*}*/ + if (decpt) + e = -(((Long)(s-decpt)) << 2); + pcheck: + s1 = s; + big = esign = 0; + switch(*s) { + case 'p': + case 'P': + switch(*++s) { + case '-': + esign = 1; + /* no break */ + case '+': + s++; + } + if ((n = hexdig[*s]) == 0 || n > 0x19) { + s = s1; + break; + } + e1 = n - 0x10; + while((n = hexdig[*++s]) !=0 && n <= 0x19) { + if (e1 & 0xf8000000) + big = 1; + e1 = 10*e1 + n - 0x10; + } + if (esign) + e1 = -e1; + e += e1; + } + *sp = (char*)s; + if (!havedig) + *sp = (char*)s0 - 1; + if (zret) + goto retz1; + if (big) { + if (esign) { +#ifdef IEEE_Arith + switch(rounding) { + case Round_up: + if (sign) + break; + goto ret_tiny; + case Round_down: + if (!sign) + break; + goto ret_tiny; + } +#endif + goto retz; +#ifdef IEEE_Arith + ret_tiny: +#ifndef NO_ERRNO + errno = ERANGE; +#endif + word0(rvp) = 0; + word1(rvp) = 1; + return; +#endif /* IEEE_Arith */ + } + switch(rounding) { + case Round_near: + goto ovfl1; + case Round_up: + if (!sign) + goto ovfl1; + goto ret_big; + case Round_down: + if (sign) + goto ovfl1; + goto ret_big; + } + ret_big: + word0(rvp) = Big0; + word1(rvp) = Big1; + return; + } + n = s1 - s0 - 1; + for(k = 0; n > (1 << (kshift-2)) - 1; n >>= 1) + k++; + b = Balloc(k); + x = b->x; + n = 0; + L = 0; +#ifdef USE_LOCALE + for(i = 0; decimalpoint[i+1]; ++i); +#endif + while(s1 > s0) { +#ifdef USE_LOCALE + if (*--s1 == decimalpoint[i]) { + s1 -= i; + continue; + } +#else + if (*--s1 == '.') + continue; +#endif + if (n == ULbits) { + *x++ = L; + L = 0; + n = 0; + } + L |= (hexdig[*s1] & 0x0f) << n; + n += 4; + } + *x++ = L; + b->wds = n = x - b->x; + n = ULbits*n - hi0bits(L); + nbits = Nbits; + lostbits = 0; + x = b->x; + if (n > nbits) { + n -= nbits; + if (any_on(b,n)) { + lostbits = 1; + k = n - 1; + if (x[k>>kshift] & 1 << (k & kmask)) { + lostbits = 2; + if (k > 0 && any_on(b,k)) + lostbits = 3; + } + } + rshift(b, n); + e += n; + } + else if (n < nbits) { + n = nbits - n; + b = lshift(b, n); + e -= n; + x = b->x; + } + if (e > Emax) { + ovfl: + Bfree(b); + ovfl1: +#ifndef NO_ERRNO + errno = ERANGE; +#endif + word0(rvp) = Exp_mask; + word1(rvp) = 0; + return; + } + denorm = 0; + if (e < emin) { + denorm = 1; + n = emin - e; + if (n >= nbits) { +#ifdef IEEE_Arith /*{*/ + switch (rounding) { + case Round_near: + if (n == nbits && (n < 2 || any_on(b,n-1))) + goto ret_tiny; + break; + case Round_up: + if (!sign) + goto ret_tiny; + break; + case Round_down: + if (sign) + goto ret_tiny; + } +#endif /* } IEEE_Arith */ + Bfree(b); + retz: +#ifndef NO_ERRNO + errno = ERANGE; +#endif + retz1: + rvp->d = 0.; + return; + } + k = n - 1; + if (lostbits) + lostbits = 1; + else if (k > 0) + lostbits = any_on(b,k); + if (x[k>>kshift] & 1 << (k & kmask)) + lostbits |= 2; + nbits -= n; + rshift(b,n); + e = emin; + } + if (lostbits) { + up = 0; + switch(rounding) { + case Round_zero: + break; + case Round_near: + if (lostbits & 2 + && (lostbits & 1) | (x[0] & 1)) + up = 1; + break; + case Round_up: + up = 1 - sign; + break; + case Round_down: + up = sign; + } + if (up) { + k = b->wds; + b = increment(b); + x = b->x; + if (denorm) { +#if 0 + if (nbits == Nbits - 1 + && x[nbits >> kshift] & 1 << (nbits & kmask)) + denorm = 0; /* not currently used */ +#endif + } + else if (b->wds > k + || ((n = nbits & kmask) !=0 + && hi0bits(x[k-1]) < 32-n)) { + rshift(b,1); + if (++e > Emax) + goto ovfl; + } + } + } +#ifdef IEEE_Arith + if (denorm) + word0(rvp) = b->wds > 1 ? b->x[1] & ~0x100000 : 0; + else + word0(rvp) = (b->x[1] & ~0x100000) | ((e + 0x3ff + 52) << 20); + word1(rvp) = b->x[0]; +#endif +#ifdef IBM + if ((j = e & 3)) { + k = b->x[0] & ((1 << j) - 1); + rshift(b,j); + if (k) { + switch(rounding) { + case Round_up: + if (!sign) + increment(b); + break; + case Round_down: + if (sign) + increment(b); + break; + case Round_near: + j = 1 << (j-1); + if (k & j && ((k & (j-1)) | lostbits)) + increment(b); + } + } + } + e >>= 2; + word0(rvp) = b->x[1] | ((e + 65 + 13) << 24); + word1(rvp) = b->x[0]; +#endif +#ifdef VAX + /* The next two lines ignore swap of low- and high-order 2 bytes. */ + /* word0(rvp) = (b->x[1] & ~0x800000) | ((e + 129 + 55) << 23); */ + /* word1(rvp) = b->x[0]; */ + word0(rvp) = ((b->x[1] & ~0x800000) >> 16) | ((e + 129 + 55) << 7) | (b->x[1] << 16); + word1(rvp) = (b->x[0] >> 16) | (b->x[0] << 16); +#endif + Bfree(b); + } +#endif /*!NO_HEX_FP}*/ + + static int +#ifdef KR_headers +dshift(b, p2) Bigint *b; int p2; +#else +dshift(Bigint *b, int p2) +#endif +{ + int rv = hi0bits(b->x[b->wds-1]) - 4; + if (p2 > 0) + rv -= p2; + return rv & kmask; + } + + static int +quorem +#ifdef KR_headers + (b, S) Bigint *b, *S; +#else + (Bigint *b, Bigint *S) +#endif +{ + int n; + ULong *bx, *bxe, q, *sx, *sxe; +#ifdef ULLong + ULLong borrow, carry, y, ys; +#else + ULong borrow, carry, y, ys; +#ifdef Pack_32 + ULong si, z, zs; +#endif +#endif + + n = S->wds; +#ifdef DEBUG + /*debug*/ if (b->wds > n) + /*debug*/ Bug("oversize b in quorem"); +#endif + if (b->wds < n) + return 0; + sx = S->x; + sxe = sx + --n; + bx = b->x; + bxe = bx + n; + q = *bxe / (*sxe + 1); /* ensure q <= true quotient */ +#ifdef DEBUG +#ifdef NO_STRTOD_BIGCOMP + /*debug*/ if (q > 9) +#else + /* An oversized q is possible when quorem is called from bigcomp and */ + /* the input is near, e.g., twice the smallest denormalized number. */ + /*debug*/ if (q > 15) +#endif + /*debug*/ Bug("oversized quotient in quorem"); +#endif + if (q) { + borrow = 0; + carry = 0; + do { +#ifdef ULLong + ys = *sx++ * (ULLong)q + carry; + carry = ys >> 32; + y = *bx - (ys & FFFFFFFF) - borrow; + borrow = y >> 32 & (ULong)1; + *bx++ = y & FFFFFFFF; +#else +#ifdef Pack_32 + si = *sx++; + ys = (si & 0xffff) * q + carry; + zs = (si >> 16) * q + (ys >> 16); + carry = zs >> 16; + y = (*bx & 0xffff) - (ys & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + z = (*bx >> 16) - (zs & 0xffff) - borrow; + borrow = (z & 0x10000) >> 16; + Storeinc(bx, z, y); +#else + ys = *sx++ * q + carry; + carry = ys >> 16; + y = *bx - (ys & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + *bx++ = y & 0xffff; +#endif +#endif + } + while(sx <= sxe); + if (!*bxe) { + bx = b->x; + while(--bxe > bx && !*bxe) + --n; + b->wds = n; + } + } + if (cmp(b, S) >= 0) { + q++; + borrow = 0; + carry = 0; + bx = b->x; + sx = S->x; + do { +#ifdef ULLong + ys = *sx++ + carry; + carry = ys >> 32; + y = *bx - (ys & FFFFFFFF) - borrow; + borrow = y >> 32 & (ULong)1; + *bx++ = y & FFFFFFFF; +#else +#ifdef Pack_32 + si = *sx++; + ys = (si & 0xffff) + carry; + zs = (si >> 16) + (ys >> 16); + carry = zs >> 16; + y = (*bx & 0xffff) - (ys & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + z = (*bx >> 16) - (zs & 0xffff) - borrow; + borrow = (z & 0x10000) >> 16; + Storeinc(bx, z, y); +#else + ys = *sx++ + carry; + carry = ys >> 16; + y = *bx - (ys & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + *bx++ = y & 0xffff; +#endif +#endif + } + while(sx <= sxe); + bx = b->x; + bxe = bx + n; + if (!*bxe) { + while(--bxe > bx && !*bxe) + --n; + b->wds = n; + } + } + return q; + } + +#if defined(Avoid_Underflow) || !defined(NO_STRTOD_BIGCOMP) /*{*/ + static double +sulp +#ifdef KR_headers + (x, bc) U *x; BCinfo *bc; +#else + (U *x, BCinfo *bc) +#endif +{ + U u; + double rv; + int i; + + rv = ulp(x); + if (!bc->scale || (i = 2*P + 1 - ((word0(x) & Exp_mask) >> Exp_shift)) <= 0) + return rv; /* Is there an example where i <= 0 ? */ + word0(&u) = Exp_1 + (i << Exp_shift); + word1(&u) = 0; + return rv * u.d; + } +#endif /*}*/ + +#ifndef NO_STRTOD_BIGCOMP + static void +bigcomp +#ifdef KR_headers + (rv, s0, bc) + U *rv; CONST char *s0; BCinfo *bc; +#else + (U *rv, const char *s0, BCinfo *bc) +#endif +{ + Bigint *b, *d; + int b2, bbits, d2, dd, dig, dsign, i, j, nd, nd0, p2, p5, speccase; + + dsign = bc->dsign; + nd = bc->nd; + nd0 = bc->nd0; + p5 = nd + bc->e0 - 1; + speccase = 0; +#ifndef Sudden_Underflow + if (rv->d == 0.) { /* special case: value near underflow-to-zero */ + /* threshold was rounded to zero */ + b = i2b(1); + p2 = Emin - P + 1; + bbits = 1; +#ifdef Avoid_Underflow + word0(rv) = (P+2) << Exp_shift; +#else + word1(rv) = 1; +#endif + i = 0; +#ifdef Honor_FLT_ROUNDS + if (bc->rounding == 1) +#endif + { + speccase = 1; + --p2; + dsign = 0; + goto have_i; + } + } + else +#endif + b = d2b(rv, &p2, &bbits); +#ifdef Avoid_Underflow + p2 -= bc->scale; +#endif + /* floor(log2(rv)) == bbits - 1 + p2 */ + /* Check for denormal case. */ + i = P - bbits; + if (i > (j = P - Emin - 1 + p2)) { +#ifdef Sudden_Underflow + Bfree(b); + b = i2b(1); + p2 = Emin; + i = P - 1; +#ifdef Avoid_Underflow + word0(rv) = (1 + bc->scale) << Exp_shift; +#else + word0(rv) = Exp_msk1; +#endif + word1(rv) = 0; +#else + i = j; +#endif + } +#ifdef Honor_FLT_ROUNDS + if (bc->rounding != 1) { + if (i > 0) + b = lshift(b, i); + if (dsign) + b = increment(b); + } + else +#endif + { + b = lshift(b, ++i); + b->x[0] |= 1; + } +#ifndef Sudden_Underflow + have_i: +#endif + p2 -= p5 + i; + d = i2b(1); + /* Arrange for convenient computation of quotients: + * shift left if necessary so divisor has 4 leading 0 bits. + */ + if (p5 > 0) + d = pow5mult(d, p5); + else if (p5 < 0) + b = pow5mult(b, -p5); + if (p2 > 0) { + b2 = p2; + d2 = 0; + } + else { + b2 = 0; + d2 = -p2; + } + i = dshift(d, d2); + if ((b2 += i) > 0) + b = lshift(b, b2); + if ((d2 += i) > 0) + d = lshift(d, d2); + + /* Now b/d = exactly half-way between the two floating-point values */ + /* on either side of the input string. Compute first digit of b/d. */ + + if (!(dig = quorem(b,d))) { + b = multadd(b, 10, 0); /* very unlikely */ + dig = quorem(b,d); + } + + /* Compare b/d with s0 */ + + for(i = 0; i < nd0; ) { + if ((dd = s0[i++] - '0' - dig)) + goto ret; + if (!b->x[0] && b->wds == 1) { + if (i < nd) + dd = 1; + goto ret; + } + b = multadd(b, 10, 0); + dig = quorem(b,d); + } + for(j = bc->dp1; i++ < nd;) { + if ((dd = s0[j++] - '0' - dig)) + goto ret; + if (!b->x[0] && b->wds == 1) { + if (i < nd) + dd = 1; + goto ret; + } + b = multadd(b, 10, 0); + dig = quorem(b,d); + } + if (b->x[0] || b->wds > 1) + dd = -1; + ret: + Bfree(b); + Bfree(d); +#ifdef Honor_FLT_ROUNDS + if (bc->rounding != 1) { + if (dd < 0) { + if (bc->rounding == 0) { + if (!dsign) + goto retlow1; + } + else if (dsign) + goto rethi1; + } + else if (dd > 0) { + if (bc->rounding == 0) { + if (dsign) + goto rethi1; + goto ret1; + } + if (!dsign) + goto rethi1; + dval(rv) += 2.*sulp(rv,bc); + } + else { + bc->inexact = 0; + if (dsign) + goto rethi1; + } + } + else +#endif + if (speccase) { + if (dd <= 0) + rv->d = 0.; + } + else if (dd < 0) { + if (!dsign) /* does not happen for round-near */ +retlow1: + dval(rv) -= sulp(rv,bc); + } + else if (dd > 0) { + if (dsign) { + rethi1: + dval(rv) += sulp(rv,bc); + } + } + else { + /* Exact half-way case: apply round-even rule. */ + if ((j = ((word0(rv) & Exp_mask) >> Exp_shift) - bc->scale) <= 0) { + i = 1 - j; + if (i <= 31) { + if (word1(rv) & (0x1 << i)) + goto odd; + } + else if (word0(rv) & (0x1 << (i-32))) + goto odd; + } + else if (word1(rv) & 1) { + odd: + if (dsign) + goto rethi1; + goto retlow1; + } + } + +#ifdef Honor_FLT_ROUNDS + ret1: +#endif + return; + } +#endif /* NO_STRTOD_BIGCOMP */ + + double +fpconv_strtod +#ifdef KR_headers + (s00, se) CONST char *s00; char **se; +#else + (const char *s00, char **se) +#endif +{ + int bb2, bb5, bbe, bd2, bd5, bbbits, bs2, c, e, e1; + int esign, i, j, k, nd, nd0, nf, nz, nz0, nz1, sign; + CONST char *s, *s0, *s1; + double aadj, aadj1; + Long L; + U aadj2, adj, rv, rv0; + ULong y, z; + BCinfo bc; + Bigint *bb, *bb1, *bd, *bd0, *bs, *delta; +#ifdef Avoid_Underflow + ULong Lsb, Lsb1; +#endif +#ifdef SET_INEXACT + int oldinexact; +#endif +#ifndef NO_STRTOD_BIGCOMP + int req_bigcomp = 0; +#endif +#ifdef Honor_FLT_ROUNDS /*{*/ +#ifdef Trust_FLT_ROUNDS /*{{ only define this if FLT_ROUNDS really works! */ + bc.rounding = Flt_Rounds; +#else /*}{*/ + bc.rounding = 1; + switch(fegetround()) { + case FE_TOWARDZERO: bc.rounding = 0; break; + case FE_UPWARD: bc.rounding = 2; break; + case FE_DOWNWARD: bc.rounding = 3; + } +#endif /*}}*/ +#endif /*}*/ +#ifdef USE_LOCALE + CONST char *s2; +#endif + + sign = nz0 = nz1 = nz = bc.dplen = bc.uflchk = 0; + dval(&rv) = 0.; + for(s = s00;;s++) switch(*s) { + case '-': + sign = 1; + /* no break */ + case '+': + if (*++s) + goto break2; + /* no break */ + case 0: + goto ret0; + case '\t': + case '\n': + case '\v': + case '\f': + case '\r': + case ' ': + continue; + default: + goto break2; + } + break2: + if (*s == '0') { +#ifndef NO_HEX_FP /*{*/ + switch(s[1]) { + case 'x': + case 'X': +#ifdef Honor_FLT_ROUNDS + gethex(&s, &rv, bc.rounding, sign); +#else + gethex(&s, &rv, 1, sign); +#endif + goto ret; + } +#endif /*}*/ + nz0 = 1; + while(*++s == '0') ; + if (!*s) + goto ret; + } + s0 = s; + y = z = 0; + for(nd = nf = 0; (c = *s) >= '0' && c <= '9'; nd++, s++) + if (nd < 9) + y = 10*y + c - '0'; + else if (nd < 16) + z = 10*z + c - '0'; + nd0 = nd; + bc.dp0 = bc.dp1 = s - s0; + for(s1 = s; s1 > s0 && *--s1 == '0'; ) + ++nz1; +#ifdef USE_LOCALE + s1 = localeconv()->decimal_point; + if (c == *s1) { + c = '.'; + if (*++s1) { + s2 = s; + for(;;) { + if (*++s2 != *s1) { + c = 0; + break; + } + if (!*++s1) { + s = s2; + break; + } + } + } + } +#endif + if (c == '.') { + c = *++s; + bc.dp1 = s - s0; + bc.dplen = bc.dp1 - bc.dp0; + if (!nd) { + for(; c == '0'; c = *++s) + nz++; + if (c > '0' && c <= '9') { + bc.dp0 = s0 - s; + bc.dp1 = bc.dp0 + bc.dplen; + s0 = s; + nf += nz; + nz = 0; + goto have_dig; + } + goto dig_done; + } + for(; c >= '0' && c <= '9'; c = *++s) { + have_dig: + nz++; + if (c -= '0') { + nf += nz; + for(i = 1; i < nz; i++) + if (nd++ < 9) + y *= 10; + else if (nd <= DBL_DIG + 1) + z *= 10; + if (nd++ < 9) + y = 10*y + c; + else if (nd <= DBL_DIG + 1) + z = 10*z + c; + nz = nz1 = 0; + } + } + } + dig_done: + e = 0; + if (c == 'e' || c == 'E') { + if (!nd && !nz && !nz0) { + goto ret0; + } + s00 = s; + esign = 0; + switch(c = *++s) { + case '-': + esign = 1; + case '+': + c = *++s; + } + if (c >= '0' && c <= '9') { + while(c == '0') + c = *++s; + if (c > '0' && c <= '9') { + L = c - '0'; + s1 = s; + while((c = *++s) >= '0' && c <= '9') + L = 10*L + c - '0'; + if (s - s1 > 8 || L > 19999) + /* Avoid confusion from exponents + * so large that e might overflow. + */ + e = 19999; /* safe for 16 bit ints */ + else + e = (int)L; + if (esign) + e = -e; + } + else + e = 0; + } + else + s = s00; + } + if (!nd) { + if (!nz && !nz0) { +#ifdef INFNAN_CHECK + /* Check for Nan and Infinity */ + if (!bc.dplen) + switch(c) { + case 'i': + case 'I': + if (match(&s,"nf")) { + --s; + if (!match(&s,"inity")) + ++s; + word0(&rv) = 0x7ff00000; + word1(&rv) = 0; + goto ret; + } + break; + case 'n': + case 'N': + if (match(&s, "an")) { + word0(&rv) = NAN_WORD0; + word1(&rv) = NAN_WORD1; +#ifndef No_Hex_NaN + if (*s == '(') /*)*/ + hexnan(&rv, &s); +#endif + goto ret; + } + } +#endif /* INFNAN_CHECK */ + ret0: + s = s00; + sign = 0; + } + goto ret; + } + bc.e0 = e1 = e -= nf; + + /* Now we have nd0 digits, starting at s0, followed by a + * decimal point, followed by nd-nd0 digits. The number we're + * after is the integer represented by those digits times + * 10**e */ + + if (!nd0) + nd0 = nd; + k = nd < DBL_DIG + 1 ? nd : DBL_DIG + 1; + dval(&rv) = y; + if (k > 9) { +#ifdef SET_INEXACT + if (k > DBL_DIG) + oldinexact = get_inexact(); +#endif + dval(&rv) = tens[k - 9] * dval(&rv) + z; + } + bd0 = 0; + if (nd <= DBL_DIG +#ifndef RND_PRODQUOT +#ifndef Honor_FLT_ROUNDS + && Flt_Rounds == 1 +#endif +#endif + ) { + if (!e) + goto ret; +#ifndef ROUND_BIASED_without_Round_Up + if (e > 0) { + if (e <= Ten_pmax) { +#ifdef VAX + goto vax_ovfl_check; +#else +#ifdef Honor_FLT_ROUNDS + /* round correctly FLT_ROUNDS = 2 or 3 */ + if (sign) { + rv.d = -rv.d; + sign = 0; + } +#endif + /* rv = */ rounded_product(dval(&rv), tens[e]); + goto ret; +#endif + } + i = DBL_DIG - nd; + if (e <= Ten_pmax + i) { + /* A fancier test would sometimes let us do + * this for larger i values. + */ +#ifdef Honor_FLT_ROUNDS + /* round correctly FLT_ROUNDS = 2 or 3 */ + if (sign) { + rv.d = -rv.d; + sign = 0; + } +#endif + e -= i; + dval(&rv) *= tens[i]; +#ifdef VAX + /* VAX exponent range is so narrow we must + * worry about overflow here... + */ + vax_ovfl_check: + word0(&rv) -= P*Exp_msk1; + /* rv = */ rounded_product(dval(&rv), tens[e]); + if ((word0(&rv) & Exp_mask) + > Exp_msk1*(DBL_MAX_EXP+Bias-1-P)) + goto ovfl; + word0(&rv) += P*Exp_msk1; +#else + /* rv = */ rounded_product(dval(&rv), tens[e]); +#endif + goto ret; + } + } +#ifndef Inaccurate_Divide + else if (e >= -Ten_pmax) { +#ifdef Honor_FLT_ROUNDS + /* round correctly FLT_ROUNDS = 2 or 3 */ + if (sign) { + rv.d = -rv.d; + sign = 0; + } +#endif + /* rv = */ rounded_quotient(dval(&rv), tens[-e]); + goto ret; + } +#endif +#endif /* ROUND_BIASED_without_Round_Up */ + } + e1 += nd - k; + +#ifdef IEEE_Arith +#ifdef SET_INEXACT + bc.inexact = 1; + if (k <= DBL_DIG) + oldinexact = get_inexact(); +#endif +#ifdef Avoid_Underflow + bc.scale = 0; +#endif +#ifdef Honor_FLT_ROUNDS + if (bc.rounding >= 2) { + if (sign) + bc.rounding = bc.rounding == 2 ? 0 : 2; + else + if (bc.rounding != 2) + bc.rounding = 0; + } +#endif +#endif /*IEEE_Arith*/ + + /* Get starting approximation = rv * 10**e1 */ + + if (e1 > 0) { + if ((i = e1 & 15)) + dval(&rv) *= tens[i]; + if (e1 &= ~15) { + if (e1 > DBL_MAX_10_EXP) { + ovfl: + /* Can't trust HUGE_VAL */ +#ifdef IEEE_Arith +#ifdef Honor_FLT_ROUNDS + switch(bc.rounding) { + case 0: /* toward 0 */ + case 3: /* toward -infinity */ + word0(&rv) = Big0; + word1(&rv) = Big1; + break; + default: + word0(&rv) = Exp_mask; + word1(&rv) = 0; + } +#else /*Honor_FLT_ROUNDS*/ + word0(&rv) = Exp_mask; + word1(&rv) = 0; +#endif /*Honor_FLT_ROUNDS*/ +#ifdef SET_INEXACT + /* set overflow bit */ + dval(&rv0) = 1e300; + dval(&rv0) *= dval(&rv0); +#endif +#else /*IEEE_Arith*/ + word0(&rv) = Big0; + word1(&rv) = Big1; +#endif /*IEEE_Arith*/ + range_err: + if (bd0) { + Bfree(bb); + Bfree(bd); + Bfree(bs); + Bfree(bd0); + Bfree(delta); + } +#ifndef NO_ERRNO + errno = ERANGE; +#endif + goto ret; + } + e1 >>= 4; + for(j = 0; e1 > 1; j++, e1 >>= 1) + if (e1 & 1) + dval(&rv) *= bigtens[j]; + /* The last multiplication could overflow. */ + word0(&rv) -= P*Exp_msk1; + dval(&rv) *= bigtens[j]; + if ((z = word0(&rv) & Exp_mask) + > Exp_msk1*(DBL_MAX_EXP+Bias-P)) + goto ovfl; + if (z > Exp_msk1*(DBL_MAX_EXP+Bias-1-P)) { + /* set to largest number */ + /* (Can't trust DBL_MAX) */ + word0(&rv) = Big0; + word1(&rv) = Big1; + } + else + word0(&rv) += P*Exp_msk1; + } + } + else if (e1 < 0) { + e1 = -e1; + if ((i = e1 & 15)) + dval(&rv) /= tens[i]; + if (e1 >>= 4) { + if (e1 >= 1 << n_bigtens) + goto undfl; +#ifdef Avoid_Underflow + if (e1 & Scale_Bit) + bc.scale = 2*P; + for(j = 0; e1 > 0; j++, e1 >>= 1) + if (e1 & 1) + dval(&rv) *= tinytens[j]; + if (bc.scale && (j = 2*P + 1 - ((word0(&rv) & Exp_mask) + >> Exp_shift)) > 0) { + /* scaled rv is denormal; clear j low bits */ + if (j >= 32) { + if (j > 54) + goto undfl; + word1(&rv) = 0; + if (j >= 53) + word0(&rv) = (P+2)*Exp_msk1; + else + word0(&rv) &= 0xffffffff << (j-32); + } + else + word1(&rv) &= 0xffffffff << j; + } +#else + for(j = 0; e1 > 1; j++, e1 >>= 1) + if (e1 & 1) + dval(&rv) *= tinytens[j]; + /* The last multiplication could underflow. */ + dval(&rv0) = dval(&rv); + dval(&rv) *= tinytens[j]; + if (!dval(&rv)) { + dval(&rv) = 2.*dval(&rv0); + dval(&rv) *= tinytens[j]; +#endif + if (!dval(&rv)) { + undfl: + dval(&rv) = 0.; + goto range_err; + } +#ifndef Avoid_Underflow + word0(&rv) = Tiny0; + word1(&rv) = Tiny1; + /* The refinement below will clean + * this approximation up. + */ + } +#endif + } + } + + /* Now the hard part -- adjusting rv to the correct value.*/ + + /* Put digits into bd: true value = bd * 10^e */ + + bc.nd = nd - nz1; +#ifndef NO_STRTOD_BIGCOMP + bc.nd0 = nd0; /* Only needed if nd > strtod_diglim, but done here */ + /* to silence an erroneous warning about bc.nd0 */ + /* possibly not being initialized. */ + if (nd > strtod_diglim) { + /* ASSERT(strtod_diglim >= 18); 18 == one more than the */ + /* minimum number of decimal digits to distinguish double values */ + /* in IEEE arithmetic. */ + i = j = 18; + if (i > nd0) + j += bc.dplen; + for(;;) { + if (--j < bc.dp1 && j >= bc.dp0) + j = bc.dp0 - 1; + if (s0[j] != '0') + break; + --i; + } + e += nd - i; + nd = i; + if (nd0 > nd) + nd0 = nd; + if (nd < 9) { /* must recompute y */ + y = 0; + for(i = 0; i < nd0; ++i) + y = 10*y + s0[i] - '0'; + for(j = bc.dp1; i < nd; ++i) + y = 10*y + s0[j++] - '0'; + } + } +#endif + bd0 = s2b(s0, nd0, nd, y, bc.dplen); + + for(;;) { + bd = Balloc(bd0->k); + Bcopy(bd, bd0); + bb = d2b(&rv, &bbe, &bbbits); /* rv = bb * 2^bbe */ + bs = i2b(1); + + if (e >= 0) { + bb2 = bb5 = 0; + bd2 = bd5 = e; + } + else { + bb2 = bb5 = -e; + bd2 = bd5 = 0; + } + if (bbe >= 0) + bb2 += bbe; + else + bd2 -= bbe; + bs2 = bb2; +#ifdef Honor_FLT_ROUNDS + if (bc.rounding != 1) + bs2++; +#endif +#ifdef Avoid_Underflow + Lsb = LSB; + Lsb1 = 0; + j = bbe - bc.scale; + i = j + bbbits - 1; /* logb(rv) */ + j = P + 1 - bbbits; + if (i < Emin) { /* denormal */ + i = Emin - i; + j -= i; + if (i < 32) + Lsb <<= i; + else if (i < 52) + Lsb1 = Lsb << (i-32); + else + Lsb1 = Exp_mask; + } +#else /*Avoid_Underflow*/ +#ifdef Sudden_Underflow +#ifdef IBM + j = 1 + 4*P - 3 - bbbits + ((bbe + bbbits - 1) & 3); +#else + j = P + 1 - bbbits; +#endif +#else /*Sudden_Underflow*/ + j = bbe; + i = j + bbbits - 1; /* logb(rv) */ + if (i < Emin) /* denormal */ + j += P - Emin; + else + j = P + 1 - bbbits; +#endif /*Sudden_Underflow*/ +#endif /*Avoid_Underflow*/ + bb2 += j; + bd2 += j; +#ifdef Avoid_Underflow + bd2 += bc.scale; +#endif + i = bb2 < bd2 ? bb2 : bd2; + if (i > bs2) + i = bs2; + if (i > 0) { + bb2 -= i; + bd2 -= i; + bs2 -= i; + } + if (bb5 > 0) { + bs = pow5mult(bs, bb5); + bb1 = mult(bs, bb); + Bfree(bb); + bb = bb1; + } + if (bb2 > 0) + bb = lshift(bb, bb2); + if (bd5 > 0) + bd = pow5mult(bd, bd5); + if (bd2 > 0) + bd = lshift(bd, bd2); + if (bs2 > 0) + bs = lshift(bs, bs2); + delta = diff(bb, bd); + bc.dsign = delta->sign; + delta->sign = 0; + i = cmp(delta, bs); +#ifndef NO_STRTOD_BIGCOMP /*{*/ + if (bc.nd > nd && i <= 0) { + if (bc.dsign) { + /* Must use bigcomp(). */ + req_bigcomp = 1; + break; + } +#ifdef Honor_FLT_ROUNDS + if (bc.rounding != 1) { + if (i < 0) { + req_bigcomp = 1; + break; + } + } + else +#endif + i = -1; /* Discarded digits make delta smaller. */ + } +#endif /*}*/ +#ifdef Honor_FLT_ROUNDS /*{*/ + if (bc.rounding != 1) { + if (i < 0) { + /* Error is less than an ulp */ + if (!delta->x[0] && delta->wds <= 1) { + /* exact */ +#ifdef SET_INEXACT + bc.inexact = 0; +#endif + break; + } + if (bc.rounding) { + if (bc.dsign) { + adj.d = 1.; + goto apply_adj; + } + } + else if (!bc.dsign) { + adj.d = -1.; + if (!word1(&rv) + && !(word0(&rv) & Frac_mask)) { + y = word0(&rv) & Exp_mask; +#ifdef Avoid_Underflow + if (!bc.scale || y > 2*P*Exp_msk1) +#else + if (y) +#endif + { + delta = lshift(delta,Log2P); + if (cmp(delta, bs) <= 0) + adj.d = -0.5; + } + } + apply_adj: +#ifdef Avoid_Underflow /*{*/ + if (bc.scale && (y = word0(&rv) & Exp_mask) + <= 2*P*Exp_msk1) + word0(&adj) += (2*P+1)*Exp_msk1 - y; +#else +#ifdef Sudden_Underflow + if ((word0(&rv) & Exp_mask) <= + P*Exp_msk1) { + word0(&rv) += P*Exp_msk1; + dval(&rv) += adj.d*ulp(dval(&rv)); + word0(&rv) -= P*Exp_msk1; + } + else +#endif /*Sudden_Underflow*/ +#endif /*Avoid_Underflow}*/ + dval(&rv) += adj.d*ulp(&rv); + } + break; + } + adj.d = ratio(delta, bs); + if (adj.d < 1.) + adj.d = 1.; + if (adj.d <= 0x7ffffffe) { + /* adj = rounding ? ceil(adj) : floor(adj); */ + y = adj.d; + if (y != adj.d) { + if (!((bc.rounding>>1) ^ bc.dsign)) + y++; + adj.d = y; + } + } +#ifdef Avoid_Underflow /*{*/ + if (bc.scale && (y = word0(&rv) & Exp_mask) <= 2*P*Exp_msk1) + word0(&adj) += (2*P+1)*Exp_msk1 - y; +#else +#ifdef Sudden_Underflow + if ((word0(&rv) & Exp_mask) <= P*Exp_msk1) { + word0(&rv) += P*Exp_msk1; + adj.d *= ulp(dval(&rv)); + if (bc.dsign) + dval(&rv) += adj.d; + else + dval(&rv) -= adj.d; + word0(&rv) -= P*Exp_msk1; + goto cont; + } +#endif /*Sudden_Underflow*/ +#endif /*Avoid_Underflow}*/ + adj.d *= ulp(&rv); + if (bc.dsign) { + if (word0(&rv) == Big0 && word1(&rv) == Big1) + goto ovfl; + dval(&rv) += adj.d; + } + else + dval(&rv) -= adj.d; + goto cont; + } +#endif /*}Honor_FLT_ROUNDS*/ + + if (i < 0) { + /* Error is less than half an ulp -- check for + * special case of mantissa a power of two. + */ + if (bc.dsign || word1(&rv) || word0(&rv) & Bndry_mask +#ifdef IEEE_Arith /*{*/ +#ifdef Avoid_Underflow + || (word0(&rv) & Exp_mask) <= (2*P+1)*Exp_msk1 +#else + || (word0(&rv) & Exp_mask) <= Exp_msk1 +#endif +#endif /*}*/ + ) { +#ifdef SET_INEXACT + if (!delta->x[0] && delta->wds <= 1) + bc.inexact = 0; +#endif + break; + } + if (!delta->x[0] && delta->wds <= 1) { + /* exact result */ +#ifdef SET_INEXACT + bc.inexact = 0; +#endif + break; + } + delta = lshift(delta,Log2P); + if (cmp(delta, bs) > 0) + goto drop_down; + break; + } + if (i == 0) { + /* exactly half-way between */ + if (bc.dsign) { + if ((word0(&rv) & Bndry_mask1) == Bndry_mask1 + && word1(&rv) == ( +#ifdef Avoid_Underflow + (bc.scale && (y = word0(&rv) & Exp_mask) <= 2*P*Exp_msk1) + ? (0xffffffff & (0xffffffff << (2*P+1-(y>>Exp_shift)))) : +#endif + 0xffffffff)) { + /*boundary case -- increment exponent*/ + if (word0(&rv) == Big0 && word1(&rv) == Big1) + goto ovfl; + word0(&rv) = (word0(&rv) & Exp_mask) + + Exp_msk1 +#ifdef IBM + | Exp_msk1 >> 4 +#endif + ; + word1(&rv) = 0; +#ifdef Avoid_Underflow + bc.dsign = 0; +#endif + break; + } + } + else if (!(word0(&rv) & Bndry_mask) && !word1(&rv)) { + drop_down: + /* boundary case -- decrement exponent */ +#ifdef Sudden_Underflow /*{{*/ + L = word0(&rv) & Exp_mask; +#ifdef IBM + if (L < Exp_msk1) +#else +#ifdef Avoid_Underflow + if (L <= (bc.scale ? (2*P+1)*Exp_msk1 : Exp_msk1)) +#else + if (L <= Exp_msk1) +#endif /*Avoid_Underflow*/ +#endif /*IBM*/ + { + if (bc.nd >nd) { + bc.uflchk = 1; + break; + } + goto undfl; + } + L -= Exp_msk1; +#else /*Sudden_Underflow}{*/ +#ifdef Avoid_Underflow + if (bc.scale) { + L = word0(&rv) & Exp_mask; + if (L <= (2*P+1)*Exp_msk1) { + if (L > (P+2)*Exp_msk1) + /* round even ==> */ + /* accept rv */ + break; + /* rv = smallest denormal */ + if (bc.nd >nd) { + bc.uflchk = 1; + break; + } + goto undfl; + } + } +#endif /*Avoid_Underflow*/ + L = (word0(&rv) & Exp_mask) - Exp_msk1; +#endif /*Sudden_Underflow}}*/ + word0(&rv) = L | Bndry_mask1; + word1(&rv) = 0xffffffff; +#ifdef IBM + goto cont; +#else +#ifndef NO_STRTOD_BIGCOMP + if (bc.nd > nd) + goto cont; +#endif + break; +#endif + } +#ifndef ROUND_BIASED +#ifdef Avoid_Underflow + if (Lsb1) { + if (!(word0(&rv) & Lsb1)) + break; + } + else if (!(word1(&rv) & Lsb)) + break; +#else + if (!(word1(&rv) & LSB)) + break; +#endif +#endif + if (bc.dsign) +#ifdef Avoid_Underflow + dval(&rv) += sulp(&rv, &bc); +#else + dval(&rv) += ulp(&rv); +#endif +#ifndef ROUND_BIASED + else { +#ifdef Avoid_Underflow + dval(&rv) -= sulp(&rv, &bc); +#else + dval(&rv) -= ulp(&rv); +#endif +#ifndef Sudden_Underflow + if (!dval(&rv)) { + if (bc.nd >nd) { + bc.uflchk = 1; + break; + } + goto undfl; + } +#endif + } +#ifdef Avoid_Underflow + bc.dsign = 1 - bc.dsign; +#endif +#endif + break; + } + if ((aadj = ratio(delta, bs)) <= 2.) { + if (bc.dsign) + aadj = aadj1 = 1.; + else if (word1(&rv) || word0(&rv) & Bndry_mask) { +#ifndef Sudden_Underflow + if (word1(&rv) == Tiny1 && !word0(&rv)) { + if (bc.nd >nd) { + bc.uflchk = 1; + break; + } + goto undfl; + } +#endif + aadj = 1.; + aadj1 = -1.; + } + else { + /* special case -- power of FLT_RADIX to be */ + /* rounded down... */ + + if (aadj < 2./FLT_RADIX) + aadj = 1./FLT_RADIX; + else + aadj *= 0.5; + aadj1 = -aadj; + } + } + else { + aadj *= 0.5; + aadj1 = bc.dsign ? aadj : -aadj; +#ifdef Check_FLT_ROUNDS + switch(bc.rounding) { + case 2: /* towards +infinity */ + aadj1 -= 0.5; + break; + case 0: /* towards 0 */ + case 3: /* towards -infinity */ + aadj1 += 0.5; + } +#else + if (Flt_Rounds == 0) + aadj1 += 0.5; +#endif /*Check_FLT_ROUNDS*/ + } + y = word0(&rv) & Exp_mask; + + /* Check for overflow */ + + if (y == Exp_msk1*(DBL_MAX_EXP+Bias-1)) { + dval(&rv0) = dval(&rv); + word0(&rv) -= P*Exp_msk1; + adj.d = aadj1 * ulp(&rv); + dval(&rv) += adj.d; + if ((word0(&rv) & Exp_mask) >= + Exp_msk1*(DBL_MAX_EXP+Bias-P)) { + if (word0(&rv0) == Big0 && word1(&rv0) == Big1) + goto ovfl; + word0(&rv) = Big0; + word1(&rv) = Big1; + goto cont; + } + else + word0(&rv) += P*Exp_msk1; + } + else { +#ifdef Avoid_Underflow + if (bc.scale && y <= 2*P*Exp_msk1) { + if (aadj <= 0x7fffffff) { + if ((z = aadj) <= 0) + z = 1; + aadj = z; + aadj1 = bc.dsign ? aadj : -aadj; + } + dval(&aadj2) = aadj1; + word0(&aadj2) += (2*P+1)*Exp_msk1 - y; + aadj1 = dval(&aadj2); + adj.d = aadj1 * ulp(&rv); + dval(&rv) += adj.d; + if (rv.d == 0.) +#ifdef NO_STRTOD_BIGCOMP + goto undfl; +#else + { + if (bc.nd > nd) + bc.dsign = 1; + break; + } +#endif + } + else { + adj.d = aadj1 * ulp(&rv); + dval(&rv) += adj.d; + } +#else +#ifdef Sudden_Underflow + if ((word0(&rv) & Exp_mask) <= P*Exp_msk1) { + dval(&rv0) = dval(&rv); + word0(&rv) += P*Exp_msk1; + adj.d = aadj1 * ulp(&rv); + dval(&rv) += adj.d; +#ifdef IBM + if ((word0(&rv) & Exp_mask) < P*Exp_msk1) +#else + if ((word0(&rv) & Exp_mask) <= P*Exp_msk1) +#endif + { + if (word0(&rv0) == Tiny0 + && word1(&rv0) == Tiny1) { + if (bc.nd >nd) { + bc.uflchk = 1; + break; + } + goto undfl; + } + word0(&rv) = Tiny0; + word1(&rv) = Tiny1; + goto cont; + } + else + word0(&rv) -= P*Exp_msk1; + } + else { + adj.d = aadj1 * ulp(&rv); + dval(&rv) += adj.d; + } +#else /*Sudden_Underflow*/ + /* Compute adj so that the IEEE rounding rules will + * correctly round rv + adj in some half-way cases. + * If rv * ulp(rv) is denormalized (i.e., + * y <= (P-1)*Exp_msk1), we must adjust aadj to avoid + * trouble from bits lost to denormalization; + * example: 1.2e-307 . + */ + if (y <= (P-1)*Exp_msk1 && aadj > 1.) { + aadj1 = (double)(int)(aadj + 0.5); + if (!bc.dsign) + aadj1 = -aadj1; + } + adj.d = aadj1 * ulp(&rv); + dval(&rv) += adj.d; +#endif /*Sudden_Underflow*/ +#endif /*Avoid_Underflow*/ + } + z = word0(&rv) & Exp_mask; +#ifndef SET_INEXACT + if (bc.nd == nd) { +#ifdef Avoid_Underflow + if (!bc.scale) +#endif + if (y == z) { + /* Can we stop now? */ + L = (Long)aadj; + aadj -= L; + /* The tolerances below are conservative. */ + if (bc.dsign || word1(&rv) || word0(&rv) & Bndry_mask) { + if (aadj < .4999999 || aadj > .5000001) + break; + } + else if (aadj < .4999999/FLT_RADIX) + break; + } + } +#endif + cont: + Bfree(bb); + Bfree(bd); + Bfree(bs); + Bfree(delta); + } + Bfree(bb); + Bfree(bd); + Bfree(bs); + Bfree(bd0); + Bfree(delta); +#ifndef NO_STRTOD_BIGCOMP + if (req_bigcomp) { + bd0 = 0; + bc.e0 += nz1; + bigcomp(&rv, s0, &bc); + y = word0(&rv) & Exp_mask; + if (y == Exp_mask) + goto ovfl; + if (y == 0 && rv.d == 0.) + goto undfl; + } +#endif +#ifdef SET_INEXACT + if (bc.inexact) { + if (!oldinexact) { + word0(&rv0) = Exp_1 + (70 << Exp_shift); + word1(&rv0) = 0; + dval(&rv0) += 1.; + } + } + else if (!oldinexact) + clear_inexact(); +#endif +#ifdef Avoid_Underflow + if (bc.scale) { + word0(&rv0) = Exp_1 - 2*P*Exp_msk1; + word1(&rv0) = 0; + dval(&rv) *= dval(&rv0); +#ifndef NO_ERRNO + /* try to avoid the bug of testing an 8087 register value */ +#ifdef IEEE_Arith + if (!(word0(&rv) & Exp_mask)) +#else + if (word0(&rv) == 0 && word1(&rv) == 0) +#endif + errno = ERANGE; +#endif + } +#endif /* Avoid_Underflow */ +#ifdef SET_INEXACT + if (bc.inexact && !(word0(&rv) & Exp_mask)) { + /* set underflow bit */ + dval(&rv0) = 1e-300; + dval(&rv0) *= dval(&rv0); + } +#endif + ret: + if (se) + *se = (char *)s; + return sign ? -dval(&rv) : dval(&rv); + } + +#ifndef MULTIPLE_THREADS + static char *dtoa_result; +#endif + + static char * +#ifdef KR_headers +rv_alloc(i) int i; +#else +rv_alloc(int i) +#endif +{ + int j, k, *r; + + j = sizeof(ULong); + for(k = 0; + sizeof(Bigint) - sizeof(ULong) - sizeof(int) + j <= i; + j <<= 1) + k++; + r = (int*)Balloc(k); + *r = k; + return +#ifndef MULTIPLE_THREADS + dtoa_result = +#endif + (char *)(r+1); + } + + static char * +#ifdef KR_headers +nrv_alloc(s, rve, n) char *s, **rve; int n; +#else +nrv_alloc(const char *s, char **rve, int n) +#endif +{ + char *rv, *t; + + t = rv = rv_alloc(n); + while((*t = *s++)) t++; + if (rve) + *rve = t; + return rv; + } + +/* freedtoa(s) must be used to free values s returned by dtoa + * when MULTIPLE_THREADS is #defined. It should be used in all cases, + * but for consistency with earlier versions of dtoa, it is optional + * when MULTIPLE_THREADS is not defined. + */ + + void +#ifdef KR_headers +freedtoa(s) char *s; +#else +freedtoa(char *s) +#endif +{ + Bigint *b = (Bigint *)((int *)s - 1); + b->maxwds = 1 << (b->k = *(int*)b); + Bfree(b); +#ifndef MULTIPLE_THREADS + if (s == dtoa_result) + dtoa_result = 0; +#endif + } + +/* dtoa for IEEE arithmetic (dmg): convert double to ASCII string. + * + * Inspired by "How to Print Floating-Point Numbers Accurately" by + * Guy L. Steele, Jr. and Jon L. White [Proc. ACM SIGPLAN '90, pp. 112-126]. + * + * Modifications: + * 1. Rather than iterating, we use a simple numeric overestimate + * to determine k = floor(log10(d)). We scale relevant + * quantities using O(log2(k)) rather than O(k) multiplications. + * 2. For some modes > 2 (corresponding to ecvt and fcvt), we don't + * try to generate digits strictly left to right. Instead, we + * compute with fewer bits and propagate the carry if necessary + * when rounding the final digit up. This is often faster. + * 3. Under the assumption that input will be rounded nearest, + * mode 0 renders 1e23 as 1e23 rather than 9.999999999999999e22. + * That is, we allow equality in stopping tests when the + * round-nearest rule will give the same floating-point value + * as would satisfaction of the stopping test with strict + * inequality. + * 4. We remove common factors of powers of 2 from relevant + * quantities. + * 5. When converting floating-point integers less than 1e16, + * we use floating-point arithmetic rather than resorting + * to multiple-precision integers. + * 6. When asked to produce fewer than 15 digits, we first try + * to get by with floating-point arithmetic; we resort to + * multiple-precision integer arithmetic only if we cannot + * guarantee that the floating-point calculation has given + * the correctly rounded result. For k requested digits and + * "uniformly" distributed input, the probability is + * something like 10^(k-15) that we must resort to the Long + * calculation. + */ + + char * +dtoa +#ifdef KR_headers + (dd, mode, ndigits, decpt, sign, rve) + double dd; int mode, ndigits, *decpt, *sign; char **rve; +#else + (double dd, int mode, int ndigits, int *decpt, int *sign, char **rve) +#endif +{ + /* Arguments ndigits, decpt, sign are similar to those + of ecvt and fcvt; trailing zeros are suppressed from + the returned string. If not null, *rve is set to point + to the end of the return value. If d is +-Infinity or NaN, + then *decpt is set to 9999. + + mode: + 0 ==> shortest string that yields d when read in + and rounded to nearest. + 1 ==> like 0, but with Steele & White stopping rule; + e.g. with IEEE P754 arithmetic , mode 0 gives + 1e23 whereas mode 1 gives 9.999999999999999e22. + 2 ==> max(1,ndigits) significant digits. This gives a + return value similar to that of ecvt, except + that trailing zeros are suppressed. + 3 ==> through ndigits past the decimal point. This + gives a return value similar to that from fcvt, + except that trailing zeros are suppressed, and + ndigits can be negative. + 4,5 ==> similar to 2 and 3, respectively, but (in + round-nearest mode) with the tests of mode 0 to + possibly return a shorter string that rounds to d. + With IEEE arithmetic and compilation with + -DHonor_FLT_ROUNDS, modes 4 and 5 behave the same + as modes 2 and 3 when FLT_ROUNDS != 1. + 6-9 ==> Debugging modes similar to mode - 4: don't try + fast floating-point estimate (if applicable). + + Values of mode other than 0-9 are treated as mode 0. + + Sufficient space is allocated to the return value + to hold the suppressed trailing zeros. + */ + + int bbits, b2, b5, be, dig, i, ieps, ilim, ilim0, ilim1, + j, j1, k, k0, k_check, leftright, m2, m5, s2, s5, + spec_case, try_quick; + Long L; +#ifndef Sudden_Underflow + int denorm; + ULong x; +#endif + Bigint *b, *b1, *delta, *mlo, *mhi, *S; + U d2, eps, u; + double ds; + char *s, *s0; +#ifndef No_leftright +#ifdef IEEE_Arith + U eps1; +#endif +#endif +#ifdef SET_INEXACT + int inexact, oldinexact; +#endif +#ifdef Honor_FLT_ROUNDS /*{*/ + int Rounding; +#ifdef Trust_FLT_ROUNDS /*{{ only define this if FLT_ROUNDS really works! */ + Rounding = Flt_Rounds; +#else /*}{*/ + Rounding = 1; + switch(fegetround()) { + case FE_TOWARDZERO: Rounding = 0; break; + case FE_UPWARD: Rounding = 2; break; + case FE_DOWNWARD: Rounding = 3; + } +#endif /*}}*/ +#endif /*}*/ + +#ifndef MULTIPLE_THREADS + if (dtoa_result) { + freedtoa(dtoa_result); + dtoa_result = 0; + } +#endif + + u.d = dd; + if (word0(&u) & Sign_bit) { + /* set sign for everything, including 0's and NaNs */ + *sign = 1; + word0(&u) &= ~Sign_bit; /* clear sign bit */ + } + else + *sign = 0; + +#if defined(IEEE_Arith) + defined(VAX) +#ifdef IEEE_Arith + if ((word0(&u) & Exp_mask) == Exp_mask) +#else + if (word0(&u) == 0x8000) +#endif + { + /* Infinity or NaN */ + *decpt = 9999; +#ifdef IEEE_Arith + if (!word1(&u) && !(word0(&u) & 0xfffff)) + return nrv_alloc("Infinity", rve, 8); +#endif + return nrv_alloc("NaN", rve, 3); + } +#endif +#ifdef IBM + dval(&u) += 0; /* normalize */ +#endif + if (!dval(&u)) { + *decpt = 1; + return nrv_alloc("0", rve, 1); + } + +#ifdef SET_INEXACT + try_quick = oldinexact = get_inexact(); + inexact = 1; +#endif +#ifdef Honor_FLT_ROUNDS + if (Rounding >= 2) { + if (*sign) + Rounding = Rounding == 2 ? 0 : 2; + else + if (Rounding != 2) + Rounding = 0; + } +#endif + + b = d2b(&u, &be, &bbits); +#ifdef Sudden_Underflow + i = (int)(word0(&u) >> Exp_shift1 & (Exp_mask>>Exp_shift1)); +#else + if ((i = (int)(word0(&u) >> Exp_shift1 & (Exp_mask>>Exp_shift1)))) { +#endif + dval(&d2) = dval(&u); + word0(&d2) &= Frac_mask1; + word0(&d2) |= Exp_11; +#ifdef IBM + if (j = 11 - hi0bits(word0(&d2) & Frac_mask)) + dval(&d2) /= 1 << j; +#endif + + /* log(x) ~=~ log(1.5) + (x-1.5)/1.5 + * log10(x) = log(x) / log(10) + * ~=~ log(1.5)/log(10) + (x-1.5)/(1.5*log(10)) + * log10(d) = (i-Bias)*log(2)/log(10) + log10(d2) + * + * This suggests computing an approximation k to log10(d) by + * + * k = (i - Bias)*0.301029995663981 + * + ( (d2-1.5)*0.289529654602168 + 0.176091259055681 ); + * + * We want k to be too large rather than too small. + * The error in the first-order Taylor series approximation + * is in our favor, so we just round up the constant enough + * to compensate for any error in the multiplication of + * (i - Bias) by 0.301029995663981; since |i - Bias| <= 1077, + * and 1077 * 0.30103 * 2^-52 ~=~ 7.2e-14, + * adding 1e-13 to the constant term more than suffices. + * Hence we adjust the constant term to 0.1760912590558. + * (We could get a more accurate k by invoking log10, + * but this is probably not worthwhile.) + */ + + i -= Bias; +#ifdef IBM + i <<= 2; + i += j; +#endif +#ifndef Sudden_Underflow + denorm = 0; + } + else { + /* d is denormalized */ + + i = bbits + be + (Bias + (P-1) - 1); + x = i > 32 ? word0(&u) << (64 - i) | word1(&u) >> (i - 32) + : word1(&u) << (32 - i); + dval(&d2) = x; + word0(&d2) -= 31*Exp_msk1; /* adjust exponent */ + i -= (Bias + (P-1) - 1) + 1; + denorm = 1; + } +#endif + ds = (dval(&d2)-1.5)*0.289529654602168 + 0.1760912590558 + i*0.301029995663981; + k = (int)ds; + if (ds < 0. && ds != k) + k--; /* want k = floor(ds) */ + k_check = 1; + if (k >= 0 && k <= Ten_pmax) { + if (dval(&u) < tens[k]) + k--; + k_check = 0; + } + j = bbits - i - 1; + if (j >= 0) { + b2 = 0; + s2 = j; + } + else { + b2 = -j; + s2 = 0; + } + if (k >= 0) { + b5 = 0; + s5 = k; + s2 += k; + } + else { + b2 -= k; + b5 = -k; + s5 = 0; + } + if (mode < 0 || mode > 9) + mode = 0; + +#ifndef SET_INEXACT +#ifdef Check_FLT_ROUNDS + try_quick = Rounding == 1; +#else + try_quick = 1; +#endif +#endif /*SET_INEXACT*/ + + if (mode > 5) { + mode -= 4; + try_quick = 0; + } + leftright = 1; + ilim = ilim1 = -1; /* Values for cases 0 and 1; done here to */ + /* silence erroneous "gcc -Wall" warning. */ + switch(mode) { + case 0: + case 1: + i = 18; + ndigits = 0; + break; + case 2: + leftright = 0; + /* no break */ + case 4: + if (ndigits <= 0) + ndigits = 1; + ilim = ilim1 = i = ndigits; + break; + case 3: + leftright = 0; + /* no break */ + case 5: + i = ndigits + k + 1; + ilim = i; + ilim1 = i - 1; + if (i <= 0) + i = 1; + } + s = s0 = rv_alloc(i); + +#ifdef Honor_FLT_ROUNDS + if (mode > 1 && Rounding != 1) + leftright = 0; +#endif + + if (ilim >= 0 && ilim <= Quick_max && try_quick) { + + /* Try to get by with floating-point arithmetic. */ + + i = 0; + dval(&d2) = dval(&u); + k0 = k; + ilim0 = ilim; + ieps = 2; /* conservative */ + if (k > 0) { + ds = tens[k&0xf]; + j = k >> 4; + if (j & Bletch) { + /* prevent overflows */ + j &= Bletch - 1; + dval(&u) /= bigtens[n_bigtens-1]; + ieps++; + } + for(; j; j >>= 1, i++) + if (j & 1) { + ieps++; + ds *= bigtens[i]; + } + dval(&u) /= ds; + } + else if ((j1 = -k)) { + dval(&u) *= tens[j1 & 0xf]; + for(j = j1 >> 4; j; j >>= 1, i++) + if (j & 1) { + ieps++; + dval(&u) *= bigtens[i]; + } + } + if (k_check && dval(&u) < 1. && ilim > 0) { + if (ilim1 <= 0) + goto fast_failed; + ilim = ilim1; + k--; + dval(&u) *= 10.; + ieps++; + } + dval(&eps) = ieps*dval(&u) + 7.; + word0(&eps) -= (P-1)*Exp_msk1; + if (ilim == 0) { + S = mhi = 0; + dval(&u) -= 5.; + if (dval(&u) > dval(&eps)) + goto one_digit; + if (dval(&u) < -dval(&eps)) + goto no_digits; + goto fast_failed; + } +#ifndef No_leftright + if (leftright) { + /* Use Steele & White method of only + * generating digits needed. + */ + dval(&eps) = 0.5/tens[ilim-1] - dval(&eps); +#ifdef IEEE_Arith + if (k0 < 0 && j1 >= 307) { + eps1.d = 1.01e256; /* 1.01 allows roundoff in the next few lines */ + word0(&eps1) -= Exp_msk1 * (Bias+P-1); + dval(&eps1) *= tens[j1 & 0xf]; + for(i = 0, j = (j1-256) >> 4; j; j >>= 1, i++) + if (j & 1) + dval(&eps1) *= bigtens[i]; + if (eps.d < eps1.d) + eps.d = eps1.d; + } +#endif + for(i = 0;;) { + L = dval(&u); + dval(&u) -= L; + *s++ = '0' + (int)L; + if (1. - dval(&u) < dval(&eps)) + goto bump_up; + if (dval(&u) < dval(&eps)) + goto ret1; + if (++i >= ilim) + break; + dval(&eps) *= 10.; + dval(&u) *= 10.; + } + } + else { +#endif + /* Generate ilim digits, then fix them up. */ + dval(&eps) *= tens[ilim-1]; + for(i = 1;; i++, dval(&u) *= 10.) { + L = (Long)(dval(&u)); + if (!(dval(&u) -= L)) + ilim = i; + *s++ = '0' + (int)L; + if (i == ilim) { + if (dval(&u) > 0.5 + dval(&eps)) + goto bump_up; + else if (dval(&u) < 0.5 - dval(&eps)) { + while(*--s == '0'); + s++; + goto ret1; + } + break; + } + } +#ifndef No_leftright + } +#endif + fast_failed: + s = s0; + dval(&u) = dval(&d2); + k = k0; + ilim = ilim0; + } + + /* Do we have a "small" integer? */ + + if (be >= 0 && k <= Int_max) { + /* Yes. */ + ds = tens[k]; + if (ndigits < 0 && ilim <= 0) { + S = mhi = 0; + if (ilim < 0 || dval(&u) <= 5*ds) + goto no_digits; + goto one_digit; + } + for(i = 1;; i++, dval(&u) *= 10.) { + L = (Long)(dval(&u) / ds); + dval(&u) -= L*ds; +#ifdef Check_FLT_ROUNDS + /* If FLT_ROUNDS == 2, L will usually be high by 1 */ + if (dval(&u) < 0) { + L--; + dval(&u) += ds; + } +#endif + *s++ = '0' + (int)L; + if (!dval(&u)) { +#ifdef SET_INEXACT + inexact = 0; +#endif + break; + } + if (i == ilim) { +#ifdef Honor_FLT_ROUNDS + if (mode > 1) + switch(Rounding) { + case 0: goto ret1; + case 2: goto bump_up; + } +#endif + dval(&u) += dval(&u); +#ifdef ROUND_BIASED + if (dval(&u) >= ds) +#else + if (dval(&u) > ds || (dval(&u) == ds && L & 1)) +#endif + { + bump_up: + while(*--s == '9') + if (s == s0) { + k++; + *s = '0'; + break; + } + ++*s++; + } + break; + } + } + goto ret1; + } + + m2 = b2; + m5 = b5; + mhi = mlo = 0; + if (leftright) { + i = +#ifndef Sudden_Underflow + denorm ? be + (Bias + (P-1) - 1 + 1) : +#endif +#ifdef IBM + 1 + 4*P - 3 - bbits + ((bbits + be - 1) & 3); +#else + 1 + P - bbits; +#endif + b2 += i; + s2 += i; + mhi = i2b(1); + } + if (m2 > 0 && s2 > 0) { + i = m2 < s2 ? m2 : s2; + b2 -= i; + m2 -= i; + s2 -= i; + } + if (b5 > 0) { + if (leftright) { + if (m5 > 0) { + mhi = pow5mult(mhi, m5); + b1 = mult(mhi, b); + Bfree(b); + b = b1; + } + if ((j = b5 - m5)) + b = pow5mult(b, j); + } + else + b = pow5mult(b, b5); + } + S = i2b(1); + if (s5 > 0) + S = pow5mult(S, s5); + + /* Check for special case that d is a normalized power of 2. */ + + spec_case = 0; + if ((mode < 2 || leftright) +#ifdef Honor_FLT_ROUNDS + && Rounding == 1 +#endif + ) { + if (!word1(&u) && !(word0(&u) & Bndry_mask) +#ifndef Sudden_Underflow + && word0(&u) & (Exp_mask & ~Exp_msk1) +#endif + ) { + /* The special case */ + b2 += Log2P; + s2 += Log2P; + spec_case = 1; + } + } + + /* Arrange for convenient computation of quotients: + * shift left if necessary so divisor has 4 leading 0 bits. + * + * Perhaps we should just compute leading 28 bits of S once + * and for all and pass them and a shift to quorem, so it + * can do shifts and ors to compute the numerator for q. + */ + i = dshift(S, s2); + b2 += i; + m2 += i; + s2 += i; + if (b2 > 0) + b = lshift(b, b2); + if (s2 > 0) + S = lshift(S, s2); + if (k_check) { + if (cmp(b,S) < 0) { + k--; + b = multadd(b, 10, 0); /* we botched the k estimate */ + if (leftright) + mhi = multadd(mhi, 10, 0); + ilim = ilim1; + } + } + if (ilim <= 0 && (mode == 3 || mode == 5)) { + if (ilim < 0 || cmp(b,S = multadd(S,5,0)) <= 0) { + /* no digits, fcvt style */ + no_digits: + k = -1 - ndigits; + goto ret; + } + one_digit: + *s++ = '1'; + k++; + goto ret; + } + if (leftright) { + if (m2 > 0) + mhi = lshift(mhi, m2); + + /* Compute mlo -- check for special case + * that d is a normalized power of 2. + */ + + mlo = mhi; + if (spec_case) { + mhi = Balloc(mhi->k); + Bcopy(mhi, mlo); + mhi = lshift(mhi, Log2P); + } + + for(i = 1;;i++) { + dig = quorem(b,S) + '0'; + /* Do we yet have the shortest decimal string + * that will round to d? + */ + j = cmp(b, mlo); + delta = diff(S, mhi); + j1 = delta->sign ? 1 : cmp(b, delta); + Bfree(delta); +#ifndef ROUND_BIASED + if (j1 == 0 && mode != 1 && !(word1(&u) & 1) +#ifdef Honor_FLT_ROUNDS + && Rounding >= 1 +#endif + ) { + if (dig == '9') + goto round_9_up; + if (j > 0) + dig++; +#ifdef SET_INEXACT + else if (!b->x[0] && b->wds <= 1) + inexact = 0; +#endif + *s++ = dig; + goto ret; + } +#endif + if (j < 0 || (j == 0 && mode != 1 +#ifndef ROUND_BIASED + && !(word1(&u) & 1) +#endif + )) { + if (!b->x[0] && b->wds <= 1) { +#ifdef SET_INEXACT + inexact = 0; +#endif + goto accept_dig; + } +#ifdef Honor_FLT_ROUNDS + if (mode > 1) + switch(Rounding) { + case 0: goto accept_dig; + case 2: goto keep_dig; + } +#endif /*Honor_FLT_ROUNDS*/ + if (j1 > 0) { + b = lshift(b, 1); + j1 = cmp(b, S); +#ifdef ROUND_BIASED + if (j1 >= 0 /*)*/ +#else + if ((j1 > 0 || (j1 == 0 && dig & 1)) +#endif + && dig++ == '9') + goto round_9_up; + } + accept_dig: + *s++ = dig; + goto ret; + } + if (j1 > 0) { +#ifdef Honor_FLT_ROUNDS + if (!Rounding) + goto accept_dig; +#endif + if (dig == '9') { /* possible if i == 1 */ + round_9_up: + *s++ = '9'; + goto roundoff; + } + *s++ = dig + 1; + goto ret; + } +#ifdef Honor_FLT_ROUNDS + keep_dig: +#endif + *s++ = dig; + if (i == ilim) + break; + b = multadd(b, 10, 0); + if (mlo == mhi) + mlo = mhi = multadd(mhi, 10, 0); + else { + mlo = multadd(mlo, 10, 0); + mhi = multadd(mhi, 10, 0); + } + } + } + else + for(i = 1;; i++) { + *s++ = dig = quorem(b,S) + '0'; + if (!b->x[0] && b->wds <= 1) { +#ifdef SET_INEXACT + inexact = 0; +#endif + goto ret; + } + if (i >= ilim) + break; + b = multadd(b, 10, 0); + } + + /* Round off last digit */ + +#ifdef Honor_FLT_ROUNDS + switch(Rounding) { + case 0: goto trimzeros; + case 2: goto roundoff; + } +#endif + b = lshift(b, 1); + j = cmp(b, S); +#ifdef ROUND_BIASED + if (j >= 0) +#else + if (j > 0 || (j == 0 && dig & 1)) +#endif + { + roundoff: + while(*--s == '9') + if (s == s0) { + k++; + *s++ = '1'; + goto ret; + } + ++*s++; + } + else { +#ifdef Honor_FLT_ROUNDS + trimzeros: +#endif + while(*--s == '0'); + s++; + } + ret: + Bfree(S); + if (mhi) { + if (mlo && mlo != mhi) + Bfree(mlo); + Bfree(mhi); + } + ret1: +#ifdef SET_INEXACT + if (inexact) { + if (!oldinexact) { + word0(&u) = Exp_1 + (70 << Exp_shift); + word1(&u) = 0; + dval(&u) += 1.; + } + } + else if (!oldinexact) + clear_inexact(); +#endif + Bfree(b); + *s = 0; + *decpt = k + 1; + if (rve) + *rve = s; + return s0; + } +#ifdef __cplusplus +} +#endif diff --git a/app/cjson/dtoa_config.h.bak b/app/cjson/dtoa_config.h.bak new file mode 100644 index 00000000..380e83b0 --- /dev/null +++ b/app/cjson/dtoa_config.h.bak @@ -0,0 +1,74 @@ +#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 { \ + int r = pthread_mutex_lock(&private_dtoa_lock[n]); \ + if (r) { \ + fprintf(stderr, "pthread_mutex_lock failed with %d\n", r); \ + abort(); \ + } \ +} while (0) + +#define FREE_DTOA_LOCK(n) do { \ + int r = pthread_mutex_unlock(&private_dtoa_lock[n]); \ + if (r) { \ + fprintf(stderr, "pthread_mutex_unlock failed with %d\n", r);\ + abort(); \ + } \ +} while (0) + +#endif /* MULTIPLE_THREADS */ + +#endif /* _DTOA_CONFIG_H */ + +/* vi:ai et sw=4 ts=4: + */ diff --git a/app/cjson/fpconv.c b/app/cjson/fpconv.c new file mode 100644 index 00000000..b67e2842 --- /dev/null +++ b/app/cjson/fpconv.c @@ -0,0 +1,208 @@ +/* fpconv - Floating point conversion routines + * + * Copyright (c) 2011-2012 Mark Pulford + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* JSON uses a '.' decimal separator. strtod() / sprintf() under C libraries + * with locale support will break when the decimal separator is a comma. + * + * fpconv_* will around these issues with a translation buffer if required. + */ + +#include "c_stdio.h" +#include "c_stdlib.h" +#include +#include "c_string.h" + +#include "fpconv.h" + +/* Lua CJSON assumes the locale is the same for all threads within a + * process and doesn't change after initialisation. + * + * This avoids the need for per thread storage or expensive checks + * for call. */ +static char locale_decimal_point = '.'; + +/* In theory multibyte decimal_points are possible, but + * Lua CJSON only supports UTF-8 and known locales only have + * single byte decimal points ([.,]). + * + * localconv() may not be thread safe (=>crash), and nl_langinfo() is + * not supported on some platforms. Use sprintf() instead - if the + * locale does change, at least Lua CJSON won't crash. */ +static void fpconv_update_locale() +{ + char buf[8]; + + c_sprintf(buf, "%g", 0.5); + + /* Failing this test might imply the platform has a buggy dtoa + * implementation or wide characters */ + if (buf[0] != '0' || buf[2] != '5' || buf[3] != 0) { + NODE_ERR("Error: wide characters found or printf() bug."); + return; + } + + locale_decimal_point = buf[1]; +} + +/* Check for a valid number character: [-+0-9a-yA-Y.] + * Eg: -0.6e+5, infinity, 0xF0.F0pF0 + * + * Used to find the probable end of a number. It doesn't matter if + * invalid characters are counted - strtod() will find the valid + * number if it exists. The risk is that slightly more memory might + * be allocated before a parse error occurs. */ +static inline int valid_number_character(char ch) +{ + char lower_ch; + + if ('0' <= ch && ch <= '9') + return 1; + if (ch == '-' || ch == '+' || ch == '.') + return 1; + + /* Hex digits, exponent (e), base (p), "infinity",.. */ + lower_ch = ch | 0x20; + if ('a' <= lower_ch && lower_ch <= 'y') + return 1; + + return 0; +} + +/* Calculate the size of the buffer required for a strtod locale + * conversion. */ +static int strtod_buffer_size(const char *s) +{ + const char *p = s; + + while (valid_number_character(*p)) + p++; + + return p - s; +} + +/* Similar to strtod(), but must be passed the current locale's decimal point + * character. Guaranteed to be called at the start of any valid number in a string */ +double fpconv_strtod(const char *nptr, char **endptr) +{ + char localbuf[FPCONV_G_FMT_BUFSIZE]; + char *buf, *endbuf, *dp; + int buflen; + double value; + + /* System strtod() is fine when decimal point is '.' */ + if (locale_decimal_point == '.') + return c_strtod(nptr, endptr); + + buflen = strtod_buffer_size(nptr); + if (!buflen) { + /* No valid characters found, standard strtod() return */ + *endptr = (char *)nptr; + return 0; + } + + /* Duplicate number into buffer */ + if (buflen >= FPCONV_G_FMT_BUFSIZE) { + /* Handle unusually large numbers */ + buf = c_malloc(buflen + 1); + if (!buf) { + NODE_ERR("not enough memory\n"); + return; + } + } else { + /* This is the common case.. */ + buf = localbuf; + } + c_memcpy(buf, nptr, buflen); + buf[buflen] = 0; + + /* Update decimal point character if found */ + dp = c_strchr(buf, '.'); + if (dp) + *dp = locale_decimal_point; + + value = c_strtod(buf, &endbuf); + *endptr = (char *)&nptr[endbuf - buf]; + if (buflen >= FPCONV_G_FMT_BUFSIZE) + c_free(buf); + + return value; +} + +/* "fmt" must point to a buffer of at least 6 characters */ +static void set_number_format(char *fmt, int precision) +{ + int d1, d2, i; + + if(!(1 <= precision && precision <= 14)) return; + + /* Create printf format (%.14g) from precision */ + d1 = precision / 10; + d2 = precision % 10; + fmt[0] = '%'; + fmt[1] = '.'; + i = 2; + if (d1) { + fmt[i++] = '0' + d1; + } + fmt[i++] = '0' + d2; + fmt[i++] = 'g'; + fmt[i] = 0; +} + +/* Assumes there is always at least 32 characters available in the target buffer */ +int fpconv_g_fmt(char *str, double num, int precision) +{ + char buf[FPCONV_G_FMT_BUFSIZE]; + char fmt[6]; + int len; + char *b; + + set_number_format(fmt, precision); + + /* Pass through when decimal point character is dot. */ + if (locale_decimal_point == '.'){ + c_sprintf(str, fmt, num); + return c_strlen(str); + } + + /* snprintf() to a buffer then translate for other decimal point characters */ + c_sprintf(buf, fmt, num); + len = c_strlen(buf); + + /* Copy into target location. Translate decimal point if required */ + b = buf; + do { + *str++ = (*b == locale_decimal_point ? '.' : *b); + } while(*b++); + + return len; +} + +void fpconv_init() +{ + fpconv_update_locale(); +} + +/* vi:ai et sw=4 ts=4: + */ diff --git a/app/cjson/fpconv.h b/app/cjson/fpconv.h new file mode 100644 index 00000000..01249088 --- /dev/null +++ b/app/cjson/fpconv.h @@ -0,0 +1,22 @@ +/* Lua CJSON floating point conversion routines */ + +/* Buffer required to store the largest string representation of a double. + * + * Longest double printed with %.14g is 21 characters long: + * -1.7976931348623e+308 */ +# define FPCONV_G_FMT_BUFSIZE 32 + +#ifdef USE_INTERNAL_FPCONV +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**); + +/* vi:ai et sw=4 ts=4: + */ diff --git a/app/cjson/g_fmt.c b/app/cjson/g_fmt.c new file mode 100644 index 00000000..50d6a1d3 --- /dev/null +++ b/app/cjson/g_fmt.c @@ -0,0 +1,111 @@ +/**************************************************************** + * + * The author of this software is David M. Gay. + * + * Copyright (c) 1991, 1996 by Lucent Technologies. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHOR NOR LUCENT MAKES ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + * + ***************************************************************/ + +/* g_fmt(buf,x) stores the closest decimal approximation to x in buf; + * it suffices to declare buf + * char buf[32]; + */ + +#ifdef __cplusplus +extern "C" { +#endif + extern char *dtoa(double, int, int, int *, int *, char **); + extern int g_fmt(char *, double, int); + extern void freedtoa(char*); +#ifdef __cplusplus + } +#endif + +int +fpconv_g_fmt(char *b, double x, int precision) +{ + register int i, k; + register char *s; + int decpt, j, sign; + char *b0, *s0, *se; + + b0 = b; +#ifdef IGNORE_ZERO_SIGN + if (!x) { + *b++ = '0'; + *b = 0; + goto done; + } +#endif + s = s0 = dtoa(x, 2, precision, &decpt, &sign, &se); + if (sign) + *b++ = '-'; + if (decpt == 9999) /* Infinity or Nan */ { + 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 > precision) { + *b++ = *s++; + if (*s) { + *b++ = '.'; + while((*b = *s++)) + b++; + } + *b++ = 'e'; + /* sprintf(b, "%+.2d", decpt - 1); */ + if (--decpt < 0) { + *b++ = '-'; + decpt = -decpt; + } + else + *b++ = '+'; + for(j = 2, k = 10; 10*k <= decpt; j++, k *= 10); + for(;;) { + i = decpt / k; + *b++ = i + '0'; + if (--j <= 0) + break; + decpt -= i*k; + decpt *= 10; + } + *b = 0; + } + else if (decpt <= 0) { + *b++ = '0'; + *b++ = '.'; + for(; decpt < 0; decpt++) + *b++ = '0'; + while((*b++ = *s++)); + b--; + } + else { + while((*b = *s++)) { + b++; + if (--decpt == 0 && *s) + *b++ = '.'; + } + for(; decpt > 0; decpt--) + *b++ = '0'; + *b = 0; + } + done0: + freedtoa(s0); +#ifdef IGNORE_ZERO_SIGN + done: +#endif + return b - b0; + } diff --git a/app/cjson/lua-cjson-2.1devel-1.rockspec b/app/cjson/lua-cjson-2.1devel-1.rockspec new file mode 100644 index 00000000..154e333d --- /dev/null +++ b/app/cjson/lua-cjson-2.1devel-1.rockspec @@ -0,0 +1,56 @@ +package = "lua-cjson" +version = "2.1devel-1" + +source = { + url = "http://www.kyne.com.au/~mark/software/download/lua-cjson-2.1devel.zip", +} + +description = { + summary = "A fast JSON encoding/parsing module", + detailed = [[ + The Lua CJSON module provides JSON support for Lua. It features: + - Fast, standards compliant encoding/parsing routines + - Full support for JSON with UTF-8, including decoding surrogate pairs + - Optional run-time support for common exceptions to the JSON specification + (infinity, NaN,..) + - No dependencies on other libraries + ]], + homepage = "http://www.kyne.com.au/~mark/software/lua-cjson.php", + license = "MIT" +} + +dependencies = { + "lua >= 5.1" +} + +build = { + type = "builtin", + modules = { + cjson = { + sources = { "lua_cjson.c", "strbuf.c", "fpconv.c" }, + defines = { +-- LuaRocks does not support platform specific configuration for Solaris. +-- Uncomment the line below on Solaris platforms if required. +-- "USE_INTERNAL_ISINF" + } + } + }, + install = { + lua = { + ["cjson.util"] = "lua/cjson/util.lua" + }, + bin = { + json2lua = "lua/json2lua.lua", + lua2json = "lua/lua2json.lua" + } + }, + -- Override default build options (per platform) + platforms = { + win32 = { modules = { cjson = { defines = { + "DISABLE_INVALID_NUMBERS" + } } } } + }, + copy_directories = { "tests" } +} + +-- vi:ai et sw=4 ts=4: diff --git a/app/cjson/lua-cjson.spec b/app/cjson/lua-cjson.spec new file mode 100644 index 00000000..13fc56d2 --- /dev/null +++ b/app/cjson/lua-cjson.spec @@ -0,0 +1,80 @@ +%define luaver 5.1 +%define lualibdir %{_libdir}/lua/%{luaver} +%define luadatadir %{_datadir}/lua/%{luaver} + +Name: lua-cjson +Version: 2.1devel +Release: 1%{?dist} +Summary: A fast JSON encoding/parsing module for Lua + +Group: Development/Libraries +License: MIT +URL: http://www.kyne.com.au/~mark/software/lua-cjson/ +Source0: http://www.kyne.com.au/~mark/software/lua-cjson/download/lua-cjson-%{version}.tar.gz +BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX) + +BuildRequires: lua >= %{luaver}, lua-devel >= %{luaver} +Requires: lua >= %{luaver} + +%description +The Lua CJSON module provides JSON support for Lua. It features: +- Fast, standards compliant encoding/parsing routines +- Full support for JSON with UTF-8, including decoding surrogate pairs +- Optional run-time support for common exceptions to the JSON specification + (infinity, NaN,..) +- No dependencies on other libraries + + +%prep +%setup -q + + +%build +make %{?_smp_mflags} CFLAGS="%{optflags}" LUA_INCLUDE_DIR="%{_includedir}" + + +%install +rm -rf "$RPM_BUILD_ROOT" +make install DESTDIR="$RPM_BUILD_ROOT" LUA_CMODULE_DIR="%{lualibdir}" +make install-extra DESTDIR="$RPM_BUILD_ROOT" LUA_MODULE_DIR="%{luadatadir}" \ + LUA_BIN_DIR="%{_bindir}" + + +%clean +rm -rf "$RPM_BUILD_ROOT" + + +%preun +/bin/rm -f "%{luadatadir}/cjson/tests/utf8.dat" + + +%files +%defattr(-,root,root,-) +%doc LICENSE NEWS performance.html performance.txt manual.html manual.txt rfc4627.txt THANKS +%{lualibdir}/* +%{luadatadir}/* +%{_bindir}/* + + +%changelog +* Thu Mar 1 2012 Mark Pulford - 2.1.0-1 +- Update for 2.1.0 + +* Sun Jan 22 2012 Mark Pulford - 2.0.0-1 +- Update for 2.0.0 +- Install lua2json / json2lua utilities + +* Wed Nov 27 2011 Mark Pulford - 1.0.4-1 +- Update for 1.0.4 + +* Wed Sep 15 2011 Mark Pulford - 1.0.3-1 +- Update for 1.0.3 + +* Sun May 29 2011 Mark Pulford - 1.0.2-1 +- Update for 1.0.2 + +* Sun May 10 2011 Mark Pulford - 1.0.1-1 +- Update for 1.0.1 + +* Sun May 1 2011 Mark Pulford - 1.0-1 +- Initial package diff --git a/app/cjson/lua/cjson/util.lua b/app/cjson/lua/cjson/util.lua new file mode 100644 index 00000000..6916dad0 --- /dev/null +++ b/app/cjson/lua/cjson/util.lua @@ -0,0 +1,271 @@ +local json = require "cjson" + +-- Various common routines used by the Lua CJSON package +-- +-- Mark Pulford + +-- Determine with a Lua table can be treated as an array. +-- Explicitly returns "not an array" for very sparse arrays. +-- Returns: +-- -1 Not an array +-- 0 Empty table +-- >0 Highest index in the array +local function is_array(table) + local max = 0 + local count = 0 + for k, v in pairs(table) do + if type(k) == "number" then + if k > max then max = k end + count = count + 1 + else + return -1 + end + end + if max > count * 2 then + return -1 + end + + return max +end + +local serialise_value + +local function serialise_table(value, indent, depth) + local spacing, spacing2, indent2 + if indent then + spacing = "\n" .. indent + spacing2 = spacing .. " " + indent2 = indent .. " " + else + spacing, spacing2, indent2 = " ", " ", false + end + depth = depth + 1 + if depth > 50 then + return "Cannot serialise any further: too many nested tables" + end + + local max = is_array(value) + + local comma = false + local fragment = { "{" .. spacing2 } + if max > 0 then + -- Serialise array + for i = 1, max do + if comma then + table.insert(fragment, "," .. spacing2) + end + table.insert(fragment, serialise_value(value[i], indent2, depth)) + comma = true + end + elseif max < 0 then + -- Serialise table + for k, v in pairs(value) do + if comma then + table.insert(fragment, "," .. spacing2) + end + table.insert(fragment, + ("[%s] = %s"):format(serialise_value(k, indent2, depth), + serialise_value(v, indent2, depth))) + comma = true + end + end + table.insert(fragment, spacing .. "}") + + return table.concat(fragment) +end + +function serialise_value(value, indent, depth) + if indent == nil then indent = "" end + if depth == nil then depth = 0 end + + if value == json.null then + return "json.null" + elseif type(value) == "string" then + return ("%q"):format(value) + elseif type(value) == "nil" or type(value) == "number" or + type(value) == "boolean" then + return tostring(value) + elseif type(value) == "table" then + return serialise_table(value, indent, depth) + else + return "\"<" .. type(value) .. ">\"" + end +end + +local function file_load(filename) + local file + if filename == nil then + file = io.stdin + else + local err + file, err = io.open(filename, "rb") + if file == nil then + error(("Unable to read '%s': %s"):format(filename, err)) + end + end + local data = file:read("*a") + + if filename ~= nil then + file:close() + end + + if data == nil then + error("Failed to read " .. filename) + end + + return data +end + +local function file_save(filename, data) + local file + if filename == nil then + file = io.stdout + else + local err + file, err = io.open(filename, "wb") + if file == nil then + error(("Unable to write '%s': %s"):format(filename, err)) + end + end + file:write(data) + if filename ~= nil then + file:close() + end +end + +local function compare_values(val1, val2) + local type1 = type(val1) + local type2 = type(val2) + if type1 ~= type2 then + return false + end + + -- Check for NaN + if type1 == "number" and val1 ~= val1 and val2 ~= val2 then + return true + end + + if type1 ~= "table" then + return val1 == val2 + end + + -- check_keys stores all the keys that must be checked in val2 + local check_keys = {} + for k, _ in pairs(val1) do + check_keys[k] = true + end + + for k, v in pairs(val2) do + if not check_keys[k] then + return false + end + + if not compare_values(val1[k], val2[k]) then + return false + end + + check_keys[k] = nil + end + for k, _ in pairs(check_keys) do + -- Not the same if any keys from val1 were not found in val2 + return false + end + return true +end + +local test_count_pass = 0 +local test_count_total = 0 + +local function run_test_summary() + return test_count_pass, test_count_total +end + +local function run_test(testname, func, input, should_work, output) + local function status_line(name, status, value) + local statusmap = { [true] = ":success", [false] = ":error" } + if status ~= nil then + name = name .. statusmap[status] + end + print(("[%s] %s"):format(name, serialise_value(value, false))) + end + + local result = { pcall(func, unpack(input)) } + local success = table.remove(result, 1) + + local correct = false + if success == should_work and compare_values(result, output) then + correct = true + test_count_pass = test_count_pass + 1 + end + test_count_total = test_count_total + 1 + + local teststatus = { [true] = "PASS", [false] = "FAIL" } + print(("==> Test [%d] %s: %s"):format(test_count_total, testname, + teststatus[correct])) + + status_line("Input", nil, input) + if not correct then + status_line("Expected", should_work, output) + end + status_line("Received", success, result) + print() + + return correct, result +end + +local function run_test_group(tests) + local function run_helper(name, func, input) + if type(name) == "string" and #name > 0 then + print("==> " .. name) + end + -- Not a protected call, these functions should never generate errors. + func(unpack(input or {})) + print() + end + + for _, v in ipairs(tests) do + -- Run the helper if "should_work" is missing + if v[4] == nil then + run_helper(unpack(v)) + else + run_test(unpack(v)) + end + end +end + +-- Run a Lua script in a separate environment +local function run_script(script, env) + local env = env or {} + local func + + -- Use setfenv() if it exists, otherwise assume Lua 5.2 load() exists + if _G.setfenv then + func = loadstring(script) + if func then + setfenv(func, env) + end + else + func = load(script, nil, nil, env) + end + + if func == nil then + error("Invalid syntax.") + end + func() + + return env +end + +-- Export functions +return { + serialise_value = serialise_value, + file_load = file_load, + file_save = file_save, + compare_values = compare_values, + run_test_summary = run_test_summary, + run_test = run_test, + run_test_group = run_test_group, + run_script = run_script +} + +-- vi:ai et sw=4 ts=4: diff --git a/app/cjson/lua/json2lua.lua b/app/cjson/lua/json2lua.lua new file mode 100644 index 00000000..014416d2 --- /dev/null +++ b/app/cjson/lua/json2lua.lua @@ -0,0 +1,14 @@ +#!/usr/bin/env lua + +-- usage: json2lua.lua [json_file] +-- +-- Eg: +-- echo '[ "testing" ]' | ./json2lua.lua +-- ./json2lua.lua test.json + +local json = require "cjson" +local util = require "cjson.util" + +local json_text = util.file_load(arg[1]) +local t = json.decode(json_text) +print(util.serialise_value(t)) diff --git a/app/cjson/lua/lua2json.lua b/app/cjson/lua/lua2json.lua new file mode 100644 index 00000000..aee8869a --- /dev/null +++ b/app/cjson/lua/lua2json.lua @@ -0,0 +1,20 @@ +#!/usr/bin/env lua + +-- usage: lua2json.lua [lua_file] +-- +-- Eg: +-- echo '{ "testing" }' | ./lua2json.lua +-- ./lua2json.lua test.lua + +local json = require "cjson" +local util = require "cjson.util" + +local env = { + json = { null = json.null }, + null = json.null +} + +local t = util.run_script("data = " .. util.file_load(arg[1]), env) +print(json.encode(t.data)) + +-- vi:ai et sw=4 ts=4: diff --git a/app/cjson/manual.txt b/app/cjson/manual.txt new file mode 100644 index 00000000..a12e3785 --- /dev/null +++ b/app/cjson/manual.txt @@ -0,0 +1,612 @@ += Lua CJSON 2.1devel Manual = +Mark Pulford +:revdate: 1st March 2012 + +Overview +-------- + +The Lua CJSON module provides JSON support for Lua. + +*Features*:: +- Fast, standards compliant encoding/parsing routines +- Full support for JSON with UTF-8, including decoding surrogate pairs +- Optional run-time support for common exceptions to the JSON + specification (infinity, NaN,..) +- No dependencies on other libraries + +*Caveats*:: +- UTF-16 and UTF-32 are not supported + +Lua CJSON is covered by the MIT license. Review the file +LICENSE+ for +details. + +The latest version of this software is available from the +http://www.kyne.com.au/%7Emark/software/lua-cjson.php[Lua CJSON website]. + +Feel free to email me if you have any patches, suggestions, or comments. + + +Installation +------------ + +Lua CJSON requires either http://www.lua.org[Lua] 5.1, Lua 5.2, or +http://www.luajit.org[LuaJIT] to build. + +The build method can be selected from 4 options: + +Make:: Unix (including Linux, BSD, Mac OSX & Solaris), Windows +CMake:: Unix, Windows +RPM:: Linux +LuaRocks:: Unix, Windows + + +Make +~~~~ + +The included +Makefile+ has generic settings. + +First, review and update the included makefile to suit your platform (if +required). + +Next, build and install the module: + +[source,sh] +make install + +Or install manually into your Lua module directory: + +[source,sh] +make +cp cjson.so $LUA_MODULE_DIRECTORY + + +CMake +~~~~~ + +http://www.cmake.org[CMake] can generate build configuration for many +different platforms (including Unix and Windows). + +First, generate the makefile for your platform using CMake. If CMake is +unable to find Lua, manually set the +LUA_DIR+ environment variable to +the base prefix of your Lua 5.1 installation. + +While +cmake+ is used in the example below, +ccmake+ or +cmake-gui+ may +be used to present an interface for changing the default build options. + +[source,sh] +mkdir build +cd build +# Optional: export LUA_DIR=$LUA51_PREFIX +cmake .. + +Next, build and install the module: + +[source,sh] +make install +# Or: +make +cp cjson.so $LUA_MODULE_DIRECTORY + +Review the +http://www.cmake.org/cmake/help/documentation.html[CMake documentation] +for further details. + + +RPM +~~~ + +Linux distributions using http://rpm.org[RPM] can create a package via +the included RPM spec file. Ensure the +rpm-build+ package (or similar) +has been installed. + +Build and install the module via RPM: + +[source,sh] +rpmbuild -tb lua-cjson-2.1devel.tar.gz +rpm -Uvh $LUA_CJSON_RPM + + +LuaRocks +~~~~~~~~ + +http://luarocks.org[LuaRocks] can be used to install and manage Lua +modules on a wide range of platforms (including Windows). + +First, extract the Lua CJSON source package. + +Next, install the module: + +[source,sh] +cd lua-cjson-2.1devel +luarocks make + +[NOTE] +LuaRocks does not support platform specific configuration for Solaris. +On Solaris, you may need to manually uncomment +USE_INTERNAL_ISINF+ in +the rockspec before building this module. + +Review the http://luarocks.org/en/Documentation[LuaRocks documentation] +for further details. + + +[[build_options]] +Build Options (#define) +~~~~~~~~~~~~~~~~~~~~~~~ + +Lua CJSON offers several +#define+ build options to address portability +issues, and enable non-default features. Some build methods may +automatically set platform specific options if required. Other features +should be enabled manually. + +USE_INTERNAL_ISINF:: Workaround for Solaris platforms missing +isinf+. +DISABLE_INVALID_NUMBERS:: Recommended on platforms where +strtod+ / + +sprintf+ are not POSIX compliant (eg, Windows MinGW). Prevents + +cjson.encode_invalid_numbers+ and +cjson.decode_invalid_numbers+ from + being enabled. However, +cjson.encode_invalid_numbers+ may still be + set to +"null"+. When using the Lua CJSON built-in floating point + conversion this option is unnecessary and is ignored. + + +Built-in floating point conversion +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Lua CJSON may be built with David Gay's +http://www.netlib.org/fp/[floating point conversion routines]. This can +increase overall performance by up to 50% on some platforms when +converting a large amount of numeric data. However, this option reduces +portability and is disabled by default. + +USE_INTERNAL_FPCONV:: Enable internal number conversion routines. +IEEE_BIG_ENDIAN:: Must be set on big endian architectures. +MULTIPLE_THREADS:: Must be set if Lua CJSON may be used in a + multi-threaded application. Requires the _pthreads_ library. + + +API (Functions) +--------------- + +Synopsis +~~~~~~~~ + +[source,lua] +------------ +-- Module instantiation +local cjson = require "cjson" +local cjson2 = cjson.new() +local cjson_safe = require "cjson.safe" + +-- Translate Lua value to/from JSON +text = cjson.encode(value) +value = cjson.decode(text) + +-- Get and/or set Lua CJSON configuration +setting = cjson.decode_invalid_numbers([setting]) +setting = cjson.encode_invalid_numbers([setting]) +keep = cjson.encode_keep_buffer([keep]) +depth = cjson.encode_max_depth([depth]) +depth = cjson.decode_max_depth([depth]) +convert, ratio, safe = cjson.encode_sparse_array([convert[, ratio[, safe]]]) +------------ + + +Module Instantiation +~~~~~~~~~~~~~~~~~~~~ + +[source,lua] +------------ +local cjson = require "cjson" +local cjson2 = cjson.new() +local cjson_safe = require "cjson.safe" +------------ + +Import Lua CJSON via the Lua +require+ function. Lua CJSON does not +register a global module table. + +The +cjson+ module will throw an error during JSON conversion if any +invalid data is encountered. Refer to <> +and <> for details. + +The +cjson.safe+ module behaves identically to the +cjson+ module, +except when errors are encountered during JSON conversion. On error, the ++cjson_safe.encode+ and +cjson_safe.decode+ functions will return ++nil+ followed by the error message. + ++cjson.new+ can be used to instantiate an independent copy of the Lua +CJSON module. The new module has a separate persistent encoding buffer, +and default settings. + +Lua CJSON can support Lua implementations using multiple preemptive +threads within a single Lua state provided the persistent encoding +buffer is not shared. This can be achieved by one of the following +methods: + +- Disabling the persistent encoding buffer with + <> +- Ensuring each thread calls <> separately (ie, + treat +cjson.encode+ as non-reentrant). +- Using a separate +cjson+ module table per preemptive thread + (+cjson.new+) + +[NOTE] +Lua CJSON uses +strtod+ and +snprintf+ to perform numeric conversion as +they are usually well supported, fast and bug free. However, these +functions require a workaround for JSON encoding/parsing under locales +using a comma decimal separator. Lua CJSON detects the current locale +during instantiation to determine and automatically implement the +workaround if required. Lua CJSON should be reinitialised via ++cjson.new+ if the locale of the current process changes. Using a +different locale per thread is not supported. + + +decode +~~~~~~ + +[source,lua] +------------ +value = cjson.decode(json_text) +------------ + ++cjson.decode+ will deserialise any UTF-8 JSON string into a Lua value +or table. + +UTF-16 and UTF-32 JSON strings are not supported. + ++cjson.decode+ requires that any NULL (ASCII 0) and double quote (ASCII +34) characters are escaped within strings. All escape codes will be +decoded and other bytes will be passed transparently. UTF-8 characters +are not validated during decoding and should be checked elsewhere if +required. + +JSON +null+ will be converted to a NULL +lightuserdata+ value. This can +be compared with +cjson.null+ for convenience. + +By default, numbers incompatible with the JSON specification (infinity, +NaN, hexadecimal) can be decoded. This default can be changed with +<>. + +.Example: Decoding +[source,lua] +json_text = '[ true, { "foo": "bar" } ]' +value = cjson.decode(json_text) +-- Returns: { true, { foo = "bar" } } + +[CAUTION] +Care must be taken after decoding JSON objects with numeric keys. Each +numeric key will be stored as a Lua +string+. Any subsequent code +assuming type +number+ may break. + + +[[decode_invalid_numbers]] +decode_invalid_numbers +~~~~~~~~~~~~~~~~~~~~~~ + +[source,lua] +------------ +setting = cjson.decode_invalid_numbers([setting]) +-- "setting" must be a boolean. Default: true. +------------ + +Lua CJSON may generate an error when trying to decode numbers not +supported by the JSON specification. _Invalid numbers_ are defined as: + +- infinity +- NaN +- hexadecimal + +Available settings: + ++true+:: Accept and decode _invalid numbers_. This is the default + setting. ++false+:: Throw an error when _invalid numbers_ are encountered. + +The current setting is always returned, and is only updated when an +argument is provided. + + +[[decode_max_depth]] +decode_max_depth +~~~~~~~~~~~~~~~~ + +[source,lua] +------------ +depth = cjson.decode_max_depth([depth]) +-- "depth" must be a positive integer. Default: 1000. +------------ + +Lua CJSON will generate an error when parsing deeply nested JSON once +the maximum array/object depth has been exceeded. This check prevents +unnecessarily complicated JSON from slowing down the application, or +crashing the application due to lack of process stack space. + +An error may be generated before the depth limit is hit if Lua is unable +to allocate more objects on the Lua stack. + +By default, Lua CJSON will reject JSON with arrays and/or objects nested +more than 1000 levels deep. + +The current setting is always returned, and is only updated when an +argument is provided. + + +[[encode]] +encode +~~~~~~ + +[source,lua] +------------ +json_text = cjson.encode(value) +------------ + ++cjson.encode+ will serialise a Lua value into a string containing the +JSON representation. + ++cjson.encode+ supports the following types: + +- +boolean+ +- +lightuserdata+ (NULL value only) +- +nil+ +- +number+ +- +string+ +- +table+ + +The remaining Lua types will generate an error: + +- +function+ +- +lightuserdata+ (non-NULL values) +- +thread+ +- +userdata+ + +By default, numbers are encoded with 14 significant digits. Refer to +<> for details. + +Lua CJSON will escape the following characters within each UTF-8 string: + +- Control characters (ASCII 0 - 31) +- Double quote (ASCII 34) +- Forward slash (ASCII 47) +- Blackslash (ASCII 92) +- Delete (ASCII 127) + +All other bytes are passed transparently. + +[CAUTION] +========= +Lua CJSON will successfully encode/decode binary strings, but this is +technically not supported by JSON and may not be compatible with other +JSON libraries. To ensure the output is valid JSON, applications should +ensure all Lua strings passed to +cjson.encode+ are UTF-8. + +Base64 is commonly used to encode binary data as the most efficient +encoding under UTF-8 can only reduce the encoded size by a further +~8%. Lua Base64 routines can be found in the +http://w3.impa.br/%7Ediego/software/luasocket/[LuaSocket] and +http://www.tecgraf.puc-rio.br/%7Elhf/ftp/lua/#lbase64[lbase64] packages. +========= + +Lua CJSON uses a heuristic to determine whether to encode a Lua table as +a JSON array or an object. A Lua table with only positive integer keys +of type +number+ will be encoded as a JSON array. All other tables will +be encoded as a JSON object. + +Lua CJSON does not use metamethods when serialising tables. + +- +rawget+ is used to iterate over Lua arrays +- +next+ is used to iterate over Lua objects + +Lua arrays with missing entries (_sparse arrays_) may optionally be +encoded in several different ways. Refer to +<> for details. + +JSON object keys are always strings. Hence +cjson.encode+ only supports +table keys which are type +number+ or +string+. All other types will +generate an error. + +[NOTE] +Standards compliant JSON must be encapsulated in either an object (+{}+) +or an array (+[]+). If strictly standards compliant JSON is desired, a +table must be passed to +cjson.encode+. + +By default, encoding the following Lua values will generate errors: + +- Numbers incompatible with the JSON specification (infinity, NaN) +- Tables nested more than 1000 levels deep +- Excessively sparse Lua arrays + +These defaults can be changed with: + +- <> +- <> +- <> + +.Example: Encoding +[source,lua] +value = { true, { foo = "bar" } } +json_text = cjson.encode(value) +-- Returns: '[true,{"foo":"bar"}]' + + +[[encode_invalid_numbers]] +encode_invalid_numbers +~~~~~~~~~~~~~~~~~~~~~~ +[source,lua] +------------ +setting = cjson.encode_invalid_numbers([setting]) +-- "setting" must a boolean or "null". Default: false. +------------ + +Lua CJSON may generate an error when encoding floating point numbers not +supported by the JSON specification (_invalid numbers_): + +- infinity +- NaN + +Available settings: + ++true+:: Allow _invalid numbers_ to be encoded using the Javascript + compatible values +NaN+ and +Infinity+. This will generate + non-standard JSON, but these values are supported by some libraries. ++"null"+:: Encode _invalid numbers_ as a JSON +null+ value. This allows + infinity and NaN to be encoded into valid JSON. ++false+:: Throw an error when attempting to encode _invalid numbers_. + This is the default setting. + +The current setting is always returned, and is only updated when an +argument is provided. + + +[[encode_keep_buffer]] +encode_keep_buffer +~~~~~~~~~~~~~~~~~~ + +[source,lua] +------------ +keep = cjson.encode_keep_buffer([keep]) +-- "keep" must be a boolean. Default: true. +------------ + +Lua CJSON can reuse the JSON encoding buffer to improve performance. + +Available settings: + ++true+:: The buffer will grow to the largest size required and is not + freed until the Lua CJSON module is garbage collected. This is the + default setting. ++false+:: Free the encode buffer after each call to +cjson.encode+. + +The current setting is always returned, and is only updated when an +argument is provided. + + +[[encode_max_depth]] +encode_max_depth +~~~~~~~~~~~~~~~~ + +[source,lua] +------------ +depth = cjson.encode_max_depth([depth]) +-- "depth" must be a positive integer. Default: 1000. +------------ + +Once the maximum table depth has been exceeded Lua CJSON will generate +an error. This prevents a deeply nested or recursive data structure from +crashing the application. + +By default, Lua CJSON will generate an error when trying to encode data +structures with more than 1000 nested tables. + +The current setting is always returned, and is only updated when an +argument is provided. + +.Example: Recursive Lua table +[source,lua] +a = {}; a[1] = a + + +[[encode_number_precision]] +encode_number_precision +~~~~~~~~~~~~~~~~~~~~~~~ + +[source,lua] +------------ +precision = cjson.encode_number_precision([precision]) +-- "precision" must be an integer between 1 and 14. Default: 14. +------------ + +The amount of significant digits returned by Lua CJSON when encoding +numbers can be changed to balance accuracy versus performance. For data +structures containing many numbers, setting ++cjson.encode_number_precision+ to a smaller integer, for example +3+, +can improve encoding performance by up to 50%. + +By default, Lua CJSON will output 14 significant digits when converting +a number to text. + +The current setting is always returned, and is only updated when an +argument is provided. + + +[[encode_sparse_array]] +encode_sparse_array +~~~~~~~~~~~~~~~~~~~ + +[source,lua] +------------ +convert, ratio, safe = cjson.encode_sparse_array([convert[, ratio[, safe]]]) +-- "convert" must be a boolean. Default: false. +-- "ratio" must be a positive integer. Default: 2. +-- "safe" must be a positive integer. Default: 10. +------------ + +Lua CJSON classifies a Lua table into one of three kinds when encoding a +JSON array. This is determined by the number of values missing from the +Lua array as follows: + +Normal:: All values are available. +Sparse:: At least 1 value is missing. +Excessively sparse:: The number of values missing exceeds the configured + ratio. + +Lua CJSON encodes sparse Lua arrays as JSON arrays using JSON +null+ for +the missing entries. + +An array is excessively sparse when all the following conditions are +met: + +- +ratio+ > +0+ +- _maximum_index_ > +safe+ +- _maximum_index_ > _item_count_ * +ratio+ + +Lua CJSON will never consider an array to be _excessively sparse_ when ++ratio+ = +0+. The +safe+ limit ensures that small Lua arrays are always +encoded as sparse arrays. + +By default, attempting to encode an _excessively sparse_ array will +generate an error. If +convert+ is set to +true+, _excessively sparse_ +arrays will be converted to a JSON object. + +The current settings are always returned. A particular setting is only +changed when the argument is provided (non-++nil++). + +.Example: Encoding a sparse array +[source,lua] +cjson.encode({ [3] = "data" }) +-- Returns: '[null,null,"data"]' + +.Example: Enabling conversion to a JSON object +[source,lua] +cjson.encode_sparse_array(true) +cjson.encode({ [1000] = "excessively sparse" }) +-- Returns: '{"1000":"excessively sparse"}' + + +API (Variables) +--------------- + +_NAME +~~~~~ + +The name of the Lua CJSON module (+"cjson"+). + + +_VERSION +~~~~~~~~ + +The version number of the Lua CJSON module (+"2.1devel"+). + + +null +~~~~ + +Lua CJSON decodes JSON +null+ as a Lua +lightuserdata+ NULL pointer. ++cjson.null+ is provided for comparison. + + +[sect1] +References +---------- + +- http://tools.ietf.org/html/rfc4627[RFC 4627] +- http://www.json.org/[JSON website] + + +// vi:ft=asciidoc tw=72: diff --git a/app/cjson/performance.txt b/app/cjson/performance.txt new file mode 100644 index 00000000..fc3a5bb5 --- /dev/null +++ b/app/cjson/performance.txt @@ -0,0 +1,89 @@ +JSON module performance comparison under Lua +============================================ +Mark Pulford +:revdate: January 22, 2012 + +This performance comparison aims to provide a guide of relative +performance between several fast and popular JSON modules. + +The examples used in this comparison were mostly sourced from the +http://json.org[JSON website] and +http://tools.ietf.org/html/rfc4627[RFC 4627]. + +Performance will vary widely between platforms and data sets. These +results should only be used as an approximation. + + +Modules +------- + +The following JSON modules for Lua were tested: + +http://chiselapp.com/user/dhkolf/repository/dkjson/[DKJSON 2.1]:: + - Lua implementation with no dependencies on other libraries + - Supports LPeg to improve decode performance + +https://github.com/brimworks/lua-yajl[Lua YAJL 2.0]:: + - C wrapper for the YAJL library + +http://www.kyne.com.au/%7Emark/software/lua-cjson.php[Lua CSJON 2.0.0]:: + - C implementation with no dependencies on other libraries + + +Summary +------- + +All modules were built and tested as follows: + +DKJSON:: Tested with/without LPeg 10.2. +Lua YAJL:: Tested with YAJL 2.0.4. +Lua CJSON:: Tested with Libc and internal floating point conversion + routines. + +The following Lua implementations were used for this comparison: + +- http://www.lua.org[Lua 5.1.4] (_Lua_) +- http://www.luajit.org[LuaJIT 2.0.0-beta9] (_JIT_) + +These results show the number of JSON operations per second sustained by +each module. All results have been normalised against the pure Lua +DKJSON implementation. + +.Decoding performance +............................................................................ + | DKJSON | Lua YAJL | Lua CJSON + | No LPeg With LPeg | | Libc Internal + | Lua JIT Lua JIT | Lua JIT | Lua JIT Lua JIT +example1 | 1x 2x 2.6x 3.4x | 7.1x 10x | 14x 20x 14x 20x +example2 | 1x 2.2x 2.9x 4.4x | 6.7x 9.9x | 14x 22x 14x 22x +example3 | 1x 2.1x 3x 4.3x | 6.9x 9.3x | 14x 21x 15x 22x +example4 | 1x 2x 2.5x 3.7x | 7.3x 10x | 12x 19x 12x 20x +example5 | 1x 2.2x 3x 4.5x | 7.8x 11x | 16x 24x 16x 24x +numbers | 1x 2.2x 2.3x 4x | 4.6x 5.5x | 8.9x 10x 13x 17x +rfc-example1 | 1x 2.1x 2.8x 4.3x | 6.1x 8.1x | 13x 19x 14x 21x +rfc-example2 | 1x 2.1x 3.1x 4.2x | 7.1x 9.2x | 15x 21x 17x 24x +types | 1x 2.2x 2.6x 4.3x | 5.3x 7.4x | 12x 20x 13x 21x +-------------|-------------------------|------------|----------------------- += Average => | 1x 2.1x 2.7x 4.1x | 6.5x 9x | 13x 20x 14x 21x +............................................................................ + +.Encoding performance +............................................................................. + | DKJSON | Lua YAJL | Lua CJSON + | No LPeg With LPeg | | Libc Internal + | Lua JIT Lua JIT | Lua JIT | Lua JIT Lua JIT +example1 | 1x 1.8x 0.97x 1.6x | 3.1x 5.2x | 23x 29x 23x 29x +example2 | 1x 2x 0.97x 1.7x | 2.6x 4.3x | 22x 28x 22x 28x +example3 | 1x 1.9x 0.98x 1.6x | 2.8x 4.3x | 13x 15x 16x 18x +example4 | 1x 1.7x 0.96x 1.3x | 3.9x 6.1x | 15x 19x 17x 21x +example5 | 1x 2x 0.98x 1.7x | 2.7x 4.5x | 20x 23x 20x 23x +numbers | 1x 2.3x 1x 2.2x | 1.3x 1.9x | 3.8x 4.1x 4.2x 4.6x +rfc-example1 | 1x 1.9x 0.97x 1.6x | 2.2x 3.2x | 8.5x 9.3x 11x 12x +rfc-example2 | 1x 1.9x 0.98x 1.6x | 2.6x 3.9x | 10x 11x 17x 19x +types | 1x 2.2x 0.97x 2x | 1.2x 1.9x | 11x 13x 12x 14x +-------------|-------------------------|------------|----------------------- += Average => | 1x 1.9x 0.98x 1.7x | 2.5x 3.9x | 14x 17x 16x 19x +............................................................................. + + +// vi:ft=asciidoc tw=72: diff --git a/app/cjson/rfc4627.txt b/app/cjson/rfc4627.txt new file mode 100644 index 00000000..67b89092 --- /dev/null +++ b/app/cjson/rfc4627.txt @@ -0,0 +1,563 @@ + + + + + + +Network Working Group D. Crockford +Request for Comments: 4627 JSON.org +Category: Informational July 2006 + + + The application/json Media Type for JavaScript Object Notation (JSON) + +Status of This Memo + + This memo provides information for the Internet community. It does + not specify an Internet standard of any kind. Distribution of this + memo is unlimited. + +Copyright Notice + + Copyright (C) The Internet Society (2006). + +Abstract + + JavaScript Object Notation (JSON) is a lightweight, text-based, + language-independent data interchange format. It was derived from + the ECMAScript Programming Language Standard. JSON defines a small + set of formatting rules for the portable representation of structured + data. + +1. Introduction + + JavaScript Object Notation (JSON) is a text format for the + serialization of structured data. It is derived from the object + literals of JavaScript, as defined in the ECMAScript Programming + Language Standard, Third Edition [ECMA]. + + JSON can represent four primitive types (strings, numbers, booleans, + and null) and two structured types (objects and arrays). + + A string is a sequence of zero or more Unicode characters [UNICODE]. + + An object is an unordered collection of zero or more name/value + pairs, where a name is a string and a value is a string, number, + boolean, null, object, or array. + + An array is an ordered sequence of zero or more values. + + The terms "object" and "array" come from the conventions of + JavaScript. + + JSON's design goals were for it to be minimal, portable, textual, and + a subset of JavaScript. + + + +Crockford Informational [Page 1] + +RFC 4627 JSON July 2006 + + +1.1. Conventions Used in This Document + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in [RFC2119]. + + The grammatical rules in this document are to be interpreted as + described in [RFC4234]. + +2. JSON Grammar + + A JSON text is a sequence of tokens. The set of tokens includes six + structural characters, strings, numbers, and three literal names. + + A JSON text is a serialized object or array. + + JSON-text = object / array + + These are the six structural characters: + + begin-array = ws %x5B ws ; [ left square bracket + + begin-object = ws %x7B ws ; { left curly bracket + + end-array = ws %x5D ws ; ] right square bracket + + end-object = ws %x7D ws ; } right curly bracket + + name-separator = ws %x3A ws ; : colon + + value-separator = ws %x2C ws ; , comma + + Insignificant whitespace is allowed before or after any of the six + structural characters. + + ws = *( + %x20 / ; Space + %x09 / ; Horizontal tab + %x0A / ; Line feed or New line + %x0D ; Carriage return + ) + +2.1. Values + + A JSON value MUST be an object, array, number, or string, or one of + the following three literal names: + + false null true + + + +Crockford Informational [Page 2] + +RFC 4627 JSON July 2006 + + + The literal names MUST be lowercase. No other literal names are + allowed. + + value = false / null / true / object / array / number / string + + false = %x66.61.6c.73.65 ; false + + null = %x6e.75.6c.6c ; null + + true = %x74.72.75.65 ; true + +2.2. Objects + + An object structure is represented as a pair of curly brackets + surrounding zero or more name/value pairs (or members). A name is a + string. A single colon comes after each name, separating the name + from the value. A single comma separates a value from a following + name. The names within an object SHOULD be unique. + + object = begin-object [ member *( value-separator member ) ] + end-object + + member = string name-separator value + +2.3. Arrays + + An array structure is represented as square brackets surrounding zero + or more values (or elements). Elements are separated by commas. + + array = begin-array [ value *( value-separator value ) ] end-array + +2.4. Numbers + + The representation of numbers is similar to that used in most + programming languages. A number contains an integer component that + may be prefixed with an optional minus sign, which may be followed by + a fraction part and/or an exponent part. + + Octal and hex forms are not allowed. Leading zeros are not allowed. + + A fraction part is a decimal point followed by one or more digits. + + An exponent part begins with the letter E in upper or lowercase, + which may be followed by a plus or minus sign. The E and optional + sign are followed by one or more digits. + + Numeric values that cannot be represented as sequences of digits + (such as Infinity and NaN) are not permitted. + + + +Crockford Informational [Page 3] + +RFC 4627 JSON July 2006 + + + number = [ minus ] int [ frac ] [ exp ] + + decimal-point = %x2E ; . + + digit1-9 = %x31-39 ; 1-9 + + e = %x65 / %x45 ; e E + + exp = e [ minus / plus ] 1*DIGIT + + frac = decimal-point 1*DIGIT + + int = zero / ( digit1-9 *DIGIT ) + + minus = %x2D ; - + + plus = %x2B ; + + + zero = %x30 ; 0 + +2.5. Strings + + The representation of strings is similar to conventions used in the C + family of programming languages. A string begins and ends with + quotation marks. All Unicode characters may be placed within the + quotation marks except for the characters that must be escaped: + quotation mark, reverse solidus, and the control characters (U+0000 + through U+001F). + + Any character may be escaped. If the character is in the Basic + Multilingual Plane (U+0000 through U+FFFF), then it may be + represented as a six-character sequence: a reverse solidus, followed + by the lowercase letter u, followed by four hexadecimal digits that + encode the character's code point. The hexadecimal letters A though + F can be upper or lowercase. So, for example, a string containing + only a single reverse solidus character may be represented as + "\u005C". + + Alternatively, there are two-character sequence escape + representations of some popular characters. So, for example, a + string containing only a single reverse solidus character may be + represented more compactly as "\\". + + To escape an extended character that is not in the Basic Multilingual + Plane, the character is represented as a twelve-character sequence, + encoding the UTF-16 surrogate pair. So, for example, a string + containing only the G clef character (U+1D11E) may be represented as + "\uD834\uDD1E". + + + +Crockford Informational [Page 4] + +RFC 4627 JSON July 2006 + + + string = quotation-mark *char quotation-mark + + char = unescaped / + escape ( + %x22 / ; " quotation mark U+0022 + %x5C / ; \ reverse solidus U+005C + %x2F / ; / solidus U+002F + %x62 / ; b backspace U+0008 + %x66 / ; f form feed U+000C + %x6E / ; n line feed U+000A + %x72 / ; r carriage return U+000D + %x74 / ; t tab U+0009 + %x75 4HEXDIG ) ; uXXXX U+XXXX + + escape = %x5C ; \ + + quotation-mark = %x22 ; " + + unescaped = %x20-21 / %x23-5B / %x5D-10FFFF + +3. Encoding + + JSON text SHALL be encoded in Unicode. The default encoding is + UTF-8. + + Since the first two characters of a JSON text will always be ASCII + characters [RFC0020], it is possible to determine whether an octet + stream is UTF-8, UTF-16 (BE or LE), or UTF-32 (BE or LE) by looking + at the pattern of nulls in the first four octets. + + 00 00 00 xx UTF-32BE + 00 xx 00 xx UTF-16BE + xx 00 00 00 UTF-32LE + xx 00 xx 00 UTF-16LE + xx xx xx xx UTF-8 + +4. Parsers + + A JSON parser transforms a JSON text into another representation. A + JSON parser MUST accept all texts that conform to the JSON grammar. + A JSON parser MAY accept non-JSON forms or extensions. + + An implementation may set limits on the size of texts that it + accepts. An implementation may set limits on the maximum depth of + nesting. An implementation may set limits on the range of numbers. + An implementation may set limits on the length and character contents + of strings. + + + + +Crockford Informational [Page 5] + +RFC 4627 JSON July 2006 + + +5. Generators + + A JSON generator produces JSON text. The resulting text MUST + strictly conform to the JSON grammar. + +6. IANA Considerations + + The MIME media type for JSON text is application/json. + + Type name: application + + Subtype name: json + + Required parameters: n/a + + Optional parameters: n/a + + Encoding considerations: 8bit if UTF-8; binary if UTF-16 or UTF-32 + + JSON may be represented using UTF-8, UTF-16, or UTF-32. When JSON + is written in UTF-8, JSON is 8bit compatible. When JSON is + written in UTF-16 or UTF-32, the binary content-transfer-encoding + must be used. + + Security considerations: + + Generally there are security issues with scripting languages. JSON + is a subset of JavaScript, but it is a safe subset that excludes + assignment and invocation. + + A JSON text can be safely passed into JavaScript's eval() function + (which compiles and executes a string) if all the characters not + enclosed in strings are in the set of characters that form JSON + tokens. This can be quickly determined in JavaScript with two + regular expressions and calls to the test and replace methods. + + var my_JSON_object = !(/[^,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]/.test( + text.replace(/"(\\.|[^"\\])*"/g, ''))) && + eval('(' + text + ')'); + + Interoperability considerations: n/a + + Published specification: RFC 4627 + + + + + + + + +Crockford Informational [Page 6] + +RFC 4627 JSON July 2006 + + + Applications that use this media type: + + JSON has been used to exchange data between applications written + in all of these programming languages: ActionScript, C, C#, + ColdFusion, Common Lisp, E, Erlang, Java, JavaScript, Lua, + Objective CAML, Perl, PHP, Python, Rebol, Ruby, and Scheme. + + Additional information: + + Magic number(s): n/a + File extension(s): .json + Macintosh file type code(s): TEXT + + Person & email address to contact for further information: + Douglas Crockford + douglas@crockford.com + + Intended usage: COMMON + + Restrictions on usage: none + + Author: + Douglas Crockford + douglas@crockford.com + + Change controller: + Douglas Crockford + douglas@crockford.com + +7. Security Considerations + + See Security Considerations in Section 6. + +8. Examples + + This is a JSON object: + + { + "Image": { + "Width": 800, + "Height": 600, + "Title": "View from 15th Floor", + "Thumbnail": { + "Url": "http://www.example.com/image/481989943", + "Height": 125, + "Width": "100" + }, + "IDs": [116, 943, 234, 38793] + + + +Crockford Informational [Page 7] + +RFC 4627 JSON July 2006 + + + } + } + + Its Image member is an object whose Thumbnail member is an object + and whose IDs member is an array of numbers. + + This is a JSON array containing two objects: + + [ + { + "precision": "zip", + "Latitude": 37.7668, + "Longitude": -122.3959, + "Address": "", + "City": "SAN FRANCISCO", + "State": "CA", + "Zip": "94107", + "Country": "US" + }, + { + "precision": "zip", + "Latitude": 37.371991, + "Longitude": -122.026020, + "Address": "", + "City": "SUNNYVALE", + "State": "CA", + "Zip": "94085", + "Country": "US" + } + ] + +9. References + +9.1. Normative References + + [ECMA] European Computer Manufacturers Association, "ECMAScript + Language Specification 3rd Edition", December 1999, + . + + [RFC0020] Cerf, V., "ASCII format for network interchange", RFC 20, + October 1969. + + [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate + Requirement Levels", BCP 14, RFC 2119, March 1997. + + [RFC4234] Crocker, D. and P. Overell, "Augmented BNF for Syntax + Specifications: ABNF", RFC 4234, October 2005. + + + +Crockford Informational [Page 8] + +RFC 4627 JSON July 2006 + + + [UNICODE] The Unicode Consortium, "The Unicode Standard Version 4.0", + 2003, . + +Author's Address + + Douglas Crockford + JSON.org + EMail: douglas@crockford.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Crockford Informational [Page 9] + +RFC 4627 JSON July 2006 + + +Full Copyright Statement + + Copyright (C) The Internet Society (2006). + + This document is subject to the rights, licenses and restrictions + contained in BCP 78, and except as set forth therein, the authors + retain all their rights. + + This document and the information contained herein are provided on an + "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS + OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET + ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, + INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE + INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED + WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + +Intellectual Property + + The IETF takes no position regarding the validity or scope of any + Intellectual Property Rights or other rights that might be claimed to + pertain to the implementation or use of the technology described in + this document or the extent to which any license under such rights + might or might not be available; nor does it represent that it has + made any independent effort to identify any such rights. Information + on the procedures with respect to rights in RFC documents can be + found in BCP 78 and BCP 79. + + Copies of IPR disclosures made to the IETF Secretariat and any + assurances of licenses to be made available, or the result of an + attempt made to obtain a general license or permission for the use of + such proprietary rights by implementers or users of this + specification can be obtained from the IETF on-line IPR repository at + http://www.ietf.org/ipr. + + The IETF invites any interested party to bring to its attention any + copyrights, patents or patent applications, or other proprietary + rights that may cover technology that may be required to implement + this standard. Please address the information to the IETF at + ietf-ipr@ietf.org. + +Acknowledgement + + Funding for the RFC Editor function is provided by the IETF + Administrative Support Activity (IASA). + + + + + + + +Crockford Informational [Page 10] + diff --git a/app/cjson/strbuf.c b/app/cjson/strbuf.c new file mode 100644 index 00000000..c9528fa9 --- /dev/null +++ b/app/cjson/strbuf.c @@ -0,0 +1,261 @@ +/* strbuf - String buffer routines + * + * Copyright (c) 2010-2012 Mark Pulford + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "c_stdio.h" +#include "c_stdlib.h" +#include "c_stdarg.h" +#include "c_string.h" + +#include "strbuf.h" + +// static void die(const char *fmt, ...) +// { +// va_list arg; + +// va_start(arg, fmt); +// vfprintf(stderr, fmt, arg); +// va_end(arg); +// fprintf(stderr, "\n"); + +// exit(-1); +// } + +void strbuf_init(strbuf_t *s, int len) +{ + int size; + + if (len <= 0) + size = STRBUF_DEFAULT_SIZE; + else + size = len + 1; /* \0 terminator */ + + s->buf = NULL; + s->size = size; + s->length = 0; + s->increment = STRBUF_DEFAULT_INCREMENT; + s->dynamic = 0; + s->reallocs = 0; + s->debug = 0; + + s->buf = c_malloc(size); + if (!s->buf){ + NODE_ERR("not enough memory\n"); + return; + } + + strbuf_ensure_null(s); +} + +strbuf_t *strbuf_new(int len) +{ + strbuf_t *s; + + s = c_malloc(sizeof(strbuf_t)); + if (!s){ + NODE_ERR("not enough memory\n"); + return; + } + + strbuf_init(s, len); + + /* Dynamic strbuf allocation / deallocation */ + s->dynamic = 1; + + return s; +} + +void strbuf_set_increment(strbuf_t *s, int increment) +{ + /* Increment > 0: Linear buffer growth rate + * Increment < -1: Exponential buffer growth rate */ + if (increment == 0 || increment == -1){ + NODE_ERR("BUG: Invalid string increment"); + return; + } + + s->increment = increment; +} + +static inline void debug_stats(strbuf_t *s) +{ + if (s->debug) { + NODE_ERR("strbuf(%lx) reallocs: %d, length: %d, size: %d\n", + (long)s, s->reallocs, s->length, s->size); + } +} + +/* If strbuf_t has not been dynamically allocated, strbuf_free() can + * be called any number of times strbuf_init() */ +void strbuf_free(strbuf_t *s) +{ + debug_stats(s); + + if (s->buf) { + c_free(s->buf); + s->buf = NULL; + } + if (s->dynamic) + c_free(s); +} + +char *strbuf_free_to_string(strbuf_t *s, int *len) +{ + char *buf; + + debug_stats(s); + + strbuf_ensure_null(s); + + buf = s->buf; + if (len) + *len = s->length; + + if (s->dynamic) + c_free(s); + + return buf; +} + +static int calculate_new_size(strbuf_t *s, int len) +{ + int reqsize, newsize; + + if (len <= 0){ + NODE_ERR("BUG: Invalid strbuf length requested"); + return 0; + } + + /* Ensure there is room for optional NULL termination */ + reqsize = len + 1; + + /* If the user has requested to shrink the buffer, do it exactly */ + if (s->size > reqsize) + return reqsize; + + newsize = s->size; + if (s->increment < 0) { + /* Exponential sizing */ + while (newsize < reqsize) + newsize *= -s->increment; + } else { + /* Linear sizing */ + newsize = ((newsize + s->increment - 1) / s->increment) * s->increment; + } + + return newsize; +} + + +/* Ensure strbuf can handle a string length bytes long (ignoring NULL + * optional termination). */ +void strbuf_resize(strbuf_t *s, int len) +{ + int newsize; + + newsize = calculate_new_size(s, len); + + if (s->debug > 1) { + NODE_ERR("strbuf(%lx) resize: %d => %d\n", + (long)s, s->size, newsize); + } + + s->size = newsize; + s->buf = (char *)c_realloc(s->buf, s->size); + if (!s->buf){ + NODE_ERR("not enough memory"); + return; + } + s->reallocs++; +} + +void strbuf_append_string(strbuf_t *s, const char *str) +{ + int space, i; + + space = strbuf_empty_length(s); + + for (i = 0; str[i]; i++) { + if (space < 1) { + strbuf_resize(s, s->length + 1); + space = strbuf_empty_length(s); + } + + s->buf[s->length] = str[i]; + s->length++; + space--; + } +} +#if 0 +/* strbuf_append_fmt() should only be used when an upper bound + * is known for the output string. */ +void strbuf_append_fmt(strbuf_t *s, int len, const char *fmt, ...) +{ + va_list arg; + int fmt_len; + + strbuf_ensure_empty_length(s, len); + + va_start(arg, fmt); + fmt_len = vsnprintf(s->buf + s->length, len, fmt, arg); + va_end(arg); + + if (fmt_len < 0) + die("BUG: Unable to convert number"); /* This should never happen.. */ + + s->length += fmt_len; +} + +/* strbuf_append_fmt_retry() can be used when the there is no known + * upper bound for the output string. */ +void strbuf_append_fmt_retry(strbuf_t *s, const char *fmt, ...) +{ + va_list arg; + int fmt_len, try; + int empty_len; + + /* If the first attempt to append fails, resize the buffer appropriately + * and try again */ + for (try = 0; ; try++) { + va_start(arg, fmt); + /* Append the new formatted string */ + /* fmt_len is the length of the string required, excluding the + * trailing NULL */ + empty_len = strbuf_empty_length(s); + /* Add 1 since there is also space to store the terminating NULL. */ + fmt_len = vsnprintf(s->buf + s->length, empty_len + 1, fmt, arg); + va_end(arg); + + if (fmt_len <= empty_len) + break; /* SUCCESS */ + if (try > 0) + die("BUG: length of formatted string changed"); + + strbuf_resize(s, s->length + fmt_len); + } + + s->length += fmt_len; +} +#endif +/* vi:ai et sw=4 ts=4: + */ diff --git a/app/cjson/strbuf.h b/app/cjson/strbuf.h new file mode 100644 index 00000000..e6044be6 --- /dev/null +++ b/app/cjson/strbuf.h @@ -0,0 +1,154 @@ +/* strbuf - String buffer routines + * + * Copyright (c) 2010-2012 Mark Pulford + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "c_stdlib.h" +#include "c_stdarg.h" + +/* Size: Total bytes allocated to *buf + * Length: String length, excluding optional NULL terminator. + * Increment: Allocation increments when resizing the string buffer. + * Dynamic: True if created via strbuf_new() + */ + +typedef struct { + char *buf; + int size; + int length; + int increment; + int dynamic; + int reallocs; + int debug; +} strbuf_t; + +#ifndef STRBUF_DEFAULT_SIZE +#define STRBUF_DEFAULT_SIZE 1023 +#endif +#ifndef STRBUF_DEFAULT_INCREMENT +#define STRBUF_DEFAULT_INCREMENT -2 +#endif + +/* Initialise */ +extern strbuf_t *strbuf_new(int len); +extern void strbuf_init(strbuf_t *s, int len); +extern void strbuf_set_increment(strbuf_t *s, int increment); + +/* Release */ +extern void strbuf_free(strbuf_t *s); +extern char *strbuf_free_to_string(strbuf_t *s, int *len); + +/* Management */ +extern void strbuf_resize(strbuf_t *s, int len); +static int strbuf_empty_length(strbuf_t *s); +static int strbuf_length(strbuf_t *s); +static char *strbuf_string(strbuf_t *s, int *len); +static void strbuf_ensure_empty_length(strbuf_t *s, int len); +static char *strbuf_empty_ptr(strbuf_t *s); +static void strbuf_extend_length(strbuf_t *s, int len); + +/* Update */ +extern void strbuf_append_fmt(strbuf_t *s, int len, const char *fmt, ...); +extern void strbuf_append_fmt_retry(strbuf_t *s, const char *format, ...); +static void strbuf_append_mem(strbuf_t *s, const char *c, int len); +extern void strbuf_append_string(strbuf_t *s, const char *str); +static void strbuf_append_char(strbuf_t *s, const char c); +static void strbuf_ensure_null(strbuf_t *s); + +/* Reset string for before use */ +static inline void strbuf_reset(strbuf_t *s) +{ + s->length = 0; +} + +static inline int strbuf_allocated(strbuf_t *s) +{ + return s->buf != NULL; +} + +/* Return bytes remaining in the string buffer + * Ensure there is space for a NULL terminator. */ +static inline int strbuf_empty_length(strbuf_t *s) +{ + return s->size - s->length - 1; +} + +static inline void strbuf_ensure_empty_length(strbuf_t *s, int len) +{ + if (len > strbuf_empty_length(s)) + strbuf_resize(s, s->length + len); +} + +static inline char *strbuf_empty_ptr(strbuf_t *s) +{ + return s->buf + s->length; +} + +static inline void strbuf_extend_length(strbuf_t *s, int len) +{ + s->length += len; +} + +static inline int strbuf_length(strbuf_t *s) +{ + return s->length; +} + +static inline void strbuf_append_char(strbuf_t *s, const char c) +{ + strbuf_ensure_empty_length(s, 1); + s->buf[s->length++] = c; +} + +static inline void strbuf_append_char_unsafe(strbuf_t *s, const char c) +{ + s->buf[s->length++] = c; +} + +static inline void strbuf_append_mem(strbuf_t *s, const char *c, int len) +{ + strbuf_ensure_empty_length(s, len); + memcpy(s->buf + s->length, c, len); + s->length += len; +} + +static inline void strbuf_append_mem_unsafe(strbuf_t *s, const char *c, int len) +{ + memcpy(s->buf + s->length, c, len); + s->length += len; +} + +static inline void strbuf_ensure_null(strbuf_t *s) +{ + s->buf[s->length] = 0; +} + +static inline char *strbuf_string(strbuf_t *s, int *len) +{ + if (len) + *len = s->length; + + return s->buf; +} + +/* vi:ai et sw=4 ts=4: + */ diff --git a/app/cjson/tests/README b/app/cjson/tests/README new file mode 100644 index 00000000..39e8bd45 --- /dev/null +++ b/app/cjson/tests/README @@ -0,0 +1,4 @@ +These JSON examples were taken from the JSON website +(http://json.org/example.html) and RFC 4627. + +Used with permission. diff --git a/app/cjson/tests/bench.lua b/app/cjson/tests/bench.lua new file mode 100644 index 00000000..648020b1 --- /dev/null +++ b/app/cjson/tests/bench.lua @@ -0,0 +1,131 @@ +#!/usr/bin/env lua + +-- This benchmark script measures wall clock time and should be +-- run on an unloaded system. +-- +-- Your Mileage May Vary. +-- +-- Mark Pulford + +local json_module = os.getenv("JSON_MODULE") or "cjson" + +require "socket" +local json = require(json_module) +local util = require "cjson.util" + +local function find_func(mod, funcnames) + for _, v in ipairs(funcnames) do + if mod[v] then + return mod[v] + end + end + + return nil +end + +local json_encode = find_func(json, { "encode", "Encode", "to_string", "stringify", "json" }) +local json_decode = find_func(json, { "decode", "Decode", "to_value", "parse" }) + +local function average(t) + local total = 0 + for _, v in ipairs(t) do + total = total + v + end + return total / #t +end + +function benchmark(tests, seconds, rep) + local function bench(func, iter) + -- Use socket.gettime() to measure microsecond resolution + -- wall clock time. + local t = socket.gettime() + for i = 1, iter do + func(i) + end + t = socket.gettime() - t + + -- Don't trust any results when the run lasted for less than a + -- millisecond - return nil. + if t < 0.001 then + return nil + end + + return (iter / t) + end + + -- Roughly calculate the number of interations required + -- to obtain a particular time period. + local function calc_iter(func, seconds) + local iter = 1 + local rate + -- Warm up the bench function first. + func() + while not rate do + rate = bench(func, iter) + iter = iter * 10 + end + return math.ceil(seconds * rate) + end + + local test_results = {} + for name, func in pairs(tests) do + -- k(number), v(string) + -- k(string), v(function) + -- k(number), v(function) + if type(func) == "string" then + name = func + func = _G[name] + end + + local iter = calc_iter(func, seconds) + + local result = {} + for i = 1, rep do + result[i] = bench(func, iter) + end + + -- Remove the slowest half (round down) of the result set + table.sort(result) + for i = 1, math.floor(#result / 2) do + table.remove(result, 1) + end + + test_results[name] = average(result) + end + + return test_results +end + +function bench_file(filename) + local data_json = util.file_load(filename) + local data_obj = json_decode(data_json) + + local function test_encode() + json_encode(data_obj) + end + local function test_decode() + json_decode(data_json) + end + + local tests = {} + if json_encode then tests.encode = test_encode end + if json_decode then tests.decode = test_decode end + + return benchmark(tests, 0.1, 5) +end + +-- Optionally load any custom configuration required for this module +local success, data = pcall(util.file_load, ("bench-%s.lua"):format(json_module)) +if success then + util.run_script(data, _G) + configure(json) +end + +for i = 1, #arg do + local results = bench_file(arg[i]) + for k, v in pairs(results) do + print(("%s\t%s\t%d"):format(arg[i], k, v)) + end +end + +-- vi:ai et sw=4 ts=4: diff --git a/app/cjson/tests/example1.json b/app/cjson/tests/example1.json new file mode 100644 index 00000000..42486cec --- /dev/null +++ b/app/cjson/tests/example1.json @@ -0,0 +1,22 @@ +{ + "glossary": { + "title": "example glossary", + "GlossDiv": { + "title": "S", + "GlossList": { + "GlossEntry": { + "ID": "SGML", + "SortAs": "SGML", + "GlossTerm": "Standard Generalized Mark up Language", + "Acronym": "SGML", + "Abbrev": "ISO 8879:1986", + "GlossDef": { + "para": "A meta-markup language, used to create markup languages such as DocBook.", + "GlossSeeAlso": ["GML", "XML"] + }, + "GlossSee": "markup" + } + } + } + } +} diff --git a/app/cjson/tests/example2.json b/app/cjson/tests/example2.json new file mode 100644 index 00000000..5600991a --- /dev/null +++ b/app/cjson/tests/example2.json @@ -0,0 +1,11 @@ +{"menu": { + "id": "file", + "value": "File", + "popup": { + "menuitem": [ + {"value": "New", "onclick": "CreateNewDoc()"}, + {"value": "Open", "onclick": "OpenDoc()"}, + {"value": "Close", "onclick": "CloseDoc()"} + ] + } +}} diff --git a/app/cjson/tests/example3.json b/app/cjson/tests/example3.json new file mode 100644 index 00000000..d7237a5a --- /dev/null +++ b/app/cjson/tests/example3.json @@ -0,0 +1,26 @@ +{"widget": { + "debug": "on", + "window": { + "title": "Sample Konfabulator Widget", + "name": "main_window", + "width": 500, + "height": 500 + }, + "image": { + "src": "Images/Sun.png", + "name": "sun1", + "hOffset": 250, + "vOffset": 250, + "alignment": "center" + }, + "text": { + "data": "Click Here", + "size": 36, + "style": "bold", + "name": "text1", + "hOffset": 250, + "vOffset": 100, + "alignment": "center", + "onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;" + } +}} diff --git a/app/cjson/tests/example4.json b/app/cjson/tests/example4.json new file mode 100644 index 00000000..d31a395b --- /dev/null +++ b/app/cjson/tests/example4.json @@ -0,0 +1,88 @@ +{"web-app": { + "servlet": [ + { + "servlet-name": "cofaxCDS", + "servlet-class": "org.cofax.cds.CDSServlet", + "init-param": { + "configGlossary:installationAt": "Philadelphia, PA", + "configGlossary:adminEmail": "ksm@pobox.com", + "configGlossary:poweredBy": "Cofax", + "configGlossary:poweredByIcon": "/images/cofax.gif", + "configGlossary:staticPath": "/content/static", + "templateProcessorClass": "org.cofax.WysiwygTemplate", + "templateLoaderClass": "org.cofax.FilesTemplateLoader", + "templatePath": "templates", + "templateOverridePath": "", + "defaultListTemplate": "listTemplate.htm", + "defaultFileTemplate": "articleTemplate.htm", + "useJSP": false, + "jspListTemplate": "listTemplate.jsp", + "jspFileTemplate": "articleTemplate.jsp", + "cachePackageTagsTrack": 200, + "cachePackageTagsStore": 200, + "cachePackageTagsRefresh": 60, + "cacheTemplatesTrack": 100, + "cacheTemplatesStore": 50, + "cacheTemplatesRefresh": 15, + "cachePagesTrack": 200, + "cachePagesStore": 100, + "cachePagesRefresh": 10, + "cachePagesDirtyRead": 10, + "searchEngineListTemplate": "forSearchEnginesList.htm", + "searchEngineFileTemplate": "forSearchEngines.htm", + "searchEngineRobotsDb": "WEB-INF/robots.db", + "useDataStore": true, + "dataStoreClass": "org.cofax.SqlDataStore", + "redirectionClass": "org.cofax.SqlRedirection", + "dataStoreName": "cofax", + "dataStoreDriver": "com.microsoft.jdbc.sqlserver.SQLServerDriver", + "dataStoreUrl": "jdbc:microsoft:sqlserver://LOCALHOST:1433;DatabaseName=goon", + "dataStoreUser": "sa", + "dataStorePassword": "dataStoreTestQuery", + "dataStoreTestQuery": "SET NOCOUNT ON;select test='test';", + "dataStoreLogFile": "/usr/local/tomcat/logs/datastore.log", + "dataStoreInitConns": 10, + "dataStoreMaxConns": 100, + "dataStoreConnUsageLimit": 100, + "dataStoreLogLevel": "debug", + "maxUrlLength": 500}}, + { + "servlet-name": "cofaxEmail", + "servlet-class": "org.cofax.cds.EmailServlet", + "init-param": { + "mailHost": "mail1", + "mailHostOverride": "mail2"}}, + { + "servlet-name": "cofaxAdmin", + "servlet-class": "org.cofax.cds.AdminServlet"}, + + { + "servlet-name": "fileServlet", + "servlet-class": "org.cofax.cds.FileServlet"}, + { + "servlet-name": "cofaxTools", + "servlet-class": "org.cofax.cms.CofaxToolsServlet", + "init-param": { + "templatePath": "toolstemplates/", + "log": 1, + "logLocation": "/usr/local/tomcat/logs/CofaxTools.log", + "logMaxSize": "", + "dataLog": 1, + "dataLogLocation": "/usr/local/tomcat/logs/dataLog.log", + "dataLogMaxSize": "", + "removePageCache": "/content/admin/remove?cache=pages&id=", + "removeTemplateCache": "/content/admin/remove?cache=templates&id=", + "fileTransferFolder": "/usr/local/tomcat/webapps/content/fileTransferFolder", + "lookInContext": 1, + "adminGroupID": 4, + "betaServer": true}}], + "servlet-mapping": { + "cofaxCDS": "/", + "cofaxEmail": "/cofaxutil/aemail/*", + "cofaxAdmin": "/admin/*", + "fileServlet": "/static/*", + "cofaxTools": "/tools/*"}, + + "taglib": { + "taglib-uri": "cofax.tld", + "taglib-location": "/WEB-INF/tlds/cofax.tld"}}} diff --git a/app/cjson/tests/example5.json b/app/cjson/tests/example5.json new file mode 100644 index 00000000..49980ca2 --- /dev/null +++ b/app/cjson/tests/example5.json @@ -0,0 +1,27 @@ +{"menu": { + "header": "SVG Viewer", + "items": [ + {"id": "Open"}, + {"id": "OpenNew", "label": "Open New"}, + null, + {"id": "ZoomIn", "label": "Zoom In"}, + {"id": "ZoomOut", "label": "Zoom Out"}, + {"id": "OriginalView", "label": "Original View"}, + null, + {"id": "Quality"}, + {"id": "Pause"}, + {"id": "Mute"}, + null, + {"id": "Find", "label": "Find..."}, + {"id": "FindAgain", "label": "Find Again"}, + {"id": "Copy"}, + {"id": "CopyAgain", "label": "Copy Again"}, + {"id": "CopySVG", "label": "Copy SVG"}, + {"id": "ViewSVG", "label": "View SVG"}, + {"id": "ViewSource", "label": "View Source"}, + {"id": "SaveAs", "label": "Save As"}, + null, + {"id": "Help"}, + {"id": "About", "label": "About Adobe CVG Viewer..."} + ] +}} diff --git a/app/cjson/tests/genutf8.pl b/app/cjson/tests/genutf8.pl new file mode 100644 index 00000000..db661a19 --- /dev/null +++ b/app/cjson/tests/genutf8.pl @@ -0,0 +1,23 @@ +#!/usr/bin/env perl + +# Create test comparison data using a different UTF-8 implementation. + +# The generated utf8.dat file must have the following MD5 sum: +# cff03b039d850f370a7362f3313e5268 + +use strict; + +# 0xD800 - 0xDFFF are used to encode supplementary codepoints +# 0x10000 - 0x10FFFF are supplementary codepoints +my (@codepoints) = (0 .. 0xD7FF, 0xE000 .. 0x10FFFF); + +my $utf8 = pack("U*", @codepoints); +defined($utf8) or die "Unable create UTF-8 string\n"; + +open(FH, ">:utf8", "utf8.dat") + or die "Unable to open utf8.dat: $!\n"; +print FH $utf8 + or die "Unable to write utf8.dat\n"; +close(FH); + +# vi:ai et sw=4 ts=4: diff --git a/app/cjson/tests/numbers.json b/app/cjson/tests/numbers.json new file mode 100644 index 00000000..4f981ff2 --- /dev/null +++ b/app/cjson/tests/numbers.json @@ -0,0 +1,7 @@ +[ 0.110001, + 0.12345678910111, + 0.412454033640, + 2.6651441426902, + 2.718281828459, + 3.1415926535898, + 2.1406926327793 ] diff --git a/app/cjson/tests/octets-escaped.dat b/app/cjson/tests/octets-escaped.dat new file mode 100644 index 00000000..ee99a6bf --- /dev/null +++ b/app/cjson/tests/octets-escaped.dat @@ -0,0 +1 @@ +"\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\n\u000b\f\r\u000e\u000f\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f !\"#$%&'()*+,-.\/0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u007f" \ No newline at end of file diff --git a/app/cjson/tests/rfc-example1.json b/app/cjson/tests/rfc-example1.json new file mode 100644 index 00000000..73532fa9 --- /dev/null +++ b/app/cjson/tests/rfc-example1.json @@ -0,0 +1,13 @@ +{ + "Image": { + "Width": 800, + "Height": 600, + "Title": "View from 15th Floor", + "Thumbnail": { + "Url": "http://www.example.com/image/481989943", + "Height": 125, + "Width": "100" + }, + "IDs": [116, 943, 234, 38793] + } +} diff --git a/app/cjson/tests/rfc-example2.json b/app/cjson/tests/rfc-example2.json new file mode 100644 index 00000000..2a0cb681 --- /dev/null +++ b/app/cjson/tests/rfc-example2.json @@ -0,0 +1,22 @@ +[ + { + "precision": "zip", + "Latitude": 37.7668, + "Longitude": -122.3959, + "Address": "", + "City": "SAN FRANCISCO", + "State": "CA", + "Zip": "94107", + "Country": "US" + }, + { + "precision": "zip", + "Latitude": 37.371991, + "Longitude": -122.026020, + "Address": "", + "City": "SUNNYVALE", + "State": "CA", + "Zip": "94085", + "Country": "US" + } +] diff --git a/app/cjson/tests/test.lua b/app/cjson/tests/test.lua new file mode 100644 index 00000000..b8fce84c --- /dev/null +++ b/app/cjson/tests/test.lua @@ -0,0 +1,425 @@ +#!/usr/bin/env lua + +-- Lua CJSON tests +-- +-- Mark Pulford +-- +-- Note: The output of this script is easier to read with "less -S" + +local json = require "cjson" +local json_safe = require "cjson.safe" +local util = require "cjson.util" + +local function gen_raw_octets() + local chars = {} + for i = 0, 255 do chars[i + 1] = string.char(i) end + return table.concat(chars) +end + +-- Generate every UTF-16 codepoint, including supplementary codes +local function gen_utf16_escaped() + -- Create raw table escapes + local utf16_escaped = {} + local count = 0 + + local function append_escape(code) + local esc = ('\\u%04X'):format(code) + table.insert(utf16_escaped, esc) + end + + table.insert(utf16_escaped, '"') + for i = 0, 0xD7FF do + append_escape(i) + end + -- Skip 0xD800 - 0xDFFF since they are used to encode supplementary + -- codepoints + for i = 0xE000, 0xFFFF do + append_escape(i) + end + -- Append surrogate pair for each supplementary codepoint + for high = 0xD800, 0xDBFF do + for low = 0xDC00, 0xDFFF do + append_escape(high) + append_escape(low) + end + end + table.insert(utf16_escaped, '"') + + return table.concat(utf16_escaped) +end + +function load_testdata() + local data = {} + + -- Data for 8bit raw <-> escaped octets tests + data.octets_raw = gen_raw_octets() + data.octets_escaped = util.file_load("octets-escaped.dat") + + -- Data for \uXXXX -> UTF-8 test + data.utf16_escaped = gen_utf16_escaped() + + -- Load matching data for utf16_escaped + local utf8_loaded + utf8_loaded, data.utf8_raw = pcall(util.file_load, "utf8.dat") + if not utf8_loaded then + data.utf8_raw = "Failed to load utf8.dat - please run genutf8.pl" + end + + data.table_cycle = {} + data.table_cycle[1] = data.table_cycle + + local big = {} + for i = 1, 1100 do + big = { { 10, false, true, json.null }, "string", a = big } + end + data.deeply_nested_data = big + + return data +end + +function test_decode_cycle(filename) + local obj1 = json.decode(util.file_load(filename)) + local obj2 = json.decode(json.encode(obj1)) + return util.compare_values(obj1, obj2) +end + +-- Set up data used in tests +local Inf = math.huge; +local NaN = math.huge * 0; + +local testdata = load_testdata() + +local cjson_tests = { + -- Test API variables + { "Check module name, version", + function () return json._NAME, json._VERSION end, { }, + true, { "cjson", "2.1devel" } }, + + -- Test decoding simple types + { "Decode string", + json.decode, { '"test string"' }, true, { "test string" } }, + { "Decode numbers", + json.decode, { '[ 0.0, -5e3, -1, 0.3e-3, 1023.2, 0e10 ]' }, + true, { { 0.0, -5000, -1, 0.0003, 1023.2, 0 } } }, + { "Decode null", + json.decode, { 'null' }, true, { json.null } }, + { "Decode true", + json.decode, { 'true' }, true, { true } }, + { "Decode false", + json.decode, { 'false' }, true, { false } }, + { "Decode object with numeric keys", + json.decode, { '{ "1": "one", "3": "three" }' }, + true, { { ["1"] = "one", ["3"] = "three" } } }, + { "Decode object with string keys", + json.decode, { '{ "a": "a", "b": "b" }' }, + true, { { a = "a", b = "b" } } }, + { "Decode array", + json.decode, { '[ "one", null, "three" ]' }, + true, { { "one", json.null, "three" } } }, + + -- Test decoding errors + { "Decode UTF-16BE [throw error]", + json.decode, { '\0"\0"' }, + false, { "JSON parser does not support UTF-16 or UTF-32" } }, + { "Decode UTF-16LE [throw error]", + json.decode, { '"\0"\0' }, + false, { "JSON parser does not support UTF-16 or UTF-32" } }, + { "Decode UTF-32BE [throw error]", + json.decode, { '\0\0\0"' }, + false, { "JSON parser does not support UTF-16 or UTF-32" } }, + { "Decode UTF-32LE [throw error]", + json.decode, { '"\0\0\0' }, + false, { "JSON parser does not support UTF-16 or UTF-32" } }, + { "Decode partial JSON [throw error]", + json.decode, { '{ "unexpected eof": ' }, + false, { "Expected value but found T_END at character 21" } }, + { "Decode with extra comma [throw error]", + json.decode, { '{ "extra data": true }, false' }, + false, { "Expected the end but found T_COMMA at character 23" } }, + { "Decode invalid escape code [throw error]", + json.decode, { [[ { "bad escape \q code" } ]] }, + false, { "Expected object key string but found invalid escape code at character 16" } }, + { "Decode invalid unicode escape [throw error]", + json.decode, { [[ { "bad unicode \u0f6 escape" } ]] }, + false, { "Expected object key string but found invalid unicode escape code at character 17" } }, + { "Decode invalid keyword [throw error]", + json.decode, { ' [ "bad barewood", test ] ' }, + false, { "Expected value but found invalid token at character 20" } }, + { "Decode invalid number #1 [throw error]", + json.decode, { '[ -+12 ]' }, + false, { "Expected value but found invalid number at character 3" } }, + { "Decode invalid number #2 [throw error]", + json.decode, { '-v' }, + false, { "Expected value but found invalid number at character 1" } }, + { "Decode invalid number exponent [throw error]", + json.decode, { '[ 0.4eg10 ]' }, + false, { "Expected comma or array end but found invalid token at character 6" } }, + + -- Test decoding nested arrays / objects + { "Set decode_max_depth(5)", + json.decode_max_depth, { 5 }, true, { 5 } }, + { "Decode array at nested limit", + json.decode, { '[[[[[ "nested" ]]]]]' }, + true, { {{{{{ "nested" }}}}} } }, + { "Decode array over nested limit [throw error]", + json.decode, { '[[[[[[ "nested" ]]]]]]' }, + false, { "Found too many nested data structures (6) at character 6" } }, + { "Decode object at nested limit", + json.decode, { '{"a":{"b":{"c":{"d":{"e":"nested"}}}}}' }, + true, { {a={b={c={d={e="nested"}}}}} } }, + { "Decode object over nested limit [throw error]", + json.decode, { '{"a":{"b":{"c":{"d":{"e":{"f":"nested"}}}}}}' }, + false, { "Found too many nested data structures (6) at character 26" } }, + { "Set decode_max_depth(1000)", + json.decode_max_depth, { 1000 }, true, { 1000 } }, + { "Decode deeply nested array [throw error]", + json.decode, { string.rep("[", 1100) .. '1100' .. string.rep("]", 1100)}, + false, { "Found too many nested data structures (1001) at character 1001" } }, + + -- Test encoding nested tables + { "Set encode_max_depth(5)", + json.encode_max_depth, { 5 }, true, { 5 } }, + { "Encode nested table as array at nested limit", + json.encode, { {{{{{"nested"}}}}} }, true, { '[[[[["nested"]]]]]' } }, + { "Encode nested table as array after nested limit [throw error]", + json.encode, { { {{{{{"nested"}}}}} } }, + false, { "Cannot serialise, excessive nesting (6)" } }, + { "Encode nested table as object at nested limit", + json.encode, { {a={b={c={d={e="nested"}}}}} }, + true, { '{"a":{"b":{"c":{"d":{"e":"nested"}}}}}' } }, + { "Encode nested table as object over nested limit [throw error]", + json.encode, { {a={b={c={d={e={f="nested"}}}}}} }, + false, { "Cannot serialise, excessive nesting (6)" } }, + { "Encode table with cycle [throw error]", + json.encode, { testdata.table_cycle }, + false, { "Cannot serialise, excessive nesting (6)" } }, + { "Set encode_max_depth(1000)", + json.encode_max_depth, { 1000 }, true, { 1000 } }, + { "Encode deeply nested data [throw error]", + json.encode, { testdata.deeply_nested_data }, + false, { "Cannot serialise, excessive nesting (1001)" } }, + + -- Test encoding simple types + { "Encode null", + json.encode, { json.null }, true, { 'null' } }, + { "Encode true", + json.encode, { true }, true, { 'true' } }, + { "Encode false", + json.encode, { false }, true, { 'false' } }, + { "Encode empty object", + json.encode, { { } }, true, { '{}' } }, + { "Encode integer", + json.encode, { 10 }, true, { '10' } }, + { "Encode string", + json.encode, { "hello" }, true, { '"hello"' } }, + { "Encode Lua function [throw error]", + json.encode, { function () end }, + false, { "Cannot serialise function: type not supported" } }, + + -- Test decoding invalid numbers + { "Set decode_invalid_numbers(true)", + json.decode_invalid_numbers, { true }, true, { true } }, + { "Decode hexadecimal", + json.decode, { '0x6.ffp1' }, true, { 13.9921875 } }, + { "Decode numbers with leading zero", + json.decode, { '[ 0123, 00.33 ]' }, true, { { 123, 0.33 } } }, + { "Decode +-Inf", + json.decode, { '[ +Inf, Inf, -Inf ]' }, true, { { Inf, Inf, -Inf } } }, + { "Decode +-Infinity", + json.decode, { '[ +Infinity, Infinity, -Infinity ]' }, + true, { { Inf, Inf, -Inf } } }, + { "Decode +-NaN", + json.decode, { '[ +NaN, NaN, -NaN ]' }, true, { { NaN, NaN, NaN } } }, + { "Decode Infrared (not infinity) [throw error]", + json.decode, { 'Infrared' }, + false, { "Expected the end but found invalid token at character 4" } }, + { "Decode Noodle (not NaN) [throw error]", + json.decode, { 'Noodle' }, + false, { "Expected value but found invalid token at character 1" } }, + { "Set decode_invalid_numbers(false)", + json.decode_invalid_numbers, { false }, true, { false } }, + { "Decode hexadecimal [throw error]", + json.decode, { '0x6' }, + false, { "Expected value but found invalid number at character 1" } }, + { "Decode numbers with leading zero [throw error]", + json.decode, { '[ 0123, 00.33 ]' }, + false, { "Expected value but found invalid number at character 3" } }, + { "Decode +-Inf [throw error]", + json.decode, { '[ +Inf, Inf, -Inf ]' }, + false, { "Expected value but found invalid token at character 3" } }, + { "Decode +-Infinity [throw error]", + json.decode, { '[ +Infinity, Infinity, -Infinity ]' }, + false, { "Expected value but found invalid token at character 3" } }, + { "Decode +-NaN [throw error]", + json.decode, { '[ +NaN, NaN, -NaN ]' }, + false, { "Expected value but found invalid token at character 3" } }, + { 'Set decode_invalid_numbers("on")', + json.decode_invalid_numbers, { "on" }, true, { true } }, + + -- Test encoding invalid numbers + { "Set encode_invalid_numbers(false)", + json.encode_invalid_numbers, { false }, true, { false } }, + { "Encode NaN [throw error]", + json.encode, { NaN }, + false, { "Cannot serialise number: must not be NaN or Infinity" } }, + { "Encode Infinity [throw error]", + json.encode, { Inf }, + false, { "Cannot serialise number: must not be NaN or Infinity" } }, + { "Set encode_invalid_numbers(\"null\")", + json.encode_invalid_numbers, { "null" }, true, { "null" } }, + { "Encode NaN as null", + json.encode, { NaN }, true, { "null" } }, + { "Encode Infinity as null", + json.encode, { Inf }, true, { "null" } }, + { "Set encode_invalid_numbers(true)", + json.encode_invalid_numbers, { true }, true, { true } }, + { "Encode NaN", + json.encode, { NaN }, true, { "NaN" } }, + { "Encode +Infinity", + json.encode, { Inf }, true, { "Infinity" } }, + { "Encode -Infinity", + json.encode, { -Inf }, true, { "-Infinity" } }, + { 'Set encode_invalid_numbers("off")', + json.encode_invalid_numbers, { "off" }, true, { false } }, + + -- Test encoding tables + { "Set encode_sparse_array(true, 2, 3)", + json.encode_sparse_array, { true, 2, 3 }, true, { true, 2, 3 } }, + { "Encode sparse table as array #1", + json.encode, { { [3] = "sparse test" } }, + true, { '[null,null,"sparse test"]' } }, + { "Encode sparse table as array #2", + json.encode, { { [1] = "one", [4] = "sparse test" } }, + true, { '["one",null,null,"sparse test"]' } }, + { "Encode sparse array as object", + json.encode, { { [1] = "one", [5] = "sparse test" } }, + true, { '{"1":"one","5":"sparse test"}' } }, + { "Encode table with numeric string key as object", + json.encode, { { ["2"] = "numeric string key test" } }, + true, { '{"2":"numeric string key test"}' } }, + { "Set encode_sparse_array(false)", + json.encode_sparse_array, { false }, true, { false, 2, 3 } }, + { "Encode table with incompatible key [throw error]", + json.encode, { { [false] = "wrong" } }, + false, { "Cannot serialise boolean: table key must be a number or string" } }, + + -- Test escaping + { "Encode all octets (8-bit clean)", + json.encode, { testdata.octets_raw }, true, { testdata.octets_escaped } }, + { "Decode all escaped octets", + json.decode, { testdata.octets_escaped }, true, { testdata.octets_raw } }, + { "Decode single UTF-16 escape", + json.decode, { [["\uF800"]] }, true, { "\239\160\128" } }, + { "Decode all UTF-16 escapes (including surrogate combinations)", + json.decode, { testdata.utf16_escaped }, true, { testdata.utf8_raw } }, + { "Decode swapped surrogate pair [throw error]", + json.decode, { [["\uDC00\uD800"]] }, + false, { "Expected value but found invalid unicode escape code at character 2" } }, + { "Decode duplicate high surrogate [throw error]", + json.decode, { [["\uDB00\uDB00"]] }, + false, { "Expected value but found invalid unicode escape code at character 2" } }, + { "Decode duplicate low surrogate [throw error]", + json.decode, { [["\uDB00\uDB00"]] }, + false, { "Expected value but found invalid unicode escape code at character 2" } }, + { "Decode missing low surrogate [throw error]", + json.decode, { [["\uDB00"]] }, + false, { "Expected value but found invalid unicode escape code at character 2" } }, + { "Decode invalid low surrogate [throw error]", + json.decode, { [["\uDB00\uD"]] }, + false, { "Expected value but found invalid unicode escape code at character 2" } }, + + -- Test locale support + -- + -- The standard Lua interpreter is ANSI C online doesn't support locales + -- by default. Force a known problematic locale to test strtod()/sprintf(). + { "Set locale to cs_CZ (comma separator)", function () + os.setlocale("cs_CZ") + json.new() + end }, + { "Encode number under comma locale", + json.encode, { 1.5 }, true, { '1.5' } }, + { "Decode number in array under comma locale", + json.decode, { '[ 10, "test" ]' }, true, { { 10, "test" } } }, + { "Revert locale to POSIX", function () + os.setlocale("C") + json.new() + end }, + + -- Test encode_keep_buffer() and enable_number_precision() + { "Set encode_keep_buffer(false)", + json.encode_keep_buffer, { false }, true, { false } }, + { "Set encode_number_precision(3)", + json.encode_number_precision, { 3 }, true, { 3 } }, + { "Encode number with precision 3", + json.encode, { 1/3 }, true, { "0.333" } }, + { "Set encode_number_precision(14)", + json.encode_number_precision, { 14 }, true, { 14 } }, + { "Set encode_keep_buffer(true)", + json.encode_keep_buffer, { true }, true, { true } }, + + -- Test config API errors + -- Function is listed as '?' due to pcall + { "Set encode_number_precision(0) [throw error]", + json.encode_number_precision, { 0 }, + false, { "bad argument #1 to '?' (expected integer between 1 and 14)" } }, + { "Set encode_number_precision(\"five\") [throw error]", + json.encode_number_precision, { "five" }, + false, { "bad argument #1 to '?' (number expected, got string)" } }, + { "Set encode_keep_buffer(nil, true) [throw error]", + json.encode_keep_buffer, { nil, true }, + false, { "bad argument #2 to '?' (found too many arguments)" } }, + { "Set encode_max_depth(\"wrong\") [throw error]", + json.encode_max_depth, { "wrong" }, + false, { "bad argument #1 to '?' (number expected, got string)" } }, + { "Set decode_max_depth(0) [throw error]", + json.decode_max_depth, { "0" }, + false, { "bad argument #1 to '?' (expected integer between 1 and 2147483647)" } }, + { "Set encode_invalid_numbers(-2) [throw error]", + json.encode_invalid_numbers, { -2 }, + false, { "bad argument #1 to '?' (invalid option '-2')" } }, + { "Set decode_invalid_numbers(true, false) [throw error]", + json.decode_invalid_numbers, { true, false }, + false, { "bad argument #2 to '?' (found too many arguments)" } }, + { "Set encode_sparse_array(\"not quite on\") [throw error]", + json.encode_sparse_array, { "not quite on" }, + false, { "bad argument #1 to '?' (invalid option 'not quite on')" } }, + + { "Reset Lua CJSON configuration", function () json = json.new() end }, + -- Wrap in a function to ensure the table returned by json.new() is used + { "Check encode_sparse_array()", + function (...) return json.encode_sparse_array(...) end, { }, + true, { false, 2, 10 } }, + + { "Encode (safe) simple value", + json_safe.encode, { true }, + true, { "true" } }, + { "Encode (safe) argument validation [throw error]", + json_safe.encode, { "arg1", "arg2" }, + false, { "bad argument #1 to '?' (expected 1 argument)" } }, + { "Decode (safe) error generation", + json_safe.decode, { "Oops" }, + true, { nil, "Expected value but found invalid token at character 1" } }, + { "Decode (safe) error generation after new()", + function(...) return json_safe.new().decode(...) end, { "Oops" }, + true, { nil, "Expected value but found invalid token at character 1" } }, +} + +print(("==> Testing Lua CJSON version %s\n"):format(json._VERSION)) + +util.run_test_group(cjson_tests) + +for _, filename in ipairs(arg) do + util.run_test("Decode cycle " .. filename, test_decode_cycle, { filename }, + true, { true }) +end + +local pass, total = util.run_test_summary() + +if pass == total then + print("==> Summary: all tests succeeded") +else + print(("==> Summary: %d/%d tests failed"):format(total - pass, total)) + os.exit(1) +end + +-- vi:ai et sw=4 ts=4: diff --git a/app/cjson/tests/types.json b/app/cjson/tests/types.json new file mode 100644 index 00000000..c01e7d20 --- /dev/null +++ b/app/cjson/tests/types.json @@ -0,0 +1 @@ +{ "array": [ 10, true, null ] } From 0144e40cf004f51dac69fe1c436a945584e71f8c Mon Sep 17 00:00:00 2001 From: AllAboutEE Date: Mon, 16 Mar 2015 00:39:29 -0500 Subject: [PATCH 25/40] Added comments and links to learn more about SMTP. --- lua_examples/email/send_email_smtp.lua | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lua_examples/email/send_email_smtp.lua b/lua_examples/email/send_email_smtp.lua index 1c79b6e5..597a5f87 100644 --- a/lua_examples/email/send_email_smtp.lua +++ b/lua_examples/email/send_email_smtp.lua @@ -1,3 +1,13 @@ +--- +-- @description a basic SMTP email example. You must use an account which can provide unencrypted authenticated access. +-- This example was tested with an AOL and Time Warner email accounts. GMail does not offer unecrypted authenticated access. +-- To obtain your email's SMTP server and port simply Google it e.g. [my email domain] SMTP settings +-- For example for timewarner you'll get to this page http://www.timewarnercable.com/en/support/faqs/faqs-internet/e-mailacco/incoming-outgoing-server-addresses.html +-- To Learn more about SMTP email visit: +-- SMTP Commands Reference - http://www.samlogic.net/articles/smtp-commands-reference.htm +-- See "SMTP transport example" in this page http://en.wikipedia.org/wiki/Simple_Mail_Transfer_Protocol +-- @author Miguel + require("base64") -- The email and password from the account you want to send emails from From d425dd7127df6d8b69def64546f5e8cecdca1240 Mon Sep 17 00:00:00 2001 From: funshine Date: Mon, 16 Mar 2015 17:09:43 +0800 Subject: [PATCH 26/40] under develop --- app/cjson/{fpconv.c => fpconv.c.bak} | 2 +- app/libc/c_string.h | 2 +- app/modules/cjson.c | 32 ++++++++++++++++++++++------ 3 files changed, 27 insertions(+), 9 deletions(-) rename app/cjson/{fpconv.c => fpconv.c.bak} (99%) diff --git a/app/cjson/fpconv.c b/app/cjson/fpconv.c.bak similarity index 99% rename from app/cjson/fpconv.c rename to app/cjson/fpconv.c.bak index b67e2842..a5ce8125 100644 --- a/app/cjson/fpconv.c +++ b/app/cjson/fpconv.c.bak @@ -30,7 +30,7 @@ #include "c_stdio.h" #include "c_stdlib.h" -#include +// #include #include "c_string.h" #include "fpconv.h" diff --git a/app/libc/c_string.h b/app/libc/c_string.h index 599ac4c8..3061ad06 100644 --- a/app/libc/c_string.h +++ b/app/libc/c_string.h @@ -25,7 +25,7 @@ #define c_strncmp os_strncmp #define c_strncpy os_strncpy // #define c_strstr os_strstr -#define c_strncasecmp c_strcmp +#define c_strncasecmp strncasecmp #define c_strstr strstr #define c_strncat strncat diff --git a/app/modules/cjson.c b/app/modules/cjson.c index 78bf2e33..361c5fa3 100644 --- a/app/modules/cjson.c +++ b/app/modules/cjson.c @@ -36,7 +36,7 @@ * difficult to know object/array sizes ahead of time. */ -#include +// #include #include "c_string.h" #include "c_math.h" #include "c_limits.h" @@ -44,7 +44,14 @@ #include "lauxlib.h" #include "strbuf.h" +#ifdef LUA_NUMBER_INTEGRAL #include "fpconv.h" +#else +#define FPCONV_G_FMT_BUFSIZE 32 +#define fpconv_strtod c_strtod +#define fpconv_init() ((void)0) +#define fpconv_g_fmt fpconv_g_fmt +#endif #ifndef CJSON_MODNAME #define CJSON_MODNAME "cjson" @@ -964,7 +971,7 @@ static void json_next_string_token(json_parse_t *json, json_token_t *token) char ch; /* Caller must ensure a string is next */ - assert(*json->ptr == '"'); + if(!(*json->ptr == '"')) return; /* Skip " */ json->ptr++; @@ -1058,10 +1065,21 @@ static int json_is_invalid_number(json_parse_t *json) return 0; /* Ordinary number */ } + char tmp[4]; // conv to lower. because c_strncasecmp == c_strcmp + int i; + for (i = 0; i < 3; ++i) + { + if(p[i]!=0) + tmp[i] = tolower(p[i]); + else + tmp[i] = 0; + } + tmp[3] = 0; + /* Reject inf/nan */ - if (!strncasecmp(p, "inf", 3)) + if (!c_strncasecmp(tmp, "inf", 3)) return 1; - if (!strncasecmp(p, "nan", 3)) + if (!c_strncasecmp(tmp, "nan", 3)) return 1; /* Pass all other numbers which may still be invalid, but @@ -1137,17 +1155,17 @@ static void json_next_token(json_parse_t *json, json_token_t *token) } json_next_number_token(json, token); return; - } else if (!strncmp(json->ptr, "true", 4)) { + } else if (!c_strncmp(json->ptr, "true", 4)) { token->type = T_BOOLEAN; token->value.boolean = 1; json->ptr += 4; return; - } else if (!strncmp(json->ptr, "false", 5)) { + } else if (!c_strncmp(json->ptr, "false", 5)) { token->type = T_BOOLEAN; token->value.boolean = 0; json->ptr += 5; return; - } else if (!strncmp(json->ptr, "null", 4)) { + } else if (!c_strncmp(json->ptr, "null", 4)) { token->type = T_NULL; json->ptr += 4; return; From c0d690ce7e7911e76d89816c08471bcfb00574a0 Mon Sep 17 00:00:00 2001 From: funshine Date: Mon, 16 Mar 2015 17:11:48 +0800 Subject: [PATCH 27/40] under development --- app/cjson/{fpconv.c.bak => fpconv.c} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename app/cjson/{fpconv.c.bak => fpconv.c} (100%) diff --git a/app/cjson/fpconv.c.bak b/app/cjson/fpconv.c similarity index 100% rename from app/cjson/fpconv.c.bak rename to app/cjson/fpconv.c From 6c092e42ed99473cfb574f1c1caddc5549cbb2e6 Mon Sep 17 00:00:00 2001 From: AllAboutEE Date: Mon, 16 Mar 2015 04:17:19 -0500 Subject: [PATCH 28/40] Fixed bug where email text after CR and LF would not be read. Added example links, Fixed bug where send function could not be called more than once due to count variable. --- lua_examples/email/read_email_imap.lua | 5 +++-- lua_examples/email/send_email_smtp.lua | 7 +++++-- lua_modules/email/imap.lua | 1 + 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/lua_examples/email/read_email_imap.lua b/lua_examples/email/read_email_imap.lua index 13aa7db6..f64e5c31 100644 --- a/lua_examples/email/read_email_imap.lua +++ b/lua_examples/email/read_email_imap.lua @@ -1,4 +1,5 @@ --- +-- Working Example: https://www.youtube.com/watch?v=PDxTR_KJLhc -- @author Miguel (AllAboutEE.com) -- @description This example will read the first email in your inbox using IMAP and -- display it through serial. The email server must provided unecrypted access. The code @@ -16,7 +17,7 @@ local IMAP_SERVER = "imap.service.com" local IMAP_PORT = "143" local IMAP_TAG = "t1" -- You do not need to change this -local IMAP_DEBUG = false -- change to true if you would like to see the entire conversation between +local IMAP_DEBUG = true -- change to true if you would like to see the entire conversation between -- the ESP8266 and IMAP server local SSID = "ssid" @@ -93,7 +94,7 @@ function do_next() -- create patterns to strip away IMAP protocl text from actual message pattern1 = "(\*.+\}\r\n)" -- to remove "* n command (BODY[n] {n}" - pattern2 = "(\r\n.+)" -- to remove ") t1 OK command completed" + pattern2 = "(%)\r\n.+)" -- to remove ") t1 OK command completed" from = string.gsub(from,pattern1,"") from = string.gsub(from,pattern2,"") diff --git a/lua_examples/email/send_email_smtp.lua b/lua_examples/email/send_email_smtp.lua index 597a5f87..646b4ff6 100644 --- a/lua_examples/email/send_email_smtp.lua +++ b/lua_examples/email/send_email_smtp.lua @@ -1,4 +1,5 @@ --- +-- Working Example: https://www.youtube.com/watch?v=CcRbFIJ8aeU -- @description a basic SMTP email example. You must use an account which can provide unencrypted authenticated access. -- This example was tested with an AOL and Time Warner email accounts. GMail does not offer unecrypted authenticated access. -- To obtain your email's SMTP server and port simply Google it e.g. [my email domain] SMTP settings @@ -37,8 +38,8 @@ local email_subject = "" local email_body = "" local count = 0 --- create a socket to the SMTP server -local smtp_socket = net.createConnection(net.TCP,0) + +local smtp_socket = nil -- will be used as socket to email server -- The display() function will be used to print the SMTP server's response function display(sck,response) @@ -101,8 +102,10 @@ end -- @param subject The email's subject -- @param body The email's body function send_email(subject,body) + count = 0 email_subject = subject email_body = body + smtp_socket = net.createConnection(net.TCP,0) smtp_socket:on("connection",connected) smtp_socket:on("receive",display) smtp_socket:connect(SMTP_PORT,SMTP_SERVER) diff --git a/lua_modules/email/imap.lua b/lua_modules/email/imap.lua index 4b2031ab..e18e0d12 100644 --- a/lua_modules/email/imap.lua +++ b/lua_modules/email/imap.lua @@ -1,4 +1,5 @@ --- +-- Working Example: https://www.youtube.com/watch?v=PDxTR_KJLhc -- IMPORTANT: run node.compile("imap.lua") after uploading this script -- to create a compiled module. Then run file.remove("imap.lua") -- @name imap From 54004a4beb7cd7ddec477aee9f517bbde712564a Mon Sep 17 00:00:00 2001 From: funshine Date: Mon, 16 Mar 2015 23:59:57 +0800 Subject: [PATCH 29/40] fix cjson bugs and reduce mem usage --- app/cjson/NEWS | 44 ---- app/cjson/{dtoa.c.bak => dtoa.c} | 3 +- .../{dtoa_config.h.bak => dtoa_config.h} | 4 +- app/cjson/g_fmt.c | 3 +- app/cjson/lua-cjson-2.1devel-1.rockspec | 56 ----- app/cjson/lua-cjson.spec | 80 ------ app/cjson/performance.txt | 89 ------- app/cjson/strbuf.c | 21 +- app/cjson/strbuf.h | 10 +- app/modules/cjson.c | 228 +++++++++++++----- 10 files changed, 188 insertions(+), 350 deletions(-) delete mode 100644 app/cjson/NEWS rename app/cjson/{dtoa.c.bak => dtoa.c} (99%) rename app/cjson/{dtoa_config.h.bak => dtoa_config.h} (99%) delete mode 100644 app/cjson/lua-cjson-2.1devel-1.rockspec delete mode 100644 app/cjson/lua-cjson.spec delete mode 100644 app/cjson/performance.txt diff --git a/app/cjson/NEWS b/app/cjson/NEWS deleted file mode 100644 index 8927d6e5..00000000 --- a/app/cjson/NEWS +++ /dev/null @@ -1,44 +0,0 @@ -Version 2.1.0 (Mar 1 2012) -* Added cjson.safe module interface which returns nil after an error -* Improved Makefile compatibility with Solaris make - -Version 2.0.0 (Jan 22 2012) -* Improved platform compatibility for strtod/sprintf locale workaround -* Added option to build with David Gay's dtoa.c for improved performance -* Added support for Lua 5.2 -* Added option to encode infinity/NaN as JSON null -* Fixed encode bug with a raised default limit and deeply nested tables -* Updated Makefile for compatibility with non-GNU make implementations -* Added CMake build support -* Added HTML manual -* Increased default nesting limit to 1000 -* Added support for re-entrant use of encode and decode -* Added support for installing lua2json and json2lua utilities -* Added encode_invalid_numbers() and decode_invalid_numbers() -* Added decode_max_depth() -* Removed registration of global cjson module table -* Removed refuse_invalid_numbers() - -Version 1.0.4 (Nov 30 2011) -* Fixed numeric conversion under locales with a comma decimal separator - -Version 1.0.3 (Sep 15 2011) -* Fixed detection of objects with numeric string keys -* Provided work around for missing isinf() on Solaris - -Version 1.0.2 (May 30 2011) -* Portability improvements for Windows - - No longer links with -lm - - Use "socket" instead of "posix" for sub-second timing -* Removed UTF-8 test dependency on Perl Text::Iconv -* Added simple CLI commands for testing Lua <-> JSON conversions -* Added cjson.encode_number_precision() - -Version 1.0.1 (May 10 2011) -* Added build support for OSX -* Removed unnecessary whitespace from JSON output -* Added cjson.encode_keep_buffer() -* Fixed memory leak on Lua stack overflow exception - -Version 1.0 (May 9 2011) -* Initial release diff --git a/app/cjson/dtoa.c.bak b/app/cjson/dtoa.c similarity index 99% rename from app/cjson/dtoa.c.bak rename to app/cjson/dtoa.c index 56398ba7..ff1e744b 100644 --- a/app/cjson/dtoa.c.bak +++ b/app/cjson/dtoa.c @@ -184,7 +184,7 @@ * a decimal expansion to decide close cases. This logic is only * used for input more than STRTOD_DIGLIM digits long (default 40). */ - +#if 0 #include "dtoa_config.h" #ifndef Long @@ -4356,3 +4356,4 @@ dtoa #ifdef __cplusplus } #endif +#endif diff --git a/app/cjson/dtoa_config.h.bak b/app/cjson/dtoa_config.h similarity index 99% rename from app/cjson/dtoa_config.h.bak rename to app/cjson/dtoa_config.h index 380e83b0..483cf85b 100644 --- a/app/cjson/dtoa_config.h.bak +++ b/app/cjson/dtoa_config.h @@ -1,6 +1,6 @@ #ifndef _DTOA_CONFIG_H #define _DTOA_CONFIG_H - +#if 0 #include #include #include @@ -67,7 +67,7 @@ static pthread_mutex_t private_dtoa_lock[2] = { } while (0) #endif /* MULTIPLE_THREADS */ - +#endif #endif /* _DTOA_CONFIG_H */ /* vi:ai et sw=4 ts=4: diff --git a/app/cjson/g_fmt.c b/app/cjson/g_fmt.c index 50d6a1d3..2bf34a83 100644 --- a/app/cjson/g_fmt.c +++ b/app/cjson/g_fmt.c @@ -21,7 +21,7 @@ * it suffices to declare buf * char buf[32]; */ - +#if 0 #ifdef __cplusplus extern "C" { #endif @@ -109,3 +109,4 @@ fpconv_g_fmt(char *b, double x, int precision) #endif return b - b0; } +#endif diff --git a/app/cjson/lua-cjson-2.1devel-1.rockspec b/app/cjson/lua-cjson-2.1devel-1.rockspec deleted file mode 100644 index 154e333d..00000000 --- a/app/cjson/lua-cjson-2.1devel-1.rockspec +++ /dev/null @@ -1,56 +0,0 @@ -package = "lua-cjson" -version = "2.1devel-1" - -source = { - url = "http://www.kyne.com.au/~mark/software/download/lua-cjson-2.1devel.zip", -} - -description = { - summary = "A fast JSON encoding/parsing module", - detailed = [[ - The Lua CJSON module provides JSON support for Lua. It features: - - Fast, standards compliant encoding/parsing routines - - Full support for JSON with UTF-8, including decoding surrogate pairs - - Optional run-time support for common exceptions to the JSON specification - (infinity, NaN,..) - - No dependencies on other libraries - ]], - homepage = "http://www.kyne.com.au/~mark/software/lua-cjson.php", - license = "MIT" -} - -dependencies = { - "lua >= 5.1" -} - -build = { - type = "builtin", - modules = { - cjson = { - sources = { "lua_cjson.c", "strbuf.c", "fpconv.c" }, - defines = { --- LuaRocks does not support platform specific configuration for Solaris. --- Uncomment the line below on Solaris platforms if required. --- "USE_INTERNAL_ISINF" - } - } - }, - install = { - lua = { - ["cjson.util"] = "lua/cjson/util.lua" - }, - bin = { - json2lua = "lua/json2lua.lua", - lua2json = "lua/lua2json.lua" - } - }, - -- Override default build options (per platform) - platforms = { - win32 = { modules = { cjson = { defines = { - "DISABLE_INVALID_NUMBERS" - } } } } - }, - copy_directories = { "tests" } -} - --- vi:ai et sw=4 ts=4: diff --git a/app/cjson/lua-cjson.spec b/app/cjson/lua-cjson.spec deleted file mode 100644 index 13fc56d2..00000000 --- a/app/cjson/lua-cjson.spec +++ /dev/null @@ -1,80 +0,0 @@ -%define luaver 5.1 -%define lualibdir %{_libdir}/lua/%{luaver} -%define luadatadir %{_datadir}/lua/%{luaver} - -Name: lua-cjson -Version: 2.1devel -Release: 1%{?dist} -Summary: A fast JSON encoding/parsing module for Lua - -Group: Development/Libraries -License: MIT -URL: http://www.kyne.com.au/~mark/software/lua-cjson/ -Source0: http://www.kyne.com.au/~mark/software/lua-cjson/download/lua-cjson-%{version}.tar.gz -BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX) - -BuildRequires: lua >= %{luaver}, lua-devel >= %{luaver} -Requires: lua >= %{luaver} - -%description -The Lua CJSON module provides JSON support for Lua. It features: -- Fast, standards compliant encoding/parsing routines -- Full support for JSON with UTF-8, including decoding surrogate pairs -- Optional run-time support for common exceptions to the JSON specification - (infinity, NaN,..) -- No dependencies on other libraries - - -%prep -%setup -q - - -%build -make %{?_smp_mflags} CFLAGS="%{optflags}" LUA_INCLUDE_DIR="%{_includedir}" - - -%install -rm -rf "$RPM_BUILD_ROOT" -make install DESTDIR="$RPM_BUILD_ROOT" LUA_CMODULE_DIR="%{lualibdir}" -make install-extra DESTDIR="$RPM_BUILD_ROOT" LUA_MODULE_DIR="%{luadatadir}" \ - LUA_BIN_DIR="%{_bindir}" - - -%clean -rm -rf "$RPM_BUILD_ROOT" - - -%preun -/bin/rm -f "%{luadatadir}/cjson/tests/utf8.dat" - - -%files -%defattr(-,root,root,-) -%doc LICENSE NEWS performance.html performance.txt manual.html manual.txt rfc4627.txt THANKS -%{lualibdir}/* -%{luadatadir}/* -%{_bindir}/* - - -%changelog -* Thu Mar 1 2012 Mark Pulford - 2.1.0-1 -- Update for 2.1.0 - -* Sun Jan 22 2012 Mark Pulford - 2.0.0-1 -- Update for 2.0.0 -- Install lua2json / json2lua utilities - -* Wed Nov 27 2011 Mark Pulford - 1.0.4-1 -- Update for 1.0.4 - -* Wed Sep 15 2011 Mark Pulford - 1.0.3-1 -- Update for 1.0.3 - -* Sun May 29 2011 Mark Pulford - 1.0.2-1 -- Update for 1.0.2 - -* Sun May 10 2011 Mark Pulford - 1.0.1-1 -- Update for 1.0.1 - -* Sun May 1 2011 Mark Pulford - 1.0-1 -- Initial package diff --git a/app/cjson/performance.txt b/app/cjson/performance.txt deleted file mode 100644 index fc3a5bb5..00000000 --- a/app/cjson/performance.txt +++ /dev/null @@ -1,89 +0,0 @@ -JSON module performance comparison under Lua -============================================ -Mark Pulford -:revdate: January 22, 2012 - -This performance comparison aims to provide a guide of relative -performance between several fast and popular JSON modules. - -The examples used in this comparison were mostly sourced from the -http://json.org[JSON website] and -http://tools.ietf.org/html/rfc4627[RFC 4627]. - -Performance will vary widely between platforms and data sets. These -results should only be used as an approximation. - - -Modules -------- - -The following JSON modules for Lua were tested: - -http://chiselapp.com/user/dhkolf/repository/dkjson/[DKJSON 2.1]:: - - Lua implementation with no dependencies on other libraries - - Supports LPeg to improve decode performance - -https://github.com/brimworks/lua-yajl[Lua YAJL 2.0]:: - - C wrapper for the YAJL library - -http://www.kyne.com.au/%7Emark/software/lua-cjson.php[Lua CSJON 2.0.0]:: - - C implementation with no dependencies on other libraries - - -Summary -------- - -All modules were built and tested as follows: - -DKJSON:: Tested with/without LPeg 10.2. -Lua YAJL:: Tested with YAJL 2.0.4. -Lua CJSON:: Tested with Libc and internal floating point conversion - routines. - -The following Lua implementations were used for this comparison: - -- http://www.lua.org[Lua 5.1.4] (_Lua_) -- http://www.luajit.org[LuaJIT 2.0.0-beta9] (_JIT_) - -These results show the number of JSON operations per second sustained by -each module. All results have been normalised against the pure Lua -DKJSON implementation. - -.Decoding performance -............................................................................ - | DKJSON | Lua YAJL | Lua CJSON - | No LPeg With LPeg | | Libc Internal - | Lua JIT Lua JIT | Lua JIT | Lua JIT Lua JIT -example1 | 1x 2x 2.6x 3.4x | 7.1x 10x | 14x 20x 14x 20x -example2 | 1x 2.2x 2.9x 4.4x | 6.7x 9.9x | 14x 22x 14x 22x -example3 | 1x 2.1x 3x 4.3x | 6.9x 9.3x | 14x 21x 15x 22x -example4 | 1x 2x 2.5x 3.7x | 7.3x 10x | 12x 19x 12x 20x -example5 | 1x 2.2x 3x 4.5x | 7.8x 11x | 16x 24x 16x 24x -numbers | 1x 2.2x 2.3x 4x | 4.6x 5.5x | 8.9x 10x 13x 17x -rfc-example1 | 1x 2.1x 2.8x 4.3x | 6.1x 8.1x | 13x 19x 14x 21x -rfc-example2 | 1x 2.1x 3.1x 4.2x | 7.1x 9.2x | 15x 21x 17x 24x -types | 1x 2.2x 2.6x 4.3x | 5.3x 7.4x | 12x 20x 13x 21x --------------|-------------------------|------------|----------------------- -= Average => | 1x 2.1x 2.7x 4.1x | 6.5x 9x | 13x 20x 14x 21x -............................................................................ - -.Encoding performance -............................................................................. - | DKJSON | Lua YAJL | Lua CJSON - | No LPeg With LPeg | | Libc Internal - | Lua JIT Lua JIT | Lua JIT | Lua JIT Lua JIT -example1 | 1x 1.8x 0.97x 1.6x | 3.1x 5.2x | 23x 29x 23x 29x -example2 | 1x 2x 0.97x 1.7x | 2.6x 4.3x | 22x 28x 22x 28x -example3 | 1x 1.9x 0.98x 1.6x | 2.8x 4.3x | 13x 15x 16x 18x -example4 | 1x 1.7x 0.96x 1.3x | 3.9x 6.1x | 15x 19x 17x 21x -example5 | 1x 2x 0.98x 1.7x | 2.7x 4.5x | 20x 23x 20x 23x -numbers | 1x 2.3x 1x 2.2x | 1.3x 1.9x | 3.8x 4.1x 4.2x 4.6x -rfc-example1 | 1x 1.9x 0.97x 1.6x | 2.2x 3.2x | 8.5x 9.3x 11x 12x -rfc-example2 | 1x 1.9x 0.98x 1.6x | 2.6x 3.9x | 10x 11x 17x 19x -types | 1x 2.2x 0.97x 2x | 1.2x 1.9x | 11x 13x 12x 14x --------------|-------------------------|------------|----------------------- -= Average => | 1x 1.9x 0.98x 1.7x | 2.5x 3.9x | 14x 17x 16x 19x -............................................................................. - - -// vi:ft=asciidoc tw=72: diff --git a/app/cjson/strbuf.c b/app/cjson/strbuf.c index c9528fa9..f4f36803 100644 --- a/app/cjson/strbuf.c +++ b/app/cjson/strbuf.c @@ -41,7 +41,7 @@ // exit(-1); // } -void strbuf_init(strbuf_t *s, int len) +int strbuf_init(strbuf_t *s, int len) { int size; @@ -61,10 +61,11 @@ void strbuf_init(strbuf_t *s, int len) s->buf = c_malloc(size); if (!s->buf){ NODE_ERR("not enough memory\n"); - return; + return -1; } strbuf_ensure_null(s); + return 0; } strbuf_t *strbuf_new(int len) @@ -74,7 +75,7 @@ strbuf_t *strbuf_new(int len) s = c_malloc(sizeof(strbuf_t)); if (!s){ NODE_ERR("not enough memory\n"); - return; + return NULL; } strbuf_init(s, len); @@ -85,16 +86,17 @@ strbuf_t *strbuf_new(int len) return s; } -void strbuf_set_increment(strbuf_t *s, int increment) +int strbuf_set_increment(strbuf_t *s, int increment) { /* Increment > 0: Linear buffer growth rate * Increment < -1: Exponential buffer growth rate */ if (increment == 0 || increment == -1){ NODE_ERR("BUG: Invalid string increment"); - return; + return -1; } s->increment = increment; + return 0; } static inline void debug_stats(strbuf_t *s) @@ -169,7 +171,7 @@ static int calculate_new_size(strbuf_t *s, int len) /* Ensure strbuf can handle a string length bytes long (ignoring NULL * optional termination). */ -void strbuf_resize(strbuf_t *s, int len) +int strbuf_resize(strbuf_t *s, int len) { int newsize; @@ -180,13 +182,14 @@ void strbuf_resize(strbuf_t *s, int len) (long)s, s->size, newsize); } - s->size = newsize; - s->buf = (char *)c_realloc(s->buf, s->size); + s->buf = (char *)c_realloc(s->buf, newsize); if (!s->buf){ NODE_ERR("not enough memory"); - return; + return -1; } + s->size = newsize; s->reallocs++; + return 0; } void strbuf_append_string(strbuf_t *s, const char *str) diff --git a/app/cjson/strbuf.h b/app/cjson/strbuf.h index e6044be6..aa1f8c5d 100644 --- a/app/cjson/strbuf.h +++ b/app/cjson/strbuf.h @@ -50,15 +50,15 @@ typedef struct { /* Initialise */ extern strbuf_t *strbuf_new(int len); -extern void strbuf_init(strbuf_t *s, int len); -extern void strbuf_set_increment(strbuf_t *s, int increment); +extern int strbuf_init(strbuf_t *s, int len); +extern int strbuf_set_increment(strbuf_t *s, int increment); /* Release */ extern void strbuf_free(strbuf_t *s); extern char *strbuf_free_to_string(strbuf_t *s, int *len); /* Management */ -extern void strbuf_resize(strbuf_t *s, int len); +extern int strbuf_resize(strbuf_t *s, int len); static int strbuf_empty_length(strbuf_t *s); static int strbuf_length(strbuf_t *s); static char *strbuf_string(strbuf_t *s, int *len); @@ -127,13 +127,13 @@ static inline void strbuf_append_char_unsafe(strbuf_t *s, const char c) static inline void strbuf_append_mem(strbuf_t *s, const char *c, int len) { strbuf_ensure_empty_length(s, len); - memcpy(s->buf + s->length, c, len); + c_memcpy(s->buf + s->length, c, len); s->length += len; } static inline void strbuf_append_mem_unsafe(strbuf_t *s, const char *c, int len) { - memcpy(s->buf + s->length, c, len); + c_memcpy(s->buf + s->length, c, len); s->length += len; } diff --git a/app/modules/cjson.c b/app/modules/cjson.c index 361c5fa3..15c263db 100644 --- a/app/modules/cjson.c +++ b/app/modules/cjson.c @@ -42,6 +42,7 @@ #include "c_limits.h" #include "lua.h" #include "lauxlib.h" +#include "flash_api.h" #include "strbuf.h" #ifdef LUA_NUMBER_INTEGRAL @@ -50,7 +51,6 @@ #define FPCONV_G_FMT_BUFSIZE 32 #define fpconv_strtod c_strtod #define fpconv_init() ((void)0) -#define fpconv_g_fmt fpconv_g_fmt #endif #ifndef CJSON_MODNAME @@ -73,7 +73,7 @@ #define DEFAULT_DECODE_MAX_DEPTH 1000 #define DEFAULT_ENCODE_INVALID_NUMBERS 0 #define DEFAULT_DECODE_INVALID_NUMBERS 1 -#define DEFAULT_ENCODE_KEEP_BUFFER 1 +#define DEFAULT_ENCODE_KEEP_BUFFER 0 #define DEFAULT_ENCODE_NUMBER_PRECISION 14 #ifdef DISABLE_INVALID_NUMBERS @@ -97,7 +97,7 @@ typedef enum { T_ERROR, T_UNKNOWN } json_token_type_t; - +#if 0 static const char *json_token_type_name[] = { "T_OBJ_BEGIN", "T_OBJ_END", @@ -115,10 +115,27 @@ static const char *json_token_type_name[] = { "T_UNKNOWN", NULL }; +#endif +static const char json_token_type_name[14][16] ICACHE_STORE_ATTR ICACHE_RODATA_ATTR = { + {'T','_','O','B','J','_','B','E','G','I','N',0}, + {'T','_','O','B','J','_','E','N','D',0}, + {'T','_','A','R','R','_','B','E','G','I','N',0}, + {'T','_','A','R','R','_','E','N','D',0}, + {'T','_','S','T','R','I','N','G',0}, + {'T','_','N','U','M','B','E','R',0}, + {'T','_','B','O','O','L','E','A','N',0}, + {'T','_','N','U','L','L',0}, + {'T','_','C','O','L','O','N',0}, + {'T','_','C','O','M','M','A',0}, + {'T','_','E','N','D',0}, + {'T','_','W','H','I','T','E','S','P','A','C','E',0}, + {'T','_','E','R','R','O','R',0}, + {'T','_','U','N','K','N','O','W','N',0} +}; typedef struct { - json_token_type_t ch2token[256]; - char escape2char[256]; /* Decoding */ + // json_token_type_t ch2token[256]; // 256*4 = 1024 byte + // char escape2char[256]; /* Decoding */ /* encode_buf is only allocated and used when * encode_keep_buffer is set */ @@ -155,6 +172,7 @@ typedef struct { int string_len; } json_token_t; +#if 0 static const char *char2escape[256] = { "\\u0000", "\\u0001", "\\u0002", "\\u0003", "\\u0004", "\\u0005", "\\u0006", "\\u0007", @@ -193,6 +211,99 @@ static const char *char2escape[256] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, }; +#endif + +/* ===== HELPER FUNCTION ===== */ +static const char escape_array[36][8] ICACHE_STORE_ATTR ICACHE_RODATA_ATTR = { + {'\\','u','0','0','0','0','\0','\0'}, + {'\\','u','0','0','0','1','\0','\0'}, + {'\\','u','0','0','0','2','\0','\0'}, + {'\\','u','0','0','0','3','\0','\0'}, + {'\\','u','0','0','0','4','\0','\0'}, + {'\\','u','0','0','0','5','\0','\0'}, + {'\\','u','0','0','0','6','\0','\0'}, + {'\\','u','0','0','0','7','\0','\0'}, + {'\\','b','\0','\0','\0','\0','\0','\0'}, + {'\\','t','\0','\0','\0','\0','\0','\0'}, + {'\\','n','\0','\0','\0','\0','\0','\0'}, + {'\\','u','0','0','0','b','\0','\0'}, + {'\\','f','\0','\0','\0','\0','\0','\0'}, + {'\\','r','\0','\0','\0','\0','\0','\0'}, + {'\\','u','0','0','0','e','\0','\0'}, + {'\\','u','0','0','0','f','\0','\0'}, + {'\\','u','0','0','1','0','\0','\0'}, + {'\\','u','0','0','1','1','\0','\0'}, + {'\\','u','0','0','1','2','\0','\0'}, + {'\\','u','0','0','1','3','\0','\0'}, + {'\\','u','0','0','1','4','\0','\0'}, + {'\\','u','0','0','1','5','\0','\0'}, + {'\\','u','0','0','1','6','\0','\0'}, + {'\\','u','0','0','1','7','\0','\0'}, + {'\\','u','0','0','1','8','\0','\0'}, + {'\\','u','0','0','1','9','\0','\0'}, + {'\\','u','0','0','1','a','\0','\0'}, + {'\\','u','0','0','1','b','\0','\0'}, + {'\\','u','0','0','1','c','\0','\0'}, + {'\\','u','0','0','1','d','\0','\0'}, + {'\\','u','0','0','1','e','\0','\0'}, + {'\\','u','0','0','1','f','\0','\0'}, + {'\\','\"','\0','\0','\0','\0','\0','\0'}, + {'\\','/','\0','\0','\0','\0','\0','\0'}, + {'\\','\\','\0','\0','\0','\0','\0','\0'}, + {'\\','u','0','0','7','f','\0','\0'} +}; + +static const char *char2escape(unsigned char c){ + if(c<32) return escape_array[c]; + + switch(c){ + case 34: return escape_array[32]; + case 47: return escape_array[33]; + case 92: return escape_array[34]; + case 127: return escape_array[35]; + default: + return NULL; + } +} + +static json_token_type_t ch2token(unsigned char c){ + switch(c){ + case '{': return T_OBJ_BEGIN; + case '}': return T_OBJ_END; + case '[': return T_ARR_BEGIN; + case ']': return T_ARR_END; + case ',': return T_COMMA; + case ':': return T_COLON; + case '\0': return T_END; + case ' ': return T_WHITESPACE; + case '\t': return T_WHITESPACE; + case '\n': return T_WHITESPACE; + case '\r': return T_WHITESPACE; + + /* Update characters that require further processing */ + case 'f': case 'i': case 'I': case 'n': case 'N': case 't': case '"': case '+': case '-': + case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': + return T_UNKNOWN; + default: + return T_ERROR; + } +} + +static char escape2char(unsigned char c){ + switch(c){ + case '"': return '"'; + case '\\': return '\\'; + case '/': return '/'; + case 'b': return '\b'; + case 't': return '\t'; + case 'n': return '\n'; + case 'f': return '\f'; + case 'r': return '\r'; + case 'u': return 'u'; + default: + return 0; + } +} /* ===== CONFIGURATION ===== */ #if 0 @@ -319,8 +430,10 @@ static int json_cfg_encode_keep_buffer(lua_State *l) /* Init / free the buffer if the setting has changed */ if (old_value ^ cfg->encode_keep_buffer) { - if (cfg->encode_keep_buffer) - strbuf_init(&cfg->encode_buf, 0); + if (cfg->encode_keep_buffer){ + if(-1==strbuf_init(&cfg->encode_buf, 0)) + return luaL_error(l, "not enough memory"); + } else strbuf_free(&cfg->encode_buf); } @@ -456,9 +569,7 @@ static json_config_t *json_fetch_config(lua_State *l) return &_cfg; } -static void cfg_init(json_config_t *cfg){ - int i; - +static int cfg_init(json_config_t *cfg){ cfg->encode_sparse_convert = DEFAULT_SPARSE_CONVERT; cfg->encode_sparse_ratio = DEFAULT_SPARSE_RATIO; cfg->encode_sparse_safe = DEFAULT_SPARSE_SAFE; @@ -470,53 +581,13 @@ static void cfg_init(json_config_t *cfg){ cfg->encode_number_precision = DEFAULT_ENCODE_NUMBER_PRECISION; #if DEFAULT_ENCODE_KEEP_BUFFER > 0 - strbuf_init(&cfg->encode_buf, 0); + if(-1==strbuf_init(&cfg->encode_buf, 0)){ + NODE_ERR("not enough memory\n"); + return -1; + } #endif - /* Decoding init */ - - /* Tag all characters as an error */ - for (i = 0; i < 256; i++) - cfg->ch2token[i] = T_ERROR; - - /* Set tokens that require no further processing */ - cfg->ch2token['{'] = T_OBJ_BEGIN; - cfg->ch2token['}'] = T_OBJ_END; - cfg->ch2token['['] = T_ARR_BEGIN; - cfg->ch2token[']'] = T_ARR_END; - cfg->ch2token[','] = T_COMMA; - cfg->ch2token[':'] = T_COLON; - cfg->ch2token['\0'] = T_END; - cfg->ch2token[' '] = T_WHITESPACE; - cfg->ch2token['\t'] = T_WHITESPACE; - cfg->ch2token['\n'] = T_WHITESPACE; - cfg->ch2token['\r'] = T_WHITESPACE; - - /* Update characters that require further processing */ - cfg->ch2token['f'] = T_UNKNOWN; /* false? */ - cfg->ch2token['i'] = T_UNKNOWN; /* inf, ininity? */ - cfg->ch2token['I'] = T_UNKNOWN; - cfg->ch2token['n'] = T_UNKNOWN; /* null, nan? */ - cfg->ch2token['N'] = T_UNKNOWN; - cfg->ch2token['t'] = T_UNKNOWN; /* true? */ - cfg->ch2token['"'] = T_UNKNOWN; /* string? */ - cfg->ch2token['+'] = T_UNKNOWN; /* number? */ - cfg->ch2token['-'] = T_UNKNOWN; - for (i = 0; i < 10; i++) - cfg->ch2token['0' + i] = T_UNKNOWN; - - /* Lookup table for parsing escape characters */ - for (i = 0; i < 256; i++) - cfg->escape2char[i] = 0; /* String error */ - cfg->escape2char['"'] = '"'; - cfg->escape2char['\\'] = '\\'; - cfg->escape2char['/'] = '/'; - cfg->escape2char['b'] = '\b'; - cfg->escape2char['t'] = '\t'; - cfg->escape2char['n'] = '\n'; - cfg->escape2char['f'] = '\f'; - cfg->escape2char['r'] = '\r'; - cfg->escape2char['u'] = 'u'; /* Unicode parsing required */ + return 0; } /* ===== ENCODING ===== */ @@ -552,9 +623,18 @@ static void json_append_string(lua_State *l, strbuf_t *json, int lindex) strbuf_append_char_unsafe(json, '\"'); for (i = 0; i < len; i++) { - escstr = char2escape[(unsigned char)str[i]]; - if (escstr) + escstr = char2escape((unsigned char)str[i]); + if (escstr){ + int i; + char temp[8]; // for now, 8-bytes is enough. + for (i=0; i < 8; ++i) + { + temp[i] = byte_of_aligned_array(escstr, i); + if(temp[i]==0) break; + } + escstr = temp; strbuf_append_string(json, escstr); + } else strbuf_append_char_unsafe(json, str[i]); } @@ -694,7 +774,12 @@ static void json_append_number(lua_State *l, json_config_t *cfg, } strbuf_ensure_empty_length(json, FPCONV_G_FMT_BUFSIZE); +#ifdef LUA_NUMBER_INTEGRAL len = fpconv_g_fmt(strbuf_empty_ptr(json), num, cfg->encode_number_precision); +#else + c_sprintf(strbuf_empty_ptr(json), "%.14g", num); + len = c_strlen(strbuf_empty_ptr(json)); +#endif strbuf_extend_length(json, len); } @@ -796,7 +881,8 @@ static int json_encode(lua_State *l) if (!cfg->encode_keep_buffer) { /* Use private buffer */ encode_buf = &local_encode_buf; - strbuf_init(encode_buf, 0); + if(-1==strbuf_init(encode_buf, 0)) + return luaL_error(l, "not enough memory"); } else { /* Reuse existing buffer */ encode_buf = &cfg->encode_buf; @@ -967,7 +1053,7 @@ static void json_set_token_error(json_token_t *token, json_parse_t *json, static void json_next_string_token(json_parse_t *json, json_token_t *token) { - char *escape2char = json->cfg->escape2char; + // char *escape2char = json->cfg->escape2char; char ch; /* Caller must ensure a string is next */ @@ -995,7 +1081,7 @@ static void json_next_string_token(json_parse_t *json, json_token_t *token) ch = *(json->ptr + 1); /* Translate escape code and append to tmp string */ - ch = escape2char[(unsigned char)ch]; + ch = escape2char((unsigned char)ch); if (ch == 'u') { if (json_append_unicode_escape(json) == 0) continue; @@ -1107,13 +1193,13 @@ static void json_next_number_token(json_parse_t *json, json_token_t *token) */ static void json_next_token(json_parse_t *json, json_token_t *token) { - const json_token_type_t *ch2token = json->cfg->ch2token; + // const json_token_type_t *ch2token = json->cfg->ch2token; int ch; /* Eat whitespace. */ while (1) { ch = (unsigned char)*(json->ptr); - token->type = ch2token[ch]; + token->type = ch2token(ch); if (token->type != T_WHITESPACE) break; json->ptr++; @@ -1194,13 +1280,23 @@ static void json_throw_parse_error(lua_State *l, json_parse_t *json, const char *exp, json_token_t *token) { const char *found; + char temp[16]; // for now, 16-bytes is enough. strbuf_free(json->tmp); if (token->type == T_ERROR) found = token->value.string; else + { found = json_token_type_name[token->type]; + int i; + for (i=0; i < 16; ++i) + { + temp[i] = byte_of_aligned_array(found, i); + if(temp[i]==0) break; + } + found = temp; + } /* Note: token->index is 0 based, display starting from 1 */ luaL_error(l, "Expected %s but found %s at character %d", @@ -1369,6 +1465,9 @@ static int json_decode(lua_State *l) * This means we no longer need to do length checks since the decoded * string must be smaller than the entire json string */ json.tmp = strbuf_new(json_len); + if(json.tmp == NULL){ + return luaL_error(l, "not enought memory"); + } json_next_token(&json, &token); json_process_value(l, &json, &token); @@ -1434,6 +1533,7 @@ static int json_protect_conversion(lua_State *l) return luaL_error(l, "Memory allocation error in CJSON protected call"); } #endif + // Module function map #define MIN_OPT_LEVEL 2 #include "lrodefs.h" @@ -1459,7 +1559,9 @@ LUALIB_API int luaopen_cjson( lua_State *L ) { /* Initialise number conversions */ fpconv_init(); - cfg_init(&_cfg); + if(-1==cfg_init(&_cfg)){ + return luaL_error(L, "BUG: Unable to init config for cjson");; + } #if LUA_OPTIMIZE_MEMORY > 0 return 0; #else // #if LUA_OPTIMIZE_MEMORY > 0 From 4d3c1a930f5a3e9f056b004d82b18aba8eb4f680 Mon Sep 17 00:00:00 2001 From: funshine Date: Tue, 17 Mar 2015 00:12:25 +0800 Subject: [PATCH 30/40] code clean up for cjson --- app/cjson/strbuf.c | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/app/cjson/strbuf.c b/app/cjson/strbuf.c index f4f36803..44a1bd69 100644 --- a/app/cjson/strbuf.c +++ b/app/cjson/strbuf.c @@ -29,18 +29,6 @@ #include "strbuf.h" -// static void die(const char *fmt, ...) -// { -// va_list arg; - -// va_start(arg, fmt); -// vfprintf(stderr, fmt, arg); -// va_end(arg); -// fprintf(stderr, "\n"); - -// exit(-1); -// } - int strbuf_init(strbuf_t *s, int len) { int size; From e19e026d491a043dcc1226c3289305875a7f7dfe Mon Sep 17 00:00:00 2001 From: funshine Date: Tue, 17 Mar 2015 00:24:30 +0800 Subject: [PATCH 31/40] update readme --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index d343075c..8e71cfb4 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@ version 0.9.5 ###A lua based firmware for wifi-soc esp8266 Build on [ESP8266 sdk 0.9.5](http://bbs.espressif.com/viewtopic.php?f=5&t=154)
Lua core based on [eLua project](http://www.eluaproject.net/)
+cjson based on [lua-cjson](https://github.com/mpx/lua-cjson)
File system based on [spiffs](https://github.com/pellepl/spiffs)
Open source development kit for NodeMCU [nodemcu-devkit](https://github.com/nodemcu/nodemcu-devkit)
Flash tool for NodeMCU [nodemcu-flasher](https://github.com/nodemcu/nodemcu-flasher)
@@ -34,6 +35,10 @@ Tencent QQ group: 309957875
- cross compiler (done) # Change log +2015-03-17
+add cjson module, only cjson.encode() and cjson.decode() is implemented.
+read doc [here](https://github.com/nodemcu/nodemcu-firmware/blob/json/app/cjson/manual.txt) + 2015-03-15
bugs fixed: #239, #273.
reduce coap module memory usage, add coap module to default built. From b648e9a8db9baece822df0acd33b3f6e452b49df Mon Sep 17 00:00:00 2001 From: Markus Gritsch Date: Tue, 17 Mar 2015 10:23:45 +0100 Subject: [PATCH 32/40] Added node.setcpufreq(mhz) function. This function allows setting the CPU frequency to node.CPU80MHZ or node.CPU160MHZ. --- app/modules/node.c | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/app/modules/node.c b/app/modules/node.c index caf2af42..53d59ae0 100644 --- a/app/modules/node.c +++ b/app/modules/node.c @@ -25,6 +25,9 @@ #include "flash_fs.h" #include "user_version.h" +#define CPU80MHZ 80 +#define CPU160MHZ 160 + // Lua: restart() static int node_restart( lua_State* L ) { @@ -391,6 +394,24 @@ static int node_compile( lua_State* L ) return 0; } +// Lua: setcpufreq(mhz) +// mhz is either CPU80MHZ od CPU160MHZ +static int node_setcpufreq(lua_State* L) +{ + // http://www.esp8266.com/viewtopic.php?f=21&t=1369 + uint32_t new_freq = luaL_checkinteger(L, 1); + if (new_freq == CPU160MHZ){ + REG_SET_BIT(0x3ff00014, BIT(0)); + os_update_cpu_frequency(CPU160MHZ); + } else { + REG_CLR_BIT(0x3ff00014, BIT(0)); + os_update_cpu_frequency(CPU80MHZ); + } + new_freq = ets_get_cpu_frequency(); + lua_pushinteger(L, new_freq); + return 1; +} + // Module function map #define MIN_OPT_LEVEL 2 #include "lrodefs.h" @@ -411,6 +432,9 @@ const LUA_REG_TYPE node_map[] = { LSTRKEY( "output" ), LFUNCVAL( node_output ) }, { LSTRKEY( "readvdd33" ), LFUNCVAL( node_readvdd33) }, { LSTRKEY( "compile" ), LFUNCVAL( node_compile) }, + { LSTRKEY( "CPU80MHZ" ), LNUMVAL( CPU80MHZ ) }, + { LSTRKEY( "CPU160MHZ" ), LNUMVAL( CPU160MHZ ) }, + { LSTRKEY( "setcpufreq" ), LFUNCVAL( node_setcpufreq) }, // Combined to dsleep(us, option) // { LSTRKEY( "dsleepsetoption" ), LFUNCVAL( node_deepsleep_setoption) }, #if LUA_OPTIMIZE_MEMORY > 0 @@ -428,5 +452,5 @@ LUALIB_API int luaopen_node( lua_State *L ) // Add constants return 1; -#endif // #if LUA_OPTIMIZE_MEMORY > 0 +#endif // #if LUA_OPTIMIZE_MEMORY > 0 } From 808a359eb850d72936735262ca50c2094ac733e3 Mon Sep 17 00:00:00 2001 From: funshine Date: Tue, 17 Mar 2015 22:42:22 +0800 Subject: [PATCH 33/40] quick fix when wide charactor issue --- app/cjson/manual.txt | 444 ------------------------------------- app/include/user_version.h | 2 +- app/modules/cjson.c | 2 +- 3 files changed, 2 insertions(+), 446 deletions(-) diff --git a/app/cjson/manual.txt b/app/cjson/manual.txt index a12e3785..d9975c00 100644 --- a/app/cjson/manual.txt +++ b/app/cjson/manual.txt @@ -20,148 +20,6 @@ The Lua CJSON module provides JSON support for Lua. Lua CJSON is covered by the MIT license. Review the file +LICENSE+ for details. -The latest version of this software is available from the -http://www.kyne.com.au/%7Emark/software/lua-cjson.php[Lua CJSON website]. - -Feel free to email me if you have any patches, suggestions, or comments. - - -Installation ------------- - -Lua CJSON requires either http://www.lua.org[Lua] 5.1, Lua 5.2, or -http://www.luajit.org[LuaJIT] to build. - -The build method can be selected from 4 options: - -Make:: Unix (including Linux, BSD, Mac OSX & Solaris), Windows -CMake:: Unix, Windows -RPM:: Linux -LuaRocks:: Unix, Windows - - -Make -~~~~ - -The included +Makefile+ has generic settings. - -First, review and update the included makefile to suit your platform (if -required). - -Next, build and install the module: - -[source,sh] -make install - -Or install manually into your Lua module directory: - -[source,sh] -make -cp cjson.so $LUA_MODULE_DIRECTORY - - -CMake -~~~~~ - -http://www.cmake.org[CMake] can generate build configuration for many -different platforms (including Unix and Windows). - -First, generate the makefile for your platform using CMake. If CMake is -unable to find Lua, manually set the +LUA_DIR+ environment variable to -the base prefix of your Lua 5.1 installation. - -While +cmake+ is used in the example below, +ccmake+ or +cmake-gui+ may -be used to present an interface for changing the default build options. - -[source,sh] -mkdir build -cd build -# Optional: export LUA_DIR=$LUA51_PREFIX -cmake .. - -Next, build and install the module: - -[source,sh] -make install -# Or: -make -cp cjson.so $LUA_MODULE_DIRECTORY - -Review the -http://www.cmake.org/cmake/help/documentation.html[CMake documentation] -for further details. - - -RPM -~~~ - -Linux distributions using http://rpm.org[RPM] can create a package via -the included RPM spec file. Ensure the +rpm-build+ package (or similar) -has been installed. - -Build and install the module via RPM: - -[source,sh] -rpmbuild -tb lua-cjson-2.1devel.tar.gz -rpm -Uvh $LUA_CJSON_RPM - - -LuaRocks -~~~~~~~~ - -http://luarocks.org[LuaRocks] can be used to install and manage Lua -modules on a wide range of platforms (including Windows). - -First, extract the Lua CJSON source package. - -Next, install the module: - -[source,sh] -cd lua-cjson-2.1devel -luarocks make - -[NOTE] -LuaRocks does not support platform specific configuration for Solaris. -On Solaris, you may need to manually uncomment +USE_INTERNAL_ISINF+ in -the rockspec before building this module. - -Review the http://luarocks.org/en/Documentation[LuaRocks documentation] -for further details. - - -[[build_options]] -Build Options (#define) -~~~~~~~~~~~~~~~~~~~~~~~ - -Lua CJSON offers several +#define+ build options to address portability -issues, and enable non-default features. Some build methods may -automatically set platform specific options if required. Other features -should be enabled manually. - -USE_INTERNAL_ISINF:: Workaround for Solaris platforms missing +isinf+. -DISABLE_INVALID_NUMBERS:: Recommended on platforms where +strtod+ / - +sprintf+ are not POSIX compliant (eg, Windows MinGW). Prevents - +cjson.encode_invalid_numbers+ and +cjson.decode_invalid_numbers+ from - being enabled. However, +cjson.encode_invalid_numbers+ may still be - set to +"null"+. When using the Lua CJSON built-in floating point - conversion this option is unnecessary and is ignored. - - -Built-in floating point conversion -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Lua CJSON may be built with David Gay's -http://www.netlib.org/fp/[floating point conversion routines]. This can -increase overall performance by up to 50% on some platforms when -converting a large amount of numeric data. However, this option reduces -portability and is disabled by default. - -USE_INTERNAL_FPCONV:: Enable internal number conversion routines. -IEEE_BIG_ENDIAN:: Must be set on big endian architectures. -MULTIPLE_THREADS:: Must be set if Lua CJSON may be used in a - multi-threaded application. Requires the _pthreads_ library. - - API (Functions) --------------- @@ -170,74 +28,15 @@ Synopsis [source,lua] ------------ --- Module instantiation -local cjson = require "cjson" -local cjson2 = cjson.new() -local cjson_safe = require "cjson.safe" -- Translate Lua value to/from JSON text = cjson.encode(value) value = cjson.decode(text) --- Get and/or set Lua CJSON configuration -setting = cjson.decode_invalid_numbers([setting]) -setting = cjson.encode_invalid_numbers([setting]) -keep = cjson.encode_keep_buffer([keep]) -depth = cjson.encode_max_depth([depth]) -depth = cjson.decode_max_depth([depth]) -convert, ratio, safe = cjson.encode_sparse_array([convert[, ratio[, safe]]]) ------------- - Module Instantiation ~~~~~~~~~~~~~~~~~~~~ -[source,lua] ------------- -local cjson = require "cjson" -local cjson2 = cjson.new() -local cjson_safe = require "cjson.safe" ------------- - -Import Lua CJSON via the Lua +require+ function. Lua CJSON does not -register a global module table. - -The +cjson+ module will throw an error during JSON conversion if any -invalid data is encountered. Refer to <> -and <> for details. - -The +cjson.safe+ module behaves identically to the +cjson+ module, -except when errors are encountered during JSON conversion. On error, the -+cjson_safe.encode+ and +cjson_safe.decode+ functions will return -+nil+ followed by the error message. - -+cjson.new+ can be used to instantiate an independent copy of the Lua -CJSON module. The new module has a separate persistent encoding buffer, -and default settings. - -Lua CJSON can support Lua implementations using multiple preemptive -threads within a single Lua state provided the persistent encoding -buffer is not shared. This can be achieved by one of the following -methods: - -- Disabling the persistent encoding buffer with - <> -- Ensuring each thread calls <> separately (ie, - treat +cjson.encode+ as non-reentrant). -- Using a separate +cjson+ module table per preemptive thread - (+cjson.new+) - -[NOTE] -Lua CJSON uses +strtod+ and +snprintf+ to perform numeric conversion as -they are usually well supported, fast and bug free. However, these -functions require a workaround for JSON encoding/parsing under locales -using a comma decimal separator. Lua CJSON detects the current locale -during instantiation to determine and automatically implement the -workaround if required. Lua CJSON should be reinitialised via -+cjson.new+ if the locale of the current process changes. Using a -different locale per thread is not supported. - - decode ~~~~~~ @@ -276,58 +75,6 @@ numeric key will be stored as a Lua +string+. Any subsequent code assuming type +number+ may break. -[[decode_invalid_numbers]] -decode_invalid_numbers -~~~~~~~~~~~~~~~~~~~~~~ - -[source,lua] ------------- -setting = cjson.decode_invalid_numbers([setting]) --- "setting" must be a boolean. Default: true. ------------- - -Lua CJSON may generate an error when trying to decode numbers not -supported by the JSON specification. _Invalid numbers_ are defined as: - -- infinity -- NaN -- hexadecimal - -Available settings: - -+true+:: Accept and decode _invalid numbers_. This is the default - setting. -+false+:: Throw an error when _invalid numbers_ are encountered. - -The current setting is always returned, and is only updated when an -argument is provided. - - -[[decode_max_depth]] -decode_max_depth -~~~~~~~~~~~~~~~~ - -[source,lua] ------------- -depth = cjson.decode_max_depth([depth]) --- "depth" must be a positive integer. Default: 1000. ------------- - -Lua CJSON will generate an error when parsing deeply nested JSON once -the maximum array/object depth has been exceeded. This check prevents -unnecessarily complicated JSON from slowing down the application, or -crashing the application due to lack of process stack space. - -An error may be generated before the depth limit is hit if Lua is unable -to allocate more objects on the Lua stack. - -By default, Lua CJSON will reject JSON with arrays and/or objects nested -more than 1000 levels deep. - -The current setting is always returned, and is only updated when an -argument is provided. - - [[encode]] encode ~~~~~~ @@ -412,201 +159,10 @@ By default, encoding the following Lua values will generate errors: - Tables nested more than 1000 levels deep - Excessively sparse Lua arrays -These defaults can be changed with: - -- <> -- <> -- <> - .Example: Encoding [source,lua] value = { true, { foo = "bar" } } json_text = cjson.encode(value) -- Returns: '[true,{"foo":"bar"}]' - -[[encode_invalid_numbers]] -encode_invalid_numbers -~~~~~~~~~~~~~~~~~~~~~~ -[source,lua] ------------- -setting = cjson.encode_invalid_numbers([setting]) --- "setting" must a boolean or "null". Default: false. ------------- - -Lua CJSON may generate an error when encoding floating point numbers not -supported by the JSON specification (_invalid numbers_): - -- infinity -- NaN - -Available settings: - -+true+:: Allow _invalid numbers_ to be encoded using the Javascript - compatible values +NaN+ and +Infinity+. This will generate - non-standard JSON, but these values are supported by some libraries. -+"null"+:: Encode _invalid numbers_ as a JSON +null+ value. This allows - infinity and NaN to be encoded into valid JSON. -+false+:: Throw an error when attempting to encode _invalid numbers_. - This is the default setting. - -The current setting is always returned, and is only updated when an -argument is provided. - - -[[encode_keep_buffer]] -encode_keep_buffer -~~~~~~~~~~~~~~~~~~ - -[source,lua] ------------- -keep = cjson.encode_keep_buffer([keep]) --- "keep" must be a boolean. Default: true. ------------- - -Lua CJSON can reuse the JSON encoding buffer to improve performance. - -Available settings: - -+true+:: The buffer will grow to the largest size required and is not - freed until the Lua CJSON module is garbage collected. This is the - default setting. -+false+:: Free the encode buffer after each call to +cjson.encode+. - -The current setting is always returned, and is only updated when an -argument is provided. - - -[[encode_max_depth]] -encode_max_depth -~~~~~~~~~~~~~~~~ - -[source,lua] ------------- -depth = cjson.encode_max_depth([depth]) --- "depth" must be a positive integer. Default: 1000. ------------- - -Once the maximum table depth has been exceeded Lua CJSON will generate -an error. This prevents a deeply nested or recursive data structure from -crashing the application. - -By default, Lua CJSON will generate an error when trying to encode data -structures with more than 1000 nested tables. - -The current setting is always returned, and is only updated when an -argument is provided. - -.Example: Recursive Lua table -[source,lua] -a = {}; a[1] = a - - -[[encode_number_precision]] -encode_number_precision -~~~~~~~~~~~~~~~~~~~~~~~ - -[source,lua] ------------- -precision = cjson.encode_number_precision([precision]) --- "precision" must be an integer between 1 and 14. Default: 14. ------------- - -The amount of significant digits returned by Lua CJSON when encoding -numbers can be changed to balance accuracy versus performance. For data -structures containing many numbers, setting -+cjson.encode_number_precision+ to a smaller integer, for example +3+, -can improve encoding performance by up to 50%. - -By default, Lua CJSON will output 14 significant digits when converting -a number to text. - -The current setting is always returned, and is only updated when an -argument is provided. - - -[[encode_sparse_array]] -encode_sparse_array -~~~~~~~~~~~~~~~~~~~ - -[source,lua] ------------- -convert, ratio, safe = cjson.encode_sparse_array([convert[, ratio[, safe]]]) --- "convert" must be a boolean. Default: false. --- "ratio" must be a positive integer. Default: 2. --- "safe" must be a positive integer. Default: 10. ------------- - -Lua CJSON classifies a Lua table into one of three kinds when encoding a -JSON array. This is determined by the number of values missing from the -Lua array as follows: - -Normal:: All values are available. -Sparse:: At least 1 value is missing. -Excessively sparse:: The number of values missing exceeds the configured - ratio. - -Lua CJSON encodes sparse Lua arrays as JSON arrays using JSON +null+ for -the missing entries. - -An array is excessively sparse when all the following conditions are -met: - -- +ratio+ > +0+ -- _maximum_index_ > +safe+ -- _maximum_index_ > _item_count_ * +ratio+ - -Lua CJSON will never consider an array to be _excessively sparse_ when -+ratio+ = +0+. The +safe+ limit ensures that small Lua arrays are always -encoded as sparse arrays. - -By default, attempting to encode an _excessively sparse_ array will -generate an error. If +convert+ is set to +true+, _excessively sparse_ -arrays will be converted to a JSON object. - -The current settings are always returned. A particular setting is only -changed when the argument is provided (non-++nil++). - -.Example: Encoding a sparse array -[source,lua] -cjson.encode({ [3] = "data" }) --- Returns: '[null,null,"data"]' - -.Example: Enabling conversion to a JSON object -[source,lua] -cjson.encode_sparse_array(true) -cjson.encode({ [1000] = "excessively sparse" }) --- Returns: '{"1000":"excessively sparse"}' - - -API (Variables) ---------------- - -_NAME -~~~~~ - -The name of the Lua CJSON module (+"cjson"+). - - -_VERSION -~~~~~~~~ - -The version number of the Lua CJSON module (+"2.1devel"+). - - -null -~~~~ - -Lua CJSON decodes JSON +null+ as a Lua +lightuserdata+ NULL pointer. -+cjson.null+ is provided for comparison. - - -[sect1] -References ----------- - -- http://tools.ietf.org/html/rfc4627[RFC 4627] -- http://www.json.org/[JSON website] - - // vi:ft=asciidoc tw=72: diff --git a/app/include/user_version.h b/app/include/user_version.h index 1576fbe3..ed8a511b 100644 --- a/app/include/user_version.h +++ b/app/include/user_version.h @@ -7,6 +7,6 @@ #define NODE_VERSION_INTERNAL 0U #define NODE_VERSION "NodeMCU 0.9.5" -#define BUILD_DATE "build 20150315" +#define BUILD_DATE "build 20150317" #endif /* __USER_VERSION_H__ */ diff --git a/app/modules/cjson.c b/app/modules/cjson.c index 15c263db..8dc6bbaf 100644 --- a/app/modules/cjson.c +++ b/app/modules/cjson.c @@ -1558,7 +1558,7 @@ const LUA_REG_TYPE cjson_map[] = LUALIB_API int luaopen_cjson( lua_State *L ) { /* Initialise number conversions */ - fpconv_init(); + // fpconv_init(); // not needed for a specific cpu. if(-1==cfg_init(&_cfg)){ return luaL_error(L, "BUG: Unable to init config for cjson");; } From 09b650be415c1ed272b031fabec7d6848b492148 Mon Sep 17 00:00:00 2001 From: devsaurus Date: Tue, 17 Mar 2015 20:52:47 +0100 Subject: [PATCH 34/40] disable untested pcd8544 driver for the moment --- app/include/u8g_config.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/include/u8g_config.h b/app/include/u8g_config.h index 08885fef..007e5450 100644 --- a/app/include/u8g_config.h +++ b/app/include/u8g_config.h @@ -14,7 +14,8 @@ // Enable display drivers #define U8G_SSD1306_128x64_I2C #define U8G_SSD1306_128x64_SPI -#define U8G_PCD8544_84x48 +// untested +#undef U8G_PCD8544_84x48 #endif /* __U8G_CONFIG_H__ */ From b34a8b46fec2b241c837ea35c6a6cf1edc0fb46a Mon Sep 17 00:00:00 2001 From: devsaurus Date: Tue, 17 Mar 2015 21:03:15 +0100 Subject: [PATCH 35/40] use RES pin for examples --- README.md | 7 ++++--- lua_examples/u8glib/u8g_bitmaps.lua | 9 +++++---- lua_examples/u8glib/u8g_graphics_test.lua | 9 +++++---- lua_examples/u8glib/u8g_rotation.lua | 9 +++++---- 4 files changed, 19 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 987c2891..aeb06675 100644 --- a/README.md +++ b/README.md @@ -398,9 +398,10 @@ disp = u8g.ssd1306_128x64_i2c(sla) ``` SSD1306 via SPI: ```lua -cs = 8 -- GPIO15, pull-down 10k to GND -dc = 4 -- GPIO2 -disp = u8g.ssd1306_128x64_spi(cs, dc) +cs = 8 -- GPIO15, pull-down 10k to GND +dc = 4 -- GPIO2 +res = 0 -- GPIO16, RES is optional YMMV +disp = u8g.ssd1306_128x64_spi(cs, dc, res) ``` This object provides all of u8glib's methods to control the display. diff --git a/lua_examples/u8glib/u8g_bitmaps.lua b/lua_examples/u8glib/u8g_bitmaps.lua index 92b5f7a0..1b388e41 100644 --- a/lua_examples/u8glib/u8g_bitmaps.lua +++ b/lua_examples/u8glib/u8g_bitmaps.lua @@ -14,12 +14,13 @@ function init_spi_display() -- Hardware SPI CLK = GPIO14 -- Hardware SPI MOSI = GPIO13 -- Hardware SPI MISO = GPIO12 (not used) - -- CS and D/C can be assigned freely to available GPIOs - cs = 8 -- GPIO15, pull-down 10k to GND - dc = 4 -- GPIO2 + -- CS, D/C, and RES can be assigned freely to available GPIOs + cs = 8 -- GPIO15, pull-down 10k to GND + dc = 4 -- GPIO2 + res = 0 -- GPIO16 spi.setup(1, spi.MASTER, spi.CPOL_LOW, spi.CPHA_LOW, spi.DATABITS_8, 0) - disp = u8g.ssd1306_128x64_spi(cs, dc) + disp = u8g.ssd1306_128x64_spi(cs, dc, res) end diff --git a/lua_examples/u8glib/u8g_graphics_test.lua b/lua_examples/u8glib/u8g_graphics_test.lua index 8a721e99..27c7a346 100644 --- a/lua_examples/u8glib/u8g_graphics_test.lua +++ b/lua_examples/u8glib/u8g_graphics_test.lua @@ -14,12 +14,13 @@ function init_spi_display() -- Hardware SPI CLK = GPIO14 -- Hardware SPI MOSI = GPIO13 -- Hardware SPI MISO = GPIO12 (not used) - -- CS and D/C can be assigned freely to available GPIOs - cs = 8 -- GPIO15, pull-down 10k to GND - dc = 4 -- GPIO2 + -- CS, D/C, and RES can be assigned freely to available GPIOs + cs = 8 -- GPIO15, pull-down 10k to GND + dc = 4 -- GPIO2 + res = 0 -- GPIO16 spi.setup(1, spi.MASTER, spi.CPOL_LOW, spi.CPHA_LOW, spi.DATABITS_8, 0) - disp = u8g.ssd1306_128x64_spi(cs, dc) + disp = u8g.ssd1306_128x64_spi(cs, dc, res) end diff --git a/lua_examples/u8glib/u8g_rotation.lua b/lua_examples/u8glib/u8g_rotation.lua index 5b20fca8..6d81c779 100644 --- a/lua_examples/u8glib/u8g_rotation.lua +++ b/lua_examples/u8glib/u8g_rotation.lua @@ -14,12 +14,13 @@ function init_spi_display() -- Hardware SPI CLK = GPIO14 -- Hardware SPI MOSI = GPIO13 -- Hardware SPI MISO = GPIO12 (not used) - -- CS and D/C can be assigned freely to available GPIOs - cs = 8 -- GPIO15, pull-down 10k to GND - dc = 4 -- GPIO2 + -- CS, D/C, and RES can be assigned freely to available GPIOs + cs = 8 -- GPIO15, pull-down 10k to GND + dc = 4 -- GPIO2 + res = 0 -- GPIO16 spi.setup(1, spi.MASTER, spi.CPOL_LOW, spi.CPHA_LOW, spi.DATABITS_8, 0) - disp = u8g.ssd1306_128x64_spi(cs, dc) + disp = u8g.ssd1306_128x64_spi(cs, dc, res) end From a70709268b5f6e386b7552e9dde7b9a646d1d6f1 Mon Sep 17 00:00:00 2001 From: funshine Date: Wed, 18 Mar 2015 14:22:17 +0800 Subject: [PATCH 36/40] merge cjson and dev to master, make a stable release --- README.md | 52 +++++++++++++++++--------------------- app/include/user_modules.h | 2 +- app/include/user_version.h | 2 +- app/libc/c_string.h | 2 +- examples/fragment.lua | 2 +- 5 files changed, 27 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index 7d9a8d63..90ec76e3 100644 --- a/README.md +++ b/README.md @@ -23,8 +23,9 @@ Tencent QQ group: 309957875
- Easy to access wireless router - Based on Lua 5.1.4 (without *debug, os* module.) - Event-Drive programming preferred. -- Build-in file, timer, pwm, i2c, spi, 1-wire, net, mqtt, coap, gpio, wifi, adc, uart and system api. +- Build-in json, file, timer, pwm, i2c, spi, 1-wire, net, mqtt, coap, gpio, wifi, adc, uart and system api. - GPIO pin re-mapped, use the index to access gpio, i2c, pwm. +- Both Integer(less memory usage) and Float version firmware provided. # To Do List (pull requests are very welcomed) - loadable c module @@ -35,9 +36,13 @@ Tencent QQ group: 309957875
- cross compiler (done) # Change log +2015-03-18
+update u8glib.
+merge everything to master. + 2015-03-17
add cjson module, only cjson.encode() and cjson.decode() is implemented.
-read doc [here](https://github.com/nodemcu/nodemcu-firmware/blob/json/app/cjson/manual.txt) +read doc [here](https://github.com/nodemcu/nodemcu-firmware/blob/master/app/cjson/manual.txt) 2015-03-15
bugs fixed: #239, #273.
@@ -63,32 +68,6 @@ raise internal LUA_BUFFERSIZE from 1024 to 4096.
lua require("mod") will load "mod.lc" file first if exist.
build latest pre_build bin. -2015-02-12
-fix float print.
-update spiffs, add file.rename api to file module.
-fix some file system bug. need more tests.
-add support to 8Mbyte, 16Mbyte flash.
-remove node.led() and node.key() api.
-some update to lua_modules and examples.
-build latest pre_build bin. - -2015-01-27
-support floating point LUA.
-use macro LUA_NUMBER_INTEGRAL in user_config.h control this feature.
-LUA_NUMBER_INTEGRAL to disable floating point support,
-// LUA_NUMBER_INTEGRAL to enable floating point support.
-fix tmr.time(). #132
-fix filesystem length. #113
-fix ssl reboots. #134
-build pre_build bin. - -2015-01-26
-applied sdk095_patch1 to sdk 0.9.5.
-added LUA examples and modules [by dvv](https://github.com/dvv).
-added node.readvdd33() API [by alonewolfx2](https://github.com/alonewolfx2).
-build pre_build bin. - - [more change log](https://github.com/nodemcu/nodemcu-firmware/wiki)
##GPIO NEW TABLE ( Build 20141219 and later) @@ -154,9 +133,10 @@ build pre_build bin. #define LUA_USE_MODULES_OW #define LUA_USE_MODULES_BIT #define LUA_USE_MODULES_MQTT -// #define LUA_USE_MODULES_COAP // need about 4k more ram for now +// #define LUA_USE_MODULES_COAP #define LUA_USE_MODULES_U8G #define LUA_USE_MODULES_WS2812 +#define LUA_USE_MODULES_CJSON #endif /* LUA_USE_MODULES */ ``` @@ -470,3 +450,17 @@ cc = coap.Client() cc:get(coap.CON, "coap://192.168.18.100:5683/.well-known/core") cc:post(coap.NON, "coap://192.168.18.100:5683/", "Hello") ``` + +####cjson + +```lua +-- Translate Lua value to/from JSON +-- text = cjson.encode(value) +-- value = cjson.decode(text) +json_text = '[ true, { "foo": "bar" } ]' +value = cjson.decode(json_text) +-- Returns: { true, { foo = "bar" } } +value = { true, { foo = "bar" } } +json_text = cjson.encode(value) +-- Returns: '[true,{"foo":"bar"}]' +``` diff --git a/app/include/user_modules.h b/app/include/user_modules.h index e72d6587..d8ddc654 100644 --- a/app/include/user_modules.h +++ b/app/include/user_modules.h @@ -4,7 +4,7 @@ #define LUA_USE_BUILTIN_STRING // for string.xxx() #define LUA_USE_BUILTIN_TABLE // for table.xxx() #define LUA_USE_BUILTIN_COROUTINE // for coroutine.xxx() -// #define LUA_USE_BUILTIN_MATH // for math.xxx(), partially work +#define LUA_USE_BUILTIN_MATH // for math.xxx(), partially work // #define LUA_USE_BUILTIN_IO // for io.xxx(), partially work // #define LUA_USE_BUILTIN_OS // for os.xxx(), not work diff --git a/app/include/user_version.h b/app/include/user_version.h index ed8a511b..bc64f36d 100644 --- a/app/include/user_version.h +++ b/app/include/user_version.h @@ -7,6 +7,6 @@ #define NODE_VERSION_INTERNAL 0U #define NODE_VERSION "NodeMCU 0.9.5" -#define BUILD_DATE "build 20150317" +#define BUILD_DATE "build 20150318" #endif /* __USER_VERSION_H__ */ diff --git a/app/libc/c_string.h b/app/libc/c_string.h index 3061ad06..eae05f73 100644 --- a/app/libc/c_string.h +++ b/app/libc/c_string.h @@ -25,7 +25,7 @@ #define c_strncmp os_strncmp #define c_strncpy os_strncpy // #define c_strstr os_strstr -#define c_strncasecmp strncasecmp +#define c_strncasecmp c_strncmp #define c_strstr strstr #define c_strncat strncat diff --git a/examples/fragment.lua b/examples/fragment.lua index a56e423a..67aa2c37 100644 --- a/examples/fragment.lua +++ b/examples/fragment.lua @@ -1,7 +1,7 @@ pwm.setup(0,500,50) pwm.setup(1,500,50) pwm.setup(2,500,50) pwm.start(0) pwm.start(1) pwm.start(2) function led(r,g,b) pwm.setduty(0,g) pwm.setduty(1,b) pwm.setduty(2,r) end -wifi.station.autoconnect(1) +wifi.sta.autoconnect(1) a=0 tmr.alarm( 1000,1,function() if a==0 then a=1 led(50,50,50) else a=0 led(0,0,0) end end) From 3aead6d2301d4f1d78f1fa9eff9553544b49f63c Mon Sep 17 00:00:00 2001 From: funshine Date: Wed, 18 Mar 2015 17:01:01 +0800 Subject: [PATCH 37/40] try to fix cjson.encode in integer only version --- app/modules/cjson.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/app/modules/cjson.c b/app/modules/cjson.c index 8dc6bbaf..47e4ab9a 100644 --- a/app/modules/cjson.c +++ b/app/modules/cjson.c @@ -45,13 +45,10 @@ #include "flash_api.h" #include "strbuf.h" -#ifdef LUA_NUMBER_INTEGRAL -#include "fpconv.h" -#else + #define FPCONV_G_FMT_BUFSIZE 32 #define fpconv_strtod c_strtod #define fpconv_init() ((void)0) -#endif #ifndef CJSON_MODNAME #define CJSON_MODNAME "cjson" @@ -774,12 +771,10 @@ static void json_append_number(lua_State *l, json_config_t *cfg, } strbuf_ensure_empty_length(json, FPCONV_G_FMT_BUFSIZE); -#ifdef LUA_NUMBER_INTEGRAL - len = fpconv_g_fmt(strbuf_empty_ptr(json), num, cfg->encode_number_precision); -#else - c_sprintf(strbuf_empty_ptr(json), "%.14g", num); + // len = fpconv_g_fmt(strbuf_empty_ptr(json), num, cfg->encode_number_precision); + c_sprintf(strbuf_empty_ptr(json), LUA_NUMBER_FMT, num); len = c_strlen(strbuf_empty_ptr(json)); -#endif + strbuf_extend_length(json, len); } From 3d14530b8057e6eb9b2076b5bca02c56505c8a56 Mon Sep 17 00:00:00 2001 From: funshine Date: Wed, 18 Mar 2015 19:37:47 +0800 Subject: [PATCH 38/40] fix cjson.encode() when in integer version --- app/cjson/fpconv.c | 3 ++- app/modules/cjson.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/cjson/fpconv.c b/app/cjson/fpconv.c index a5ce8125..f199b459 100644 --- a/app/cjson/fpconv.c +++ b/app/cjson/fpconv.c @@ -35,6 +35,7 @@ #include "fpconv.h" +#if 0 /* Lua CJSON assumes the locale is the same for all threads within a * process and doesn't change after initialisation. * @@ -203,6 +204,6 @@ void fpconv_init() { fpconv_update_locale(); } - +#endif /* vi:ai et sw=4 ts=4: */ diff --git a/app/modules/cjson.c b/app/modules/cjson.c index 47e4ab9a..22afdf70 100644 --- a/app/modules/cjson.c +++ b/app/modules/cjson.c @@ -772,7 +772,7 @@ static void json_append_number(lua_State *l, json_config_t *cfg, strbuf_ensure_empty_length(json, FPCONV_G_FMT_BUFSIZE); // len = fpconv_g_fmt(strbuf_empty_ptr(json), num, cfg->encode_number_precision); - c_sprintf(strbuf_empty_ptr(json), LUA_NUMBER_FMT, num); + c_sprintf(strbuf_empty_ptr(json), LUA_NUMBER_FMT, (LUA_NUMBER)num); len = c_strlen(strbuf_empty_ptr(json)); strbuf_extend_length(json, len); From 2d254bd47ced50a23409faa4ad8871d1e29c19cb Mon Sep 17 00:00:00 2001 From: devsaurus Date: Wed, 18 Mar 2015 22:33:16 +0100 Subject: [PATCH 39/40] use correct function for stop in U8G_COM_MSG_WRITE_BYTE --- app/modules/u8g.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/modules/u8g.c b/app/modules/u8g.c index 59cb20d9..fc39803b 100644 --- a/app/modules/u8g.c +++ b/app/modules/u8g.c @@ -916,7 +916,7 @@ uint8_t u8g_com_esp8266_ssd_i2c_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, voi case U8G_COM_MSG_WRITE_BYTE: //u8g->pin_list[U8G_PI_SET_A0] = 1; if ( u8g_com_esp8266_ssd_start_sequence(u8g) == 0 ) - return platform_i2c_stop( ESP_I2C_ID ), 0; + return platform_i2c_send_stop( ESP_I2C_ID ), 0; // ignore return value -> tolerate missing ACK if ( platform_i2c_send_byte( ESP_I2C_ID, arg_val) == 0 ) ; //return platform_i2c_send_stop( ESP_I2C_ID ), 0; From 4332b21ef3eefd1c775f6acde942738cb80e0f4c Mon Sep 17 00:00:00 2001 From: funshine Date: Fri, 20 Mar 2015 19:42:10 +0800 Subject: [PATCH 40/40] minor fix --- app/smart/smart.c | 10 +++++++++- app/smart/smart.h | 34 ++++++++++++++++++++++++++++++++++ app/spiffs/spiffs_nucleus.h | 4 +--- 3 files changed, 44 insertions(+), 4 deletions(-) diff --git a/app/smart/smart.c b/app/smart/smart.c index b8114836..c362191a 100644 --- a/app/smart/smart.c +++ b/app/smart/smart.c @@ -127,11 +127,19 @@ int smart_check(uint8_t *nibble, uint16_t len, uint8_t *dst, uint8_t *got){ return res; } -void detect(uint8 *buf, uint16 len){ +void detect(uint8 *arg, uint16 len){ uint16_t seq; int16_t seq_delta = 0; uint16_t byte_num = 0, bit_num = 0; int16_t c = 0; + uint8 *buf = NULL; + if( len == 12 ){ + return; + } else if (len >= 64){ + buf = arg + sizeof(struct RxControl); + } else { + return; + } if( ( (buf[0]) & TYPE_SUBTYPE_MASK) != TYPE_SUBTYPE_QOS_DATA){ return; } diff --git a/app/smart/smart.h b/app/smart/smart.h index a702830a..434aecbe 100644 --- a/app/smart/smart.h +++ b/app/smart/smart.h @@ -59,6 +59,40 @@ extern "C" { #define STATION_CHECK_TIME (2*1000) +struct RxControl{ + signed rssi:8;//表示该包的信号强度 + unsigned rate:4; + unsigned is_group:1; + unsigned:1; + unsigned sig_mode:2;//表示该包是否是11n 的包,0 表示非11n,非0 表示11n + unsigned legacy_length:12;//如果不是11n 的包,它表示包的长度 + unsigned damatch0:1; + unsigned damatch1:1; + unsigned bssidmatch0:1; + unsigned bssidmatch1:1; + unsigned MCS:7;//如果是11n 的包,它表示包的调制编码序列,有效值:0-76 + unsigned CWB:1;//如果是11n 的包,它表示是否为HT40 的包 + unsigned HT_length:16;//如果是11n 的包,它表示包的长度 + unsigned Smoothing:1; + unsigned Not_Sounding:1; + unsigned:1; + unsigned Aggregation:1; + unsigned STBC:2; + unsigned FEC_CODING:1;//如果是11n 的包,它表示是否为LDPC 的包 + unsigned SGI:1; + unsigned rxend_state:8; + unsigned ampdu_cnt:8; + unsigned channel:4;//表示该包所在的信道 + unsigned:12; +}; + +struct sniffer_buf{ + struct RxControl rx_ctrl; // 12-bytes + u8 buf[48];//包含ieee80211 包头 + u16 cnt;//包的个数 + u16 len[1];//包的长度 +}; + struct _my_addr_map { uint8 addr[ADDR_LENGTH*3]; uint8_t addr_len; diff --git a/app/spiffs/spiffs_nucleus.h b/app/spiffs/spiffs_nucleus.h index b4a34bcf..cc414432 100644 --- a/app/spiffs/spiffs_nucleus.h +++ b/app/spiffs/spiffs_nucleus.h @@ -395,13 +395,11 @@ typedef struct __attribute(( packed )) { // common page header spiffs_page_header p_hdr; // alignment - u8_t _align[4 - (sizeof(spiffs_page_header)&3)==0 ? 4 : (sizeof(spiffs_page_header)&3)]; + u8_t _align[4 - ((sizeof(spiffs_page_header)+sizeof(spiffs_obj_type)+SPIFFS_OBJ_NAME_LEN)&3)==0 ? 4 : ((sizeof(spiffs_page_header)+sizeof(spiffs_obj_type)+SPIFFS_OBJ_NAME_LEN)&3)]; // size of object u32_t size; // type of object spiffs_obj_type type; - // alignment2 - u8_t _align2[4 - (sizeof(spiffs_obj_type)&3)==0 ? 4 : (sizeof(spiffs_obj_type)&3)]; // name of object u8_t name[SPIFFS_OBJ_NAME_LEN]; } spiffs_page_object_ix_header;