8787#define DEFAULT_DECODE_ARRAY_WITH_ARRAY_MT 0
8888#define DEFAULT_ENCODE_ESCAPE_FORWARD_SLASH 1
8989#define DEFAULT_ENCODE_SKIP_UNSUPPORTED_VALUE_TYPES 0
90+ #define DEFAULT_ENCODE_INDENT NULL
9091
9192#ifdef DISABLE_INVALID_NUMBERS
9293#undef DEFAULT_DECODE_INVALID_NUMBERS
@@ -168,6 +169,7 @@ typedef struct {
168169 int encode_keep_buffer ;
169170 int encode_empty_table_as_object ;
170171 int encode_escape_forward_slash ;
172+ const char * encode_indent ;
171173
172174 int decode_invalid_numbers ;
173175 int decode_max_depth ;
@@ -177,6 +179,7 @@ typedef struct {
177179
178180typedef struct {
179181 const char * * char2escape [256 ];
182+ const char * indent ;
180183} json_encode_options_t ;
181184
182185typedef struct {
@@ -330,6 +333,20 @@ static int json_enum_option(lua_State *l, int optindex, int *setting,
330333}
331334*/
332335
336+ /* Process string option for a configuration function */
337+ /*
338+ static int json_string_option(lua_State *l, int optindex, const char **setting)
339+ {
340+ if (!lua_isnil(l, optindex)) {
341+ const char *value = luaL_checkstring(l, optindex);
342+ *setting = value;
343+ }
344+
345+ lua_pushstring(l, *setting ? *setting : "");
346+ return 1;
347+ }
348+ */
349+
333350/* Configures handling of extremely sparse arrays:
334351 * convert: Convert extremely sparse arrays into objects? Otherwise error.
335352 * ratio: 0: always allow sparse; 1: never allow sparse; >1: use ratio
@@ -436,6 +453,41 @@ static int json_cfg_encode_keep_buffer(lua_State *l)
436453}
437454*/
438455
456+ /* Returns nonzero if the string consists only of allowed whitespace characters */
457+ static int is_allowed_ws (const char * s ) {
458+ if (!s ) return 0 ;
459+ for (const unsigned char * p = (const unsigned char * )s ; * p ; p ++ ) {
460+ switch (* p ) {
461+ case 0x20 : /* space */
462+ case 0x09 : /* tab */
463+ case 0x0A : /* newline */
464+ case 0x0D : /* carriage return */
465+ break ;
466+ default :
467+ return 0 ;
468+ }
469+ }
470+ return 1 ;
471+ }
472+
473+ /* Configure how to indent output */
474+ /*
475+ static int json_cfg_encode_indent(lua_State *l)
476+ {
477+ json_config_t *cfg = json_arg_init(l, 1);
478+
479+ const char *indent = cfg->encode_indent;
480+ json_string_option(l, 1, &indent);
481+ if (indent[0] == '\0') indent = NULL;
482+ else if (!is_allowed_ws(indent)) {
483+ luaL_argerror(l, 1, "indent must contain only spaces, tabs, line feeds, or carriage returns");
484+ }
485+ cfg->encode_indent = indent;
486+
487+ return 1;
488+ }
489+ */
490+
439491#if defined(DISABLE_INVALID_NUMBERS ) && !defined(USE_INTERNAL_FPCONV )
440492void json_verify_invalid_number_setting (lua_State * l , int * setting )
441493{
@@ -533,6 +585,7 @@ static void json_create_config(lua_State *l)
533585 cfg -> decode_array_with_array_mt = DEFAULT_DECODE_ARRAY_WITH_ARRAY_MT ;
534586 cfg -> encode_escape_forward_slash = DEFAULT_ENCODE_ESCAPE_FORWARD_SLASH ;
535587 cfg -> encode_skip_unsupported_value_types = DEFAULT_ENCODE_SKIP_UNSUPPORTED_VALUE_TYPES ;
588+ cfg -> encode_indent = DEFAULT_ENCODE_INDENT ;
536589
537590#if DEFAULT_ENCODE_KEEP_BUFFER > 0
538591 strbuf_init (& cfg -> encode_buf , 0 );
@@ -704,6 +757,13 @@ static void json_check_encode_depth(lua_State *l, json_config_t *cfg,
704757static int json_append_data (lua_State * l , json_encode_t * cfg ,
705758 int current_depth );
706759
760+ static void json_append_newline_and_indent (strbuf_t * json , json_encode_t * ctx , int depth )
761+ {
762+ strbuf_append_char (json , '\n' );
763+ for (int i = 0 ; i < depth ; i ++ )
764+ strbuf_append_string (json , ctx -> options -> indent );
765+ }
766+
707767/* json_append_array args:
708768 * - lua_State
709769 * - JSON strbuf
@@ -712,15 +772,22 @@ static void json_append_array(lua_State *l, json_encode_t *ctx, int current_dept
712772 int array_length , int raw )
713773{
714774 int comma , i , json_pos , err ;
775+ int has_items = 0 ;
715776 strbuf_t * json = ctx -> json ;
716777
717778 strbuf_append_char (json , '[' );
718779
719780 comma = 0 ;
720781 for (i = 1 ; i <= array_length ; i ++ ) {
782+ has_items = 1 ;
783+
721784 json_pos = strbuf_length (json );
722785 if (comma ++ > 0 )
723786 strbuf_append_char (json , ',' );
787+
788+ if (ctx -> options -> indent )
789+ json_append_newline_and_indent (json , ctx , current_depth );
790+
724791 if (raw ) {
725792 lua_rawgeti (l , -1 , i );
726793 } else {
@@ -742,6 +809,9 @@ static void json_append_array(lua_State *l, json_encode_t *ctx, int current_dept
742809 lua_pop (l , 1 );
743810 }
744811
812+ if (has_items && ctx -> options -> indent )
813+ json_append_newline_and_indent (json , ctx , current_depth - 1 );
814+
745815 strbuf_append_char (json , ']' );
746816}
747817
@@ -798,6 +868,7 @@ static void json_append_object(lua_State *l, json_encode_t *ctx,
798868 int current_depth )
799869{
800870 int comma , keytype , json_pos , err ;
871+ int has_items = 0 ;
801872 strbuf_t * json = ctx -> json ;
802873
803874 /* Object */
@@ -807,12 +878,17 @@ static void json_append_object(lua_State *l, json_encode_t *ctx,
807878 /* table, startkey */
808879 comma = 0 ;
809880 while (lua_next (l , -2 ) != 0 ) {
881+ has_items = 1 ;
882+
810883 json_pos = strbuf_length (json );
811884 if (comma ++ > 0 )
812885 strbuf_append_char (json , ',' );
813886 else
814887 comma = 1 ;
815888
889+ if (ctx -> options -> indent )
890+ json_append_newline_and_indent (json , ctx , current_depth );
891+
816892 /* table, key, value */
817893 keytype = lua_type (l , -2 );
818894 if (keytype == LUA_TNUMBER ) {
@@ -827,6 +903,8 @@ static void json_append_object(lua_State *l, json_encode_t *ctx,
827903 "table key must be a number or string" );
828904 /* never returns */
829905 }
906+ if (ctx -> options -> indent )
907+ strbuf_append_char (json , ' ' );
830908
831909 /* table, key, value */
832910 err = json_append_data (l , ctx , current_depth );
@@ -841,6 +919,9 @@ static void json_append_object(lua_State *l, json_encode_t *ctx,
841919 /* table, key */
842920 }
843921
922+ if (has_items && ctx -> options -> indent )
923+ json_append_newline_and_indent (json , ctx , current_depth - 1 );
924+
844925 strbuf_append_char (json , '}' );
845926}
846927
@@ -966,7 +1047,10 @@ static int json_append_data(lua_State *l, json_encode_t *ctx,
9661047static int json_encode (lua_State * l )
9671048{
9681049 json_config_t * cfg = json_fetch_config (l );
969- json_encode_options_t options = { .char2escape = { char2escape } };
1050+ json_encode_options_t options = {
1051+ .char2escape = { char2escape },
1052+ .indent = DEFAULT_ENCODE_INDENT ,
1053+ };
9701054 json_encode_t ctx = { .options = & options , .cfg = cfg };
9711055 strbuf_t local_encode_buf ;
9721056 strbuf_t * encode_buf ;
@@ -979,26 +1063,31 @@ static int json_encode(lua_State *l)
9791063 break ;
9801064 case 2 :
9811065 luaL_checktype (l , 2 , LUA_TTABLE );
982- lua_getfield (l , 2 , "escape_slash" );
9831066
984- /* We only handle the escape_slash option for now */
985- if (lua_isnil (l , -1 )) {
986- lua_pop (l , 2 );
987- break ;
1067+ lua_getfield (l , 2 , "escape_slash" );
1068+ if (!lua_isnil (l , -1 )) {
1069+ luaL_checktype (l , -1 , LUA_TBOOLEAN );
1070+
1071+ int escape_slash = lua_toboolean (l , -1 );
1072+ if (escape_slash ) {
1073+ /* This can be optimised by adding a new hard-coded escape table for this case,
1074+ * but this path will rarely if ever be used, so let's just memcpy. */
1075+ memcpy (customChar2escape , char2escape , sizeof (char2escape ));
1076+ customChar2escape ['/' ] = "\\/" ;
1077+ * ctx .options -> char2escape = customChar2escape ;
1078+ }
9881079 }
1080+ lua_pop (l , 1 );
9891081
990- luaL_checktype (l , -1 , LUA_TBOOLEAN );
991-
992- int escape_slash = lua_toboolean (l , -1 );
993-
994- if (escape_slash ) {
995- /* This can be optimised by adding a new hard-coded escape table for this case,
996- * but this path will rarely if ever be used, so let's just memcpy.*/
997- memcpy (customChar2escape , char2escape , sizeof (char2escape ));
998- customChar2escape ['/' ] = "\\/" ;
999- * ctx .options -> char2escape = customChar2escape ;
1082+ lua_getfield (l , 2 , "indent" );
1083+ if (!lua_isnil (l , -1 )) {
1084+ options .indent = luaL_checkstring (l , -1 );
1085+ if (options .indent [0 ] == '\0' ) options .indent = NULL ;
1086+ else if (!is_allowed_ws (options .indent ))
1087+ luaL_error (l , "indent must contain only spaces, tabs, line feeds, or carriage returns" );
10001088 }
10011089
1090+ /* Also pop the opts table */
10021091 lua_pop (l , 2 );
10031092 break ;
10041093 default :
@@ -1710,6 +1799,7 @@ int lua_cjson_new(lua_State *l)
17101799 { "decode_invalid_numbers", json_cfg_decode_invalid_numbers },
17111800 { "encode_escape_forward_slash", json_cfg_encode_escape_forward_slash },
17121801 { "encode_skip_unsupported_value_types", json_cfg_encode_skip_unsupported_value_types },
1802+ { "encode_indent", json_cfg_encode_indent },
17131803 */
17141804 { "new" , lua_cjson_new },
17151805 { NULL , NULL }
0 commit comments