mirror of
https://github.com/cloudwu/skynet.git
synced 2026-07-22 11:03:12 +00:00
Compare commits
136 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a77b71530d | ||
|
|
803a79059a | ||
|
|
6c9ad16077 | ||
|
|
2ab689f7c1 | ||
|
|
f8a50929a8 | ||
|
|
af3ca3bb2f | ||
|
|
aaf8617dd5 | ||
|
|
1d3e393fd4 | ||
|
|
c3df405e33 | ||
|
|
b1ef8a33d9 | ||
|
|
954625a530 | ||
|
|
9850ca1b94 | ||
|
|
01e1a6aafa | ||
|
|
0f4a771792 | ||
|
|
73bd788a6c | ||
|
|
8cb5378df9 | ||
|
|
82bddd9cb8 | ||
|
|
24ff08f997 | ||
|
|
fc8983227d | ||
|
|
9bdc36526e | ||
|
|
24857a3d8d | ||
|
|
b221c9e84c | ||
|
|
8d2fcef273 | ||
|
|
1d8ca31d55 | ||
|
|
3b95ecd9b2 | ||
|
|
7cc25a6238 | ||
|
|
1ef5430e0a | ||
|
|
b39fc6ee23 | ||
|
|
a074cd79bd | ||
|
|
883757b936 | ||
|
|
35e5a938b0 | ||
|
|
31d7b648b4 | ||
|
|
44e6693a40 | ||
|
|
c458eba6b4 | ||
|
|
6d51c21c06 | ||
|
|
d2aa2069f7 | ||
|
|
8de9c22fd7 | ||
|
|
d649d0adf1 | ||
|
|
0aa5274799 | ||
|
|
bfc3fd425a | ||
|
|
05f508fb42 | ||
|
|
c8b8dc1276 | ||
|
|
1c62785d71 | ||
|
|
4a8bf4f39a | ||
|
|
2f6bfe9104 | ||
|
|
535bbc7219 | ||
|
|
9713b055a0 | ||
|
|
7dee9a97c5 | ||
|
|
aab65a2728 | ||
|
|
c8775ee6f5 | ||
|
|
016b4dc58f | ||
|
|
36d77aaa2b | ||
|
|
f32f613ac0 | ||
|
|
885c5b56bb | ||
|
|
b923f93aee | ||
|
|
ce6efc535c | ||
|
|
320a608c36 | ||
|
|
f47e4275b6 | ||
|
|
228ad16d74 | ||
|
|
9b901c6a38 | ||
|
|
37d64a7028 | ||
|
|
f8bf05513c | ||
|
|
bac9348db9 | ||
|
|
3074dbea0b | ||
|
|
fe4122e579 | ||
|
|
b52cce9056 | ||
|
|
bc938c7b08 | ||
|
|
872b3c2743 | ||
|
|
01269c88ff | ||
|
|
db9ab88a6c | ||
|
|
18af238256 | ||
|
|
7f6767b298 | ||
|
|
810585207d | ||
|
|
3b23f3d27b | ||
|
|
169ca2a2b8 | ||
|
|
8b7ba7bdbe | ||
|
|
a77cec2aea | ||
|
|
8fa51dfc33 | ||
|
|
e95b7b9cf1 | ||
|
|
165bed46a8 | ||
|
|
0dd7b2e959 | ||
|
|
0fde2ea571 | ||
|
|
bfb8bcb86e | ||
|
|
9bb8099008 | ||
|
|
98630badd5 | ||
|
|
d0c4a4012a | ||
|
|
16f63df0c3 | ||
|
|
04688e98a7 | ||
|
|
e0feea9995 | ||
|
|
55ea556060 | ||
|
|
4803e50977 | ||
|
|
f393b64124 | ||
|
|
9ea756ce4f | ||
|
|
f9438461eb | ||
|
|
0fb23f9ab8 | ||
|
|
0390818a02 | ||
|
|
e002796d2e | ||
|
|
b8a0e35f21 | ||
|
|
bd52c5fece | ||
|
|
d2cea9b70f | ||
|
|
7e24d46047 | ||
|
|
8078d777ab | ||
|
|
939ef564ce | ||
|
|
6bf8dd9a31 | ||
|
|
809e6c3a89 | ||
|
|
845acd3314 | ||
|
|
2cf69a6ef0 | ||
|
|
978e010e67 | ||
|
|
cde423cb73 | ||
|
|
cfe8b19c8a | ||
|
|
f69f6e8e70 | ||
|
|
d7b228f342 | ||
|
|
0954a906f4 | ||
|
|
023e4abd87 | ||
|
|
cab10edd1d | ||
|
|
87a02ffe72 | ||
|
|
ae5a9bb855 | ||
|
|
c48e0e4c32 | ||
|
|
0747a7d7e1 | ||
|
|
4b0a648b21 | ||
|
|
40519e9ee1 | ||
|
|
ce5adba5b2 | ||
|
|
c78466b486 | ||
|
|
deed51d309 | ||
|
|
68a952ff17 | ||
|
|
434cd1ca79 | ||
|
|
8e09fad233 | ||
|
|
e9d4075e43 | ||
|
|
fce05f0cfc | ||
|
|
4cce476dfc | ||
|
|
ad5c37200b | ||
|
|
c0e1365dc2 | ||
|
|
984f727385 | ||
|
|
315945b2bd | ||
|
|
bf686da723 | ||
|
|
6733134ad4 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -8,3 +8,4 @@
|
||||
/luaclib
|
||||
*.so
|
||||
*.dSYM
|
||||
.DS_Store
|
||||
|
||||
90
3rd/lpeg/HISTORY
Normal file
90
3rd/lpeg/HISTORY
Normal file
@@ -0,0 +1,90 @@
|
||||
HISTORY for LPeg 0.12
|
||||
|
||||
* Changes from version 0.11 to 0.12
|
||||
---------------------------------
|
||||
+ no "unsigned short" limit for pattern sizes
|
||||
+ mathtime captures considered nullable
|
||||
+ some bugs fixed
|
||||
|
||||
* Changes from version 0.10 to 0.11
|
||||
-------------------------------
|
||||
+ complete reimplementation of the code generator
|
||||
+ new syntax for table captures
|
||||
+ new functions in module 're'
|
||||
+ other small improvements
|
||||
|
||||
* Changes from version 0.9 to 0.10
|
||||
-------------------------------
|
||||
+ backtrack stack has configurable size
|
||||
+ better error messages
|
||||
+ Notation for non-terminals in 're' back to A instead o <A>
|
||||
+ experimental look-behind pattern
|
||||
+ support for external extensions
|
||||
+ works with Lua 5.2
|
||||
+ consumes less C stack
|
||||
|
||||
- "and" predicates do not keep captures
|
||||
|
||||
* Changes from version 0.8 to 0.9
|
||||
-------------------------------
|
||||
+ The accumulator capture was replaced by a fold capture;
|
||||
programs that used the old 'lpeg.Ca' will need small changes.
|
||||
+ Some support for character classes from old C locales.
|
||||
+ A new named-group capture.
|
||||
|
||||
* Changes from version 0.7 to 0.8
|
||||
-------------------------------
|
||||
+ New "match-time" capture.
|
||||
+ New "argument capture" that allows passing arguments into the pattern.
|
||||
+ Better documentation for 're'.
|
||||
+ Several small improvements for 're'.
|
||||
+ The 're' module has an incompatibility with previous versions:
|
||||
now, any use of a non-terminal must be enclosed in angle brackets
|
||||
(like <B>).
|
||||
|
||||
* Changes from version 0.6 to 0.7
|
||||
-------------------------------
|
||||
+ Several improvements in module 're':
|
||||
- better documentation;
|
||||
- support for most captures (all but accumulator);
|
||||
- limited repetitions p{n,m}.
|
||||
+ Small improvements in efficiency.
|
||||
+ Several small bugs corrected (special thanks to Hans Hagen
|
||||
and Taco Hoekwater).
|
||||
|
||||
* Changes from version 0.5 to 0.6
|
||||
-------------------------------
|
||||
+ Support for non-numeric indices in grammars.
|
||||
+ Some bug fixes (thanks to the luatex team).
|
||||
+ Some new optimizations; (thanks to Mike Pall).
|
||||
+ A new page layout (thanks to Andre Carregal).
|
||||
+ Minimal documentation for module 're'.
|
||||
|
||||
* Changes from version 0.4 to 0.5
|
||||
-------------------------------
|
||||
+ Several optimizations.
|
||||
+ lpeg.P now accepts booleans.
|
||||
+ Some new examples.
|
||||
+ A proper license.
|
||||
+ Several small improvements.
|
||||
|
||||
* Changes from version 0.3 to 0.4
|
||||
-------------------------------
|
||||
+ Static check for loops in repetitions and grammars.
|
||||
+ Removed label option in captures.
|
||||
+ The implementation of captures uses less memory.
|
||||
|
||||
* Changes from version 0.2 to 0.3
|
||||
-------------------------------
|
||||
+ User-defined patterns in Lua.
|
||||
+ Several new captures.
|
||||
|
||||
* Changes from version 0.1 to 0.2
|
||||
-------------------------------
|
||||
+ Several small corrections.
|
||||
+ Handles embedded zeros like any other character.
|
||||
+ Capture "name" can be any Lua value.
|
||||
+ Unlimited number of captures.
|
||||
+ Match gets an optional initial position.
|
||||
|
||||
(end of HISTORY)
|
||||
537
3rd/lpeg/lpcap.c
Normal file
537
3rd/lpeg/lpcap.c
Normal file
@@ -0,0 +1,537 @@
|
||||
/*
|
||||
** $Id: lpcap.c,v 1.4 2013/03/21 20:25:12 roberto Exp $
|
||||
** Copyright 2007, Lua.org & PUC-Rio (see 'lpeg.html' for license)
|
||||
*/
|
||||
|
||||
#include "lua.h"
|
||||
#include "lauxlib.h"
|
||||
|
||||
#include "lpcap.h"
|
||||
#include "lptypes.h"
|
||||
|
||||
|
||||
#define captype(cap) ((cap)->kind)
|
||||
|
||||
#define isclosecap(cap) (captype(cap) == Cclose)
|
||||
|
||||
#define closeaddr(c) ((c)->s + (c)->siz - 1)
|
||||
|
||||
#define isfullcap(cap) ((cap)->siz != 0)
|
||||
|
||||
#define getfromktable(cs,v) lua_rawgeti((cs)->L, ktableidx((cs)->ptop), v)
|
||||
|
||||
#define pushluaval(cs) getfromktable(cs, (cs)->cap->idx)
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** Put at the cache for Lua values the value indexed by 'v' in ktable
|
||||
** of the running pattern (if it is not there yet); returns its index.
|
||||
*/
|
||||
static int updatecache (CapState *cs, int v) {
|
||||
int idx = cs->ptop + 1; /* stack index of cache for Lua values */
|
||||
if (v != cs->valuecached) { /* not there? */
|
||||
getfromktable(cs, v); /* get value from 'ktable' */
|
||||
lua_replace(cs->L, idx); /* put it at reserved stack position */
|
||||
cs->valuecached = v; /* keep track of what is there */
|
||||
}
|
||||
return idx;
|
||||
}
|
||||
|
||||
|
||||
static int pushcapture (CapState *cs);
|
||||
|
||||
|
||||
/*
|
||||
** Goes back in a list of captures looking for an open capture
|
||||
** corresponding to a close
|
||||
*/
|
||||
static Capture *findopen (Capture *cap) {
|
||||
int n = 0; /* number of closes waiting an open */
|
||||
for (;;) {
|
||||
cap--;
|
||||
if (isclosecap(cap)) n++; /* one more open to skip */
|
||||
else if (!isfullcap(cap))
|
||||
if (n-- == 0) return cap;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Go to the next capture
|
||||
*/
|
||||
static void nextcap (CapState *cs) {
|
||||
Capture *cap = cs->cap;
|
||||
if (!isfullcap(cap)) { /* not a single capture? */
|
||||
int n = 0; /* number of opens waiting a close */
|
||||
for (;;) { /* look for corresponding close */
|
||||
cap++;
|
||||
if (isclosecap(cap)) {
|
||||
if (n-- == 0) break;
|
||||
}
|
||||
else if (!isfullcap(cap)) n++;
|
||||
}
|
||||
}
|
||||
cs->cap = cap + 1; /* + 1 to skip last close (or entire single capture) */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Push on the Lua stack all values generated by nested captures inside
|
||||
** the current capture. Returns number of values pushed. 'addextra'
|
||||
** makes it push the entire match after all captured values. The
|
||||
** entire match is pushed also if there are no other nested values,
|
||||
** so the function never returns zero.
|
||||
*/
|
||||
static int pushnestedvalues (CapState *cs, int addextra) {
|
||||
Capture *co = cs->cap;
|
||||
if (isfullcap(cs->cap++)) { /* no nested captures? */
|
||||
lua_pushlstring(cs->L, co->s, co->siz - 1); /* push whole match */
|
||||
return 1; /* that is it */
|
||||
}
|
||||
else {
|
||||
int n = 0;
|
||||
while (!isclosecap(cs->cap)) /* repeat for all nested patterns */
|
||||
n += pushcapture(cs);
|
||||
if (addextra || n == 0) { /* need extra? */
|
||||
lua_pushlstring(cs->L, co->s, cs->cap->s - co->s); /* push whole match */
|
||||
n++;
|
||||
}
|
||||
cs->cap++; /* skip close entry */
|
||||
return n;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Push only the first value generated by nested captures
|
||||
*/
|
||||
static void pushonenestedvalue (CapState *cs) {
|
||||
int n = pushnestedvalues(cs, 0);
|
||||
if (n > 1)
|
||||
lua_pop(cs->L, n - 1); /* pop extra values */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Try to find a named group capture with the name given at the top of
|
||||
** the stack; goes backward from 'cap'.
|
||||
*/
|
||||
static Capture *findback (CapState *cs, Capture *cap) {
|
||||
lua_State *L = cs->L;
|
||||
while (cap-- > cs->ocap) { /* repeat until end of list */
|
||||
if (isclosecap(cap))
|
||||
cap = findopen(cap); /* skip nested captures */
|
||||
else if (!isfullcap(cap))
|
||||
continue; /* opening an enclosing capture: skip and get previous */
|
||||
if (captype(cap) == Cgroup) {
|
||||
getfromktable(cs, cap->idx); /* get group name */
|
||||
if (lua_equal(L, -2, -1)) { /* right group? */
|
||||
lua_pop(L, 2); /* remove reference name and group name */
|
||||
return cap;
|
||||
}
|
||||
else lua_pop(L, 1); /* remove group name */
|
||||
}
|
||||
}
|
||||
luaL_error(L, "back reference '%s' not found", lua_tostring(L, -1));
|
||||
return NULL; /* to avoid warnings */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Back-reference capture. Return number of values pushed.
|
||||
*/
|
||||
static int backrefcap (CapState *cs) {
|
||||
int n;
|
||||
Capture *curr = cs->cap;
|
||||
pushluaval(cs); /* reference name */
|
||||
cs->cap = findback(cs, curr); /* find corresponding group */
|
||||
n = pushnestedvalues(cs, 0); /* push group's values */
|
||||
cs->cap = curr + 1;
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Table capture: creates a new table and populates it with nested
|
||||
** captures.
|
||||
*/
|
||||
static int tablecap (CapState *cs) {
|
||||
lua_State *L = cs->L;
|
||||
int n = 0;
|
||||
lua_newtable(L);
|
||||
if (isfullcap(cs->cap++))
|
||||
return 1; /* table is empty */
|
||||
while (!isclosecap(cs->cap)) {
|
||||
if (captype(cs->cap) == Cgroup && cs->cap->idx != 0) { /* named group? */
|
||||
pushluaval(cs); /* push group name */
|
||||
pushonenestedvalue(cs);
|
||||
lua_settable(L, -3);
|
||||
}
|
||||
else { /* not a named group */
|
||||
int i;
|
||||
int k = pushcapture(cs);
|
||||
for (i = k; i > 0; i--) /* store all values into table */
|
||||
lua_rawseti(L, -(i + 1), n + i);
|
||||
n += k;
|
||||
}
|
||||
}
|
||||
cs->cap++; /* skip close entry */
|
||||
return 1; /* number of values pushed (only the table) */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Table-query capture
|
||||
*/
|
||||
static int querycap (CapState *cs) {
|
||||
int idx = cs->cap->idx;
|
||||
pushonenestedvalue(cs); /* get nested capture */
|
||||
lua_gettable(cs->L, updatecache(cs, idx)); /* query cap. value at table */
|
||||
if (!lua_isnil(cs->L, -1))
|
||||
return 1;
|
||||
else { /* no value */
|
||||
lua_pop(cs->L, 1); /* remove nil */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Fold capture
|
||||
*/
|
||||
static int foldcap (CapState *cs) {
|
||||
int n;
|
||||
lua_State *L = cs->L;
|
||||
int idx = cs->cap->idx;
|
||||
if (isfullcap(cs->cap++) || /* no nested captures? */
|
||||
isclosecap(cs->cap) || /* no nested captures (large subject)? */
|
||||
(n = pushcapture(cs)) == 0) /* nested captures with no values? */
|
||||
return luaL_error(L, "no initial value for fold capture");
|
||||
if (n > 1)
|
||||
lua_pop(L, n - 1); /* leave only one result for accumulator */
|
||||
while (!isclosecap(cs->cap)) {
|
||||
lua_pushvalue(L, updatecache(cs, idx)); /* get folding function */
|
||||
lua_insert(L, -2); /* put it before accumulator */
|
||||
n = pushcapture(cs); /* get next capture's values */
|
||||
lua_call(L, n + 1, 1); /* call folding function */
|
||||
}
|
||||
cs->cap++; /* skip close entry */
|
||||
return 1; /* only accumulator left on the stack */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Function capture
|
||||
*/
|
||||
static int functioncap (CapState *cs) {
|
||||
int n;
|
||||
int top = lua_gettop(cs->L);
|
||||
pushluaval(cs); /* push function */
|
||||
n = pushnestedvalues(cs, 0); /* push nested captures */
|
||||
lua_call(cs->L, n, LUA_MULTRET); /* call function */
|
||||
return lua_gettop(cs->L) - top; /* return function's results */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Select capture
|
||||
*/
|
||||
static int numcap (CapState *cs) {
|
||||
int idx = cs->cap->idx; /* value to select */
|
||||
if (idx == 0) { /* no values? */
|
||||
nextcap(cs); /* skip entire capture */
|
||||
return 0; /* no value produced */
|
||||
}
|
||||
else {
|
||||
int n = pushnestedvalues(cs, 0);
|
||||
if (n < idx) /* invalid index? */
|
||||
return luaL_error(cs->L, "no capture '%d'", idx);
|
||||
else {
|
||||
lua_pushvalue(cs->L, -(n - idx + 1)); /* get selected capture */
|
||||
lua_replace(cs->L, -(n + 1)); /* put it in place of 1st capture */
|
||||
lua_pop(cs->L, n - 1); /* remove other captures */
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Return the stack index of the first runtime capture in the given
|
||||
** list of captures (or zero if no runtime captures)
|
||||
*/
|
||||
int finddyncap (Capture *cap, Capture *last) {
|
||||
for (; cap < last; cap++) {
|
||||
if (cap->kind == Cruntime)
|
||||
return cap->idx; /* stack position of first capture */
|
||||
}
|
||||
return 0; /* no dynamic captures in this segment */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Calls a runtime capture. Returns number of captures removed by
|
||||
** the call, including the initial Cgroup. (Captures to be added are
|
||||
** on the Lua stack.)
|
||||
*/
|
||||
int runtimecap (CapState *cs, Capture *close, const char *s, int *rem) {
|
||||
int n, id;
|
||||
lua_State *L = cs->L;
|
||||
int otop = lua_gettop(L);
|
||||
Capture *open = findopen(close);
|
||||
assert(captype(open) == Cgroup);
|
||||
id = finddyncap(open, close); /* get first dynamic capture argument */
|
||||
close->kind = Cclose; /* closes the group */
|
||||
close->s = s;
|
||||
cs->cap = open; cs->valuecached = 0; /* prepare capture state */
|
||||
luaL_checkstack(L, 4, "too many runtime captures");
|
||||
pushluaval(cs); /* push function to be called */
|
||||
lua_pushvalue(L, SUBJIDX); /* push original subject */
|
||||
lua_pushinteger(L, s - cs->s + 1); /* push current position */
|
||||
n = pushnestedvalues(cs, 0); /* push nested captures */
|
||||
lua_call(L, n + 2, LUA_MULTRET); /* call dynamic function */
|
||||
if (id > 0) { /* are there old dynamic captures to be removed? */
|
||||
int i;
|
||||
for (i = id; i <= otop; i++)
|
||||
lua_remove(L, id); /* remove old dynamic captures */
|
||||
*rem = otop - id + 1; /* total number of dynamic captures removed */
|
||||
}
|
||||
else
|
||||
*rem = 0; /* no dynamic captures removed */
|
||||
return close - open; /* number of captures of all kinds removed */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Auxiliary structure for substitution and string captures: keep
|
||||
** information about nested captures for future use, avoiding to push
|
||||
** string results into Lua
|
||||
*/
|
||||
typedef struct StrAux {
|
||||
int isstring; /* whether capture is a string */
|
||||
union {
|
||||
Capture *cp; /* if not a string, respective capture */
|
||||
struct { /* if it is a string... */
|
||||
const char *s; /* ... starts here */
|
||||
const char *e; /* ... ends here */
|
||||
} s;
|
||||
} u;
|
||||
} StrAux;
|
||||
|
||||
#define MAXSTRCAPS 10
|
||||
|
||||
/*
|
||||
** Collect values from current capture into array 'cps'. Current
|
||||
** capture must be Cstring (first call) or Csimple (recursive calls).
|
||||
** (In first call, fills %0 with whole match for Cstring.)
|
||||
** Returns number of elements in the array that were filled.
|
||||
*/
|
||||
static int getstrcaps (CapState *cs, StrAux *cps, int n) {
|
||||
int k = n++;
|
||||
cps[k].isstring = 1; /* get string value */
|
||||
cps[k].u.s.s = cs->cap->s; /* starts here */
|
||||
if (!isfullcap(cs->cap++)) { /* nested captures? */
|
||||
while (!isclosecap(cs->cap)) { /* traverse them */
|
||||
if (n >= MAXSTRCAPS) /* too many captures? */
|
||||
nextcap(cs); /* skip extra captures (will not need them) */
|
||||
else if (captype(cs->cap) == Csimple) /* string? */
|
||||
n = getstrcaps(cs, cps, n); /* put info. into array */
|
||||
else {
|
||||
cps[n].isstring = 0; /* not a string */
|
||||
cps[n].u.cp = cs->cap; /* keep original capture */
|
||||
nextcap(cs);
|
||||
n++;
|
||||
}
|
||||
}
|
||||
cs->cap++; /* skip close */
|
||||
}
|
||||
cps[k].u.s.e = closeaddr(cs->cap - 1); /* ends here */
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** add next capture value (which should be a string) to buffer 'b'
|
||||
*/
|
||||
static int addonestring (luaL_Buffer *b, CapState *cs, const char *what);
|
||||
|
||||
|
||||
/*
|
||||
** String capture: add result to buffer 'b' (instead of pushing
|
||||
** it into the stack)
|
||||
*/
|
||||
static void stringcap (luaL_Buffer *b, CapState *cs) {
|
||||
StrAux cps[MAXSTRCAPS];
|
||||
int n;
|
||||
size_t len, i;
|
||||
const char *fmt; /* format string */
|
||||
fmt = lua_tolstring(cs->L, updatecache(cs, cs->cap->idx), &len);
|
||||
n = getstrcaps(cs, cps, 0) - 1; /* collect nested captures */
|
||||
for (i = 0; i < len; i++) { /* traverse them */
|
||||
if (fmt[i] != '%') /* not an escape? */
|
||||
luaL_addchar(b, fmt[i]); /* add it to buffer */
|
||||
else if (fmt[++i] < '0' || fmt[i] > '9') /* not followed by a digit? */
|
||||
luaL_addchar(b, fmt[i]); /* add to buffer */
|
||||
else {
|
||||
int l = fmt[i] - '0'; /* capture index */
|
||||
if (l > n)
|
||||
luaL_error(cs->L, "invalid capture index (%d)", l);
|
||||
else if (cps[l].isstring)
|
||||
luaL_addlstring(b, cps[l].u.s.s, cps[l].u.s.e - cps[l].u.s.s);
|
||||
else {
|
||||
Capture *curr = cs->cap;
|
||||
cs->cap = cps[l].u.cp; /* go back to evaluate that nested capture */
|
||||
if (!addonestring(b, cs, "capture"))
|
||||
luaL_error(cs->L, "no values in capture index %d", l);
|
||||
cs->cap = curr; /* continue from where it stopped */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Substitution capture: add result to buffer 'b'
|
||||
*/
|
||||
static void substcap (luaL_Buffer *b, CapState *cs) {
|
||||
const char *curr = cs->cap->s;
|
||||
if (isfullcap(cs->cap)) /* no nested captures? */
|
||||
luaL_addlstring(b, curr, cs->cap->siz - 1); /* keep original text */
|
||||
else {
|
||||
cs->cap++; /* skip open entry */
|
||||
while (!isclosecap(cs->cap)) { /* traverse nested captures */
|
||||
const char *next = cs->cap->s;
|
||||
luaL_addlstring(b, curr, next - curr); /* add text up to capture */
|
||||
if (addonestring(b, cs, "replacement"))
|
||||
curr = closeaddr(cs->cap - 1); /* continue after match */
|
||||
else /* no capture value */
|
||||
curr = next; /* keep original text in final result */
|
||||
}
|
||||
luaL_addlstring(b, curr, cs->cap->s - curr); /* add last piece of text */
|
||||
}
|
||||
cs->cap++; /* go to next capture */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Evaluates a capture and adds its first value to buffer 'b'; returns
|
||||
** whether there was a value
|
||||
*/
|
||||
static int addonestring (luaL_Buffer *b, CapState *cs, const char *what) {
|
||||
switch (captype(cs->cap)) {
|
||||
case Cstring:
|
||||
stringcap(b, cs); /* add capture directly to buffer */
|
||||
return 1;
|
||||
case Csubst:
|
||||
substcap(b, cs); /* add capture directly to buffer */
|
||||
return 1;
|
||||
default: {
|
||||
lua_State *L = cs->L;
|
||||
int n = pushcapture(cs);
|
||||
if (n > 0) {
|
||||
if (n > 1) lua_pop(L, n - 1); /* only one result */
|
||||
if (!lua_isstring(L, -1))
|
||||
luaL_error(L, "invalid %s value (a %s)", what, luaL_typename(L, -1));
|
||||
luaL_addvalue(b);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Push all values of the current capture into the stack; returns
|
||||
** number of values pushed
|
||||
*/
|
||||
static int pushcapture (CapState *cs) {
|
||||
lua_State *L = cs->L;
|
||||
luaL_checkstack(L, 4, "too many captures");
|
||||
switch (captype(cs->cap)) {
|
||||
case Cposition: {
|
||||
lua_pushinteger(L, cs->cap->s - cs->s + 1);
|
||||
cs->cap++;
|
||||
return 1;
|
||||
}
|
||||
case Cconst: {
|
||||
pushluaval(cs);
|
||||
cs->cap++;
|
||||
return 1;
|
||||
}
|
||||
case Carg: {
|
||||
int arg = (cs->cap++)->idx;
|
||||
if (arg + FIXEDARGS > cs->ptop)
|
||||
return luaL_error(L, "reference to absent argument #%d", arg);
|
||||
lua_pushvalue(L, arg + FIXEDARGS);
|
||||
return 1;
|
||||
}
|
||||
case Csimple: {
|
||||
int k = pushnestedvalues(cs, 1);
|
||||
lua_insert(L, -k); /* make whole match be first result */
|
||||
return k;
|
||||
}
|
||||
case Cruntime: {
|
||||
lua_pushvalue(L, (cs->cap++)->idx); /* value is in the stack */
|
||||
return 1;
|
||||
}
|
||||
case Cstring: {
|
||||
luaL_Buffer b;
|
||||
luaL_buffinit(L, &b);
|
||||
stringcap(&b, cs);
|
||||
luaL_pushresult(&b);
|
||||
return 1;
|
||||
}
|
||||
case Csubst: {
|
||||
luaL_Buffer b;
|
||||
luaL_buffinit(L, &b);
|
||||
substcap(&b, cs);
|
||||
luaL_pushresult(&b);
|
||||
return 1;
|
||||
}
|
||||
case Cgroup: {
|
||||
if (cs->cap->idx == 0) /* anonymous group? */
|
||||
return pushnestedvalues(cs, 0); /* add all nested values */
|
||||
else { /* named group: add no values */
|
||||
nextcap(cs); /* skip capture */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
case Cbackref: return backrefcap(cs);
|
||||
case Ctable: return tablecap(cs);
|
||||
case Cfunction: return functioncap(cs);
|
||||
case Cnum: return numcap(cs);
|
||||
case Cquery: return querycap(cs);
|
||||
case Cfold: return foldcap(cs);
|
||||
default: assert(0); return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Prepare a CapState structure and traverse the entire list of
|
||||
** captures in the stack pushing its results. 's' is the subject
|
||||
** string, 'r' is the final position of the match, and 'ptop'
|
||||
** the index in the stack where some useful values were pushed.
|
||||
** Returns the number of results pushed. (If the list produces no
|
||||
** results, push the final position of the match.)
|
||||
*/
|
||||
int getcaptures (lua_State *L, const char *s, const char *r, int ptop) {
|
||||
Capture *capture = (Capture *)lua_touserdata(L, caplistidx(ptop));
|
||||
int n = 0;
|
||||
if (!isclosecap(capture)) { /* is there any capture? */
|
||||
CapState cs;
|
||||
cs.ocap = cs.cap = capture; cs.L = L;
|
||||
cs.s = s; cs.valuecached = 0; cs.ptop = ptop;
|
||||
do { /* collect their values */
|
||||
n += pushcapture(&cs);
|
||||
} while (!isclosecap(cs.cap));
|
||||
}
|
||||
if (n == 0) { /* no capture values? */
|
||||
lua_pushinteger(L, r - s + 1); /* return only end position */
|
||||
n = 1;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
43
3rd/lpeg/lpcap.h
Normal file
43
3rd/lpeg/lpcap.h
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
** $Id: lpcap.h,v 1.1 2013/03/21 20:25:12 roberto Exp $
|
||||
*/
|
||||
|
||||
#if !defined(lpcap_h)
|
||||
#define lpcap_h
|
||||
|
||||
|
||||
#include "lptypes.h"
|
||||
|
||||
|
||||
/* kinds of captures */
|
||||
typedef enum CapKind {
|
||||
Cclose, Cposition, Cconst, Cbackref, Carg, Csimple, Ctable, Cfunction,
|
||||
Cquery, Cstring, Cnum, Csubst, Cfold, Cruntime, Cgroup
|
||||
} CapKind;
|
||||
|
||||
|
||||
typedef struct Capture {
|
||||
const char *s; /* subject position */
|
||||
short idx; /* extra info about capture (group name, arg index, etc.) */
|
||||
byte kind; /* kind of capture */
|
||||
byte siz; /* size of full capture + 1 (0 = not a full capture) */
|
||||
} Capture;
|
||||
|
||||
|
||||
typedef struct CapState {
|
||||
Capture *cap; /* current capture */
|
||||
Capture *ocap; /* (original) capture list */
|
||||
lua_State *L;
|
||||
int ptop; /* index of last argument to 'match' */
|
||||
const char *s; /* original string */
|
||||
int valuecached; /* value stored in cache slot */
|
||||
} CapState;
|
||||
|
||||
|
||||
int runtimecap (CapState *cs, Capture *close, const char *s, int *rem);
|
||||
int getcaptures (lua_State *L, const char *s, const char *r, int ptop);
|
||||
int finddyncap (Capture *cap, Capture *last);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
963
3rd/lpeg/lpcode.c
Normal file
963
3rd/lpeg/lpcode.c
Normal file
@@ -0,0 +1,963 @@
|
||||
/*
|
||||
** $Id: lpcode.c,v 1.18 2013/04/12 16:30:33 roberto Exp $
|
||||
** Copyright 2007, Lua.org & PUC-Rio (see 'lpeg.html' for license)
|
||||
*/
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
|
||||
#include "lua.h"
|
||||
#include "lauxlib.h"
|
||||
|
||||
#include "lptypes.h"
|
||||
#include "lpcode.h"
|
||||
|
||||
|
||||
/* signals a "no-instruction */
|
||||
#define NOINST -1
|
||||
|
||||
|
||||
|
||||
static const Charset fullset_ =
|
||||
{{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}};
|
||||
|
||||
static const Charset *fullset = &fullset_;
|
||||
|
||||
/*
|
||||
** {======================================================
|
||||
** Analysis and some optimizations
|
||||
** =======================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
** Check whether a charset is empty (IFail), singleton (IChar),
|
||||
** full (IAny), or none of those (ISet).
|
||||
*/
|
||||
static Opcode charsettype (const byte *cs, int *c) {
|
||||
int count = 0;
|
||||
int i;
|
||||
int candidate = -1; /* candidate position for a char */
|
||||
for (i = 0; i < CHARSETSIZE; i++) {
|
||||
int b = cs[i];
|
||||
if (b == 0) {
|
||||
if (count > 1) return ISet; /* else set is still empty */
|
||||
}
|
||||
else if (b == 0xFF) {
|
||||
if (count < (i * BITSPERCHAR))
|
||||
return ISet;
|
||||
else count += BITSPERCHAR; /* set is still full */
|
||||
}
|
||||
else if ((b & (b - 1)) == 0) { /* byte has only one bit? */
|
||||
if (count > 0)
|
||||
return ISet; /* set is neither full nor empty */
|
||||
else { /* set has only one char till now; track it */
|
||||
count++;
|
||||
candidate = i;
|
||||
}
|
||||
}
|
||||
else return ISet; /* byte is neither empty, full, nor singleton */
|
||||
}
|
||||
switch (count) {
|
||||
case 0: return IFail; /* empty set */
|
||||
case 1: { /* singleton; find character bit inside byte */
|
||||
int b = cs[candidate];
|
||||
*c = candidate * BITSPERCHAR;
|
||||
if ((b & 0xF0) != 0) { *c += 4; b >>= 4; }
|
||||
if ((b & 0x0C) != 0) { *c += 2; b >>= 2; }
|
||||
if ((b & 0x02) != 0) { *c += 1; }
|
||||
return IChar;
|
||||
}
|
||||
default: {
|
||||
assert(count == CHARSETSIZE * BITSPERCHAR); /* full set */
|
||||
return IAny;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** A few basic operations on Charsets
|
||||
*/
|
||||
static void cs_complement (Charset *cs) {
|
||||
loopset(i, cs->cs[i] = ~cs->cs[i]);
|
||||
}
|
||||
|
||||
|
||||
static int cs_equal (const byte *cs1, const byte *cs2) {
|
||||
loopset(i, if (cs1[i] != cs2[i]) return 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** computes whether sets cs1 and cs2 are disjoint
|
||||
*/
|
||||
static int cs_disjoint (const Charset *cs1, const Charset *cs2) {
|
||||
loopset(i, if ((cs1->cs[i] & cs2->cs[i]) != 0) return 0;)
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Convert a 'char' pattern (TSet, TChar, TAny) to a charset
|
||||
*/
|
||||
int tocharset (TTree *tree, Charset *cs) {
|
||||
switch (tree->tag) {
|
||||
case TSet: { /* copy set */
|
||||
loopset(i, cs->cs[i] = treebuffer(tree)[i]);
|
||||
return 1;
|
||||
}
|
||||
case TChar: { /* only one char */
|
||||
assert(0 <= tree->u.n && tree->u.n <= UCHAR_MAX);
|
||||
loopset(i, cs->cs[i] = 0); /* erase all chars */
|
||||
setchar(cs->cs, tree->u.n); /* add that one */
|
||||
return 1;
|
||||
}
|
||||
case TAny: {
|
||||
loopset(i, cs->cs[i] = 0xFF); /* add all to the set */
|
||||
return 1;
|
||||
}
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Checks whether a pattern has captures
|
||||
*/
|
||||
int hascaptures (TTree *tree) {
|
||||
tailcall:
|
||||
switch (tree->tag) {
|
||||
case TCapture: case TRunTime:
|
||||
return 1;
|
||||
default: {
|
||||
switch (numsiblings[tree->tag]) {
|
||||
case 1: /* return hascaptures(sib1(tree)); */
|
||||
tree = sib1(tree); goto tailcall;
|
||||
case 2:
|
||||
if (hascaptures(sib1(tree))) return 1;
|
||||
/* else return hascaptures(sib2(tree)); */
|
||||
tree = sib2(tree); goto tailcall;
|
||||
default: assert(numsiblings[tree->tag] == 0); return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Checks how a pattern behaves regarding the empty string,
|
||||
** in one of two different ways:
|
||||
** A pattern is *nullable* if it can match without consuming any character;
|
||||
** A pattern is *nofail* if it never fails for any string
|
||||
** (including the empty string).
|
||||
** The difference is only for predicates and run-time captures;
|
||||
** for other patterns, the two properties are equivalent.
|
||||
** (With predicates, &'a' is nullable but not nofail. Of course,
|
||||
** nofail => nullable.)
|
||||
** These functions are all convervative in the following way:
|
||||
** p is nullable => nullable(p)
|
||||
** nofail(p) => p cannot fail
|
||||
** The function assumes that TOpenCall is not nullable;
|
||||
** this will be checked again when the grammar is fixed.)
|
||||
** Run-time captures can do whatever they want, so the result
|
||||
** is conservative.
|
||||
*/
|
||||
int checkaux (TTree *tree, int pred) {
|
||||
tailcall:
|
||||
switch (tree->tag) {
|
||||
case TChar: case TSet: case TAny:
|
||||
case TFalse: case TOpenCall:
|
||||
return 0; /* not nullable */
|
||||
case TRep: case TTrue:
|
||||
return 1; /* no fail */
|
||||
case TNot: case TBehind: /* can match empty, but can fail */
|
||||
if (pred == PEnofail) return 0;
|
||||
else return 1; /* PEnullable */
|
||||
case TAnd: /* can match empty; fail iff body does */
|
||||
if (pred == PEnullable) return 1;
|
||||
/* else return checkaux(sib1(tree), pred); */
|
||||
tree = sib1(tree); goto tailcall;
|
||||
case TRunTime: /* can fail; match empty iff body does */
|
||||
if (pred == PEnofail) return 0;
|
||||
/* else return checkaux(sib1(tree), pred); */
|
||||
tree = sib1(tree); goto tailcall;
|
||||
case TSeq:
|
||||
if (!checkaux(sib1(tree), pred)) return 0;
|
||||
/* else return checkaux(sib2(tree), pred); */
|
||||
tree = sib2(tree); goto tailcall;
|
||||
case TChoice:
|
||||
if (checkaux(sib2(tree), pred)) return 1;
|
||||
/* else return checkaux(sib1(tree), pred); */
|
||||
tree = sib1(tree); goto tailcall;
|
||||
case TCapture: case TGrammar: case TRule:
|
||||
/* return checkaux(sib1(tree), pred); */
|
||||
tree = sib1(tree); goto tailcall;
|
||||
case TCall: /* return checkaux(sib2(tree), pred); */
|
||||
tree = sib2(tree); goto tailcall;
|
||||
default: assert(0); return 0;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** number of characters to match a pattern (or -1 if variable)
|
||||
** ('count' avoids infinite loops for grammars)
|
||||
*/
|
||||
int fixedlenx (TTree *tree, int count, int len) {
|
||||
tailcall:
|
||||
switch (tree->tag) {
|
||||
case TChar: case TSet: case TAny:
|
||||
return len + 1;
|
||||
case TFalse: case TTrue: case TNot: case TAnd: case TBehind:
|
||||
return len;
|
||||
case TRep: case TRunTime: case TOpenCall:
|
||||
return -1;
|
||||
case TCapture: case TRule: case TGrammar:
|
||||
/* return fixedlenx(sib1(tree), count); */
|
||||
tree = sib1(tree); goto tailcall;
|
||||
case TCall:
|
||||
if (count++ >= MAXRULES)
|
||||
return -1; /* may be a loop */
|
||||
/* else return fixedlenx(sib2(tree), count); */
|
||||
tree = sib2(tree); goto tailcall;
|
||||
case TSeq: {
|
||||
len = fixedlenx(sib1(tree), count, len);
|
||||
if (len < 0) return -1;
|
||||
/* else return fixedlenx(sib2(tree), count, len); */
|
||||
tree = sib2(tree); goto tailcall;
|
||||
}
|
||||
case TChoice: {
|
||||
int n1, n2;
|
||||
n1 = fixedlenx(sib1(tree), count, len);
|
||||
if (n1 < 0) return -1;
|
||||
n2 = fixedlenx(sib2(tree), count, len);
|
||||
if (n1 == n2) return n1;
|
||||
else return -1;
|
||||
}
|
||||
default: assert(0); return 0;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Computes the 'first set' of a pattern.
|
||||
** The result is a conservative aproximation:
|
||||
** match p ax -> x' for some x ==> a in first(p).
|
||||
** The set 'follow' is the first set of what follows the
|
||||
** pattern (full set if nothing follows it).
|
||||
** The function returns 0 when this set can be used for
|
||||
** tests that avoid the pattern altogether.
|
||||
** A non-zero return can happen for two reasons:
|
||||
** 1) match p '' -> '' ==> returns 1.
|
||||
** (tests cannot be used because they always fail for an empty input)
|
||||
** 2) there is a match-time capture ==> returns 2.
|
||||
** (match-time captures should not be avoided by optimizations)
|
||||
*/
|
||||
static int getfirst (TTree *tree, const Charset *follow, Charset *firstset) {
|
||||
tailcall:
|
||||
switch (tree->tag) {
|
||||
case TChar: case TSet: case TAny: {
|
||||
tocharset(tree, firstset);
|
||||
return 0;
|
||||
}
|
||||
case TTrue: {
|
||||
loopset(i, firstset->cs[i] = follow->cs[i]);
|
||||
return 1;
|
||||
}
|
||||
case TFalse: {
|
||||
loopset(i, firstset->cs[i] = 0);
|
||||
return 0;
|
||||
}
|
||||
case TChoice: {
|
||||
Charset csaux;
|
||||
int e1 = getfirst(sib1(tree), follow, firstset);
|
||||
int e2 = getfirst(sib2(tree), follow, &csaux);
|
||||
loopset(i, firstset->cs[i] |= csaux.cs[i]);
|
||||
return e1 | e2;
|
||||
}
|
||||
case TSeq: {
|
||||
if (!nullable(sib1(tree))) {
|
||||
/* return getfirst(sib1(tree), fullset, firstset); */
|
||||
tree = sib1(tree); follow = fullset; goto tailcall;
|
||||
}
|
||||
else { /* FIRST(p1 p2, fl) = FIRST(p1, FIRST(p2, fl)) */
|
||||
Charset csaux;
|
||||
int e2 = getfirst(sib2(tree), follow, &csaux);
|
||||
int e1 = getfirst(sib1(tree), &csaux, firstset);
|
||||
if (e1 == 0) return 0; /* 'e1' ensures that first can be used */
|
||||
else if ((e1 | e2) & 2) /* one of the children has a matchtime? */
|
||||
return 2; /* pattern has a matchtime capture */
|
||||
else return e2; /* else depends on 'e2' */
|
||||
}
|
||||
}
|
||||
case TRep: {
|
||||
getfirst(sib1(tree), follow, firstset);
|
||||
loopset(i, firstset->cs[i] |= follow->cs[i]);
|
||||
return 1; /* accept the empty string */
|
||||
}
|
||||
case TCapture: case TGrammar: case TRule: {
|
||||
/* return getfirst(sib1(tree), follow, firstset); */
|
||||
tree = sib1(tree); goto tailcall;
|
||||
}
|
||||
case TRunTime: { /* function invalidates any follow info. */
|
||||
int e = getfirst(sib1(tree), fullset, firstset);
|
||||
if (e) return 2; /* function is not "protected"? */
|
||||
else return 0; /* pattern inside capture ensures first can be used */
|
||||
}
|
||||
case TCall: {
|
||||
/* return getfirst(sib2(tree), follow, firstset); */
|
||||
tree = sib2(tree); goto tailcall;
|
||||
}
|
||||
case TAnd: {
|
||||
int e = getfirst(sib1(tree), follow, firstset);
|
||||
loopset(i, firstset->cs[i] &= follow->cs[i]);
|
||||
return e;
|
||||
}
|
||||
case TNot: {
|
||||
if (tocharset(sib1(tree), firstset)) {
|
||||
cs_complement(firstset);
|
||||
return 1;
|
||||
}
|
||||
/* else go through */
|
||||
}
|
||||
case TBehind: { /* instruction gives no new information */
|
||||
/* call 'getfirst' to check for math-time captures */
|
||||
int e = getfirst(sib1(tree), follow, firstset);
|
||||
loopset(i, firstset->cs[i] = follow->cs[i]); /* uses follow */
|
||||
return e | 1; /* always can accept the empty string */
|
||||
}
|
||||
default: assert(0); return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** If it returns true, then pattern can fail only depending on the next
|
||||
** character of the subject
|
||||
*/
|
||||
static int headfail (TTree *tree) {
|
||||
tailcall:
|
||||
switch (tree->tag) {
|
||||
case TChar: case TSet: case TAny: case TFalse:
|
||||
return 1;
|
||||
case TTrue: case TRep: case TRunTime: case TNot:
|
||||
case TBehind:
|
||||
return 0;
|
||||
case TCapture: case TGrammar: case TRule: case TAnd:
|
||||
tree = sib1(tree); goto tailcall; /* return headfail(sib1(tree)); */
|
||||
case TCall:
|
||||
tree = sib2(tree); goto tailcall; /* return headfail(sib2(tree)); */
|
||||
case TSeq:
|
||||
if (!nofail(sib2(tree))) return 0;
|
||||
/* else return headfail(sib1(tree)); */
|
||||
tree = sib1(tree); goto tailcall;
|
||||
case TChoice:
|
||||
if (!headfail(sib1(tree))) return 0;
|
||||
/* else return headfail(sib2(tree)); */
|
||||
tree = sib2(tree); goto tailcall;
|
||||
default: assert(0); return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Check whether the code generation for the given tree can benefit
|
||||
** from a follow set (to avoid computing the follow set when it is
|
||||
** not needed)
|
||||
*/
|
||||
static int needfollow (TTree *tree) {
|
||||
tailcall:
|
||||
switch (tree->tag) {
|
||||
case TChar: case TSet: case TAny:
|
||||
case TFalse: case TTrue: case TAnd: case TNot:
|
||||
case TRunTime: case TGrammar: case TCall: case TBehind:
|
||||
return 0;
|
||||
case TChoice: case TRep:
|
||||
return 1;
|
||||
case TCapture:
|
||||
tree = sib1(tree); goto tailcall;
|
||||
case TSeq:
|
||||
tree = sib2(tree); goto tailcall;
|
||||
default: assert(0); return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* }====================================================== */
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** {======================================================
|
||||
** Code generation
|
||||
** =======================================================
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
** size of an instruction
|
||||
*/
|
||||
int sizei (const Instruction *i) {
|
||||
switch((Opcode)i->i.code) {
|
||||
case ISet: case ISpan: return CHARSETINSTSIZE;
|
||||
case ITestSet: return CHARSETINSTSIZE + 1;
|
||||
case ITestChar: case ITestAny: case IChoice: case IJmp:
|
||||
case ICall: case IOpenCall: case ICommit: case IPartialCommit:
|
||||
case IBackCommit: return 2;
|
||||
default: return 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** state for the compiler
|
||||
*/
|
||||
typedef struct CompileState {
|
||||
Pattern *p; /* pattern being compiled */
|
||||
int ncode; /* next position in p->code to be filled */
|
||||
lua_State *L;
|
||||
} CompileState;
|
||||
|
||||
|
||||
/*
|
||||
** code generation is recursive; 'opt' indicates that the code is
|
||||
** being generated under a 'IChoice' operator jumping to its end.
|
||||
** 'tt' points to a previous test protecting this code. 'fl' is
|
||||
** the follow set of the pattern.
|
||||
*/
|
||||
static void codegen (CompileState *compst, TTree *tree, int opt, int tt,
|
||||
const Charset *fl);
|
||||
|
||||
|
||||
void reallocprog (lua_State *L, Pattern *p, int nsize) {
|
||||
void *ud;
|
||||
lua_Alloc f = lua_getallocf(L, &ud);
|
||||
void *newblock = f(ud, p->code, p->codesize * sizeof(Instruction),
|
||||
nsize * sizeof(Instruction));
|
||||
if (newblock == NULL && nsize > 0)
|
||||
luaL_error(L, "not enough memory");
|
||||
p->code = (Instruction *)newblock;
|
||||
p->codesize = nsize;
|
||||
}
|
||||
|
||||
|
||||
static int nextinstruction (CompileState *compst) {
|
||||
int size = compst->p->codesize;
|
||||
if (compst->ncode >= size)
|
||||
reallocprog(compst->L, compst->p, size * 2);
|
||||
return compst->ncode++;
|
||||
}
|
||||
|
||||
|
||||
#define getinstr(cs,i) ((cs)->p->code[i])
|
||||
|
||||
|
||||
static int addinstruction (CompileState *compst, Opcode op, int aux) {
|
||||
int i = nextinstruction(compst);
|
||||
getinstr(compst, i).i.code = op;
|
||||
getinstr(compst, i).i.aux = aux;
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
static int addoffsetinst (CompileState *compst, Opcode op) {
|
||||
int i = addinstruction(compst, op, 0); /* instruction */
|
||||
addinstruction(compst, (Opcode)0, 0); /* open space for offset */
|
||||
assert(op == ITestSet || sizei(&getinstr(compst, i)) == 2);
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
static void setoffset (CompileState *compst, int instruction, int offset) {
|
||||
getinstr(compst, instruction + 1).offset = offset;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Add a capture instruction:
|
||||
** 'op' is the capture instruction; 'cap' the capture kind;
|
||||
** 'key' the key into ktable; 'aux' is optional offset
|
||||
**
|
||||
*/
|
||||
static int addinstcap (CompileState *compst, Opcode op, int cap, int key,
|
||||
int aux) {
|
||||
int i = addinstruction(compst, op, joinkindoff(cap, aux));
|
||||
getinstr(compst, i).i.key = key;
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
#define gethere(compst) ((compst)->ncode)
|
||||
|
||||
#define target(code,i) ((i) + code[i + 1].offset)
|
||||
|
||||
|
||||
static void jumptothere (CompileState *compst, int instruction, int target) {
|
||||
if (instruction >= 0)
|
||||
setoffset(compst, instruction, target - instruction);
|
||||
}
|
||||
|
||||
|
||||
static void jumptohere (CompileState *compst, int instruction) {
|
||||
jumptothere(compst, instruction, gethere(compst));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Code an IChar instruction, or IAny if there is an equivalent
|
||||
** test dominating it
|
||||
*/
|
||||
static void codechar (CompileState *compst, int c, int tt) {
|
||||
if (tt >= 0 && getinstr(compst, tt).i.code == ITestChar &&
|
||||
getinstr(compst, tt).i.aux == c)
|
||||
addinstruction(compst, IAny, 0);
|
||||
else
|
||||
addinstruction(compst, IChar, c);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Add a charset posfix to an instruction
|
||||
*/
|
||||
static void addcharset (CompileState *compst, const byte *cs) {
|
||||
int p = gethere(compst);
|
||||
int i;
|
||||
for (i = 0; i < (int)CHARSETINSTSIZE - 1; i++)
|
||||
nextinstruction(compst); /* space for buffer */
|
||||
/* fill buffer with charset */
|
||||
loopset(j, getinstr(compst, p).buff[j] = cs[j]);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** code a char set, optimizing unit sets for IChar, "complete"
|
||||
** sets for IAny, and empty sets for IFail; also use an IAny
|
||||
** when instruction is dominated by an equivalent test.
|
||||
*/
|
||||
static void codecharset (CompileState *compst, const byte *cs, int tt) {
|
||||
int c = 0; /* (=) to avoid warnings */
|
||||
Opcode op = charsettype(cs, &c);
|
||||
switch (op) {
|
||||
case IChar: codechar(compst, c, tt); break;
|
||||
case ISet: { /* non-trivial set? */
|
||||
if (tt >= 0 && getinstr(compst, tt).i.code == ITestSet &&
|
||||
cs_equal(cs, getinstr(compst, tt + 2).buff))
|
||||
addinstruction(compst, IAny, 0);
|
||||
else {
|
||||
addinstruction(compst, ISet, 0);
|
||||
addcharset(compst, cs);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: addinstruction(compst, op, c); break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** code a test set, optimizing unit sets for ITestChar, "complete"
|
||||
** sets for ITestAny, and empty sets for IJmp (always fails).
|
||||
** 'e' is true iff test should accept the empty string. (Test
|
||||
** instructions in the current VM never accept the empty string.)
|
||||
*/
|
||||
static int codetestset (CompileState *compst, Charset *cs, int e) {
|
||||
if (e) return NOINST; /* no test */
|
||||
else {
|
||||
int c = 0;
|
||||
Opcode op = charsettype(cs->cs, &c);
|
||||
switch (op) {
|
||||
case IFail: return addoffsetinst(compst, IJmp); /* always jump */
|
||||
case IAny: return addoffsetinst(compst, ITestAny);
|
||||
case IChar: {
|
||||
int i = addoffsetinst(compst, ITestChar);
|
||||
getinstr(compst, i).i.aux = c;
|
||||
return i;
|
||||
}
|
||||
case ISet: {
|
||||
int i = addoffsetinst(compst, ITestSet);
|
||||
addcharset(compst, cs->cs);
|
||||
return i;
|
||||
}
|
||||
default: assert(0); return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Find the final destination of a sequence of jumps
|
||||
*/
|
||||
static int finaltarget (Instruction *code, int i) {
|
||||
while (code[i].i.code == IJmp)
|
||||
i = target(code, i);
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** final label (after traversing any jumps)
|
||||
*/
|
||||
static int finallabel (Instruction *code, int i) {
|
||||
return finaltarget(code, target(code, i));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** <behind(p)> == behind n; <p> (where n = fixedlen(p))
|
||||
*/
|
||||
static void codebehind (CompileState *compst, TTree *tree) {
|
||||
if (tree->u.n > 0)
|
||||
addinstruction(compst, IBehind, tree->u.n);
|
||||
codegen(compst, sib1(tree), 0, NOINST, fullset);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Choice; optimizations:
|
||||
** - when p1 is headfail
|
||||
** - when first(p1) and first(p2) are disjoint; than
|
||||
** a character not in first(p1) cannot go to p1, and a character
|
||||
** in first(p1) cannot go to p2 (at it is not in first(p2)).
|
||||
** (The optimization is not valid if p1 accepts the empty string,
|
||||
** as then there is no character at all...)
|
||||
** - when p2 is empty and opt is true; a IPartialCommit can resuse
|
||||
** the Choice already active in the stack.
|
||||
*/
|
||||
static void codechoice (CompileState *compst, TTree *p1, TTree *p2, int opt,
|
||||
const Charset *fl) {
|
||||
int emptyp2 = (p2->tag == TTrue);
|
||||
Charset cs1, cs2;
|
||||
int e1 = getfirst(p1, fullset, &cs1);
|
||||
if (headfail(p1) ||
|
||||
(!e1 && (getfirst(p2, fl, &cs2), cs_disjoint(&cs1, &cs2)))) {
|
||||
/* <p1 / p2> == test (fail(p1)) -> L1 ; p1 ; jmp L2; L1: p2; L2: */
|
||||
int test = codetestset(compst, &cs1, 0);
|
||||
int jmp = NOINST;
|
||||
codegen(compst, p1, 0, test, fl);
|
||||
if (!emptyp2)
|
||||
jmp = addoffsetinst(compst, IJmp);
|
||||
jumptohere(compst, test);
|
||||
codegen(compst, p2, opt, NOINST, fl);
|
||||
jumptohere(compst, jmp);
|
||||
}
|
||||
else if (opt && emptyp2) {
|
||||
/* p1? == IPartialCommit; p1 */
|
||||
jumptohere(compst, addoffsetinst(compst, IPartialCommit));
|
||||
codegen(compst, p1, 1, NOINST, fullset);
|
||||
}
|
||||
else {
|
||||
/* <p1 / p2> ==
|
||||
test(fail(p1)) -> L1; choice L1; <p1>; commit L2; L1: <p2>; L2: */
|
||||
int pcommit;
|
||||
int test = codetestset(compst, &cs1, e1);
|
||||
int pchoice = addoffsetinst(compst, IChoice);
|
||||
codegen(compst, p1, emptyp2, test, fullset);
|
||||
pcommit = addoffsetinst(compst, ICommit);
|
||||
jumptohere(compst, pchoice);
|
||||
jumptohere(compst, test);
|
||||
codegen(compst, p2, opt, NOINST, fl);
|
||||
jumptohere(compst, pcommit);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** And predicate
|
||||
** optimization: fixedlen(p) = n ==> <&p> == <p>; behind n
|
||||
** (valid only when 'p' has no captures)
|
||||
*/
|
||||
static void codeand (CompileState *compst, TTree *tree, int tt) {
|
||||
int n = fixedlen(tree);
|
||||
if (n >= 0 && n <= MAXBEHIND && !hascaptures(tree)) {
|
||||
codegen(compst, tree, 0, tt, fullset);
|
||||
if (n > 0)
|
||||
addinstruction(compst, IBehind, n);
|
||||
}
|
||||
else { /* default: Choice L1; p1; BackCommit L2; L1: Fail; L2: */
|
||||
int pcommit;
|
||||
int pchoice = addoffsetinst(compst, IChoice);
|
||||
codegen(compst, tree, 0, tt, fullset);
|
||||
pcommit = addoffsetinst(compst, IBackCommit);
|
||||
jumptohere(compst, pchoice);
|
||||
addinstruction(compst, IFail, 0);
|
||||
jumptohere(compst, pcommit);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Captures: if pattern has fixed (and not too big) length, use
|
||||
** a single IFullCapture instruction after the match; otherwise,
|
||||
** enclose the pattern with OpenCapture - CloseCapture.
|
||||
*/
|
||||
static void codecapture (CompileState *compst, TTree *tree, int tt,
|
||||
const Charset *fl) {
|
||||
int len = fixedlen(sib1(tree));
|
||||
if (len >= 0 && len <= MAXOFF && !hascaptures(sib1(tree))) {
|
||||
codegen(compst, sib1(tree), 0, tt, fl);
|
||||
addinstcap(compst, IFullCapture, tree->cap, tree->key, len);
|
||||
}
|
||||
else {
|
||||
addinstcap(compst, IOpenCapture, tree->cap, tree->key, 0);
|
||||
codegen(compst, sib1(tree), 0, tt, fl);
|
||||
addinstcap(compst, ICloseCapture, Cclose, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void coderuntime (CompileState *compst, TTree *tree, int tt) {
|
||||
addinstcap(compst, IOpenCapture, Cgroup, tree->key, 0);
|
||||
codegen(compst, sib1(tree), 0, tt, fullset);
|
||||
addinstcap(compst, ICloseRunTime, Cclose, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Repetion; optimizations:
|
||||
** When pattern is a charset, can use special instruction ISpan.
|
||||
** When pattern is head fail, or if it starts with characters that
|
||||
** are disjoint from what follows the repetions, a simple test
|
||||
** is enough (a fail inside the repetition would backtrack to fail
|
||||
** again in the following pattern, so there is no need for a choice).
|
||||
** When 'opt' is true, the repetion can reuse the Choice already
|
||||
** active in the stack.
|
||||
*/
|
||||
static void coderep (CompileState *compst, TTree *tree, int opt,
|
||||
const Charset *fl) {
|
||||
Charset st;
|
||||
if (tocharset(tree, &st)) {
|
||||
addinstruction(compst, ISpan, 0);
|
||||
addcharset(compst, st.cs);
|
||||
}
|
||||
else {
|
||||
int e1 = getfirst(tree, fullset, &st);
|
||||
if (headfail(tree) || (!e1 && cs_disjoint(&st, fl))) {
|
||||
/* L1: test (fail(p1)) -> L2; <p>; jmp L1; L2: */
|
||||
int jmp;
|
||||
int test = codetestset(compst, &st, 0);
|
||||
codegen(compst, tree, opt, test, fullset);
|
||||
jmp = addoffsetinst(compst, IJmp);
|
||||
jumptohere(compst, test);
|
||||
jumptothere(compst, jmp, test);
|
||||
}
|
||||
else {
|
||||
/* test(fail(p1)) -> L2; choice L2; L1: <p>; partialcommit L1; L2: */
|
||||
/* or (if 'opt'): partialcommit L1; L1: <p>; partialcommit L1; */
|
||||
int commit, l2;
|
||||
int test = codetestset(compst, &st, e1);
|
||||
int pchoice = NOINST;
|
||||
if (opt)
|
||||
jumptohere(compst, addoffsetinst(compst, IPartialCommit));
|
||||
else
|
||||
pchoice = addoffsetinst(compst, IChoice);
|
||||
l2 = gethere(compst);
|
||||
codegen(compst, tree, 0, NOINST, fullset);
|
||||
commit = addoffsetinst(compst, IPartialCommit);
|
||||
jumptothere(compst, commit, l2);
|
||||
jumptohere(compst, pchoice);
|
||||
jumptohere(compst, test);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Not predicate; optimizations:
|
||||
** In any case, if first test fails, 'not' succeeds, so it can jump to
|
||||
** the end. If pattern is headfail, that is all (it cannot fail
|
||||
** in other parts); this case includes 'not' of simple sets. Otherwise,
|
||||
** use the default code (a choice plus a failtwice).
|
||||
*/
|
||||
static void codenot (CompileState *compst, TTree *tree) {
|
||||
Charset st;
|
||||
int e = getfirst(tree, fullset, &st);
|
||||
int test = codetestset(compst, &st, e);
|
||||
if (headfail(tree)) /* test (fail(p1)) -> L1; fail; L1: */
|
||||
addinstruction(compst, IFail, 0);
|
||||
else {
|
||||
/* test(fail(p))-> L1; choice L1; <p>; failtwice; L1: */
|
||||
int pchoice = addoffsetinst(compst, IChoice);
|
||||
codegen(compst, tree, 0, NOINST, fullset);
|
||||
addinstruction(compst, IFailTwice, 0);
|
||||
jumptohere(compst, pchoice);
|
||||
}
|
||||
jumptohere(compst, test);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** change open calls to calls, using list 'positions' to find
|
||||
** correct offsets; also optimize tail calls
|
||||
*/
|
||||
static void correctcalls (CompileState *compst, int *positions,
|
||||
int from, int to) {
|
||||
int i;
|
||||
Instruction *code = compst->p->code;
|
||||
for (i = from; i < to; i += sizei(&code[i])) {
|
||||
if (code[i].i.code == IOpenCall) {
|
||||
int n = code[i].i.key; /* rule number */
|
||||
int rule = positions[n]; /* rule position */
|
||||
assert(rule == from || code[rule - 1].i.code == IRet);
|
||||
if (code[finaltarget(code, i + 2)].i.code == IRet) /* call; ret ? */
|
||||
code[i].i.code = IJmp; /* tail call */
|
||||
else
|
||||
code[i].i.code = ICall;
|
||||
jumptothere(compst, i, rule); /* call jumps to respective rule */
|
||||
}
|
||||
}
|
||||
assert(i == to);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Code for a grammar:
|
||||
** call L1; jmp L2; L1: rule 1; ret; rule 2; ret; ...; L2:
|
||||
*/
|
||||
static void codegrammar (CompileState *compst, TTree *grammar) {
|
||||
int positions[MAXRULES];
|
||||
int rulenumber = 0;
|
||||
TTree *rule;
|
||||
int firstcall = addoffsetinst(compst, ICall); /* call initial rule */
|
||||
int jumptoend = addoffsetinst(compst, IJmp); /* jump to the end */
|
||||
int start = gethere(compst); /* here starts the initial rule */
|
||||
jumptohere(compst, firstcall);
|
||||
for (rule = sib1(grammar); rule->tag == TRule; rule = sib2(rule)) {
|
||||
positions[rulenumber++] = gethere(compst); /* save rule position */
|
||||
codegen(compst, sib1(rule), 0, NOINST, fullset); /* code rule */
|
||||
addinstruction(compst, IRet, 0);
|
||||
}
|
||||
assert(rule->tag == TTrue);
|
||||
jumptohere(compst, jumptoend);
|
||||
correctcalls(compst, positions, start, gethere(compst));
|
||||
}
|
||||
|
||||
|
||||
static void codecall (CompileState *compst, TTree *call) {
|
||||
int c = addoffsetinst(compst, IOpenCall); /* to be corrected later */
|
||||
getinstr(compst, c).i.key = sib2(call)->cap; /* rule number */
|
||||
assert(sib2(call)->tag == TRule);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Code first child of a sequence
|
||||
** (second child is called in-place to allow tail call)
|
||||
** Return 'tt' for second child
|
||||
*/
|
||||
static int codeseq1 (CompileState *compst, TTree *p1, TTree *p2,
|
||||
int tt, const Charset *fl) {
|
||||
if (needfollow(p1)) {
|
||||
Charset fl1;
|
||||
getfirst(p2, fl, &fl1); /* p1 follow is p2 first */
|
||||
codegen(compst, p1, 0, tt, &fl1);
|
||||
}
|
||||
else /* use 'fullset' as follow */
|
||||
codegen(compst, p1, 0, tt, fullset);
|
||||
if (fixedlen(p1) != 0) /* can 'p1' consume anything? */
|
||||
return NOINST; /* invalidate test */
|
||||
else return tt; /* else 'tt' still protects sib2 */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Main code-generation function: dispatch to auxiliar functions
|
||||
** according to kind of tree
|
||||
*/
|
||||
static void codegen (CompileState *compst, TTree *tree, int opt, int tt,
|
||||
const Charset *fl) {
|
||||
tailcall:
|
||||
switch (tree->tag) {
|
||||
case TChar: codechar(compst, tree->u.n, tt); break;
|
||||
case TAny: addinstruction(compst, IAny, 0); break;
|
||||
case TSet: codecharset(compst, treebuffer(tree), tt); break;
|
||||
case TTrue: break;
|
||||
case TFalse: addinstruction(compst, IFail, 0); break;
|
||||
case TChoice: codechoice(compst, sib1(tree), sib2(tree), opt, fl); break;
|
||||
case TRep: coderep(compst, sib1(tree), opt, fl); break;
|
||||
case TBehind: codebehind(compst, tree); break;
|
||||
case TNot: codenot(compst, sib1(tree)); break;
|
||||
case TAnd: codeand(compst, sib1(tree), tt); break;
|
||||
case TCapture: codecapture(compst, tree, tt, fl); break;
|
||||
case TRunTime: coderuntime(compst, tree, tt); break;
|
||||
case TGrammar: codegrammar(compst, tree); break;
|
||||
case TCall: codecall(compst, tree); break;
|
||||
case TSeq: {
|
||||
tt = codeseq1(compst, sib1(tree), sib2(tree), tt, fl); /* code 'p1' */
|
||||
/* codegen(compst, p2, opt, tt, fl); */
|
||||
tree = sib2(tree); goto tailcall;
|
||||
}
|
||||
default: assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Optimize jumps and other jump-like instructions.
|
||||
** * Update labels of instructions with labels to their final
|
||||
** destinations (e.g., choice L1; ... L1: jmp L2: becomes
|
||||
** choice L2)
|
||||
** * Jumps to other instructions that do jumps become those
|
||||
** instructions (e.g., jump to return becomes a return; jump
|
||||
** to commit becomes a commit)
|
||||
*/
|
||||
static void peephole (CompileState *compst) {
|
||||
Instruction *code = compst->p->code;
|
||||
int i;
|
||||
for (i = 0; i < compst->ncode; i += sizei(&code[i])) {
|
||||
switch (code[i].i.code) {
|
||||
case IChoice: case ICall: case ICommit: case IPartialCommit:
|
||||
case IBackCommit: case ITestChar: case ITestSet:
|
||||
case ITestAny: { /* instructions with labels */
|
||||
jumptothere(compst, i, finallabel(code, i)); /* optimize label */
|
||||
break;
|
||||
}
|
||||
case IJmp: {
|
||||
int ft = finaltarget(code, i);
|
||||
switch (code[ft].i.code) { /* jumping to what? */
|
||||
case IRet: case IFail: case IFailTwice:
|
||||
case IEnd: { /* instructions with unconditional implicit jumps */
|
||||
code[i] = code[ft]; /* jump becomes that instruction */
|
||||
code[i + 1].i.code = IAny; /* 'no-op' for target position */
|
||||
break;
|
||||
}
|
||||
case ICommit: case IPartialCommit:
|
||||
case IBackCommit: { /* inst. with unconditional explicit jumps */
|
||||
int fft = finallabel(code, ft);
|
||||
code[i] = code[ft]; /* jump becomes that instruction... */
|
||||
jumptothere(compst, i, fft); /* but must correct its offset */
|
||||
i--; /* reoptimize its label */
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
jumptothere(compst, i, ft); /* optimize label */
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
assert(code[i - 1].i.code == IEnd);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Compile a pattern
|
||||
*/
|
||||
Instruction *compile (lua_State *L, Pattern *p) {
|
||||
CompileState compst;
|
||||
compst.p = p; compst.ncode = 0; compst.L = L;
|
||||
reallocprog(L, p, 2); /* minimum initial size */
|
||||
codegen(&compst, p->tree, 0, NOINST, fullset);
|
||||
addinstruction(&compst, IEnd, 0);
|
||||
reallocprog(L, p, compst.ncode); /* set final size */
|
||||
peephole(&compst);
|
||||
return p->code;
|
||||
}
|
||||
|
||||
|
||||
/* }====================================================== */
|
||||
|
||||
34
3rd/lpeg/lpcode.h
Normal file
34
3rd/lpeg/lpcode.h
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
** $Id: lpcode.h,v 1.5 2013/04/04 21:24:45 roberto Exp $
|
||||
*/
|
||||
|
||||
#if !defined(lpcode_h)
|
||||
#define lpcode_h
|
||||
|
||||
#include "lua.h"
|
||||
|
||||
#include "lptypes.h"
|
||||
#include "lptree.h"
|
||||
#include "lpvm.h"
|
||||
|
||||
int tocharset (TTree *tree, Charset *cs);
|
||||
int checkaux (TTree *tree, int pred);
|
||||
int fixedlenx (TTree *tree, int count, int len);
|
||||
int hascaptures (TTree *tree);
|
||||
int lp_gc (lua_State *L);
|
||||
Instruction *compile (lua_State *L, Pattern *p);
|
||||
void reallocprog (lua_State *L, Pattern *p, int nsize);
|
||||
int sizei (const Instruction *i);
|
||||
|
||||
|
||||
#define PEnullable 0
|
||||
#define PEnofail 1
|
||||
|
||||
#define nofail(t) checkaux(t, PEnofail)
|
||||
#define nullable(t) checkaux(t, PEnullable)
|
||||
|
||||
#define fixedlen(t) fixedlenx(t, 0, 0)
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
BIN
3rd/lpeg/lpeg-128.gif
Normal file
BIN
3rd/lpeg/lpeg-128.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.8 KiB |
1429
3rd/lpeg/lpeg.html
Normal file
1429
3rd/lpeg/lpeg.html
Normal file
File diff suppressed because it is too large
Load Diff
244
3rd/lpeg/lpprint.c
Normal file
244
3rd/lpeg/lpprint.c
Normal file
@@ -0,0 +1,244 @@
|
||||
/*
|
||||
** $Id: lpprint.c,v 1.7 2013/04/12 16:29:49 roberto Exp $
|
||||
** Copyright 2007, Lua.org & PUC-Rio (see 'lpeg.html' for license)
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
#include "lptypes.h"
|
||||
#include "lpprint.h"
|
||||
#include "lpcode.h"
|
||||
|
||||
|
||||
#if defined(LPEG_DEBUG)
|
||||
|
||||
/*
|
||||
** {======================================================
|
||||
** Printing patterns (for debugging)
|
||||
** =======================================================
|
||||
*/
|
||||
|
||||
|
||||
void printcharset (const byte *st) {
|
||||
int i;
|
||||
printf("[");
|
||||
for (i = 0; i <= UCHAR_MAX; i++) {
|
||||
int first = i;
|
||||
while (testchar(st, i) && i <= UCHAR_MAX) i++;
|
||||
if (i - 1 == first) /* unary range? */
|
||||
printf("(%02x)", first);
|
||||
else if (i - 1 > first) /* non-empty range? */
|
||||
printf("(%02x-%02x)", first, i - 1);
|
||||
}
|
||||
printf("]");
|
||||
}
|
||||
|
||||
|
||||
static void printcapkind (int kind) {
|
||||
const char *const modes[] = {
|
||||
"close", "position", "constant", "backref",
|
||||
"argument", "simple", "table", "function",
|
||||
"query", "string", "num", "substitution", "fold",
|
||||
"runtime", "group"};
|
||||
printf("%s", modes[kind]);
|
||||
}
|
||||
|
||||
|
||||
static void printjmp (const Instruction *op, const Instruction *p) {
|
||||
printf("-> %d", (int)(p + (p + 1)->offset - op));
|
||||
}
|
||||
|
||||
|
||||
static void printinst (const Instruction *op, const Instruction *p) {
|
||||
const char *const names[] = {
|
||||
"any", "char", "set",
|
||||
"testany", "testchar", "testset",
|
||||
"span", "behind",
|
||||
"ret", "end",
|
||||
"choice", "jmp", "call", "open_call",
|
||||
"commit", "partial_commit", "back_commit", "failtwice", "fail", "giveup",
|
||||
"fullcapture", "opencapture", "closecapture", "closeruntime"
|
||||
};
|
||||
printf("%02ld: %s ", (long)(p - op), names[p->i.code]);
|
||||
switch ((Opcode)p->i.code) {
|
||||
case IChar: {
|
||||
printf("'%c'", p->i.aux);
|
||||
break;
|
||||
}
|
||||
case ITestChar: {
|
||||
printf("'%c'", p->i.aux); printjmp(op, p);
|
||||
break;
|
||||
}
|
||||
case IFullCapture: {
|
||||
printcapkind(getkind(p));
|
||||
printf(" (size = %d) (idx = %d)", getoff(p), p->i.key);
|
||||
break;
|
||||
}
|
||||
case IOpenCapture: {
|
||||
printcapkind(getkind(p));
|
||||
printf(" (idx = %d)", p->i.key);
|
||||
break;
|
||||
}
|
||||
case ISet: {
|
||||
printcharset((p+1)->buff);
|
||||
break;
|
||||
}
|
||||
case ITestSet: {
|
||||
printcharset((p+2)->buff); printjmp(op, p);
|
||||
break;
|
||||
}
|
||||
case ISpan: {
|
||||
printcharset((p+1)->buff);
|
||||
break;
|
||||
}
|
||||
case IOpenCall: {
|
||||
printf("-> %d", (p + 1)->offset);
|
||||
break;
|
||||
}
|
||||
case IBehind: {
|
||||
printf("%d", p->i.aux);
|
||||
break;
|
||||
}
|
||||
case IJmp: case ICall: case ICommit: case IChoice:
|
||||
case IPartialCommit: case IBackCommit: case ITestAny: {
|
||||
printjmp(op, p);
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
|
||||
void printpatt (Instruction *p, int n) {
|
||||
Instruction *op = p;
|
||||
while (p < op + n) {
|
||||
printinst(op, p);
|
||||
p += sizei(p);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if defined(LPEG_DEBUG)
|
||||
static void printcap (Capture *cap) {
|
||||
printcapkind(cap->kind);
|
||||
printf(" (idx: %d - size: %d) -> %p\n", cap->idx, cap->siz, cap->s);
|
||||
}
|
||||
|
||||
|
||||
void printcaplist (Capture *cap, Capture *limit) {
|
||||
printf(">======\n");
|
||||
for (; cap->s && (limit == NULL || cap < limit); cap++)
|
||||
printcap(cap);
|
||||
printf("=======\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
/* }====================================================== */
|
||||
|
||||
|
||||
/*
|
||||
** {======================================================
|
||||
** Printing trees (for debugging)
|
||||
** =======================================================
|
||||
*/
|
||||
|
||||
static const char *tagnames[] = {
|
||||
"char", "set", "any",
|
||||
"true", "false",
|
||||
"rep",
|
||||
"seq", "choice",
|
||||
"not", "and",
|
||||
"call", "opencall", "rule", "grammar",
|
||||
"behind",
|
||||
"capture", "run-time"
|
||||
};
|
||||
|
||||
|
||||
void printtree (TTree *tree, int ident) {
|
||||
int i;
|
||||
for (i = 0; i < ident; i++) printf(" ");
|
||||
printf("%s", tagnames[tree->tag]);
|
||||
switch (tree->tag) {
|
||||
case TChar: {
|
||||
int c = tree->u.n;
|
||||
if (isprint(c))
|
||||
printf(" '%c'\n", c);
|
||||
else
|
||||
printf(" (%02X)\n", c);
|
||||
break;
|
||||
}
|
||||
case TSet: {
|
||||
printcharset(treebuffer(tree));
|
||||
printf("\n");
|
||||
break;
|
||||
}
|
||||
case TOpenCall: case TCall: {
|
||||
printf(" key: %d\n", tree->key);
|
||||
break;
|
||||
}
|
||||
case TBehind: {
|
||||
printf(" %d\n", tree->u.n);
|
||||
printtree(sib1(tree), ident + 2);
|
||||
break;
|
||||
}
|
||||
case TCapture: {
|
||||
printf(" cap: %d key: %d n: %d\n", tree->cap, tree->key, tree->u.n);
|
||||
printtree(sib1(tree), ident + 2);
|
||||
break;
|
||||
}
|
||||
case TRule: {
|
||||
printf(" n: %d key: %d\n", tree->cap, tree->key);
|
||||
printtree(sib1(tree), ident + 2);
|
||||
break; /* do not print next rule as a sibling */
|
||||
}
|
||||
case TGrammar: {
|
||||
TTree *rule = sib1(tree);
|
||||
printf(" %d\n", tree->u.n); /* number of rules */
|
||||
for (i = 0; i < tree->u.n; i++) {
|
||||
printtree(rule, ident + 2);
|
||||
rule = sib2(rule);
|
||||
}
|
||||
assert(rule->tag == TTrue); /* sentinel */
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
int sibs = numsiblings[tree->tag];
|
||||
printf("\n");
|
||||
if (sibs >= 1) {
|
||||
printtree(sib1(tree), ident + 2);
|
||||
if (sibs >= 2)
|
||||
printtree(sib2(tree), ident + 2);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void printktable (lua_State *L, int idx) {
|
||||
int n, i;
|
||||
lua_getfenv(L, idx);
|
||||
if (lua_isnil(L, -1)) /* no ktable? */
|
||||
return;
|
||||
n = lua_objlen(L, -1);
|
||||
printf("[");
|
||||
for (i = 1; i <= n; i++) {
|
||||
printf("%d = ", i);
|
||||
lua_rawgeti(L, -1, i);
|
||||
if (lua_isstring(L, -1))
|
||||
printf("%s ", lua_tostring(L, -1));
|
||||
else
|
||||
printf("%s ", lua_typename(L, lua_type(L, -1)));
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
printf("]\n");
|
||||
/* leave ktable at the stack */
|
||||
}
|
||||
|
||||
/* }====================================================== */
|
||||
|
||||
#endif
|
||||
35
3rd/lpeg/lpprint.h
Normal file
35
3rd/lpeg/lpprint.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
** $Id: lpprint.h,v 1.1 2013/03/21 20:25:12 roberto Exp $
|
||||
*/
|
||||
|
||||
|
||||
#if !defined(lpprint_h)
|
||||
#define lpprint_h
|
||||
|
||||
|
||||
#include "lptree.h"
|
||||
#include "lpvm.h"
|
||||
|
||||
|
||||
#if defined(LPEG_DEBUG)
|
||||
|
||||
void printpatt (Instruction *p, int n);
|
||||
void printtree (TTree *tree, int ident);
|
||||
void printktable (lua_State *L, int idx);
|
||||
void printcharset (const byte *st);
|
||||
void printcaplist (Capture *cap, Capture *limit);
|
||||
|
||||
#else
|
||||
|
||||
#define printktable(L,idx) \
|
||||
luaL_error(L, "function only implemented in debug mode")
|
||||
#define printtree(tree,i) \
|
||||
luaL_error(L, "function only implemented in debug mode")
|
||||
#define printpatt(p,n) \
|
||||
luaL_error(L, "function only implemented in debug mode")
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
1232
3rd/lpeg/lptree.c
Normal file
1232
3rd/lpeg/lptree.c
Normal file
File diff suppressed because it is too large
Load Diff
77
3rd/lpeg/lptree.h
Normal file
77
3rd/lpeg/lptree.h
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
** $Id: lptree.h,v 1.2 2013/03/24 13:51:12 roberto Exp $
|
||||
*/
|
||||
|
||||
#if !defined(lptree_h)
|
||||
#define lptree_h
|
||||
|
||||
|
||||
#include "lptypes.h"
|
||||
|
||||
|
||||
/*
|
||||
** types of trees
|
||||
*/
|
||||
typedef enum TTag {
|
||||
TChar = 0, TSet, TAny, /* standard PEG elements */
|
||||
TTrue, TFalse,
|
||||
TRep,
|
||||
TSeq, TChoice,
|
||||
TNot, TAnd,
|
||||
TCall,
|
||||
TOpenCall,
|
||||
TRule, /* sib1 is rule's pattern, sib2 is 'next' rule */
|
||||
TGrammar, /* sib1 is initial (and first) rule */
|
||||
TBehind, /* match behind */
|
||||
TCapture, /* regular capture */
|
||||
TRunTime /* run-time capture */
|
||||
} TTag;
|
||||
|
||||
/* number of siblings for each tree */
|
||||
extern const byte numsiblings[];
|
||||
|
||||
|
||||
/*
|
||||
** Tree trees
|
||||
** The first sibling of a tree (if there is one) is immediately after
|
||||
** the tree. A reference to a second sibling (ps) is its position
|
||||
** relative to the position of the tree itself. A key in ktable
|
||||
** uses the (unique) address of the original tree that created that
|
||||
** entry. NULL means no data.
|
||||
*/
|
||||
typedef struct TTree {
|
||||
byte tag;
|
||||
byte cap; /* kind of capture (if it is a capture) */
|
||||
unsigned short key; /* key in ktable for Lua data (0 if no key) */
|
||||
union {
|
||||
int ps; /* occasional second sibling */
|
||||
int n; /* occasional counter */
|
||||
} u;
|
||||
} TTree;
|
||||
|
||||
|
||||
/*
|
||||
** A complete pattern has its tree plus, if already compiled,
|
||||
** its corresponding code
|
||||
*/
|
||||
typedef struct Pattern {
|
||||
union Instruction *code;
|
||||
int codesize;
|
||||
TTree tree[1];
|
||||
} Pattern;
|
||||
|
||||
|
||||
/* number of siblings for each tree */
|
||||
extern const byte numsiblings[];
|
||||
|
||||
/* access to siblings */
|
||||
#define sib1(t) ((t) + 1)
|
||||
#define sib2(t) ((t) + (t)->u.ps)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
147
3rd/lpeg/lptypes.h
Normal file
147
3rd/lpeg/lptypes.h
Normal file
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
** $Id: lptypes.h,v 1.8 2013/04/12 16:26:38 roberto Exp $
|
||||
** LPeg - PEG pattern matching for Lua
|
||||
** Copyright 2007, Lua.org & PUC-Rio (see 'lpeg.html' for license)
|
||||
** written by Roberto Ierusalimschy
|
||||
*/
|
||||
|
||||
#if !defined(lptypes_h)
|
||||
#define lptypes_h
|
||||
|
||||
|
||||
#if !defined(LPEG_DEBUG)
|
||||
#define NDEBUG
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "lua.h"
|
||||
|
||||
|
||||
#define VERSION "0.12"
|
||||
|
||||
|
||||
#define PATTERN_T "lpeg-pattern"
|
||||
#define MAXSTACKIDX "lpeg-maxstack"
|
||||
|
||||
|
||||
/*
|
||||
** compatibility with Lua 5.2
|
||||
*/
|
||||
#if (LUA_VERSION_NUM == 502)
|
||||
|
||||
#undef lua_equal
|
||||
#define lua_equal(L,idx1,idx2) lua_compare(L,(idx1),(idx2),LUA_OPEQ)
|
||||
|
||||
#undef lua_getfenv
|
||||
#define lua_getfenv lua_getuservalue
|
||||
#undef lua_setfenv
|
||||
#define lua_setfenv lua_setuservalue
|
||||
|
||||
#undef lua_objlen
|
||||
#define lua_objlen lua_rawlen
|
||||
|
||||
#undef luaL_register
|
||||
#define luaL_register(L,n,f) \
|
||||
{ if ((n) == NULL) luaL_setfuncs(L,f,0); else luaL_newlib(L,f); }
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/* default maximum size for call/backtrack stack */
|
||||
#if !defined(MAXBACK)
|
||||
#define MAXBACK 100
|
||||
#endif
|
||||
|
||||
|
||||
/* maximum number of rules in a grammar */
|
||||
#define MAXRULES 200
|
||||
|
||||
|
||||
|
||||
/* initial size for capture's list */
|
||||
#define INITCAPSIZE 32
|
||||
|
||||
|
||||
/* index, on Lua stack, for subject */
|
||||
#define SUBJIDX 2
|
||||
|
||||
/* number of fixed arguments to 'match' (before capture arguments) */
|
||||
#define FIXEDARGS 3
|
||||
|
||||
/* index, on Lua stack, for capture list */
|
||||
#define caplistidx(ptop) ((ptop) + 2)
|
||||
|
||||
/* index, on Lua stack, for pattern's ktable */
|
||||
#define ktableidx(ptop) ((ptop) + 3)
|
||||
|
||||
/* index, on Lua stack, for backtracking stack */
|
||||
#define stackidx(ptop) ((ptop) + 4)
|
||||
|
||||
|
||||
|
||||
typedef unsigned char byte;
|
||||
|
||||
|
||||
#define BITSPERCHAR 8
|
||||
|
||||
#define CHARSETSIZE ((UCHAR_MAX/BITSPERCHAR) + 1)
|
||||
|
||||
|
||||
|
||||
typedef struct Charset {
|
||||
byte cs[CHARSETSIZE];
|
||||
} Charset;
|
||||
|
||||
|
||||
|
||||
#define loopset(v,b) { int v; for (v = 0; v < CHARSETSIZE; v++) {b;} }
|
||||
|
||||
/* access to charset */
|
||||
#define treebuffer(t) ((byte *)((t) + 1))
|
||||
|
||||
/* number of slots needed for 'n' bytes */
|
||||
#define bytes2slots(n) (((n) - 1) / sizeof(TTree) + 1)
|
||||
|
||||
/* set 'b' bit in charset 'cs' */
|
||||
#define setchar(cs,b) ((cs)[(b) >> 3] |= (1 << ((b) & 7)))
|
||||
|
||||
|
||||
/*
|
||||
** in capture instructions, 'kind' of capture and its offset are
|
||||
** packed in field 'aux', 4 bits for each
|
||||
*/
|
||||
#define getkind(op) ((op)->i.aux & 0xF)
|
||||
#define getoff(op) (((op)->i.aux >> 4) & 0xF)
|
||||
#define joinkindoff(k,o) ((k) | ((o) << 4))
|
||||
|
||||
#define MAXOFF 0xF
|
||||
#define MAXAUX 0xFF
|
||||
|
||||
|
||||
/* maximum number of bytes to look behind */
|
||||
#define MAXBEHIND MAXAUX
|
||||
|
||||
|
||||
/* maximum size (in elements) for a pattern */
|
||||
#define MAXPATTSIZE (SHRT_MAX - 10)
|
||||
|
||||
|
||||
/* size (in elements) for an instruction plus extra l bytes */
|
||||
#define instsize(l) (((l) + sizeof(Instruction) - 1)/sizeof(Instruction) + 1)
|
||||
|
||||
|
||||
/* size (in elements) for a ISet instruction */
|
||||
#define CHARSETINSTSIZE instsize(CHARSETSIZE)
|
||||
|
||||
/* size (in elements) for a IFunc instruction */
|
||||
#define funcinstsize(p) ((p)->i.aux + 2)
|
||||
|
||||
|
||||
|
||||
#define testchar(st,c) (((int)(st)[((c) >> 3)] & (1 << ((c) & 7))))
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
355
3rd/lpeg/lpvm.c
Normal file
355
3rd/lpeg/lpvm.c
Normal file
@@ -0,0 +1,355 @@
|
||||
/*
|
||||
** $Id: lpvm.c,v 1.5 2013/04/12 16:29:49 roberto Exp $
|
||||
** Copyright 2007, Lua.org & PUC-Rio (see 'lpeg.html' for license)
|
||||
*/
|
||||
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
#include "lua.h"
|
||||
#include "lauxlib.h"
|
||||
|
||||
#include "lpcap.h"
|
||||
#include "lptypes.h"
|
||||
#include "lpvm.h"
|
||||
#include "lpprint.h"
|
||||
|
||||
|
||||
/* initial size for call/backtrack stack */
|
||||
#if !defined(INITBACK)
|
||||
#define INITBACK 100
|
||||
#endif
|
||||
|
||||
|
||||
#define getoffset(p) (((p) + 1)->offset)
|
||||
|
||||
static const Instruction giveup = {{IGiveup, 0, 0}};
|
||||
|
||||
|
||||
/*
|
||||
** {======================================================
|
||||
** Virtual Machine
|
||||
** =======================================================
|
||||
*/
|
||||
|
||||
|
||||
typedef struct Stack {
|
||||
const char *s; /* saved position (or NULL for calls) */
|
||||
const Instruction *p; /* next instruction */
|
||||
int caplevel;
|
||||
} Stack;
|
||||
|
||||
|
||||
#define getstackbase(L, ptop) ((Stack *)lua_touserdata(L, stackidx(ptop)))
|
||||
|
||||
|
||||
/*
|
||||
** Double the size of the array of captures
|
||||
*/
|
||||
static Capture *doublecap (lua_State *L, Capture *cap, int captop, int ptop) {
|
||||
Capture *newc;
|
||||
if (captop >= INT_MAX/((int)sizeof(Capture) * 2))
|
||||
luaL_error(L, "too many captures");
|
||||
newc = (Capture *)lua_newuserdata(L, captop * 2 * sizeof(Capture));
|
||||
memcpy(newc, cap, captop * sizeof(Capture));
|
||||
lua_replace(L, caplistidx(ptop));
|
||||
return newc;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Double the size of the stack
|
||||
*/
|
||||
static Stack *doublestack (lua_State *L, Stack **stacklimit, int ptop) {
|
||||
Stack *stack = getstackbase(L, ptop);
|
||||
Stack *newstack;
|
||||
int n = *stacklimit - stack; /* current stack size */
|
||||
int max, newn;
|
||||
lua_getfield(L, LUA_REGISTRYINDEX, MAXSTACKIDX);
|
||||
max = lua_tointeger(L, -1); /* maximum allowed size */
|
||||
lua_pop(L, 1);
|
||||
if (n >= max) /* already at maximum size? */
|
||||
luaL_error(L, "too many pending calls/choices");
|
||||
newn = 2 * n; /* new size */
|
||||
if (newn > max) newn = max;
|
||||
newstack = (Stack *)lua_newuserdata(L, newn * sizeof(Stack));
|
||||
memcpy(newstack, stack, n * sizeof(Stack));
|
||||
lua_replace(L, stackidx(ptop));
|
||||
*stacklimit = newstack + newn;
|
||||
return newstack + n; /* return next position */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Interpret the result of a dynamic capture: false -> fail;
|
||||
** true -> keep current position; number -> next position.
|
||||
** Return new subject position. 'fr' is stack index where
|
||||
** is the result; 'curr' is current subject position; 'limit'
|
||||
** is subject's size.
|
||||
*/
|
||||
static int resdyncaptures (lua_State *L, int fr, int curr, int limit) {
|
||||
lua_Integer res;
|
||||
if (!lua_toboolean(L, fr)) { /* false value? */
|
||||
lua_settop(L, fr - 1); /* remove results */
|
||||
return -1; /* and fail */
|
||||
}
|
||||
else if (lua_isboolean(L, fr)) /* true? */
|
||||
res = curr; /* keep current position */
|
||||
else {
|
||||
res = lua_tointeger(L, fr) - 1; /* new position */
|
||||
if (res < curr || res > limit)
|
||||
luaL_error(L, "invalid position returned by match-time capture");
|
||||
}
|
||||
lua_remove(L, fr); /* remove first result (offset) */
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Add capture values returned by a dynamic capture to the capture list
|
||||
** 'base', nested inside a group capture. 'fd' indexes the first capture
|
||||
** value, 'n' is the number of values (at least 1).
|
||||
*/
|
||||
static void adddyncaptures (const char *s, Capture *base, int n, int fd) {
|
||||
int i;
|
||||
/* Cgroup capture is already there */
|
||||
assert(base[0].kind == Cgroup && base[0].siz == 0);
|
||||
base[0].idx = 0; /* make it an anonymous group */
|
||||
for (i = 1; i <= n; i++) { /* add runtime captures */
|
||||
base[i].kind = Cruntime;
|
||||
base[i].siz = 1; /* mark it as closed */
|
||||
base[i].idx = fd + i - 1; /* stack index of capture value */
|
||||
base[i].s = s;
|
||||
}
|
||||
base[i].kind = Cclose; /* close group */
|
||||
base[i].siz = 1;
|
||||
base[i].s = s;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Remove dynamic captures from the Lua stack (called in case of failure)
|
||||
*/
|
||||
static int removedyncap (lua_State *L, Capture *capture,
|
||||
int level, int last) {
|
||||
int id = finddyncap(capture + level, capture + last); /* index of 1st cap. */
|
||||
int top = lua_gettop(L);
|
||||
if (id == 0) return 0; /* no dynamic captures? */
|
||||
lua_settop(L, id - 1); /* remove captures */
|
||||
return top - id + 1; /* number of values removed */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Opcode interpreter
|
||||
*/
|
||||
const char *match (lua_State *L, const char *o, const char *s, const char *e,
|
||||
Instruction *op, Capture *capture, int ptop) {
|
||||
Stack stackbase[INITBACK];
|
||||
Stack *stacklimit = stackbase + INITBACK;
|
||||
Stack *stack = stackbase; /* point to first empty slot in stack */
|
||||
int capsize = INITCAPSIZE;
|
||||
int captop = 0; /* point to first empty slot in captures */
|
||||
int ndyncap = 0; /* number of dynamic captures (in Lua stack) */
|
||||
const Instruction *p = op; /* current instruction */
|
||||
stack->p = &giveup; stack->s = s; stack->caplevel = 0; stack++;
|
||||
lua_pushlightuserdata(L, stackbase);
|
||||
for (;;) {
|
||||
#if defined(DEBUG)
|
||||
printf("s: |%s| stck:%d, dyncaps:%d, caps:%d ",
|
||||
s, stack - getstackbase(L, ptop), ndyncap, captop);
|
||||
printinst(op, p);
|
||||
printcaplist(capture, capture + captop);
|
||||
#endif
|
||||
assert(stackidx(ptop) + ndyncap == lua_gettop(L) && ndyncap <= captop);
|
||||
switch ((Opcode)p->i.code) {
|
||||
case IEnd: {
|
||||
assert(stack == getstackbase(L, ptop) + 1);
|
||||
capture[captop].kind = Cclose;
|
||||
capture[captop].s = NULL;
|
||||
return s;
|
||||
}
|
||||
case IGiveup: {
|
||||
assert(stack == getstackbase(L, ptop));
|
||||
return NULL;
|
||||
}
|
||||
case IRet: {
|
||||
assert(stack > getstackbase(L, ptop) && (stack - 1)->s == NULL);
|
||||
p = (--stack)->p;
|
||||
continue;
|
||||
}
|
||||
case IAny: {
|
||||
if (s < e) { p++; s++; }
|
||||
else goto fail;
|
||||
continue;
|
||||
}
|
||||
case ITestAny: {
|
||||
if (s < e) p += 2;
|
||||
else p += getoffset(p);
|
||||
continue;
|
||||
}
|
||||
case IChar: {
|
||||
if ((byte)*s == p->i.aux && s < e) { p++; s++; }
|
||||
else goto fail;
|
||||
continue;
|
||||
}
|
||||
case ITestChar: {
|
||||
if ((byte)*s == p->i.aux && s < e) p += 2;
|
||||
else p += getoffset(p);
|
||||
continue;
|
||||
}
|
||||
case ISet: {
|
||||
int c = (byte)*s;
|
||||
if (testchar((p+1)->buff, c) && s < e)
|
||||
{ p += CHARSETINSTSIZE; s++; }
|
||||
else goto fail;
|
||||
continue;
|
||||
}
|
||||
case ITestSet: {
|
||||
int c = (byte)*s;
|
||||
if (testchar((p + 2)->buff, c) && s < e)
|
||||
p += 1 + CHARSETINSTSIZE;
|
||||
else p += getoffset(p);
|
||||
continue;
|
||||
}
|
||||
case IBehind: {
|
||||
int n = p->i.aux;
|
||||
if (n > s - o) goto fail;
|
||||
s -= n; p++;
|
||||
continue;
|
||||
}
|
||||
case ISpan: {
|
||||
for (; s < e; s++) {
|
||||
int c = (byte)*s;
|
||||
if (!testchar((p+1)->buff, c)) break;
|
||||
}
|
||||
p += CHARSETINSTSIZE;
|
||||
continue;
|
||||
}
|
||||
case IJmp: {
|
||||
p += getoffset(p);
|
||||
continue;
|
||||
}
|
||||
case IChoice: {
|
||||
if (stack == stacklimit)
|
||||
stack = doublestack(L, &stacklimit, ptop);
|
||||
stack->p = p + getoffset(p);
|
||||
stack->s = s;
|
||||
stack->caplevel = captop;
|
||||
stack++;
|
||||
p += 2;
|
||||
continue;
|
||||
}
|
||||
case ICall: {
|
||||
if (stack == stacklimit)
|
||||
stack = doublestack(L, &stacklimit, ptop);
|
||||
stack->s = NULL;
|
||||
stack->p = p + 2; /* save return address */
|
||||
stack++;
|
||||
p += getoffset(p);
|
||||
continue;
|
||||
}
|
||||
case ICommit: {
|
||||
assert(stack > getstackbase(L, ptop) && (stack - 1)->s != NULL);
|
||||
stack--;
|
||||
p += getoffset(p);
|
||||
continue;
|
||||
}
|
||||
case IPartialCommit: {
|
||||
assert(stack > getstackbase(L, ptop) && (stack - 1)->s != NULL);
|
||||
(stack - 1)->s = s;
|
||||
(stack - 1)->caplevel = captop;
|
||||
p += getoffset(p);
|
||||
continue;
|
||||
}
|
||||
case IBackCommit: {
|
||||
assert(stack > getstackbase(L, ptop) && (stack - 1)->s != NULL);
|
||||
s = (--stack)->s;
|
||||
captop = stack->caplevel;
|
||||
p += getoffset(p);
|
||||
continue;
|
||||
}
|
||||
case IFailTwice:
|
||||
assert(stack > getstackbase(L, ptop));
|
||||
stack--;
|
||||
/* go through */
|
||||
case IFail:
|
||||
fail: { /* pattern failed: try to backtrack */
|
||||
do { /* remove pending calls */
|
||||
assert(stack > getstackbase(L, ptop));
|
||||
s = (--stack)->s;
|
||||
} while (s == NULL);
|
||||
if (ndyncap > 0) /* is there matchtime captures? */
|
||||
ndyncap -= removedyncap(L, capture, stack->caplevel, captop);
|
||||
captop = stack->caplevel;
|
||||
p = stack->p;
|
||||
continue;
|
||||
}
|
||||
case ICloseRunTime: {
|
||||
CapState cs;
|
||||
int rem, res, n;
|
||||
int fr = lua_gettop(L) + 1; /* stack index of first result */
|
||||
cs.s = o; cs.L = L; cs.ocap = capture; cs.ptop = ptop;
|
||||
n = runtimecap(&cs, capture + captop, s, &rem); /* call function */
|
||||
captop -= n; /* remove nested captures */
|
||||
fr -= rem; /* 'rem' items were popped from Lua stack */
|
||||
res = resdyncaptures(L, fr, s - o, e - o); /* get result */
|
||||
if (res == -1) /* fail? */
|
||||
goto fail;
|
||||
s = o + res; /* else update current position */
|
||||
n = lua_gettop(L) - fr + 1; /* number of new captures */
|
||||
ndyncap += n - rem; /* update number of dynamic captures */
|
||||
if (n > 0) { /* any new capture? */
|
||||
if ((captop += n + 2) >= capsize) {
|
||||
capture = doublecap(L, capture, captop, ptop);
|
||||
capsize = 2 * captop;
|
||||
}
|
||||
/* add new captures to 'capture' list */
|
||||
adddyncaptures(s, capture + captop - n - 2, n, fr);
|
||||
}
|
||||
p++;
|
||||
continue;
|
||||
}
|
||||
case ICloseCapture: {
|
||||
const char *s1 = s;
|
||||
assert(captop > 0);
|
||||
/* if possible, turn capture into a full capture */
|
||||
if (capture[captop - 1].siz == 0 &&
|
||||
s1 - capture[captop - 1].s < UCHAR_MAX) {
|
||||
capture[captop - 1].siz = s1 - capture[captop - 1].s + 1;
|
||||
p++;
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
capture[captop].siz = 1; /* mark entry as closed */
|
||||
capture[captop].s = s;
|
||||
goto pushcapture;
|
||||
}
|
||||
}
|
||||
case IOpenCapture:
|
||||
capture[captop].siz = 0; /* mark entry as open */
|
||||
capture[captop].s = s;
|
||||
goto pushcapture;
|
||||
case IFullCapture:
|
||||
capture[captop].siz = getoff(p) + 1; /* save capture size */
|
||||
capture[captop].s = s - getoff(p);
|
||||
/* goto pushcapture; */
|
||||
pushcapture: {
|
||||
capture[captop].idx = p->i.key;
|
||||
capture[captop].kind = getkind(p);
|
||||
if (++captop >= capsize) {
|
||||
capture = doublecap(L, capture, captop, ptop);
|
||||
capsize = 2 * captop;
|
||||
}
|
||||
p++;
|
||||
continue;
|
||||
}
|
||||
default: assert(0); return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* }====================================================== */
|
||||
|
||||
|
||||
63
3rd/lpeg/lpvm.h
Normal file
63
3rd/lpeg/lpvm.h
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
** $Id: lpvm.h,v 1.2 2013/04/03 20:37:18 roberto Exp $
|
||||
*/
|
||||
|
||||
#if !defined(lpvm_h)
|
||||
#define lpvm_h
|
||||
|
||||
#include "lpcap.h"
|
||||
|
||||
|
||||
/* Virtual Machine's instructions */
|
||||
typedef enum Opcode {
|
||||
IAny, /* if no char, fail */
|
||||
IChar, /* if char != aux, fail */
|
||||
ISet, /* if char not in buff, fail */
|
||||
ITestAny, /* in no char, jump to 'offset' */
|
||||
ITestChar, /* if char != aux, jump to 'offset' */
|
||||
ITestSet, /* if char not in buff, jump to 'offset' */
|
||||
ISpan, /* read a span of chars in buff */
|
||||
IBehind, /* walk back 'aux' characters (fail if not possible) */
|
||||
IRet, /* return from a rule */
|
||||
IEnd, /* end of pattern */
|
||||
IChoice, /* stack a choice; next fail will jump to 'offset' */
|
||||
IJmp, /* jump to 'offset' */
|
||||
ICall, /* call rule at 'offset' */
|
||||
IOpenCall, /* call rule number 'key' (must be closed to a ICall) */
|
||||
ICommit, /* pop choice and jump to 'offset' */
|
||||
IPartialCommit, /* update top choice to current position and jump */
|
||||
IBackCommit, /* "fails" but jump to its own 'offset' */
|
||||
IFailTwice, /* pop one choice and then fail */
|
||||
IFail, /* go back to saved state on choice and jump to saved offset */
|
||||
IGiveup, /* internal use */
|
||||
IFullCapture, /* complete capture of last 'off' chars */
|
||||
IOpenCapture, /* start a capture */
|
||||
ICloseCapture,
|
||||
ICloseRunTime
|
||||
} Opcode;
|
||||
|
||||
|
||||
|
||||
typedef union Instruction {
|
||||
struct Inst {
|
||||
byte code;
|
||||
byte aux;
|
||||
short key;
|
||||
} i;
|
||||
int offset;
|
||||
byte buff[1];
|
||||
} Instruction;
|
||||
|
||||
|
||||
int getposition (lua_State *L, int t, int i);
|
||||
void printpatt (Instruction *p, int n);
|
||||
const char *match (lua_State *L, const char *o, const char *s, const char *e,
|
||||
Instruction *op, Capture *capture, int ptop);
|
||||
int verify (lua_State *L, Instruction *op, const Instruction *p,
|
||||
Instruction *e, int postable, int rule);
|
||||
void checkrule (lua_State *L, Instruction *op, int from, int to,
|
||||
int postable, int rule);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
55
3rd/lpeg/makefile
Normal file
55
3rd/lpeg/makefile
Normal file
@@ -0,0 +1,55 @@
|
||||
LIBNAME = lpeg
|
||||
LUADIR = /usr/include/lua5.1/
|
||||
|
||||
COPT = -O2
|
||||
# COPT = -DLPEG_DEBUG -g
|
||||
|
||||
CWARNS = -Wall -Wextra -pedantic \
|
||||
-Waggregate-return \
|
||||
-Wcast-align \
|
||||
-Wcast-qual \
|
||||
-Wdisabled-optimization \
|
||||
-Wpointer-arith \
|
||||
-Wshadow \
|
||||
-Wsign-compare \
|
||||
-Wundef \
|
||||
-Wwrite-strings \
|
||||
-Wbad-function-cast \
|
||||
-Wdeclaration-after-statement \
|
||||
-Wmissing-prototypes \
|
||||
-Wnested-externs \
|
||||
-Wstrict-prototypes \
|
||||
# -Wunreachable-code \
|
||||
|
||||
|
||||
CFLAGS = $(CWARNS) $(COPT) -ansi -I$(LUADIR) -fPIC
|
||||
CC = gcc
|
||||
|
||||
FILES = lpvm.o lpcap.o lptree.o lpcode.o lpprint.o
|
||||
|
||||
# For Linux
|
||||
linux:
|
||||
make lpeg.so "DLLFLAGS = -shared -fPIC"
|
||||
|
||||
# For Mac OS
|
||||
macosx:
|
||||
make lpeg.so "DLLFLAGS = -bundle -undefined dynamic_lookup"
|
||||
|
||||
lpeg.so: $(FILES)
|
||||
env $(CC) $(DLLFLAGS) $(FILES) -o lpeg.so
|
||||
|
||||
$(FILES): makefile
|
||||
|
||||
test: test.lua re.lua lpeg.so
|
||||
./test.lua
|
||||
|
||||
clean:
|
||||
rm -f $(FILES) lpeg.so
|
||||
|
||||
|
||||
lpcap.o: lpcap.c lpcap.h lptypes.h
|
||||
lpcode.o: lpcode.c lptypes.h lpcode.h lptree.h lpvm.h lpcap.h
|
||||
lpprint.o: lpprint.c lptypes.h lpprint.h lptree.h lpvm.h lpcap.h
|
||||
lptree.o: lptree.c lptypes.h lpcap.h lpcode.h lptree.h lpvm.h lpprint.h
|
||||
lpvm.o: lpvm.c lpcap.h lptypes.h lpvm.h lpprint.h lptree.h
|
||||
|
||||
498
3rd/lpeg/re.html
Normal file
498
3rd/lpeg/re.html
Normal file
@@ -0,0 +1,498 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<title>LPeg.re - Regex syntax for LPEG</title>
|
||||
<link rel="stylesheet"
|
||||
href="http://www.inf.puc-rio.br/~roberto/lpeg/doc.css"
|
||||
type="text/css"/>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!-- $Id: re.html,v 1.21 2013/03/28 20:43:30 roberto Exp $ -->
|
||||
|
||||
<div id="container">
|
||||
|
||||
<div id="product">
|
||||
<div id="product_logo">
|
||||
<a href="http://www.inf.puc-rio.br/~roberto/lpeg/">
|
||||
<img alt="LPeg logo" src="lpeg-128.gif"/>
|
||||
</a>
|
||||
</div>
|
||||
<div id="product_name"><big><strong>LPeg.re</strong></big></div>
|
||||
<div id="product_description">
|
||||
Regex syntax for LPEG
|
||||
</div>
|
||||
</div> <!-- id="product" -->
|
||||
|
||||
<div id="main">
|
||||
|
||||
<div id="navigation">
|
||||
<h1>re</h1>
|
||||
|
||||
<ul>
|
||||
<li><a href="#basic">Basic Constructions</a></li>
|
||||
<li><a href="#func">Functions</a></li>
|
||||
<li><a href="#ex">Some Examples</a></li>
|
||||
<li><a href="#license">License</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div> <!-- id="navigation" -->
|
||||
|
||||
<div id="content">
|
||||
|
||||
<h2><a name="basic"></a>The <code>re</code> Module</h2>
|
||||
|
||||
<p>
|
||||
The <code>re</code> module
|
||||
(provided by file <code>re.lua</code> in the distribution)
|
||||
supports a somewhat conventional regex syntax
|
||||
for pattern usage within <a href="lpeg.html">LPeg</a>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The next table summarizes <code>re</code>'s syntax.
|
||||
A <code>p</code> represents an arbitrary pattern;
|
||||
<code>num</code> represents a number (<code>[0-9]+</code>);
|
||||
<code>name</code> represents an identifier
|
||||
(<code>[a-zA-Z][a-zA-Z0-9_]*</code>).
|
||||
Constructions are listed in order of decreasing precedence.
|
||||
<table border="1">
|
||||
<tbody><tr><td><b>Syntax</b></td><td><b>Description</b></td></tr>
|
||||
<tr><td><code>( p )</code></td> <td>grouping</td></tr>
|
||||
<tr><td><code>'string'</code></td> <td>literal string</td></tr>
|
||||
<tr><td><code>"string"</code></td> <td>literal string</td></tr>
|
||||
<tr><td><code>[class]</code></td> <td>character class</td></tr>
|
||||
<tr><td><code>.</code></td> <td>any character</td></tr>
|
||||
<tr><td><code>%name</code></td>
|
||||
<td>pattern <code>defs[name]</code> or a pre-defined pattern</td></tr>
|
||||
<tr><td><code>name</code></td><td>non terminal</td></tr>
|
||||
<tr><td><code><name></code></td><td>non terminal</td></tr>
|
||||
<tr><td><code>{}</code></td> <td>position capture</td></tr>
|
||||
<tr><td><code>{ p }</code></td> <td>simple capture</td></tr>
|
||||
<tr><td><code>{: p :}</code></td> <td>anonymous group capture</td></tr>
|
||||
<tr><td><code>{:name: p :}</code></td> <td>named group capture</td></tr>
|
||||
<tr><td><code>{~ p ~}</code></td> <td>substitution capture</td></tr>
|
||||
<tr><td><code>{| p |}</code></td> <td>table capture</td></tr>
|
||||
<tr><td><code>=name</code></td> <td>back reference
|
||||
</td></tr>
|
||||
<tr><td><code>p ?</code></td> <td>optional match</td></tr>
|
||||
<tr><td><code>p *</code></td> <td>zero or more repetitions</td></tr>
|
||||
<tr><td><code>p +</code></td> <td>one or more repetitions</td></tr>
|
||||
<tr><td><code>p^num</code></td> <td>exactly <code>n</code> repetitions</td></tr>
|
||||
<tr><td><code>p^+num</code></td>
|
||||
<td>at least <code>n</code> repetitions</td></tr>
|
||||
<tr><td><code>p^-num</code></td>
|
||||
<td>at most <code>n</code> repetitions</td></tr>
|
||||
<tr><td><code>p -> 'string'</code></td> <td>string capture</td></tr>
|
||||
<tr><td><code>p -> "string"</code></td> <td>string capture</td></tr>
|
||||
<tr><td><code>p -> num</code></td> <td>numbered capture</td></tr>
|
||||
<tr><td><code>p -> name</code></td> <td>function/query/string capture
|
||||
equivalent to <code>p / defs[name]</code></td></tr>
|
||||
<tr><td><code>p => name</code></td> <td>match-time capture
|
||||
equivalent to <code>lpeg.Cmt(p, defs[name])</code></td></tr>
|
||||
<tr><td><code>& p</code></td> <td>and predicate</td></tr>
|
||||
<tr><td><code>! p</code></td> <td>not predicate</td></tr>
|
||||
<tr><td><code>p1 p2</code></td> <td>concatenation</td></tr>
|
||||
<tr><td><code>p1 / p2</code></td> <td>ordered choice</td></tr>
|
||||
<tr><td>(<code>name <- p</code>)<sup>+</sup></td> <td>grammar</td></tr>
|
||||
</tbody></table>
|
||||
<p>
|
||||
Any space appearing in a syntax description can be
|
||||
replaced by zero or more space characters and Lua-style comments
|
||||
(<code>--</code> until end of line).
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Character classes define sets of characters.
|
||||
An initial <code>^</code> complements the resulting set.
|
||||
A range <em>x</em><code>-</code><em>y</em> includes in the set
|
||||
all characters with codes between the codes of <em>x</em> and <em>y</em>.
|
||||
A pre-defined class <code>%</code><em>name</em> includes all
|
||||
characters of that class.
|
||||
A simple character includes itself in the set.
|
||||
The only special characters inside a class are <code>^</code>
|
||||
(special only if it is the first character);
|
||||
<code>]</code>
|
||||
(can be included in the set as the first character,
|
||||
after the optional <code>^</code>);
|
||||
<code>%</code> (special only if followed by a letter);
|
||||
and <code>-</code>
|
||||
(can be included in the set as the first or the last character).
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Currently the pre-defined classes are similar to those from the
|
||||
Lua's string library
|
||||
(<code>%a</code> for letters,
|
||||
<code>%A</code> for non letters, etc.).
|
||||
There is also a class <code>%nl</code>
|
||||
containing only the newline character,
|
||||
which is particularly handy for grammars written inside long strings,
|
||||
as long strings do not interpret escape sequences like <code>\n</code>.
|
||||
</p>
|
||||
|
||||
|
||||
<h2><a name="func">Functions</a></h2>
|
||||
|
||||
<h3><code>re.compile (string, [, defs])</code></h3>
|
||||
<p>
|
||||
Compiles the given string and
|
||||
returns an equivalent LPeg pattern.
|
||||
The given string may define either an expression or a grammar.
|
||||
The optional <code>defs</code> table provides extra Lua values
|
||||
to be used by the pattern.
|
||||
</p>
|
||||
|
||||
<h3><code>re.find (subject, pattern [, init])</code></h3>
|
||||
<p>
|
||||
Searches the given pattern in the given subject.
|
||||
If it finds a match,
|
||||
returns the index where this occurrence starts and
|
||||
the index where it ends.
|
||||
Otherwise, returns nil.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
An optional numeric argument <code>init</code> makes the search
|
||||
starts at that position in the subject string.
|
||||
As usual in Lua libraries,
|
||||
a negative value counts from the end.
|
||||
</p>
|
||||
|
||||
<h3><code>re.gsub (subject, pattern, replacement)</code></h3>
|
||||
<p>
|
||||
Does a <em>global substitution</em>,
|
||||
replacing all occurrences of <code>pattern</code>
|
||||
in the given <code>subject</code> by <code>replacement</code>.
|
||||
|
||||
<h3><code>re.match (subject, pattern)</code></h3>
|
||||
<p>
|
||||
Matches the given pattern against the given subject,
|
||||
returning all captures.
|
||||
</p>
|
||||
|
||||
<h3><code>re.updatelocale ()</code></h3>
|
||||
<p>
|
||||
Updates the pre-defined character classes to the current locale.
|
||||
</p>
|
||||
|
||||
|
||||
<h2><a name="ex">Some Examples</a></h2>
|
||||
|
||||
<h3>A complete simple program</h3>
|
||||
<p>
|
||||
The next code shows a simple complete Lua program using
|
||||
the <code>re</code> module:
|
||||
</p>
|
||||
<pre class="example">
|
||||
local re = require"re"
|
||||
|
||||
-- find the position of the first numeral in a string
|
||||
print(re.find("the number 423 is odd", "[0-9]+")) --> 12 14
|
||||
|
||||
-- returns all words in a string
|
||||
print(re.match("the number 423 is odd", "({%a+} / .)*"))
|
||||
--> the number is odd
|
||||
|
||||
-- returns the first numeral in a string
|
||||
print(re.match("the number 423 is odd", "s <- {%d+} / . s"))
|
||||
--> 423
|
||||
|
||||
print(re.gsub("hello World", "[aeiou]", "."))
|
||||
--> h.ll. W.rld
|
||||
</pre>
|
||||
|
||||
|
||||
<h3>Balanced parentheses</h3>
|
||||
<p>
|
||||
The following call will produce the same pattern produced by the
|
||||
Lua expression in the
|
||||
<a href="lpeg.html#balanced">balanced parentheses</a> example:
|
||||
</p>
|
||||
<pre class="example">
|
||||
b = re.compile[[ balanced <- "(" ([^()] / balanced)* ")" ]]
|
||||
</pre>
|
||||
|
||||
<h3>String reversal</h3>
|
||||
<p>
|
||||
The next example reverses a string:
|
||||
</p>
|
||||
<pre class="example">
|
||||
rev = re.compile[[ R <- (!.) -> '' / ({.} R) -> '%2%1']]
|
||||
print(rev:match"0123456789") --> 9876543210
|
||||
</pre>
|
||||
|
||||
<h3>CSV decoder</h3>
|
||||
<p>
|
||||
The next example replicates the <a href="lpeg.html#CSV">CSV decoder</a>:
|
||||
</p>
|
||||
<pre class="example">
|
||||
record = re.compile[[
|
||||
record <- {| field (',' field)* |} (%nl / !.)
|
||||
field <- escaped / nonescaped
|
||||
nonescaped <- { [^,"%nl]* }
|
||||
escaped <- '"' {~ ([^"] / '""' -> '"')* ~} '"'
|
||||
]]
|
||||
</pre>
|
||||
|
||||
<h3>Lua's long strings</h3>
|
||||
<p>
|
||||
The next example matches Lua long strings:
|
||||
</p>
|
||||
<pre class="example">
|
||||
c = re.compile([[
|
||||
longstring <- ('[' {:eq: '='* :} '[' close)
|
||||
close <- ']' =eq ']' / . close
|
||||
]])
|
||||
|
||||
print(c:match'[==[]]===]]]]==]===[]') --> 17
|
||||
</pre>
|
||||
|
||||
<h3>Abstract Syntax Trees</h3>
|
||||
<p>
|
||||
This example shows a simple way to build an
|
||||
abstract syntax tree (AST) for a given grammar.
|
||||
To keep our example simple,
|
||||
let us consider the following grammar
|
||||
for lists of names:
|
||||
</p>
|
||||
<pre class="example">
|
||||
p = re.compile[[
|
||||
listname <- (name s)*
|
||||
name <- [a-z][a-z]*
|
||||
s <- %s*
|
||||
]]
|
||||
</pre>
|
||||
<p>
|
||||
Now, we will add captures to build a corresponding AST.
|
||||
As a first step, the pattern will build a table to
|
||||
represent each non terminal;
|
||||
terminals will be represented by their corresponding strings:
|
||||
</p>
|
||||
<pre class="example">
|
||||
c = re.compile[[
|
||||
listname <- {| (name s)* |}
|
||||
name <- {| {[a-z][a-z]*} |}
|
||||
s <- %s*
|
||||
]]
|
||||
</pre>
|
||||
<p>
|
||||
Now, a match against <code>"hi hello bye"</code>
|
||||
results in the table
|
||||
<code>{{"hi"}, {"hello"}, {"bye"}}</code>.
|
||||
</p>
|
||||
<p>
|
||||
For such a simple grammar,
|
||||
this AST is more than enough;
|
||||
actually, the tables around each single name
|
||||
are already overkilling.
|
||||
More complex grammars,
|
||||
however, may need some more structure.
|
||||
Specifically,
|
||||
it would be useful if each table had
|
||||
a <code>tag</code> field telling what non terminal
|
||||
that table represents.
|
||||
We can add such a tag using
|
||||
<a href="lpeg.html/#cap-g">named group captures</a>:
|
||||
</p>
|
||||
<pre class="example">
|
||||
x = re.compile[[
|
||||
listname <- {| {:tag: '' -> 'list':} (name s)* |}
|
||||
name <- {| {:tag: '' -> 'id':} {[a-z][a-z]*} |}
|
||||
s <- ' '*
|
||||
]]
|
||||
</pre>
|
||||
<p>
|
||||
With these group captures,
|
||||
a match against <code>"hi hello bye"</code>
|
||||
results in the following table:
|
||||
</p>
|
||||
<pre class="example">
|
||||
{tag="list",
|
||||
{tag="id", "hi"},
|
||||
{tag="id", "hello"},
|
||||
{tag="id", "bye"}
|
||||
}
|
||||
</pre>
|
||||
|
||||
|
||||
<h3>Indented blocks</h3>
|
||||
<p>
|
||||
This example breaks indented blocks into tables,
|
||||
respecting the indentation:
|
||||
</p>
|
||||
<pre class="example">
|
||||
p = re.compile[[
|
||||
block <- {| {:ident:' '*:} line
|
||||
((=ident !' ' line) / &(=ident ' ') block)* |}
|
||||
line <- {[^%nl]*} %nl
|
||||
]]
|
||||
</pre>
|
||||
<p>
|
||||
As an example,
|
||||
consider the following text:
|
||||
</p>
|
||||
<pre class="example">
|
||||
t = p:match[[
|
||||
first line
|
||||
subline 1
|
||||
subline 2
|
||||
second line
|
||||
third line
|
||||
subline 3.1
|
||||
subline 3.1.1
|
||||
subline 3.2
|
||||
]]
|
||||
</pre>
|
||||
<p>
|
||||
The resulting table <code>t</code> will be like this:
|
||||
</p>
|
||||
<pre class="example">
|
||||
{'first line'; {'subline 1'; 'subline 2'; ident = ' '};
|
||||
'second line';
|
||||
'third line'; { 'subline 3.1'; {'subline 3.1.1'; ident = ' '};
|
||||
'subline 3.2'; ident = ' '};
|
||||
ident = ''}
|
||||
</pre>
|
||||
|
||||
<h3>Macro expander</h3>
|
||||
<p>
|
||||
This example implements a simple macro expander.
|
||||
Macros must be defined as part of the pattern,
|
||||
following some simple rules:
|
||||
</p>
|
||||
<pre class="example">
|
||||
p = re.compile[[
|
||||
text <- {~ item* ~}
|
||||
item <- macro / [^()] / '(' item* ')'
|
||||
arg <- ' '* {~ (!',' item)* ~}
|
||||
args <- '(' arg (',' arg)* ')'
|
||||
-- now we define some macros
|
||||
macro <- ('apply' args) -> '%1(%2)'
|
||||
/ ('add' args) -> '%1 + %2'
|
||||
/ ('mul' args) -> '%1 * %2'
|
||||
]]
|
||||
|
||||
print(p:match"add(mul(a,b), apply(f,x))") --> a * b + f(x)
|
||||
</pre>
|
||||
<p>
|
||||
A <code>text</code> is a sequence of items,
|
||||
wherein we apply a substitution capture to expand any macros.
|
||||
An <code>item</code> is either a macro,
|
||||
any character different from parentheses,
|
||||
or a parenthesized expression.
|
||||
A macro argument (<code>arg</code>) is a sequence
|
||||
of items different from a comma.
|
||||
(Note that a comma may appear inside an item,
|
||||
e.g., inside a parenthesized expression.)
|
||||
Again we do a substitution capture to expand any macro
|
||||
in the argument before expanding the outer macro.
|
||||
<code>args</code> is a list of arguments separated by commas.
|
||||
Finally we define the macros.
|
||||
Each macro is a string substitution;
|
||||
it replaces the macro name and its arguments by its corresponding string,
|
||||
with each <code>%</code><em>n</em> replaced by the <em>n</em>-th argument.
|
||||
</p>
|
||||
|
||||
<h3>Patterns</h3>
|
||||
<p>
|
||||
This example shows the complete syntax
|
||||
of patterns accepted by <code>re</code>.
|
||||
</p>
|
||||
<pre class="example">
|
||||
p = [=[
|
||||
|
||||
pattern <- exp !.
|
||||
exp <- S (alternative / grammar)
|
||||
|
||||
alternative <- seq ('/' S seq)*
|
||||
seq <- prefix*
|
||||
prefix <- '&' S prefix / '!' S prefix / suffix
|
||||
suffix <- primary S (([+*?]
|
||||
/ '^' [+-]? num
|
||||
/ '->' S (string / '{}' / name)
|
||||
/ '=>' S name) S)*
|
||||
|
||||
primary <- '(' exp ')' / string / class / defined
|
||||
/ '{:' (name ':')? exp ':}'
|
||||
/ '=' name
|
||||
/ '{}'
|
||||
/ '{~' exp '~}'
|
||||
/ '{' exp '}'
|
||||
/ '.'
|
||||
/ name S !arrow
|
||||
/ '<' name '>' -- old-style non terminals
|
||||
|
||||
grammar <- definition+
|
||||
definition <- name S arrow exp
|
||||
|
||||
class <- '[' '^'? item (!']' item)* ']'
|
||||
item <- defined / range / .
|
||||
range <- . '-' [^]]
|
||||
|
||||
S <- (%s / '--' [^%nl]*)* -- spaces and comments
|
||||
name <- [A-Za-z][A-Za-z0-9_]*
|
||||
arrow <- '<-'
|
||||
num <- [0-9]+
|
||||
string <- '"' [^"]* '"' / "'" [^']* "'"
|
||||
defined <- '%' name
|
||||
|
||||
]=]
|
||||
|
||||
print(re.match(p, p)) -- a self description must match itself
|
||||
</pre>
|
||||
|
||||
|
||||
|
||||
<h2><a name="license">License</a></h2>
|
||||
|
||||
<p>
|
||||
Copyright © 2008-2010 Lua.org, PUC-Rio.
|
||||
</p>
|
||||
<p>
|
||||
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:
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The above copyright notice and this permission notice
|
||||
shall be included in all copies or substantial portions of the Software.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
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.
|
||||
</p>
|
||||
|
||||
</div> <!-- id="content" -->
|
||||
|
||||
</div> <!-- id="main" -->
|
||||
|
||||
<div id="about">
|
||||
<p><small>
|
||||
$Id: re.html,v 1.21 2013/03/28 20:43:30 roberto Exp $
|
||||
</small></p>
|
||||
</div> <!-- id="about" -->
|
||||
|
||||
</div> <!-- id="container" -->
|
||||
|
||||
</body>
|
||||
</html>
|
||||
259
3rd/lpeg/re.lua
Normal file
259
3rd/lpeg/re.lua
Normal file
@@ -0,0 +1,259 @@
|
||||
-- $Id: re.lua,v 1.44 2013/03/26 20:11:40 roberto Exp $
|
||||
|
||||
-- imported functions and modules
|
||||
local tonumber, type, print, error = tonumber, type, print, error
|
||||
local setmetatable = setmetatable
|
||||
local m = require"lpeg"
|
||||
|
||||
-- 'm' will be used to parse expressions, and 'mm' will be used to
|
||||
-- create expressions; that is, 're' runs on 'm', creating patterns
|
||||
-- on 'mm'
|
||||
local mm = m
|
||||
|
||||
-- pattern's metatable
|
||||
local mt = getmetatable(mm.P(0))
|
||||
|
||||
|
||||
|
||||
-- No more global accesses after this point
|
||||
local version = _VERSION
|
||||
if version == "Lua 5.2" then _ENV = nil end
|
||||
|
||||
|
||||
local any = m.P(1)
|
||||
|
||||
|
||||
-- Pre-defined names
|
||||
local Predef = { nl = m.P"\n" }
|
||||
|
||||
|
||||
local mem
|
||||
local fmem
|
||||
local gmem
|
||||
|
||||
|
||||
local function updatelocale ()
|
||||
mm.locale(Predef)
|
||||
Predef.a = Predef.alpha
|
||||
Predef.c = Predef.cntrl
|
||||
Predef.d = Predef.digit
|
||||
Predef.g = Predef.graph
|
||||
Predef.l = Predef.lower
|
||||
Predef.p = Predef.punct
|
||||
Predef.s = Predef.space
|
||||
Predef.u = Predef.upper
|
||||
Predef.w = Predef.alnum
|
||||
Predef.x = Predef.xdigit
|
||||
Predef.A = any - Predef.a
|
||||
Predef.C = any - Predef.c
|
||||
Predef.D = any - Predef.d
|
||||
Predef.G = any - Predef.g
|
||||
Predef.L = any - Predef.l
|
||||
Predef.P = any - Predef.p
|
||||
Predef.S = any - Predef.s
|
||||
Predef.U = any - Predef.u
|
||||
Predef.W = any - Predef.w
|
||||
Predef.X = any - Predef.x
|
||||
mem = {} -- restart memoization
|
||||
fmem = {}
|
||||
gmem = {}
|
||||
local mt = {__mode = "v"}
|
||||
setmetatable(mem, mt)
|
||||
setmetatable(fmem, mt)
|
||||
setmetatable(gmem, mt)
|
||||
end
|
||||
|
||||
|
||||
updatelocale()
|
||||
|
||||
|
||||
|
||||
local I = m.P(function (s,i) print(i, s:sub(1, i-1)); return i end)
|
||||
|
||||
|
||||
local function getdef (id, defs)
|
||||
local c = defs and defs[id]
|
||||
if not c then error("undefined name: " .. id) end
|
||||
return c
|
||||
end
|
||||
|
||||
|
||||
local function patt_error (s, i)
|
||||
local msg = (#s < i + 20) and s:sub(i)
|
||||
or s:sub(i,i+20) .. "..."
|
||||
msg = ("pattern error near '%s'"):format(msg)
|
||||
error(msg, 2)
|
||||
end
|
||||
|
||||
local function mult (p, n)
|
||||
local np = mm.P(true)
|
||||
while n >= 1 do
|
||||
if n%2 >= 1 then np = np * p end
|
||||
p = p * p
|
||||
n = n/2
|
||||
end
|
||||
return np
|
||||
end
|
||||
|
||||
local function equalcap (s, i, c)
|
||||
if type(c) ~= "string" then return nil end
|
||||
local e = #c + i
|
||||
if s:sub(i, e - 1) == c then return e else return nil end
|
||||
end
|
||||
|
||||
|
||||
local S = (Predef.space + "--" * (any - Predef.nl)^0)^0
|
||||
|
||||
local name = m.R("AZ", "az", "__") * m.R("AZ", "az", "__", "09")^0
|
||||
|
||||
local arrow = S * "<-"
|
||||
|
||||
local seq_follow = m.P"/" + ")" + "}" + ":}" + "~}" + "|}" + (name * arrow) + -1
|
||||
|
||||
name = m.C(name)
|
||||
|
||||
|
||||
-- a defined name only have meaning in a given environment
|
||||
local Def = name * m.Carg(1)
|
||||
|
||||
local num = m.C(m.R"09"^1) * S / tonumber
|
||||
|
||||
local String = "'" * m.C((any - "'")^0) * "'" +
|
||||
'"' * m.C((any - '"')^0) * '"'
|
||||
|
||||
|
||||
local defined = "%" * Def / function (c,Defs)
|
||||
local cat = Defs and Defs[c] or Predef[c]
|
||||
if not cat then error ("name '" .. c .. "' undefined") end
|
||||
return cat
|
||||
end
|
||||
|
||||
local Range = m.Cs(any * (m.P"-"/"") * (any - "]")) / mm.R
|
||||
|
||||
local item = defined + Range + m.C(any)
|
||||
|
||||
local Class =
|
||||
"["
|
||||
* (m.C(m.P"^"^-1)) -- optional complement symbol
|
||||
* m.Cf(item * (item - "]")^0, mt.__add) /
|
||||
function (c, p) return c == "^" and any - p or p end
|
||||
* "]"
|
||||
|
||||
local function adddef (t, k, exp)
|
||||
if t[k] then
|
||||
error("'"..k.."' already defined as a rule")
|
||||
else
|
||||
t[k] = exp
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
local function firstdef (n, r) return adddef({n}, n, r) end
|
||||
|
||||
|
||||
local function NT (n, b)
|
||||
if not b then
|
||||
error("rule '"..n.."' used outside a grammar")
|
||||
else return mm.V(n)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local exp = m.P{ "Exp",
|
||||
Exp = S * ( m.V"Grammar"
|
||||
+ m.Cf(m.V"Seq" * ("/" * S * m.V"Seq")^0, mt.__add) );
|
||||
Seq = m.Cf(m.Cc(m.P"") * m.V"Prefix"^0 , mt.__mul)
|
||||
* (#seq_follow + patt_error);
|
||||
Prefix = "&" * S * m.V"Prefix" / mt.__len
|
||||
+ "!" * S * m.V"Prefix" / mt.__unm
|
||||
+ m.V"Suffix";
|
||||
Suffix = m.Cf(m.V"Primary" * S *
|
||||
( ( m.P"+" * m.Cc(1, mt.__pow)
|
||||
+ m.P"*" * m.Cc(0, mt.__pow)
|
||||
+ m.P"?" * m.Cc(-1, mt.__pow)
|
||||
+ "^" * ( m.Cg(num * m.Cc(mult))
|
||||
+ m.Cg(m.C(m.S"+-" * m.R"09"^1) * m.Cc(mt.__pow))
|
||||
)
|
||||
+ "->" * S * ( m.Cg((String + num) * m.Cc(mt.__div))
|
||||
+ m.P"{}" * m.Cc(nil, m.Ct)
|
||||
+ m.Cg(Def / getdef * m.Cc(mt.__div))
|
||||
)
|
||||
+ "=>" * S * m.Cg(Def / getdef * m.Cc(m.Cmt))
|
||||
) * S
|
||||
)^0, function (a,b,f) return f(a,b) end );
|
||||
Primary = "(" * m.V"Exp" * ")"
|
||||
+ String / mm.P
|
||||
+ Class
|
||||
+ defined
|
||||
+ "{:" * (name * ":" + m.Cc(nil)) * m.V"Exp" * ":}" /
|
||||
function (n, p) return mm.Cg(p, n) end
|
||||
+ "=" * name / function (n) return mm.Cmt(mm.Cb(n), equalcap) end
|
||||
+ m.P"{}" / mm.Cp
|
||||
+ "{~" * m.V"Exp" * "~}" / mm.Cs
|
||||
+ "{|" * m.V"Exp" * "|}" / mm.Ct
|
||||
+ "{" * m.V"Exp" * "}" / mm.C
|
||||
+ m.P"." * m.Cc(any)
|
||||
+ (name * -arrow + "<" * name * ">") * m.Cb("G") / NT;
|
||||
Definition = name * arrow * m.V"Exp";
|
||||
Grammar = m.Cg(m.Cc(true), "G") *
|
||||
m.Cf(m.V"Definition" / firstdef * m.Cg(m.V"Definition")^0,
|
||||
adddef) / mm.P
|
||||
}
|
||||
|
||||
local pattern = S * m.Cg(m.Cc(false), "G") * exp / mm.P * (-any + patt_error)
|
||||
|
||||
|
||||
local function compile (p, defs)
|
||||
if mm.type(p) == "pattern" then return p end -- already compiled
|
||||
local cp = pattern:match(p, 1, defs)
|
||||
if not cp then error("incorrect pattern", 3) end
|
||||
return cp
|
||||
end
|
||||
|
||||
local function match (s, p, i)
|
||||
local cp = mem[p]
|
||||
if not cp then
|
||||
cp = compile(p)
|
||||
mem[p] = cp
|
||||
end
|
||||
return cp:match(s, i or 1)
|
||||
end
|
||||
|
||||
local function find (s, p, i)
|
||||
local cp = fmem[p]
|
||||
if not cp then
|
||||
cp = compile(p) / 0
|
||||
cp = mm.P{ mm.Cp() * cp * mm.Cp() + 1 * mm.V(1) }
|
||||
fmem[p] = cp
|
||||
end
|
||||
local i, e = cp:match(s, i or 1)
|
||||
if i then return i, e - 1
|
||||
else return i
|
||||
end
|
||||
end
|
||||
|
||||
local function gsub (s, p, rep)
|
||||
local g = gmem[p] or {} -- ensure gmem[p] is not collected while here
|
||||
gmem[p] = g
|
||||
local cp = g[rep]
|
||||
if not cp then
|
||||
cp = compile(p)
|
||||
cp = mm.Cs((cp / rep + 1)^0)
|
||||
g[rep] = cp
|
||||
end
|
||||
return cp:match(s)
|
||||
end
|
||||
|
||||
|
||||
-- exported names
|
||||
local re = {
|
||||
compile = compile,
|
||||
match = match,
|
||||
find = find,
|
||||
gsub = gsub,
|
||||
updatelocale = updatelocale,
|
||||
}
|
||||
|
||||
if version == "Lua 5.1" then _G.re = re end
|
||||
|
||||
return re
|
||||
1386
3rd/lpeg/test.lua
Executable file
1386
3rd/lpeg/test.lua
Executable file
File diff suppressed because it is too large
Load Diff
@@ -1,76 +0,0 @@
|
||||
# If Lua is installed in a non-standard location, please set the LUA_DIR
|
||||
# environment variable to point to prefix for the install. Eg:
|
||||
# Unix: export LUA_DIR=/home/user/pkg
|
||||
# Windows: set LUA_DIR=c:\lua51
|
||||
|
||||
project(lua-cjson C)
|
||||
cmake_minimum_required(VERSION 2.6)
|
||||
|
||||
option(USE_INTERNAL_FPCONV "Use internal strtod() / g_fmt() code for performance")
|
||||
option(MULTIPLE_THREADS "Support multi-threaded apps with internal fpconv - recommended" ON)
|
||||
|
||||
if(NOT CMAKE_BUILD_TYPE)
|
||||
set(CMAKE_BUILD_TYPE Release CACHE STRING
|
||||
"Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel."
|
||||
FORCE)
|
||||
endif()
|
||||
|
||||
find_package(Lua51 REQUIRED)
|
||||
include_directories(${LUA_INCLUDE_DIR})
|
||||
|
||||
if(NOT USE_INTERNAL_FPCONV)
|
||||
# Use libc number conversion routines (strtod(), sprintf())
|
||||
set(FPCONV_SOURCES fpconv.c)
|
||||
else()
|
||||
# Use internal number conversion routines
|
||||
add_definitions(-DUSE_INTERNAL_FPCONV)
|
||||
set(FPCONV_SOURCES g_fmt.c dtoa.c)
|
||||
|
||||
include(TestBigEndian)
|
||||
TEST_BIG_ENDIAN(IEEE_BIG_ENDIAN)
|
||||
if(IEEE_BIG_ENDIAN)
|
||||
add_definitions(-DIEEE_BIG_ENDIAN)
|
||||
endif()
|
||||
|
||||
if(MULTIPLE_THREADS)
|
||||
set(CMAKE_THREAD_PREFER_PTHREAD TRUE)
|
||||
find_package(Threads REQUIRED)
|
||||
if(NOT CMAKE_USE_PTHREADS_INIT)
|
||||
message(FATAL_ERROR
|
||||
"Pthreads not found - required by MULTIPLE_THREADS option")
|
||||
endif()
|
||||
add_definitions(-DMULTIPLE_THREADS)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Handle platforms missing isinf() macro (Eg, some Solaris systems).
|
||||
include(CheckSymbolExists)
|
||||
CHECK_SYMBOL_EXISTS(isinf math.h HAVE_ISINF)
|
||||
if(NOT HAVE_ISINF)
|
||||
add_definitions(-DUSE_INTERNAL_ISINF)
|
||||
endif()
|
||||
|
||||
set(_MODULE_LINK "${CMAKE_THREAD_LIBS_INIT}")
|
||||
get_filename_component(_lua_lib_dir ${LUA_LIBRARY} PATH)
|
||||
|
||||
if(APPLE)
|
||||
set(CMAKE_SHARED_MODULE_CREATE_C_FLAGS
|
||||
"${CMAKE_SHARED_MODULE_CREATE_C_FLAGS} -undefined dynamic_lookup")
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
# Win32 modules need to be linked to the Lua library.
|
||||
set(_MODULE_LINK ${LUA_LIBRARY} ${_MODULE_LINK})
|
||||
set(_lua_module_dir "${_lua_lib_dir}")
|
||||
# Windows sprintf()/strtod() handle NaN/inf differently. Not supported.
|
||||
add_definitions(-DDISABLE_INVALID_NUMBERS)
|
||||
else()
|
||||
set(_lua_module_dir "${_lua_lib_dir}/lua/5.1")
|
||||
endif()
|
||||
|
||||
add_library(cjson MODULE lua_cjson.c strbuf.c ${FPCONV_SOURCES})
|
||||
set_target_properties(cjson PROPERTIES PREFIX "")
|
||||
target_link_libraries(cjson ${_MODULE_LINK})
|
||||
install(TARGETS cjson DESTINATION "${_lua_module_dir}")
|
||||
|
||||
# vi:ai et sw=4 ts=4:
|
||||
@@ -1,20 +0,0 @@
|
||||
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.
|
||||
@@ -1,119 +0,0 @@
|
||||
##### Available defines for CJSON_CFLAGS #####
|
||||
##
|
||||
## USE_INTERNAL_ISINF: Workaround for Solaris platforms missing isinf().
|
||||
## DISABLE_INVALID_NUMBERS: Permanently disable invalid JSON numbers:
|
||||
## NaN, Infinity, hex.
|
||||
##
|
||||
## Optional built-in number conversion uses the following defines:
|
||||
## USE_INTERNAL_FPCONV: Use builtin strtod/dtoa for numeric conversions.
|
||||
## IEEE_BIG_ENDIAN: Required on big endian architectures.
|
||||
## MULTIPLE_THREADS: Must be set when Lua CJSON may be used in a
|
||||
## multi-threaded application. Requries _pthreads_.
|
||||
|
||||
##### Build defaults #####
|
||||
LUA_VERSION = 5.1
|
||||
TARGET = cjson.so
|
||||
PREFIX = /usr/local
|
||||
#CFLAGS = -g -Wall -pedantic -fno-inline
|
||||
CFLAGS = -O3 -Wall -pedantic -DNDEBUG
|
||||
CJSON_CFLAGS = -fpic
|
||||
CJSON_LDFLAGS = -shared
|
||||
LUA_INCLUDE_DIR = $(PREFIX)/include
|
||||
LUA_CMODULE_DIR = $(PREFIX)/lib/lua/$(LUA_VERSION)
|
||||
LUA_MODULE_DIR = $(PREFIX)/share/lua/$(LUA_VERSION)
|
||||
LUA_BIN_DIR = $(PREFIX)/bin
|
||||
|
||||
##### Platform overrides #####
|
||||
##
|
||||
## Tweak one of the platform sections below to suit your situation.
|
||||
##
|
||||
## See http://lua-users.org/wiki/BuildingModules for further platform
|
||||
## specific details.
|
||||
|
||||
## Linux
|
||||
|
||||
## FreeBSD
|
||||
#LUA_INCLUDE_DIR = $(PREFIX)/include/lua51
|
||||
|
||||
## MacOSX (Macports)
|
||||
#PREFIX = /opt/local
|
||||
#CJSON_LDFLAGS = -bundle -undefined dynamic_lookup
|
||||
|
||||
## Solaris
|
||||
#CC = gcc
|
||||
#CJSON_CFLAGS = -fpic -DUSE_INTERNAL_ISINF
|
||||
|
||||
## Windows (MinGW)
|
||||
#TARGET = cjson.dll
|
||||
#PREFIX = /home/user/opt
|
||||
#CJSON_CFLAGS = -DDISABLE_INVALID_NUMBERS
|
||||
#CJSON_LDFLAGS = -shared -L$(PREFIX)/lib -llua51
|
||||
#LUA_BIN_SUFFIX = .lua
|
||||
|
||||
##### Number conversion configuration #####
|
||||
|
||||
## Use Libc support for number conversion (default)
|
||||
FPCONV_OBJS = fpconv.o
|
||||
|
||||
## Use built in number conversion
|
||||
#FPCONV_OBJS = g_fmt.o dtoa.o
|
||||
#CJSON_CFLAGS += -DUSE_INTERNAL_FPCONV
|
||||
|
||||
## Compile built in number conversion for big endian architectures
|
||||
#CJSON_CFLAGS += -DIEEE_BIG_ENDIAN
|
||||
|
||||
## Compile built in number conversion to support multi-threaded
|
||||
## applications (recommended)
|
||||
#CJSON_CFLAGS += -pthread -DMULTIPLE_THREADS
|
||||
#CJSON_LDFLAGS += -pthread
|
||||
|
||||
##### End customisable sections #####
|
||||
|
||||
TEST_FILES = README bench.lua genutf8.pl test.lua octets-escaped.dat \
|
||||
example1.json example2.json example3.json example4.json \
|
||||
example5.json numbers.json rfc-example1.json \
|
||||
rfc-example2.json types.json
|
||||
DATAPERM = 644
|
||||
EXECPERM = 755
|
||||
|
||||
ASCIIDOC = asciidoc
|
||||
|
||||
BUILD_CFLAGS = -I$(LUA_INCLUDE_DIR) $(CJSON_CFLAGS)
|
||||
OBJS = lua_cjson.o strbuf.o $(FPCONV_OBJS)
|
||||
|
||||
.PHONY: all clean install install-extra doc
|
||||
|
||||
.SUFFIXES: .html .txt
|
||||
|
||||
.c.o:
|
||||
$(CC) -c $(CFLAGS) $(CPPFLAGS) $(BUILD_CFLAGS) -o $@ $<
|
||||
|
||||
.txt.html:
|
||||
$(ASCIIDOC) -n -a toc $<
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
doc: manual.html performance.html
|
||||
|
||||
$(TARGET): $(OBJS)
|
||||
$(CC) $(LDFLAGS) $(CJSON_LDFLAGS) -o $@ $(OBJS)
|
||||
|
||||
install: $(TARGET)
|
||||
mkdir -p $(DESTDIR)/$(LUA_CMODULE_DIR)
|
||||
cp $(TARGET) $(DESTDIR)/$(LUA_CMODULE_DIR)
|
||||
chmod $(EXECPERM) $(DESTDIR)/$(LUA_CMODULE_DIR)/$(TARGET)
|
||||
|
||||
install-extra:
|
||||
mkdir -p $(DESTDIR)/$(LUA_MODULE_DIR)/cjson/tests \
|
||||
$(DESTDIR)/$(LUA_BIN_DIR)
|
||||
cp lua/cjson/util.lua $(DESTDIR)/$(LUA_MODULE_DIR)/cjson
|
||||
chmod $(DATAPERM) $(DESTDIR)/$(LUA_MODULE_DIR)/cjson/util.lua
|
||||
cp lua/lua2json.lua $(DESTDIR)/$(LUA_BIN_DIR)/lua2json$(LUA_BIN_SUFFIX)
|
||||
chmod $(EXECPERM) $(DESTDIR)/$(LUA_BIN_DIR)/lua2json$(LUA_BIN_SUFFIX)
|
||||
cp lua/json2lua.lua $(DESTDIR)/$(LUA_BIN_DIR)/json2lua$(LUA_BIN_SUFFIX)
|
||||
chmod $(EXECPERM) $(DESTDIR)/$(LUA_BIN_DIR)/json2lua$(LUA_BIN_SUFFIX)
|
||||
cd tests; cp $(TEST_FILES) $(DESTDIR)/$(LUA_MODULE_DIR)/cjson/tests
|
||||
cd tests; chmod $(DATAPERM) $(TEST_FILES); chmod $(EXECPERM) *.lua *.pl
|
||||
|
||||
clean:
|
||||
rm -f *.o $(TARGET)
|
||||
@@ -1,44 +0,0 @@
|
||||
Version 2.1.0 (Mar 1 2012)
|
||||
* Added cjson.safe module interface which returns nil after an error
|
||||
* Improved Makefile compatibility with Solaris make
|
||||
|
||||
Version 2.0.0 (Jan 22 2012)
|
||||
* Improved platform compatibility for strtod/sprintf locale workaround
|
||||
* Added option to build with David Gay's dtoa.c for improved performance
|
||||
* Added support for Lua 5.2
|
||||
* Added option to encode infinity/NaN as JSON null
|
||||
* Fixed encode bug with a raised default limit and deeply nested tables
|
||||
* Updated Makefile for compatibility with non-GNU make implementations
|
||||
* Added CMake build support
|
||||
* Added HTML manual
|
||||
* Increased default nesting limit to 1000
|
||||
* Added support for re-entrant use of encode and decode
|
||||
* Added support for installing lua2json and json2lua utilities
|
||||
* Added encode_invalid_numbers() and decode_invalid_numbers()
|
||||
* Added decode_max_depth()
|
||||
* Removed registration of global cjson module table
|
||||
* Removed refuse_invalid_numbers()
|
||||
|
||||
Version 1.0.4 (Nov 30 2011)
|
||||
* Fixed numeric conversion under locales with a comma decimal separator
|
||||
|
||||
Version 1.0.3 (Sep 15 2011)
|
||||
* Fixed detection of objects with numeric string keys
|
||||
* Provided work around for missing isinf() on Solaris
|
||||
|
||||
Version 1.0.2 (May 30 2011)
|
||||
* Portability improvements for Windows
|
||||
- No longer links with -lm
|
||||
- Use "socket" instead of "posix" for sub-second timing
|
||||
* Removed UTF-8 test dependency on Perl Text::Iconv
|
||||
* Added simple CLI commands for testing Lua <-> JSON conversions
|
||||
* Added cjson.encode_number_precision()
|
||||
|
||||
Version 1.0.1 (May 10 2011)
|
||||
* Added build support for OSX
|
||||
* Removed unnecessary whitespace from JSON output
|
||||
* Added cjson.encode_keep_buffer()
|
||||
* Fixed memory leak on Lua stack overflow exception
|
||||
|
||||
Version 1.0 (May 9 2011)
|
||||
* Initial release
|
||||
@@ -1,9 +0,0 @@
|
||||
The following people have helped with bug reports, testing and/or
|
||||
suggestions:
|
||||
|
||||
- Louis-Philippe Perron (@loopole)
|
||||
- Ondřej Jirman
|
||||
- Steve Donovan <steve.j.donovan@gmail.com>
|
||||
- Zhang "agentzh" Yichun <agentzh@gmail.com>
|
||||
|
||||
Thanks!
|
||||
4358
3rd/lua-cjson/dtoa.c
4358
3rd/lua-cjson/dtoa.c
File diff suppressed because it is too large
Load Diff
@@ -1,74 +0,0 @@
|
||||
#ifndef _DTOA_CONFIG_H
|
||||
#define _DTOA_CONFIG_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/* Ensure dtoa.c does not USE_LOCALE. Lua CJSON must not use locale
|
||||
* aware conversion routines. */
|
||||
#undef USE_LOCALE
|
||||
|
||||
/* dtoa.c should not touch errno, Lua CJSON does not use it, and it
|
||||
* may not be threadsafe */
|
||||
#define NO_ERRNO
|
||||
|
||||
#define Long int32_t
|
||||
#define ULong uint32_t
|
||||
#define Llong int64_t
|
||||
#define ULLong uint64_t
|
||||
|
||||
#ifdef IEEE_BIG_ENDIAN
|
||||
#define IEEE_MC68k
|
||||
#else
|
||||
#define IEEE_8087
|
||||
#endif
|
||||
|
||||
#define MALLOC(n) xmalloc(n)
|
||||
|
||||
static void *xmalloc(size_t size)
|
||||
{
|
||||
void *p;
|
||||
|
||||
p = malloc(size);
|
||||
if (!p) {
|
||||
fprintf(stderr, "Out of memory");
|
||||
abort();
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
#ifdef MULTIPLE_THREADS
|
||||
|
||||
/* Enable locking to support multi-threaded applications */
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
static pthread_mutex_t private_dtoa_lock[2] = {
|
||||
PTHREAD_MUTEX_INITIALIZER,
|
||||
PTHREAD_MUTEX_INITIALIZER
|
||||
};
|
||||
|
||||
#define ACQUIRE_DTOA_LOCK(n) do { \
|
||||
int r = pthread_mutex_lock(&private_dtoa_lock[n]); \
|
||||
if (r) { \
|
||||
fprintf(stderr, "pthread_mutex_lock failed with %d\n", r); \
|
||||
abort(); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define FREE_DTOA_LOCK(n) do { \
|
||||
int r = pthread_mutex_unlock(&private_dtoa_lock[n]); \
|
||||
if (r) { \
|
||||
fprintf(stderr, "pthread_mutex_unlock failed with %d\n", r);\
|
||||
abort(); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#endif /* MULTIPLE_THREADS */
|
||||
|
||||
#endif /* _DTOA_CONFIG_H */
|
||||
|
||||
/* vi:ai et sw=4 ts=4:
|
||||
*/
|
||||
@@ -1,205 +0,0 @@
|
||||
/* fpconv - Floating point conversion routines
|
||||
*
|
||||
* Copyright (c) 2011-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.
|
||||
*/
|
||||
|
||||
/* JSON uses a '.' decimal separator. strtod() / sprintf() under C libraries
|
||||
* with locale support will break when the decimal separator is a comma.
|
||||
*
|
||||
* fpconv_* will around these issues with a translation buffer if required.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "fpconv.h"
|
||||
|
||||
/* Lua CJSON assumes the locale is the same for all threads within a
|
||||
* process and doesn't change after initialisation.
|
||||
*
|
||||
* This avoids the need for per thread storage or expensive checks
|
||||
* for call. */
|
||||
static char locale_decimal_point = '.';
|
||||
|
||||
/* In theory multibyte decimal_points are possible, but
|
||||
* Lua CJSON only supports UTF-8 and known locales only have
|
||||
* single byte decimal points ([.,]).
|
||||
*
|
||||
* localconv() may not be thread safe (=>crash), and nl_langinfo() is
|
||||
* not supported on some platforms. Use sprintf() instead - if the
|
||||
* locale does change, at least Lua CJSON won't crash. */
|
||||
static void fpconv_update_locale()
|
||||
{
|
||||
char buf[8];
|
||||
|
||||
snprintf(buf, sizeof(buf), "%g", 0.5);
|
||||
|
||||
/* Failing this test might imply the platform has a buggy dtoa
|
||||
* implementation or wide characters */
|
||||
if (buf[0] != '0' || buf[2] != '5' || buf[3] != 0) {
|
||||
fprintf(stderr, "Error: wide characters found or printf() bug.");
|
||||
abort();
|
||||
}
|
||||
|
||||
locale_decimal_point = buf[1];
|
||||
}
|
||||
|
||||
/* Check for a valid number character: [-+0-9a-yA-Y.]
|
||||
* Eg: -0.6e+5, infinity, 0xF0.F0pF0
|
||||
*
|
||||
* Used to find the probable end of a number. It doesn't matter if
|
||||
* invalid characters are counted - strtod() will find the valid
|
||||
* number if it exists. The risk is that slightly more memory might
|
||||
* be allocated before a parse error occurs. */
|
||||
static inline int valid_number_character(char ch)
|
||||
{
|
||||
char lower_ch;
|
||||
|
||||
if ('0' <= ch && ch <= '9')
|
||||
return 1;
|
||||
if (ch == '-' || ch == '+' || ch == '.')
|
||||
return 1;
|
||||
|
||||
/* Hex digits, exponent (e), base (p), "infinity",.. */
|
||||
lower_ch = ch | 0x20;
|
||||
if ('a' <= lower_ch && lower_ch <= 'y')
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Calculate the size of the buffer required for a strtod locale
|
||||
* conversion. */
|
||||
static int strtod_buffer_size(const char *s)
|
||||
{
|
||||
const char *p = s;
|
||||
|
||||
while (valid_number_character(*p))
|
||||
p++;
|
||||
|
||||
return p - s;
|
||||
}
|
||||
|
||||
/* Similar to strtod(), but must be passed the current locale's decimal point
|
||||
* character. Guaranteed to be called at the start of any valid number in a string */
|
||||
double fpconv_strtod(const char *nptr, char **endptr)
|
||||
{
|
||||
char localbuf[FPCONV_G_FMT_BUFSIZE];
|
||||
char *buf, *endbuf, *dp;
|
||||
int buflen;
|
||||
double value;
|
||||
|
||||
/* System strtod() is fine when decimal point is '.' */
|
||||
if (locale_decimal_point == '.')
|
||||
return strtod(nptr, endptr);
|
||||
|
||||
buflen = strtod_buffer_size(nptr);
|
||||
if (!buflen) {
|
||||
/* No valid characters found, standard strtod() return */
|
||||
*endptr = (char *)nptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Duplicate number into buffer */
|
||||
if (buflen >= FPCONV_G_FMT_BUFSIZE) {
|
||||
/* Handle unusually large numbers */
|
||||
buf = malloc(buflen + 1);
|
||||
if (!buf) {
|
||||
fprintf(stderr, "Out of memory");
|
||||
abort();
|
||||
}
|
||||
} else {
|
||||
/* This is the common case.. */
|
||||
buf = localbuf;
|
||||
}
|
||||
memcpy(buf, nptr, buflen);
|
||||
buf[buflen] = 0;
|
||||
|
||||
/* Update decimal point character if found */
|
||||
dp = strchr(buf, '.');
|
||||
if (dp)
|
||||
*dp = locale_decimal_point;
|
||||
|
||||
value = strtod(buf, &endbuf);
|
||||
*endptr = (char *)&nptr[endbuf - buf];
|
||||
if (buflen >= FPCONV_G_FMT_BUFSIZE)
|
||||
free(buf);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/* "fmt" must point to a buffer of at least 6 characters */
|
||||
static void set_number_format(char *fmt, int precision)
|
||||
{
|
||||
int d1, d2, i;
|
||||
|
||||
assert(1 <= precision && precision <= 14);
|
||||
|
||||
/* Create printf format (%.14g) from precision */
|
||||
d1 = precision / 10;
|
||||
d2 = precision % 10;
|
||||
fmt[0] = '%';
|
||||
fmt[1] = '.';
|
||||
i = 2;
|
||||
if (d1) {
|
||||
fmt[i++] = '0' + d1;
|
||||
}
|
||||
fmt[i++] = '0' + d2;
|
||||
fmt[i++] = 'g';
|
||||
fmt[i] = 0;
|
||||
}
|
||||
|
||||
/* Assumes there is always at least 32 characters available in the target buffer */
|
||||
int fpconv_g_fmt(char *str, double num, int precision)
|
||||
{
|
||||
char buf[FPCONV_G_FMT_BUFSIZE];
|
||||
char fmt[6];
|
||||
int len;
|
||||
char *b;
|
||||
|
||||
set_number_format(fmt, precision);
|
||||
|
||||
/* Pass through when decimal point character is dot. */
|
||||
if (locale_decimal_point == '.')
|
||||
return snprintf(str, FPCONV_G_FMT_BUFSIZE, fmt, num);
|
||||
|
||||
/* snprintf() to a buffer then translate for other decimal point characters */
|
||||
len = snprintf(buf, FPCONV_G_FMT_BUFSIZE, fmt, num);
|
||||
|
||||
/* Copy into target location. Translate decimal point if required */
|
||||
b = buf;
|
||||
do {
|
||||
*str++ = (*b == locale_decimal_point ? '.' : *b);
|
||||
} while(*b++);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
void fpconv_init()
|
||||
{
|
||||
fpconv_update_locale();
|
||||
}
|
||||
|
||||
/* vi:ai et sw=4 ts=4:
|
||||
*/
|
||||
@@ -1,22 +0,0 @@
|
||||
/* Lua CJSON floating point conversion routines */
|
||||
|
||||
/* Buffer required to store the largest string representation of a double.
|
||||
*
|
||||
* Longest double printed with %.14g is 21 characters long:
|
||||
* -1.7976931348623e+308 */
|
||||
# define FPCONV_G_FMT_BUFSIZE 32
|
||||
|
||||
#ifdef USE_INTERNAL_FPCONV
|
||||
static inline void fpconv_init()
|
||||
{
|
||||
/* Do nothing - not required */
|
||||
}
|
||||
#else
|
||||
extern inline void fpconv_init();
|
||||
#endif
|
||||
|
||||
extern int fpconv_g_fmt(char*, double, int);
|
||||
extern double fpconv_strtod(const char*, char**);
|
||||
|
||||
/* vi:ai et sw=4 ts=4:
|
||||
*/
|
||||
@@ -1,111 +0,0 @@
|
||||
/****************************************************************
|
||||
*
|
||||
* The author of this software is David M. Gay.
|
||||
*
|
||||
* Copyright (c) 1991, 1996 by Lucent Technologies.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose without fee is hereby granted, provided that this entire notice
|
||||
* is included in all copies of any software which is or includes a copy
|
||||
* or modification of this software and in all copies of the supporting
|
||||
* documentation for such software.
|
||||
*
|
||||
* THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
|
||||
* WARRANTY. IN PARTICULAR, NEITHER THE AUTHOR NOR LUCENT MAKES ANY
|
||||
* REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
|
||||
* OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
|
||||
*
|
||||
***************************************************************/
|
||||
|
||||
/* g_fmt(buf,x) stores the closest decimal approximation to x in buf;
|
||||
* it suffices to declare buf
|
||||
* char buf[32];
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
extern char *dtoa(double, int, int, int *, int *, char **);
|
||||
extern int g_fmt(char *, double, int);
|
||||
extern void freedtoa(char*);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
int
|
||||
fpconv_g_fmt(char *b, double x, int precision)
|
||||
{
|
||||
register int i, k;
|
||||
register char *s;
|
||||
int decpt, j, sign;
|
||||
char *b0, *s0, *se;
|
||||
|
||||
b0 = b;
|
||||
#ifdef IGNORE_ZERO_SIGN
|
||||
if (!x) {
|
||||
*b++ = '0';
|
||||
*b = 0;
|
||||
goto done;
|
||||
}
|
||||
#endif
|
||||
s = s0 = dtoa(x, 2, precision, &decpt, &sign, &se);
|
||||
if (sign)
|
||||
*b++ = '-';
|
||||
if (decpt == 9999) /* Infinity or Nan */ {
|
||||
while((*b++ = *s++));
|
||||
/* "b" is used to calculate the return length. Decrement to exclude the
|
||||
* Null terminator from the length */
|
||||
b--;
|
||||
goto done0;
|
||||
}
|
||||
if (decpt <= -4 || decpt > precision) {
|
||||
*b++ = *s++;
|
||||
if (*s) {
|
||||
*b++ = '.';
|
||||
while((*b = *s++))
|
||||
b++;
|
||||
}
|
||||
*b++ = 'e';
|
||||
/* sprintf(b, "%+.2d", decpt - 1); */
|
||||
if (--decpt < 0) {
|
||||
*b++ = '-';
|
||||
decpt = -decpt;
|
||||
}
|
||||
else
|
||||
*b++ = '+';
|
||||
for(j = 2, k = 10; 10*k <= decpt; j++, k *= 10);
|
||||
for(;;) {
|
||||
i = decpt / k;
|
||||
*b++ = i + '0';
|
||||
if (--j <= 0)
|
||||
break;
|
||||
decpt -= i*k;
|
||||
decpt *= 10;
|
||||
}
|
||||
*b = 0;
|
||||
}
|
||||
else if (decpt <= 0) {
|
||||
*b++ = '0';
|
||||
*b++ = '.';
|
||||
for(; decpt < 0; decpt++)
|
||||
*b++ = '0';
|
||||
while((*b++ = *s++));
|
||||
b--;
|
||||
}
|
||||
else {
|
||||
while((*b = *s++)) {
|
||||
b++;
|
||||
if (--decpt == 0 && *s)
|
||||
*b++ = '.';
|
||||
}
|
||||
for(; decpt > 0; decpt--)
|
||||
*b++ = '0';
|
||||
*b = 0;
|
||||
}
|
||||
done0:
|
||||
freedtoa(s0);
|
||||
#ifdef IGNORE_ZERO_SIGN
|
||||
done:
|
||||
#endif
|
||||
return b - b0;
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
package = "lua-cjson"
|
||||
version = "2.1.0-1"
|
||||
|
||||
source = {
|
||||
url = "http://www.kyne.com.au/~mark/software/download/lua-cjson-2.1.0.zip",
|
||||
}
|
||||
|
||||
description = {
|
||||
summary = "A fast JSON encoding/parsing module",
|
||||
detailed = [[
|
||||
The Lua CJSON module provides JSON support for Lua. It features:
|
||||
- Fast, standards compliant encoding/parsing routines
|
||||
- Full support for JSON with UTF-8, including decoding surrogate pairs
|
||||
- Optional run-time support for common exceptions to the JSON specification
|
||||
(infinity, NaN,..)
|
||||
- No dependencies on other libraries
|
||||
]],
|
||||
homepage = "http://www.kyne.com.au/~mark/software/lua-cjson.php",
|
||||
license = "MIT"
|
||||
}
|
||||
|
||||
dependencies = {
|
||||
"lua >= 5.1"
|
||||
}
|
||||
|
||||
build = {
|
||||
type = "builtin",
|
||||
modules = {
|
||||
cjson = {
|
||||
sources = { "lua_cjson.c", "strbuf.c", "fpconv.c" },
|
||||
defines = {
|
||||
-- LuaRocks does not support platform specific configuration for Solaris.
|
||||
-- Uncomment the line below on Solaris platforms if required.
|
||||
-- "USE_INTERNAL_ISINF"
|
||||
}
|
||||
}
|
||||
},
|
||||
install = {
|
||||
lua = {
|
||||
["cjson.util"] = "lua/cjson/util.lua"
|
||||
},
|
||||
bin = {
|
||||
json2lua = "lua/json2lua.lua",
|
||||
lua2json = "lua/lua2json.lua"
|
||||
}
|
||||
},
|
||||
-- Override default build options (per platform)
|
||||
platforms = {
|
||||
win32 = { modules = { cjson = { defines = {
|
||||
"DISABLE_INVALID_NUMBERS"
|
||||
} } } }
|
||||
},
|
||||
copy_directories = { "tests" }
|
||||
}
|
||||
|
||||
-- vi:ai et sw=4 ts=4:
|
||||
@@ -1,80 +0,0 @@
|
||||
%define luaver 5.1
|
||||
%define lualibdir %{_libdir}/lua/%{luaver}
|
||||
%define luadatadir %{_datadir}/lua/%{luaver}
|
||||
|
||||
Name: lua-cjson
|
||||
Version: 2.1.0
|
||||
Release: 1%{?dist}
|
||||
Summary: A fast JSON encoding/parsing module for Lua
|
||||
|
||||
Group: Development/Libraries
|
||||
License: MIT
|
||||
URL: http://www.kyne.com.au/~mark/software/lua-cjson/
|
||||
Source0: http://www.kyne.com.au/~mark/software/lua-cjson/download/lua-cjson-%{version}.tar.gz
|
||||
BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX)
|
||||
|
||||
BuildRequires: lua >= %{luaver}, lua-devel >= %{luaver}
|
||||
Requires: lua >= %{luaver}
|
||||
|
||||
%description
|
||||
The Lua CJSON module provides JSON support for Lua. It features:
|
||||
- Fast, standards compliant encoding/parsing routines
|
||||
- Full support for JSON with UTF-8, including decoding surrogate pairs
|
||||
- Optional run-time support for common exceptions to the JSON specification
|
||||
(infinity, NaN,..)
|
||||
- No dependencies on other libraries
|
||||
|
||||
|
||||
%prep
|
||||
%setup -q
|
||||
|
||||
|
||||
%build
|
||||
make %{?_smp_mflags} CFLAGS="%{optflags}" LUA_INCLUDE_DIR="%{_includedir}"
|
||||
|
||||
|
||||
%install
|
||||
rm -rf "$RPM_BUILD_ROOT"
|
||||
make install DESTDIR="$RPM_BUILD_ROOT" LUA_CMODULE_DIR="%{lualibdir}"
|
||||
make install-extra DESTDIR="$RPM_BUILD_ROOT" LUA_MODULE_DIR="%{luadatadir}" \
|
||||
LUA_BIN_DIR="%{_bindir}"
|
||||
|
||||
|
||||
%clean
|
||||
rm -rf "$RPM_BUILD_ROOT"
|
||||
|
||||
|
||||
%preun
|
||||
/bin/rm -f "%{luadatadir}/cjson/tests/utf8.dat"
|
||||
|
||||
|
||||
%files
|
||||
%defattr(-,root,root,-)
|
||||
%doc LICENSE NEWS performance.html performance.txt manual.html manual.txt rfc4627.txt THANKS
|
||||
%{lualibdir}/*
|
||||
%{luadatadir}/*
|
||||
%{_bindir}/*
|
||||
|
||||
|
||||
%changelog
|
||||
* Thu Mar 1 2012 Mark Pulford <mark@kyne.com.au> - 2.1.0-1
|
||||
- Update for 2.1.0
|
||||
|
||||
* Sun Jan 22 2012 Mark Pulford <mark@kyne.com.au> - 2.0.0-1
|
||||
- Update for 2.0.0
|
||||
- Install lua2json / json2lua utilities
|
||||
|
||||
* Wed Nov 27 2011 Mark Pulford <mark@kyne.com.au> - 1.0.4-1
|
||||
- Update for 1.0.4
|
||||
|
||||
* Wed Sep 15 2011 Mark Pulford <mark@kyne.com.au> - 1.0.3-1
|
||||
- Update for 1.0.3
|
||||
|
||||
* Sun May 29 2011 Mark Pulford <mark@kyne.com.au> - 1.0.2-1
|
||||
- Update for 1.0.2
|
||||
|
||||
* Sun May 10 2011 Mark Pulford <mark@kyne.com.au> - 1.0.1-1
|
||||
- Update for 1.0.1
|
||||
|
||||
* Sun May 1 2011 Mark Pulford <mark@kyne.com.au> - 1.0-1
|
||||
- Initial package
|
||||
@@ -1,271 +0,0 @@
|
||||
local json = require "cjson"
|
||||
|
||||
-- Various common routines used by the Lua CJSON package
|
||||
--
|
||||
-- Mark Pulford <mark@kyne.com.au>
|
||||
|
||||
-- Determine with a Lua table can be treated as an array.
|
||||
-- Explicitly returns "not an array" for very sparse arrays.
|
||||
-- Returns:
|
||||
-- -1 Not an array
|
||||
-- 0 Empty table
|
||||
-- >0 Highest index in the array
|
||||
local function is_array(table)
|
||||
local max = 0
|
||||
local count = 0
|
||||
for k, v in pairs(table) do
|
||||
if type(k) == "number" then
|
||||
if k > max then max = k end
|
||||
count = count + 1
|
||||
else
|
||||
return -1
|
||||
end
|
||||
end
|
||||
if max > count * 2 then
|
||||
return -1
|
||||
end
|
||||
|
||||
return max
|
||||
end
|
||||
|
||||
local serialise_value
|
||||
|
||||
local function serialise_table(value, indent, depth)
|
||||
local spacing, spacing2, indent2
|
||||
if indent then
|
||||
spacing = "\n" .. indent
|
||||
spacing2 = spacing .. " "
|
||||
indent2 = indent .. " "
|
||||
else
|
||||
spacing, spacing2, indent2 = " ", " ", false
|
||||
end
|
||||
depth = depth + 1
|
||||
if depth > 50 then
|
||||
return "Cannot serialise any further: too many nested tables"
|
||||
end
|
||||
|
||||
local max = is_array(value)
|
||||
|
||||
local comma = false
|
||||
local fragment = { "{" .. spacing2 }
|
||||
if max > 0 then
|
||||
-- Serialise array
|
||||
for i = 1, max do
|
||||
if comma then
|
||||
table.insert(fragment, "," .. spacing2)
|
||||
end
|
||||
table.insert(fragment, serialise_value(value[i], indent2, depth))
|
||||
comma = true
|
||||
end
|
||||
elseif max < 0 then
|
||||
-- Serialise table
|
||||
for k, v in pairs(value) do
|
||||
if comma then
|
||||
table.insert(fragment, "," .. spacing2)
|
||||
end
|
||||
table.insert(fragment,
|
||||
("[%s] = %s"):format(serialise_value(k, indent2, depth),
|
||||
serialise_value(v, indent2, depth)))
|
||||
comma = true
|
||||
end
|
||||
end
|
||||
table.insert(fragment, spacing .. "}")
|
||||
|
||||
return table.concat(fragment)
|
||||
end
|
||||
|
||||
function serialise_value(value, indent, depth)
|
||||
if indent == nil then indent = "" end
|
||||
if depth == nil then depth = 0 end
|
||||
|
||||
if value == json.null then
|
||||
return "json.null"
|
||||
elseif type(value) == "string" then
|
||||
return ("%q"):format(value)
|
||||
elseif type(value) == "nil" or type(value) == "number" or
|
||||
type(value) == "boolean" then
|
||||
return tostring(value)
|
||||
elseif type(value) == "table" then
|
||||
return serialise_table(value, indent, depth)
|
||||
else
|
||||
return "\"<" .. type(value) .. ">\""
|
||||
end
|
||||
end
|
||||
|
||||
local function file_load(filename)
|
||||
local file
|
||||
if filename == nil then
|
||||
file = io.stdin
|
||||
else
|
||||
local err
|
||||
file, err = io.open(filename, "rb")
|
||||
if file == nil then
|
||||
error(("Unable to read '%s': %s"):format(filename, err))
|
||||
end
|
||||
end
|
||||
local data = file:read("*a")
|
||||
|
||||
if filename ~= nil then
|
||||
file:close()
|
||||
end
|
||||
|
||||
if data == nil then
|
||||
error("Failed to read " .. filename)
|
||||
end
|
||||
|
||||
return data
|
||||
end
|
||||
|
||||
local function file_save(filename, data)
|
||||
local file
|
||||
if filename == nil then
|
||||
file = io.stdout
|
||||
else
|
||||
local err
|
||||
file, err = io.open(filename, "wb")
|
||||
if file == nil then
|
||||
error(("Unable to write '%s': %s"):format(filename, err))
|
||||
end
|
||||
end
|
||||
file:write(data)
|
||||
if filename ~= nil then
|
||||
file:close()
|
||||
end
|
||||
end
|
||||
|
||||
local function compare_values(val1, val2)
|
||||
local type1 = type(val1)
|
||||
local type2 = type(val2)
|
||||
if type1 ~= type2 then
|
||||
return false
|
||||
end
|
||||
|
||||
-- Check for NaN
|
||||
if type1 == "number" and val1 ~= val1 and val2 ~= val2 then
|
||||
return true
|
||||
end
|
||||
|
||||
if type1 ~= "table" then
|
||||
return val1 == val2
|
||||
end
|
||||
|
||||
-- check_keys stores all the keys that must be checked in val2
|
||||
local check_keys = {}
|
||||
for k, _ in pairs(val1) do
|
||||
check_keys[k] = true
|
||||
end
|
||||
|
||||
for k, v in pairs(val2) do
|
||||
if not check_keys[k] then
|
||||
return false
|
||||
end
|
||||
|
||||
if not compare_values(val1[k], val2[k]) then
|
||||
return false
|
||||
end
|
||||
|
||||
check_keys[k] = nil
|
||||
end
|
||||
for k, _ in pairs(check_keys) do
|
||||
-- Not the same if any keys from val1 were not found in val2
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
local test_count_pass = 0
|
||||
local test_count_total = 0
|
||||
|
||||
local function run_test_summary()
|
||||
return test_count_pass, test_count_total
|
||||
end
|
||||
|
||||
local function run_test(testname, func, input, should_work, output)
|
||||
local function status_line(name, status, value)
|
||||
local statusmap = { [true] = ":success", [false] = ":error" }
|
||||
if status ~= nil then
|
||||
name = name .. statusmap[status]
|
||||
end
|
||||
print(("[%s] %s"):format(name, serialise_value(value, false)))
|
||||
end
|
||||
|
||||
local result = { pcall(func, unpack(input)) }
|
||||
local success = table.remove(result, 1)
|
||||
|
||||
local correct = false
|
||||
if success == should_work and compare_values(result, output) then
|
||||
correct = true
|
||||
test_count_pass = test_count_pass + 1
|
||||
end
|
||||
test_count_total = test_count_total + 1
|
||||
|
||||
local teststatus = { [true] = "PASS", [false] = "FAIL" }
|
||||
print(("==> Test [%d] %s: %s"):format(test_count_total, testname,
|
||||
teststatus[correct]))
|
||||
|
||||
status_line("Input", nil, input)
|
||||
if not correct then
|
||||
status_line("Expected", should_work, output)
|
||||
end
|
||||
status_line("Received", success, result)
|
||||
print()
|
||||
|
||||
return correct, result
|
||||
end
|
||||
|
||||
local function run_test_group(tests)
|
||||
local function run_helper(name, func, input)
|
||||
if type(name) == "string" and #name > 0 then
|
||||
print("==> " .. name)
|
||||
end
|
||||
-- Not a protected call, these functions should never generate errors.
|
||||
func(unpack(input or {}))
|
||||
print()
|
||||
end
|
||||
|
||||
for _, v in ipairs(tests) do
|
||||
-- Run the helper if "should_work" is missing
|
||||
if v[4] == nil then
|
||||
run_helper(unpack(v))
|
||||
else
|
||||
run_test(unpack(v))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Run a Lua script in a separate environment
|
||||
local function run_script(script, env)
|
||||
local env = env or {}
|
||||
local func
|
||||
|
||||
-- Use setfenv() if it exists, otherwise assume Lua 5.2 load() exists
|
||||
if _G.setfenv then
|
||||
func = loadstring(script)
|
||||
if func then
|
||||
setfenv(func, env)
|
||||
end
|
||||
else
|
||||
func = load(script, nil, nil, env)
|
||||
end
|
||||
|
||||
if func == nil then
|
||||
error("Invalid syntax.")
|
||||
end
|
||||
func()
|
||||
|
||||
return env
|
||||
end
|
||||
|
||||
-- Export functions
|
||||
return {
|
||||
serialise_value = serialise_value,
|
||||
file_load = file_load,
|
||||
file_save = file_save,
|
||||
compare_values = compare_values,
|
||||
run_test_summary = run_test_summary,
|
||||
run_test = run_test,
|
||||
run_test_group = run_test_group,
|
||||
run_script = run_script
|
||||
}
|
||||
|
||||
-- vi:ai et sw=4 ts=4:
|
||||
@@ -1,14 +0,0 @@
|
||||
#!/usr/bin/env lua
|
||||
|
||||
-- usage: json2lua.lua [json_file]
|
||||
--
|
||||
-- Eg:
|
||||
-- echo '[ "testing" ]' | ./json2lua.lua
|
||||
-- ./json2lua.lua test.json
|
||||
|
||||
local json = require "cjson"
|
||||
local util = require "cjson.util"
|
||||
|
||||
local json_text = util.file_load(arg[1])
|
||||
local t = json.decode(json_text)
|
||||
print(util.serialise_value(t))
|
||||
@@ -1,20 +0,0 @@
|
||||
#!/usr/bin/env lua
|
||||
|
||||
-- usage: lua2json.lua [lua_file]
|
||||
--
|
||||
-- Eg:
|
||||
-- echo '{ "testing" }' | ./lua2json.lua
|
||||
-- ./lua2json.lua test.lua
|
||||
|
||||
local json = require "cjson"
|
||||
local util = require "cjson.util"
|
||||
|
||||
local env = {
|
||||
json = { null = json.null },
|
||||
null = json.null
|
||||
}
|
||||
|
||||
local t = util.run_script("data = " .. util.file_load(arg[1]), env)
|
||||
print(json.encode(t.data))
|
||||
|
||||
-- vi:ai et sw=4 ts=4:
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,611 +0,0 @@
|
||||
= Lua CJSON 2.1.0 Manual =
|
||||
Mark Pulford <mark@kyne.com.au>
|
||||
:revdate: 1st March 2012
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
The Lua CJSON module provides JSON support for Lua.
|
||||
|
||||
*Features*::
|
||||
- Fast, standards compliant encoding/parsing routines
|
||||
- Full support for JSON with UTF-8, including decoding surrogate pairs
|
||||
- Optional run-time support for common exceptions to the JSON
|
||||
specification (infinity, NaN,..)
|
||||
- No dependencies on other libraries
|
||||
|
||||
*Caveats*::
|
||||
- UTF-16 and UTF-32 are not supported
|
||||
|
||||
Lua CJSON is covered by the MIT license. Review the file +LICENSE+ for
|
||||
details.
|
||||
|
||||
The latest version of this software is available from the
|
||||
http://www.kyne.com.au/%7Emark/software/lua-cjson.php[Lua CJSON website].
|
||||
|
||||
Feel free to email me if you have any patches, suggestions, or comments.
|
||||
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
Lua CJSON requires either http://www.lua.org[Lua] 5.1, Lua 5.2, or
|
||||
http://www.luajit.org[LuaJIT] to build.
|
||||
|
||||
The build method can be selected from 4 options:
|
||||
|
||||
Make:: Unix (including Linux, BSD, Mac OSX & Solaris), Windows
|
||||
CMake:: Unix, Windows
|
||||
RPM:: Linux
|
||||
LuaRocks:: Unix, Windows
|
||||
|
||||
|
||||
Make
|
||||
~~~~
|
||||
|
||||
The included +Makefile+ has generic settings.
|
||||
|
||||
First, review and update the included makefile to suit your platform (if
|
||||
required).
|
||||
|
||||
Next, build and install the module:
|
||||
|
||||
[source,sh]
|
||||
make install
|
||||
|
||||
Or install manually into your Lua module directory:
|
||||
|
||||
[source,sh]
|
||||
make
|
||||
cp cjson.so $LUA_MODULE_DIRECTORY
|
||||
|
||||
|
||||
CMake
|
||||
~~~~~
|
||||
|
||||
http://www.cmake.org[CMake] can generate build configuration for many
|
||||
different platforms (including Unix and Windows).
|
||||
|
||||
First, generate the makefile for your platform using CMake. If CMake is
|
||||
unable to find Lua, manually set the +LUA_DIR+ environment variable to
|
||||
the base prefix of your Lua 5.1 installation.
|
||||
|
||||
While +cmake+ is used in the example below, +ccmake+ or +cmake-gui+ may
|
||||
be used to present an interface for changing the default build options.
|
||||
|
||||
[source,sh]
|
||||
mkdir build
|
||||
cd build
|
||||
# Optional: export LUA_DIR=$LUA51_PREFIX
|
||||
cmake ..
|
||||
|
||||
Next, build and install the module:
|
||||
|
||||
[source,sh]
|
||||
make install
|
||||
# Or:
|
||||
make
|
||||
cp cjson.so $LUA_MODULE_DIRECTORY
|
||||
|
||||
Review the
|
||||
http://www.cmake.org/cmake/help/documentation.html[CMake documentation]
|
||||
for further details.
|
||||
|
||||
|
||||
RPM
|
||||
~~~
|
||||
|
||||
Linux distributions using http://rpm.org[RPM] can create a package via
|
||||
the included RPM spec file. Ensure the +rpm-build+ package (or similar)
|
||||
has been installed.
|
||||
|
||||
Build and install the module via RPM:
|
||||
|
||||
[source,sh]
|
||||
rpmbuild -tb lua-cjson-2.1.0.tar.gz
|
||||
rpm -Uvh $LUA_CJSON_RPM
|
||||
|
||||
|
||||
LuaRocks
|
||||
~~~~~~~~
|
||||
|
||||
http://luarocks.org[LuaRocks] can be used to install and manage Lua
|
||||
modules on a wide range of platforms (including Windows).
|
||||
|
||||
First, extract the Lua CJSON source package.
|
||||
|
||||
Next, install the module:
|
||||
|
||||
[source,sh]
|
||||
cd lua-cjson-2.1.0
|
||||
luarocks make
|
||||
|
||||
[NOTE]
|
||||
LuaRocks does not support platform specific configuration for Solaris.
|
||||
On Solaris, you may need to manually uncomment +USE_INTERNAL_ISINF+ in
|
||||
the rockspec before building this module.
|
||||
|
||||
Review the http://luarocks.org/en/Documentation[LuaRocks documentation]
|
||||
for further details.
|
||||
|
||||
|
||||
[[build_options]]
|
||||
Build Options (#define)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Lua CJSON offers several +#define+ build options to address portability
|
||||
issues, and enable non-default features. Some build methods may
|
||||
automatically set platform specific options if required. Other features
|
||||
should be enabled manually.
|
||||
|
||||
USE_INTERNAL_ISINF:: Workaround for Solaris platforms missing +isinf+.
|
||||
DISABLE_INVALID_NUMBERS:: Recommended on platforms where +strtod+ /
|
||||
+sprintf+ are not POSIX compliant (eg, Windows MinGW). Prevents
|
||||
+cjson.encode_invalid_numbers+ and +cjson.decode_invalid_numbers+ from
|
||||
being enabled. However, +cjson.encode_invalid_numbers+ may still be
|
||||
set to +"null"+. When using the Lua CJSON built-in floating point
|
||||
conversion this option is unnecessary and is ignored.
|
||||
|
||||
|
||||
Built-in floating point conversion
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Lua CJSON may be built with David Gay's
|
||||
http://www.netlib.org/fp/[floating point conversion routines]. This can
|
||||
increase overall performance by up to 50% on some platforms when
|
||||
converting a large amount of numeric data. However, this option reduces
|
||||
portability and is disabled by default.
|
||||
|
||||
USE_INTERNAL_FPCONV:: Enable internal number conversion routines.
|
||||
IEEE_BIG_ENDIAN:: Must be set on big endian architectures.
|
||||
MULTIPLE_THREADS:: Must be set if Lua CJSON may be used in a
|
||||
multi-threaded application. Requires the _pthreads_ library.
|
||||
|
||||
|
||||
API (Functions)
|
||||
---------------
|
||||
|
||||
Synopsis
|
||||
~~~~~~~~
|
||||
|
||||
[source,lua]
|
||||
------------
|
||||
-- Module instantiation
|
||||
local cjson = require "cjson"
|
||||
local cjson2 = cjson.new()
|
||||
local cjson_safe = require "cjson.safe"
|
||||
|
||||
-- Translate Lua value to/from JSON
|
||||
text = cjson.encode(value)
|
||||
value = cjson.decode(text)
|
||||
|
||||
-- Get and/or set Lua CJSON configuration
|
||||
setting = cjson.decode_invalid_numbers([setting])
|
||||
setting = cjson.encode_invalid_numbers([setting])
|
||||
keep = cjson.encode_keep_buffer([keep])
|
||||
depth = cjson.encode_max_depth([depth])
|
||||
depth = cjson.decode_max_depth([depth])
|
||||
convert, ratio, safe = cjson.encode_sparse_array([convert[, ratio[, safe]]])
|
||||
------------
|
||||
|
||||
|
||||
Module Instantiation
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
[source,lua]
|
||||
------------
|
||||
local cjson = require "cjson"
|
||||
local cjson2 = cjson.new()
|
||||
local cjson_safe = require "cjson.safe"
|
||||
------------
|
||||
|
||||
Import Lua CJSON via the Lua +require+ function. Lua CJSON does not
|
||||
register a global module table.
|
||||
|
||||
The +cjson+ module will throw an error during JSON conversion if any
|
||||
invalid data is encountered. Refer to <<cjson_encode,+cjson.encode+>>
|
||||
and <<cjson_decode,+cjson.decode+>> for details.
|
||||
|
||||
The +cjson.safe+ module behaves identically to the +cjson+ module,
|
||||
except when errors are encountered during JSON conversion. On error, the
|
||||
+cjson_safe.encode+ and +cjson_safe.decode+ functions will return
|
||||
+nil+ followed by the error message.
|
||||
|
||||
+cjson.new+ can be used to instantiate an independent copy of the Lua
|
||||
CJSON module. The new module has a separate persistent encoding buffer,
|
||||
and default settings.
|
||||
|
||||
Lua CJSON can support Lua implementations using multiple preemptive
|
||||
threads within a single Lua state provided the persistent encoding
|
||||
buffer is not shared. This can be achieved by one of the following
|
||||
methods:
|
||||
|
||||
- Disabling the persistent encoding buffer with
|
||||
<<encode_keep_buffer,+cjson.encode_keep_buffer+>>
|
||||
- Ensuring each thread calls <<encode,+cjson.encode+>> separately (ie,
|
||||
treat +cjson.encode+ as non-reentrant).
|
||||
- Using a separate +cjson+ module table per preemptive thread
|
||||
(+cjson.new+)
|
||||
|
||||
[NOTE]
|
||||
Lua CJSON uses +strtod+ and +snprintf+ to perform numeric conversion as
|
||||
they are usually well supported, fast and bug free. However, these
|
||||
functions require a workaround for JSON encoding/parsing under locales
|
||||
using a comma decimal separator. Lua CJSON detects the current locale
|
||||
during instantiation to determine and automatically implement the
|
||||
workaround if required. Lua CJSON should be reinitialised via
|
||||
+cjson.new+ if the locale of the current process changes. Using a
|
||||
different locale per thread is not supported.
|
||||
|
||||
|
||||
decode
|
||||
~~~~~~
|
||||
|
||||
[source,lua]
|
||||
------------
|
||||
value = cjson.decode(json_text)
|
||||
------------
|
||||
|
||||
+cjson.decode+ will deserialise any UTF-8 JSON string into a Lua value
|
||||
or table.
|
||||
|
||||
UTF-16 and UTF-32 JSON strings are not supported.
|
||||
|
||||
+cjson.decode+ requires that any NULL (ASCII 0) and double quote (ASCII
|
||||
34) characters are escaped within strings. All escape codes will be
|
||||
decoded and other bytes will be passed transparently. UTF-8 characters
|
||||
are not validated during decoding and should be checked elsewhere if
|
||||
required.
|
||||
|
||||
JSON +null+ will be converted to a NULL +lightuserdata+ value. This can
|
||||
be compared with +cjson.null+ for convenience.
|
||||
|
||||
By default, numbers incompatible with the JSON specification (infinity,
|
||||
NaN, hexadecimal) can be decoded. This default can be changed with
|
||||
<<decode_invalid_numbers,+cjson.decode_invalid_numbers+>>.
|
||||
|
||||
.Example: Decoding
|
||||
[source,lua]
|
||||
json_text = '[ true, { "foo": "bar" } ]'
|
||||
value = cjson.decode(json_text)
|
||||
-- Returns: { true, { foo = "bar" } }
|
||||
|
||||
[CAUTION]
|
||||
Care must be taken after decoding JSON objects with numeric keys. Each
|
||||
numeric key will be stored as a Lua +string+. Any subsequent code
|
||||
assuming type +number+ may break.
|
||||
|
||||
|
||||
[[decode_invalid_numbers]]
|
||||
decode_invalid_numbers
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
[source,lua]
|
||||
------------
|
||||
setting = cjson.decode_invalid_numbers([setting])
|
||||
-- "setting" must be a boolean. Default: true.
|
||||
------------
|
||||
|
||||
Lua CJSON may generate an error when trying to decode numbers not
|
||||
supported by the JSON specification. _Invalid numbers_ are defined as:
|
||||
|
||||
- infinity
|
||||
- not-a-number (NaN)
|
||||
- hexadecimal
|
||||
|
||||
Available settings:
|
||||
|
||||
+true+:: Accept and decode _invalid numbers_. This is the default
|
||||
setting.
|
||||
+false+:: Throw an error when _invalid numbers_ are encountered.
|
||||
|
||||
The current setting is always returned, and is only updated when an
|
||||
argument is provided.
|
||||
|
||||
|
||||
[[decode_max_depth]]
|
||||
decode_max_depth
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
[source,lua]
|
||||
------------
|
||||
depth = cjson.decode_max_depth([depth])
|
||||
-- "depth" must be a positive integer. Default: 1000.
|
||||
------------
|
||||
|
||||
Lua CJSON will generate an error when parsing deeply nested JSON once
|
||||
the maximum array/object depth has been exceeded. This check prevents
|
||||
unnecessarily complicated JSON from slowing down the application, or
|
||||
crashing the application due to lack of process stack space.
|
||||
|
||||
An error may be generated before the depth limit is hit if Lua is unable
|
||||
to allocate more objects on the Lua stack.
|
||||
|
||||
By default, Lua CJSON will reject JSON with arrays and/or objects nested
|
||||
more than 1000 levels deep.
|
||||
|
||||
The current setting is always returned, and is only updated when an
|
||||
argument is provided.
|
||||
|
||||
|
||||
[[encode]]
|
||||
encode
|
||||
~~~~~~
|
||||
|
||||
[source,lua]
|
||||
------------
|
||||
json_text = cjson.encode(value)
|
||||
------------
|
||||
|
||||
+cjson.encode+ will serialise a Lua value into a string containing the
|
||||
JSON representation.
|
||||
|
||||
+cjson.encode+ supports the following types:
|
||||
|
||||
- +boolean+
|
||||
- +lightuserdata+ (NULL value only)
|
||||
- +nil+
|
||||
- +number+
|
||||
- +string+
|
||||
- +table+
|
||||
|
||||
The remaining Lua types will generate an error:
|
||||
|
||||
- +function+
|
||||
- +lightuserdata+ (non-NULL values)
|
||||
- +thread+
|
||||
- +userdata+
|
||||
|
||||
By default, numbers are encoded with 14 significant digits. Refer to
|
||||
<<encode_number_precision,+cjson.encode_number_precision+>> for details.
|
||||
|
||||
Lua CJSON will escape the following characters within each UTF-8 string:
|
||||
|
||||
- Control characters (ASCII 0 - 31)
|
||||
- Double quote (ASCII 34)
|
||||
- Forward slash (ASCII 47)
|
||||
- Blackslash (ASCII 92)
|
||||
- Delete (ASCII 127)
|
||||
|
||||
All other bytes are passed transparently.
|
||||
|
||||
[CAUTION]
|
||||
=========
|
||||
Lua CJSON will successfully encode/decode binary strings, but this is
|
||||
technically not supported by JSON and may not be compatible with other
|
||||
JSON libraries. To ensure the output is valid JSON, applications should
|
||||
ensure all Lua strings passed to +cjson.encode+ are UTF-8.
|
||||
|
||||
Base64 is commonly used to encode binary data as the most efficient
|
||||
encoding under UTF-8 can only reduce the encoded size by a further
|
||||
~8%. Lua Base64 routines can be found in the
|
||||
http://w3.impa.br/%7Ediego/software/luasocket/[LuaSocket] and
|
||||
http://www.tecgraf.puc-rio.br/%7Elhf/ftp/lua/#lbase64[lbase64] packages.
|
||||
=========
|
||||
|
||||
Lua CJSON uses a heuristic to determine whether to encode a Lua table as
|
||||
a JSON array or an object. A Lua table with only positive integer keys
|
||||
of type +number+ will be encoded as a JSON array. All other tables will
|
||||
be encoded as a JSON object.
|
||||
|
||||
Lua CJSON does not use metamethods when serialising tables.
|
||||
|
||||
- +rawget+ is used to iterate over Lua arrays
|
||||
- +next+ is used to iterate over Lua objects
|
||||
|
||||
Lua arrays with missing entries (_sparse arrays_) may optionally be
|
||||
encoded in several different ways. Refer to
|
||||
<<encode_sparse_array,+cjson.encode_sparse_array+>> for details.
|
||||
|
||||
JSON object keys are always strings. Hence +cjson.encode+ only supports
|
||||
table keys which are type +number+ or +string+. All other types will
|
||||
generate an error.
|
||||
|
||||
[NOTE]
|
||||
Standards compliant JSON must be encapsulated in either an object (+{}+)
|
||||
or an array (+[]+). If strictly standards compliant JSON is desired, a
|
||||
table must be passed to +cjson.encode+.
|
||||
|
||||
By default, encoding the following Lua values will generate errors:
|
||||
|
||||
- Numbers incompatible with the JSON specification (infinity, NaN)
|
||||
- Tables nested more than 1000 levels deep
|
||||
- Excessively sparse Lua arrays
|
||||
|
||||
These defaults can be changed with:
|
||||
|
||||
- <<encode_invalid_numbers,+cjson.encode_invalid_numbers+>>
|
||||
- <<encode_max_depth,+cjson.encode_max_depth+>>
|
||||
- <<encode_sparse_array,+cjson.encode_sparse_array+>>
|
||||
|
||||
.Example: Encoding
|
||||
[source,lua]
|
||||
value = { true, { foo = "bar" } }
|
||||
json_text = cjson.encode(value)
|
||||
-- Returns: '[true,{"foo":"bar"}]'
|
||||
|
||||
|
||||
[[encode_invalid_numbers]]
|
||||
encode_invalid_numbers
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
[source,lua]
|
||||
------------
|
||||
setting = cjson.encode_invalid_numbers([setting])
|
||||
-- "setting" must a boolean or "null". Default: false.
|
||||
------------
|
||||
|
||||
Lua CJSON may generate an error when encoding floating point numbers not
|
||||
supported by the JSON specification (_invalid numbers_):
|
||||
|
||||
- infinity
|
||||
- not-a-number (NaN)
|
||||
|
||||
Available settings:
|
||||
|
||||
+true+:: Allow _invalid numbers_ to be encoded. This will generate
|
||||
non-standard JSON, but this output is supported by some libraries.
|
||||
+"null"+:: Encode _invalid numbers_ as a JSON +null+ value. This allows
|
||||
infinity and NaN to be encoded into valid JSON.
|
||||
+false+:: Throw an error when attempting to encode _invalid numbers_.
|
||||
This is the default setting.
|
||||
|
||||
The current setting is always returned, and is only updated when an
|
||||
argument is provided.
|
||||
|
||||
|
||||
[[encode_keep_buffer]]
|
||||
encode_keep_buffer
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
[source,lua]
|
||||
------------
|
||||
keep = cjson.encode_keep_buffer([keep])
|
||||
-- "keep" must be a boolean. Default: true.
|
||||
------------
|
||||
|
||||
Lua CJSON can reuse the JSON encoding buffer to improve performance.
|
||||
|
||||
Available settings:
|
||||
|
||||
+true+:: The buffer will grow to the largest size required and is not
|
||||
freed until the Lua CJSON module is garbage collected. This is the
|
||||
default setting.
|
||||
+false+:: Free the encode buffer after each call to +cjson.encode+.
|
||||
|
||||
The current setting is always returned, and is only updated when an
|
||||
argument is provided.
|
||||
|
||||
|
||||
[[encode_max_depth]]
|
||||
encode_max_depth
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
[source,lua]
|
||||
------------
|
||||
depth = cjson.encode_max_depth([depth])
|
||||
-- "depth" must be a positive integer. Default: 1000.
|
||||
------------
|
||||
|
||||
Once the maximum table depth has been exceeded Lua CJSON will generate
|
||||
an error. This prevents a deeply nested or recursive data structure from
|
||||
crashing the application.
|
||||
|
||||
By default, Lua CJSON will generate an error when trying to encode data
|
||||
structures with more than 1000 nested tables.
|
||||
|
||||
The current setting is always returned, and is only updated when an
|
||||
argument is provided.
|
||||
|
||||
.Example: Recursive Lua table
|
||||
[source,lua]
|
||||
a = {}; a[1] = a
|
||||
|
||||
|
||||
[[encode_number_precision]]
|
||||
encode_number_precision
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
[source,lua]
|
||||
------------
|
||||
precision = cjson.encode_number_precision([precision])
|
||||
-- "precision" must be an integer between 1 and 14. Default: 14.
|
||||
------------
|
||||
|
||||
The amount of significant digits returned by Lua CJSON when encoding
|
||||
numbers can be changed to balance accuracy versus performance. For data
|
||||
structures containing many numbers, setting
|
||||
+cjson.encode_number_precision+ to a smaller integer, for example +3+,
|
||||
can improve encoding performance by up to 50%.
|
||||
|
||||
By default, Lua CJSON will output 14 significant digits when converting
|
||||
a number to text.
|
||||
|
||||
The current setting is always returned, and is only updated when an
|
||||
argument is provided.
|
||||
|
||||
|
||||
[[encode_sparse_array]]
|
||||
encode_sparse_array
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
[source,lua]
|
||||
------------
|
||||
convert, ratio, safe = cjson.encode_sparse_array([convert[, ratio[, safe]]])
|
||||
-- "convert" must be a boolean. Default: false.
|
||||
-- "ratio" must be a positive integer. Default: 2.
|
||||
-- "safe" must be a positive integer. Default: 10.
|
||||
------------
|
||||
|
||||
Lua CJSON classifies a Lua table into one of three kinds when encoding a
|
||||
JSON array. This is determined by the number of values missing from the
|
||||
Lua array as follows:
|
||||
|
||||
Normal:: All values are available.
|
||||
Sparse:: At least 1 value is missing.
|
||||
Excessively sparse:: The number of values missing exceeds the configured
|
||||
ratio.
|
||||
|
||||
Lua CJSON encodes sparse Lua arrays as JSON arrays using JSON +null+ for
|
||||
the missing entries.
|
||||
|
||||
An array is excessively sparse when all the following conditions are
|
||||
met:
|
||||
|
||||
- +ratio+ > +0+
|
||||
- _maximum_index_ > +safe+
|
||||
- _maximum_index_ > _item_count_ * +ratio+
|
||||
|
||||
Lua CJSON will never consider an array to be _excessively sparse_ when
|
||||
+ratio+ = +0+. The +safe+ limit ensures that small Lua arrays are always
|
||||
encoded as sparse arrays.
|
||||
|
||||
By default, attempting to encode an _excessively sparse_ array will
|
||||
generate an error. If +convert+ is set to +true+, _excessively sparse_
|
||||
arrays will be converted to a JSON object.
|
||||
|
||||
The current settings are always returned. A particular setting is only
|
||||
changed when the argument is provided (non-++nil++).
|
||||
|
||||
.Example: Encoding a sparse array
|
||||
[source,lua]
|
||||
cjson.encode({ [3] = "data" })
|
||||
-- Returns: '[null,null,"data"]'
|
||||
|
||||
.Example: Enabling conversion to a JSON object
|
||||
[source,lua]
|
||||
cjson.encode_sparse_array(true)
|
||||
cjson.encode({ [1000] = "excessively sparse" })
|
||||
-- Returns: '{"1000":"excessively sparse"}'
|
||||
|
||||
|
||||
API (Variables)
|
||||
---------------
|
||||
|
||||
_NAME
|
||||
~~~~~
|
||||
|
||||
The name of the Lua CJSON module (+"cjson"+).
|
||||
|
||||
|
||||
_VERSION
|
||||
~~~~~~~~
|
||||
|
||||
The version number of the Lua CJSON module (+"2.1.0"+).
|
||||
|
||||
|
||||
null
|
||||
~~~~
|
||||
|
||||
Lua CJSON decodes JSON +null+ as a Lua +lightuserdata+ NULL pointer.
|
||||
+cjson.null+ is provided for comparison.
|
||||
|
||||
|
||||
[sect1]
|
||||
References
|
||||
----------
|
||||
|
||||
- http://tools.ietf.org/html/rfc4627[RFC 4627]
|
||||
- http://www.json.org/[JSON website]
|
||||
|
||||
|
||||
// vi:ft=asciidoc tw=72:
|
||||
@@ -1,617 +0,0 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
|
||||
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<meta name="generator" content="AsciiDoc 8.4.5" />
|
||||
<title>JSON module performance comparison under Lua</title>
|
||||
<style type="text/css">
|
||||
/* Debug borders */
|
||||
p, li, dt, dd, div, pre, h1, h2, h3, h4, h5, h6 {
|
||||
/*
|
||||
border: 1px solid red;
|
||||
*/
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 1em 5% 1em 5%;
|
||||
}
|
||||
|
||||
a {
|
||||
color: blue;
|
||||
text-decoration: underline;
|
||||
}
|
||||
a:visited {
|
||||
color: fuchsia;
|
||||
}
|
||||
|
||||
em {
|
||||
font-style: italic;
|
||||
color: navy;
|
||||
}
|
||||
|
||||
strong {
|
||||
font-weight: bold;
|
||||
color: #083194;
|
||||
}
|
||||
|
||||
tt {
|
||||
color: navy;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
color: #527bbd;
|
||||
font-family: sans-serif;
|
||||
margin-top: 1.2em;
|
||||
margin-bottom: 0.5em;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
h1, h2, h3 {
|
||||
border-bottom: 2px solid silver;
|
||||
}
|
||||
h2 {
|
||||
padding-top: 0.5em;
|
||||
}
|
||||
h3 {
|
||||
float: left;
|
||||
}
|
||||
h3 + * {
|
||||
clear: left;
|
||||
}
|
||||
|
||||
div.sectionbody {
|
||||
font-family: serif;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
hr {
|
||||
border: 1px solid silver;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-top: 0.5em;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
ul, ol, li > p {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
pre {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
span#author {
|
||||
color: #527bbd;
|
||||
font-family: sans-serif;
|
||||
font-weight: bold;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
span#email {
|
||||
}
|
||||
span#revnumber, span#revdate, span#revremark {
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
div#footer {
|
||||
font-family: sans-serif;
|
||||
font-size: small;
|
||||
border-top: 2px solid silver;
|
||||
padding-top: 0.5em;
|
||||
margin-top: 4.0em;
|
||||
}
|
||||
div#footer-text {
|
||||
float: left;
|
||||
padding-bottom: 0.5em;
|
||||
}
|
||||
div#footer-badges {
|
||||
float: right;
|
||||
padding-bottom: 0.5em;
|
||||
}
|
||||
|
||||
div#preamble {
|
||||
margin-top: 1.5em;
|
||||
margin-bottom: 1.5em;
|
||||
}
|
||||
div.tableblock, div.imageblock, div.exampleblock, div.verseblock,
|
||||
div.quoteblock, div.literalblock, div.listingblock, div.sidebarblock,
|
||||
div.admonitionblock {
|
||||
margin-top: 1.5em;
|
||||
margin-bottom: 1.5em;
|
||||
}
|
||||
div.admonitionblock {
|
||||
margin-top: 2.5em;
|
||||
margin-bottom: 2.5em;
|
||||
}
|
||||
|
||||
div.content { /* Block element content. */
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* Block element titles. */
|
||||
div.title, caption.title {
|
||||
color: #527bbd;
|
||||
font-family: sans-serif;
|
||||
font-weight: bold;
|
||||
text-align: left;
|
||||
margin-top: 1.0em;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
div.title + * {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
td div.title:first-child {
|
||||
margin-top: 0.0em;
|
||||
}
|
||||
div.content div.title:first-child {
|
||||
margin-top: 0.0em;
|
||||
}
|
||||
div.content + div.title {
|
||||
margin-top: 0.0em;
|
||||
}
|
||||
|
||||
div.sidebarblock > div.content {
|
||||
background: #ffffee;
|
||||
border: 1px solid silver;
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
div.listingblock > div.content {
|
||||
border: 1px solid silver;
|
||||
background: #f4f4f4;
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
div.quoteblock {
|
||||
padding-left: 2.0em;
|
||||
margin-right: 10%;
|
||||
}
|
||||
div.quoteblock > div.attribution {
|
||||
padding-top: 0.5em;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
div.verseblock {
|
||||
padding-left: 2.0em;
|
||||
margin-right: 10%;
|
||||
}
|
||||
div.verseblock > div.content {
|
||||
white-space: pre;
|
||||
}
|
||||
div.verseblock > div.attribution {
|
||||
padding-top: 0.75em;
|
||||
text-align: left;
|
||||
}
|
||||
/* DEPRECATED: Pre version 8.2.7 verse style literal block. */
|
||||
div.verseblock + div.attribution {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
div.admonitionblock .icon {
|
||||
vertical-align: top;
|
||||
font-size: 1.1em;
|
||||
font-weight: bold;
|
||||
text-decoration: underline;
|
||||
color: #527bbd;
|
||||
padding-right: 0.5em;
|
||||
}
|
||||
div.admonitionblock td.content {
|
||||
padding-left: 0.5em;
|
||||
border-left: 2px solid silver;
|
||||
}
|
||||
|
||||
div.exampleblock > div.content {
|
||||
border-left: 2px solid silver;
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
div.imageblock div.content { padding-left: 0; }
|
||||
span.image img { border-style: none; }
|
||||
a.image:visited { color: white; }
|
||||
|
||||
dl {
|
||||
margin-top: 0.8em;
|
||||
margin-bottom: 0.8em;
|
||||
}
|
||||
dt {
|
||||
margin-top: 0.5em;
|
||||
margin-bottom: 0;
|
||||
font-style: normal;
|
||||
color: navy;
|
||||
}
|
||||
dd > *:first-child {
|
||||
margin-top: 0.1em;
|
||||
}
|
||||
|
||||
ul, ol {
|
||||
list-style-position: outside;
|
||||
}
|
||||
ol.arabic {
|
||||
list-style-type: decimal;
|
||||
}
|
||||
ol.loweralpha {
|
||||
list-style-type: lower-alpha;
|
||||
}
|
||||
ol.upperalpha {
|
||||
list-style-type: upper-alpha;
|
||||
}
|
||||
ol.lowerroman {
|
||||
list-style-type: lower-roman;
|
||||
}
|
||||
ol.upperroman {
|
||||
list-style-type: upper-roman;
|
||||
}
|
||||
|
||||
div.compact ul, div.compact ol,
|
||||
div.compact p, div.compact p,
|
||||
div.compact div, div.compact div {
|
||||
margin-top: 0.1em;
|
||||
margin-bottom: 0.1em;
|
||||
}
|
||||
|
||||
div.tableblock > table {
|
||||
border: 3px solid #527bbd;
|
||||
}
|
||||
thead {
|
||||
font-family: sans-serif;
|
||||
font-weight: bold;
|
||||
}
|
||||
tfoot {
|
||||
font-weight: bold;
|
||||
}
|
||||
td > div.verse {
|
||||
white-space: pre;
|
||||
}
|
||||
p.table {
|
||||
margin-top: 0;
|
||||
}
|
||||
/* Because the table frame attribute is overriden by CSS in most browsers. */
|
||||
div.tableblock > table[frame="void"] {
|
||||
border-style: none;
|
||||
}
|
||||
div.tableblock > table[frame="hsides"] {
|
||||
border-left-style: none;
|
||||
border-right-style: none;
|
||||
}
|
||||
div.tableblock > table[frame="vsides"] {
|
||||
border-top-style: none;
|
||||
border-bottom-style: none;
|
||||
}
|
||||
|
||||
|
||||
div.hdlist {
|
||||
margin-top: 0.8em;
|
||||
margin-bottom: 0.8em;
|
||||
}
|
||||
div.hdlist tr {
|
||||
padding-bottom: 15px;
|
||||
}
|
||||
dt.hdlist1.strong, td.hdlist1.strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
td.hdlist1 {
|
||||
vertical-align: top;
|
||||
font-style: normal;
|
||||
padding-right: 0.8em;
|
||||
color: navy;
|
||||
}
|
||||
td.hdlist2 {
|
||||
vertical-align: top;
|
||||
}
|
||||
div.hdlist.compact tr {
|
||||
margin: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.comment {
|
||||
background: yellow;
|
||||
}
|
||||
|
||||
@media print {
|
||||
div#footer-badges { display: none; }
|
||||
}
|
||||
|
||||
div#toctitle {
|
||||
color: #527bbd;
|
||||
font-family: sans-serif;
|
||||
font-size: 1.1em;
|
||||
font-weight: bold;
|
||||
margin-top: 1.0em;
|
||||
margin-bottom: 0.1em;
|
||||
}
|
||||
|
||||
div.toclevel1, div.toclevel2, div.toclevel3, div.toclevel4 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
div.toclevel2 {
|
||||
margin-left: 2em;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
div.toclevel3 {
|
||||
margin-left: 4em;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
div.toclevel4 {
|
||||
margin-left: 6em;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
/* Workarounds for IE6's broken and incomplete CSS2. */
|
||||
|
||||
div.sidebar-content {
|
||||
background: #ffffee;
|
||||
border: 1px solid silver;
|
||||
padding: 0.5em;
|
||||
}
|
||||
div.sidebar-title, div.image-title {
|
||||
color: #527bbd;
|
||||
font-family: sans-serif;
|
||||
font-weight: bold;
|
||||
margin-top: 0.0em;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
div.listingblock div.content {
|
||||
border: 1px solid silver;
|
||||
background: #f4f4f4;
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
div.quoteblock-attribution {
|
||||
padding-top: 0.5em;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
div.verseblock-content {
|
||||
white-space: pre;
|
||||
}
|
||||
div.verseblock-attribution {
|
||||
padding-top: 0.75em;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
div.exampleblock-content {
|
||||
border-left: 2px solid silver;
|
||||
padding-left: 0.5em;
|
||||
}
|
||||
|
||||
/* IE6 sets dynamically generated links as visited. */
|
||||
div#toc a:visited { color: blue; }
|
||||
</style>
|
||||
<script type="text/javascript">
|
||||
/*<+'])');
|
||||
// Function that scans the DOM tree for header elements (the DOM2
|
||||
// nodeIterator API would be a better technique but not supported by all
|
||||
// browsers).
|
||||
var iterate = function (el) {
|
||||
for (var i = el.firstChild; i != null; i = i.nextSibling) {
|
||||
if (i.nodeType == 1 /* Node.ELEMENT_NODE */) {
|
||||
var mo = re.exec(i.tagName)
|
||||
if (mo)
|
||||
result[result.length] = new TocEntry(i, getText(i), mo[1]-1);
|
||||
iterate(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
iterate(el);
|
||||
return result;
|
||||
}
|
||||
|
||||
// This function does the work. toclevels = 1..4.
|
||||
function generateToc(toclevels) {
|
||||
var toc = document.getElementById("toc");
|
||||
var entries = tocEntries(document.getElementsByTagName("body")[0], toclevels);
|
||||
for (var i = 0; i < entries.length; ++i) {
|
||||
var entry = entries[i];
|
||||
if (entry.element.id == "")
|
||||
entry.element.id = "toc" + i;
|
||||
var a = document.createElement("a");
|
||||
a.href = "#" + entry.element.id;
|
||||
a.appendChild(document.createTextNode(entry.text));
|
||||
var div = document.createElement("div");
|
||||
div.appendChild(a);
|
||||
div.className = "toclevel" + entry.toclevel;
|
||||
toc.appendChild(div);
|
||||
}
|
||||
if (entries.length == 0)
|
||||
document.getElementById("header").removeChild(toc);
|
||||
}
|
||||
/*]]>*/
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="header">
|
||||
<h1>JSON module performance comparison under Lua</h1>
|
||||
<span id="author">Mark Pulford</span><br />
|
||||
<span id="email"><tt><<a href="mailto:mark@kyne.com.au">mark@kyne.com.au</a>></tt></span><br />
|
||||
<span id="revdate">January 22, 2012</span>
|
||||
<div id="toc">
|
||||
<div id="toctitle">Table of Contents</div>
|
||||
<noscript><p><b>JavaScript must be enabled in your browser to display the table of contents.</b></p></noscript>
|
||||
</div>
|
||||
</div>
|
||||
<div id="preamble">
|
||||
<div class="sectionbody">
|
||||
<div class="paragraph"><p>This performance comparison aims to provide a guide of relative
|
||||
performance between several fast and popular JSON modules.</p></div>
|
||||
<div class="paragraph"><p>The examples used in this comparison were mostly sourced from the
|
||||
<a href="http://json.org">JSON website</a> and
|
||||
<a href="http://tools.ietf.org/html/rfc4627">RFC 4627</a>.</p></div>
|
||||
<div class="paragraph"><p>Performance will vary widely between platforms and data sets. These
|
||||
results should only be used as an approximation.</p></div>
|
||||
</div>
|
||||
</div>
|
||||
<h2 id="_modules">1. Modules</h2>
|
||||
<div class="sectionbody">
|
||||
<div class="paragraph"><p>The following JSON modules for Lua were tested:</p></div>
|
||||
<div class="dlist"><dl>
|
||||
<dt class="hdlist1">
|
||||
<a href="http://chiselapp.com/user/dhkolf/repository/dkjson/">DKJSON 2.1</a>
|
||||
</dt>
|
||||
<dd>
|
||||
<div class="ulist"><ul>
|
||||
<li>
|
||||
<p>
|
||||
Lua implementation with no dependencies on other libraries
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
Supports LPeg to improve decode performance
|
||||
</p>
|
||||
</li>
|
||||
</ul></div>
|
||||
</dd>
|
||||
<dt class="hdlist1">
|
||||
<a href="https://github.com/brimworks/lua-yajl">Lua YAJL 2.0</a>
|
||||
</dt>
|
||||
<dd>
|
||||
<div class="ulist"><ul>
|
||||
<li>
|
||||
<p>
|
||||
C wrapper for the YAJL library
|
||||
</p>
|
||||
</li>
|
||||
</ul></div>
|
||||
</dd>
|
||||
<dt class="hdlist1">
|
||||
<a href="http://www.kyne.com.au/%7Emark/software/lua-cjson.php">Lua CSJON 2.0.0</a>
|
||||
</dt>
|
||||
<dd>
|
||||
<div class="ulist"><ul>
|
||||
<li>
|
||||
<p>
|
||||
C implementation with no dependencies on other libraries
|
||||
</p>
|
||||
</li>
|
||||
</ul></div>
|
||||
</dd>
|
||||
</dl></div>
|
||||
</div>
|
||||
<h2 id="_summary">2. Summary</h2>
|
||||
<div class="sectionbody">
|
||||
<div class="paragraph"><p>All modules were built and tested as follows:</p></div>
|
||||
<div class="dlist"><dl>
|
||||
<dt class="hdlist1">
|
||||
DKJSON
|
||||
</dt>
|
||||
<dd>
|
||||
<p>
|
||||
Tested with/without LPeg 10.2.
|
||||
</p>
|
||||
</dd>
|
||||
<dt class="hdlist1">
|
||||
Lua YAJL
|
||||
</dt>
|
||||
<dd>
|
||||
<p>
|
||||
Tested with YAJL 2.0.4.
|
||||
</p>
|
||||
</dd>
|
||||
<dt class="hdlist1">
|
||||
Lua CJSON
|
||||
</dt>
|
||||
<dd>
|
||||
<p>
|
||||
Tested with Libc and internal floating point conversion
|
||||
routines.
|
||||
</p>
|
||||
</dd>
|
||||
</dl></div>
|
||||
<div class="paragraph"><p>The following Lua implementations were used for this comparison:</p></div>
|
||||
<div class="ulist"><ul>
|
||||
<li>
|
||||
<p>
|
||||
<a href="http://www.lua.org">Lua 5.1.4</a> (<em>Lua</em>)
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
<a href="http://www.luajit.org">LuaJIT 2.0.0-beta9</a> (<em>JIT</em>)
|
||||
</p>
|
||||
</li>
|
||||
</ul></div>
|
||||
<div class="paragraph"><p>These results show the number of JSON operations per second sustained by
|
||||
each module. All results have been normalised against the pure Lua
|
||||
DKJSON implementation.</p></div>
|
||||
<div class="literalblock">
|
||||
<div class="title">Decoding performance</div>
|
||||
<div class="content">
|
||||
<pre><tt> | DKJSON | Lua YAJL | Lua CJSON
|
||||
| No LPeg With LPeg | | Libc Internal
|
||||
| Lua JIT Lua JIT | Lua JIT | Lua JIT Lua JIT
|
||||
example1 | 1x 2x 2.6x 3.4x | 7.1x 10x | 14x 20x 14x 20x
|
||||
example2 | 1x 2.2x 2.9x 4.4x | 6.7x 9.9x | 14x 22x 14x 22x
|
||||
example3 | 1x 2.1x 3x 4.3x | 6.9x 9.3x | 14x 21x 15x 22x
|
||||
example4 | 1x 2x 2.5x 3.7x | 7.3x 10x | 12x 19x 12x 20x
|
||||
example5 | 1x 2.2x 3x 4.5x | 7.8x 11x | 16x 24x 16x 24x
|
||||
numbers | 1x 2.2x 2.3x 4x | 4.6x 5.5x | 8.9x 10x 13x 17x
|
||||
rfc-example1 | 1x 2.1x 2.8x 4.3x | 6.1x 8.1x | 13x 19x 14x 21x
|
||||
rfc-example2 | 1x 2.1x 3.1x 4.2x | 7.1x 9.2x | 15x 21x 17x 24x
|
||||
types | 1x 2.2x 2.6x 4.3x | 5.3x 7.4x | 12x 20x 13x 21x
|
||||
-------------|-------------------------|------------|-----------------------
|
||||
= Average => | 1x 2.1x 2.7x 4.1x | 6.5x 9x | 13x 20x 14x 21x</tt></pre>
|
||||
</div></div>
|
||||
<div class="literalblock">
|
||||
<div class="title">Encoding performance</div>
|
||||
<div class="content">
|
||||
<pre><tt> | DKJSON | Lua YAJL | Lua CJSON
|
||||
| No LPeg With LPeg | | Libc Internal
|
||||
| Lua JIT Lua JIT | Lua JIT | Lua JIT Lua JIT
|
||||
example1 | 1x 1.8x 0.97x 1.6x | 3.1x 5.2x | 23x 29x 23x 29x
|
||||
example2 | 1x 2x 0.97x 1.7x | 2.6x 4.3x | 22x 28x 22x 28x
|
||||
example3 | 1x 1.9x 0.98x 1.6x | 2.8x 4.3x | 13x 15x 16x 18x
|
||||
example4 | 1x 1.7x 0.96x 1.3x | 3.9x 6.1x | 15x 19x 17x 21x
|
||||
example5 | 1x 2x 0.98x 1.7x | 2.7x 4.5x | 20x 23x 20x 23x
|
||||
numbers | 1x 2.3x 1x 2.2x | 1.3x 1.9x | 3.8x 4.1x 4.2x 4.6x
|
||||
rfc-example1 | 1x 1.9x 0.97x 1.6x | 2.2x 3.2x | 8.5x 9.3x 11x 12x
|
||||
rfc-example2 | 1x 1.9x 0.98x 1.6x | 2.6x 3.9x | 10x 11x 17x 19x
|
||||
types | 1x 2.2x 0.97x 2x | 1.2x 1.9x | 11x 13x 12x 14x
|
||||
-------------|-------------------------|------------|-----------------------
|
||||
= Average => | 1x 1.9x 0.98x 1.7x | 2.5x 3.9x | 14x 17x 16x 19x</tt></pre>
|
||||
</div></div>
|
||||
</div>
|
||||
<div id="footer">
|
||||
<div id="footer-text">
|
||||
Last updated 2012-03-01 22:52:50 CST
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,89 +0,0 @@
|
||||
JSON module performance comparison under Lua
|
||||
============================================
|
||||
Mark Pulford <mark@kyne.com.au>
|
||||
:revdate: January 22, 2012
|
||||
|
||||
This performance comparison aims to provide a guide of relative
|
||||
performance between several fast and popular JSON modules.
|
||||
|
||||
The examples used in this comparison were mostly sourced from the
|
||||
http://json.org[JSON website] and
|
||||
http://tools.ietf.org/html/rfc4627[RFC 4627].
|
||||
|
||||
Performance will vary widely between platforms and data sets. These
|
||||
results should only be used as an approximation.
|
||||
|
||||
|
||||
Modules
|
||||
-------
|
||||
|
||||
The following JSON modules for Lua were tested:
|
||||
|
||||
http://chiselapp.com/user/dhkolf/repository/dkjson/[DKJSON 2.1]::
|
||||
- Lua implementation with no dependencies on other libraries
|
||||
- Supports LPeg to improve decode performance
|
||||
|
||||
https://github.com/brimworks/lua-yajl[Lua YAJL 2.0]::
|
||||
- C wrapper for the YAJL library
|
||||
|
||||
http://www.kyne.com.au/%7Emark/software/lua-cjson.php[Lua CSJON 2.0.0]::
|
||||
- C implementation with no dependencies on other libraries
|
||||
|
||||
|
||||
Summary
|
||||
-------
|
||||
|
||||
All modules were built and tested as follows:
|
||||
|
||||
DKJSON:: Tested with/without LPeg 10.2.
|
||||
Lua YAJL:: Tested with YAJL 2.0.4.
|
||||
Lua CJSON:: Tested with Libc and internal floating point conversion
|
||||
routines.
|
||||
|
||||
The following Lua implementations were used for this comparison:
|
||||
|
||||
- http://www.lua.org[Lua 5.1.4] (_Lua_)
|
||||
- http://www.luajit.org[LuaJIT 2.0.0-beta9] (_JIT_)
|
||||
|
||||
These results show the number of JSON operations per second sustained by
|
||||
each module. All results have been normalised against the pure Lua
|
||||
DKJSON implementation.
|
||||
|
||||
.Decoding performance
|
||||
............................................................................
|
||||
| DKJSON | Lua YAJL | Lua CJSON
|
||||
| No LPeg With LPeg | | Libc Internal
|
||||
| Lua JIT Lua JIT | Lua JIT | Lua JIT Lua JIT
|
||||
example1 | 1x 2x 2.6x 3.4x | 7.1x 10x | 14x 20x 14x 20x
|
||||
example2 | 1x 2.2x 2.9x 4.4x | 6.7x 9.9x | 14x 22x 14x 22x
|
||||
example3 | 1x 2.1x 3x 4.3x | 6.9x 9.3x | 14x 21x 15x 22x
|
||||
example4 | 1x 2x 2.5x 3.7x | 7.3x 10x | 12x 19x 12x 20x
|
||||
example5 | 1x 2.2x 3x 4.5x | 7.8x 11x | 16x 24x 16x 24x
|
||||
numbers | 1x 2.2x 2.3x 4x | 4.6x 5.5x | 8.9x 10x 13x 17x
|
||||
rfc-example1 | 1x 2.1x 2.8x 4.3x | 6.1x 8.1x | 13x 19x 14x 21x
|
||||
rfc-example2 | 1x 2.1x 3.1x 4.2x | 7.1x 9.2x | 15x 21x 17x 24x
|
||||
types | 1x 2.2x 2.6x 4.3x | 5.3x 7.4x | 12x 20x 13x 21x
|
||||
-------------|-------------------------|------------|-----------------------
|
||||
= Average => | 1x 2.1x 2.7x 4.1x | 6.5x 9x | 13x 20x 14x 21x
|
||||
............................................................................
|
||||
|
||||
.Encoding performance
|
||||
.............................................................................
|
||||
| DKJSON | Lua YAJL | Lua CJSON
|
||||
| No LPeg With LPeg | | Libc Internal
|
||||
| Lua JIT Lua JIT | Lua JIT | Lua JIT Lua JIT
|
||||
example1 | 1x 1.8x 0.97x 1.6x | 3.1x 5.2x | 23x 29x 23x 29x
|
||||
example2 | 1x 2x 0.97x 1.7x | 2.6x 4.3x | 22x 28x 22x 28x
|
||||
example3 | 1x 1.9x 0.98x 1.6x | 2.8x 4.3x | 13x 15x 16x 18x
|
||||
example4 | 1x 1.7x 0.96x 1.3x | 3.9x 6.1x | 15x 19x 17x 21x
|
||||
example5 | 1x 2x 0.98x 1.7x | 2.7x 4.5x | 20x 23x 20x 23x
|
||||
numbers | 1x 2.3x 1x 2.2x | 1.3x 1.9x | 3.8x 4.1x 4.2x 4.6x
|
||||
rfc-example1 | 1x 1.9x 0.97x 1.6x | 2.2x 3.2x | 8.5x 9.3x 11x 12x
|
||||
rfc-example2 | 1x 1.9x 0.98x 1.6x | 2.6x 3.9x | 10x 11x 17x 19x
|
||||
types | 1x 2.2x 0.97x 2x | 1.2x 1.9x | 11x 13x 12x 14x
|
||||
-------------|-------------------------|------------|-----------------------
|
||||
= Average => | 1x 1.9x 0.98x 1.7x | 2.5x 3.9x | 14x 17x 16x 19x
|
||||
.............................................................................
|
||||
|
||||
|
||||
// vi:ft=asciidoc tw=72:
|
||||
@@ -1,563 +0,0 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Network Working Group D. Crockford
|
||||
Request for Comments: 4627 JSON.org
|
||||
Category: Informational July 2006
|
||||
|
||||
|
||||
The application/json Media Type for JavaScript Object Notation (JSON)
|
||||
|
||||
Status of This Memo
|
||||
|
||||
This memo provides information for the Internet community. It does
|
||||
not specify an Internet standard of any kind. Distribution of this
|
||||
memo is unlimited.
|
||||
|
||||
Copyright Notice
|
||||
|
||||
Copyright (C) The Internet Society (2006).
|
||||
|
||||
Abstract
|
||||
|
||||
JavaScript Object Notation (JSON) is a lightweight, text-based,
|
||||
language-independent data interchange format. It was derived from
|
||||
the ECMAScript Programming Language Standard. JSON defines a small
|
||||
set of formatting rules for the portable representation of structured
|
||||
data.
|
||||
|
||||
1. Introduction
|
||||
|
||||
JavaScript Object Notation (JSON) is a text format for the
|
||||
serialization of structured data. It is derived from the object
|
||||
literals of JavaScript, as defined in the ECMAScript Programming
|
||||
Language Standard, Third Edition [ECMA].
|
||||
|
||||
JSON can represent four primitive types (strings, numbers, booleans,
|
||||
and null) and two structured types (objects and arrays).
|
||||
|
||||
A string is a sequence of zero or more Unicode characters [UNICODE].
|
||||
|
||||
An object is an unordered collection of zero or more name/value
|
||||
pairs, where a name is a string and a value is a string, number,
|
||||
boolean, null, object, or array.
|
||||
|
||||
An array is an ordered sequence of zero or more values.
|
||||
|
||||
The terms "object" and "array" come from the conventions of
|
||||
JavaScript.
|
||||
|
||||
JSON's design goals were for it to be minimal, portable, textual, and
|
||||
a subset of JavaScript.
|
||||
|
||||
|
||||
|
||||
Crockford Informational [Page 1]
|
||||
|
||||
RFC 4627 JSON July 2006
|
||||
|
||||
|
||||
1.1. Conventions Used in This Document
|
||||
|
||||
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
|
||||
"SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
|
||||
document are to be interpreted as described in [RFC2119].
|
||||
|
||||
The grammatical rules in this document are to be interpreted as
|
||||
described in [RFC4234].
|
||||
|
||||
2. JSON Grammar
|
||||
|
||||
A JSON text is a sequence of tokens. The set of tokens includes six
|
||||
structural characters, strings, numbers, and three literal names.
|
||||
|
||||
A JSON text is a serialized object or array.
|
||||
|
||||
JSON-text = object / array
|
||||
|
||||
These are the six structural characters:
|
||||
|
||||
begin-array = ws %x5B ws ; [ left square bracket
|
||||
|
||||
begin-object = ws %x7B ws ; { left curly bracket
|
||||
|
||||
end-array = ws %x5D ws ; ] right square bracket
|
||||
|
||||
end-object = ws %x7D ws ; } right curly bracket
|
||||
|
||||
name-separator = ws %x3A ws ; : colon
|
||||
|
||||
value-separator = ws %x2C ws ; , comma
|
||||
|
||||
Insignificant whitespace is allowed before or after any of the six
|
||||
structural characters.
|
||||
|
||||
ws = *(
|
||||
%x20 / ; Space
|
||||
%x09 / ; Horizontal tab
|
||||
%x0A / ; Line feed or New line
|
||||
%x0D ; Carriage return
|
||||
)
|
||||
|
||||
2.1. Values
|
||||
|
||||
A JSON value MUST be an object, array, number, or string, or one of
|
||||
the following three literal names:
|
||||
|
||||
false null true
|
||||
|
||||
|
||||
|
||||
Crockford Informational [Page 2]
|
||||
|
||||
RFC 4627 JSON July 2006
|
||||
|
||||
|
||||
The literal names MUST be lowercase. No other literal names are
|
||||
allowed.
|
||||
|
||||
value = false / null / true / object / array / number / string
|
||||
|
||||
false = %x66.61.6c.73.65 ; false
|
||||
|
||||
null = %x6e.75.6c.6c ; null
|
||||
|
||||
true = %x74.72.75.65 ; true
|
||||
|
||||
2.2. Objects
|
||||
|
||||
An object structure is represented as a pair of curly brackets
|
||||
surrounding zero or more name/value pairs (or members). A name is a
|
||||
string. A single colon comes after each name, separating the name
|
||||
from the value. A single comma separates a value from a following
|
||||
name. The names within an object SHOULD be unique.
|
||||
|
||||
object = begin-object [ member *( value-separator member ) ]
|
||||
end-object
|
||||
|
||||
member = string name-separator value
|
||||
|
||||
2.3. Arrays
|
||||
|
||||
An array structure is represented as square brackets surrounding zero
|
||||
or more values (or elements). Elements are separated by commas.
|
||||
|
||||
array = begin-array [ value *( value-separator value ) ] end-array
|
||||
|
||||
2.4. Numbers
|
||||
|
||||
The representation of numbers is similar to that used in most
|
||||
programming languages. A number contains an integer component that
|
||||
may be prefixed with an optional minus sign, which may be followed by
|
||||
a fraction part and/or an exponent part.
|
||||
|
||||
Octal and hex forms are not allowed. Leading zeros are not allowed.
|
||||
|
||||
A fraction part is a decimal point followed by one or more digits.
|
||||
|
||||
An exponent part begins with the letter E in upper or lowercase,
|
||||
which may be followed by a plus or minus sign. The E and optional
|
||||
sign are followed by one or more digits.
|
||||
|
||||
Numeric values that cannot be represented as sequences of digits
|
||||
(such as Infinity and NaN) are not permitted.
|
||||
|
||||
|
||||
|
||||
Crockford Informational [Page 3]
|
||||
|
||||
RFC 4627 JSON July 2006
|
||||
|
||||
|
||||
number = [ minus ] int [ frac ] [ exp ]
|
||||
|
||||
decimal-point = %x2E ; .
|
||||
|
||||
digit1-9 = %x31-39 ; 1-9
|
||||
|
||||
e = %x65 / %x45 ; e E
|
||||
|
||||
exp = e [ minus / plus ] 1*DIGIT
|
||||
|
||||
frac = decimal-point 1*DIGIT
|
||||
|
||||
int = zero / ( digit1-9 *DIGIT )
|
||||
|
||||
minus = %x2D ; -
|
||||
|
||||
plus = %x2B ; +
|
||||
|
||||
zero = %x30 ; 0
|
||||
|
||||
2.5. Strings
|
||||
|
||||
The representation of strings is similar to conventions used in the C
|
||||
family of programming languages. A string begins and ends with
|
||||
quotation marks. All Unicode characters may be placed within the
|
||||
quotation marks except for the characters that must be escaped:
|
||||
quotation mark, reverse solidus, and the control characters (U+0000
|
||||
through U+001F).
|
||||
|
||||
Any character may be escaped. If the character is in the Basic
|
||||
Multilingual Plane (U+0000 through U+FFFF), then it may be
|
||||
represented as a six-character sequence: a reverse solidus, followed
|
||||
by the lowercase letter u, followed by four hexadecimal digits that
|
||||
encode the character's code point. The hexadecimal letters A though
|
||||
F can be upper or lowercase. So, for example, a string containing
|
||||
only a single reverse solidus character may be represented as
|
||||
"\u005C".
|
||||
|
||||
Alternatively, there are two-character sequence escape
|
||||
representations of some popular characters. So, for example, a
|
||||
string containing only a single reverse solidus character may be
|
||||
represented more compactly as "\\".
|
||||
|
||||
To escape an extended character that is not in the Basic Multilingual
|
||||
Plane, the character is represented as a twelve-character sequence,
|
||||
encoding the UTF-16 surrogate pair. So, for example, a string
|
||||
containing only the G clef character (U+1D11E) may be represented as
|
||||
"\uD834\uDD1E".
|
||||
|
||||
|
||||
|
||||
Crockford Informational [Page 4]
|
||||
|
||||
RFC 4627 JSON July 2006
|
||||
|
||||
|
||||
string = quotation-mark *char quotation-mark
|
||||
|
||||
char = unescaped /
|
||||
escape (
|
||||
%x22 / ; " quotation mark U+0022
|
||||
%x5C / ; \ reverse solidus U+005C
|
||||
%x2F / ; / solidus U+002F
|
||||
%x62 / ; b backspace U+0008
|
||||
%x66 / ; f form feed U+000C
|
||||
%x6E / ; n line feed U+000A
|
||||
%x72 / ; r carriage return U+000D
|
||||
%x74 / ; t tab U+0009
|
||||
%x75 4HEXDIG ) ; uXXXX U+XXXX
|
||||
|
||||
escape = %x5C ; \
|
||||
|
||||
quotation-mark = %x22 ; "
|
||||
|
||||
unescaped = %x20-21 / %x23-5B / %x5D-10FFFF
|
||||
|
||||
3. Encoding
|
||||
|
||||
JSON text SHALL be encoded in Unicode. The default encoding is
|
||||
UTF-8.
|
||||
|
||||
Since the first two characters of a JSON text will always be ASCII
|
||||
characters [RFC0020], it is possible to determine whether an octet
|
||||
stream is UTF-8, UTF-16 (BE or LE), or UTF-32 (BE or LE) by looking
|
||||
at the pattern of nulls in the first four octets.
|
||||
|
||||
00 00 00 xx UTF-32BE
|
||||
00 xx 00 xx UTF-16BE
|
||||
xx 00 00 00 UTF-32LE
|
||||
xx 00 xx 00 UTF-16LE
|
||||
xx xx xx xx UTF-8
|
||||
|
||||
4. Parsers
|
||||
|
||||
A JSON parser transforms a JSON text into another representation. A
|
||||
JSON parser MUST accept all texts that conform to the JSON grammar.
|
||||
A JSON parser MAY accept non-JSON forms or extensions.
|
||||
|
||||
An implementation may set limits on the size of texts that it
|
||||
accepts. An implementation may set limits on the maximum depth of
|
||||
nesting. An implementation may set limits on the range of numbers.
|
||||
An implementation may set limits on the length and character contents
|
||||
of strings.
|
||||
|
||||
|
||||
|
||||
|
||||
Crockford Informational [Page 5]
|
||||
|
||||
RFC 4627 JSON July 2006
|
||||
|
||||
|
||||
5. Generators
|
||||
|
||||
A JSON generator produces JSON text. The resulting text MUST
|
||||
strictly conform to the JSON grammar.
|
||||
|
||||
6. IANA Considerations
|
||||
|
||||
The MIME media type for JSON text is application/json.
|
||||
|
||||
Type name: application
|
||||
|
||||
Subtype name: json
|
||||
|
||||
Required parameters: n/a
|
||||
|
||||
Optional parameters: n/a
|
||||
|
||||
Encoding considerations: 8bit if UTF-8; binary if UTF-16 or UTF-32
|
||||
|
||||
JSON may be represented using UTF-8, UTF-16, or UTF-32. When JSON
|
||||
is written in UTF-8, JSON is 8bit compatible. When JSON is
|
||||
written in UTF-16 or UTF-32, the binary content-transfer-encoding
|
||||
must be used.
|
||||
|
||||
Security considerations:
|
||||
|
||||
Generally there are security issues with scripting languages. JSON
|
||||
is a subset of JavaScript, but it is a safe subset that excludes
|
||||
assignment and invocation.
|
||||
|
||||
A JSON text can be safely passed into JavaScript's eval() function
|
||||
(which compiles and executes a string) if all the characters not
|
||||
enclosed in strings are in the set of characters that form JSON
|
||||
tokens. This can be quickly determined in JavaScript with two
|
||||
regular expressions and calls to the test and replace methods.
|
||||
|
||||
var my_JSON_object = !(/[^,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]/.test(
|
||||
text.replace(/"(\\.|[^"\\])*"/g, ''))) &&
|
||||
eval('(' + text + ')');
|
||||
|
||||
Interoperability considerations: n/a
|
||||
|
||||
Published specification: RFC 4627
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Crockford Informational [Page 6]
|
||||
|
||||
RFC 4627 JSON July 2006
|
||||
|
||||
|
||||
Applications that use this media type:
|
||||
|
||||
JSON has been used to exchange data between applications written
|
||||
in all of these programming languages: ActionScript, C, C#,
|
||||
ColdFusion, Common Lisp, E, Erlang, Java, JavaScript, Lua,
|
||||
Objective CAML, Perl, PHP, Python, Rebol, Ruby, and Scheme.
|
||||
|
||||
Additional information:
|
||||
|
||||
Magic number(s): n/a
|
||||
File extension(s): .json
|
||||
Macintosh file type code(s): TEXT
|
||||
|
||||
Person & email address to contact for further information:
|
||||
Douglas Crockford
|
||||
douglas@crockford.com
|
||||
|
||||
Intended usage: COMMON
|
||||
|
||||
Restrictions on usage: none
|
||||
|
||||
Author:
|
||||
Douglas Crockford
|
||||
douglas@crockford.com
|
||||
|
||||
Change controller:
|
||||
Douglas Crockford
|
||||
douglas@crockford.com
|
||||
|
||||
7. Security Considerations
|
||||
|
||||
See Security Considerations in Section 6.
|
||||
|
||||
8. Examples
|
||||
|
||||
This is a JSON object:
|
||||
|
||||
{
|
||||
"Image": {
|
||||
"Width": 800,
|
||||
"Height": 600,
|
||||
"Title": "View from 15th Floor",
|
||||
"Thumbnail": {
|
||||
"Url": "http://www.example.com/image/481989943",
|
||||
"Height": 125,
|
||||
"Width": "100"
|
||||
},
|
||||
"IDs": [116, 943, 234, 38793]
|
||||
|
||||
|
||||
|
||||
Crockford Informational [Page 7]
|
||||
|
||||
RFC 4627 JSON July 2006
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Its Image member is an object whose Thumbnail member is an object
|
||||
and whose IDs member is an array of numbers.
|
||||
|
||||
This is a JSON array containing two objects:
|
||||
|
||||
[
|
||||
{
|
||||
"precision": "zip",
|
||||
"Latitude": 37.7668,
|
||||
"Longitude": -122.3959,
|
||||
"Address": "",
|
||||
"City": "SAN FRANCISCO",
|
||||
"State": "CA",
|
||||
"Zip": "94107",
|
||||
"Country": "US"
|
||||
},
|
||||
{
|
||||
"precision": "zip",
|
||||
"Latitude": 37.371991,
|
||||
"Longitude": -122.026020,
|
||||
"Address": "",
|
||||
"City": "SUNNYVALE",
|
||||
"State": "CA",
|
||||
"Zip": "94085",
|
||||
"Country": "US"
|
||||
}
|
||||
]
|
||||
|
||||
9. References
|
||||
|
||||
9.1. Normative References
|
||||
|
||||
[ECMA] European Computer Manufacturers Association, "ECMAScript
|
||||
Language Specification 3rd Edition", December 1999,
|
||||
<http://www.ecma-international.org/publications/files/
|
||||
ecma-st/ECMA-262.pdf>.
|
||||
|
||||
[RFC0020] Cerf, V., "ASCII format for network interchange", RFC 20,
|
||||
October 1969.
|
||||
|
||||
[RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
|
||||
Requirement Levels", BCP 14, RFC 2119, March 1997.
|
||||
|
||||
[RFC4234] Crocker, D. and P. Overell, "Augmented BNF for Syntax
|
||||
Specifications: ABNF", RFC 4234, October 2005.
|
||||
|
||||
|
||||
|
||||
Crockford Informational [Page 8]
|
||||
|
||||
RFC 4627 JSON July 2006
|
||||
|
||||
|
||||
[UNICODE] The Unicode Consortium, "The Unicode Standard Version 4.0",
|
||||
2003, <http://www.unicode.org/versions/Unicode4.1.0/>.
|
||||
|
||||
Author's Address
|
||||
|
||||
Douglas Crockford
|
||||
JSON.org
|
||||
EMail: douglas@crockford.com
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Crockford Informational [Page 9]
|
||||
|
||||
RFC 4627 JSON July 2006
|
||||
|
||||
|
||||
Full Copyright Statement
|
||||
|
||||
Copyright (C) The Internet Society (2006).
|
||||
|
||||
This document is subject to the rights, licenses and restrictions
|
||||
contained in BCP 78, and except as set forth therein, the authors
|
||||
retain all their rights.
|
||||
|
||||
This document and the information contained herein are provided on an
|
||||
"AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS
|
||||
OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET
|
||||
ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED,
|
||||
INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE
|
||||
INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
Intellectual Property
|
||||
|
||||
The IETF takes no position regarding the validity or scope of any
|
||||
Intellectual Property Rights or other rights that might be claimed to
|
||||
pertain to the implementation or use of the technology described in
|
||||
this document or the extent to which any license under such rights
|
||||
might or might not be available; nor does it represent that it has
|
||||
made any independent effort to identify any such rights. Information
|
||||
on the procedures with respect to rights in RFC documents can be
|
||||
found in BCP 78 and BCP 79.
|
||||
|
||||
Copies of IPR disclosures made to the IETF Secretariat and any
|
||||
assurances of licenses to be made available, or the result of an
|
||||
attempt made to obtain a general license or permission for the use of
|
||||
such proprietary rights by implementers or users of this
|
||||
specification can be obtained from the IETF on-line IPR repository at
|
||||
http://www.ietf.org/ipr.
|
||||
|
||||
The IETF invites any interested party to bring to its attention any
|
||||
copyrights, patents or patent applications, or other proprietary
|
||||
rights that may cover technology that may be required to implement
|
||||
this standard. Please address the information to the IETF at
|
||||
ietf-ipr@ietf.org.
|
||||
|
||||
Acknowledgement
|
||||
|
||||
Funding for the RFC Editor function is provided by the IETF
|
||||
Administrative Support Activity (IASA).
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Crockford Informational [Page 10]
|
||||
|
||||
@@ -1,92 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
PLATFORM="`uname -s`"
|
||||
[ "$1" ] && VERSION="$1" || VERSION="2.1.0"
|
||||
|
||||
set -e
|
||||
|
||||
# Portable "ggrep -A" replacement.
|
||||
# Work around Solaris awk record limit of 2559 bytes.
|
||||
# contextgrep PATTERN POST_MATCH_LINES
|
||||
contextgrep() {
|
||||
cut -c -2500 | awk "/$1/ { count = ($2 + 1) } count > 0 { count--; print }"
|
||||
}
|
||||
|
||||
do_tests() {
|
||||
echo
|
||||
cd tests
|
||||
lua -e 'print("Testing Lua CJSON version " .. require("cjson")._VERSION)'
|
||||
./test.lua | contextgrep 'FAIL|Summary' 3 | grep -v PASS | cut -c -150
|
||||
cd ..
|
||||
}
|
||||
|
||||
echo "===== Setting LuaRocks PATH ====="
|
||||
eval "`luarocks path`"
|
||||
|
||||
echo "===== Building UTF-8 test data ====="
|
||||
( cd tests && ./genutf8.pl; )
|
||||
|
||||
echo "===== Cleaning old build data ====="
|
||||
make clean
|
||||
rm -f tests/cjson.so
|
||||
|
||||
echo "===== Verifying cjson.so is not installed ====="
|
||||
|
||||
cd tests
|
||||
if lua -e 'require "cjson"' 2>/dev/null
|
||||
then
|
||||
cat <<EOT
|
||||
Please ensure you do not have the Lua CJSON module installed before
|
||||
running these tests.
|
||||
EOT
|
||||
exit
|
||||
fi
|
||||
cd ..
|
||||
|
||||
echo "===== Testing LuaRocks build ====="
|
||||
luarocks make --local
|
||||
do_tests
|
||||
luarocks remove --local lua-cjson
|
||||
make clean
|
||||
|
||||
echo "===== Testing Makefile build ====="
|
||||
make
|
||||
cp -r lua/cjson cjson.so tests
|
||||
do_tests
|
||||
make clean
|
||||
rm -rf tests/cjson{,.so}
|
||||
|
||||
echo "===== Testing Cmake build ====="
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
make
|
||||
cd ..
|
||||
cp -r lua/cjson build/cjson.so tests
|
||||
do_tests
|
||||
rm -rf build tests/cjson{,.so}
|
||||
|
||||
if [ "$PLATFORM" = "Linux" ]
|
||||
then
|
||||
echo "===== Testing RPM build ====="
|
||||
SRCTGZ=""
|
||||
TGZ=lua-cjson-$VERSION.tar.gz
|
||||
for D in .. packages .
|
||||
do
|
||||
[ -r "$D/$TGZ" ] && SRCTGZ="$D/$TGZ"
|
||||
done
|
||||
if [ "$SRCTGZ" ]
|
||||
then
|
||||
LOG=/tmp/build.$$
|
||||
rpmbuild -tb "$SRCTGZ" > "$LOG"
|
||||
RPM="`awk '/^Wrote: / && ! /debuginfo/ { print $2}' < "$LOG"`"
|
||||
sudo -- rpm -Uvh \"$RPM\"
|
||||
do_tests
|
||||
sudo -- rpm -e lua-cjson
|
||||
rm -f "$LOG"
|
||||
else
|
||||
echo "==> skipping, $TGZ not found"
|
||||
fi
|
||||
fi
|
||||
|
||||
# vi:ai et sw=4 ts=4:
|
||||
@@ -1,251 +0,0 @@
|
||||
/* strbuf - String buffer routines
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "strbuf.h"
|
||||
|
||||
static void die(const char *fmt, ...)
|
||||
{
|
||||
va_list arg;
|
||||
|
||||
va_start(arg, fmt);
|
||||
vfprintf(stderr, fmt, arg);
|
||||
va_end(arg);
|
||||
fprintf(stderr, "\n");
|
||||
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
void strbuf_init(strbuf_t *s, int len)
|
||||
{
|
||||
int size;
|
||||
|
||||
if (len <= 0)
|
||||
size = STRBUF_DEFAULT_SIZE;
|
||||
else
|
||||
size = len + 1; /* \0 terminator */
|
||||
|
||||
s->buf = NULL;
|
||||
s->size = size;
|
||||
s->length = 0;
|
||||
s->increment = STRBUF_DEFAULT_INCREMENT;
|
||||
s->dynamic = 0;
|
||||
s->reallocs = 0;
|
||||
s->debug = 0;
|
||||
|
||||
s->buf = malloc(size);
|
||||
if (!s->buf)
|
||||
die("Out of memory");
|
||||
|
||||
strbuf_ensure_null(s);
|
||||
}
|
||||
|
||||
strbuf_t *strbuf_new(int len)
|
||||
{
|
||||
strbuf_t *s;
|
||||
|
||||
s = malloc(sizeof(strbuf_t));
|
||||
if (!s)
|
||||
die("Out of memory");
|
||||
|
||||
strbuf_init(s, len);
|
||||
|
||||
/* Dynamic strbuf allocation / deallocation */
|
||||
s->dynamic = 1;
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
void strbuf_set_increment(strbuf_t *s, int increment)
|
||||
{
|
||||
/* Increment > 0: Linear buffer growth rate
|
||||
* Increment < -1: Exponential buffer growth rate */
|
||||
if (increment == 0 || increment == -1)
|
||||
die("BUG: Invalid string increment");
|
||||
|
||||
s->increment = increment;
|
||||
}
|
||||
|
||||
static inline void debug_stats(strbuf_t *s)
|
||||
{
|
||||
if (s->debug) {
|
||||
fprintf(stderr, "strbuf(%lx) reallocs: %d, length: %d, size: %d\n",
|
||||
(long)s, s->reallocs, s->length, s->size);
|
||||
}
|
||||
}
|
||||
|
||||
/* If strbuf_t has not been dynamically allocated, strbuf_free() can
|
||||
* be called any number of times strbuf_init() */
|
||||
void strbuf_free(strbuf_t *s)
|
||||
{
|
||||
debug_stats(s);
|
||||
|
||||
if (s->buf) {
|
||||
free(s->buf);
|
||||
s->buf = NULL;
|
||||
}
|
||||
if (s->dynamic)
|
||||
free(s);
|
||||
}
|
||||
|
||||
char *strbuf_free_to_string(strbuf_t *s, int *len)
|
||||
{
|
||||
char *buf;
|
||||
|
||||
debug_stats(s);
|
||||
|
||||
strbuf_ensure_null(s);
|
||||
|
||||
buf = s->buf;
|
||||
if (len)
|
||||
*len = s->length;
|
||||
|
||||
if (s->dynamic)
|
||||
free(s);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
static int calculate_new_size(strbuf_t *s, int len)
|
||||
{
|
||||
int reqsize, newsize;
|
||||
|
||||
if (len <= 0)
|
||||
die("BUG: Invalid strbuf length requested");
|
||||
|
||||
/* Ensure there is room for optional NULL termination */
|
||||
reqsize = len + 1;
|
||||
|
||||
/* If the user has requested to shrink the buffer, do it exactly */
|
||||
if (s->size > reqsize)
|
||||
return reqsize;
|
||||
|
||||
newsize = s->size;
|
||||
if (s->increment < 0) {
|
||||
/* Exponential sizing */
|
||||
while (newsize < reqsize)
|
||||
newsize *= -s->increment;
|
||||
} else {
|
||||
/* Linear sizing */
|
||||
newsize = ((newsize + s->increment - 1) / s->increment) * s->increment;
|
||||
}
|
||||
|
||||
return newsize;
|
||||
}
|
||||
|
||||
|
||||
/* Ensure strbuf can handle a string length bytes long (ignoring NULL
|
||||
* optional termination). */
|
||||
void strbuf_resize(strbuf_t *s, int len)
|
||||
{
|
||||
int newsize;
|
||||
|
||||
newsize = calculate_new_size(s, len);
|
||||
|
||||
if (s->debug > 1) {
|
||||
fprintf(stderr, "strbuf(%lx) resize: %d => %d\n",
|
||||
(long)s, s->size, newsize);
|
||||
}
|
||||
|
||||
s->size = newsize;
|
||||
s->buf = realloc(s->buf, s->size);
|
||||
if (!s->buf)
|
||||
die("Out of memory");
|
||||
s->reallocs++;
|
||||
}
|
||||
|
||||
void strbuf_append_string(strbuf_t *s, const char *str)
|
||||
{
|
||||
int space, i;
|
||||
|
||||
space = strbuf_empty_length(s);
|
||||
|
||||
for (i = 0; str[i]; i++) {
|
||||
if (space < 1) {
|
||||
strbuf_resize(s, s->length + 1);
|
||||
space = strbuf_empty_length(s);
|
||||
}
|
||||
|
||||
s->buf[s->length] = str[i];
|
||||
s->length++;
|
||||
space--;
|
||||
}
|
||||
}
|
||||
|
||||
/* strbuf_append_fmt() should only be used when an upper bound
|
||||
* is known for the output string. */
|
||||
void strbuf_append_fmt(strbuf_t *s, int len, const char *fmt, ...)
|
||||
{
|
||||
va_list arg;
|
||||
int fmt_len;
|
||||
|
||||
strbuf_ensure_empty_length(s, len);
|
||||
|
||||
va_start(arg, fmt);
|
||||
fmt_len = vsnprintf(s->buf + s->length, len, fmt, arg);
|
||||
va_end(arg);
|
||||
|
||||
if (fmt_len < 0)
|
||||
die("BUG: Unable to convert number"); /* This should never happen.. */
|
||||
|
||||
s->length += fmt_len;
|
||||
}
|
||||
|
||||
/* strbuf_append_fmt_retry() can be used when the there is no known
|
||||
* upper bound for the output string. */
|
||||
void strbuf_append_fmt_retry(strbuf_t *s, const char *fmt, ...)
|
||||
{
|
||||
va_list arg;
|
||||
int fmt_len, try;
|
||||
int empty_len;
|
||||
|
||||
/* If the first attempt to append fails, resize the buffer appropriately
|
||||
* and try again */
|
||||
for (try = 0; ; try++) {
|
||||
va_start(arg, fmt);
|
||||
/* Append the new formatted string */
|
||||
/* fmt_len is the length of the string required, excluding the
|
||||
* trailing NULL */
|
||||
empty_len = strbuf_empty_length(s);
|
||||
/* Add 1 since there is also space to store the terminating NULL. */
|
||||
fmt_len = vsnprintf(s->buf + s->length, empty_len + 1, fmt, arg);
|
||||
va_end(arg);
|
||||
|
||||
if (fmt_len <= empty_len)
|
||||
break; /* SUCCESS */
|
||||
if (try > 0)
|
||||
die("BUG: length of formatted string changed");
|
||||
|
||||
strbuf_resize(s, s->length + fmt_len);
|
||||
}
|
||||
|
||||
s->length += fmt_len;
|
||||
}
|
||||
|
||||
/* vi:ai et sw=4 ts=4:
|
||||
*/
|
||||
@@ -1,154 +0,0 @@
|
||||
/* strbuf - String buffer routines
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
/* Size: Total bytes allocated to *buf
|
||||
* Length: String length, excluding optional NULL terminator.
|
||||
* Increment: Allocation increments when resizing the string buffer.
|
||||
* Dynamic: True if created via strbuf_new()
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
char *buf;
|
||||
int size;
|
||||
int length;
|
||||
int increment;
|
||||
int dynamic;
|
||||
int reallocs;
|
||||
int debug;
|
||||
} strbuf_t;
|
||||
|
||||
#ifndef STRBUF_DEFAULT_SIZE
|
||||
#define STRBUF_DEFAULT_SIZE 1023
|
||||
#endif
|
||||
#ifndef STRBUF_DEFAULT_INCREMENT
|
||||
#define STRBUF_DEFAULT_INCREMENT -2
|
||||
#endif
|
||||
|
||||
/* Initialise */
|
||||
extern strbuf_t *strbuf_new(int len);
|
||||
extern void strbuf_init(strbuf_t *s, int len);
|
||||
extern void strbuf_set_increment(strbuf_t *s, int increment);
|
||||
|
||||
/* Release */
|
||||
extern void strbuf_free(strbuf_t *s);
|
||||
extern char *strbuf_free_to_string(strbuf_t *s, int *len);
|
||||
|
||||
/* Management */
|
||||
extern void strbuf_resize(strbuf_t *s, int len);
|
||||
static int strbuf_empty_length(strbuf_t *s);
|
||||
static int strbuf_length(strbuf_t *s);
|
||||
static char *strbuf_string(strbuf_t *s, int *len);
|
||||
static void strbuf_ensure_empty_length(strbuf_t *s, int len);
|
||||
static char *strbuf_empty_ptr(strbuf_t *s);
|
||||
static void strbuf_extend_length(strbuf_t *s, int len);
|
||||
|
||||
/* Update */
|
||||
extern void strbuf_append_fmt(strbuf_t *s, int len, const char *fmt, ...);
|
||||
extern void strbuf_append_fmt_retry(strbuf_t *s, const char *format, ...);
|
||||
static void strbuf_append_mem(strbuf_t *s, const char *c, int len);
|
||||
extern void strbuf_append_string(strbuf_t *s, const char *str);
|
||||
static void strbuf_append_char(strbuf_t *s, const char c);
|
||||
static void strbuf_ensure_null(strbuf_t *s);
|
||||
|
||||
/* Reset string for before use */
|
||||
static inline void strbuf_reset(strbuf_t *s)
|
||||
{
|
||||
s->length = 0;
|
||||
}
|
||||
|
||||
static inline int strbuf_allocated(strbuf_t *s)
|
||||
{
|
||||
return s->buf != NULL;
|
||||
}
|
||||
|
||||
/* Return bytes remaining in the string buffer
|
||||
* Ensure there is space for a NULL terminator. */
|
||||
static inline int strbuf_empty_length(strbuf_t *s)
|
||||
{
|
||||
return s->size - s->length - 1;
|
||||
}
|
||||
|
||||
static inline void strbuf_ensure_empty_length(strbuf_t *s, int len)
|
||||
{
|
||||
if (len > strbuf_empty_length(s))
|
||||
strbuf_resize(s, s->length + len);
|
||||
}
|
||||
|
||||
static inline char *strbuf_empty_ptr(strbuf_t *s)
|
||||
{
|
||||
return s->buf + s->length;
|
||||
}
|
||||
|
||||
static inline void strbuf_extend_length(strbuf_t *s, int len)
|
||||
{
|
||||
s->length += len;
|
||||
}
|
||||
|
||||
static inline int strbuf_length(strbuf_t *s)
|
||||
{
|
||||
return s->length;
|
||||
}
|
||||
|
||||
static inline void strbuf_append_char(strbuf_t *s, const char c)
|
||||
{
|
||||
strbuf_ensure_empty_length(s, 1);
|
||||
s->buf[s->length++] = c;
|
||||
}
|
||||
|
||||
static inline void strbuf_append_char_unsafe(strbuf_t *s, const char c)
|
||||
{
|
||||
s->buf[s->length++] = c;
|
||||
}
|
||||
|
||||
static inline void strbuf_append_mem(strbuf_t *s, const char *c, int len)
|
||||
{
|
||||
strbuf_ensure_empty_length(s, len);
|
||||
memcpy(s->buf + s->length, c, len);
|
||||
s->length += len;
|
||||
}
|
||||
|
||||
static inline void strbuf_append_mem_unsafe(strbuf_t *s, const char *c, int len)
|
||||
{
|
||||
memcpy(s->buf + s->length, c, len);
|
||||
s->length += len;
|
||||
}
|
||||
|
||||
static inline void strbuf_ensure_null(strbuf_t *s)
|
||||
{
|
||||
s->buf[s->length] = 0;
|
||||
}
|
||||
|
||||
static inline char *strbuf_string(strbuf_t *s, int *len)
|
||||
{
|
||||
if (len)
|
||||
*len = s->length;
|
||||
|
||||
return s->buf;
|
||||
}
|
||||
|
||||
/* vi:ai et sw=4 ts=4:
|
||||
*/
|
||||
@@ -1,4 +0,0 @@
|
||||
These JSON examples were taken from the JSON website
|
||||
(http://json.org/example.html) and RFC 4627.
|
||||
|
||||
Used with permission.
|
||||
@@ -1,131 +0,0 @@
|
||||
#!/usr/bin/env lua
|
||||
|
||||
-- This benchmark script measures wall clock time and should be
|
||||
-- run on an unloaded system.
|
||||
--
|
||||
-- Your Mileage May Vary.
|
||||
--
|
||||
-- Mark Pulford <mark@kyne.com.au>
|
||||
|
||||
local json_module = os.getenv("JSON_MODULE") or "cjson"
|
||||
|
||||
require "socket"
|
||||
local json = require(json_module)
|
||||
local util = require "cjson.util"
|
||||
|
||||
local function find_func(mod, funcnames)
|
||||
for _, v in ipairs(funcnames) do
|
||||
if mod[v] then
|
||||
return mod[v]
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
local json_encode = find_func(json, { "encode", "Encode", "to_string", "stringify", "json" })
|
||||
local json_decode = find_func(json, { "decode", "Decode", "to_value", "parse" })
|
||||
|
||||
local function average(t)
|
||||
local total = 0
|
||||
for _, v in ipairs(t) do
|
||||
total = total + v
|
||||
end
|
||||
return total / #t
|
||||
end
|
||||
|
||||
function benchmark(tests, seconds, rep)
|
||||
local function bench(func, iter)
|
||||
-- Use socket.gettime() to measure microsecond resolution
|
||||
-- wall clock time.
|
||||
local t = socket.gettime()
|
||||
for i = 1, iter do
|
||||
func(i)
|
||||
end
|
||||
t = socket.gettime() - t
|
||||
|
||||
-- Don't trust any results when the run lasted for less than a
|
||||
-- millisecond - return nil.
|
||||
if t < 0.001 then
|
||||
return nil
|
||||
end
|
||||
|
||||
return (iter / t)
|
||||
end
|
||||
|
||||
-- Roughly calculate the number of interations required
|
||||
-- to obtain a particular time period.
|
||||
local function calc_iter(func, seconds)
|
||||
local iter = 1
|
||||
local rate
|
||||
-- Warm up the bench function first.
|
||||
func()
|
||||
while not rate do
|
||||
rate = bench(func, iter)
|
||||
iter = iter * 10
|
||||
end
|
||||
return math.ceil(seconds * rate)
|
||||
end
|
||||
|
||||
local test_results = {}
|
||||
for name, func in pairs(tests) do
|
||||
-- k(number), v(string)
|
||||
-- k(string), v(function)
|
||||
-- k(number), v(function)
|
||||
if type(func) == "string" then
|
||||
name = func
|
||||
func = _G[name]
|
||||
end
|
||||
|
||||
local iter = calc_iter(func, seconds)
|
||||
|
||||
local result = {}
|
||||
for i = 1, rep do
|
||||
result[i] = bench(func, iter)
|
||||
end
|
||||
|
||||
-- Remove the slowest half (round down) of the result set
|
||||
table.sort(result)
|
||||
for i = 1, math.floor(#result / 2) do
|
||||
table.remove(result, 1)
|
||||
end
|
||||
|
||||
test_results[name] = average(result)
|
||||
end
|
||||
|
||||
return test_results
|
||||
end
|
||||
|
||||
function bench_file(filename)
|
||||
local data_json = util.file_load(filename)
|
||||
local data_obj = json_decode(data_json)
|
||||
|
||||
local function test_encode()
|
||||
json_encode(data_obj)
|
||||
end
|
||||
local function test_decode()
|
||||
json_decode(data_json)
|
||||
end
|
||||
|
||||
local tests = {}
|
||||
if json_encode then tests.encode = test_encode end
|
||||
if json_decode then tests.decode = test_decode end
|
||||
|
||||
return benchmark(tests, 0.1, 5)
|
||||
end
|
||||
|
||||
-- Optionally load any custom configuration required for this module
|
||||
local success, data = pcall(util.file_load, ("bench-%s.lua"):format(json_module))
|
||||
if success then
|
||||
util.run_script(data, _G)
|
||||
configure(json)
|
||||
end
|
||||
|
||||
for i = 1, #arg do
|
||||
local results = bench_file(arg[i])
|
||||
for k, v in pairs(results) do
|
||||
print(("%s\t%s\t%d"):format(arg[i], k, v))
|
||||
end
|
||||
end
|
||||
|
||||
-- vi:ai et sw=4 ts=4:
|
||||
@@ -1,22 +0,0 @@
|
||||
{
|
||||
"glossary": {
|
||||
"title": "example glossary",
|
||||
"GlossDiv": {
|
||||
"title": "S",
|
||||
"GlossList": {
|
||||
"GlossEntry": {
|
||||
"ID": "SGML",
|
||||
"SortAs": "SGML",
|
||||
"GlossTerm": "Standard Generalized Mark up Language",
|
||||
"Acronym": "SGML",
|
||||
"Abbrev": "ISO 8879:1986",
|
||||
"GlossDef": {
|
||||
"para": "A meta-markup language, used to create markup languages such as DocBook.",
|
||||
"GlossSeeAlso": ["GML", "XML"]
|
||||
},
|
||||
"GlossSee": "markup"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
{"menu": {
|
||||
"id": "file",
|
||||
"value": "File",
|
||||
"popup": {
|
||||
"menuitem": [
|
||||
{"value": "New", "onclick": "CreateNewDoc()"},
|
||||
{"value": "Open", "onclick": "OpenDoc()"},
|
||||
{"value": "Close", "onclick": "CloseDoc()"}
|
||||
]
|
||||
}
|
||||
}}
|
||||
@@ -1,26 +0,0 @@
|
||||
{"widget": {
|
||||
"debug": "on",
|
||||
"window": {
|
||||
"title": "Sample Konfabulator Widget",
|
||||
"name": "main_window",
|
||||
"width": 500,
|
||||
"height": 500
|
||||
},
|
||||
"image": {
|
||||
"src": "Images/Sun.png",
|
||||
"name": "sun1",
|
||||
"hOffset": 250,
|
||||
"vOffset": 250,
|
||||
"alignment": "center"
|
||||
},
|
||||
"text": {
|
||||
"data": "Click Here",
|
||||
"size": 36,
|
||||
"style": "bold",
|
||||
"name": "text1",
|
||||
"hOffset": 250,
|
||||
"vOffset": 100,
|
||||
"alignment": "center",
|
||||
"onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;"
|
||||
}
|
||||
}}
|
||||
@@ -1,88 +0,0 @@
|
||||
{"web-app": {
|
||||
"servlet": [
|
||||
{
|
||||
"servlet-name": "cofaxCDS",
|
||||
"servlet-class": "org.cofax.cds.CDSServlet",
|
||||
"init-param": {
|
||||
"configGlossary:installationAt": "Philadelphia, PA",
|
||||
"configGlossary:adminEmail": "ksm@pobox.com",
|
||||
"configGlossary:poweredBy": "Cofax",
|
||||
"configGlossary:poweredByIcon": "/images/cofax.gif",
|
||||
"configGlossary:staticPath": "/content/static",
|
||||
"templateProcessorClass": "org.cofax.WysiwygTemplate",
|
||||
"templateLoaderClass": "org.cofax.FilesTemplateLoader",
|
||||
"templatePath": "templates",
|
||||
"templateOverridePath": "",
|
||||
"defaultListTemplate": "listTemplate.htm",
|
||||
"defaultFileTemplate": "articleTemplate.htm",
|
||||
"useJSP": false,
|
||||
"jspListTemplate": "listTemplate.jsp",
|
||||
"jspFileTemplate": "articleTemplate.jsp",
|
||||
"cachePackageTagsTrack": 200,
|
||||
"cachePackageTagsStore": 200,
|
||||
"cachePackageTagsRefresh": 60,
|
||||
"cacheTemplatesTrack": 100,
|
||||
"cacheTemplatesStore": 50,
|
||||
"cacheTemplatesRefresh": 15,
|
||||
"cachePagesTrack": 200,
|
||||
"cachePagesStore": 100,
|
||||
"cachePagesRefresh": 10,
|
||||
"cachePagesDirtyRead": 10,
|
||||
"searchEngineListTemplate": "forSearchEnginesList.htm",
|
||||
"searchEngineFileTemplate": "forSearchEngines.htm",
|
||||
"searchEngineRobotsDb": "WEB-INF/robots.db",
|
||||
"useDataStore": true,
|
||||
"dataStoreClass": "org.cofax.SqlDataStore",
|
||||
"redirectionClass": "org.cofax.SqlRedirection",
|
||||
"dataStoreName": "cofax",
|
||||
"dataStoreDriver": "com.microsoft.jdbc.sqlserver.SQLServerDriver",
|
||||
"dataStoreUrl": "jdbc:microsoft:sqlserver://LOCALHOST:1433;DatabaseName=goon",
|
||||
"dataStoreUser": "sa",
|
||||
"dataStorePassword": "dataStoreTestQuery",
|
||||
"dataStoreTestQuery": "SET NOCOUNT ON;select test='test';",
|
||||
"dataStoreLogFile": "/usr/local/tomcat/logs/datastore.log",
|
||||
"dataStoreInitConns": 10,
|
||||
"dataStoreMaxConns": 100,
|
||||
"dataStoreConnUsageLimit": 100,
|
||||
"dataStoreLogLevel": "debug",
|
||||
"maxUrlLength": 500}},
|
||||
{
|
||||
"servlet-name": "cofaxEmail",
|
||||
"servlet-class": "org.cofax.cds.EmailServlet",
|
||||
"init-param": {
|
||||
"mailHost": "mail1",
|
||||
"mailHostOverride": "mail2"}},
|
||||
{
|
||||
"servlet-name": "cofaxAdmin",
|
||||
"servlet-class": "org.cofax.cds.AdminServlet"},
|
||||
|
||||
{
|
||||
"servlet-name": "fileServlet",
|
||||
"servlet-class": "org.cofax.cds.FileServlet"},
|
||||
{
|
||||
"servlet-name": "cofaxTools",
|
||||
"servlet-class": "org.cofax.cms.CofaxToolsServlet",
|
||||
"init-param": {
|
||||
"templatePath": "toolstemplates/",
|
||||
"log": 1,
|
||||
"logLocation": "/usr/local/tomcat/logs/CofaxTools.log",
|
||||
"logMaxSize": "",
|
||||
"dataLog": 1,
|
||||
"dataLogLocation": "/usr/local/tomcat/logs/dataLog.log",
|
||||
"dataLogMaxSize": "",
|
||||
"removePageCache": "/content/admin/remove?cache=pages&id=",
|
||||
"removeTemplateCache": "/content/admin/remove?cache=templates&id=",
|
||||
"fileTransferFolder": "/usr/local/tomcat/webapps/content/fileTransferFolder",
|
||||
"lookInContext": 1,
|
||||
"adminGroupID": 4,
|
||||
"betaServer": true}}],
|
||||
"servlet-mapping": {
|
||||
"cofaxCDS": "/",
|
||||
"cofaxEmail": "/cofaxutil/aemail/*",
|
||||
"cofaxAdmin": "/admin/*",
|
||||
"fileServlet": "/static/*",
|
||||
"cofaxTools": "/tools/*"},
|
||||
|
||||
"taglib": {
|
||||
"taglib-uri": "cofax.tld",
|
||||
"taglib-location": "/WEB-INF/tlds/cofax.tld"}}}
|
||||
@@ -1,27 +0,0 @@
|
||||
{"menu": {
|
||||
"header": "SVG Viewer",
|
||||
"items": [
|
||||
{"id": "Open"},
|
||||
{"id": "OpenNew", "label": "Open New"},
|
||||
null,
|
||||
{"id": "ZoomIn", "label": "Zoom In"},
|
||||
{"id": "ZoomOut", "label": "Zoom Out"},
|
||||
{"id": "OriginalView", "label": "Original View"},
|
||||
null,
|
||||
{"id": "Quality"},
|
||||
{"id": "Pause"},
|
||||
{"id": "Mute"},
|
||||
null,
|
||||
{"id": "Find", "label": "Find..."},
|
||||
{"id": "FindAgain", "label": "Find Again"},
|
||||
{"id": "Copy"},
|
||||
{"id": "CopyAgain", "label": "Copy Again"},
|
||||
{"id": "CopySVG", "label": "Copy SVG"},
|
||||
{"id": "ViewSVG", "label": "View SVG"},
|
||||
{"id": "ViewSource", "label": "View Source"},
|
||||
{"id": "SaveAs", "label": "Save As"},
|
||||
null,
|
||||
{"id": "Help"},
|
||||
{"id": "About", "label": "About Adobe CVG Viewer..."}
|
||||
]
|
||||
}}
|
||||
@@ -1,23 +0,0 @@
|
||||
#!/usr/bin/env perl
|
||||
|
||||
# Create test comparison data using a different UTF-8 implementation.
|
||||
|
||||
# The generated utf8.dat file must have the following MD5 sum:
|
||||
# cff03b039d850f370a7362f3313e5268
|
||||
|
||||
use strict;
|
||||
|
||||
# 0xD800 - 0xDFFF are used to encode supplementary codepoints
|
||||
# 0x10000 - 0x10FFFF are supplementary codepoints
|
||||
my (@codepoints) = (0 .. 0xD7FF, 0xE000 .. 0x10FFFF);
|
||||
|
||||
my $utf8 = pack("U*", @codepoints);
|
||||
defined($utf8) or die "Unable create UTF-8 string\n";
|
||||
|
||||
open(FH, ">:utf8", "utf8.dat")
|
||||
or die "Unable to open utf8.dat: $!\n";
|
||||
print FH $utf8
|
||||
or die "Unable to write utf8.dat\n";
|
||||
close(FH);
|
||||
|
||||
# vi:ai et sw=4 ts=4:
|
||||
@@ -1,7 +0,0 @@
|
||||
[ 0.110001,
|
||||
0.12345678910111,
|
||||
0.412454033640,
|
||||
2.6651441426902,
|
||||
2.718281828459,
|
||||
3.1415926535898,
|
||||
2.1406926327793 ]
|
||||
@@ -1 +0,0 @@
|
||||
"\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\n\u000b\f\r\u000e\u000f\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f !\"#$%&'()*+,-.\/0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u007f€亗儎厗噲墛媽崕彁憭摂晼棙櫄洔潪煚、¥ウЖ┆<D096><E29486><EFBFBD>辈炒刀犯购患骄坷谅媚牌侨墒颂臀闲岩釉罩棕仝圮蒉哙徕沅彐玷殛腱眍镳耱篝貊鼬<E8B28A><E9BCAC><EFBFBD><EFBFBD>"
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"Image": {
|
||||
"Width": 800,
|
||||
"Height": 600,
|
||||
"Title": "View from 15th Floor",
|
||||
"Thumbnail": {
|
||||
"Url": "http://www.example.com/image/481989943",
|
||||
"Height": 125,
|
||||
"Width": "100"
|
||||
},
|
||||
"IDs": [116, 943, 234, 38793]
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
[
|
||||
{
|
||||
"precision": "zip",
|
||||
"Latitude": 37.7668,
|
||||
"Longitude": -122.3959,
|
||||
"Address": "",
|
||||
"City": "SAN FRANCISCO",
|
||||
"State": "CA",
|
||||
"Zip": "94107",
|
||||
"Country": "US"
|
||||
},
|
||||
{
|
||||
"precision": "zip",
|
||||
"Latitude": 37.371991,
|
||||
"Longitude": -122.026020,
|
||||
"Address": "",
|
||||
"City": "SUNNYVALE",
|
||||
"State": "CA",
|
||||
"Zip": "94085",
|
||||
"Country": "US"
|
||||
}
|
||||
]
|
||||
@@ -1,423 +0,0 @@
|
||||
#!/usr/bin/env lua
|
||||
|
||||
-- Lua CJSON tests
|
||||
--
|
||||
-- Mark Pulford <mark@kyne.com.au>
|
||||
--
|
||||
-- Note: The output of this script is easier to read with "less -S"
|
||||
|
||||
local json = require "cjson"
|
||||
local json_safe = require "cjson.safe"
|
||||
local util = require "cjson.util"
|
||||
|
||||
local function gen_raw_octets()
|
||||
local chars = {}
|
||||
for i = 0, 255 do chars[i + 1] = string.char(i) end
|
||||
return table.concat(chars)
|
||||
end
|
||||
|
||||
-- Generate every UTF-16 codepoint, including supplementary codes
|
||||
local function gen_utf16_escaped()
|
||||
-- Create raw table escapes
|
||||
local utf16_escaped = {}
|
||||
local count = 0
|
||||
|
||||
local function append_escape(code)
|
||||
local esc = ('\\u%04X'):format(code)
|
||||
table.insert(utf16_escaped, esc)
|
||||
end
|
||||
|
||||
table.insert(utf16_escaped, '"')
|
||||
for i = 0, 0xD7FF do
|
||||
append_escape(i)
|
||||
end
|
||||
-- Skip 0xD800 - 0xDFFF since they are used to encode supplementary
|
||||
-- codepoints
|
||||
for i = 0xE000, 0xFFFF do
|
||||
append_escape(i)
|
||||
end
|
||||
-- Append surrogate pair for each supplementary codepoint
|
||||
for high = 0xD800, 0xDBFF do
|
||||
for low = 0xDC00, 0xDFFF do
|
||||
append_escape(high)
|
||||
append_escape(low)
|
||||
end
|
||||
end
|
||||
table.insert(utf16_escaped, '"')
|
||||
|
||||
return table.concat(utf16_escaped)
|
||||
end
|
||||
|
||||
function load_testdata()
|
||||
local data = {}
|
||||
|
||||
-- Data for 8bit raw <-> escaped octets tests
|
||||
data.octets_raw = gen_raw_octets()
|
||||
data.octets_escaped = util.file_load("octets-escaped.dat")
|
||||
|
||||
-- Data for \uXXXX -> UTF-8 test
|
||||
data.utf16_escaped = gen_utf16_escaped()
|
||||
|
||||
-- Load matching data for utf16_escaped
|
||||
local utf8_loaded
|
||||
utf8_loaded, data.utf8_raw = pcall(util.file_load, "utf8.dat")
|
||||
if not utf8_loaded then
|
||||
data.utf8_raw = "Failed to load utf8.dat - please run genutf8.pl"
|
||||
end
|
||||
|
||||
data.table_cycle = {}
|
||||
data.table_cycle[1] = data.table_cycle
|
||||
|
||||
local big = {}
|
||||
for i = 1, 1100 do
|
||||
big = { { 10, false, true, json.null }, "string", a = big }
|
||||
end
|
||||
data.deeply_nested_data = big
|
||||
|
||||
return data
|
||||
end
|
||||
|
||||
function test_decode_cycle(filename)
|
||||
local obj1 = json.decode(util.file_load(filename))
|
||||
local obj2 = json.decode(json.encode(obj1))
|
||||
return util.compare_values(obj1, obj2)
|
||||
end
|
||||
|
||||
-- Set up data used in tests
|
||||
local Inf = math.huge;
|
||||
local NaN = math.huge * 0;
|
||||
|
||||
local testdata = load_testdata()
|
||||
|
||||
local cjson_tests = {
|
||||
-- Test API variables
|
||||
{ "Check module name, version",
|
||||
function () return json._NAME, json._VERSION end, { },
|
||||
true, { "cjson", "2.1.0" } },
|
||||
|
||||
-- Test decoding simple types
|
||||
{ "Decode string",
|
||||
json.decode, { '"test string"' }, true, { "test string" } },
|
||||
{ "Decode numbers",
|
||||
json.decode, { '[ 0.0, -5e3, -1, 0.3e-3, 1023.2, 0e10 ]' },
|
||||
true, { { 0.0, -5000, -1, 0.0003, 1023.2, 0 } } },
|
||||
{ "Decode null",
|
||||
json.decode, { 'null' }, true, { json.null } },
|
||||
{ "Decode true",
|
||||
json.decode, { 'true' }, true, { true } },
|
||||
{ "Decode false",
|
||||
json.decode, { 'false' }, true, { false } },
|
||||
{ "Decode object with numeric keys",
|
||||
json.decode, { '{ "1": "one", "3": "three" }' },
|
||||
true, { { ["1"] = "one", ["3"] = "three" } } },
|
||||
{ "Decode object with string keys",
|
||||
json.decode, { '{ "a": "a", "b": "b" }' },
|
||||
true, { { a = "a", b = "b" } } },
|
||||
{ "Decode array",
|
||||
json.decode, { '[ "one", null, "three" ]' },
|
||||
true, { { "one", json.null, "three" } } },
|
||||
|
||||
-- Test decoding errors
|
||||
{ "Decode UTF-16BE [throw error]",
|
||||
json.decode, { '\0"\0"' },
|
||||
false, { "JSON parser does not support UTF-16 or UTF-32" } },
|
||||
{ "Decode UTF-16LE [throw error]",
|
||||
json.decode, { '"\0"\0' },
|
||||
false, { "JSON parser does not support UTF-16 or UTF-32" } },
|
||||
{ "Decode UTF-32BE [throw error]",
|
||||
json.decode, { '\0\0\0"' },
|
||||
false, { "JSON parser does not support UTF-16 or UTF-32" } },
|
||||
{ "Decode UTF-32LE [throw error]",
|
||||
json.decode, { '"\0\0\0' },
|
||||
false, { "JSON parser does not support UTF-16 or UTF-32" } },
|
||||
{ "Decode partial JSON [throw error]",
|
||||
json.decode, { '{ "unexpected eof": ' },
|
||||
false, { "Expected value but found T_END at character 21" } },
|
||||
{ "Decode with extra comma [throw error]",
|
||||
json.decode, { '{ "extra data": true }, false' },
|
||||
false, { "Expected the end but found T_COMMA at character 23" } },
|
||||
{ "Decode invalid escape code [throw error]",
|
||||
json.decode, { [[ { "bad escape \q code" } ]] },
|
||||
false, { "Expected object key string but found invalid escape code at character 16" } },
|
||||
{ "Decode invalid unicode escape [throw error]",
|
||||
json.decode, { [[ { "bad unicode \u0f6 escape" } ]] },
|
||||
false, { "Expected object key string but found invalid unicode escape code at character 17" } },
|
||||
{ "Decode invalid keyword [throw error]",
|
||||
json.decode, { ' [ "bad barewood", test ] ' },
|
||||
false, { "Expected value but found invalid token at character 20" } },
|
||||
{ "Decode invalid number #1 [throw error]",
|
||||
json.decode, { '[ -+12 ]' },
|
||||
false, { "Expected value but found invalid number at character 3" } },
|
||||
{ "Decode invalid number #2 [throw error]",
|
||||
json.decode, { '-v' },
|
||||
false, { "Expected value but found invalid number at character 1" } },
|
||||
{ "Decode invalid number exponent [throw error]",
|
||||
json.decode, { '[ 0.4eg10 ]' },
|
||||
false, { "Expected comma or array end but found invalid token at character 6" } },
|
||||
|
||||
-- Test decoding nested arrays / objects
|
||||
{ "Set decode_max_depth(5)",
|
||||
json.decode_max_depth, { 5 }, true, { 5 } },
|
||||
{ "Decode array at nested limit",
|
||||
json.decode, { '[[[[[ "nested" ]]]]]' },
|
||||
true, { {{{{{ "nested" }}}}} } },
|
||||
{ "Decode array over nested limit [throw error]",
|
||||
json.decode, { '[[[[[[ "nested" ]]]]]]' },
|
||||
false, { "Found too many nested data structures (6) at character 6" } },
|
||||
{ "Decode object at nested limit",
|
||||
json.decode, { '{"a":{"b":{"c":{"d":{"e":"nested"}}}}}' },
|
||||
true, { {a={b={c={d={e="nested"}}}}} } },
|
||||
{ "Decode object over nested limit [throw error]",
|
||||
json.decode, { '{"a":{"b":{"c":{"d":{"e":{"f":"nested"}}}}}}' },
|
||||
false, { "Found too many nested data structures (6) at character 26" } },
|
||||
{ "Set decode_max_depth(1000)",
|
||||
json.decode_max_depth, { 1000 }, true, { 1000 } },
|
||||
{ "Decode deeply nested array [throw error]",
|
||||
json.decode, { string.rep("[", 1100) .. '1100' .. string.rep("]", 1100)},
|
||||
false, { "Found too many nested data structures (1001) at character 1001" } },
|
||||
|
||||
-- Test encoding nested tables
|
||||
{ "Set encode_max_depth(5)",
|
||||
json.encode_max_depth, { 5 }, true, { 5 } },
|
||||
{ "Encode nested table as array at nested limit",
|
||||
json.encode, { {{{{{"nested"}}}}} }, true, { '[[[[["nested"]]]]]' } },
|
||||
{ "Encode nested table as array after nested limit [throw error]",
|
||||
json.encode, { { {{{{{"nested"}}}}} } },
|
||||
false, { "Cannot serialise, excessive nesting (6)" } },
|
||||
{ "Encode nested table as object at nested limit",
|
||||
json.encode, { {a={b={c={d={e="nested"}}}}} },
|
||||
true, { '{"a":{"b":{"c":{"d":{"e":"nested"}}}}}' } },
|
||||
{ "Encode nested table as object over nested limit [throw error]",
|
||||
json.encode, { {a={b={c={d={e={f="nested"}}}}}} },
|
||||
false, { "Cannot serialise, excessive nesting (6)" } },
|
||||
{ "Encode table with cycle [throw error]",
|
||||
json.encode, { testdata.table_cycle },
|
||||
false, { "Cannot serialise, excessive nesting (6)" } },
|
||||
{ "Set encode_max_depth(1000)",
|
||||
json.encode_max_depth, { 1000 }, true, { 1000 } },
|
||||
{ "Encode deeply nested data [throw error]",
|
||||
json.encode, { testdata.deeply_nested_data },
|
||||
false, { "Cannot serialise, excessive nesting (1001)" } },
|
||||
|
||||
-- Test encoding simple types
|
||||
{ "Encode null",
|
||||
json.encode, { json.null }, true, { 'null' } },
|
||||
{ "Encode true",
|
||||
json.encode, { true }, true, { 'true' } },
|
||||
{ "Encode false",
|
||||
json.encode, { false }, true, { 'false' } },
|
||||
{ "Encode empty object",
|
||||
json.encode, { { } }, true, { '{}' } },
|
||||
{ "Encode integer",
|
||||
json.encode, { 10 }, true, { '10' } },
|
||||
{ "Encode string",
|
||||
json.encode, { "hello" }, true, { '"hello"' } },
|
||||
{ "Encode Lua function [throw error]",
|
||||
json.encode, { function () end },
|
||||
false, { "Cannot serialise function: type not supported" } },
|
||||
|
||||
-- Test decoding invalid numbers
|
||||
{ "Set decode_invalid_numbers(true)",
|
||||
json.decode_invalid_numbers, { true }, true, { true } },
|
||||
{ "Decode hexadecimal",
|
||||
json.decode, { '0x6.ffp1' }, true, { 13.9921875 } },
|
||||
{ "Decode numbers with leading zero",
|
||||
json.decode, { '[ 0123, 00.33 ]' }, true, { { 123, 0.33 } } },
|
||||
{ "Decode +-Inf",
|
||||
json.decode, { '[ +Inf, Inf, -Inf ]' }, true, { { Inf, Inf, -Inf } } },
|
||||
{ "Decode +-Infinity",
|
||||
json.decode, { '[ +Infinity, Infinity, -Infinity ]' },
|
||||
true, { { Inf, Inf, -Inf } } },
|
||||
{ "Decode +-NaN",
|
||||
json.decode, { '[ +NaN, NaN, -NaN ]' }, true, { { NaN, NaN, NaN } } },
|
||||
{ "Decode Infrared (not infinity) [throw error]",
|
||||
json.decode, { 'Infrared' },
|
||||
false, { "Expected the end but found invalid token at character 4" } },
|
||||
{ "Decode Noodle (not NaN) [throw error]",
|
||||
json.decode, { 'Noodle' },
|
||||
false, { "Expected value but found invalid token at character 1" } },
|
||||
{ "Set decode_invalid_numbers(false)",
|
||||
json.decode_invalid_numbers, { false }, true, { false } },
|
||||
{ "Decode hexadecimal [throw error]",
|
||||
json.decode, { '0x6' },
|
||||
false, { "Expected value but found invalid number at character 1" } },
|
||||
{ "Decode numbers with leading zero [throw error]",
|
||||
json.decode, { '[ 0123, 00.33 ]' },
|
||||
false, { "Expected value but found invalid number at character 3" } },
|
||||
{ "Decode +-Inf [throw error]",
|
||||
json.decode, { '[ +Inf, Inf, -Inf ]' },
|
||||
false, { "Expected value but found invalid token at character 3" } },
|
||||
{ "Decode +-Infinity [throw error]",
|
||||
json.decode, { '[ +Infinity, Infinity, -Infinity ]' },
|
||||
false, { "Expected value but found invalid token at character 3" } },
|
||||
{ "Decode +-NaN [throw error]",
|
||||
json.decode, { '[ +NaN, NaN, -NaN ]' },
|
||||
false, { "Expected value but found invalid token at character 3" } },
|
||||
{ 'Set decode_invalid_numbers("on")',
|
||||
json.decode_invalid_numbers, { "on" }, true, { true } },
|
||||
|
||||
-- Test encoding invalid numbers
|
||||
{ "Set encode_invalid_numbers(false)",
|
||||
json.encode_invalid_numbers, { false }, true, { false } },
|
||||
{ "Encode NaN [throw error]",
|
||||
json.encode, { NaN },
|
||||
false, { "Cannot serialise number: must not be NaN or Inf" } },
|
||||
{ "Encode Infinity [throw error]",
|
||||
json.encode, { Inf },
|
||||
false, { "Cannot serialise number: must not be NaN or Inf" } },
|
||||
{ "Set encode_invalid_numbers(\"null\")",
|
||||
json.encode_invalid_numbers, { "null" }, true, { "null" } },
|
||||
{ "Encode NaN as null",
|
||||
json.encode, { NaN }, true, { "null" } },
|
||||
{ "Encode Infinity as null",
|
||||
json.encode, { Inf }, true, { "null" } },
|
||||
{ "Set encode_invalid_numbers(true)",
|
||||
json.encode_invalid_numbers, { true }, true, { true } },
|
||||
{ "Encode NaN",
|
||||
json.encode, { NaN }, true, { "nan" } },
|
||||
{ "Encode Infinity",
|
||||
json.encode, { Inf }, true, { "inf" } },
|
||||
{ 'Set encode_invalid_numbers("off")',
|
||||
json.encode_invalid_numbers, { "off" }, true, { false } },
|
||||
|
||||
-- Test encoding tables
|
||||
{ "Set encode_sparse_array(true, 2, 3)",
|
||||
json.encode_sparse_array, { true, 2, 3 }, true, { true, 2, 3 } },
|
||||
{ "Encode sparse table as array #1",
|
||||
json.encode, { { [3] = "sparse test" } },
|
||||
true, { '[null,null,"sparse test"]' } },
|
||||
{ "Encode sparse table as array #2",
|
||||
json.encode, { { [1] = "one", [4] = "sparse test" } },
|
||||
true, { '["one",null,null,"sparse test"]' } },
|
||||
{ "Encode sparse array as object",
|
||||
json.encode, { { [1] = "one", [5] = "sparse test" } },
|
||||
true, { '{"1":"one","5":"sparse test"}' } },
|
||||
{ "Encode table with numeric string key as object",
|
||||
json.encode, { { ["2"] = "numeric string key test" } },
|
||||
true, { '{"2":"numeric string key test"}' } },
|
||||
{ "Set encode_sparse_array(false)",
|
||||
json.encode_sparse_array, { false }, true, { false, 2, 3 } },
|
||||
{ "Encode table with incompatible key [throw error]",
|
||||
json.encode, { { [false] = "wrong" } },
|
||||
false, { "Cannot serialise boolean: table key must be a number or string" } },
|
||||
|
||||
-- Test escaping
|
||||
{ "Encode all octets (8-bit clean)",
|
||||
json.encode, { testdata.octets_raw }, true, { testdata.octets_escaped } },
|
||||
{ "Decode all escaped octets",
|
||||
json.decode, { testdata.octets_escaped }, true, { testdata.octets_raw } },
|
||||
{ "Decode single UTF-16 escape",
|
||||
json.decode, { [["\uF800"]] }, true, { "\239\160\128" } },
|
||||
{ "Decode all UTF-16 escapes (including surrogate combinations)",
|
||||
json.decode, { testdata.utf16_escaped }, true, { testdata.utf8_raw } },
|
||||
{ "Decode swapped surrogate pair [throw error]",
|
||||
json.decode, { [["\uDC00\uD800"]] },
|
||||
false, { "Expected value but found invalid unicode escape code at character 2" } },
|
||||
{ "Decode duplicate high surrogate [throw error]",
|
||||
json.decode, { [["\uDB00\uDB00"]] },
|
||||
false, { "Expected value but found invalid unicode escape code at character 2" } },
|
||||
{ "Decode duplicate low surrogate [throw error]",
|
||||
json.decode, { [["\uDB00\uDB00"]] },
|
||||
false, { "Expected value but found invalid unicode escape code at character 2" } },
|
||||
{ "Decode missing low surrogate [throw error]",
|
||||
json.decode, { [["\uDB00"]] },
|
||||
false, { "Expected value but found invalid unicode escape code at character 2" } },
|
||||
{ "Decode invalid low surrogate [throw error]",
|
||||
json.decode, { [["\uDB00\uD"]] },
|
||||
false, { "Expected value but found invalid unicode escape code at character 2" } },
|
||||
|
||||
-- Test locale support
|
||||
--
|
||||
-- The standard Lua interpreter is ANSI C online doesn't support locales
|
||||
-- by default. Force a known problematic locale to test strtod()/sprintf().
|
||||
{ "Set locale to cs_CZ (comma separator)", function ()
|
||||
os.setlocale("cs_CZ")
|
||||
json.new()
|
||||
end },
|
||||
{ "Encode number under comma locale",
|
||||
json.encode, { 1.5 }, true, { '1.5' } },
|
||||
{ "Decode number in array under comma locale",
|
||||
json.decode, { '[ 10, "test" ]' }, true, { { 10, "test" } } },
|
||||
{ "Revert locale to POSIX", function ()
|
||||
os.setlocale("C")
|
||||
json.new()
|
||||
end },
|
||||
|
||||
-- Test encode_keep_buffer() and enable_number_precision()
|
||||
{ "Set encode_keep_buffer(false)",
|
||||
json.encode_keep_buffer, { false }, true, { false } },
|
||||
{ "Set encode_number_precision(3)",
|
||||
json.encode_number_precision, { 3 }, true, { 3 } },
|
||||
{ "Encode number with precision 3",
|
||||
json.encode, { 1/3 }, true, { "0.333" } },
|
||||
{ "Set encode_number_precision(14)",
|
||||
json.encode_number_precision, { 14 }, true, { 14 } },
|
||||
{ "Set encode_keep_buffer(true)",
|
||||
json.encode_keep_buffer, { true }, true, { true } },
|
||||
|
||||
-- Test config API errors
|
||||
-- Function is listed as '?' due to pcall
|
||||
{ "Set encode_number_precision(0) [throw error]",
|
||||
json.encode_number_precision, { 0 },
|
||||
false, { "bad argument #1 to '?' (expected integer between 1 and 14)" } },
|
||||
{ "Set encode_number_precision(\"five\") [throw error]",
|
||||
json.encode_number_precision, { "five" },
|
||||
false, { "bad argument #1 to '?' (number expected, got string)" } },
|
||||
{ "Set encode_keep_buffer(nil, true) [throw error]",
|
||||
json.encode_keep_buffer, { nil, true },
|
||||
false, { "bad argument #2 to '?' (found too many arguments)" } },
|
||||
{ "Set encode_max_depth(\"wrong\") [throw error]",
|
||||
json.encode_max_depth, { "wrong" },
|
||||
false, { "bad argument #1 to '?' (number expected, got string)" } },
|
||||
{ "Set decode_max_depth(0) [throw error]",
|
||||
json.decode_max_depth, { "0" },
|
||||
false, { "bad argument #1 to '?' (expected integer between 1 and 2147483647)" } },
|
||||
{ "Set encode_invalid_numbers(-2) [throw error]",
|
||||
json.encode_invalid_numbers, { -2 },
|
||||
false, { "bad argument #1 to '?' (invalid option '-2')" } },
|
||||
{ "Set decode_invalid_numbers(true, false) [throw error]",
|
||||
json.decode_invalid_numbers, { true, false },
|
||||
false, { "bad argument #2 to '?' (found too many arguments)" } },
|
||||
{ "Set encode_sparse_array(\"not quite on\") [throw error]",
|
||||
json.encode_sparse_array, { "not quite on" },
|
||||
false, { "bad argument #1 to '?' (invalid option 'not quite on')" } },
|
||||
|
||||
{ "Reset Lua CJSON configuration", function () json = json.new() end },
|
||||
-- Wrap in a function to ensure the table returned by json.new() is used
|
||||
{ "Check encode_sparse_array()",
|
||||
function (...) return json.encode_sparse_array(...) end, { },
|
||||
true, { false, 2, 10 } },
|
||||
|
||||
{ "Encode (safe) simple value",
|
||||
json_safe.encode, { true },
|
||||
true, { "true" } },
|
||||
{ "Encode (safe) argument validation [throw error]",
|
||||
json_safe.encode, { "arg1", "arg2" },
|
||||
false, { "bad argument #1 to '?' (expected 1 argument)" } },
|
||||
{ "Decode (safe) error generation",
|
||||
json_safe.decode, { "Oops" },
|
||||
true, { nil, "Expected value but found invalid token at character 1" } },
|
||||
{ "Decode (safe) error generation after new()",
|
||||
function(...) return json_safe.new().decode(...) end, { "Oops" },
|
||||
true, { nil, "Expected value but found invalid token at character 1" } },
|
||||
}
|
||||
|
||||
print(("==> Testing Lua CJSON version %s\n"):format(json._VERSION))
|
||||
|
||||
util.run_test_group(cjson_tests)
|
||||
|
||||
for _, filename in ipairs(arg) do
|
||||
util.run_test("Decode cycle " .. filename, test_decode_cycle, { filename },
|
||||
true, { true })
|
||||
end
|
||||
|
||||
local pass, total = util.run_test_summary()
|
||||
|
||||
if pass == total then
|
||||
print("==> Summary: all tests succeeded")
|
||||
else
|
||||
print(("==> Summary: %d/%d tests failed"):format(total - pass, total))
|
||||
os.exit(1)
|
||||
end
|
||||
|
||||
-- vi:ai et sw=4 ts=4:
|
||||
@@ -1 +0,0 @@
|
||||
{ "array": [ 10, true, null ] }
|
||||
@@ -403,7 +403,7 @@ static int traverseephemeron (global_State *g, Table *h) {
|
||||
reallymarkobject(g, gcvalue(gval(n))); /* mark it now */
|
||||
}
|
||||
}
|
||||
if (prop)
|
||||
if (g->gcstate != GCSatomic || prop)
|
||||
linktable(h, &g->ephemeron); /* have to propagate again */
|
||||
else if (hasclears) /* does table have white keys? */
|
||||
linktable(h, &g->allweak); /* may have to clean white keys */
|
||||
|
||||
63
HISTORY.md
63
HISTORY.md
@@ -1,3 +1,66 @@
|
||||
v0.8.0 (2014-10-27)
|
||||
-----------
|
||||
* Add mysql client driver
|
||||
* Bugfix : skynet.queue
|
||||
|
||||
v0.7.4 (2014-10-13)
|
||||
-----------
|
||||
* Bugfix : clear coroutine pool when GC
|
||||
* hotfix : A bug introduce by 0.7.3
|
||||
|
||||
v0.7.3 (2014-10-13)
|
||||
-----------
|
||||
* Add some logs (warning) when overload
|
||||
* Bugfix: crash on exit
|
||||
|
||||
v0.7.2 (2014-9-29)
|
||||
-----------
|
||||
* Bugfix : datacenter.wait
|
||||
* Bugfix : error in forker coroutine
|
||||
* Add skynet.term
|
||||
* Accept socket report port
|
||||
* sharedata can be update more than once
|
||||
|
||||
v0.7.1 (2014-9-22)
|
||||
-----------
|
||||
* bugfix: wakeup sleep should return BREAK
|
||||
* bugfix: sharedatad load string
|
||||
* bugfix: dataserver forward error msg
|
||||
|
||||
v0.7.0 (2014-9-8)
|
||||
-----------
|
||||
* Use sproto instead of cjson
|
||||
* Add message logger
|
||||
* Add hmac-sha1
|
||||
* Some minor bugfix
|
||||
|
||||
v0.6.2 (2014-9-1)
|
||||
-----------
|
||||
* bugfix: only skynet.call response PTYPE_ERROR
|
||||
|
||||
v0.6.1 (2014-8-25)
|
||||
-----------
|
||||
* bugfix: datacenter.wakeup
|
||||
* change struct msg name to avoid conflict in mac
|
||||
* improve seri library
|
||||
|
||||
v0.6.0 (2014-8-18)
|
||||
-----------
|
||||
* add sharedata
|
||||
* bugfix: service exit before init would not report back
|
||||
* add skynet.response and check multicall skynet.ret
|
||||
* skynet.newservice throw error when lanuch faild
|
||||
* Don't check imported function in snax.hotfix
|
||||
* snax service add change SERVICE_PATH and add it to package.path
|
||||
* skynet.redirect support string address
|
||||
* bugfix: skynet.harbor.link may block
|
||||
* add skynet.harbor.queryname to query globalname
|
||||
* add cluster.proxy
|
||||
* add DEBUG command exit (send a message to lua service by DEBUG)
|
||||
* add DEBUG command run (debug_console command inject)
|
||||
* bugfix : socketchannel connect once
|
||||
* bugfix : mongo driver
|
||||
|
||||
v0.5.2 (2014-8-11)
|
||||
-----------
|
||||
* Bugfix : httpd request
|
||||
|
||||
28
Makefile
28
Makefile
@@ -42,13 +42,14 @@ jemalloc : $(MALLOC_STATICLIB)
|
||||
|
||||
CSERVICE = snlua logger gate harbor
|
||||
LUA_CLIB = skynet socketdriver int64 bson mongo md5 netpack \
|
||||
cjson clientsocket memory profile multicast \
|
||||
cluster crypt
|
||||
clientsocket memory profile multicast \
|
||||
cluster crypt sharedata stm sproto lpeg \
|
||||
mysqlaux
|
||||
|
||||
SKYNET_SRC = skynet_main.c skynet_handle.c skynet_module.c skynet_mq.c \
|
||||
skynet_server.c skynet_start.c skynet_timer.c skynet_error.c \
|
||||
skynet_harbor.c skynet_env.c skynet_monitor.c skynet_socket.c socket_server.c \
|
||||
malloc_hook.c skynet_daemon.c
|
||||
malloc_hook.c skynet_daemon.c skynet_log.c
|
||||
|
||||
all : \
|
||||
$(SKYNET_BUILD_PATH)/skynet \
|
||||
@@ -92,9 +93,6 @@ $(LUA_CLIB_PATH)/md5.so : 3rd/lua-md5/md5.c 3rd/lua-md5/md5lib.c 3rd/lua-md5/com
|
||||
$(LUA_CLIB_PATH)/netpack.so : lualib-src/lua-netpack.c | $(LUA_CLIB_PATH)
|
||||
$(CC) $(CFLAGS) $(SHARED) $^ -Iskynet-src -o $@
|
||||
|
||||
$(LUA_CLIB_PATH)/cjson.so : | $(LUA_CLIB_PATH)
|
||||
cd 3rd/lua-cjson && $(MAKE) LUA_INCLUDE_DIR=../../$(LUA_INC) CC=$(CC) CJSON_LDFLAGS="$(SHARED)" && cd ../.. && cp 3rd/lua-cjson/cjson.so $@
|
||||
|
||||
$(LUA_CLIB_PATH)/clientsocket.so : lualib-src/lua-clientsocket.c | $(LUA_CLIB_PATH)
|
||||
$(CC) $(CFLAGS) $(SHARED) $^ -o $@ -lpthread
|
||||
|
||||
@@ -110,14 +108,28 @@ $(LUA_CLIB_PATH)/multicast.so : lualib-src/lua-multicast.c | $(LUA_CLIB_PATH)
|
||||
$(LUA_CLIB_PATH)/cluster.so : lualib-src/lua-cluster.c | $(LUA_CLIB_PATH)
|
||||
$(CC) $(CFLAGS) $(SHARED) -Iskynet-src $^ -o $@
|
||||
|
||||
$(LUA_CLIB_PATH)/crypt.so : lualib-src/lua-crypt.c | $(LUA_CLIB_PATH)
|
||||
$(LUA_CLIB_PATH)/crypt.so : lualib-src/lua-crypt.c lualib-src/lsha1.c | $(LUA_CLIB_PATH)
|
||||
$(CC) $(CFLAGS) $(SHARED) $^ -o $@
|
||||
|
||||
$(LUA_CLIB_PATH)/sharedata.so : lualib-src/lua-sharedata.c | $(LUA_CLIB_PATH)
|
||||
$(CC) $(CFLAGS) $(SHARED) $^ -o $@
|
||||
|
||||
$(LUA_CLIB_PATH)/stm.so : lualib-src/lua-stm.c | $(LUA_CLIB_PATH)
|
||||
$(CC) $(CFLAGS) $(SHARED) -Iskynet-src $^ -o $@
|
||||
|
||||
$(LUA_CLIB_PATH)/sproto.so : lualib-src/sproto/sproto.c lualib-src/sproto/lsproto.c | $(LUA_CLIB_PATH)
|
||||
$(CC) $(CFLAGS) $(SHARED) -Ilualib-src/sproto $^ -o $@
|
||||
|
||||
$(LUA_CLIB_PATH)/lpeg.so : 3rd/lpeg/lpcap.c 3rd/lpeg/lpcode.c 3rd/lpeg/lpprint.c 3rd/lpeg/lptree.c 3rd/lpeg/lpvm.c | $(LUA_CLIB_PATH)
|
||||
$(CC) $(CFLAGS) $(SHARED) -I3rd/lpeg $^ -o $@
|
||||
|
||||
$(LUA_CLIB_PATH)/mysqlaux.so : lualib-src/lua_mysqlaux.c | $(LUA_CLIB_PATH)
|
||||
$(CC) $(CFLAGS) $(SHARED) $^ -o $@
|
||||
|
||||
clean :
|
||||
rm -f $(SKYNET_BUILD_PATH)/skynet $(CSERVICE_PATH)/*.so $(LUA_CLIB_PATH)/*.so
|
||||
|
||||
cleanall: clean
|
||||
cd 3rd/lua-cjson && $(MAKE) clean
|
||||
cd 3rd/jemalloc && $(MAKE) clean
|
||||
rm -f $(LUA_STATICLIB)
|
||||
|
||||
|
||||
@@ -1,40 +1,83 @@
|
||||
local skynet = require "skynet"
|
||||
local jsonpack = require "jsonpack"
|
||||
local netpack = require "netpack"
|
||||
local socket = require "socket"
|
||||
local sproto = require "sproto"
|
||||
local bit32 = require "bit32"
|
||||
|
||||
local host
|
||||
local send_request
|
||||
|
||||
local CMD = {}
|
||||
|
||||
local REQUEST = {}
|
||||
local client_fd
|
||||
|
||||
local function send_client(v)
|
||||
socket.write(client_fd, netpack.pack(jsonpack.pack(0, {true, v})))
|
||||
function REQUEST:get()
|
||||
print("get", self.what)
|
||||
local r = skynet.call("SIMPLEDB", "lua", "get", self.what)
|
||||
return { result = r }
|
||||
end
|
||||
|
||||
local function response_client(session,v)
|
||||
socket.write(client_fd, netpack.pack(jsonpack.response(session,v)))
|
||||
function REQUEST:set()
|
||||
print("set", self.what, self.value)
|
||||
local r = skynet.call("SIMPLEDB", "lua", "set", self.what, self.value)
|
||||
end
|
||||
|
||||
function REQUEST:handshake()
|
||||
return { msg = "Welcome to skynet, I will send heartbeat every 5 sec." }
|
||||
end
|
||||
|
||||
local function request(name, args, response)
|
||||
local f = assert(REQUEST[name])
|
||||
local r = f(args)
|
||||
if response then
|
||||
return response(r)
|
||||
end
|
||||
end
|
||||
|
||||
local function send_package(pack)
|
||||
local size = #pack
|
||||
local package = string.char(bit32.extract(size,8,8)) ..
|
||||
string.char(bit32.extract(size,0,8))..
|
||||
pack
|
||||
|
||||
socket.write(client_fd, package)
|
||||
end
|
||||
|
||||
skynet.register_protocol {
|
||||
name = "client",
|
||||
id = skynet.PTYPE_CLIENT,
|
||||
unpack = function (msg, sz)
|
||||
return jsonpack.unpack(skynet.tostring(msg,sz))
|
||||
return host:dispatch(msg, sz)
|
||||
end,
|
||||
dispatch = function (_, _, session, args)
|
||||
local ok, result = pcall(skynet.call,"SIMPLEDB", "lua", table.unpack(args))
|
||||
if ok then
|
||||
response_client(session, { true, result })
|
||||
dispatch = function (_, _, type, ...)
|
||||
if type == "REQUEST" then
|
||||
local ok, result = pcall(request, ...)
|
||||
if ok then
|
||||
if result then
|
||||
send_package(result)
|
||||
end
|
||||
else
|
||||
skynet.error(result)
|
||||
end
|
||||
else
|
||||
response_client(session, { false, "Invalid command" })
|
||||
assert(type == "RESPONSE")
|
||||
error "This example doesn't support request client"
|
||||
end
|
||||
end
|
||||
}
|
||||
|
||||
function CMD.start(gate , fd)
|
||||
function CMD.start(gate, fd, proto)
|
||||
host = sproto.new(proto.c2s):host "package"
|
||||
send_request = host:attach(sproto.new(proto.s2c))
|
||||
skynet.fork(function()
|
||||
while true do
|
||||
send_package(send_request "heartbeat")
|
||||
skynet.sleep(500)
|
||||
end
|
||||
end)
|
||||
|
||||
client_fd = fd
|
||||
skynet.call(gate, "lua", "forward", fd)
|
||||
send_client "Welcome to skynet"
|
||||
end
|
||||
|
||||
skynet.start(function()
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
package.cpath = "luaclib/?.so"
|
||||
package.path = "lualib/?.lua;examples/?.lua"
|
||||
|
||||
local socket = require "clientsocket"
|
||||
local cjson = require "cjson"
|
||||
local bit32 = require "bit32"
|
||||
local proto = require "proto"
|
||||
local sproto = require "sproto"
|
||||
|
||||
local host = sproto.new(proto.s2c):host "package"
|
||||
local request = host:attach(sproto.new(proto.c2s))
|
||||
|
||||
local fd = assert(socket.connect("127.0.0.1", 8888))
|
||||
|
||||
@@ -46,34 +51,62 @@ end
|
||||
|
||||
local session = 0
|
||||
|
||||
local function send_request(v)
|
||||
local function send_request(name, args)
|
||||
session = session + 1
|
||||
local str = string.format("%d+%s",session, cjson.encode(v))
|
||||
local str = request(name, args, session)
|
||||
send_package(fd, str)
|
||||
print("Request:", session)
|
||||
end
|
||||
|
||||
local last = ""
|
||||
|
||||
while true do
|
||||
local function print_request(name, args)
|
||||
print("REQUEST", name)
|
||||
if args then
|
||||
for k,v in pairs(args) do
|
||||
print(k,v)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function print_response(session, args)
|
||||
print("RESPONSE", session)
|
||||
if args then
|
||||
for k,v in pairs(args) do
|
||||
print(k,v)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function print_package(t, ...)
|
||||
if t == "REQUEST" then
|
||||
print_request(...)
|
||||
else
|
||||
assert(t == "RESPONSE")
|
||||
print_response(...)
|
||||
end
|
||||
end
|
||||
|
||||
local function dispatch_package()
|
||||
while true do
|
||||
local v
|
||||
v, last = recv_package(last)
|
||||
if not v then
|
||||
break
|
||||
end
|
||||
local session,t,str = string.match(v, "(%d+)(.)(.*)")
|
||||
assert(t == '-' or t == '+')
|
||||
session = tonumber(session)
|
||||
local result = cjson.decode(str)
|
||||
print("Response:",session, result[1], result[2])
|
||||
|
||||
print_package(host:dispatch(v))
|
||||
end
|
||||
end
|
||||
|
||||
send_request("handshake")
|
||||
send_request("set", { what = "hello", value = "world" })
|
||||
while true do
|
||||
dispatch_package()
|
||||
local cmd = socket.readstdin()
|
||||
if cmd then
|
||||
local args = {}
|
||||
string.gsub(cmd, '[^ ]+', function(v) table.insert(args, v) end )
|
||||
send_request(args)
|
||||
send_request("get", { what = cmd })
|
||||
else
|
||||
socket.usleep(100)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2,8 +2,9 @@ local skynet = require "skynet"
|
||||
local cluster = require "cluster"
|
||||
|
||||
skynet.start(function()
|
||||
skynet.newservice("simpledb")
|
||||
print(skynet.call("SIMPLEDB", "lua", "SET", "a", "foobar"))
|
||||
print(skynet.call("SIMPLEDB", "lua", "GET", "a"))
|
||||
local sdb = skynet.newservice("simpledb")
|
||||
skynet.name(".simpledb", sdb)
|
||||
print(skynet.call(".simpledb", "lua", "SET", "a", "foobar"))
|
||||
print(skynet.call(".simpledb", "lua", "GET", "a"))
|
||||
cluster.open "db"
|
||||
end)
|
||||
|
||||
@@ -2,5 +2,7 @@ local skynet = require "skynet"
|
||||
local cluster = require "cluster"
|
||||
|
||||
skynet.start(function()
|
||||
print(cluster.call("db", "SIMPLEDB", "GET", "a"))
|
||||
local proxy = cluster.proxy("db", ".simpledb")
|
||||
print(skynet.call(proxy, "lua", "GET", "a"))
|
||||
print(cluster.call("db", ".simpledb", "GET", "a"))
|
||||
end)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
root = "./"
|
||||
thread = 8
|
||||
logger = nil
|
||||
logpath = "."
|
||||
harbor = 1
|
||||
address = "127.0.0.1:2526"
|
||||
master = "127.0.0.1:2013"
|
||||
|
||||
11
examples/config.mysql
Normal file
11
examples/config.mysql
Normal file
@@ -0,0 +1,11 @@
|
||||
root = "./"
|
||||
thread = 8
|
||||
logger = nil
|
||||
harbor = 0
|
||||
start = "main_mysql" -- main script
|
||||
bootstrap = "snlua bootstrap" -- The service for bootstrap
|
||||
luaservice = root.."service/?.lua;"..root.."test/?.lua;"..root.."examples/?.lua"
|
||||
lualoader = "lualib/loader.lua"
|
||||
snax = root.."examples/?.lua;"..root.."test/?.lua"
|
||||
cpath = root.."cservice/?.so"
|
||||
-- daemon = "./skynet.pid"
|
||||
@@ -4,5 +4,6 @@ skynet.start(function()
|
||||
skynet.dispatch("lua", function(session, address, ...)
|
||||
print("[GLOBALLOG]", skynet.address(address), ...)
|
||||
end)
|
||||
skynet.register ".log"
|
||||
skynet.register "LOG"
|
||||
end)
|
||||
|
||||
19
examples/injectlaunch.lua
Normal file
19
examples/injectlaunch.lua
Normal file
@@ -0,0 +1,19 @@
|
||||
if not _P then
|
||||
print[[
|
||||
This file is examples to show how to inject code into lua service.
|
||||
It is used to inject into launcher service to change the command.LAUNCH to command.LOGLAUNCH.
|
||||
telnet the debug_console service (nc 127.0.0.1 8000), and run:
|
||||
inject 3 examples/injectlaunch.lua -- 3 means launcher service
|
||||
]]
|
||||
return
|
||||
end
|
||||
local command = _P.lua.command
|
||||
|
||||
if command.RAWLAUNCH then
|
||||
command.LAUNCH, command.RAWLAUNCH = command.RAWLAUNCH
|
||||
print "restore command.LAUNCH"
|
||||
else
|
||||
command.RAWLAUNCH = command.LAUNCH
|
||||
command.LAUNCH = command.LOGLAUNCH
|
||||
print "replace command.LAUNCH"
|
||||
end
|
||||
@@ -1,9 +1,16 @@
|
||||
local skynet = require "skynet"
|
||||
local harbor = require "skynet.harbor"
|
||||
|
||||
local function monitor_master()
|
||||
harbor.linkmaster()
|
||||
print("master is down")
|
||||
skynet.exit()
|
||||
end
|
||||
|
||||
skynet.start(function()
|
||||
print("Log server start")
|
||||
skynet.monitor "simplemonitor"
|
||||
local log = skynet.newservice("globallog")
|
||||
skynet.exit()
|
||||
skynet.fork(monitor_master)
|
||||
end)
|
||||
|
||||
|
||||
10
examples/main_mysql.lua
Normal file
10
examples/main_mysql.lua
Normal file
@@ -0,0 +1,10 @@
|
||||
local skynet = require "skynet"
|
||||
|
||||
|
||||
skynet.start(function()
|
||||
print("Main Server start")
|
||||
local console = skynet.newservice("testmysql")
|
||||
|
||||
print("Main Server exit")
|
||||
skynet.exit()
|
||||
end)
|
||||
44
examples/proto.lua
Normal file
44
examples/proto.lua
Normal file
@@ -0,0 +1,44 @@
|
||||
local sprotoparser = require "sprotoparser"
|
||||
|
||||
local proto = {}
|
||||
|
||||
proto.c2s = sprotoparser.parse [[
|
||||
.package {
|
||||
type 0 : integer
|
||||
session 1 : integer
|
||||
}
|
||||
|
||||
handshake 1 {
|
||||
response {
|
||||
msg 0 : string
|
||||
}
|
||||
}
|
||||
|
||||
get 2 {
|
||||
request {
|
||||
what 0 : string
|
||||
}
|
||||
response {
|
||||
result 0 : string
|
||||
}
|
||||
}
|
||||
|
||||
set 3 {
|
||||
request {
|
||||
what 0 : string
|
||||
value 1 : string
|
||||
}
|
||||
}
|
||||
|
||||
]]
|
||||
|
||||
proto.s2c = sprotoparser.parse [[
|
||||
.package {
|
||||
type 0 : integer
|
||||
session 1 : integer
|
||||
}
|
||||
|
||||
heartbeat 1 {}
|
||||
]]
|
||||
|
||||
return proto
|
||||
72
examples/share.lua
Normal file
72
examples/share.lua
Normal file
@@ -0,0 +1,72 @@
|
||||
local skynet = require "skynet"
|
||||
local sharedata = require "sharedata"
|
||||
|
||||
local mode = ...
|
||||
|
||||
if mode == "host" then
|
||||
|
||||
skynet.start(function()
|
||||
skynet.error("new foobar")
|
||||
sharedata.new("foobar", { a=1, b= { "hello", "world" } })
|
||||
|
||||
skynet.fork(function()
|
||||
skynet.sleep(200) -- sleep 2s
|
||||
skynet.error("update foobar a = 2")
|
||||
sharedata.update("foobar", { a =2 })
|
||||
skynet.sleep(200) -- sleep 2s
|
||||
skynet.error("update foobar a = 3")
|
||||
sharedata.update("foobar", { a = 3, b = { "change" } })
|
||||
skynet.sleep(100)
|
||||
skynet.error("delete foobar")
|
||||
sharedata.delete "foobar"
|
||||
end)
|
||||
end)
|
||||
|
||||
else
|
||||
|
||||
|
||||
skynet.start(function()
|
||||
skynet.newservice(SERVICE_NAME, "host")
|
||||
|
||||
local obj = sharedata.query "foobar"
|
||||
|
||||
local b = obj.b
|
||||
skynet.error(string.format("a=%d", obj.a))
|
||||
|
||||
for k,v in ipairs(b) do
|
||||
skynet.error(string.format("b[%d]=%s", k,v))
|
||||
end
|
||||
|
||||
for i = 1, 5 do
|
||||
skynet.sleep(100)
|
||||
skynet.error("second " ..i)
|
||||
for k,v in pairs(obj) do
|
||||
skynet.error(string.format("%s = %s", k , tostring(v)))
|
||||
end
|
||||
end
|
||||
|
||||
local ok, err = pcall(function()
|
||||
local tmp = { b[1], b[2] } -- b is invalid , so pcall should failed
|
||||
end)
|
||||
|
||||
if not ok then
|
||||
skynet.error(err)
|
||||
end
|
||||
|
||||
-- obj. b is not the same with local b
|
||||
for k,v in ipairs(obj.b) do
|
||||
skynet.error(string.format("b[%d] = %s", k , tostring(v)))
|
||||
end
|
||||
|
||||
collectgarbage()
|
||||
skynet.error("sleep")
|
||||
skynet.sleep(100)
|
||||
b = nil
|
||||
collectgarbage()
|
||||
skynet.error("sleep")
|
||||
skynet.sleep(100)
|
||||
|
||||
skynet.exit()
|
||||
end)
|
||||
|
||||
end
|
||||
@@ -66,6 +66,7 @@ skynet.start(function()
|
||||
end
|
||||
local balance = 1
|
||||
local id = socket.listen("0.0.0.0", 8001)
|
||||
skynet.error("Listen web port 8001")
|
||||
socket.start(id , function(id, addr)
|
||||
skynet.error(string.format("%s connected, pass it to agent :%08x", addr, agent[balance]))
|
||||
skynet.send(agent[balance], "lua", id)
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
package.path = "./examples/?.lua;" .. package.path
|
||||
|
||||
local skynet = require "skynet"
|
||||
local netpack = require "netpack"
|
||||
local proto = require "proto"
|
||||
|
||||
local CMD = {}
|
||||
local SOCKET = {}
|
||||
@@ -7,8 +10,9 @@ local gate
|
||||
local agent = {}
|
||||
|
||||
function SOCKET.open(fd, addr)
|
||||
skynet.error("New client from : " .. addr)
|
||||
agent[fd] = skynet.newservice("agent")
|
||||
skynet.call(agent[fd], "lua", "start", gate, fd)
|
||||
skynet.call(agent[fd], "lua", "start", gate, fd, proto)
|
||||
end
|
||||
|
||||
local function close_agent(fd)
|
||||
|
||||
311
lualib-src/lsha1.c
Normal file
311
lualib-src/lsha1.c
Normal file
@@ -0,0 +1,311 @@
|
||||
/*
|
||||
SHA-1 in C
|
||||
By Steve Reid <sreid@sea-to-sky.net>
|
||||
100% Public Domain
|
||||
|
||||
-----------------
|
||||
Modified 7/98
|
||||
By James H. Brown <jbrown@burgoyne.com>
|
||||
Still 100% Public Domain
|
||||
|
||||
Corrected a problem which generated improper hash values on 16 bit machines
|
||||
Routine SHA1Update changed from
|
||||
void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int
|
||||
len)
|
||||
to
|
||||
void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned
|
||||
long len)
|
||||
|
||||
The 'len' parameter was declared an int which works fine on 32 bit machines.
|
||||
However, on 16 bit machines an int is too small for the shifts being done
|
||||
against
|
||||
it. This caused the hash function to generate incorrect values if len was
|
||||
greater than 8191 (8K - 1) due to the 'len << 3' on line 3 of SHA1Update().
|
||||
|
||||
Since the file IO in main() reads 16K at a time, any file 8K or larger would
|
||||
be guaranteed to generate the wrong hash (e.g. Test Vector #3, a million
|
||||
"a"s).
|
||||
|
||||
I also changed the declaration of variables i & j in SHA1Update to
|
||||
unsigned long from unsigned int for the same reason.
|
||||
|
||||
These changes should make no difference to any 32 bit implementations since
|
||||
an
|
||||
int and a long are the same size in those environments.
|
||||
|
||||
--
|
||||
I also corrected a few compiler warnings generated by Borland C.
|
||||
1. Added #include <process.h> for exit() prototype
|
||||
2. Removed unused variable 'j' in SHA1Final
|
||||
3. Changed exit(0) to return(0) at end of main.
|
||||
|
||||
ALL changes I made can be located by searching for comments containing 'JHB'
|
||||
-----------------
|
||||
Modified 8/98
|
||||
By Steve Reid <sreid@sea-to-sky.net>
|
||||
Still 100% public domain
|
||||
|
||||
1- Removed #include <process.h> and used return() instead of exit()
|
||||
2- Fixed overwriting of finalcount in SHA1Final() (discovered by Chris Hall)
|
||||
3- Changed email address from steve@edmweb.com to sreid@sea-to-sky.net
|
||||
|
||||
-----------------
|
||||
Modified 4/01
|
||||
By Saul Kravitz <Saul.Kravitz@celera.com>
|
||||
Still 100% PD
|
||||
Modified to run on Compaq Alpha hardware.
|
||||
|
||||
-----------------
|
||||
Modified 07/2002
|
||||
By Ralph Giles <giles@ghostscript.com>
|
||||
Still 100% public domain
|
||||
modified for use with stdint types, autoconf
|
||||
code cleanup, removed attribution comments
|
||||
switched SHA1Final() argument order for consistency
|
||||
use SHA1_ prefix for public api
|
||||
move public api to sha1.h
|
||||
|
||||
-----------------
|
||||
Modufiled 08/2014
|
||||
By Cloud Wu <cloudwu@gmail.com>
|
||||
Still 100% PD
|
||||
Lua binding
|
||||
*/
|
||||
|
||||
/*
|
||||
Test Vectors (from FIPS PUB 180-1)
|
||||
"abc"
|
||||
A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
|
||||
"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
|
||||
84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
|
||||
A million repetitions of "a"
|
||||
34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct {
|
||||
uint32_t state[5];
|
||||
uint32_t count[2];
|
||||
uint8_t buffer[64];
|
||||
} SHA1_CTX;
|
||||
|
||||
#define SHA1_DIGEST_SIZE 20
|
||||
|
||||
static void SHA1_Transform(uint32_t state[5], const uint8_t buffer[64]);
|
||||
|
||||
#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
|
||||
|
||||
/* blk0() and blk() perform the initial expand. */
|
||||
/* I got the idea of expanding during the round function from SSLeay */
|
||||
/* FIXME: can we do this in an endian-proof way? */
|
||||
#ifdef WORDS_BIGENDIAN
|
||||
#define blk0(i) block.l[i]
|
||||
#else
|
||||
#define blk0(i) (block.l[i] = (rol(block.l[i],24)&0xFF00FF00) \
|
||||
|(rol(block.l[i],8)&0x00FF00FF))
|
||||
#endif
|
||||
#define blk(i) (block.l[i&15] = rol(block.l[(i+13)&15]^block.l[(i+8)&15] \
|
||||
^block.l[(i+2)&15]^block.l[i&15],1))
|
||||
|
||||
/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
|
||||
#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
|
||||
#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
|
||||
#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
|
||||
#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
|
||||
#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
|
||||
|
||||
|
||||
/* Hash a single 512-bit block. This is the core of the algorithm. */
|
||||
static void SHA1_Transform(uint32_t state[5], const uint8_t buffer[64])
|
||||
{
|
||||
uint32_t a, b, c, d, e;
|
||||
typedef union {
|
||||
uint8_t c[64];
|
||||
uint32_t l[16];
|
||||
} CHAR64LONG16;
|
||||
CHAR64LONG16 block;
|
||||
|
||||
memcpy(&block, buffer, 64);
|
||||
|
||||
/* Copy context->state[] to working vars */
|
||||
a = state[0];
|
||||
b = state[1];
|
||||
c = state[2];
|
||||
d = state[3];
|
||||
e = state[4];
|
||||
|
||||
/* 4 rounds of 20 operations each. Loop unrolled. */
|
||||
R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
|
||||
R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
|
||||
R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
|
||||
R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
|
||||
R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
|
||||
R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
|
||||
R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
|
||||
R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
|
||||
R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
|
||||
R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
|
||||
R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
|
||||
R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
|
||||
R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
|
||||
R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
|
||||
R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
|
||||
R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
|
||||
R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
|
||||
R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
|
||||
R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
|
||||
R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
|
||||
|
||||
/* Add the working vars back into context.state[] */
|
||||
state[0] += a;
|
||||
state[1] += b;
|
||||
state[2] += c;
|
||||
state[3] += d;
|
||||
state[4] += e;
|
||||
|
||||
/* Wipe variables */
|
||||
a = b = c = d = e = 0;
|
||||
}
|
||||
|
||||
|
||||
/* SHA1Init - Initialize new context */
|
||||
static void sat_SHA1_Init(SHA1_CTX* context)
|
||||
{
|
||||
/* SHA1 initialization constants */
|
||||
context->state[0] = 0x67452301;
|
||||
context->state[1] = 0xEFCDAB89;
|
||||
context->state[2] = 0x98BADCFE;
|
||||
context->state[3] = 0x10325476;
|
||||
context->state[4] = 0xC3D2E1F0;
|
||||
context->count[0] = context->count[1] = 0;
|
||||
}
|
||||
|
||||
|
||||
/* Run your data through this. */
|
||||
static void sat_SHA1_Update(SHA1_CTX* context, const uint8_t* data, const size_t len)
|
||||
{
|
||||
size_t i, j;
|
||||
|
||||
#ifdef VERBOSE
|
||||
SHAPrintContext(context, "before");
|
||||
#endif
|
||||
|
||||
j = (context->count[0] >> 3) & 63;
|
||||
if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++;
|
||||
context->count[1] += (len >> 29);
|
||||
if ((j + len) > 63) {
|
||||
memcpy(&context->buffer[j], data, (i = 64-j));
|
||||
SHA1_Transform(context->state, context->buffer);
|
||||
for ( ; i + 63 < len; i += 64) {
|
||||
SHA1_Transform(context->state, data + i);
|
||||
}
|
||||
j = 0;
|
||||
}
|
||||
else i = 0;
|
||||
memcpy(&context->buffer[j], &data[i], len - i);
|
||||
|
||||
#ifdef VERBOSE
|
||||
SHAPrintContext(context, "after ");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/* Add padding and return the message digest. */
|
||||
static void sat_SHA1_Final(SHA1_CTX* context, uint8_t digest[SHA1_DIGEST_SIZE])
|
||||
{
|
||||
uint32_t i;
|
||||
uint8_t finalcount[8];
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
|
||||
>> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */
|
||||
}
|
||||
sat_SHA1_Update(context, (uint8_t *)"\200", 1);
|
||||
while ((context->count[0] & 504) != 448) {
|
||||
sat_SHA1_Update(context, (uint8_t *)"\0", 1);
|
||||
}
|
||||
sat_SHA1_Update(context, finalcount, 8); /* Should cause a SHA1_Transform() */
|
||||
for (i = 0; i < SHA1_DIGEST_SIZE; i++) {
|
||||
digest[i] = (uint8_t)
|
||||
((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
|
||||
}
|
||||
|
||||
/* Wipe variables */
|
||||
i = 0;
|
||||
memset(context->buffer, 0, 64);
|
||||
memset(context->state, 0, 20);
|
||||
memset(context->count, 0, 8);
|
||||
memset(finalcount, 0, 8); /* SWR */
|
||||
}
|
||||
|
||||
#include <lua.h>
|
||||
#include <lauxlib.h>
|
||||
|
||||
int
|
||||
lsha1(lua_State *L) {
|
||||
size_t sz = 0;
|
||||
const uint8_t * buffer = (const uint8_t *)luaL_checklstring(L, 1, &sz);
|
||||
uint8_t digest[SHA1_DIGEST_SIZE];
|
||||
SHA1_CTX ctx;
|
||||
sat_SHA1_Init(&ctx);
|
||||
sat_SHA1_Update(&ctx, buffer, sz);
|
||||
sat_SHA1_Final(&ctx, digest);
|
||||
lua_pushlstring(L, (const char *)digest, SHA1_DIGEST_SIZE);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
#define BLOCKSIZE 64
|
||||
|
||||
static inline void
|
||||
xor_key(uint8_t key[BLOCKSIZE], uint32_t xor) {
|
||||
int i;
|
||||
for (i=0;i<BLOCKSIZE;i+=sizeof(uint32_t)) {
|
||||
uint32_t * k = (uint32_t *)&key[i];
|
||||
*k ^= xor;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
lhmac_sha1(lua_State *L) {
|
||||
size_t key_sz = 0;
|
||||
const uint8_t * key = (const uint8_t *)luaL_checklstring(L, 1, &key_sz);
|
||||
size_t text_sz = 0;
|
||||
const uint8_t * text = (const uint8_t *)luaL_checklstring(L, 2, &text_sz);
|
||||
SHA1_CTX ctx1, ctx2;
|
||||
uint8_t digest1[SHA1_DIGEST_SIZE];
|
||||
uint8_t digest2[SHA1_DIGEST_SIZE];
|
||||
uint8_t rkey[BLOCKSIZE];
|
||||
memset(rkey, 0, BLOCKSIZE);
|
||||
|
||||
if (key_sz > BLOCKSIZE) {
|
||||
SHA1_CTX ctx;
|
||||
sat_SHA1_Init(&ctx);
|
||||
sat_SHA1_Update(&ctx, key, key_sz);
|
||||
sat_SHA1_Final(&ctx, rkey);
|
||||
key_sz = SHA1_DIGEST_SIZE;
|
||||
} else {
|
||||
memcpy(rkey, key, key_sz);
|
||||
}
|
||||
|
||||
xor_key(rkey, 0x5c5c5c5c);
|
||||
sat_SHA1_Init(&ctx1);
|
||||
sat_SHA1_Update(&ctx1, rkey, BLOCKSIZE);
|
||||
|
||||
xor_key(rkey, 0x5c5c5c5c ^ 0x36363636);
|
||||
sat_SHA1_Init(&ctx2);
|
||||
sat_SHA1_Update(&ctx2, rkey, BLOCKSIZE);
|
||||
sat_SHA1_Update(&ctx2, text, text_sz);
|
||||
sat_SHA1_Final(&ctx2, digest2);
|
||||
|
||||
sat_SHA1_Update(&ctx1, digest2, SHA1_DIGEST_SIZE);
|
||||
sat_SHA1_Final(&ctx1, digest1);
|
||||
|
||||
lua_pushlstring(L, (const char *)digest1, SHA1_DIGEST_SIZE);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -203,7 +203,7 @@ lunpackresponse(lua_State *L) {
|
||||
}
|
||||
|
||||
int
|
||||
luaopen_cluster_c(lua_State *L) {
|
||||
luaopen_cluster_core(lua_State *L) {
|
||||
luaL_Reg l[] = {
|
||||
{ "packrequest", lpackrequest },
|
||||
{ "unpackrequest", lunpackrequest },
|
||||
|
||||
@@ -589,11 +589,11 @@ read64(lua_State *L, uint32_t xx[2], uint32_t yy[2]) {
|
||||
size_t sz = 0;
|
||||
const uint8_t *x = (const uint8_t *)luaL_checklstring(L, 1, &sz);
|
||||
if (sz != 8) {
|
||||
luaL_error(L, "Invalid hmac x");
|
||||
luaL_error(L, "Invalid uint64 x");
|
||||
}
|
||||
const uint8_t *y = (const uint8_t *)luaL_checklstring(L, 2, &sz);
|
||||
if (sz != 8) {
|
||||
luaL_error(L, "Invalid hmac y");
|
||||
luaL_error(L, "Invalid uint64 y");
|
||||
}
|
||||
xx[0] = x[0] | x[1]<<8 | x[2]<<16 | x[3]<<24;
|
||||
xx[1] = x[4] | x[5]<<8 | x[6]<<16 | x[7]<<24;
|
||||
@@ -703,7 +703,7 @@ ldhexchange(lua_State *L) {
|
||||
size_t sz = 0;
|
||||
const uint8_t *x = (const uint8_t *)luaL_checklstring(L, 1, &sz);
|
||||
if (sz != 8) {
|
||||
luaL_error(L, "Invalid hmac x");
|
||||
luaL_error(L, "Invalid dh uint64 key");
|
||||
}
|
||||
uint32_t xx[2];
|
||||
xx[0] = x[0] | x[1]<<8 | x[2]<<16 | x[3]<<24;
|
||||
@@ -836,6 +836,10 @@ lb64decode(lua_State *L) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// defined in lsha1.c
|
||||
int lsha1(lua_State *L);
|
||||
int lhmac_sha1(lua_State *L);
|
||||
|
||||
int
|
||||
luaopen_crypt(lua_State *L) {
|
||||
luaL_checkversion(L);
|
||||
@@ -852,6 +856,8 @@ luaopen_crypt(lua_State *L) {
|
||||
{ "dhsecret", ldhsecret },
|
||||
{ "base64encode", lb64encode },
|
||||
{ "base64decode", lb64decode },
|
||||
{ "sha1", lsha1 },
|
||||
{ "hmac_sha1", lhmac_sha1 },
|
||||
{ NULL, NULL },
|
||||
};
|
||||
luaL_newlib(L,l);
|
||||
|
||||
@@ -507,8 +507,8 @@ op_insert(lua_State *L) {
|
||||
for (i=1;i<=s;i++) {
|
||||
lua_rawgeti(L,3,i);
|
||||
document doc = lua_touserdata(L,-1);
|
||||
lua_pop(L,1); // must call lua_pop before luaL_addlstring, because addlstring may change stack top
|
||||
luaL_addlstring(&b, (const char *)doc, get_length(doc));
|
||||
lua_pop(L,1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -149,7 +149,7 @@ mc_nextid(lua_State *L) {
|
||||
}
|
||||
|
||||
int
|
||||
luaopen_multicast_c(lua_State *L) {
|
||||
luaopen_multicast_core(lua_State *L) {
|
||||
luaL_Reg l[] = {
|
||||
{ "pack", mc_packlocal },
|
||||
{ "unpack", mc_unpacklocal },
|
||||
|
||||
@@ -240,12 +240,19 @@ filter_data_(lua_State *L, int fd, uint8_t * buffer, int size) {
|
||||
buffer += need;
|
||||
size -= need;
|
||||
if (size == 0) {
|
||||
lua_pushvalue(L, lua_upvalueindex(TYPE_DATA));
|
||||
lua_pushinteger(L, fd);
|
||||
lua_pushlightuserdata(L, uc->pack.buffer);
|
||||
lua_pushinteger(L, uc->pack.size);
|
||||
skynet_free(uc);
|
||||
return 5;
|
||||
if (q == NULL || q->head == q->tail ) {
|
||||
lua_pushvalue(L, lua_upvalueindex(TYPE_DATA));
|
||||
lua_pushinteger(L, fd);
|
||||
lua_pushlightuserdata(L, uc->pack.buffer);
|
||||
lua_pushinteger(L, uc->pack.size);
|
||||
skynet_free(uc);
|
||||
return 5;
|
||||
}
|
||||
else{
|
||||
push_data(L, fd, uc->pack.buffer, uc->pack.size, 0);
|
||||
skynet_free(uc);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
// more data
|
||||
push_data(L, fd, uc->pack.buffer, uc->pack.size, 0);
|
||||
@@ -274,13 +281,21 @@ filter_data_(lua_State *L, int fd, uint8_t * buffer, int size) {
|
||||
}
|
||||
if (size == pack_size) {
|
||||
// just one package
|
||||
lua_pushvalue(L, lua_upvalueindex(TYPE_DATA));
|
||||
lua_pushinteger(L, fd);
|
||||
void * result = skynet_malloc(pack_size);
|
||||
memcpy(result, buffer, size);
|
||||
lua_pushlightuserdata(L, result);
|
||||
lua_pushinteger(L, size);
|
||||
return 5;
|
||||
if ( q == NULL || q->head == q->tail) {
|
||||
lua_pushvalue(L, lua_upvalueindex(TYPE_DATA));
|
||||
lua_pushinteger(L, fd);
|
||||
void * result = skynet_malloc(pack_size);
|
||||
memcpy(result, buffer, size);
|
||||
lua_pushlightuserdata(L, result);
|
||||
lua_pushinteger(L, size);
|
||||
return 5;
|
||||
}
|
||||
else{
|
||||
push_data(L, fd, buffer, pack_size, 1);
|
||||
buffer += pack_size;
|
||||
size -= pack_size;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
// more data
|
||||
push_data(L, fd, buffer, pack_size, 1);
|
||||
@@ -302,9 +317,9 @@ filter_data(lua_State *L, int fd, uint8_t * buffer, int size) {
|
||||
}
|
||||
|
||||
static void
|
||||
pushstring(lua_State *L, const char * msg) {
|
||||
pushstring(lua_State *L, const char * msg, int size) {
|
||||
if (msg) {
|
||||
lua_pushstring(L, msg);
|
||||
lua_pushlstring(L, msg, size);
|
||||
} else {
|
||||
lua_pushliteral(L, "");
|
||||
}
|
||||
@@ -350,12 +365,12 @@ lfilter(lua_State *L) {
|
||||
lua_pushvalue(L, lua_upvalueindex(TYPE_OPEN));
|
||||
// ignore listen id (message->id);
|
||||
lua_pushinteger(L, message->ud);
|
||||
pushstring(L, buffer);
|
||||
pushstring(L, buffer, size);
|
||||
return 4;
|
||||
case SKYNET_SOCKET_TYPE_ERROR:
|
||||
lua_pushvalue(L, lua_upvalueindex(TYPE_ERROR));
|
||||
lua_pushinteger(L, message->id);
|
||||
pushstring(L, buffer);
|
||||
pushstring(L, buffer, size);
|
||||
return 4;
|
||||
default:
|
||||
// never get here
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
https://github.com/cloudwu/lua-serialize
|
||||
modify from https://github.com/cloudwu/lua-serialize
|
||||
*/
|
||||
|
||||
#include "skynet_malloc.h"
|
||||
@@ -35,14 +35,13 @@ struct block {
|
||||
|
||||
struct write_block {
|
||||
struct block * head;
|
||||
int len;
|
||||
struct block * current;
|
||||
int len;
|
||||
int ptr;
|
||||
};
|
||||
|
||||
struct read_block {
|
||||
char * buffer;
|
||||
struct block * current;
|
||||
int len;
|
||||
int ptr;
|
||||
};
|
||||
@@ -78,38 +77,17 @@ _again:
|
||||
|
||||
static void
|
||||
wb_init(struct write_block *wb , struct block *b) {
|
||||
if (b==NULL) {
|
||||
wb->head = blk_alloc();
|
||||
wb->len = 0;
|
||||
wb->current = wb->head;
|
||||
wb->ptr = 0;
|
||||
wb_push(wb, &wb->len, sizeof(wb->len));
|
||||
} else {
|
||||
wb->head = b;
|
||||
int * plen = (int *)b->buffer;
|
||||
int sz = *plen;
|
||||
wb->len = sz;
|
||||
while (b->next) {
|
||||
sz -= BLOCK_SIZE;
|
||||
b = b->next;
|
||||
}
|
||||
wb->current = b;
|
||||
wb->ptr = sz;
|
||||
}
|
||||
}
|
||||
|
||||
static struct block *
|
||||
wb_close(struct write_block *b) {
|
||||
b->current = b->head;
|
||||
b->ptr = 0;
|
||||
wb_push(b, &b->len, sizeof(b->len));
|
||||
b->current = NULL;
|
||||
return b->head;
|
||||
wb->head = b;
|
||||
assert(b->next == NULL);
|
||||
wb->len = 0;
|
||||
wb->current = wb->head;
|
||||
wb->ptr = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
wb_free(struct write_block *wb) {
|
||||
struct block *blk = wb->head;
|
||||
blk = blk->next; // the first block is on stack
|
||||
while (blk) {
|
||||
struct block * next = blk->next;
|
||||
skynet_free(blk);
|
||||
@@ -124,7 +102,6 @@ wb_free(struct write_block *wb) {
|
||||
static void
|
||||
rball_init(struct read_block * rb, char * buffer, int size) {
|
||||
rb->buffer = buffer;
|
||||
rb->current = NULL;
|
||||
rb->len = size;
|
||||
rb->ptr = 0;
|
||||
}
|
||||
@@ -135,98 +112,45 @@ rb_read(struct read_block *rb, void *buffer, int sz) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (rb->buffer) {
|
||||
int ptr = rb->ptr;
|
||||
rb->ptr += sz;
|
||||
rb->len -= sz;
|
||||
return rb->buffer + ptr;
|
||||
}
|
||||
|
||||
if (rb->ptr == BLOCK_SIZE) {
|
||||
struct block * next = rb->current->next;
|
||||
skynet_free(rb->current);
|
||||
rb->current = next;
|
||||
rb->ptr = 0;
|
||||
}
|
||||
|
||||
int copy = BLOCK_SIZE - rb->ptr;
|
||||
|
||||
if (sz <= copy) {
|
||||
void * ret = rb->current->buffer + rb->ptr;
|
||||
rb->ptr += sz;
|
||||
rb->len -= sz;
|
||||
return ret;
|
||||
}
|
||||
|
||||
char * tmp = buffer;
|
||||
|
||||
memcpy(tmp, rb->current->buffer + rb->ptr, copy);
|
||||
sz -= copy;
|
||||
tmp += copy;
|
||||
rb->len -= copy;
|
||||
|
||||
for (;;) {
|
||||
struct block * next = rb->current->next;
|
||||
skynet_free(rb->current);
|
||||
rb->current = next;
|
||||
|
||||
if (sz < BLOCK_SIZE) {
|
||||
memcpy(tmp, rb->current->buffer, sz);
|
||||
rb->ptr = sz;
|
||||
rb->len -= sz;
|
||||
return buffer;
|
||||
}
|
||||
memcpy(tmp, rb->current->buffer, BLOCK_SIZE);
|
||||
sz -= BLOCK_SIZE;
|
||||
tmp += BLOCK_SIZE;
|
||||
rb->len -= BLOCK_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
rb_close(struct read_block *rb) {
|
||||
while (rb->current) {
|
||||
struct block * next = rb->current->next;
|
||||
skynet_free(rb->current);
|
||||
rb->current = next;
|
||||
}
|
||||
rb->len = 0;
|
||||
rb->ptr = 0;
|
||||
int ptr = rb->ptr;
|
||||
rb->ptr += sz;
|
||||
rb->len -= sz;
|
||||
return rb->buffer + ptr;
|
||||
}
|
||||
|
||||
static inline void
|
||||
wb_nil(struct write_block *wb) {
|
||||
int n = TYPE_NIL;
|
||||
uint8_t n = TYPE_NIL;
|
||||
wb_push(wb, &n, 1);
|
||||
}
|
||||
|
||||
static inline void
|
||||
wb_boolean(struct write_block *wb, int boolean) {
|
||||
int n = COMBINE_TYPE(TYPE_BOOLEAN , boolean ? 1 : 0);
|
||||
uint8_t n = COMBINE_TYPE(TYPE_BOOLEAN , boolean ? 1 : 0);
|
||||
wb_push(wb, &n, 1);
|
||||
}
|
||||
|
||||
static inline void
|
||||
wb_integer(struct write_block *wb, int v, int type) {
|
||||
if (v == 0) {
|
||||
int n = COMBINE_TYPE(type , 0);
|
||||
uint8_t n = COMBINE_TYPE(type , 0);
|
||||
wb_push(wb, &n, 1);
|
||||
} else if (v<0) {
|
||||
int n = COMBINE_TYPE(type , 4);
|
||||
uint8_t n = COMBINE_TYPE(type , 4);
|
||||
wb_push(wb, &n, 1);
|
||||
wb_push(wb, &v, 4);
|
||||
} else if (v<0x100) {
|
||||
int n = COMBINE_TYPE(type , 1);
|
||||
uint8_t n = COMBINE_TYPE(type , 1);
|
||||
wb_push(wb, &n, 1);
|
||||
uint8_t byte = (uint8_t)v;
|
||||
wb_push(wb, &byte, 1);
|
||||
} else if (v<0x10000) {
|
||||
int n = COMBINE_TYPE(type , 2);
|
||||
uint8_t n = COMBINE_TYPE(type , 2);
|
||||
wb_push(wb, &n, 1);
|
||||
uint16_t word = (uint16_t)v;
|
||||
wb_push(wb, &word, 2);
|
||||
} else {
|
||||
int n = COMBINE_TYPE(type , 4);
|
||||
uint8_t n = COMBINE_TYPE(type , 4);
|
||||
wb_push(wb, &n, 1);
|
||||
wb_push(wb, &v, 4);
|
||||
}
|
||||
@@ -234,14 +158,14 @@ wb_integer(struct write_block *wb, int v, int type) {
|
||||
|
||||
static inline void
|
||||
wb_number(struct write_block *wb, double v) {
|
||||
int n = COMBINE_TYPE(TYPE_NUMBER , 8);
|
||||
uint8_t n = COMBINE_TYPE(TYPE_NUMBER , 8);
|
||||
wb_push(wb, &n, 1);
|
||||
wb_push(wb, &v, 8);
|
||||
}
|
||||
|
||||
static inline void
|
||||
wb_pointer(struct write_block *wb, void *v) {
|
||||
int n = TYPE_USERDATA;
|
||||
uint8_t n = TYPE_USERDATA;
|
||||
wb_push(wb, &n, 1);
|
||||
wb_push(wb, &v, sizeof(v));
|
||||
}
|
||||
@@ -249,7 +173,7 @@ wb_pointer(struct write_block *wb, void *v) {
|
||||
static inline void
|
||||
wb_string(struct write_block *wb, const char *str, int len) {
|
||||
if (len < MAX_COOKIE) {
|
||||
int n = COMBINE_TYPE(TYPE_SHORT_STRING, len);
|
||||
uint8_t n = COMBINE_TYPE(TYPE_SHORT_STRING, len);
|
||||
wb_push(wb, &n, 1);
|
||||
if (len > 0) {
|
||||
wb_push(wb, str, len);
|
||||
@@ -271,24 +195,24 @@ wb_string(struct write_block *wb, const char *str, int len) {
|
||||
}
|
||||
}
|
||||
|
||||
static void _pack_one(lua_State *L, struct write_block *b, int index, int depth);
|
||||
static void pack_one(lua_State *L, struct write_block *b, int index, int depth);
|
||||
|
||||
static int
|
||||
wb_table_array(lua_State *L, struct write_block * wb, int index, int depth) {
|
||||
int array_size = lua_rawlen(L,index);
|
||||
if (array_size >= MAX_COOKIE-1) {
|
||||
int n = COMBINE_TYPE(TYPE_TABLE, MAX_COOKIE-1);
|
||||
uint8_t n = COMBINE_TYPE(TYPE_TABLE, MAX_COOKIE-1);
|
||||
wb_push(wb, &n, 1);
|
||||
wb_integer(wb, array_size,TYPE_NUMBER);
|
||||
} else {
|
||||
int n = COMBINE_TYPE(TYPE_TABLE, array_size);
|
||||
uint8_t n = COMBINE_TYPE(TYPE_TABLE, array_size);
|
||||
wb_push(wb, &n, 1);
|
||||
}
|
||||
|
||||
int i;
|
||||
for (i=1;i<=array_size;i++) {
|
||||
lua_rawgeti(L,index,i);
|
||||
_pack_one(L, wb, -1, depth);
|
||||
pack_one(L, wb, -1, depth);
|
||||
lua_pop(L,1);
|
||||
}
|
||||
|
||||
@@ -307,8 +231,8 @@ wb_table_hash(lua_State *L, struct write_block * wb, int index, int depth, int a
|
||||
continue;
|
||||
}
|
||||
}
|
||||
_pack_one(L,wb,-2,depth);
|
||||
_pack_one(L,wb,-1,depth);
|
||||
pack_one(L,wb,-2,depth);
|
||||
pack_one(L,wb,-1,depth);
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
wb_nil(wb);
|
||||
@@ -324,7 +248,7 @@ wb_table(lua_State *L, struct write_block *wb, int index, int depth) {
|
||||
}
|
||||
|
||||
static void
|
||||
_pack_one(lua_State *L, struct write_block *b, int index, int depth) {
|
||||
pack_one(lua_State *L, struct write_block *b, int index, int depth) {
|
||||
if (depth > MAX_DEPTH) {
|
||||
wb_free(b);
|
||||
luaL_error(L, "serialize can't pack too depth table");
|
||||
@@ -370,27 +294,24 @@ _pack_one(lua_State *L, struct write_block *b, int index, int depth) {
|
||||
}
|
||||
|
||||
static void
|
||||
_pack_from(lua_State *L, struct write_block *b, int from) {
|
||||
pack_from(lua_State *L, struct write_block *b, int from) {
|
||||
int n = lua_gettop(L) - from;
|
||||
int i;
|
||||
for (i=1;i<=n;i++) {
|
||||
_pack_one(L, b , from + i, 0);
|
||||
pack_one(L, b , from + i, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
__invalid_stream(lua_State *L, struct read_block *rb, int line) {
|
||||
invalid_stream_line(lua_State *L, struct read_block *rb, int line) {
|
||||
int len = rb->len;
|
||||
if (rb->buffer == NULL) {
|
||||
rb_close(rb);
|
||||
}
|
||||
luaL_error(L, "Invalid serialize stream %d (line:%d)", len, line);
|
||||
}
|
||||
|
||||
#define _invalid_stream(L,rb) __invalid_stream(L,rb,__LINE__)
|
||||
#define invalid_stream(L,rb) invalid_stream_line(L,rb,__LINE__)
|
||||
|
||||
static int
|
||||
_get_integer(lua_State *L, struct read_block *rb, int cookie) {
|
||||
get_integer(lua_State *L, struct read_block *rb, int cookie) {
|
||||
switch (cookie) {
|
||||
case 0:
|
||||
return 0;
|
||||
@@ -398,93 +319,93 @@ _get_integer(lua_State *L, struct read_block *rb, int cookie) {
|
||||
uint8_t n = 0;
|
||||
uint8_t * pn = rb_read(rb,&n,1);
|
||||
if (pn == NULL)
|
||||
_invalid_stream(L,rb);
|
||||
invalid_stream(L,rb);
|
||||
return *pn;
|
||||
}
|
||||
case 2: {
|
||||
uint16_t n = 0;
|
||||
uint16_t * pn = rb_read(rb,&n,2);
|
||||
if (pn == NULL)
|
||||
_invalid_stream(L,rb);
|
||||
invalid_stream(L,rb);
|
||||
return *pn;
|
||||
}
|
||||
case 4: {
|
||||
int n = 0;
|
||||
int * pn = rb_read(rb,&n,4);
|
||||
if (pn == NULL)
|
||||
_invalid_stream(L,rb);
|
||||
invalid_stream(L,rb);
|
||||
return *pn;
|
||||
}
|
||||
default:
|
||||
_invalid_stream(L,rb);
|
||||
invalid_stream(L,rb);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static double
|
||||
_get_number(lua_State *L, struct read_block *rb, int cookie) {
|
||||
get_number(lua_State *L, struct read_block *rb, int cookie) {
|
||||
if (cookie == 8) {
|
||||
double n = 0;
|
||||
double * pn = rb_read(rb,&n,8);
|
||||
if (pn == NULL)
|
||||
_invalid_stream(L,rb);
|
||||
invalid_stream(L,rb);
|
||||
return *pn;
|
||||
} else {
|
||||
return (double)_get_integer(L,rb,cookie);
|
||||
return (double)get_integer(L,rb,cookie);
|
||||
}
|
||||
}
|
||||
|
||||
static void *
|
||||
_get_pointer(lua_State *L, struct read_block *rb) {
|
||||
get_pointer(lua_State *L, struct read_block *rb) {
|
||||
void * userdata = 0;
|
||||
void ** v = (void **)rb_read(rb,&userdata,sizeof(userdata));
|
||||
if (v == NULL) {
|
||||
_invalid_stream(L,rb);
|
||||
invalid_stream(L,rb);
|
||||
}
|
||||
return *v;
|
||||
}
|
||||
|
||||
static void
|
||||
_get_buffer(lua_State *L, struct read_block *rb, int len) {
|
||||
get_buffer(lua_State *L, struct read_block *rb, int len) {
|
||||
char tmp[len];
|
||||
char * p = rb_read(rb,tmp,len);
|
||||
if (p == NULL) {
|
||||
_invalid_stream(L,rb);
|
||||
invalid_stream(L,rb);
|
||||
}
|
||||
lua_pushlstring(L,p,len);
|
||||
}
|
||||
|
||||
static void _unpack_one(lua_State *L, struct read_block *rb);
|
||||
static void unpack_one(lua_State *L, struct read_block *rb);
|
||||
|
||||
static void
|
||||
_unpack_table(lua_State *L, struct read_block *rb, int array_size) {
|
||||
unpack_table(lua_State *L, struct read_block *rb, int array_size) {
|
||||
if (array_size == MAX_COOKIE-1) {
|
||||
uint8_t type = 0;
|
||||
uint8_t *t = rb_read(rb, &type, 1);
|
||||
if (t==NULL || (*t & 7) != TYPE_NUMBER) {
|
||||
_invalid_stream(L,rb);
|
||||
invalid_stream(L,rb);
|
||||
}
|
||||
array_size = (int)_get_number(L,rb,*t >> 3);
|
||||
array_size = (int)get_number(L,rb,*t >> 3);
|
||||
}
|
||||
lua_createtable(L,array_size,0);
|
||||
int i;
|
||||
for (i=1;i<=array_size;i++) {
|
||||
_unpack_one(L,rb);
|
||||
unpack_one(L,rb);
|
||||
lua_rawseti(L,-2,i);
|
||||
}
|
||||
for (;;) {
|
||||
_unpack_one(L,rb);
|
||||
unpack_one(L,rb);
|
||||
if (lua_isnil(L,-1)) {
|
||||
lua_pop(L,1);
|
||||
return;
|
||||
}
|
||||
_unpack_one(L,rb);
|
||||
unpack_one(L,rb);
|
||||
lua_rawset(L,-3);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
_push_value(lua_State *L, struct read_block *rb, int type, int cookie) {
|
||||
push_value(lua_State *L, struct read_block *rb, int type, int cookie) {
|
||||
switch(type) {
|
||||
case TYPE_NIL:
|
||||
lua_pushnil(L);
|
||||
@@ -493,82 +414,69 @@ _push_value(lua_State *L, struct read_block *rb, int type, int cookie) {
|
||||
lua_pushboolean(L,cookie);
|
||||
break;
|
||||
case TYPE_NUMBER:
|
||||
lua_pushnumber(L,_get_number(L,rb,cookie));
|
||||
lua_pushnumber(L,get_number(L,rb,cookie));
|
||||
break;
|
||||
case TYPE_USERDATA:
|
||||
lua_pushlightuserdata(L,_get_pointer(L,rb));
|
||||
lua_pushlightuserdata(L,get_pointer(L,rb));
|
||||
break;
|
||||
case TYPE_SHORT_STRING:
|
||||
_get_buffer(L,rb,cookie);
|
||||
get_buffer(L,rb,cookie);
|
||||
break;
|
||||
case TYPE_LONG_STRING: {
|
||||
uint32_t len;
|
||||
if (cookie == 2) {
|
||||
uint16_t *plen = rb_read(rb, &len, 2);
|
||||
if (plen == NULL) {
|
||||
_invalid_stream(L,rb);
|
||||
invalid_stream(L,rb);
|
||||
}
|
||||
_get_buffer(L,rb,(int)*plen);
|
||||
get_buffer(L,rb,(int)*plen);
|
||||
} else {
|
||||
if (cookie != 4) {
|
||||
_invalid_stream(L,rb);
|
||||
invalid_stream(L,rb);
|
||||
}
|
||||
uint32_t *plen = rb_read(rb, &len, 4);
|
||||
if (plen == NULL) {
|
||||
_invalid_stream(L,rb);
|
||||
invalid_stream(L,rb);
|
||||
}
|
||||
_get_buffer(L,rb,(int)*plen);
|
||||
get_buffer(L,rb,(int)*plen);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TYPE_TABLE: {
|
||||
_unpack_table(L,rb,cookie);
|
||||
unpack_table(L,rb,cookie);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
_invalid_stream(L,rb);
|
||||
invalid_stream(L,rb);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
_unpack_one(lua_State *L, struct read_block *rb) {
|
||||
unpack_one(lua_State *L, struct read_block *rb) {
|
||||
uint8_t type = 0;
|
||||
uint8_t *t = rb_read(rb, &type, 1);
|
||||
if (t==NULL) {
|
||||
_invalid_stream(L, rb);
|
||||
invalid_stream(L, rb);
|
||||
}
|
||||
_push_value(L, rb, *t & 0x7, *t>>3);
|
||||
push_value(L, rb, *t & 0x7, *t>>3);
|
||||
}
|
||||
|
||||
static void
|
||||
_seri(lua_State *L, struct block *b) {
|
||||
uint32_t len = 0;
|
||||
memcpy(&len, b->buffer ,sizeof(len));
|
||||
|
||||
len -= 4;
|
||||
seri(lua_State *L, struct block *b, int len) {
|
||||
uint8_t * buffer = skynet_malloc(len);
|
||||
uint8_t * ptr = buffer;
|
||||
int sz = len;
|
||||
if (len < BLOCK_SIZE - 4) {
|
||||
memcpy(ptr, b->buffer+4, len);
|
||||
} else {
|
||||
memcpy(ptr, b->buffer+4, BLOCK_SIZE-4);
|
||||
ptr += BLOCK_SIZE-4;
|
||||
len -= BLOCK_SIZE-4;
|
||||
b = b->next;
|
||||
|
||||
while(len>0) {
|
||||
if (len >= BLOCK_SIZE) {
|
||||
memcpy(ptr, b->buffer, BLOCK_SIZE);
|
||||
ptr += BLOCK_SIZE;
|
||||
len -= BLOCK_SIZE;
|
||||
} else {
|
||||
memcpy(ptr, b->buffer, len);
|
||||
break;
|
||||
}
|
||||
while(len>0) {
|
||||
if (len >= BLOCK_SIZE) {
|
||||
memcpy(ptr, b->buffer, BLOCK_SIZE);
|
||||
ptr += BLOCK_SIZE;
|
||||
len -= BLOCK_SIZE;
|
||||
b = b->next;
|
||||
} else {
|
||||
memcpy(ptr, b->buffer, len);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -611,7 +519,7 @@ _luaseri_unpack(lua_State *L) {
|
||||
uint8_t *t = rb_read(&rb, &type, 1);
|
||||
if (t==NULL)
|
||||
break;
|
||||
_push_value(L, &rb, *t & 0x7, *t>>3);
|
||||
push_value(L, &rb, *t & 0x7, *t>>3);
|
||||
}
|
||||
|
||||
// Need not free buffer
|
||||
@@ -621,17 +529,15 @@ _luaseri_unpack(lua_State *L) {
|
||||
|
||||
int
|
||||
_luaseri_pack(lua_State *L) {
|
||||
struct block temp;
|
||||
temp.next = NULL;
|
||||
struct write_block wb;
|
||||
wb_init(&wb, NULL);
|
||||
_pack_from(L,&wb,0);
|
||||
struct block * b = wb_close(&wb);
|
||||
_seri(L,b);
|
||||
wb_init(&wb, &temp);
|
||||
pack_from(L,&wb,0);
|
||||
assert(wb.head == &temp);
|
||||
seri(L, &temp, wb.len);
|
||||
|
||||
while (b) {
|
||||
struct block * next = b->next;
|
||||
skynet_free(b);
|
||||
b = next;
|
||||
}
|
||||
wb_free(&wb);
|
||||
|
||||
return 2;
|
||||
}
|
||||
|
||||
786
lualib-src/lua-sharedata.c
Normal file
786
lualib-src/lua-sharedata.c
Normal file
@@ -0,0 +1,786 @@
|
||||
// build: gcc -O2 -Wall --shared -o conf.so luaconf.c
|
||||
|
||||
#include <lua.h>
|
||||
#include <lauxlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#define KEYTYPE_INTEGER 0
|
||||
#define KEYTYPE_STRING 1
|
||||
|
||||
#define VALUETYPE_NIL 0
|
||||
#define VALUETYPE_NUMBER 1
|
||||
#define VALUETYPE_STRING 2
|
||||
#define VALUETYPE_BOOLEAN 3
|
||||
#define VALUETYPE_TABLE 4
|
||||
|
||||
struct table;
|
||||
|
||||
union value {
|
||||
lua_Number n;
|
||||
struct table * tbl;
|
||||
int string;
|
||||
int boolean;
|
||||
};
|
||||
|
||||
struct node {
|
||||
union value v;
|
||||
int key; // integer key or index of string table
|
||||
int next; // next slot index
|
||||
uint32_t keyhash;
|
||||
uint8_t keytype; // key type must be integer or string
|
||||
uint8_t valuetype; // value type can be number/string/boolean/table
|
||||
uint8_t nocolliding; // 0 means colliding slot
|
||||
};
|
||||
|
||||
struct table;
|
||||
|
||||
struct state {
|
||||
int dirty;
|
||||
int ref;
|
||||
struct table * root;
|
||||
};
|
||||
|
||||
struct table {
|
||||
int sizearray;
|
||||
int sizehash;
|
||||
uint8_t *arraytype;
|
||||
union value * array;
|
||||
struct node * hash;
|
||||
lua_State * L;
|
||||
};
|
||||
|
||||
struct context {
|
||||
lua_State * L;
|
||||
struct table * tbl;
|
||||
int string_index;
|
||||
};
|
||||
|
||||
struct ctrl {
|
||||
struct table * root;
|
||||
struct table * update;
|
||||
};
|
||||
|
||||
static int
|
||||
countsize(lua_State *L, int sizearray) {
|
||||
int n = 0;
|
||||
lua_pushnil(L);
|
||||
while (lua_next(L, 1) != 0) {
|
||||
int type = lua_type(L, -2);
|
||||
++n;
|
||||
if (type == LUA_TNUMBER) {
|
||||
lua_Number key = lua_tonumber(L, -2);
|
||||
int nkey = (int)key;
|
||||
if ((lua_Number)nkey != key) {
|
||||
luaL_error(L, "Invalid key %f", key);
|
||||
}
|
||||
if (nkey > 0 && nkey <= sizearray) {
|
||||
--n;
|
||||
}
|
||||
} else if (type != LUA_TSTRING && type != LUA_TTABLE) {
|
||||
luaL_error(L, "Invalid key type %s", lua_typename(L, type));
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
calchash(const char * str, size_t l) {
|
||||
uint32_t h = (uint32_t)l;
|
||||
size_t l1;
|
||||
size_t step = (l >> 5) + 1;
|
||||
for (l1 = l; l1 >= step; l1 -= step) {
|
||||
h = h ^ ((h<<5) + (h>>2) + (uint8_t)(str[l1 - 1]));
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
static int
|
||||
stringindex(struct context *ctx, const char * str, size_t sz) {
|
||||
lua_State *L = ctx->L;
|
||||
lua_pushlstring(L, str, sz);
|
||||
lua_pushvalue(L, -1);
|
||||
lua_rawget(L, 1);
|
||||
int index;
|
||||
// stringmap(1) str index
|
||||
if (lua_isnil(L, -1)) {
|
||||
index = ++ctx->string_index;
|
||||
lua_pop(L, 1);
|
||||
lua_pushinteger(L, index);
|
||||
lua_rawset(L, 1);
|
||||
} else {
|
||||
index = lua_tointeger(L, -1);
|
||||
lua_pop(L, 2);
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
static int convtable(lua_State *L);
|
||||
|
||||
static void
|
||||
setvalue(struct context * ctx, lua_State *L, int index, struct node *n) {
|
||||
int vt = lua_type(L, index);
|
||||
switch(vt) {
|
||||
case LUA_TNIL:
|
||||
n->valuetype = VALUETYPE_NIL;
|
||||
break;
|
||||
case LUA_TNUMBER:
|
||||
n->v.n = lua_tonumber(L, index);
|
||||
n->valuetype = VALUETYPE_NUMBER;
|
||||
break;
|
||||
case LUA_TSTRING: {
|
||||
size_t sz = 0;
|
||||
const char * str = lua_tolstring(L, index, &sz);
|
||||
n->v.string = stringindex(ctx, str, sz);
|
||||
n->valuetype = VALUETYPE_STRING;
|
||||
break;
|
||||
}
|
||||
case LUA_TBOOLEAN:
|
||||
n->v.boolean = lua_toboolean(L, index);
|
||||
n->valuetype = VALUETYPE_BOOLEAN;
|
||||
break;
|
||||
case LUA_TTABLE: {
|
||||
struct table *tbl = ctx->tbl;
|
||||
ctx->tbl = (struct table *)malloc(sizeof(struct table));
|
||||
if (ctx->tbl == NULL) {
|
||||
ctx->tbl = tbl;
|
||||
luaL_error(L, "memory error");
|
||||
// never get here
|
||||
}
|
||||
memset(ctx->tbl, 0, sizeof(struct table));
|
||||
int absidx = lua_absindex(L, index);
|
||||
|
||||
lua_pushcfunction(L, convtable);
|
||||
lua_pushvalue(L, absidx);
|
||||
lua_pushlightuserdata(L, ctx);
|
||||
|
||||
lua_call(L, 2, 0);
|
||||
|
||||
n->v.tbl = ctx->tbl;
|
||||
n->valuetype = VALUETYPE_TABLE;
|
||||
|
||||
ctx->tbl = tbl;
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
luaL_error(L, "Unsupport value type %s", lua_typename(L, vt));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
setarray(struct context *ctx, lua_State *L, int index, int key) {
|
||||
struct node n;
|
||||
setvalue(ctx, L, index, &n);
|
||||
struct table *tbl = ctx->tbl;
|
||||
--key; // base 0
|
||||
tbl->arraytype[key] = n.valuetype;
|
||||
tbl->array[key] = n.v;
|
||||
}
|
||||
|
||||
static int
|
||||
ishashkey(struct context * ctx, lua_State *L, int index, int *key, uint32_t *keyhash, int *keytype) {
|
||||
int sizearray = ctx->tbl->sizearray;
|
||||
int kt = lua_type(L, index);
|
||||
if (kt == LUA_TNUMBER) {
|
||||
*key = lua_tointeger(L, index);
|
||||
if (*key > 0 && *key <= sizearray) {
|
||||
return 0;
|
||||
}
|
||||
*keyhash = (uint32_t)*key;
|
||||
*keytype = KEYTYPE_INTEGER;
|
||||
} else {
|
||||
size_t sz = 0;
|
||||
const char * s = lua_tolstring(L, index, &sz);
|
||||
*keyhash = calchash(s, sz);
|
||||
*key = stringindex(ctx, s, sz);
|
||||
*keytype = KEYTYPE_STRING;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void
|
||||
fillnocolliding(lua_State *L, struct context *ctx) {
|
||||
struct table * tbl = ctx->tbl;
|
||||
lua_pushnil(L);
|
||||
while (lua_next(L, 1) != 0) {
|
||||
int key;
|
||||
int keytype;
|
||||
uint32_t keyhash;
|
||||
if (!ishashkey(ctx, L, -2, &key, &keyhash, &keytype)) {
|
||||
setarray(ctx, L, -1, key);
|
||||
} else {
|
||||
struct node * n = &tbl->hash[keyhash % tbl->sizehash];
|
||||
if (n->valuetype == VALUETYPE_NIL) {
|
||||
n->key = key;
|
||||
n->keytype = keytype;
|
||||
n->keyhash = keyhash;
|
||||
n->next = -1;
|
||||
n->nocolliding = 1;
|
||||
setvalue(ctx, L, -1, n); // set n->v , n->valuetype
|
||||
}
|
||||
}
|
||||
lua_pop(L,1);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
fillcolliding(lua_State *L, struct context *ctx) {
|
||||
struct table * tbl = ctx->tbl;
|
||||
int sizehash = tbl->sizehash;
|
||||
int emptyslot = 0;
|
||||
int i;
|
||||
lua_pushnil(L);
|
||||
while (lua_next(L, 1) != 0) {
|
||||
int key;
|
||||
int keytype;
|
||||
uint32_t keyhash;
|
||||
if (ishashkey(ctx, L, -2, &key, &keyhash, &keytype)) {
|
||||
struct node * mainpos = &tbl->hash[keyhash % tbl->sizehash];
|
||||
if (!(mainpos->keytype == keytype && mainpos->key == key)) {
|
||||
// the key has not insert
|
||||
struct node * n = NULL;
|
||||
for (i=emptyslot;i<sizehash;i++) {
|
||||
if (tbl->hash[i].valuetype == VALUETYPE_NIL) {
|
||||
n = &tbl->hash[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert(n);
|
||||
n->next = mainpos->next;
|
||||
mainpos->next = n - tbl->hash;
|
||||
mainpos->nocolliding = 0;
|
||||
n->key = key;
|
||||
n->keytype = keytype;
|
||||
n->keyhash = keyhash;
|
||||
n->nocolliding = 0;
|
||||
setvalue(ctx, L, -1, n); // set n->v , n->valuetype
|
||||
}
|
||||
}
|
||||
lua_pop(L,1);
|
||||
}
|
||||
}
|
||||
|
||||
// table need convert
|
||||
// struct context * ctx
|
||||
static int
|
||||
convtable(lua_State *L) {
|
||||
int i;
|
||||
struct context *ctx = lua_touserdata(L,2);
|
||||
struct table *tbl = ctx->tbl;
|
||||
|
||||
tbl->L = ctx->L;
|
||||
|
||||
int sizearray = lua_rawlen(L, 1);
|
||||
if (sizearray) {
|
||||
tbl->arraytype = (uint8_t *)malloc(sizearray * sizeof(uint8_t));
|
||||
if (tbl->arraytype == NULL) {
|
||||
goto memerror;
|
||||
}
|
||||
for (i=0;i<sizearray;i++) {
|
||||
tbl->arraytype[i] = VALUETYPE_NIL;
|
||||
}
|
||||
tbl->array = (union value *)malloc(sizearray * sizeof(union value));
|
||||
if (tbl->array == NULL) {
|
||||
goto memerror;
|
||||
}
|
||||
tbl->sizearray = sizearray;
|
||||
}
|
||||
int sizehash = countsize(L, sizearray);
|
||||
if (sizehash) {
|
||||
tbl->hash = (struct node *)malloc(sizehash * sizeof(struct node));
|
||||
if (tbl->hash == NULL) {
|
||||
goto memerror;
|
||||
}
|
||||
for (i=0;i<sizehash;i++) {
|
||||
tbl->hash[i].valuetype = VALUETYPE_NIL;
|
||||
tbl->hash[i].nocolliding = 0;
|
||||
}
|
||||
tbl->sizehash = sizehash;
|
||||
|
||||
fillnocolliding(L, ctx);
|
||||
fillcolliding(L, ctx);
|
||||
} else {
|
||||
int i;
|
||||
for (i=1;i<=sizearray;i++) {
|
||||
lua_rawgeti(L, 1, i);
|
||||
setarray(ctx, L, -1, i);
|
||||
lua_pop(L,1);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
memerror:
|
||||
return luaL_error(L, "memory error");
|
||||
}
|
||||
|
||||
static void
|
||||
delete_tbl(struct table *tbl) {
|
||||
int i;
|
||||
for (i=0;i<tbl->sizearray;i++) {
|
||||
if (tbl->arraytype[i] == VALUETYPE_TABLE) {
|
||||
delete_tbl(tbl->array[i].tbl);
|
||||
}
|
||||
}
|
||||
for (i=0;i<tbl->sizehash;i++) {
|
||||
if (tbl->hash[i].valuetype == VALUETYPE_TABLE) {
|
||||
delete_tbl(tbl->hash[i].v.tbl);
|
||||
}
|
||||
}
|
||||
free(tbl->arraytype);
|
||||
free(tbl->array);
|
||||
free(tbl->hash);
|
||||
free(tbl);
|
||||
}
|
||||
|
||||
static int
|
||||
pconv(lua_State *L) {
|
||||
struct context *ctx = lua_touserdata(L,1);
|
||||
lua_State * pL = lua_touserdata(L, 2);
|
||||
int ret;
|
||||
|
||||
lua_settop(L, 0);
|
||||
|
||||
// init L (may throw memory error)
|
||||
// create a table for string map
|
||||
lua_newtable(L);
|
||||
|
||||
lua_pushcfunction(pL, convtable);
|
||||
lua_pushvalue(pL,1);
|
||||
lua_pushlightuserdata(pL, ctx);
|
||||
|
||||
ret = lua_pcall(pL, 2, 0, 0);
|
||||
if (ret != LUA_OK) {
|
||||
size_t sz = 0;
|
||||
const char * error = lua_tolstring(pL, -1, &sz);
|
||||
lua_pushlstring(L, error, sz);
|
||||
lua_error(L);
|
||||
// never get here
|
||||
}
|
||||
|
||||
luaL_checkstack(L, ctx->string_index + 3, NULL);
|
||||
lua_settop(L,1);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void
|
||||
convert_stringmap(struct context *ctx, struct table *tbl) {
|
||||
lua_State *L = ctx->L;
|
||||
lua_settop(L, ctx->string_index + 1);
|
||||
lua_pushvalue(L, 1);
|
||||
struct state * s = lua_newuserdata(L, sizeof(*s));
|
||||
s->dirty = 0;
|
||||
s->ref = 0;
|
||||
s->root = tbl;
|
||||
lua_replace(L, 1);
|
||||
lua_replace(L, -2);
|
||||
|
||||
lua_pushnil(L);
|
||||
// ... stringmap nil
|
||||
while (lua_next(L, -2) != 0) {
|
||||
int idx = lua_tointeger(L, -1);
|
||||
lua_pop(L, 1);
|
||||
lua_pushvalue(L, -1);
|
||||
lua_replace(L, idx);
|
||||
}
|
||||
|
||||
lua_pop(L, 1);
|
||||
|
||||
lua_gc(L, LUA_GCCOLLECT, 0);
|
||||
}
|
||||
|
||||
static int
|
||||
lnewconf(lua_State *L) {
|
||||
int ret;
|
||||
struct context ctx;
|
||||
struct table * tbl = NULL;
|
||||
luaL_checktype(L,1,LUA_TTABLE);
|
||||
ctx.L = luaL_newstate();
|
||||
ctx.tbl = NULL;
|
||||
ctx.string_index = 1; // 1 reserved for dirty flag
|
||||
if (ctx.L == NULL) {
|
||||
lua_pushliteral(L, "memory error");
|
||||
goto error;
|
||||
}
|
||||
tbl = (struct table *)malloc(sizeof(struct table));
|
||||
if (tbl == NULL) {
|
||||
// lua_pushliteral may fail because of memory error, close first.
|
||||
lua_close(ctx.L);
|
||||
ctx.L = NULL;
|
||||
lua_pushliteral(L, "memory error");
|
||||
goto error;
|
||||
}
|
||||
memset(tbl, 0, sizeof(struct table));
|
||||
ctx.tbl = tbl;
|
||||
|
||||
lua_pushcfunction(ctx.L, pconv);
|
||||
lua_pushlightuserdata(ctx.L , &ctx);
|
||||
lua_pushlightuserdata(ctx.L , L);
|
||||
|
||||
ret = lua_pcall(ctx.L, 2, 1, 0);
|
||||
|
||||
if (ret != LUA_OK) {
|
||||
size_t sz = 0;
|
||||
const char * error = lua_tolstring(ctx.L, -1, &sz);
|
||||
lua_pushlstring(L, error, sz);
|
||||
goto error;
|
||||
}
|
||||
|
||||
convert_stringmap(&ctx, tbl);
|
||||
|
||||
lua_pushlightuserdata(L, tbl);
|
||||
|
||||
return 1;
|
||||
error:
|
||||
if (ctx.L) {
|
||||
lua_close(ctx.L);
|
||||
}
|
||||
if (tbl) {
|
||||
delete_tbl(tbl);
|
||||
}
|
||||
lua_error(L);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static struct table *
|
||||
get_table(lua_State *L, int index) {
|
||||
struct table *tbl = lua_touserdata(L,index);
|
||||
if (tbl == NULL) {
|
||||
luaL_error(L, "Need a conf object");
|
||||
}
|
||||
return tbl;
|
||||
}
|
||||
|
||||
static int
|
||||
ldeleteconf(lua_State *L) {
|
||||
struct table *tbl = get_table(L,1);
|
||||
lua_close(tbl->L);
|
||||
delete_tbl(tbl);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
pushvalue(lua_State *L, lua_State *sL, uint8_t vt, union value *v) {
|
||||
switch(vt) {
|
||||
case VALUETYPE_NUMBER:
|
||||
lua_pushnumber(L, v->n);
|
||||
break;
|
||||
case VALUETYPE_STRING: {
|
||||
size_t sz = 0;
|
||||
const char *str = lua_tolstring(sL, v->string, &sz);
|
||||
lua_pushlstring(L, str, sz);
|
||||
break;
|
||||
}
|
||||
case VALUETYPE_BOOLEAN:
|
||||
lua_pushboolean(L, v->boolean);
|
||||
break;
|
||||
case VALUETYPE_TABLE:
|
||||
lua_pushlightuserdata(L, v->tbl);
|
||||
break;
|
||||
default:
|
||||
lua_pushnil(L);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static struct node *
|
||||
lookup_key(struct table *tbl, uint32_t keyhash, int key, int keytype, const char *str, size_t sz) {
|
||||
if (tbl->sizehash == 0)
|
||||
return NULL;
|
||||
struct node *n = &tbl->hash[keyhash % tbl->sizehash];
|
||||
if (keyhash != n->keyhash && n->nocolliding)
|
||||
return NULL;
|
||||
for (;;) {
|
||||
if (keyhash == n->keyhash) {
|
||||
if (n->keytype == KEYTYPE_INTEGER) {
|
||||
if (keytype == KEYTYPE_INTEGER && n->key == key) {
|
||||
return n;
|
||||
}
|
||||
} else {
|
||||
// n->keytype == KEYTYPE_STRING
|
||||
if (keytype == KEYTYPE_STRING) {
|
||||
size_t sz2 = 0;
|
||||
const char * str2 = lua_tolstring(tbl->L, n->key, &sz2);
|
||||
if (sz == sz2 && memcmp(str,str2,sz) == 0) {
|
||||
return n;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (n->next < 0) {
|
||||
return NULL;
|
||||
}
|
||||
n = &tbl->hash[n->next];
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
lindexconf(lua_State *L) {
|
||||
struct table *tbl = get_table(L,1);
|
||||
int kt = lua_type(L,2);
|
||||
uint32_t keyhash;
|
||||
int key = 0;
|
||||
int keytype;
|
||||
size_t sz = 0;
|
||||
const char * str = NULL;
|
||||
if (kt == LUA_TNUMBER) {
|
||||
lua_Number k = lua_tonumber(L, 2);
|
||||
key = (int)k;
|
||||
if ((lua_Number)key != k) {
|
||||
return luaL_error(L, "Invalid key %f", k);
|
||||
}
|
||||
if (key > 0 && key <= tbl->sizearray) {
|
||||
--key;
|
||||
pushvalue(L, tbl->L, tbl->arraytype[key], &tbl->array[key]);
|
||||
return 1;
|
||||
}
|
||||
keytype = KEYTYPE_INTEGER;
|
||||
keyhash = (uint32_t)key;
|
||||
} else {
|
||||
str = luaL_checklstring(L, 2, &sz);
|
||||
keyhash = calchash(str, sz);
|
||||
keytype = KEYTYPE_STRING;
|
||||
}
|
||||
|
||||
struct node *n = lookup_key(tbl, keyhash, key, keytype, str, sz);
|
||||
if (n) {
|
||||
pushvalue(L, tbl->L, n->valuetype, &n->v);
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
pushkey(lua_State *L, lua_State *sL, struct node *n) {
|
||||
if (n->keytype == KEYTYPE_INTEGER) {
|
||||
lua_pushinteger(L, n->key);
|
||||
} else {
|
||||
size_t sz = 0;
|
||||
const char * str = lua_tolstring(sL, n->key, &sz);
|
||||
lua_pushlstring(L, str, sz);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
pushfirsthash(lua_State *L, struct table * tbl) {
|
||||
if (tbl->sizehash) {
|
||||
pushkey(L, tbl->L, &tbl->hash[0]);
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
lnextkey(lua_State *L) {
|
||||
struct table *tbl = get_table(L,1);
|
||||
if (lua_isnoneornil(L,2)) {
|
||||
if (tbl->sizearray > 0) {
|
||||
int i;
|
||||
for (i=0;i<tbl->sizearray;i++) {
|
||||
if (tbl->arraytype[i] != VALUETYPE_NIL) {
|
||||
lua_pushinteger(L, i+1);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return pushfirsthash(L, tbl);
|
||||
}
|
||||
int kt = lua_type(L,2);
|
||||
uint32_t keyhash;
|
||||
int key = 0;
|
||||
int keytype;
|
||||
size_t sz=0;
|
||||
const char *str = NULL;
|
||||
int sizearray = tbl->sizearray;
|
||||
if (kt == LUA_TNUMBER) {
|
||||
lua_Number k = lua_tonumber(L,2);
|
||||
key = (int)k;
|
||||
if ((lua_Number)key != k) {
|
||||
return 0;
|
||||
}
|
||||
if (key > 0 && key <= sizearray) {
|
||||
int i;
|
||||
for (i=key;i<sizearray;i++) {
|
||||
if (tbl->arraytype[i] != VALUETYPE_NIL) {
|
||||
lua_pushinteger(L, i+1);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return pushfirsthash(L, tbl);
|
||||
}
|
||||
keyhash = (uint32_t)key;
|
||||
keytype = KEYTYPE_INTEGER;
|
||||
} else {
|
||||
str = luaL_checklstring(L, 2, &sz);
|
||||
keyhash = calchash(str, sz);
|
||||
keytype = KEYTYPE_STRING;
|
||||
}
|
||||
|
||||
struct node *n = lookup_key(tbl, keyhash, key, keytype, str, sz);
|
||||
if (n) {
|
||||
++n;
|
||||
int index = n-tbl->hash;
|
||||
if (index == tbl->sizehash) {
|
||||
return 0;
|
||||
}
|
||||
pushkey(L, tbl->L, n);
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
llen(lua_State *L) {
|
||||
struct table *tbl = get_table(L,1);
|
||||
lua_pushinteger(L, tbl->sizearray);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
lhashlen(lua_State *L) {
|
||||
struct table *tbl = get_table(L,1);
|
||||
lua_pushinteger(L, tbl->sizehash);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
releaseobj(lua_State *L) {
|
||||
struct ctrl *c = lua_touserdata(L, 1);
|
||||
struct table *tbl = c->root;
|
||||
struct state *s = lua_touserdata(tbl->L, 1);
|
||||
__sync_fetch_and_sub(&s->ref, 1);
|
||||
c->root = NULL;
|
||||
c->update = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
lboxconf(lua_State *L) {
|
||||
struct table * tbl = get_table(L,1);
|
||||
struct state * s = lua_touserdata(tbl->L, 1);
|
||||
__sync_fetch_and_add(&s->ref, 1);
|
||||
|
||||
struct ctrl * c = lua_newuserdata(L, sizeof(*c));
|
||||
c->root = tbl;
|
||||
c->update = NULL;
|
||||
if (luaL_newmetatable(L, "confctrl")) {
|
||||
lua_pushcfunction(L, releaseobj);
|
||||
lua_setfield(L, -2, "__gc");
|
||||
}
|
||||
lua_setmetatable(L, -2);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
lmarkdirty(lua_State *L) {
|
||||
struct table *tbl = get_table(L,1);
|
||||
struct state * s = lua_touserdata(tbl->L, 1);
|
||||
s->dirty = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
lisdirty(lua_State *L) {
|
||||
struct table *tbl = get_table(L,1);
|
||||
struct state * s = lua_touserdata(tbl->L, 1);
|
||||
int d = s->dirty;
|
||||
lua_pushboolean(L, d);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
lgetref(lua_State *L) {
|
||||
struct table *tbl = get_table(L,1);
|
||||
struct state * s = lua_touserdata(tbl->L, 1);
|
||||
lua_pushinteger(L , s->ref);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
lincref(lua_State *L) {
|
||||
struct table *tbl = get_table(L,1);
|
||||
struct state * s = lua_touserdata(tbl->L, 1);
|
||||
int ref = __sync_add_and_fetch(&s->ref, 1);
|
||||
lua_pushinteger(L , ref);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
ldecref(lua_State *L) {
|
||||
struct table *tbl = get_table(L,1);
|
||||
struct state * s = lua_touserdata(tbl->L, 1);
|
||||
int ref = __sync_sub_and_fetch(&s->ref, 1);
|
||||
lua_pushinteger(L , ref);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
lneedupdate(lua_State *L) {
|
||||
struct ctrl * c = lua_touserdata(L, 1);
|
||||
if (c->update) {
|
||||
lua_pushlightuserdata(L, c->update);
|
||||
lua_getuservalue(L, 1);
|
||||
return 2;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
lupdate(lua_State *L) {
|
||||
luaL_checktype(L, 1, LUA_TUSERDATA);
|
||||
luaL_checktype(L, 2, LUA_TLIGHTUSERDATA);
|
||||
luaL_checktype(L, 3, LUA_TTABLE);
|
||||
struct ctrl * c= lua_touserdata(L, 1);
|
||||
struct table *n = lua_touserdata(L, 2);
|
||||
if (c->root == n) {
|
||||
return luaL_error(L, "You should update a new object");
|
||||
}
|
||||
lua_settop(L, 3);
|
||||
lua_setuservalue(L, 1);
|
||||
c->update = n;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
luaopen_sharedata_core(lua_State *L) {
|
||||
luaL_Reg l[] = {
|
||||
// used by host
|
||||
{ "new", lnewconf },
|
||||
{ "delete", ldeleteconf },
|
||||
{ "markdirty", lmarkdirty },
|
||||
{ "getref", lgetref },
|
||||
{ "incref", lincref },
|
||||
{ "decref", ldecref },
|
||||
|
||||
// used by client
|
||||
{ "box", lboxconf },
|
||||
{ "index", lindexconf },
|
||||
{ "nextkey", lnextkey },
|
||||
{ "len", llen },
|
||||
{ "hashlen", lhashlen },
|
||||
{ "isdirty", lisdirty },
|
||||
{ "needupdate", lneedupdate },
|
||||
{ "update", lupdate },
|
||||
{ NULL, NULL },
|
||||
};
|
||||
luaL_checkversion(L);
|
||||
luaL_newlib(L, l);
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -73,10 +73,17 @@ _cb(struct skynet_context * context, void * ud, int type, int session, uint32_t
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
forward_cb(struct skynet_context * context, void * ud, int type, int session, uint32_t source, const void * msg, size_t sz) {
|
||||
_cb(context, ud, type, session, source, msg, sz);
|
||||
// don't delete msg in forward mode.
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
_callback(lua_State *L) {
|
||||
struct skynet_context * context = lua_touserdata(L, lua_upvalueindex(1));
|
||||
|
||||
int forward = lua_toboolean(L, 2);
|
||||
luaL_checktype(L,1,LUA_TFUNCTION);
|
||||
lua_settop(L,1);
|
||||
lua_rawsetp(L, LUA_REGISTRYINDEX, _cb);
|
||||
@@ -84,7 +91,11 @@ _callback(lua_State *L) {
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_MAINTHREAD);
|
||||
lua_State *gL = lua_tothread(L,-1);
|
||||
|
||||
skynet_callback(context, gL, _cb);
|
||||
if (forward) {
|
||||
skynet_callback(context, gL, forward_cb);
|
||||
} else {
|
||||
skynet_callback(context, gL, _cb);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -115,44 +126,13 @@ _genid(lua_State *L) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// copy from _send
|
||||
|
||||
static int
|
||||
_sendname(lua_State *L, struct skynet_context * context, const char * dest) {
|
||||
int type = luaL_checkinteger(L, 2);
|
||||
int session = 0;
|
||||
if (lua_isnil(L,3)) {
|
||||
type |= PTYPE_TAG_ALLOCSESSION;
|
||||
} else {
|
||||
session = luaL_checkinteger(L,3);
|
||||
static const char *
|
||||
get_dest_string(lua_State *L, int index) {
|
||||
const char * dest_string = lua_tostring(L, index);
|
||||
if (dest_string == NULL) {
|
||||
luaL_error(L, "dest address type (%s) must be a string or number.", lua_typename(L, lua_type(L,index)));
|
||||
}
|
||||
|
||||
int mtype = lua_type(L,4);
|
||||
switch (mtype) {
|
||||
case LUA_TSTRING: {
|
||||
size_t len = 0;
|
||||
void * msg = (void *)lua_tolstring(L,4,&len);
|
||||
session = skynet_sendname(context, dest, type, session , msg, len);
|
||||
break;
|
||||
}
|
||||
case LUA_TNIL :
|
||||
session = skynet_sendname(context, dest, type, session , NULL, 0);
|
||||
break;
|
||||
case LUA_TLIGHTUSERDATA: {
|
||||
luaL_checktype(L, 4, LUA_TLIGHTUSERDATA);
|
||||
void * msg = lua_touserdata(L,4);
|
||||
int size = luaL_checkinteger(L,5);
|
||||
session = skynet_sendname(context, dest, type | PTYPE_TAG_DONTCOPY, session, msg, size);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
luaL_error(L, "skynet.send invalid param %s", lua_typename(L,lua_type(L,4)));
|
||||
}
|
||||
if (session < 0) {
|
||||
return 0;
|
||||
}
|
||||
lua_pushinteger(L,session);
|
||||
return 1;
|
||||
return dest_string;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -167,28 +147,10 @@ _sendname(lua_State *L, struct skynet_context * context, const char * dest) {
|
||||
static int
|
||||
_send(lua_State *L) {
|
||||
struct skynet_context * context = lua_touserdata(L, lua_upvalueindex(1));
|
||||
int addr_type = lua_type(L,1);
|
||||
uint32_t dest = 0;
|
||||
switch(addr_type) {
|
||||
case LUA_TNUMBER:
|
||||
dest = lua_tounsigned(L,1);
|
||||
break;
|
||||
case LUA_TSTRING: {
|
||||
const char * addrname = lua_tostring(L,1);
|
||||
if (addrname[0] == '.' || addrname[0] == ':') {
|
||||
dest = skynet_queryname(context, addrname);
|
||||
if (dest == 0) {
|
||||
luaL_error(L, "Invalid name %s", addrname);
|
||||
}
|
||||
} else if ('0' <= addrname[0] && addrname[0] <= '9') {
|
||||
luaL_error(L, "Invalid name %s: must not start with a digit", addrname);
|
||||
} else {
|
||||
return _sendname(L, context, addrname);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return luaL_error(L, "address must be number or string, got %s",lua_typename(L,addr_type));
|
||||
uint32_t dest = lua_tounsigned(L, 1);
|
||||
const char * dest_string = NULL;
|
||||
if (dest == 0) {
|
||||
dest_string = get_dest_string(L, 1);
|
||||
}
|
||||
|
||||
int type = luaL_checkinteger(L, 2);
|
||||
@@ -207,13 +169,21 @@ _send(lua_State *L) {
|
||||
if (len == 0) {
|
||||
msg = NULL;
|
||||
}
|
||||
session = skynet_send(context, 0, dest, type, session , msg, len);
|
||||
if (dest_string) {
|
||||
session = skynet_sendname(context, 0, dest_string, type, session , msg, len);
|
||||
} else {
|
||||
session = skynet_send(context, 0, dest, type, session , msg, len);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case LUA_TLIGHTUSERDATA: {
|
||||
void * msg = lua_touserdata(L,4);
|
||||
int size = luaL_checkinteger(L,5);
|
||||
session = skynet_send(context, 0, dest, type | PTYPE_TAG_DONTCOPY, session, msg, size);
|
||||
if (dest_string) {
|
||||
session = skynet_sendname(context, 0, dest_string, type | PTYPE_TAG_DONTCOPY, session, msg, size);
|
||||
} else {
|
||||
session = skynet_send(context, 0, dest, type | PTYPE_TAG_DONTCOPY, session, msg, size);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -231,7 +201,11 @@ _send(lua_State *L) {
|
||||
static int
|
||||
_redirect(lua_State *L) {
|
||||
struct skynet_context * context = lua_touserdata(L, lua_upvalueindex(1));
|
||||
uint32_t dest = luaL_checkunsigned(L,1);
|
||||
uint32_t dest = lua_tounsigned(L,1);
|
||||
const char * dest_string = NULL;
|
||||
if (dest == 0) {
|
||||
dest_string = get_dest_string(L, 1);
|
||||
}
|
||||
uint32_t source = luaL_checkunsigned(L,2);
|
||||
int type = luaL_checkinteger(L,3);
|
||||
int session = luaL_checkinteger(L,4);
|
||||
@@ -244,13 +218,21 @@ _redirect(lua_State *L) {
|
||||
if (len == 0) {
|
||||
msg = NULL;
|
||||
}
|
||||
session = skynet_send(context, source, dest, type, session , msg, len);
|
||||
if (dest_string) {
|
||||
session = skynet_sendname(context, source, dest_string, type, session , msg, len);
|
||||
} else {
|
||||
session = skynet_send(context, source, dest, type, session , msg, len);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case LUA_TLIGHTUSERDATA: {
|
||||
void * msg = lua_touserdata(L,5);
|
||||
int size = luaL_checkinteger(L,6);
|
||||
session = skynet_send(context, source, dest, type | PTYPE_TAG_DONTCOPY, session, msg, size);
|
||||
if (dest_string) {
|
||||
session = skynet_sendname(context, source, dest_string, type | PTYPE_TAG_DONTCOPY, session, msg, size);
|
||||
} else {
|
||||
session = skynet_send(context, source, dest, type | PTYPE_TAG_DONTCOPY, session, msg, size);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -299,8 +281,28 @@ lpackstring(lua_State *L) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
ltrash(lua_State *L) {
|
||||
int t = lua_type(L,1);
|
||||
switch (t) {
|
||||
case LUA_TSTRING: {
|
||||
break;
|
||||
}
|
||||
case LUA_TLIGHTUSERDATA: {
|
||||
void * msg = lua_touserdata(L,1);
|
||||
luaL_checkinteger(L,2);
|
||||
skynet_free(msg);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
luaL_error(L, "skynet.trash invalid param %s", lua_typename(L,t));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
luaopen_skynet_c(lua_State *L) {
|
||||
luaopen_skynet_core(lua_State *L) {
|
||||
luaL_checkversion(L);
|
||||
|
||||
luaL_Reg l[] = {
|
||||
@@ -314,6 +316,7 @@ luaopen_skynet_c(lua_State *L) {
|
||||
{ "pack", _luaseri_pack },
|
||||
{ "unpack", _luaseri_unpack },
|
||||
{ "packstring", lpackstring },
|
||||
{ "trash" , ltrash },
|
||||
{ "callback", _callback },
|
||||
{ NULL, NULL },
|
||||
};
|
||||
|
||||
244
lualib-src/lua-stm.c
Normal file
244
lualib-src/lua-stm.c
Normal file
@@ -0,0 +1,244 @@
|
||||
#include <lua.h>
|
||||
#include <lauxlib.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "rwlock.h"
|
||||
#include "skynet_malloc.h"
|
||||
|
||||
struct stm_object {
|
||||
struct rwlock lock;
|
||||
int reference;
|
||||
struct stm_copy * copy;
|
||||
};
|
||||
|
||||
struct stm_copy {
|
||||
int reference;
|
||||
uint32_t sz;
|
||||
void * msg;
|
||||
};
|
||||
|
||||
// msg should alloc by skynet_malloc
|
||||
static struct stm_copy *
|
||||
stm_newcopy(void * msg, int32_t sz) {
|
||||
struct stm_copy * copy = skynet_malloc(sizeof(*copy));
|
||||
copy->reference = 1;
|
||||
copy->sz = sz;
|
||||
copy->msg = msg;
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
static struct stm_object *
|
||||
stm_new(void * msg, int32_t sz) {
|
||||
struct stm_object * obj = skynet_malloc(sizeof(*obj));
|
||||
rwlock_init(&obj->lock);
|
||||
obj->reference = 1;
|
||||
obj->copy = stm_newcopy(msg, sz);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
static void
|
||||
stm_releasecopy(struct stm_copy *copy) {
|
||||
if (copy == NULL)
|
||||
return;
|
||||
if (__sync_sub_and_fetch(©->reference, 1) == 0) {
|
||||
skynet_free(copy->msg);
|
||||
skynet_free(copy);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
stm_release(struct stm_object *obj) {
|
||||
assert(obj->copy);
|
||||
rwlock_wlock(&obj->lock);
|
||||
// writer release the stm object, so release the last copy .
|
||||
stm_releasecopy(obj->copy);
|
||||
obj->copy = NULL;
|
||||
if (--obj->reference > 0) {
|
||||
// stm object grab by readers, reset the copy to NULL.
|
||||
rwlock_wunlock(&obj->lock);
|
||||
return;
|
||||
}
|
||||
// no one grab the stm object, no need to unlock wlock.
|
||||
skynet_free(obj);
|
||||
}
|
||||
|
||||
static void
|
||||
stm_releasereader(struct stm_object *obj) {
|
||||
rwlock_rlock(&obj->lock);
|
||||
if (__sync_sub_and_fetch(&obj->reference,1) == 0) {
|
||||
// last reader, no writer. so no need to unlock
|
||||
assert(obj->copy == NULL);
|
||||
skynet_free(obj);
|
||||
return;
|
||||
}
|
||||
rwlock_runlock(&obj->lock);
|
||||
}
|
||||
|
||||
static void
|
||||
stm_grab(struct stm_object *obj) {
|
||||
rwlock_rlock(&obj->lock);
|
||||
int ref = __sync_fetch_and_add(&obj->reference,1);
|
||||
rwlock_runlock(&obj->lock);
|
||||
assert(ref > 0);
|
||||
}
|
||||
|
||||
static struct stm_copy *
|
||||
stm_copy(struct stm_object *obj) {
|
||||
rwlock_rlock(&obj->lock);
|
||||
struct stm_copy * ret = obj->copy;
|
||||
if (ret) {
|
||||
int ref = __sync_fetch_and_add(&ret->reference,1);
|
||||
assert(ref > 0);
|
||||
}
|
||||
rwlock_runlock(&obj->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
stm_update(struct stm_object *obj, void *msg, int32_t sz) {
|
||||
struct stm_copy *copy = stm_newcopy(msg, sz);
|
||||
rwlock_wlock(&obj->lock);
|
||||
struct stm_copy *oldcopy = obj->copy;
|
||||
obj->copy = copy;
|
||||
rwlock_wunlock(&obj->lock);
|
||||
|
||||
stm_releasecopy(oldcopy);
|
||||
}
|
||||
|
||||
// lua binding
|
||||
|
||||
struct boxstm {
|
||||
struct stm_object * obj;
|
||||
};
|
||||
|
||||
static int
|
||||
lcopy(lua_State *L) {
|
||||
struct boxstm * box = lua_touserdata(L, 1);
|
||||
stm_grab(box->obj);
|
||||
lua_pushlightuserdata(L, box->obj);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
lnewwriter(lua_State *L) {
|
||||
void * msg = lua_touserdata(L, 1);
|
||||
uint32_t sz = luaL_checkunsigned(L, 2);
|
||||
struct boxstm * box = lua_newuserdata(L, sizeof(*box));
|
||||
box->obj = stm_new(msg,sz);
|
||||
lua_pushvalue(L, lua_upvalueindex(1));
|
||||
lua_setmetatable(L, -2);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
ldeletewriter(lua_State *L) {
|
||||
struct boxstm * box = lua_touserdata(L, 1);
|
||||
stm_release(box->obj);
|
||||
box->obj = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
lupdate(lua_State *L) {
|
||||
struct boxstm * box = lua_touserdata(L, 1);
|
||||
void * msg = lua_touserdata(L, 2);
|
||||
uint32_t sz = luaL_checkunsigned(L, 3);
|
||||
stm_update(box->obj, msg, sz);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct boxreader {
|
||||
struct stm_object *obj;
|
||||
struct stm_copy *lastcopy;
|
||||
};
|
||||
|
||||
static int
|
||||
lnewreader(lua_State *L) {
|
||||
struct boxreader * box = lua_newuserdata(L, sizeof(*box));
|
||||
box->obj = lua_touserdata(L, 1);
|
||||
box->lastcopy = NULL;
|
||||
lua_pushvalue(L, lua_upvalueindex(1));
|
||||
lua_setmetatable(L, -2);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
ldeletereader(lua_State *L) {
|
||||
struct boxreader * box = lua_touserdata(L, 1);
|
||||
stm_releasereader(box->obj);
|
||||
box->obj = NULL;
|
||||
stm_releasecopy(box->lastcopy);
|
||||
box->lastcopy = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
lread(lua_State *L) {
|
||||
struct boxreader * box = lua_touserdata(L, 1);
|
||||
luaL_checktype(L, 2, LUA_TFUNCTION);
|
||||
struct stm_copy * copy = stm_copy(box->obj);
|
||||
if (copy == box->lastcopy) {
|
||||
// not update
|
||||
stm_releasecopy(copy);
|
||||
lua_pushboolean(L, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
stm_releasecopy(box->lastcopy);
|
||||
box->lastcopy = copy;
|
||||
if (copy) {
|
||||
lua_settop(L, 2);
|
||||
lua_pushlightuserdata(L, copy->msg);
|
||||
lua_pushunsigned(L, copy->sz);
|
||||
lua_call(L, 2, LUA_MULTRET);
|
||||
lua_pushboolean(L, 1);
|
||||
lua_replace(L, 1);
|
||||
return lua_gettop(L);
|
||||
} else {
|
||||
lua_pushboolean(L, 0);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
luaopen_stm(lua_State *L) {
|
||||
luaL_checkversion(L);
|
||||
lua_createtable(L, 0, 3);
|
||||
|
||||
lua_pushcfunction(L, lcopy);
|
||||
lua_setfield(L, -2, "copy");
|
||||
|
||||
luaL_Reg writer[] = {
|
||||
{ "new", lnewwriter },
|
||||
{ NULL, NULL },
|
||||
};
|
||||
lua_createtable(L, 0, 2);
|
||||
lua_pushcfunction(L, ldeletewriter),
|
||||
lua_setfield(L, -2, "__gc");
|
||||
lua_pushcfunction(L, lupdate),
|
||||
lua_setfield(L, -2, "__call");
|
||||
luaL_setfuncs(L, writer, 1);
|
||||
|
||||
luaL_Reg reader[] = {
|
||||
{ "newcopy", lnewreader },
|
||||
{ NULL, NULL },
|
||||
};
|
||||
lua_createtable(L, 0, 2);
|
||||
lua_pushcfunction(L, ldeletereader),
|
||||
lua_setfield(L, -2, "__gc");
|
||||
lua_pushcfunction(L, lread),
|
||||
lua_setfield(L, -2, "__call");
|
||||
luaL_setfuncs(L, reader, 1);
|
||||
|
||||
return 1;
|
||||
}
|
||||
170
lualib-src/lua_mysqlaux.c
Executable file
170
lualib-src/lua_mysqlaux.c
Executable file
@@ -0,0 +1,170 @@
|
||||
//
|
||||
// lua_mysqlaux.c
|
||||
//
|
||||
// Created by changfeng on 6/17/14.
|
||||
// Copyright (c) 2014 changfeng. All rights reserved.
|
||||
//
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <lua.h>
|
||||
#include <lauxlib.h>
|
||||
|
||||
static unsigned int num_escape_sql_str(unsigned char *dst, unsigned char *src, size_t size)
|
||||
{
|
||||
unsigned int n =0;
|
||||
while (size) {
|
||||
/* the highest bit of all the UTF-8 chars
|
||||
* is always 1 */
|
||||
if ((*src & 0x80) == 0) {
|
||||
switch (*src) {
|
||||
case '\0':
|
||||
case '\b':
|
||||
case '\n':
|
||||
case '\r':
|
||||
case '\t':
|
||||
case 26: /* \z */
|
||||
case '\\':
|
||||
case '\'':
|
||||
case '"':
|
||||
n++;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
src++;
|
||||
size--;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
static unsigned char*
|
||||
escape_sql_str(unsigned char *dst, unsigned char *src, size_t size)
|
||||
{
|
||||
|
||||
while (size) {
|
||||
if ((*src & 0x80) == 0) {
|
||||
switch (*src) {
|
||||
case '\0':
|
||||
*dst++ = '\\';
|
||||
*dst++ = '0';
|
||||
break;
|
||||
|
||||
case '\b':
|
||||
*dst++ = '\\';
|
||||
*dst++ = 'b';
|
||||
break;
|
||||
|
||||
case '\n':
|
||||
*dst++ = '\\';
|
||||
*dst++ = 'n';
|
||||
break;
|
||||
|
||||
case '\r':
|
||||
*dst++ = '\\';
|
||||
*dst++ = 'r';
|
||||
break;
|
||||
|
||||
case '\t':
|
||||
*dst++ = '\\';
|
||||
*dst++ = 't';
|
||||
break;
|
||||
|
||||
case 26:
|
||||
*dst++ = '\\';
|
||||
*dst++ = 'z';
|
||||
break;
|
||||
|
||||
case '\\':
|
||||
*dst++ = '\\';
|
||||
*dst++ = '\\';
|
||||
break;
|
||||
|
||||
case '\'':
|
||||
*dst++ = '\\';
|
||||
*dst++ = '\'';
|
||||
break;
|
||||
|
||||
case '"':
|
||||
*dst++ = '\\';
|
||||
*dst++ = '"';
|
||||
break;
|
||||
|
||||
default:
|
||||
*dst++ = *src;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
*dst++ = *src;
|
||||
}
|
||||
src++;
|
||||
size--;
|
||||
} /* while (size) */
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static int
|
||||
quote_sql_str(lua_State *L)
|
||||
{
|
||||
size_t len, dlen, escape;
|
||||
unsigned char *p;
|
||||
unsigned char *src, *dst;
|
||||
|
||||
if (lua_gettop(L) != 1) {
|
||||
return luaL_error(L, "expecting one argument");
|
||||
}
|
||||
|
||||
src = (unsigned char *) luaL_checklstring(L, 1, &len);
|
||||
|
||||
if (len == 0) {
|
||||
dst = (unsigned char *) "''";
|
||||
dlen = sizeof("''") - 1;
|
||||
lua_pushlstring(L, (char *) dst, dlen);
|
||||
return 1;
|
||||
}
|
||||
|
||||
escape = num_escape_sql_str(NULL, src, len);
|
||||
|
||||
dlen = sizeof("''") - 1 + len + escape;
|
||||
p = lua_newuserdata(L, dlen);
|
||||
|
||||
dst = p;
|
||||
|
||||
*p++ = '\'';
|
||||
|
||||
if (escape == 0) {
|
||||
memcpy(p, src, len);
|
||||
p+=len;
|
||||
} else {
|
||||
p = (unsigned char *) escape_sql_str(p, src, len);
|
||||
}
|
||||
|
||||
*p++ = '\'';
|
||||
|
||||
if (p != dst + dlen) {
|
||||
return luaL_error(L, "quote sql string error");
|
||||
}
|
||||
|
||||
lua_pushlstring(L, (char *) dst, p - dst);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static struct luaL_Reg mysqlauxlib[] = {
|
||||
{"quote_sql_str",quote_sql_str},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
|
||||
int luaopen_mysqlaux_c (lua_State *L) {
|
||||
lua_newtable(L);
|
||||
luaL_setfuncs(L, mysqlauxlib, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
1
lualib-src/sproto/README
Normal file
1
lualib-src/sproto/README
Normal file
@@ -0,0 +1 @@
|
||||
Check https://github.com/cloudwu/sproto for more
|
||||
471
lualib-src/sproto/lsproto.c
Normal file
471
lualib-src/sproto/lsproto.c
Normal file
@@ -0,0 +1,471 @@
|
||||
#include <string.h>
|
||||
#include "msvcint.h"
|
||||
|
||||
#include "lua.h"
|
||||
#include "lauxlib.h"
|
||||
#include "sproto.h"
|
||||
|
||||
#define ENCODE_BUFFERSIZE 2050
|
||||
|
||||
//#define ENCODE_BUFFERSIZE 2050
|
||||
#define ENCODE_MAXSIZE 0x1000000
|
||||
#define ENCODE_DEEPLEVEL 64
|
||||
|
||||
#ifndef luaL_newlib /* using LuaJIT */
|
||||
/*
|
||||
** set functions from list 'l' into table at top - 'nup'; each
|
||||
** function gets the 'nup' elements at the top as upvalues.
|
||||
** Returns with only the table at the stack.
|
||||
*/
|
||||
LUALIB_API void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) {
|
||||
#ifdef luaL_checkversion
|
||||
luaL_checkversion(L);
|
||||
#endif
|
||||
luaL_checkstack(L, nup, "too many upvalues");
|
||||
for (; l->name != NULL; l++) { /* fill the table with given functions */
|
||||
int i;
|
||||
for (i = 0; i < nup; i++) /* copy upvalues to the top */
|
||||
lua_pushvalue(L, -nup);
|
||||
lua_pushcclosure(L, l->func, nup); /* closure with those upvalues */
|
||||
lua_setfield(L, -(nup + 2), l->name);
|
||||
}
|
||||
lua_pop(L, nup); /* remove upvalues */
|
||||
}
|
||||
|
||||
#define luaL_newlibtable(L,l) \
|
||||
lua_createtable(L, 0, sizeof(l)/sizeof((l)[0]) - 1)
|
||||
|
||||
#define luaL_newlib(L,l) (luaL_newlibtable(L,l), luaL_setfuncs(L,l,0))
|
||||
#endif
|
||||
|
||||
static int
|
||||
lnewproto(lua_State *L) {
|
||||
size_t sz = 0;
|
||||
void * buffer = (void *)luaL_checklstring(L,1,&sz);
|
||||
struct sproto * sp = sproto_create(buffer, sz);
|
||||
if (sp) {
|
||||
lua_pushlightuserdata(L, sp);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
ldeleteproto(lua_State *L) {
|
||||
struct sproto * sp = lua_touserdata(L,1);
|
||||
if (sp == NULL) {
|
||||
return luaL_argerror(L, 1, "Need a sproto object");
|
||||
}
|
||||
sproto_release(sp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
lquerytype(lua_State *L) {
|
||||
const char * type_name;
|
||||
struct sproto *sp = lua_touserdata(L,1);
|
||||
struct sproto_type *st;
|
||||
if (sp == NULL) {
|
||||
return luaL_argerror(L, 1, "Need a sproto object");
|
||||
}
|
||||
type_name = luaL_checkstring(L,2);
|
||||
st = sproto_type(sp, type_name);
|
||||
if (st) {
|
||||
lua_pushlightuserdata(L, st);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return luaL_error(L, "type %s not found", type_name);
|
||||
}
|
||||
|
||||
struct encode_ud {
|
||||
lua_State *L;
|
||||
struct sproto_type *st;
|
||||
int tbl_index;
|
||||
const char * array_tag;
|
||||
int array_index;
|
||||
int deep;
|
||||
};
|
||||
|
||||
static int
|
||||
encode(void *ud, const char *tagname, int type, int index, struct sproto_type *st, void *value, int length) {
|
||||
struct encode_ud *self = ud;
|
||||
lua_State *L = self->L;
|
||||
if (self->deep >= ENCODE_DEEPLEVEL)
|
||||
return luaL_error(L, "The table is too deep");
|
||||
if (index > 0) {
|
||||
if (tagname != self->array_tag) {
|
||||
self->array_tag = tagname;
|
||||
lua_getfield(L, self->tbl_index, tagname);
|
||||
if (lua_isnil(L, -1)) {
|
||||
if (self->array_index) {
|
||||
lua_replace(L, self->array_index);
|
||||
}
|
||||
self->array_index = 0;
|
||||
return 0;
|
||||
}
|
||||
if (self->array_index) {
|
||||
lua_replace(L, self->array_index);
|
||||
} else {
|
||||
self->array_index = lua_gettop(L);
|
||||
}
|
||||
}
|
||||
lua_rawgeti(L, self->array_index, index);
|
||||
} else {
|
||||
lua_getfield(L, self->tbl_index, tagname);
|
||||
}
|
||||
if (lua_isnil(L, -1)) {
|
||||
lua_pop(L,1);
|
||||
return 0;
|
||||
}
|
||||
switch (type) {
|
||||
case SPROTO_TINTEGER: {
|
||||
lua_Integer v = luaL_checkinteger(L, -1);
|
||||
lua_Integer vh;
|
||||
lua_pop(L,1);
|
||||
// notice: in lua 5.2, lua_Integer maybe 52bit
|
||||
vh = v >> 31;
|
||||
if (vh == 0 || vh == -1) {
|
||||
*(uint32_t *)value = (uint32_t)v;
|
||||
return 4;
|
||||
}
|
||||
else {
|
||||
*(uint64_t *)value = (uint64_t)v;
|
||||
return 8;
|
||||
}
|
||||
}
|
||||
case SPROTO_TBOOLEAN: {
|
||||
int v = lua_toboolean(L, -1);
|
||||
*(int *)value = v;
|
||||
lua_pop(L,1);
|
||||
return 4;
|
||||
}
|
||||
case SPROTO_TSTRING: {
|
||||
size_t sz = 0;
|
||||
const char * str = luaL_checklstring(L, -1, &sz);
|
||||
if (sz > length)
|
||||
return -1;
|
||||
memcpy(value, str, sz);
|
||||
lua_pop(L,1);
|
||||
return sz;
|
||||
}
|
||||
case SPROTO_TSTRUCT: {
|
||||
struct encode_ud sub;
|
||||
int r;
|
||||
sub.L = L;
|
||||
sub.st = st;
|
||||
sub.tbl_index = lua_gettop(L);
|
||||
sub.array_tag = NULL;
|
||||
sub.array_index = 0;
|
||||
sub.deep = self->deep + 1;
|
||||
r = sproto_encode(st, value, length, encode, &sub);
|
||||
lua_pop(L,1);
|
||||
return r;
|
||||
}
|
||||
default:
|
||||
return luaL_error(L, "Invalid field type %d", type);
|
||||
}
|
||||
}
|
||||
|
||||
static void *
|
||||
expand_buffer(lua_State *L, int osz, int nsz) {
|
||||
void *output;
|
||||
do {
|
||||
osz *= 2;
|
||||
} while (osz < nsz);
|
||||
if (osz > ENCODE_MAXSIZE) {
|
||||
luaL_error(L, "object is too large (>%d)", ENCODE_MAXSIZE);
|
||||
return NULL;
|
||||
}
|
||||
output = lua_newuserdata(L, osz);
|
||||
lua_replace(L, lua_upvalueindex(1));
|
||||
lua_pushinteger(L, osz);
|
||||
lua_replace(L, lua_upvalueindex(2));
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/*
|
||||
lightuserdata sproto_type
|
||||
table source
|
||||
|
||||
return string
|
||||
*/
|
||||
static int
|
||||
lencode(lua_State *L) {
|
||||
struct encode_ud self;
|
||||
void * buffer = lua_touserdata(L, lua_upvalueindex(1));
|
||||
int sz = lua_tointeger(L, lua_upvalueindex(2));
|
||||
|
||||
struct sproto_type * st = lua_touserdata(L, 1);
|
||||
if (st == NULL) {
|
||||
return luaL_argerror(L, 1, "Need a sproto_type object");
|
||||
}
|
||||
luaL_checktype(L, 2, LUA_TTABLE);
|
||||
luaL_checkstack(L, ENCODE_DEEPLEVEL + 8, NULL);
|
||||
self.L = L;
|
||||
self.st = st;
|
||||
self.tbl_index = 2;
|
||||
self.array_tag = NULL;
|
||||
self.array_index = 0;
|
||||
self.deep = 0;
|
||||
for (;;) {
|
||||
int r = sproto_encode(st, buffer, sz, encode, &self);
|
||||
if (r<0) {
|
||||
buffer = expand_buffer(L, sz, sz*2);
|
||||
sz *= 2;
|
||||
} else {
|
||||
lua_pushlstring(L, buffer, r);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct decode_ud {
|
||||
lua_State *L;
|
||||
const char * array_tag;
|
||||
int array_index;
|
||||
int result_index;
|
||||
int deep;
|
||||
};
|
||||
|
||||
static int
|
||||
decode(void *ud, const char *tagname, int type, int index, struct sproto_type *st, void *value, int length) {
|
||||
struct decode_ud * self = ud;
|
||||
lua_State *L = self->L;
|
||||
if (self->deep >= ENCODE_DEEPLEVEL)
|
||||
return luaL_error(L, "The table is too deep");
|
||||
if (index > 0) {
|
||||
// It's array
|
||||
if (tagname != self->array_tag) {
|
||||
self->array_tag = tagname;
|
||||
lua_newtable(L);
|
||||
lua_pushvalue(L, -1);
|
||||
lua_setfield(L, self->result_index, tagname);
|
||||
if (self->array_index) {
|
||||
lua_replace(L, self->array_index);
|
||||
} else {
|
||||
self->array_index = lua_gettop(L);
|
||||
}
|
||||
}
|
||||
}
|
||||
switch (type) {
|
||||
case SPROTO_TINTEGER: {
|
||||
// notice: in lua 5.2, 52bit integer support (not 64)
|
||||
lua_Integer v = *(lua_Integer *)value;
|
||||
lua_pushinteger(L, v);
|
||||
break;
|
||||
}
|
||||
case SPROTO_TBOOLEAN: {
|
||||
int v = *(lua_Integer*)value;
|
||||
lua_pushboolean(L,v);
|
||||
break;
|
||||
}
|
||||
case SPROTO_TSTRING: {
|
||||
lua_pushlstring(L, value, length);
|
||||
break;
|
||||
}
|
||||
case SPROTO_TSTRUCT: {
|
||||
struct decode_ud sub;
|
||||
int r;
|
||||
lua_newtable(L);
|
||||
sub.L = L;
|
||||
sub.result_index = lua_gettop(L);
|
||||
sub.deep = self->deep + 1;
|
||||
sub.array_index = 0;
|
||||
sub.array_tag = NULL;
|
||||
|
||||
r = sproto_decode(st, value, length, decode, &sub);
|
||||
if (r < 0 || r != length)
|
||||
return r;
|
||||
lua_settop(L, sub.result_index);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
luaL_error(L, "Invalid type");
|
||||
}
|
||||
if (index > 0) {
|
||||
lua_rawseti(L, self->array_index, index);
|
||||
} else {
|
||||
lua_setfield(L, self->result_index, tagname);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const void *
|
||||
getbuffer(lua_State *L, int index, size_t *sz) {
|
||||
const void * buffer = NULL;
|
||||
int t = lua_type(L, index);
|
||||
if (t == LUA_TSTRING) {
|
||||
buffer = lua_tolstring(L, index, sz);
|
||||
} else {
|
||||
if (t != LUA_TUSERDATA && t != LUA_TLIGHTUSERDATA) {
|
||||
luaL_argerror(L, index, "Need a string or userdata");
|
||||
return NULL;
|
||||
}
|
||||
buffer = lua_touserdata(L, index);
|
||||
*sz = luaL_checkinteger(L, index+1);
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/*
|
||||
lightuserdata sproto_type
|
||||
string source / (lightuserdata , integer)
|
||||
return table
|
||||
*/
|
||||
static int
|
||||
ldecode(lua_State *L) {
|
||||
struct sproto_type * st = lua_touserdata(L, 1);
|
||||
const void * buffer;
|
||||
struct decode_ud self;
|
||||
size_t sz;
|
||||
int r;
|
||||
if (st == NULL) {
|
||||
return luaL_argerror(L, 1, "Need a sproto_type object");
|
||||
}
|
||||
sz = 0;
|
||||
buffer = getbuffer(L, 2, &sz);
|
||||
if (!lua_istable(L, -1)) {
|
||||
lua_newtable(L);
|
||||
}
|
||||
luaL_checkstack(L, ENCODE_DEEPLEVEL*2 + 8, NULL);
|
||||
self.L = L;
|
||||
self.result_index = lua_gettop(L);
|
||||
self.array_index = 0;
|
||||
self.array_tag = NULL;
|
||||
self.deep = 0;
|
||||
r = sproto_decode(st, buffer, (int)sz, decode, &self);
|
||||
if (r < 0) {
|
||||
return luaL_error(L, "decode error");
|
||||
}
|
||||
lua_settop(L, self.result_index);
|
||||
lua_pushinteger(L, r);
|
||||
return 2;
|
||||
}
|
||||
|
||||
static int
|
||||
ldumpproto(lua_State *L) {
|
||||
struct sproto * sp = lua_touserdata(L, 1);
|
||||
if (sp == NULL) {
|
||||
return luaL_argerror(L, 1, "Need a sproto_type object");
|
||||
}
|
||||
sproto_dump(sp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
string source / (lightuserdata , integer)
|
||||
return string
|
||||
*/
|
||||
static int
|
||||
lpack(lua_State *L) {
|
||||
size_t sz=0;
|
||||
const void * buffer = getbuffer(L, 1, &sz);
|
||||
// the worst-case space overhead of packing is 2 bytes per 2 KiB of input (256 words = 2KiB).
|
||||
size_t maxsz = (sz + 2047) / 2048 * 2 + sz;
|
||||
void * output = lua_touserdata(L, lua_upvalueindex(1));
|
||||
int bytes;
|
||||
int osz = lua_tointeger(L, lua_upvalueindex(2));
|
||||
if (osz < maxsz) {
|
||||
output = expand_buffer(L, osz, maxsz);
|
||||
}
|
||||
bytes = sproto_pack(buffer, sz, output, maxsz);
|
||||
if (bytes > maxsz) {
|
||||
return luaL_error(L, "packing error, return size = %d", bytes);
|
||||
}
|
||||
lua_pushlstring(L, output, bytes);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
lunpack(lua_State *L) {
|
||||
size_t sz=0;
|
||||
const void * buffer = getbuffer(L, 1, &sz);
|
||||
void * output = lua_touserdata(L, lua_upvalueindex(1));
|
||||
int osz = lua_tointeger(L, lua_upvalueindex(2));
|
||||
int r = sproto_unpack(buffer, sz, output, osz);
|
||||
if (r < 0)
|
||||
return luaL_error(L, "Invalid unpack stream");
|
||||
if (r > osz) {
|
||||
output = expand_buffer(L, osz, r);
|
||||
}
|
||||
r = sproto_unpack(buffer, sz, output, r);
|
||||
if (r < 0)
|
||||
return luaL_error(L, "Invalid unpack stream");
|
||||
lua_pushlstring(L, output, r);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void
|
||||
pushfunction_withbuffer(lua_State *L, const char * name, lua_CFunction func) {
|
||||
lua_newuserdata(L, ENCODE_BUFFERSIZE);
|
||||
lua_pushinteger(L, ENCODE_BUFFERSIZE);
|
||||
lua_pushcclosure(L, func, 2);
|
||||
lua_setfield(L, -2, name);
|
||||
}
|
||||
|
||||
static int
|
||||
lprotocol(lua_State *L) {
|
||||
struct sproto * sp = lua_touserdata(L, 1);
|
||||
struct sproto_type * request;
|
||||
struct sproto_type * response;
|
||||
int t;
|
||||
int tag;
|
||||
if (sp == NULL) {
|
||||
return luaL_argerror(L, 1, "Need a sproto_type object");
|
||||
}
|
||||
t = lua_type(L,2);
|
||||
if (t == LUA_TNUMBER) {
|
||||
const char * name;
|
||||
tag = lua_tointeger(L, 2);
|
||||
name = sproto_protoname(sp, tag);
|
||||
if (name == NULL)
|
||||
return 0;
|
||||
lua_pushstring(L, name);
|
||||
} else {
|
||||
const char * name = lua_tostring(L, 2);
|
||||
tag = sproto_prototag(sp, name);
|
||||
if (tag < 0)
|
||||
return 0;
|
||||
lua_pushinteger(L, tag);
|
||||
}
|
||||
request = sproto_protoquery(sp, tag, SPROTO_REQUEST);
|
||||
if (request == NULL) {
|
||||
lua_pushnil(L);
|
||||
} else {
|
||||
lua_pushlightuserdata(L, request);
|
||||
}
|
||||
response = sproto_protoquery(sp, tag, SPROTO_RESPONSE);
|
||||
if (response == NULL) {
|
||||
lua_pushnil(L);
|
||||
} else {
|
||||
lua_pushlightuserdata(L, response);
|
||||
}
|
||||
return 3;
|
||||
}
|
||||
|
||||
int
|
||||
luaopen_sproto_core(lua_State *L) {
|
||||
#ifdef luaL_checkversion
|
||||
luaL_checkversion(L);
|
||||
#endif
|
||||
luaL_Reg l[] = {
|
||||
{ "newproto", lnewproto },
|
||||
{ "deleteproto", ldeleteproto },
|
||||
{ "dumpproto", ldumpproto },
|
||||
{ "querytype", lquerytype },
|
||||
{ "decode", ldecode },
|
||||
{ "protocol", lprotocol },
|
||||
{ NULL, NULL },
|
||||
};
|
||||
luaL_newlib(L,l);
|
||||
pushfunction_withbuffer(L, "encode", lencode);
|
||||
pushfunction_withbuffer(L, "pack", lpack);
|
||||
pushfunction_withbuffer(L, "unpack", lunpack);
|
||||
return 1;
|
||||
}
|
||||
32
lualib-src/sproto/msvcint.h
Normal file
32
lualib-src/sproto/msvcint.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#ifndef msvc_int_h
|
||||
#define msvc_int_h
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# define inline __inline
|
||||
# ifndef _MSC_STDINT_H_
|
||||
# if (_MSC_VER < 1300)
|
||||
typedef signed char int8_t;
|
||||
typedef signed short int16_t;
|
||||
typedef signed int int32_t;
|
||||
typedef unsigned char uint8_t;
|
||||
typedef unsigned short uint16_t;
|
||||
typedef unsigned int uint32_t;
|
||||
# else
|
||||
typedef signed __int8 int8_t;
|
||||
typedef signed __int16 int16_t;
|
||||
typedef signed __int32 int32_t;
|
||||
typedef unsigned __int8 uint8_t;
|
||||
typedef unsigned __int16 uint16_t;
|
||||
typedef unsigned __int32 uint32_t;
|
||||
# endif
|
||||
typedef signed __int64 int64_t;
|
||||
typedef unsigned __int64 uint64_t;
|
||||
# endif
|
||||
|
||||
#else
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
1236
lualib-src/sproto/sproto.c
Normal file
1236
lualib-src/sproto/sproto.c
Normal file
File diff suppressed because it is too large
Load Diff
39
lualib-src/sproto/sproto.h
Normal file
39
lualib-src/sproto/sproto.h
Normal file
@@ -0,0 +1,39 @@
|
||||
#ifndef sproto_h
|
||||
#define sproto_h
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
struct sproto;
|
||||
struct sproto_type;
|
||||
|
||||
#define SPROTO_REQUEST 0
|
||||
#define SPROTO_RESPONSE 1
|
||||
|
||||
#define SPROTO_TINTEGER 0
|
||||
#define SPROTO_TBOOLEAN 1
|
||||
#define SPROTO_TSTRING 2
|
||||
#define SPROTO_TSTRUCT 3
|
||||
|
||||
struct sproto * sproto_create(const void * proto, size_t sz);
|
||||
void sproto_release(struct sproto *);
|
||||
|
||||
int sproto_prototag(struct sproto *, const char * name);
|
||||
const char * sproto_protoname(struct sproto *, int proto);
|
||||
// SPROTO_REQUEST(0) : request, SPROTO_RESPONSE(1): response
|
||||
struct sproto_type * sproto_protoquery(struct sproto *, int proto, int what);
|
||||
|
||||
struct sproto_type * sproto_type(struct sproto *, const char * type_name);
|
||||
|
||||
int sproto_pack(const void * src, int srcsz, void * buffer, int bufsz);
|
||||
int sproto_unpack(const void * src, int srcsz, void * buffer, int bufsz);
|
||||
|
||||
typedef int (*sproto_callback)(void *ud, const char *tagname, int type, int index, struct sproto_type *, void *value, int length);
|
||||
|
||||
int sproto_decode(struct sproto_type *, const void * data, int size, sproto_callback cb, void *ud);
|
||||
int sproto_encode(struct sproto_type *, void * buffer, int size, sproto_callback cb, void *ud);
|
||||
|
||||
// for debug use
|
||||
void sproto_dump(struct sproto *);
|
||||
const char * sproto_name(struct sproto_type *);
|
||||
|
||||
#endif
|
||||
@@ -4,7 +4,7 @@ local clusterd
|
||||
local cluster = {}
|
||||
|
||||
function cluster.call(node, address, ...)
|
||||
-- skynet.pack(...) will free by cluster.c.packrequest
|
||||
-- skynet.pack(...) will free by cluster.core.packrequest
|
||||
return skynet.call(clusterd, "lua", "req", node, address, skynet.pack(...))
|
||||
end
|
||||
|
||||
@@ -20,6 +20,10 @@ function cluster.reload()
|
||||
skynet.call(clusterd, "lua", "reload")
|
||||
end
|
||||
|
||||
function cluster.proxy(node, name)
|
||||
return skynet.call(clusterd, "lua", "proxy", node, name)
|
||||
end
|
||||
|
||||
skynet.init(function()
|
||||
clusterd = skynet.uniqueservice("clusterd")
|
||||
end)
|
||||
|
||||
@@ -81,10 +81,10 @@ function httpc.request(method, host, url, recvheader, header, content)
|
||||
end
|
||||
local fd = socket.connect(hostname, port)
|
||||
local ok , statuscode, body = pcall(request, fd,method, host, url, recvheader, header, content)
|
||||
socket.close(fd)
|
||||
if ok then
|
||||
return statuscode, body
|
||||
else
|
||||
socket.close(fd)
|
||||
error(statuscode)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -83,7 +83,7 @@ local function readall(readbytes, bodylimit)
|
||||
else
|
||||
-- identity mode
|
||||
if length then
|
||||
if length > bodylimit then
|
||||
if bodylimit and length > bodylimit then
|
||||
return 413
|
||||
end
|
||||
if #body >= length then
|
||||
@@ -130,6 +130,7 @@ local function writeall(writefunc, statuscode, bodyfunc, header)
|
||||
end
|
||||
else
|
||||
writefunc("\r\n0\r\n\r\n")
|
||||
break
|
||||
end
|
||||
end
|
||||
else
|
||||
|
||||
@@ -4,14 +4,15 @@ local LIMIT = 8192
|
||||
|
||||
local function chunksize(readbytes, body)
|
||||
while true do
|
||||
if #body > 128 then
|
||||
return
|
||||
end
|
||||
body = body .. readbytes()
|
||||
local f,e = body:find("\r\n",1,true)
|
||||
if f then
|
||||
return tonumber(body:sub(1,f-1),16), body:sub(e+1)
|
||||
end
|
||||
if #body > 128 then
|
||||
-- pervent the attacker send very long stream without \r\n
|
||||
return
|
||||
end
|
||||
body = body .. readbytes()
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
local cjson = require "cjson"
|
||||
|
||||
local jsonpack = {}
|
||||
|
||||
function jsonpack.pack(session, v)
|
||||
return string.format("%d+%s", session, cjson.encode(v))
|
||||
end
|
||||
|
||||
function jsonpack.response(session, v)
|
||||
return string.format("%d-%s",session, cjson.encode(v))
|
||||
end
|
||||
|
||||
function jsonpack.unpack(msg)
|
||||
local session,t,str = string.match(msg, "(%d+)(.)(.*)")
|
||||
assert(t == '+')
|
||||
return tonumber(session) , cjson.decode(str)
|
||||
end
|
||||
|
||||
return jsonpack
|
||||
@@ -129,6 +129,7 @@ function mongo.client( conf )
|
||||
response = dispatch_reply,
|
||||
auth = mongo_auth(obj),
|
||||
backup = backup,
|
||||
nodelay = true,
|
||||
}
|
||||
setmetatable(obj, client_meta)
|
||||
obj.__sock:connect(true) -- try connect only once
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
-- This is a deprecated module, use skynet.queue instead.
|
||||
|
||||
local skynet = require "skynet"
|
||||
local c = require "skynet.c"
|
||||
local c = require "skynet.core"
|
||||
|
||||
local mqueue = {}
|
||||
local init_once
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
local skynet = require "skynet"
|
||||
local mc = require "multicast.c"
|
||||
local mc = require "multicast.core"
|
||||
|
||||
local multicastd
|
||||
local multicast = {}
|
||||
|
||||
747
lualib/mysql.lua
Executable file
747
lualib/mysql.lua
Executable file
@@ -0,0 +1,747 @@
|
||||
-- Copyright (C) 2012 Yichun Zhang (agentzh)
|
||||
-- Copyright (C) 2014 Chang Feng
|
||||
-- This file is modified version from https://github.com/openresty/lua-resty-mysql
|
||||
-- The license is under the BSD license.
|
||||
|
||||
local socketchannel = require "socketchannel"
|
||||
local bit = require "bit32"
|
||||
local mysqlaux = require "mysqlaux.c"
|
||||
local crypt = require "crypt"
|
||||
|
||||
|
||||
local sub = string.sub
|
||||
local strbyte = string.byte
|
||||
local strchar = string.char
|
||||
local strfind = string.find
|
||||
local strrep = string.rep
|
||||
local null = nil
|
||||
local band = bit.band
|
||||
local bxor = bit.bxor
|
||||
local bor = bit.bor
|
||||
local lshift = bit.lshift
|
||||
local rshift = bit.rshift
|
||||
local sha1= crypt.sha1
|
||||
local concat = table.concat
|
||||
local unpack = unpack
|
||||
local setmetatable = setmetatable
|
||||
local error = error
|
||||
local tonumber = tonumber
|
||||
local new_tab = function (narr, nrec) return {} end
|
||||
|
||||
|
||||
local _M = { _VERSION = '0.13' }
|
||||
-- constants
|
||||
|
||||
local STATE_CONNECTED = 1
|
||||
local STATE_COMMAND_SENT = 2
|
||||
|
||||
local COM_QUERY = 0x03
|
||||
|
||||
local SERVER_MORE_RESULTS_EXISTS = 8
|
||||
|
||||
-- 16MB - 1, the default max allowed packet size used by libmysqlclient
|
||||
local FULL_PACKET_SIZE = 16777215
|
||||
|
||||
|
||||
local mt = { __index = _M }
|
||||
|
||||
|
||||
-- mysql field value type converters
|
||||
local converters = new_tab(0, 8)
|
||||
|
||||
for i = 0x01, 0x05 do
|
||||
-- tiny, short, long, float, double
|
||||
converters[i] = tonumber
|
||||
end
|
||||
-- converters[0x08] = tonumber -- long long
|
||||
converters[0x09] = tonumber -- int24
|
||||
converters[0x0d] = tonumber -- year
|
||||
converters[0xf6] = tonumber -- newdecimal
|
||||
|
||||
|
||||
local function _get_byte2(data, i)
|
||||
local a, b = strbyte(data, i, i + 1)
|
||||
return bor(a, lshift(b, 8)), i + 2
|
||||
end
|
||||
|
||||
|
||||
local function _get_byte3(data, i)
|
||||
local a, b, c = strbyte(data, i, i + 2)
|
||||
return bor(a, lshift(b, 8), lshift(c, 16)), i + 3
|
||||
end
|
||||
|
||||
|
||||
local function _get_byte4(data, i)
|
||||
local a, b, c, d = strbyte(data, i, i + 3)
|
||||
return bor(a, lshift(b, 8), lshift(c, 16), lshift(d, 24)), i + 4
|
||||
end
|
||||
|
||||
|
||||
local function _get_byte8(data, i)
|
||||
local a, b, c, d, e, f, g, h = strbyte(data, i, i + 7)
|
||||
|
||||
-- XXX workaround for the lack of 64-bit support in bitop:
|
||||
local lo = bor(a, lshift(b, 8), lshift(c, 16), lshift(d, 24))
|
||||
local hi = bor(e, lshift(f, 8), lshift(g, 16), lshift(h, 24))
|
||||
return lo + hi * 4294967296, i + 8
|
||||
|
||||
-- return bor(a, lshift(b, 8), lshift(c, 16), lshift(d, 24), lshift(e, 32),
|
||||
-- lshift(f, 40), lshift(g, 48), lshift(h, 56)), i + 8
|
||||
end
|
||||
|
||||
|
||||
local function _set_byte2(n)
|
||||
return strchar(band(n, 0xff), band(rshift(n, 8), 0xff))
|
||||
end
|
||||
|
||||
|
||||
local function _set_byte3(n)
|
||||
return strchar(band(n, 0xff),
|
||||
band(rshift(n, 8), 0xff),
|
||||
band(rshift(n, 16), 0xff))
|
||||
end
|
||||
|
||||
|
||||
local function _set_byte4(n)
|
||||
return strchar(band(n, 0xff),
|
||||
band(rshift(n, 8), 0xff),
|
||||
band(rshift(n, 16), 0xff),
|
||||
band(rshift(n, 24), 0xff))
|
||||
end
|
||||
|
||||
|
||||
local function _from_cstring(data, i)
|
||||
local last = strfind(data, "\0", i, true)
|
||||
if not last then
|
||||
return nil, nil
|
||||
end
|
||||
|
||||
return sub(data, i, last), last + 1
|
||||
end
|
||||
|
||||
|
||||
local function _to_cstring(data)
|
||||
return data .. "\0"
|
||||
end
|
||||
|
||||
|
||||
local function _to_binary_coded_string(data)
|
||||
return strchar(#data) .. data
|
||||
end
|
||||
|
||||
|
||||
local function _dump(data)
|
||||
local len = #data
|
||||
local bytes = new_tab(len, 0)
|
||||
for i = 1, len do
|
||||
bytes[i] = strbyte(data, i)
|
||||
end
|
||||
return concat(bytes, " ")
|
||||
end
|
||||
|
||||
|
||||
|
||||
local function _dumphex(bytes)
|
||||
local result ={}
|
||||
|
||||
for i = 1, string.len(bytes) do
|
||||
local charcode = tonumber(strbyte(bytes, i, i))
|
||||
local hexstr = string.format("%02X", charcode)
|
||||
result[i]=hexstr
|
||||
end
|
||||
|
||||
local res=table.concat(result, " ")
|
||||
return res
|
||||
end
|
||||
|
||||
|
||||
local function _compute_token(password, scramble)
|
||||
if password == "" then
|
||||
return ""
|
||||
end
|
||||
--_dump(scramble)
|
||||
|
||||
local stage1 = sha1(password)
|
||||
--print("stage1:", _dumphex(stage1) )
|
||||
local stage2 = sha1(stage1)
|
||||
local stage3 = sha1(scramble .. stage2)
|
||||
local n = #stage1
|
||||
local bytes = new_tab(n, 0)
|
||||
for i = 1, n do
|
||||
bytes[i] = strchar(bxor(strbyte(stage3, i), strbyte(stage1, i)))
|
||||
end
|
||||
|
||||
return concat(bytes)
|
||||
end
|
||||
|
||||
local function _compose_packet(self, req, size)
|
||||
self.packet_no = self.packet_no + 1
|
||||
|
||||
local packet = _set_byte3(size) .. strchar(self.packet_no) .. req
|
||||
return packet
|
||||
end
|
||||
|
||||
|
||||
local function _send_packet(self, req, size)
|
||||
local sock = self.sock
|
||||
|
||||
self.packet_no = self.packet_no + 1
|
||||
|
||||
|
||||
local packet = _set_byte3(size) .. strchar(self.packet_no) .. req
|
||||
|
||||
return socket.write(self.sock,packet)
|
||||
end
|
||||
|
||||
|
||||
local function _recv_packet(self,sock)
|
||||
|
||||
|
||||
local data = sock:read( 4)
|
||||
if not data then
|
||||
return nil, nil, "failed to receive packet header: "
|
||||
end
|
||||
|
||||
|
||||
local len, pos = _get_byte3(data, 1)
|
||||
|
||||
|
||||
if len == 0 then
|
||||
return nil, nil, "empty packet"
|
||||
end
|
||||
|
||||
if len > self._max_packet_size then
|
||||
return nil, nil, "packet size too big: " .. len
|
||||
end
|
||||
|
||||
local num = strbyte(data, pos)
|
||||
|
||||
self.packet_no = num
|
||||
|
||||
data = sock:read(len)
|
||||
|
||||
if not data then
|
||||
return nil, nil, "failed to read packet content: "
|
||||
end
|
||||
|
||||
|
||||
local field_count = strbyte(data, 1)
|
||||
local typ
|
||||
if field_count == 0x00 then
|
||||
typ = "OK"
|
||||
elseif field_count == 0xff then
|
||||
typ = "ERR"
|
||||
elseif field_count == 0xfe then
|
||||
typ = "EOF"
|
||||
elseif field_count <= 250 then
|
||||
typ = "DATA"
|
||||
end
|
||||
|
||||
return data, typ
|
||||
end
|
||||
|
||||
|
||||
local function _from_length_coded_bin(data, pos)
|
||||
local first = strbyte(data, pos)
|
||||
|
||||
if not first then
|
||||
return nil, pos
|
||||
end
|
||||
|
||||
if first >= 0 and first <= 250 then
|
||||
return first, pos + 1
|
||||
end
|
||||
|
||||
if first == 251 then
|
||||
return null, pos + 1
|
||||
end
|
||||
|
||||
if first == 252 then
|
||||
pos = pos + 1
|
||||
return _get_byte2(data, pos)
|
||||
end
|
||||
|
||||
if first == 253 then
|
||||
pos = pos + 1
|
||||
return _get_byte3(data, pos)
|
||||
end
|
||||
|
||||
if first == 254 then
|
||||
pos = pos + 1
|
||||
return _get_byte8(data, pos)
|
||||
end
|
||||
|
||||
return false, pos + 1
|
||||
end
|
||||
|
||||
|
||||
local function _from_length_coded_str(data, pos)
|
||||
local len
|
||||
len, pos = _from_length_coded_bin(data, pos)
|
||||
if len == nil or len == null then
|
||||
return null, pos
|
||||
end
|
||||
|
||||
return sub(data, pos, pos + len - 1), pos + len
|
||||
end
|
||||
|
||||
|
||||
local function _parse_ok_packet(packet)
|
||||
local res = new_tab(0, 5)
|
||||
local pos
|
||||
|
||||
res.affected_rows, pos = _from_length_coded_bin(packet, 2)
|
||||
|
||||
res.insert_id, pos = _from_length_coded_bin(packet, pos)
|
||||
|
||||
res.server_status, pos = _get_byte2(packet, pos)
|
||||
|
||||
res.warning_count, pos = _get_byte2(packet, pos)
|
||||
|
||||
|
||||
local message = sub(packet, pos)
|
||||
if message and message ~= "" then
|
||||
res.message = message
|
||||
end
|
||||
|
||||
|
||||
return res
|
||||
end
|
||||
|
||||
|
||||
local function _parse_eof_packet(packet)
|
||||
local pos = 2
|
||||
|
||||
local warning_count, pos = _get_byte2(packet, pos)
|
||||
local status_flags = _get_byte2(packet, pos)
|
||||
|
||||
return warning_count, status_flags
|
||||
end
|
||||
|
||||
|
||||
local function _parse_err_packet(packet)
|
||||
local errno, pos = _get_byte2(packet, 2)
|
||||
local marker = sub(packet, pos, pos)
|
||||
local sqlstate
|
||||
if marker == '#' then
|
||||
-- with sqlstate
|
||||
pos = pos + 1
|
||||
sqlstate = sub(packet, pos, pos + 5 - 1)
|
||||
pos = pos + 5
|
||||
end
|
||||
|
||||
local message = sub(packet, pos)
|
||||
return errno, message, sqlstate
|
||||
end
|
||||
|
||||
|
||||
local function _parse_result_set_header_packet(packet)
|
||||
local field_count, pos = _from_length_coded_bin(packet, 1)
|
||||
|
||||
local extra
|
||||
extra = _from_length_coded_bin(packet, pos)
|
||||
|
||||
return field_count, extra
|
||||
end
|
||||
|
||||
|
||||
local function _parse_field_packet(data)
|
||||
local col = new_tab(0, 2)
|
||||
local catalog, db, table, orig_table, orig_name, charsetnr, length
|
||||
local pos
|
||||
catalog, pos = _from_length_coded_str(data, 1)
|
||||
|
||||
|
||||
db, pos = _from_length_coded_str(data, pos)
|
||||
table, pos = _from_length_coded_str(data, pos)
|
||||
orig_table, pos = _from_length_coded_str(data, pos)
|
||||
col.name, pos = _from_length_coded_str(data, pos)
|
||||
|
||||
orig_name, pos = _from_length_coded_str(data, pos)
|
||||
|
||||
pos = pos + 1 -- ignore the filler
|
||||
|
||||
charsetnr, pos = _get_byte2(data, pos)
|
||||
|
||||
length, pos = _get_byte4(data, pos)
|
||||
|
||||
col.type = strbyte(data, pos)
|
||||
|
||||
--[[
|
||||
pos = pos + 1
|
||||
|
||||
col.flags, pos = _get_byte2(data, pos)
|
||||
|
||||
col.decimals = strbyte(data, pos)
|
||||
pos = pos + 1
|
||||
|
||||
local default = sub(data, pos + 2)
|
||||
if default and default ~= "" then
|
||||
col.default = default
|
||||
end
|
||||
--]]
|
||||
|
||||
return col
|
||||
end
|
||||
|
||||
|
||||
local function _parse_row_data_packet(data, cols, compact)
|
||||
local pos = 1
|
||||
local ncols = #cols
|
||||
local row
|
||||
if compact then
|
||||
row = new_tab(ncols, 0)
|
||||
else
|
||||
row = new_tab(0, ncols)
|
||||
end
|
||||
for i = 1, ncols do
|
||||
local value
|
||||
value, pos = _from_length_coded_str(data, pos)
|
||||
local col = cols[i]
|
||||
local typ = col.type
|
||||
local name = col.name
|
||||
|
||||
if value ~= null then
|
||||
local conv = converters[typ]
|
||||
if conv then
|
||||
value = conv(value)
|
||||
end
|
||||
end
|
||||
|
||||
if compact then
|
||||
row[i] = value
|
||||
|
||||
else
|
||||
row[name] = value
|
||||
end
|
||||
end
|
||||
|
||||
return row
|
||||
end
|
||||
|
||||
|
||||
local function _recv_field_packet(self, sock)
|
||||
local packet, typ, err = _recv_packet(self, sock)
|
||||
if not packet then
|
||||
return nil, err
|
||||
end
|
||||
|
||||
if typ == "ERR" then
|
||||
local errno, msg, sqlstate = _parse_err_packet(packet)
|
||||
return nil, msg, errno, sqlstate
|
||||
end
|
||||
|
||||
if typ ~= 'DATA' then
|
||||
return nil, "bad field packet type: " .. typ
|
||||
end
|
||||
|
||||
-- typ == 'DATA'
|
||||
|
||||
return _parse_field_packet(packet)
|
||||
end
|
||||
|
||||
local function _recv_decode_packet_resp(self)
|
||||
return function(sock)
|
||||
return true, _recv_packet(self,sock)
|
||||
end
|
||||
end
|
||||
|
||||
local function _recv_auth_resp(self)
|
||||
return function(sock)
|
||||
local packet, typ, err = _recv_packet(self,sock)
|
||||
if not packet then
|
||||
--print("recv auth resp : failed to receive the result packet")
|
||||
error ("failed to receive the result packet"..err)
|
||||
--return nil,err
|
||||
end
|
||||
|
||||
if typ == 'ERR' then
|
||||
local errno, msg, sqlstate = _parse_err_packet(packet)
|
||||
error( string.format("errno:%d, msg:%s,sqlstate:%s",errno,msg,sqlstate))
|
||||
--return nil, errno,msg, sqlstate
|
||||
end
|
||||
|
||||
if typ == 'EOF' then
|
||||
error "old pre-4.1 authentication protocol not supported"
|
||||
end
|
||||
|
||||
if typ ~= 'OK' then
|
||||
error "bad packet type: "
|
||||
end
|
||||
return true, true
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function _mysql_login(self,user,password,database)
|
||||
|
||||
return function(sockchannel)
|
||||
local packet, typ, err = sockchannel:response( _recv_decode_packet_resp(self) )
|
||||
--local aat={}
|
||||
if not packet then
|
||||
error( err )
|
||||
end
|
||||
|
||||
if typ == "ERR" then
|
||||
local errno, msg, sqlstate = _parse_err_packet(packet)
|
||||
error( string.format("errno:%d, msg:%s,sqlstate:%s",errno,msg,sqlstate))
|
||||
end
|
||||
|
||||
self.protocol_ver = strbyte(packet)
|
||||
|
||||
local server_ver, pos = _from_cstring(packet, 2)
|
||||
if not server_ver then
|
||||
error "bad handshake initialization packet: bad server version"
|
||||
end
|
||||
|
||||
self._server_ver = server_ver
|
||||
|
||||
|
||||
local thread_id, pos = _get_byte4(packet, pos)
|
||||
|
||||
local scramble1 = sub(packet, pos, pos + 8 - 1)
|
||||
if not scramble1 then
|
||||
error "1st part of scramble not found"
|
||||
end
|
||||
|
||||
pos = pos + 9 -- skip filler
|
||||
|
||||
-- two lower bytes
|
||||
self._server_capabilities, pos = _get_byte2(packet, pos)
|
||||
|
||||
self._server_lang = strbyte(packet, pos)
|
||||
pos = pos + 1
|
||||
|
||||
self._server_status, pos = _get_byte2(packet, pos)
|
||||
|
||||
local more_capabilities
|
||||
more_capabilities, pos = _get_byte2(packet, pos)
|
||||
|
||||
self._server_capabilities = bor(self._server_capabilities,
|
||||
lshift(more_capabilities, 16))
|
||||
|
||||
|
||||
local len = 21 - 8 - 1
|
||||
|
||||
pos = pos + 1 + 10
|
||||
|
||||
local scramble_part2 = sub(packet, pos, pos + len - 1)
|
||||
if not scramble_part2 then
|
||||
error "2nd part of scramble not found"
|
||||
end
|
||||
|
||||
|
||||
local scramble = scramble1..scramble_part2
|
||||
local token = _compute_token(password, scramble)
|
||||
|
||||
local client_flags = 260047;
|
||||
|
||||
local req = _set_byte4(client_flags)
|
||||
.. _set_byte4(self._max_packet_size)
|
||||
.. "\0" -- TODO: add support for charset encoding
|
||||
.. strrep("\0", 23)
|
||||
.. _to_cstring(user)
|
||||
.. _to_binary_coded_string(token)
|
||||
.. _to_cstring(database)
|
||||
|
||||
local packet_len = 4 + 4 + 1 + 23 + #user + 1
|
||||
+ #token + 1 + #database + 1
|
||||
|
||||
local authpacket=_compose_packet(self,req,packet_len)
|
||||
return sockchannel:request(authpacket,_recv_auth_resp(self))
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function _compose_query(self, query)
|
||||
|
||||
self.packet_no = -1
|
||||
|
||||
local cmd_packet = strchar(COM_QUERY) .. query
|
||||
local packet_len = 1 + #query
|
||||
|
||||
local querypacket = _compose_packet(self, cmd_packet, packet_len)
|
||||
return querypacket
|
||||
end
|
||||
|
||||
|
||||
|
||||
local function read_result(self, sock)
|
||||
local packet, typ, err = _recv_packet(self, sock)
|
||||
if not packet then
|
||||
return nil, err
|
||||
--error( err )
|
||||
end
|
||||
|
||||
if typ == "ERR" then
|
||||
local errno, msg, sqlstate = _parse_err_packet(packet)
|
||||
return nil, msg, errno, sqlstate
|
||||
--error( string.format("errno:%d, msg:%s,sqlstate:%s",errno,msg,sqlstate))
|
||||
end
|
||||
|
||||
if typ == 'OK' then
|
||||
local res = _parse_ok_packet(packet)
|
||||
if res and band(res.server_status, SERVER_MORE_RESULTS_EXISTS) ~= 0 then
|
||||
return res, "again"
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
if typ ~= 'DATA' then
|
||||
return nil, "packet type " .. typ .. " not supported"
|
||||
--error( "packet type " .. typ .. " not supported" )
|
||||
end
|
||||
|
||||
-- typ == 'DATA'
|
||||
|
||||
local field_count, extra = _parse_result_set_header_packet(packet)
|
||||
|
||||
local cols = new_tab(field_count, 0)
|
||||
for i = 1, field_count do
|
||||
local col, err, errno, sqlstate = _recv_field_packet(self, sock)
|
||||
if not col then
|
||||
return nil, err, errno, sqlstate
|
||||
--error( string.format("errno:%d, msg:%s,sqlstate:%s",errno,msg,sqlstate))
|
||||
end
|
||||
|
||||
cols[i] = col
|
||||
end
|
||||
|
||||
local packet, typ, err = _recv_packet(self, sock)
|
||||
if not packet then
|
||||
--error( err)
|
||||
return nil, err
|
||||
end
|
||||
|
||||
if typ ~= 'EOF' then
|
||||
--error ( "unexpected packet type " .. typ .. " while eof packet is ".. "expected" )
|
||||
return nil, "unexpected packet type " .. typ .. " while eof packet is ".. "expected"
|
||||
end
|
||||
|
||||
-- typ == 'EOF'
|
||||
|
||||
local compact = self.compact
|
||||
|
||||
local rows = new_tab( 4, 0)
|
||||
local i = 0
|
||||
while true do
|
||||
packet, typ, err = _recv_packet(self, sock)
|
||||
if not packet then
|
||||
--error (err)
|
||||
return nil, err
|
||||
end
|
||||
|
||||
if typ == 'EOF' then
|
||||
local warning_count, status_flags = _parse_eof_packet(packet)
|
||||
|
||||
if band(status_flags, SERVER_MORE_RESULTS_EXISTS) ~= 0 then
|
||||
return rows, "again"
|
||||
end
|
||||
|
||||
break
|
||||
end
|
||||
|
||||
-- if typ ~= 'DATA' then
|
||||
-- return nil, 'bad row packet type: ' .. typ
|
||||
-- end
|
||||
|
||||
-- typ == 'DATA'
|
||||
|
||||
local row = _parse_row_data_packet(packet, cols, compact)
|
||||
i = i + 1
|
||||
rows[i] = row
|
||||
end
|
||||
|
||||
return rows
|
||||
end
|
||||
|
||||
local function _query_resp(self)
|
||||
return function(sock)
|
||||
local res, err, errno, sqlstate = read_result(self,sock)
|
||||
if not res then
|
||||
local badresult ={}
|
||||
badresult.badresult = true
|
||||
badresult.err = err
|
||||
badresult.errno = errno
|
||||
badresult.sqlstate = sqlstate
|
||||
return true , badresult
|
||||
end
|
||||
if err ~= "again" then
|
||||
return true, res
|
||||
end
|
||||
local mulitresultset = {res}
|
||||
mulitresultset.mulitresultset = true
|
||||
local i =2
|
||||
while err =="again" do
|
||||
res, err, errno, sqlstate = read_result(self,sock)
|
||||
if not res then
|
||||
return true, mulitresultset
|
||||
end
|
||||
mulitresultset[i]=res
|
||||
i=i+1
|
||||
end
|
||||
return true, mulitresultset
|
||||
end
|
||||
end
|
||||
|
||||
function _M.connect( opts)
|
||||
|
||||
local self = setmetatable( {}, mt)
|
||||
|
||||
local max_packet_size = opts.max_packet_size
|
||||
if not max_packet_size then
|
||||
max_packet_size = 1024 * 1024 -- default 1 MB
|
||||
end
|
||||
self._max_packet_size = max_packet_size
|
||||
self.compact = opts.compact_arrays
|
||||
|
||||
|
||||
local database = opts.database or ""
|
||||
local user = opts.user or ""
|
||||
local password = opts.password or ""
|
||||
|
||||
local channel = socketchannel.channel {
|
||||
host = opts.host,
|
||||
port = opts.port or 3306,
|
||||
auth = _mysql_login(self,user,password,database ),
|
||||
}
|
||||
-- try connect first only once
|
||||
channel:connect(true)
|
||||
self.sockchannel = channel
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
|
||||
function _M.disconnect(self)
|
||||
self.sockchannel:close()
|
||||
setmetatable(self, nil)
|
||||
end
|
||||
|
||||
|
||||
function _M.query(self, query)
|
||||
local querypacket = _compose_query(self, query)
|
||||
local sockchannel = self.sockchannel
|
||||
if not self.query_resp then
|
||||
self.query_resp = _query_resp(self)
|
||||
end
|
||||
return sockchannel:request( querypacket, self.query_resp )
|
||||
end
|
||||
|
||||
function _M.server_ver(self)
|
||||
return self._server_ver
|
||||
end
|
||||
|
||||
|
||||
function _M.quote_sql_str( str)
|
||||
return mysqlaux.quote_sql_str(str)
|
||||
end
|
||||
|
||||
function _M.set_compact_arrays(self, value)
|
||||
self.compact = value
|
||||
end
|
||||
|
||||
|
||||
return _M
|
||||
@@ -84,6 +84,7 @@ function redis.connect(db_conf)
|
||||
host = db_conf.host,
|
||||
port = db_conf.port or 6379,
|
||||
auth = redis_login(db_conf.auth, db_conf.db),
|
||||
nodelay = true,
|
||||
}
|
||||
-- try connect first only once
|
||||
channel:connect(true)
|
||||
@@ -199,6 +200,7 @@ function redis.watch(db_conf)
|
||||
host = db_conf.host,
|
||||
port = db_conf.port or 6379,
|
||||
auth = watch_login(obj, db_conf.auth),
|
||||
nodelay = true,
|
||||
}
|
||||
obj.__sock = channel
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user