Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
ruanhaishen
Nodemcu Firmware
Commits
54004a4b
Commit
54004a4b
authored
Mar 16, 2015
by
funshine
Browse files
fix cjson bugs and reduce mem usage
parent
c0d690ce
Changes
10
Hide whitespace changes
Inline
Side-by-side
app/cjson/NEWS
deleted
100644 → 0
View file @
c0d690ce
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
app/cjson/dtoa.c
.bak
→
app/cjson/dtoa.c
View file @
54004a4b
...
@@ -184,7 +184,7 @@
...
@@ -184,7 +184,7 @@
* a decimal expansion to decide close cases. This logic is only
* a decimal expansion to decide close cases. This logic is only
* used for input more than STRTOD_DIGLIM digits long (default 40).
* used for input more than STRTOD_DIGLIM digits long (default 40).
*/
*/
#if 0
#include "dtoa_config.h"
#include "dtoa_config.h"
#ifndef Long
#ifndef Long
...
@@ -4356,3 +4356,4 @@ dtoa
...
@@ -4356,3 +4356,4 @@ dtoa
#ifdef __cplusplus
#ifdef __cplusplus
}
}
#endif
#endif
#endif
app/cjson/dtoa_config.h
.bak
→
app/cjson/dtoa_config.h
View file @
54004a4b
#ifndef _DTOA_CONFIG_H
#ifndef _DTOA_CONFIG_H
#define _DTOA_CONFIG_H
#define _DTOA_CONFIG_H
#if 0
#include <stdio.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdint.h>
...
@@ -67,7 +67,7 @@ static pthread_mutex_t private_dtoa_lock[2] = {
...
@@ -67,7 +67,7 @@ static pthread_mutex_t private_dtoa_lock[2] = {
} while (0)
} while (0)
#endif
/* MULTIPLE_THREADS */
#endif
/* MULTIPLE_THREADS */
#endif
#endif
/* _DTOA_CONFIG_H */
#endif
/* _DTOA_CONFIG_H */
/* vi:ai et sw=4 ts=4:
/* vi:ai et sw=4 ts=4:
...
...
app/cjson/g_fmt.c
View file @
54004a4b
...
@@ -21,7 +21,7 @@
...
@@ -21,7 +21,7 @@
* it suffices to declare buf
* it suffices to declare buf
* char buf[32];
* char buf[32];
*/
*/
#if 0
#ifdef __cplusplus
#ifdef __cplusplus
extern "C" {
extern "C" {
#endif
#endif
...
@@ -109,3 +109,4 @@ fpconv_g_fmt(char *b, double x, int precision)
...
@@ -109,3 +109,4 @@ fpconv_g_fmt(char *b, double x, int precision)
#endif
#endif
return b - b0;
return b - b0;
}
}
#endif
app/cjson/lua-cjson-2.1devel-1.rockspec
deleted
100644 → 0
View file @
c0d690ce
package
=
"lua-cjson"
version
=
"2.1devel-1"
source
=
{
url
=
"http://www.kyne.com.au/~mark/software/download/lua-cjson-2.1devel.zip"
,
}
description
=
{
summary
=
"A fast JSON encoding/parsing module"
,
detailed
=
[[
The
Lua
CJSON
module
provides
JSON
support
for
Lua
.
It
features
:
-
Fast
,
standards
compliant
encoding
/
parsing
routines
-
Full
support
for
JSON
with
UTF
-
8
,
including
decoding
surrogate
pairs
-
Optional
run
-
time
support
for
common
exceptions
to
the
JSON
specification
(
infinity
,
NaN
,..)
-
No
dependencies
on
other
libraries
]],
homepage
=
"http://www.kyne.com.au/~mark/software/lua-cjson.php"
,
license
=
"MIT"
}
dependencies
=
{
"lua >= 5.1"
}
build
=
{
type
=
"builtin"
,
modules
=
{
cjson
=
{
sources
=
{
"lua_cjson.c"
,
"strbuf.c"
,
"fpconv.c"
},
defines
=
{
--
LuaRocks
does
not
support
platform
specific
configuration
for
Solaris
.
--
Uncomment
the
line
below
on
Solaris
platforms
if
required
.
--
"USE_INTERNAL_ISINF"
}
}
},
install
=
{
lua
=
{
[
"cjson.util"
]
=
"lua/cjson/util.lua"
},
bin
=
{
json2lua
=
"lua/json2lua.lua"
,
lua2json
=
"lua/lua2json.lua"
}
},
--
Override
default
build
options
(
per
platform
)
platforms
=
{
win32
=
{
modules
=
{
cjson
=
{
defines
=
{
"DISABLE_INVALID_NUMBERS"
}
}
}
}
},
copy_directories
=
{
"tests"
}
}
--
vi
:
ai
et
sw
=
4
ts
=
4
:
app/cjson/lua-cjson.spec
deleted
100644 → 0
View file @
c0d690ce
%define luaver 5.1
%define lualibdir %{_libdir}/lua/%{luaver}
%define luadatadir %{_datadir}/lua/%{luaver}
Name: lua-cjson
Version: 2.1devel
Release: 1%{?dist}
Summary: A fast JSON encoding/parsing module for Lua
Group: Development/Libraries
License: MIT
URL: http://www.kyne.com.au/~mark/software/lua-cjson/
Source0: http://www.kyne.com.au/~mark/software/lua-cjson/download/lua-cjson-%{version}.tar.gz
BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX)
BuildRequires: lua >= %{luaver}, lua-devel >= %{luaver}
Requires: lua >= %{luaver}
%description
The Lua CJSON module provides JSON support for Lua. It features:
- Fast, standards compliant encoding/parsing routines
- Full support for JSON with UTF-8, including decoding surrogate pairs
- Optional run-time support for common exceptions to the JSON specification
(infinity, NaN,..)
- No dependencies on other libraries
%prep
%setup -q
%build
make %{?_smp_mflags} CFLAGS="%{optflags}" LUA_INCLUDE_DIR="%{_includedir}"
%install
rm -rf "$RPM_BUILD_ROOT"
make install DESTDIR="$RPM_BUILD_ROOT" LUA_CMODULE_DIR="%{lualibdir}"
make install-extra DESTDIR="$RPM_BUILD_ROOT" LUA_MODULE_DIR="%{luadatadir}" \
LUA_BIN_DIR="%{_bindir}"
%clean
rm -rf "$RPM_BUILD_ROOT"
%preun
/bin/rm -f "%{luadatadir}/cjson/tests/utf8.dat"
%files
%defattr(-,root,root,-)
%doc LICENSE NEWS performance.html performance.txt manual.html manual.txt rfc4627.txt THANKS
%{lualibdir}/*
%{luadatadir}/*
%{_bindir}/*
%changelog
* Thu Mar 1 2012 Mark Pulford <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
app/cjson/performance.txt
deleted
100644 → 0
View file @
c0d690ce
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:
app/cjson/strbuf.c
View file @
54004a4b
...
@@ -41,7 +41,7 @@
...
@@ -41,7 +41,7 @@
// exit(-1);
// exit(-1);
// }
// }
void
strbuf_init
(
strbuf_t
*
s
,
int
len
)
int
strbuf_init
(
strbuf_t
*
s
,
int
len
)
{
{
int
size
;
int
size
;
...
@@ -61,10 +61,11 @@ void strbuf_init(strbuf_t *s, int len)
...
@@ -61,10 +61,11 @@ void strbuf_init(strbuf_t *s, int len)
s
->
buf
=
c_malloc
(
size
);
s
->
buf
=
c_malloc
(
size
);
if
(
!
s
->
buf
){
if
(
!
s
->
buf
){
NODE_ERR
(
"not enough memory
\n
"
);
NODE_ERR
(
"not enough memory
\n
"
);
return
;
return
-
1
;
}
}
strbuf_ensure_null
(
s
);
strbuf_ensure_null
(
s
);
return
0
;
}
}
strbuf_t
*
strbuf_new
(
int
len
)
strbuf_t
*
strbuf_new
(
int
len
)
...
@@ -74,7 +75,7 @@ strbuf_t *strbuf_new(int len)
...
@@ -74,7 +75,7 @@ strbuf_t *strbuf_new(int len)
s
=
c_malloc
(
sizeof
(
strbuf_t
));
s
=
c_malloc
(
sizeof
(
strbuf_t
));
if
(
!
s
){
if
(
!
s
){
NODE_ERR
(
"not enough memory
\n
"
);
NODE_ERR
(
"not enough memory
\n
"
);
return
;
return
NULL
;
}
}
strbuf_init
(
s
,
len
);
strbuf_init
(
s
,
len
);
...
@@ -85,16 +86,17 @@ strbuf_t *strbuf_new(int len)
...
@@ -85,16 +86,17 @@ strbuf_t *strbuf_new(int len)
return
s
;
return
s
;
}
}
void
strbuf_set_increment
(
strbuf_t
*
s
,
int
increment
)
int
strbuf_set_increment
(
strbuf_t
*
s
,
int
increment
)
{
{
/* Increment > 0: Linear buffer growth rate
/* Increment > 0: Linear buffer growth rate
* Increment < -1: Exponential buffer growth rate */
* Increment < -1: Exponential buffer growth rate */
if
(
increment
==
0
||
increment
==
-
1
){
if
(
increment
==
0
||
increment
==
-
1
){
NODE_ERR
(
"BUG: Invalid string increment"
);
NODE_ERR
(
"BUG: Invalid string increment"
);
return
;
return
-
1
;
}
}
s
->
increment
=
increment
;
s
->
increment
=
increment
;
return
0
;
}
}
static
inline
void
debug_stats
(
strbuf_t
*
s
)
static
inline
void
debug_stats
(
strbuf_t
*
s
)
...
@@ -169,7 +171,7 @@ static int calculate_new_size(strbuf_t *s, int len)
...
@@ -169,7 +171,7 @@ static int calculate_new_size(strbuf_t *s, int len)
/* Ensure strbuf can handle a string length bytes long (ignoring NULL
/* Ensure strbuf can handle a string length bytes long (ignoring NULL
* optional termination). */
* optional termination). */
void
strbuf_resize
(
strbuf_t
*
s
,
int
len
)
int
strbuf_resize
(
strbuf_t
*
s
,
int
len
)
{
{
int
newsize
;
int
newsize
;
...
@@ -180,13 +182,14 @@ void strbuf_resize(strbuf_t *s, int len)
...
@@ -180,13 +182,14 @@ void strbuf_resize(strbuf_t *s, int len)
(
long
)
s
,
s
->
size
,
newsize
);
(
long
)
s
,
s
->
size
,
newsize
);
}
}
s
->
size
=
newsize
;
s
->
buf
=
(
char
*
)
c_realloc
(
s
->
buf
,
newsize
);
s
->
buf
=
(
char
*
)
c_realloc
(
s
->
buf
,
s
->
size
);
if
(
!
s
->
buf
){
if
(
!
s
->
buf
){
NODE_ERR
(
"not enough memory"
);
NODE_ERR
(
"not enough memory"
);
return
;
return
-
1
;
}
}
s
->
size
=
newsize
;
s
->
reallocs
++
;
s
->
reallocs
++
;
return
0
;
}
}
void
strbuf_append_string
(
strbuf_t
*
s
,
const
char
*
str
)
void
strbuf_append_string
(
strbuf_t
*
s
,
const
char
*
str
)
...
...
app/cjson/strbuf.h
View file @
54004a4b
...
@@ -50,15 +50,15 @@ typedef struct {
...
@@ -50,15 +50,15 @@ typedef struct {
/* Initialise */
/* Initialise */
extern
strbuf_t
*
strbuf_new
(
int
len
);
extern
strbuf_t
*
strbuf_new
(
int
len
);
extern
void
strbuf_init
(
strbuf_t
*
s
,
int
len
);
extern
int
strbuf_init
(
strbuf_t
*
s
,
int
len
);
extern
void
strbuf_set_increment
(
strbuf_t
*
s
,
int
increment
);
extern
int
strbuf_set_increment
(
strbuf_t
*
s
,
int
increment
);
/* Release */
/* Release */
extern
void
strbuf_free
(
strbuf_t
*
s
);
extern
void
strbuf_free
(
strbuf_t
*
s
);
extern
char
*
strbuf_free_to_string
(
strbuf_t
*
s
,
int
*
len
);
extern
char
*
strbuf_free_to_string
(
strbuf_t
*
s
,
int
*
len
);
/* Management */
/* Management */
extern
void
strbuf_resize
(
strbuf_t
*
s
,
int
len
);
extern
int
strbuf_resize
(
strbuf_t
*
s
,
int
len
);
static
int
strbuf_empty_length
(
strbuf_t
*
s
);
static
int
strbuf_empty_length
(
strbuf_t
*
s
);
static
int
strbuf_length
(
strbuf_t
*
s
);
static
int
strbuf_length
(
strbuf_t
*
s
);
static
char
*
strbuf_string
(
strbuf_t
*
s
,
int
*
len
);
static
char
*
strbuf_string
(
strbuf_t
*
s
,
int
*
len
);
...
@@ -127,13 +127,13 @@ static inline void strbuf_append_char_unsafe(strbuf_t *s, const char c)
...
@@ -127,13 +127,13 @@ static inline void strbuf_append_char_unsafe(strbuf_t *s, const char c)
static
inline
void
strbuf_append_mem
(
strbuf_t
*
s
,
const
char
*
c
,
int
len
)
static
inline
void
strbuf_append_mem
(
strbuf_t
*
s
,
const
char
*
c
,
int
len
)
{
{
strbuf_ensure_empty_length
(
s
,
len
);
strbuf_ensure_empty_length
(
s
,
len
);
memcpy
(
s
->
buf
+
s
->
length
,
c
,
len
);
c_
memcpy
(
s
->
buf
+
s
->
length
,
c
,
len
);
s
->
length
+=
len
;
s
->
length
+=
len
;
}
}
static
inline
void
strbuf_append_mem_unsafe
(
strbuf_t
*
s
,
const
char
*
c
,
int
len
)
static
inline
void
strbuf_append_mem_unsafe
(
strbuf_t
*
s
,
const
char
*
c
,
int
len
)
{
{
memcpy
(
s
->
buf
+
s
->
length
,
c
,
len
);
c_
memcpy
(
s
->
buf
+
s
->
length
,
c
,
len
);
s
->
length
+=
len
;
s
->
length
+=
len
;
}
}
...
...
app/modules/cjson.c
View file @
54004a4b
...
@@ -42,6 +42,7 @@
...
@@ -42,6 +42,7 @@
#include "c_limits.h"
#include "c_limits.h"
#include "lua.h"
#include "lua.h"
#include "lauxlib.h"
#include "lauxlib.h"
#include "flash_api.h"
#include "strbuf.h"
#include "strbuf.h"
#ifdef LUA_NUMBER_INTEGRAL
#ifdef LUA_NUMBER_INTEGRAL
...
@@ -50,7 +51,6 @@
...
@@ -50,7 +51,6 @@
#define FPCONV_G_FMT_BUFSIZE 32
#define FPCONV_G_FMT_BUFSIZE 32
#define fpconv_strtod c_strtod
#define fpconv_strtod c_strtod
#define fpconv_init() ((void)0)
#define fpconv_init() ((void)0)
#define fpconv_g_fmt fpconv_g_fmt
#endif
#endif
#ifndef CJSON_MODNAME
#ifndef CJSON_MODNAME
...
@@ -73,7 +73,7 @@
...
@@ -73,7 +73,7 @@
#define DEFAULT_DECODE_MAX_DEPTH 1000
#define DEFAULT_DECODE_MAX_DEPTH 1000
#define DEFAULT_ENCODE_INVALID_NUMBERS 0
#define DEFAULT_ENCODE_INVALID_NUMBERS 0
#define DEFAULT_DECODE_INVALID_NUMBERS 1
#define DEFAULT_DECODE_INVALID_NUMBERS 1
#define DEFAULT_ENCODE_KEEP_BUFFER
1
#define DEFAULT_ENCODE_KEEP_BUFFER
0
#define DEFAULT_ENCODE_NUMBER_PRECISION 14
#define DEFAULT_ENCODE_NUMBER_PRECISION 14
#ifdef DISABLE_INVALID_NUMBERS
#ifdef DISABLE_INVALID_NUMBERS
...
@@ -97,7 +97,7 @@ typedef enum {
...
@@ -97,7 +97,7 @@ typedef enum {
T_ERROR
,
T_ERROR
,
T_UNKNOWN
T_UNKNOWN
}
json_token_type_t
;
}
json_token_type_t
;
#if 0
static const char *json_token_type_name[] = {
static const char *json_token_type_name[] = {
"T_OBJ_BEGIN",
"T_OBJ_BEGIN",
"T_OBJ_END",
"T_OBJ_END",
...
@@ -115,10 +115,27 @@ static const char *json_token_type_name[] = {
...
@@ -115,10 +115,27 @@ static const char *json_token_type_name[] = {
"T_UNKNOWN",
"T_UNKNOWN",
NULL
NULL
};
};
#endif
static
const
char
json_token_type_name
[
14
][
16
]
ICACHE_STORE_ATTR
ICACHE_RODATA_ATTR
=
{
{
'T'
,
'_'
,
'O'
,
'B'
,
'J'
,
'_'
,
'B'
,
'E'
,
'G'
,
'I'
,
'N'
,
0
},
{
'T'
,
'_'
,
'O'
,
'B'
,
'J'
,
'_'
,
'E'
,
'N'
,
'D'
,
0
},
{
'T'
,
'_'
,
'A'
,
'R'
,
'R'
,
'_'
,
'B'
,
'E'
,
'G'
,
'I'
,
'N'
,
0
},
{
'T'
,
'_'
,
'A'
,
'R'
,
'R'
,
'_'
,
'E'
,
'N'
,
'D'
,
0
},
{
'T'
,
'_'
,
'S'
,
'T'
,
'R'
,
'I'
,
'N'
,
'G'
,
0
},
{
'T'
,
'_'
,
'N'
,
'U'
,
'M'
,
'B'
,
'E'
,
'R'
,
0
},
{
'T'
,
'_'
,
'B'
,
'O'
,
'O'
,
'L'
,
'E'
,
'A'
,
'N'
,
0
},
{
'T'
,
'_'
,
'N'
,
'U'
,
'L'
,
'L'
,
0
},
{
'T'
,
'_'
,
'C'
,
'O'
,
'L'
,
'O'
,
'N'
,
0
},
{
'T'
,
'_'
,
'C'
,
'O'
,
'M'
,
'M'
,
'A'
,
0
},
{
'T'
,
'_'
,
'E'
,
'N'
,
'D'
,
0
},
{
'T'
,
'_'
,
'W'
,
'H'
,
'I'
,
'T'
,
'E'
,
'S'
,
'P'
,
'A'
,
'C'
,
'E'
,
0
},
{
'T'
,
'_'
,
'E'
,
'R'
,
'R'
,
'O'
,
'R'
,
0
},
{
'T'
,
'_'
,
'U'
,
'N'
,
'K'
,
'N'
,
'O'
,
'W'
,
'N'
,
0
}
};
typedef
struct
{
typedef
struct
{
json_token_type_t
ch2token
[
256
];
//
json_token_type_t ch2token[256];
// 256*4 = 1024 byte
char
escape2char
[
256
];
/* Decoding */
//
char escape2char[256]; /* Decoding */
/* encode_buf is only allocated and used when
/* encode_buf is only allocated and used when
* encode_keep_buffer is set */
* encode_keep_buffer is set */
...
@@ -155,6 +172,7 @@ typedef struct {
...
@@ -155,6 +172,7 @@ typedef struct {
int
string_len
;
int
string_len
;
}
json_token_t
;
}
json_token_t
;
#if 0
static const char *char2escape[256] = {
static const char *char2escape[256] = {
"\\u0000", "\\u0001", "\\u0002", "\\u0003",
"\\u0000", "\\u0001", "\\u0002", "\\u0003",
"\\u0004", "\\u0005", "\\u0006", "\\u0007",
"\\u0004", "\\u0005", "\\u0006", "\\u0007",
...
@@ -193,6 +211,99 @@ static const char *char2escape[256] = {
...
@@ -193,6 +211,99 @@ static const char *char2escape[256] = {
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
};
};
#endif
/* ===== HELPER FUNCTION ===== */
static
const
char
escape_array
[
36
][
8
]
ICACHE_STORE_ATTR
ICACHE_RODATA_ATTR
=
{
{
'\\'
,
'u'
,
'0'
,
'0'
,
'0'
,
'0'
,
'\0'
,
'\0'
},
{
'\\'
,
'u'
,
'0'
,
'0'
,
'0'
,
'1'
,
'\0'
,
'\0'
},
{
'\\'
,
'u'
,
'0'
,
'0'
,
'0'
,
'2'
,
'\0'
,
'\0'
},
{
'\\'
,
'u'
,
'0'
,
'0'
,
'0'
,
'3'
,
'\0'
,
'\0'
},
{
'\\'
,
'u'
,
'0'
,
'0'
,
'0'
,
'4'
,
'\0'
,
'\0'
},
{
'\\'
,
'u'
,
'0'
,
'0'
,
'0'
,
'5'
,
'\0'
,
'\0'
},
{
'\\'
,
'u'
,
'0'
,
'0'
,
'0'
,
'6'
,
'\0'
,
'\0'
},
{
'\\'
,
'u'
,
'0'
,
'0'
,
'0'
,
'7'
,
'\0'
,
'\0'
},
{
'\\'
,
'b'
,
'\0'
,
'\0'
,
'\0'
,
'\0'
,
'\0'
,
'\0'
},
{
'\\'
,
't'
,
'\0'
,
'\0'
,
'\0'
,
'\0'
,
'\0'
,
'\0'
},
{
'\\'
,
'n'
,
'\0'
,
'\0'
,
'\0'
,
'\0'
,
'\0'
,
'\0'
},
{
'\\'
,
'u'
,
'0'
,
'0'
,
'0'
,
'b'
,
'\0'
,
'\0'
},
{
'\\'
,
'f'
,
'\0'
,
'\0'
,
'\0'
,
'\0'
,
'\0'
,
'\0'
},
{
'\\'
,
'r'
,
'\0'
,
'\0'
,
'\0'
,
'\0'
,
'\0'
,
'\0'
},
{
'\\'
,
'u'
,
'0'
,
'0'
,
'0'
,
'e'
,
'\0'
,
'\0'
},
{
'\\'
,
'u'
,
'0'
,
'0'
,
'0'
,
'f'
,
'\0'
,
'\0'
},
{
'\\'
,
'u'
,
'0'
,
'0'
,
'1'
,
'0'
,
'\0'
,
'\0'
},
{
'\\'
,
'u'
,
'0'
,
'0'
,
'1'
,
'1'
,
'\0'
,
'\0'
},
{
'\\'
,
'u'
,
'0'
,
'0'
,
'1'
,
'2'
,
'\0'
,
'\0'
},
{
'\\'
,
'u'
,
'0'
,
'0'
,
'1'
,
'3'
,
'\0'
,
'\0'
},
{
'\\'
,
'u'
,
'0'
,
'0'
,
'1'
,
'4'
,
'\0'
,
'\0'
},
{
'\\'
,
'u'
,
'0'
,
'0'
,
'1'
,
'5'
,
'\0'
,
'\0'
},
{
'\\'
,
'u'
,
'0'
,
'0'
,
'1'
,
'6'
,
'\0'
,
'\0'
},
{
'\\'
,
'u'
,
'0'
,
'0'
,
'1'
,
'7'
,
'\0'
,
'\0'
},
{
'\\'
,
'u'
,
'0'
,
'0'
,
'1'
,
'8'
,
'\0'
,
'\0'
},
{
'\\'
,
'u'
,
'0'
,
'0'
,
'1'
,
'9'
,
'\0'
,
'\0'
},
{
'\\'
,
'u'
,
'0'
,
'0'
,
'1'
,
'a'
,
'\0'
,
'\0'
},
{
'\\'
,
'u'
,
'0'
,
'0'
,
'1'
,
'b'
,
'\0'
,
'\0'
},
{
'\\'
,
'u'
,
'0'
,
'0'
,
'1'
,
'c'
,
'\0'
,
'\0'
},
{
'\\'
,
'u'
,
'0'
,
'0'
,
'1'
,
'd'
,
'\0'
,
'\0'
},
{
'\\'
,
'u'
,
'0'
,
'0'
,
'1'
,
'e'
,
'\0'
,
'\0'
},
{
'\\'
,
'u'
,
'0'
,
'0'
,
'1'
,
'f'
,
'\0'
,
'\0'
},
{
'\\'
,
'\"'
,
'\0'
,
'\0'
,
'\0'
,
'\0'
,
'\0'
,
'\0'
},
{
'\\'
,
'/'
,
'\0'
,
'\0'
,
'\0'
,
'\0'
,
'\0'
,
'\0'
},
{
'\\'
,
'\\'
,
'\0'
,
'\0'
,
'\0'
,
'\0'
,
'\0'
,
'\0'
},
{
'\\'
,
'u'
,
'0'
,
'0'
,
'7'
,
'f'
,
'\0'
,
'\0'
}
};
static
const
char
*
char2escape
(
unsigned
char
c
){
if
(
c
<
32
)
return
escape_array
[
c
];
switch
(
c
){
case
34
:
return
escape_array
[
32
];
case
47
:
return
escape_array
[
33
];
case
92
:
return
escape_array
[
34
];
case
127
:
return
escape_array
[
35
];
default:
return
NULL
;
}
}
static
json_token_type_t
ch2token
(
unsigned
char
c
){
switch
(
c
){
case
'{'
:
return
T_OBJ_BEGIN
;
case
'}'
:
return
T_OBJ_END
;
case
'['
:
return
T_ARR_BEGIN
;
case
']'
:
return
T_ARR_END
;
case
','
:
return
T_COMMA
;
case
':'
:
return
T_COLON
;
case
'\0'
:
return
T_END
;
case
' '
:
return
T_WHITESPACE
;
case
'\t'
:
return
T_WHITESPACE
;
case
'\n'
:
return
T_WHITESPACE
;
case
'\r'
:
return
T_WHITESPACE
;
/* Update characters that require further processing */
case
'f'
:
case
'i'
:
case
'I'
:
case
'n'
:
case
'N'
:
case
't'
:
case
'"'
:
case
'+'
:
case
'-'
:
case
'0'
:
case
'1'
:
case
'2'
:
case
'3'
:
case
'4'
:
case
'5'
:
case
'6'
:
case
'7'
:
case
'8'
:
case
'9'
:
return
T_UNKNOWN
;
default:
return
T_ERROR
;
}
}
static
char
escape2char
(
unsigned
char
c
){
switch
(
c
){
case
'"'
:
return
'"'
;
case
'\\'
:
return
'\\'
;
case
'/'
:
return
'/'
;
case
'b'
:
return
'\b'
;
case
't'
:
return
'\t'
;
case
'n'
:
return
'\n'
;
case
'f'
:
return
'\f'
;
case
'r'
:
return
'\r'
;
case
'u'
:
return
'u'
;
default:
return
0
;
}
}
/* ===== CONFIGURATION ===== */
/* ===== CONFIGURATION ===== */
#if 0
#if 0
...
@@ -319,8 +430,10 @@ static int json_cfg_encode_keep_buffer(lua_State *l)
...
@@ -319,8 +430,10 @@ static int json_cfg_encode_keep_buffer(lua_State *l)
/* Init / free the buffer if the setting has changed */
/* Init / free the buffer if the setting has changed */
if (old_value ^ cfg->encode_keep_buffer) {
if (old_value ^ cfg->encode_keep_buffer) {
if (cfg->encode_keep_buffer)
if (cfg->encode_keep_buffer){
strbuf_init(&cfg->encode_buf, 0);
if(-1==strbuf_init(&cfg->encode_buf, 0))
return luaL_error(l, "not enough memory");
}
else
else
strbuf_free(&cfg->encode_buf);
strbuf_free(&cfg->encode_buf);
}
}
...
@@ -456,9 +569,7 @@ static json_config_t *json_fetch_config(lua_State *l)
...
@@ -456,9 +569,7 @@ static json_config_t *json_fetch_config(lua_State *l)
return
&
_cfg
;
return
&
_cfg
;
}
}
static
void
cfg_init
(
json_config_t
*
cfg
){
static
int
cfg_init
(
json_config_t
*
cfg
){
int
i
;
cfg
->
encode_sparse_convert
=
DEFAULT_SPARSE_CONVERT
;
cfg
->
encode_sparse_convert
=
DEFAULT_SPARSE_CONVERT
;
cfg
->
encode_sparse_ratio
=
DEFAULT_SPARSE_RATIO
;
cfg
->
encode_sparse_ratio
=
DEFAULT_SPARSE_RATIO
;
cfg
->
encode_sparse_safe
=
DEFAULT_SPARSE_SAFE
;
cfg
->
encode_sparse_safe
=
DEFAULT_SPARSE_SAFE
;
...
@@ -470,53 +581,13 @@ static void cfg_init(json_config_t *cfg){
...
@@ -470,53 +581,13 @@ static void cfg_init(json_config_t *cfg){
cfg
->
encode_number_precision
=
DEFAULT_ENCODE_NUMBER_PRECISION
;
cfg
->
encode_number_precision
=
DEFAULT_ENCODE_NUMBER_PRECISION
;
#if DEFAULT_ENCODE_KEEP_BUFFER > 0
#if DEFAULT_ENCODE_KEEP_BUFFER > 0
strbuf_init
(
&
cfg
->
encode_buf
,
0
);
if
(
-
1
==
strbuf_init
(
&
cfg
->
encode_buf
,
0
)){
NODE_ERR
(
"not enough memory
\n
"
);
return
-
1
;
}
#endif
#endif
/* Decoding init */
return
0
;
/* Tag all characters as an error */
for
(
i
=
0
;
i
<
256
;
i
++
)
cfg
->
ch2token
[
i
]
=
T_ERROR
;
/* Set tokens that require no further processing */
cfg
->
ch2token
[
'{'
]
=
T_OBJ_BEGIN
;
cfg
->
ch2token
[
'}'
]
=
T_OBJ_END
;
cfg
->
ch2token
[
'['
]
=
T_ARR_BEGIN
;
cfg
->
ch2token
[
']'
]
=
T_ARR_END
;
cfg
->
ch2token
[
','
]
=
T_COMMA
;
cfg
->
ch2token
[
':'
]
=
T_COLON
;
cfg
->
ch2token
[
'\0'
]
=
T_END
;
cfg
->
ch2token
[
' '
]
=
T_WHITESPACE
;
cfg
->
ch2token
[
'\t'
]
=
T_WHITESPACE
;
cfg
->
ch2token
[
'\n'
]
=
T_WHITESPACE
;
cfg
->
ch2token
[
'\r'
]
=
T_WHITESPACE
;
/* Update characters that require further processing */
cfg
->
ch2token
[
'f'
]
=
T_UNKNOWN
;
/* false? */
cfg
->
ch2token
[
'i'
]
=
T_UNKNOWN
;
/* inf, ininity? */
cfg
->
ch2token
[
'I'
]
=
T_UNKNOWN
;
cfg
->
ch2token
[
'n'
]
=
T_UNKNOWN
;
/* null, nan? */
cfg
->
ch2token
[
'N'
]
=
T_UNKNOWN
;
cfg
->
ch2token
[
't'
]
=
T_UNKNOWN
;
/* true? */
cfg
->
ch2token
[
'"'
]
=
T_UNKNOWN
;
/* string? */
cfg
->
ch2token
[
'+'
]
=
T_UNKNOWN
;
/* number? */
cfg
->
ch2token
[
'-'
]
=
T_UNKNOWN
;
for
(
i
=
0
;
i
<
10
;
i
++
)
cfg
->
ch2token
[
'0'
+
i
]
=
T_UNKNOWN
;
/* Lookup table for parsing escape characters */
for
(
i
=
0
;
i
<
256
;
i
++
)
cfg
->
escape2char
[
i
]
=
0
;
/* String error */
cfg
->
escape2char
[
'"'
]
=
'"'
;
cfg
->
escape2char
[
'\\'
]
=
'\\'
;
cfg
->
escape2char
[
'/'
]
=
'/'
;
cfg
->
escape2char
[
'b'
]
=
'\b'
;
cfg
->
escape2char
[
't'
]
=
'\t'
;
cfg
->
escape2char
[
'n'
]
=
'\n'
;
cfg
->
escape2char
[
'f'
]
=
'\f'
;
cfg
->
escape2char
[
'r'
]
=
'\r'
;
cfg
->
escape2char
[
'u'
]
=
'u'
;
/* Unicode parsing required */
}
}
/* ===== ENCODING ===== */
/* ===== ENCODING ===== */
...
@@ -552,9 +623,18 @@ static void json_append_string(lua_State *l, strbuf_t *json, int lindex)
...
@@ -552,9 +623,18 @@ static void json_append_string(lua_State *l, strbuf_t *json, int lindex)
strbuf_append_char_unsafe
(
json
,
'\"'
);
strbuf_append_char_unsafe
(
json
,
'\"'
);
for
(
i
=
0
;
i
<
len
;
i
++
)
{
for
(
i
=
0
;
i
<
len
;
i
++
)
{
escstr
=
char2escape
[(
unsigned
char
)
str
[
i
]];
escstr
=
char2escape
((
unsigned
char
)
str
[
i
]);
if
(
escstr
)
if
(
escstr
){
int
i
;
char
temp
[
8
];
// for now, 8-bytes is enough.
for
(
i
=
0
;
i
<
8
;
++
i
)
{
temp
[
i
]
=
byte_of_aligned_array
(
escstr
,
i
);
if
(
temp
[
i
]
==
0
)
break
;
}
escstr
=
temp
;
strbuf_append_string
(
json
,
escstr
);
strbuf_append_string
(
json
,
escstr
);
}
else
else
strbuf_append_char_unsafe
(
json
,
str
[
i
]);
strbuf_append_char_unsafe
(
json
,
str
[
i
]);
}
}
...
@@ -694,7 +774,12 @@ static void json_append_number(lua_State *l, json_config_t *cfg,
...
@@ -694,7 +774,12 @@ static void json_append_number(lua_State *l, json_config_t *cfg,
}
}
strbuf_ensure_empty_length
(
json
,
FPCONV_G_FMT_BUFSIZE
);
strbuf_ensure_empty_length
(
json
,
FPCONV_G_FMT_BUFSIZE
);
#ifdef LUA_NUMBER_INTEGRAL
len
=
fpconv_g_fmt
(
strbuf_empty_ptr
(
json
),
num
,
cfg
->
encode_number_precision
);
len
=
fpconv_g_fmt
(
strbuf_empty_ptr
(
json
),
num
,
cfg
->
encode_number_precision
);
#else
c_sprintf
(
strbuf_empty_ptr
(
json
),
"%.14g"
,
num
);
len
=
c_strlen
(
strbuf_empty_ptr
(
json
));
#endif
strbuf_extend_length
(
json
,
len
);
strbuf_extend_length
(
json
,
len
);
}
}
...
@@ -796,7 +881,8 @@ static int json_encode(lua_State *l)
...
@@ -796,7 +881,8 @@ static int json_encode(lua_State *l)
if
(
!
cfg
->
encode_keep_buffer
)
{
if
(
!
cfg
->
encode_keep_buffer
)
{
/* Use private buffer */
/* Use private buffer */
encode_buf
=
&
local_encode_buf
;
encode_buf
=
&
local_encode_buf
;
strbuf_init
(
encode_buf
,
0
);
if
(
-
1
==
strbuf_init
(
encode_buf
,
0
))
return
luaL_error
(
l
,
"not enough memory"
);
}
else
{
}
else
{
/* Reuse existing buffer */
/* Reuse existing buffer */
encode_buf
=
&
cfg
->
encode_buf
;
encode_buf
=
&
cfg
->
encode_buf
;
...
@@ -967,7 +1053,7 @@ static void json_set_token_error(json_token_t *token, json_parse_t *json,
...
@@ -967,7 +1053,7 @@ static void json_set_token_error(json_token_t *token, json_parse_t *json,
static
void
json_next_string_token
(
json_parse_t
*
json
,
json_token_t
*
token
)
static
void
json_next_string_token
(
json_parse_t
*
json
,
json_token_t
*
token
)
{
{
char
*
escape2char
=
json
->
cfg
->
escape2char
;
//
char *escape2char = json->cfg->escape2char;
char
ch
;
char
ch
;
/* Caller must ensure a string is next */
/* Caller must ensure a string is next */
...
@@ -995,7 +1081,7 @@ static void json_next_string_token(json_parse_t *json, json_token_t *token)
...
@@ -995,7 +1081,7 @@ static void json_next_string_token(json_parse_t *json, json_token_t *token)
ch
=
*
(
json
->
ptr
+
1
);
ch
=
*
(
json
->
ptr
+
1
);
/* Translate escape code and append to tmp string */
/* Translate escape code and append to tmp string */
ch
=
escape2char
[
(
unsigned
char
)
ch
]
;
ch
=
escape2char
(
(
unsigned
char
)
ch
)
;
if
(
ch
==
'u'
)
{
if
(
ch
==
'u'
)
{
if
(
json_append_unicode_escape
(
json
)
==
0
)
if
(
json_append_unicode_escape
(
json
)
==
0
)
continue
;
continue
;
...
@@ -1107,13 +1193,13 @@ static void json_next_number_token(json_parse_t *json, json_token_t *token)
...
@@ -1107,13 +1193,13 @@ static void json_next_number_token(json_parse_t *json, json_token_t *token)
*/
*/
static
void
json_next_token
(
json_parse_t
*
json
,
json_token_t
*
token
)
static
void
json_next_token
(
json_parse_t
*
json
,
json_token_t
*
token
)
{
{
const
json_token_type_t
*
ch2token
=
json
->
cfg
->
ch2token
;
//
const json_token_type_t *ch2token = json->cfg->ch2token;
int
ch
;
int
ch
;
/* Eat whitespace. */
/* Eat whitespace. */
while
(
1
)
{
while
(
1
)
{
ch
=
(
unsigned
char
)
*
(
json
->
ptr
);
ch
=
(
unsigned
char
)
*
(
json
->
ptr
);
token
->
type
=
ch2token
[
ch
]
;
token
->
type
=
ch2token
(
ch
)
;
if
(
token
->
type
!=
T_WHITESPACE
)
if
(
token
->
type
!=
T_WHITESPACE
)
break
;
break
;
json
->
ptr
++
;
json
->
ptr
++
;
...
@@ -1194,13 +1280,23 @@ static void json_throw_parse_error(lua_State *l, json_parse_t *json,
...
@@ -1194,13 +1280,23 @@ static void json_throw_parse_error(lua_State *l, json_parse_t *json,
const
char
*
exp
,
json_token_t
*
token
)
const
char
*
exp
,
json_token_t
*
token
)
{
{
const
char
*
found
;
const
char
*
found
;
char
temp
[
16
];
// for now, 16-bytes is enough.
strbuf_free
(
json
->
tmp
);
strbuf_free
(
json
->
tmp
);
if
(
token
->
type
==
T_ERROR
)
if
(
token
->
type
==
T_ERROR
)
found
=
token
->
value
.
string
;
found
=
token
->
value
.
string
;
else
else
{
found
=
json_token_type_name
[
token
->
type
];
found
=
json_token_type_name
[
token
->
type
];
int
i
;
for
(
i
=
0
;
i
<
16
;
++
i
)
{
temp
[
i
]
=
byte_of_aligned_array
(
found
,
i
);
if
(
temp
[
i
]
==
0
)
break
;
}
found
=
temp
;
}
/* Note: token->index is 0 based, display starting from 1 */
/* Note: token->index is 0 based, display starting from 1 */
luaL_error
(
l
,
"Expected %s but found %s at character %d"
,
luaL_error
(
l
,
"Expected %s but found %s at character %d"
,
...
@@ -1369,6 +1465,9 @@ static int json_decode(lua_State *l)
...
@@ -1369,6 +1465,9 @@ static int json_decode(lua_State *l)
* This means we no longer need to do length checks since the decoded
* This means we no longer need to do length checks since the decoded
* string must be smaller than the entire json string */
* string must be smaller than the entire json string */
json
.
tmp
=
strbuf_new
(
json_len
);
json
.
tmp
=
strbuf_new
(
json_len
);
if
(
json
.
tmp
==
NULL
){
return
luaL_error
(
l
,
"not enought memory"
);
}
json_next_token
(
&
json
,
&
token
);
json_next_token
(
&
json
,
&
token
);
json_process_value
(
l
,
&
json
,
&
token
);
json_process_value
(
l
,
&
json
,
&
token
);
...
@@ -1434,6 +1533,7 @@ static int json_protect_conversion(lua_State *l)
...
@@ -1434,6 +1533,7 @@ static int json_protect_conversion(lua_State *l)
return luaL_error(l, "Memory allocation error in CJSON protected call");
return luaL_error(l, "Memory allocation error in CJSON protected call");
}
}
#endif
#endif
// Module function map
// Module function map
#define MIN_OPT_LEVEL 2
#define MIN_OPT_LEVEL 2
#include "lrodefs.h"
#include "lrodefs.h"
...
@@ -1459,7 +1559,9 @@ LUALIB_API int luaopen_cjson( lua_State *L )
...
@@ -1459,7 +1559,9 @@ LUALIB_API int luaopen_cjson( lua_State *L )
{
{
/* Initialise number conversions */
/* Initialise number conversions */
fpconv_init
();
fpconv_init
();
cfg_init
(
&
_cfg
);
if
(
-
1
==
cfg_init
(
&
_cfg
)){
return
luaL_error
(
L
,
"BUG: Unable to init config for cjson"
);;
}
#if LUA_OPTIMIZE_MEMORY > 0
#if LUA_OPTIMIZE_MEMORY > 0
return
0
;
return
0
;
#else // #if LUA_OPTIMIZE_MEMORY > 0
#else // #if LUA_OPTIMIZE_MEMORY > 0
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment