Commit d5731dd9 authored by funshine's avatar funshine
Browse files

merge from dev

parents 4d5242e1 4332b21e
...@@ -47,6 +47,7 @@ INCLUDES += -I ../platform ...@@ -47,6 +47,7 @@ INCLUDES += -I ../platform
INCLUDES += -I ../wofs INCLUDES += -I ../wofs
INCLUDES += -I ../spiffs INCLUDES += -I ../spiffs
INCLUDES += -I ../smart INCLUDES += -I ../smart
INCLUDES += -I ../cjson
PDIR := ../$(PDIR) PDIR := ../$(PDIR)
sinclude $(PDIR)Makefile sinclude $(PDIR)Makefile
...@@ -79,6 +79,9 @@ LUALIB_API int ( luaopen_file )( lua_State *L ); ...@@ -79,6 +79,9 @@ LUALIB_API int ( luaopen_file )( lua_State *L );
#define AUXLIB_OW "ow" #define AUXLIB_OW "ow"
LUALIB_API int ( luaopen_ow )( lua_State *L ); LUALIB_API int ( luaopen_ow )( lua_State *L );
#define AUXLIB_CJSON "cjson"
LUALIB_API int ( luaopen_ow )( lua_State *L );
// Helper macros // Helper macros
#define MOD_CHECK_ID( mod, id )\ #define MOD_CHECK_ID( mod, id )\
if( !platform_ ## mod ## _exists( id ) )\ if( !platform_ ## mod ## _exists( id ) )\
...@@ -98,5 +101,9 @@ LUALIB_API int ( luaopen_ow )( lua_State *L ); ...@@ -98,5 +101,9 @@ LUALIB_API int ( luaopen_ow )( lua_State *L );
lua_pushnumber( L, val );\ lua_pushnumber( L, val );\
lua_setfield( L, -2, name ) lua_setfield( L, -2, name )
#define MOD_REG_LUDATA( L, name, val )\
lua_pushlightuserdata( L, val );\
lua_setfield( L, -2, name )
#endif #endif
/* Lua CJSON - JSON support for Lua
*
* Copyright (c) 2010-2012 Mark Pulford <mark@kyne.com.au>
*
* 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 <assert.h>
#include "c_string.h"
#include "c_math.h"
#include "c_limits.h"
#include "lua.h"
#include "lauxlib.h"
#include "flash_api.h"
#include "strbuf.h"
#define FPCONV_G_FMT_BUFSIZE 32
#define fpconv_strtod c_strtod
#define fpconv_init() ((void)0)
#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 0
#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;
#if 0
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
};
#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]; // 256*4 = 1024 byte
// 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;
#if 0
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,
};
#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
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){
if(-1==strbuf_init(&cfg->encode_buf, 0))
return luaL_error(l, "not enough memory");
}
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 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;
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
if(-1==strbuf_init(&cfg->encode_buf, 0)){
NODE_ERR("not enough memory\n");
return -1;
}
#endif
return 0;
}
/* ===== 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){
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]);
}
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);
c_sprintf(strbuf_empty_ptr(json), LUA_NUMBER_FMT, (LUA_NUMBER)num);
len = c_strlen(strbuf_empty_ptr(json));
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;
if(-1==strbuf_init(encode_buf, 0))
return luaL_error(l, "not enough memory");
} 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 */
if(!(*json->ptr == '"')) return;
/* 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 */
}
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 (!c_strncasecmp(tmp, "inf", 3))
return 1;
if (!c_strncasecmp(tmp, "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 (!c_strncmp(json->ptr, "true", 4)) {
token->type = T_BOOLEAN;
token->value.boolean = 1;
json->ptr += 4;
return;
} else if (!c_strncmp(json->ptr, "false", 5)) {
token->type = T_BOOLEAN;
token->value.boolean = 0;
json->ptr += 5;
return;
} else if (!c_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;
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",
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);
if(json.tmp == NULL){
return luaL_error(l, "not enought memory");
}
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(); // not needed for a specific cpu.
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
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:
*/
...@@ -338,7 +338,7 @@ LUALIB_API int luaopen_file( lua_State *L ) ...@@ -338,7 +338,7 @@ LUALIB_API int luaopen_file( lua_State *L )
#if LUA_OPTIMIZE_MEMORY > 0 #if LUA_OPTIMIZE_MEMORY > 0
return 0; return 0;
#else // #if LUA_OPTIMIZE_MEMORY > 0 #else // #if LUA_OPTIMIZE_MEMORY > 0
luaL_register( L, AUXLIB_NODE, file_map ); luaL_register( L, AUXLIB_FILE, file_map );
// Add constants // Add constants
return 1; return 1;
......
...@@ -141,6 +141,13 @@ ...@@ -141,6 +141,13 @@
#define ROM_MODULES_WS2812 #define ROM_MODULES_WS2812
#endif #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 \ #define LUA_MODULES_ROM \
ROM_MODULES_GPIO \ ROM_MODULES_GPIO \
...@@ -159,7 +166,8 @@ ...@@ -159,7 +166,8 @@
ROM_MODULES_UART \ ROM_MODULES_UART \
ROM_MODULES_OW \ ROM_MODULES_OW \
ROM_MODULES_BIT \ ROM_MODULES_BIT \
ROM_MODULES_WS2812 ROM_MODULES_WS2812 \
ROM_MODULES_CJSON
#endif #endif
...@@ -25,6 +25,9 @@ ...@@ -25,6 +25,9 @@
#include "flash_fs.h" #include "flash_fs.h"
#include "user_version.h" #include "user_version.h"
#define CPU80MHZ 80
#define CPU160MHZ 160
// Lua: restart() // Lua: restart()
static int node_restart( lua_State* L ) static int node_restart( lua_State* L )
{ {
...@@ -392,6 +395,24 @@ static int node_compile( lua_State* L ) ...@@ -392,6 +395,24 @@ static int node_compile( lua_State* L )
return 0; 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 // Module function map
#define MIN_OPT_LEVEL 2 #define MIN_OPT_LEVEL 2
#include "lrodefs.h" #include "lrodefs.h"
...@@ -412,6 +433,9 @@ const LUA_REG_TYPE node_map[] = ...@@ -412,6 +433,9 @@ const LUA_REG_TYPE node_map[] =
{ LSTRKEY( "output" ), LFUNCVAL( node_output ) }, { LSTRKEY( "output" ), LFUNCVAL( node_output ) },
{ LSTRKEY( "readvdd33" ), LFUNCVAL( node_readvdd33) }, { LSTRKEY( "readvdd33" ), LFUNCVAL( node_readvdd33) },
{ LSTRKEY( "compile" ), LFUNCVAL( node_compile) }, { LSTRKEY( "compile" ), LFUNCVAL( node_compile) },
{ LSTRKEY( "CPU80MHZ" ), LNUMVAL( CPU80MHZ ) },
{ LSTRKEY( "CPU160MHZ" ), LNUMVAL( CPU160MHZ ) },
{ LSTRKEY( "setcpufreq" ), LFUNCVAL( node_setcpufreq) },
// Combined to dsleep(us, option) // Combined to dsleep(us, option)
// { LSTRKEY( "dsleepsetoption" ), LFUNCVAL( node_deepsleep_setoption) }, // { LSTRKEY( "dsleepsetoption" ), LFUNCVAL( node_deepsleep_setoption) },
#if LUA_OPTIMIZE_MEMORY > 0 #if LUA_OPTIMIZE_MEMORY > 0
...@@ -429,5 +453,5 @@ LUALIB_API int luaopen_node( lua_State *L ) ...@@ -429,5 +453,5 @@ LUALIB_API int luaopen_node( lua_State *L )
// Add constants // Add constants
return 1; return 1;
#endif // #if LUA_OPTIMIZE_MEMORY > 0 #endif // #if LUA_OPTIMIZE_MEMORY > 0
} }
// Module for U8glib // Module for U8glib
//#include "lua.h"
#include "lualib.h" #include "lualib.h"
#include "lauxlib.h" #include "lauxlib.h"
#include "platform.h" #include "platform.h"
...@@ -8,25 +7,24 @@ ...@@ -8,25 +7,24 @@
#include "lrotable.h" #include "lrotable.h"
//#include "c_string.h" //#include "c_string.h"
//#include "c_stdlib.h" #include "c_stdlib.h"
#include "u8g.h" #include "u8g.h"
typedef u8g_t lu8g_userdata_t; #include "u8g_config.h"
struct _lu8g_userdata_t
// Font look-up array
static const u8g_fntpgm_uint8_t *font_array[] =
{ {
#undef U8G_FONT_TABLE_ENTRY u8g_t u8g;
#define U8G_FONT_TABLE_ENTRY(font) u8g_ ## font , u8g_pb_t pb;
U8G_FONT_TABLE u8g_dev_t dev;
NULL
}; };
typedef struct _lu8g_userdata_t lu8g_userdata_t;
// shorthand macro for the u8g structure inside the userdata
#define LU8G (&(lud->u8g))
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 // 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) u8g_pgm_uint8_t ICACHE_FLASH_ATTR u8g_pgm_read(const u8g_pgm_uint8_t *adr)
...@@ -34,19 +32,9 @@ u8g_pgm_uint8_t ICACHE_FLASH_ATTR u8g_pgm_read(const u8g_pgm_uint8_t *adr) ...@@ -34,19 +32,9 @@ u8g_pgm_uint8_t ICACHE_FLASH_ATTR u8g_pgm_read(const u8g_pgm_uint8_t *adr)
uint32_t iadr = (uint32_t)adr; uint32_t iadr = (uint32_t)adr;
// set up pointer to 4-byte aligned memory location // set up pointer to 4-byte aligned memory location
uint32_t *ptr = (uint32_t *)(iadr & ~0x3); uint32_t *ptr = (uint32_t *)(iadr & ~0x3);
uint32_t pgm_data;
if (ptr == u8g_pgm_cached_iadr) // read 4-byte aligned
{ uint32_t pgm_data = *ptr;
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;
}
// return the correct byte within the retrieved 32bit word // return the correct byte within the retrieved 32bit word
return pgm_data >> ((iadr % 4) * 8); return pgm_data >> ((iadr % 4) * 8);
...@@ -79,7 +67,7 @@ static int lu8g_begin( lua_State *L ) ...@@ -79,7 +67,7 @@ static int lu8g_begin( lua_State *L )
if ((lud = get_lud( L )) == NULL) if ((lud = get_lud( L )) == NULL)
return 0; return 0;
u8g_Begin( lud ); u8g_Begin( LU8G );
return 0; return 0;
} }
...@@ -92,9 +80,9 @@ static int lu8g_setFont( lua_State *L ) ...@@ -92,9 +80,9 @@ static int lu8g_setFont( lua_State *L )
if ((lud = get_lud( L )) == NULL) if ((lud = get_lud( L )) == NULL)
return 0; return 0;
lua_Integer fontnr = luaL_checkinteger( L, 2 ); u8g_fntpgm_uint8_t *font = (u8g_fntpgm_uint8_t *)lua_touserdata( L, 2 );
if ((fontnr >= 0) && (fontnr < (sizeof( font_array ) / sizeof( u8g_fntpgm_uint8_t )))) if (font != NULL)
u8g_SetFont( lud, font_array[fontnr] ); u8g_SetFont( LU8G, font );
return 0; return 0;
} }
...@@ -107,7 +95,7 @@ static int lu8g_setFontRefHeightAll( lua_State *L ) ...@@ -107,7 +95,7 @@ static int lu8g_setFontRefHeightAll( lua_State *L )
if ((lud = get_lud( L )) == NULL) if ((lud = get_lud( L )) == NULL)
return 0; return 0;
u8g_SetFontRefHeightAll( lud ); u8g_SetFontRefHeightAll( LU8G );
return 0; return 0;
} }
...@@ -120,7 +108,7 @@ static int lu8g_setFontRefHeightExtendedText( lua_State *L ) ...@@ -120,7 +108,7 @@ static int lu8g_setFontRefHeightExtendedText( lua_State *L )
if ((lud = get_lud( L )) == NULL) if ((lud = get_lud( L )) == NULL)
return 0; return 0;
u8g_SetFontRefHeightExtendedText( lud ); u8g_SetFontRefHeightExtendedText( LU8G );
return 0; return 0;
} }
...@@ -133,7 +121,7 @@ static int lu8g_setFontRefHeightText( lua_State *L ) ...@@ -133,7 +121,7 @@ static int lu8g_setFontRefHeightText( lua_State *L )
if ((lud = get_lud( L )) == NULL) if ((lud = get_lud( L )) == NULL)
return 0; return 0;
u8g_SetFontRefHeightText( lud ); u8g_SetFontRefHeightText( LU8G );
return 0; return 0;
} }
...@@ -146,7 +134,7 @@ static int lu8g_setDefaultBackgroundColor( lua_State *L ) ...@@ -146,7 +134,7 @@ static int lu8g_setDefaultBackgroundColor( lua_State *L )
if ((lud = get_lud( L )) == NULL) if ((lud = get_lud( L )) == NULL)
return 0; return 0;
u8g_SetDefaultBackgroundColor( lud ); u8g_SetDefaultBackgroundColor( LU8G );
return 0; return 0;
} }
...@@ -159,7 +147,7 @@ static int lu8g_setDefaultForegroundColor( lua_State *L ) ...@@ -159,7 +147,7 @@ static int lu8g_setDefaultForegroundColor( lua_State *L )
if ((lud = get_lud( L )) == NULL) if ((lud = get_lud( L )) == NULL)
return 0; return 0;
u8g_SetDefaultForegroundColor( lud ); u8g_SetDefaultForegroundColor( LU8G );
return 0; return 0;
} }
...@@ -172,7 +160,7 @@ static int lu8g_setFontPosBaseline( lua_State *L ) ...@@ -172,7 +160,7 @@ static int lu8g_setFontPosBaseline( lua_State *L )
if ((lud = get_lud( L )) == NULL) if ((lud = get_lud( L )) == NULL)
return 0; return 0;
u8g_SetFontPosBaseline( lud ); u8g_SetFontPosBaseline( LU8G );
return 0; return 0;
} }
...@@ -185,7 +173,7 @@ static int lu8g_setFontPosBottom( lua_State *L ) ...@@ -185,7 +173,7 @@ static int lu8g_setFontPosBottom( lua_State *L )
if ((lud = get_lud( L )) == NULL) if ((lud = get_lud( L )) == NULL)
return 0; return 0;
u8g_SetFontPosBottom( lud ); u8g_SetFontPosBottom( LU8G );
return 0; return 0;
} }
...@@ -198,7 +186,7 @@ static int lu8g_setFontPosCenter( lua_State *L ) ...@@ -198,7 +186,7 @@ static int lu8g_setFontPosCenter( lua_State *L )
if ((lud = get_lud( L )) == NULL) if ((lud = get_lud( L )) == NULL)
return 0; return 0;
u8g_SetFontPosCenter( lud ); u8g_SetFontPosCenter( LU8G );
return 0; return 0;
} }
...@@ -211,7 +199,7 @@ static int lu8g_setFontPosTop( lua_State *L ) ...@@ -211,7 +199,7 @@ static int lu8g_setFontPosTop( lua_State *L )
if ((lud = get_lud( L )) == NULL) if ((lud = get_lud( L )) == NULL)
return 0; return 0;
u8g_SetFontPosTop( lud ); u8g_SetFontPosTop( LU8G );
return 0; return 0;
} }
...@@ -224,7 +212,7 @@ static int lu8g_getFontAscent( lua_State *L ) ...@@ -224,7 +212,7 @@ static int lu8g_getFontAscent( lua_State *L )
if ((lud = get_lud( L )) == NULL) if ((lud = get_lud( L )) == NULL)
return 0; return 0;
lua_pushinteger( L, u8g_GetFontAscent( lud ) ); lua_pushinteger( L, u8g_GetFontAscent( LU8G ) );
return 1; return 1;
} }
...@@ -237,7 +225,7 @@ static int lu8g_getFontDescent( lua_State *L ) ...@@ -237,7 +225,7 @@ static int lu8g_getFontDescent( lua_State *L )
if ((lud = get_lud( L )) == NULL) if ((lud = get_lud( L )) == NULL)
return 0; return 0;
lua_pushinteger( L, u8g_GetFontDescent( lud ) ); lua_pushinteger( L, u8g_GetFontDescent( LU8G ) );
return 1; return 1;
} }
...@@ -250,7 +238,7 @@ static int lu8g_getFontLineSpacing( lua_State *L ) ...@@ -250,7 +238,7 @@ static int lu8g_getFontLineSpacing( lua_State *L )
if ((lud = get_lud( L )) == NULL) if ((lud = get_lud( L )) == NULL)
return 0; return 0;
lua_pushinteger( L, u8g_GetFontLineSpacing( lud ) ); lua_pushinteger( L, u8g_GetFontLineSpacing( LU8G ) );
return 1; return 1;
} }
...@@ -263,7 +251,7 @@ static int lu8g_getMode( lua_State *L ) ...@@ -263,7 +251,7 @@ static int lu8g_getMode( lua_State *L )
if ((lud = get_lud( L )) == NULL) if ((lud = get_lud( L )) == NULL)
return 0; return 0;
lua_pushinteger( L, u8g_GetMode( lud ) ); lua_pushinteger( L, u8g_GetMode( LU8G ) );
return 1; return 1;
} }
...@@ -276,7 +264,7 @@ static int lu8g_setColorIndex( lua_State *L ) ...@@ -276,7 +264,7 @@ static int lu8g_setColorIndex( lua_State *L )
if ((lud = get_lud( L )) == NULL) if ((lud = get_lud( L )) == NULL)
return 0; return 0;
u8g_SetColorIndex( lud, luaL_checkinteger( L, 2 ) ); u8g_SetColorIndex( LU8G, luaL_checkinteger( L, 2 ) );
return 0; return 0;
} }
...@@ -289,7 +277,7 @@ static int lu8g_getColorIndex( lua_State *L ) ...@@ -289,7 +277,7 @@ static int lu8g_getColorIndex( lua_State *L )
if ((lud = get_lud( L )) == NULL) if ((lud = get_lud( L )) == NULL)
return 0; return 0;
lua_pushinteger( L, u8g_GetColorIndex( lud ) ); lua_pushinteger( L, u8g_GetColorIndex( LU8G ) );
return 1; return 1;
} }
...@@ -311,16 +299,16 @@ static int lu8g_generic_drawStr( lua_State *L, uint8_t rot ) ...@@ -311,16 +299,16 @@ static int lu8g_generic_drawStr( lua_State *L, uint8_t rot )
switch (rot) switch (rot)
{ {
case 1: 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; break;
case 2: 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; break;
case 3: 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; break;
default: default:
lua_pushinteger( L, u8g_DrawStr( lud, args[0], args[1], s ) ); lua_pushinteger( L, u8g_DrawStr( LU8G, args[0], args[1], s ) );
break; break;
} }
...@@ -371,7 +359,7 @@ static int lu8g_drawLine( lua_State *L ) ...@@ -371,7 +359,7 @@ static int lu8g_drawLine( lua_State *L )
u8g_uint_t args[4]; u8g_uint_t args[4];
lu8g_get_int_args( L, 2, 4, args ); 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; return 0;
} }
...@@ -387,7 +375,7 @@ static int lu8g_drawTriangle( lua_State *L ) ...@@ -387,7 +375,7 @@ static int lu8g_drawTriangle( lua_State *L )
u8g_uint_t args[6]; u8g_uint_t args[6];
lu8g_get_int_args( L, 2, 6, args ); 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; return 0;
} }
...@@ -403,7 +391,7 @@ static int lu8g_drawBox( lua_State *L ) ...@@ -403,7 +391,7 @@ static int lu8g_drawBox( lua_State *L )
u8g_uint_t args[4]; u8g_uint_t args[4];
lu8g_get_int_args( L, 2, 4, args ); 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; return 0;
} }
...@@ -419,7 +407,7 @@ static int lu8g_drawRBox( lua_State *L ) ...@@ -419,7 +407,7 @@ static int lu8g_drawRBox( lua_State *L )
u8g_uint_t args[5]; u8g_uint_t args[5];
lu8g_get_int_args( L, 2, 5, args ); 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; return 0;
} }
...@@ -435,7 +423,7 @@ static int lu8g_drawFrame( lua_State *L ) ...@@ -435,7 +423,7 @@ static int lu8g_drawFrame( lua_State *L )
u8g_uint_t args[4]; u8g_uint_t args[4];
lu8g_get_int_args( L, 2, 4, args ); 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; return 0;
} }
...@@ -451,7 +439,7 @@ static int lu8g_drawRFrame( lua_State *L ) ...@@ -451,7 +439,7 @@ static int lu8g_drawRFrame( lua_State *L )
u8g_uint_t args[5]; u8g_uint_t args[5];
lu8g_get_int_args( L, 2, 5, args ); 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; return 0;
} }
...@@ -469,7 +457,7 @@ static int lu8g_drawDisc( lua_State *L ) ...@@ -469,7 +457,7 @@ static int lu8g_drawDisc( lua_State *L )
u8g_uint_t opt = luaL_optinteger( L, (1+3) + 1, U8G_DRAW_ALL ); 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; return 0;
} }
...@@ -487,7 +475,7 @@ static int lu8g_drawCircle( lua_State *L ) ...@@ -487,7 +475,7 @@ static int lu8g_drawCircle( lua_State *L )
u8g_uint_t opt = luaL_optinteger( L, (1+3) + 1, U8G_DRAW_ALL ); 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; return 0;
} }
...@@ -505,7 +493,7 @@ static int lu8g_drawEllipse( lua_State *L ) ...@@ -505,7 +493,7 @@ static int lu8g_drawEllipse( lua_State *L )
u8g_uint_t opt = luaL_optinteger( L, (1+4) + 1, U8G_DRAW_ALL ); 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; return 0;
} }
...@@ -523,7 +511,7 @@ static int lu8g_drawFilledEllipse( lua_State *L ) ...@@ -523,7 +511,7 @@ static int lu8g_drawFilledEllipse( lua_State *L )
u8g_uint_t opt = luaL_optinteger( L, (1+4) + 1, U8G_DRAW_ALL ); 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; return 0;
} }
...@@ -539,7 +527,7 @@ static int lu8g_drawPixel( lua_State *L ) ...@@ -539,7 +527,7 @@ static int lu8g_drawPixel( lua_State *L )
u8g_uint_t args[2]; u8g_uint_t args[2];
lu8g_get_int_args( L, 2, 2, args ); lu8g_get_int_args( L, 2, 2, args );
u8g_DrawPixel( lud, args[0], args[1] ); u8g_DrawPixel( LU8G, args[0], args[1] );
return 0; return 0;
} }
...@@ -555,7 +543,7 @@ static int lu8g_drawHLine( lua_State *L ) ...@@ -555,7 +543,7 @@ static int lu8g_drawHLine( lua_State *L )
u8g_uint_t args[3]; u8g_uint_t args[3];
lu8g_get_int_args( L, 2, 3, args ); 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; return 0;
} }
...@@ -571,7 +559,47 @@ static int lu8g_drawVLine( lua_State *L ) ...@@ -571,7 +559,47 @@ static int lu8g_drawVLine( lua_State *L )
u8g_uint_t args[3]; u8g_uint_t args[3];
lu8g_get_int_args( L, 2, 3, args ); 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;
}
// 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( LU8G, args[0], args[1], args[2], args[3], (const uint8_t *)xbm_data );
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( LU8G, args[0], args[1], args[2], args[3], (const uint8_t *)bm_data );
return 0; return 0;
} }
...@@ -584,7 +612,7 @@ static int lu8g_setScale2x2( lua_State *L ) ...@@ -584,7 +612,7 @@ static int lu8g_setScale2x2( lua_State *L )
if ((lud = get_lud( L )) == NULL) if ((lud = get_lud( L )) == NULL)
return 0; return 0;
u8g_SetScale2x2( lud ); u8g_SetScale2x2( LU8G );
return 0; return 0;
} }
...@@ -597,7 +625,7 @@ static int lu8g_undoScale( lua_State *L ) ...@@ -597,7 +625,7 @@ static int lu8g_undoScale( lua_State *L )
if ((lud = get_lud( L )) == NULL) if ((lud = get_lud( L )) == NULL)
return 0; return 0;
u8g_UndoScale( lud ); u8g_UndoScale( LU8G );
return 0; return 0;
} }
...@@ -610,7 +638,7 @@ static int lu8g_firstPage( lua_State *L ) ...@@ -610,7 +638,7 @@ static int lu8g_firstPage( lua_State *L )
if ((lud = get_lud( L )) == NULL) if ((lud = get_lud( L )) == NULL)
return 0; return 0;
u8g_FirstPage( lud ); u8g_FirstPage( LU8G );
return 0; return 0;
} }
...@@ -623,7 +651,7 @@ static int lu8g_nextPage( lua_State *L ) ...@@ -623,7 +651,7 @@ static int lu8g_nextPage( lua_State *L )
if ((lud = get_lud( L )) == NULL) if ((lud = get_lud( L )) == NULL)
return 0; return 0;
lua_pushboolean( L, u8g_NextPage( lud ) ); lua_pushboolean( L, u8g_NextPage( LU8G ) );
return 1; return 1;
} }
...@@ -636,7 +664,7 @@ static int lu8g_sleepOn( lua_State *L ) ...@@ -636,7 +664,7 @@ static int lu8g_sleepOn( lua_State *L )
if ((lud = get_lud( L )) == NULL) if ((lud = get_lud( L )) == NULL)
return 0; return 0;
u8g_SleepOn( lud ); u8g_SleepOn( LU8G );
return 0; return 0;
} }
...@@ -649,7 +677,7 @@ static int lu8g_sleepOff( lua_State *L ) ...@@ -649,7 +677,7 @@ static int lu8g_sleepOff( lua_State *L )
if ((lud = get_lud( L )) == NULL) if ((lud = get_lud( L )) == NULL)
return 0; return 0;
u8g_SleepOff( lud ); u8g_SleepOff( LU8G );
return 0; return 0;
} }
...@@ -662,7 +690,7 @@ static int lu8g_setRot90( lua_State *L ) ...@@ -662,7 +690,7 @@ static int lu8g_setRot90( lua_State *L )
if ((lud = get_lud( L )) == NULL) if ((lud = get_lud( L )) == NULL)
return 0; return 0;
u8g_SetRot90( lud ); u8g_SetRot90( LU8G );
return 0; return 0;
} }
...@@ -675,7 +703,7 @@ static int lu8g_setRot180( lua_State *L ) ...@@ -675,7 +703,7 @@ static int lu8g_setRot180( lua_State *L )
if ((lud = get_lud( L )) == NULL) if ((lud = get_lud( L )) == NULL)
return 0; return 0;
u8g_SetRot180( lud ); u8g_SetRot180( LU8G );
return 0; return 0;
} }
...@@ -688,7 +716,7 @@ static int lu8g_setRot270( lua_State *L ) ...@@ -688,7 +716,7 @@ static int lu8g_setRot270( lua_State *L )
if ((lud = get_lud( L )) == NULL) if ((lud = get_lud( L )) == NULL)
return 0; return 0;
u8g_SetRot270( lud ); u8g_SetRot270( LU8G );
return 0; return 0;
} }
...@@ -701,7 +729,7 @@ static int lu8g_undoRotation( lua_State *L ) ...@@ -701,7 +729,7 @@ static int lu8g_undoRotation( lua_State *L )
if ((lud = get_lud( L )) == NULL) if ((lud = get_lud( L )) == NULL)
return 0; return 0;
u8g_UndoRotation( lud ); u8g_UndoRotation( LU8G );
return 0; return 0;
} }
...@@ -714,7 +742,7 @@ static int lu8g_getWidth( lua_State *L ) ...@@ -714,7 +742,7 @@ static int lu8g_getWidth( lua_State *L )
if ((lud = get_lud( L )) == NULL) if ((lud = get_lud( L )) == NULL)
return 0; return 0;
lua_pushinteger( L, u8g_GetWidth( lud ) ); lua_pushinteger( L, u8g_GetWidth( LU8G ) );
return 1; return 1;
} }
...@@ -727,7 +755,7 @@ static int lu8g_getHeight( lua_State *L ) ...@@ -727,7 +755,7 @@ static int lu8g_getHeight( lua_State *L )
if ((lud = get_lud( L )) == NULL) if ((lud = get_lud( L )) == NULL)
return 0; return 0;
lua_pushinteger( L, u8g_GetHeight( lud ) ); lua_pushinteger( L, u8g_GetHeight( LU8G ) );
return 1; return 1;
} }
...@@ -776,6 +804,83 @@ static uint8_t u8g_com_esp8266_ssd_start_sequence(u8g_t *u8g) ...@@ -776,6 +804,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) uint8_t u8g_com_esp8266_ssd_i2c_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr)
{ {
switch(msg) switch(msg)
...@@ -811,7 +916,7 @@ uint8_t u8g_com_esp8266_ssd_i2c_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, voi ...@@ -811,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: case U8G_COM_MSG_WRITE_BYTE:
//u8g->pin_list[U8G_PI_SET_A0] = 1; //u8g->pin_list[U8G_PI_SET_A0] = 1;
if ( u8g_com_esp8266_ssd_start_sequence(u8g) == 0 ) 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 // ignore return value -> tolerate missing ACK
if ( platform_i2c_send_byte( ESP_I2C_ID, arg_val) == 0 ) if ( platform_i2c_send_byte( ESP_I2C_ID, arg_val) == 0 )
; //return platform_i2c_send_stop( ESP_I2C_ID ), 0; ; //return platform_i2c_send_stop( ESP_I2C_ID ), 0;
...@@ -865,10 +970,29 @@ uint8_t u8g_com_esp8266_ssd_i2c_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, voi ...@@ -865,10 +970,29 @@ 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 );
lud->pb.buf = NULL;
}
return 0;
}
// device constructors // 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 ) static int lu8g_ssd1306_128x64_i2c( lua_State *L )
{ {
unsigned addr = luaL_checkinteger( L, 1 ); unsigned addr = luaL_checkinteger( L, 1 );
...@@ -878,9 +1002,127 @@ static int lu8g_ssd1306_128x64_i2c( lua_State *L ) ...@@ -878,9 +1002,127 @@ static int lu8g_ssd1306_128x64_i2c( lua_State *L )
lu8g_userdata_t *lud = (lu8g_userdata_t *) lua_newuserdata( L, sizeof( lu8g_userdata_t ) ); 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;
// 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_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" );
// 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);
#endif
// set its metatable
luaL_getmetatable(L, "u8g.display");
lua_setmetatable(L, -2);
return 1;
}
// Lua: object = u8g.ssd1306_128x64_spi( cs, dc, [res] )
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" );
unsigned res = luaL_optinteger( L, 3, U8G_PIN_NONE );
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_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
#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, res );
#else
u8g_InitHWSPI( LU8G, &u8g_dev_ssd1306_128x64_spi, cs, dc, res );
#endif
u8g_InitI2C( lud, &u8g_dev_ssd1306_128x64_i2c, U8G_I2C_OPT_NONE); // set its metatable
luaL_getmetatable(L, "u8g.display");
lua_setmetatable(L, -2);
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 // set its metatable
...@@ -931,6 +1173,8 @@ static const LUA_REG_TYPE lu8g_display_map[] = ...@@ -931,6 +1173,8 @@ static const LUA_REG_TYPE lu8g_display_map[] =
{ LSTRKEY( "drawPixel" ), LFUNCVAL( lu8g_drawPixel ) }, { LSTRKEY( "drawPixel" ), LFUNCVAL( lu8g_drawPixel ) },
{ LSTRKEY( "drawHLine" ), LFUNCVAL( lu8g_drawHLine ) }, { LSTRKEY( "drawHLine" ), LFUNCVAL( lu8g_drawHLine ) },
{ LSTRKEY( "drawVLine" ), LFUNCVAL( lu8g_drawVLine ) }, { LSTRKEY( "drawVLine" ), LFUNCVAL( lu8g_drawVLine ) },
{ LSTRKEY( "drawBitmap" ), LFUNCVAL( lu8g_drawBitmap ) },
{ LSTRKEY( "drawXBM" ), LFUNCVAL( lu8g_drawXBM ) },
{ LSTRKEY( "setScale2x2" ), LFUNCVAL( lu8g_setScale2x2 ) }, { LSTRKEY( "setScale2x2" ), LFUNCVAL( lu8g_setScale2x2 ) },
{ LSTRKEY( "undoScale" ), LFUNCVAL( lu8g_undoScale ) }, { LSTRKEY( "undoScale" ), LFUNCVAL( lu8g_undoScale ) },
{ LSTRKEY( "firstPage" ), LFUNCVAL( lu8g_firstPage ) }, { LSTRKEY( "firstPage" ), LFUNCVAL( lu8g_firstPage ) },
...@@ -943,6 +1187,7 @@ static const LUA_REG_TYPE lu8g_display_map[] = ...@@ -943,6 +1187,7 @@ static const LUA_REG_TYPE lu8g_display_map[] =
{ LSTRKEY( "undoRotation" ), LFUNCVAL( lu8g_undoRotation ) }, { LSTRKEY( "undoRotation" ), LFUNCVAL( lu8g_undoRotation ) },
{ LSTRKEY( "getWidth" ), LFUNCVAL( lu8g_getWidth ) }, { LSTRKEY( "getWidth" ), LFUNCVAL( lu8g_getWidth ) },
{ LSTRKEY( "getHeight" ), LFUNCVAL( lu8g_getHeight ) }, { LSTRKEY( "getHeight" ), LFUNCVAL( lu8g_getHeight ) },
{ LSTRKEY( "__gc" ), LFUNCVAL( lu8g_close_display ) },
#if LUA_OPTIMIZE_MEMORY > 0 #if LUA_OPTIMIZE_MEMORY > 0
{ LSTRKEY( "__index" ), LROVAL ( lu8g_display_map ) }, { LSTRKEY( "__index" ), LROVAL ( lu8g_display_map ) },
#endif #endif
...@@ -951,12 +1196,21 @@ static const LUA_REG_TYPE lu8g_display_map[] = ...@@ -951,12 +1196,21 @@ static const LUA_REG_TYPE lu8g_display_map[] =
const LUA_REG_TYPE lu8g_map[] = const LUA_REG_TYPE lu8g_map[] =
{ {
#ifdef U8G_SSD1306_128x64_I2C
{ LSTRKEY( "ssd1306_128x64_i2c" ), LFUNCVAL ( lu8g_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 #if LUA_OPTIMIZE_MEMORY > 0
// Register fonts // Register fonts
#undef U8G_FONT_TABLE_ENTRY #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 U8G_FONT_TABLE
// Options for circle/ ellipse drwing // Options for circle/ ellipse drwing
...@@ -992,7 +1246,7 @@ LUALIB_API int luaopen_u8g( lua_State *L ) ...@@ -992,7 +1246,7 @@ LUALIB_API int luaopen_u8g( lua_State *L )
// Register fonts // Register fonts
#undef U8G_FONT_TABLE_ENTRY #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 U8G_FONT_TABLE
// Options for circle/ ellipse drawing // Options for circle/ ellipse drawing
......
...@@ -127,11 +127,19 @@ int smart_check(uint8_t *nibble, uint16_t len, uint8_t *dst, uint8_t *got){ ...@@ -127,11 +127,19 @@ int smart_check(uint8_t *nibble, uint16_t len, uint8_t *dst, uint8_t *got){
return res; return res;
} }
void detect(uint8 *buf, uint16 len){ void detect(uint8 *arg, uint16 len){
uint16_t seq; uint16_t seq;
int16_t seq_delta = 0; int16_t seq_delta = 0;
uint16_t byte_num = 0, bit_num = 0; uint16_t byte_num = 0, bit_num = 0;
int16_t c = 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){ if( ( (buf[0]) & TYPE_SUBTYPE_MASK) != TYPE_SUBTYPE_QOS_DATA){
return; return;
} }
......
...@@ -59,6 +59,40 @@ extern "C" { ...@@ -59,6 +59,40 @@ extern "C" {
#define STATION_CHECK_TIME (2*1000) #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 { struct _my_addr_map {
uint8 addr[ADDR_LENGTH*3]; uint8 addr[ADDR_LENGTH*3];
uint8_t addr_len; uint8_t addr_len;
......
...@@ -395,13 +395,11 @@ typedef struct __attribute(( packed )) { ...@@ -395,13 +395,11 @@ typedef struct __attribute(( packed )) {
// common page header // common page header
spiffs_page_header p_hdr; spiffs_page_header p_hdr;
// alignment // 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 // size of object
u32_t size; u32_t size;
// type of object // type of object
spiffs_obj_type type; spiffs_obj_type type;
// alignment2
u8_t _align2[4 - (sizeof(spiffs_obj_type)&3)==0 ? 4 : (sizeof(spiffs_obj_type)&3)];
// name of object // name of object
u8_t name[SPIFFS_OBJ_NAME_LEN]; u8_t name[SPIFFS_OBJ_NAME_LEN];
} spiffs_page_object_ix_header; } spiffs_page_object_ix_header;
......
...@@ -654,6 +654,7 @@ uint8_t u8g_com_arduino_port_d_wr_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, v ...@@ -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_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_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_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_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 */ 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) ...@@ -723,6 +724,10 @@ defined(__18CXX) || defined(__PIC32MX)
#define U8G_COM_HW_SPI u8g_com_atmega_hw_spi_fn #define U8G_COM_HW_SPI u8g_com_atmega_hw_spi_fn
#define U8G_COM_ST7920_HW_SPI u8g_com_atmega_st7920_hw_spi_fn #define U8G_COM_ST7920_HW_SPI u8g_com_atmega_st7920_hw_spi_fn
#endif #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 #endif
#ifndef U8G_COM_HW_SPI #ifndef U8G_COM_HW_SPI
#define U8G_COM_HW_SPI u8g_com_null_fn #define U8G_COM_HW_SPI u8g_com_null_fn
......
pwm.setup(0,500,50) pwm.setup(1,500,50) pwm.setup(2,500,50) pwm.setup(0,500,50) pwm.setup(1,500,50) pwm.setup(2,500,50)
pwm.start(0) pwm.start(1) pwm.start(2) 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 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 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) tmr.alarm( 1000,1,function() if a==0 then a=1 led(50,50,50) else a=0 led(0,0,0) end end)
......
---
-- 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
-- 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")
local IMAP_USERNAME = "email@domain.com"
local IMAP_PASSWORD = "password"
-- 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" -- You do not need to change this
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"
local SSID_PASSWORD = "password"
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,
IMAP_DEBUG)
imap.login(sck)
end
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()
-- Check if the IMAP reply was processed
if(imap.response_processed() == true) then
-- The IMAP reply was processed
if (count == 0) then
-- 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
-- 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
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
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
body = imap.get_body() -- store the BODY response in body
imap.logout(imap_socket) -- Logout of the email account
count = count + 1
else
-- 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)
---
-- 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
-- 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
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 count = 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)
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
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
-- 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)
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)
end
-- Send an email
send_email(
"ESP8266",
[[Hi,
How are your IoT projects coming along?
Best Wishes,
ESP8266]])
-- setup I2c and connect display
function init_i2c_display()
-- 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, 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, res)
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(state)
disp:setFont(u8g.font_6x10)
disp:drawStr( 0, 10, "Bitmap picture")
disp:drawBitmap( 0 + (state * 10), 20 + (state * 4), 1, 8, bm_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(delay)
-- read XBM picture
file.open("u8glib_logo.xbm", "r")
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 + 1*8, 1 do
disp:firstPage()
repeat
draw(draw_state)
until disp:nextPage() == false
tmr.delay(delay)
tmr.wdclr()
end
print("--- Bitmap Test done ---")
end
--init_i2c_display()
init_spi_display()
bitmap_test(50000)
-- setup I2c and connect display -- setup I2c and connect display
function init_i2c_display() function init_i2c_display()
sda = 5 -- SDA and SCL can be assigned freely to available GPIOs
scl = 6 sda = 5 -- GPIO14
scl = 6 -- GPIO12
sla = 0x3c sla = 0x3c
i2c.setup(0, sda, scl, i2c.SLOW) i2c.setup(0, sda, scl, i2c.SLOW)
disp = u8g.ssd1306_128x64_i2c(sla) disp = u8g.ssd1306_128x64_i2c(sla)
end 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, 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, res)
end
-- graphic test components -- graphic test components
function prepare() function prepare()
...@@ -122,9 +137,7 @@ function draw(draw_state) ...@@ -122,9 +137,7 @@ function draw(draw_state)
end end
end end
function graphics_test() function graphics_test(delay)
init_i2c_display()
print("--- Starting Graphics Test ---") print("--- Starting Graphics Test ---")
-- cycle through all components -- cycle through all components
...@@ -135,6 +148,7 @@ function graphics_test() ...@@ -135,6 +148,7 @@ function graphics_test()
draw(draw_state) draw(draw_state)
until disp:nextPage() == false until disp:nextPage() == false
--print(node.heap()) --print(node.heap())
tmr.delay(delay)
-- re-trigger Watchdog! -- re-trigger Watchdog!
tmr.wdclr() tmr.wdclr()
end end
...@@ -142,4 +156,6 @@ function graphics_test() ...@@ -142,4 +156,6 @@ function graphics_test()
print("--- Graphics Test done ---") print("--- Graphics Test done ---")
end end
graphics_test() --init_i2c_display()
init_spi_display()
graphics_test(50000)
-- setup I2c and connect display -- setup I2c and connect display
function init_i2c_display() function init_i2c_display()
sda = 5 -- SDA and SCL can be assigned freely to available GPIOs
scl = 6 sda = 5 -- GPIO14
scl = 6 -- GPIO12
sla = 0x3c sla = 0x3c
i2c.setup(0, sda, scl, i2c.SLOW) i2c.setup(0, sda, scl, i2c.SLOW)
disp = u8g.ssd1306_128x64_i2c(sla) disp = u8g.ssd1306_128x64_i2c(sla)
end 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, 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, res)
end
-- the draw() routine -- the draw() routine
function draw() function draw()
disp:setFont(u8g.font_6x10) disp:setFont(u8g.font_6x10)
...@@ -35,13 +51,12 @@ function rotate() ...@@ -35,13 +51,12 @@ function rotate()
dir = dir + 1 dir = dir + 1
dir = bit.band(dir, 3) dir = bit.band(dir, 3)
-- schedule next rotation step in 1000ms
next_rotation = tmr.now() / 1000 + 1000 next_rotation = tmr.now() / 1000 + 1000
end end
end end
function rotation_test() function rotation_test()
init_i2c_display()
print("--- Starting Rotation Test ---") print("--- Starting Rotation Test ---")
dir = 0 dir = 0
next_rotation = 0 next_rotation = 0
...@@ -55,10 +70,13 @@ function rotation_test() ...@@ -55,10 +70,13 @@ function rotation_test()
draw(draw_state) draw(draw_state)
until disp:nextPage() == false until disp:nextPage() == false
tmr.delay(100000)
tmr.wdclr() tmr.wdclr()
end end
print("--- Rotation Test done ---") print("--- Rotation Test done ---")
end end
--init_i2c_display()
init_spi_display()
rotation_test() rotation_test()
-- Lua 5.1+ base64 v3.0 (c) 2009 by Alex Kloss <alexthkloss@web.de>
-- 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
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment