9191#define DEFAULT_DECODE_ARRAY_WITH_ARRAY_MT 0
9292#define DEFAULT_ENCODE_ESCAPE_FORWARD_SLASH 1
9393#define DEFAULT_ENCODE_SKIP_UNSUPPORTED_VALUE_TYPES 0
94+ #define DEFAULT_ENCODE_PRETTY_ENABLED 0
95+ #define DEFAULT_ENCODE_PRETTY_INDENT " "
96+ #define DEFAULT_ENCODE_PRETTY_ITEM_SEPARATOR "\n"
97+ #define DEFAULT_ENCODE_PRETTY_KEY_SEPARATOR " "
9498
9599#ifdef DISABLE_INVALID_NUMBERS
96100#undef DEFAULT_DECODE_INVALID_NUMBERS
@@ -172,6 +176,10 @@ typedef struct {
172176 int encode_keep_buffer ;
173177 int encode_empty_table_as_object ;
174178 int encode_escape_forward_slash ;
179+ int encode_pretty_enabled ;
180+ const char * encode_pretty_indent ;
181+ const char * encode_pretty_item_separator ;
182+ const char * encode_pretty_key_separator ;
175183
176184 int decode_invalid_numbers ;
177185 int decode_max_depth ;
@@ -310,6 +318,18 @@ static int json_enum_option(lua_State *l, int optindex, int *setting,
310318 return 1 ;
311319}
312320
321+ /* Process string option for a configuration function */
322+ static int json_string_option (lua_State * l , int optindex , const char * * setting )
323+ {
324+ if (!lua_isnil (l , optindex )) {
325+ const char * value = luaL_checkstring (l , optindex );
326+ * setting = value ;
327+ }
328+
329+ lua_pushstring (l , * setting ? * setting : "" );
330+ return 1 ;
331+ }
332+
313333/* Configures handling of extremely sparse arrays:
314334 * convert: Convert extremely sparse arrays into objects? Otherwise error.
315335 * ratio: 0: always allow sparse; 1: never allow sparse; >1: use ratio
@@ -400,6 +420,73 @@ static int json_cfg_encode_keep_buffer(lua_State *l)
400420 return 1 ;
401421}
402422
423+ /* Returns nonzero if the string consists only of allowed whitespace characters */
424+ static int is_allowed_ws (const char * s ) {
425+ if (!s ) return 0 ;
426+ for (const unsigned char * p = (const unsigned char * )s ; * p ; p ++ ) {
427+ switch (* p ) {
428+ case 0x20 : /* space */
429+ case 0x09 : /* tab */
430+ case 0x0A : /* newline */
431+ case 0x0D : /* carriage return */
432+ break ;
433+ default :
434+ return 0 ;
435+ }
436+ }
437+ return 1 ;
438+ }
439+
440+ /* Configure pretty encoding */
441+ static int json_cfg_encode_pretty (lua_State * l )
442+ {
443+ json_config_t * cfg = json_arg_init (l , 4 );
444+
445+ json_enum_option (l , 1 , & cfg -> encode_pretty_enabled , NULL , 1 );
446+
447+ /* Process "indent" option, it can be either a string or a non-negative integer. */
448+ int optindex = 2 ;
449+ if (!lua_isnil (l , optindex )) {
450+ if (lua_isnumber (l , optindex )) {
451+ lua_Integer n = luaL_checkinteger (l , optindex );
452+ luaL_argcheck (l , n >= 0 , optindex , "indent must be non-negative" );
453+
454+ luaL_argcheck (l , (unsigned long long )n <= SIZE_MAX , optindex , "indent is too large" );
455+ size_t indentLen = (size_t )n ;
456+
457+ strbuf_t b ;
458+ strbuf_init (& b , indentLen );
459+ for (size_t i = 0 ; i < indentLen ; i ++ )
460+ strbuf_append_char_unsafe (& b , ' ' );
461+
462+ cfg -> encode_pretty_indent = strbuf_free_to_string (& b , & indentLen );
463+ } else if (lua_isstring (l , optindex )) {
464+ const char * indent = luaL_checkstring (l , optindex );
465+ if (!is_allowed_ws (indent ))
466+ luaL_argerror (l , optindex , "indent may only contain space, tab, LF, or CR" );
467+ cfg -> encode_pretty_indent = indent ;
468+ } else {
469+ luaL_argerror (l , optindex , "string or number expected" );
470+ }
471+ }
472+ lua_pushstring (l , cfg -> encode_pretty_indent ? cfg -> encode_pretty_indent : "" );
473+
474+
475+ const char * item_separator = cfg -> encode_pretty_item_separator ;
476+ json_string_option (l , 3 , & item_separator );
477+ if (!is_allowed_ws (item_separator ))
478+ luaL_argerror (l , optindex , "item_separator may only contain space, tab, LF, or CR" );
479+ cfg -> encode_pretty_item_separator = item_separator ;
480+
481+ const char * key_separator = cfg -> encode_pretty_key_separator ;
482+ json_string_option (l , 4 , & key_separator );
483+ if (!is_allowed_ws (key_separator ))
484+ luaL_argerror (l , optindex , "key_separator may only contain space, tab, LF, or CR" );
485+ cfg -> encode_pretty_key_separator = key_separator ;
486+
487+ return 4 ;
488+ }
489+
403490#if defined(DISABLE_INVALID_NUMBERS ) && !defined(USE_INTERNAL_FPCONV )
404491void json_verify_invalid_number_setting (lua_State * l , int * setting )
405492{
@@ -491,6 +578,10 @@ static void json_create_config(lua_State *l)
491578 cfg -> decode_array_with_array_mt = DEFAULT_DECODE_ARRAY_WITH_ARRAY_MT ;
492579 cfg -> encode_escape_forward_slash = DEFAULT_ENCODE_ESCAPE_FORWARD_SLASH ;
493580 cfg -> encode_skip_unsupported_value_types = DEFAULT_ENCODE_SKIP_UNSUPPORTED_VALUE_TYPES ;
581+ cfg -> encode_pretty_enabled = DEFAULT_ENCODE_PRETTY_ENABLED ;
582+ cfg -> encode_pretty_indent = DEFAULT_ENCODE_PRETTY_INDENT ;
583+ cfg -> encode_pretty_item_separator = DEFAULT_ENCODE_PRETTY_ITEM_SEPARATOR ;
584+ cfg -> encode_pretty_key_separator = DEFAULT_ENCODE_PRETTY_KEY_SEPARATOR ;
494585
495586#if DEFAULT_ENCODE_KEEP_BUFFER > 0
496587 strbuf_init (& cfg -> encode_buf , 0 );
@@ -660,6 +751,13 @@ static void json_check_encode_depth(lua_State *l, json_config_t *cfg,
660751static int json_append_data (lua_State * l , json_config_t * cfg ,
661752 int current_depth , strbuf_t * json );
662753
754+ static void json_append_item_separator_and_indent (strbuf_t * json , json_config_t * cfg , int depth )
755+ {
756+ strbuf_append_string (json , cfg -> encode_pretty_item_separator );
757+ for (int i = 0 ; i < depth ; i ++ )
758+ strbuf_append_string (json , cfg -> encode_pretty_indent );
759+ }
760+
663761/* json_append_array args:
664762 * - lua_State
665763 * - JSON strbuf
@@ -668,15 +766,21 @@ static void json_append_array(lua_State *l, json_config_t *cfg, int current_dept
668766 strbuf_t * json , int array_length , int raw )
669767{
670768 int comma , i , json_pos , err ;
769+ int has_items = 0 ;
671770
672771 strbuf_append_char (json , '[' );
673772
674773 comma = 0 ;
675774 for (i = 1 ; i <= array_length ; i ++ ) {
775+ has_items = 1 ;
776+
676777 json_pos = strbuf_length (json );
677778 if (comma ++ > 0 )
678779 strbuf_append_char (json , ',' );
679780
781+ if (cfg -> encode_pretty_enabled )
782+ json_append_item_separator_and_indent (json , cfg , current_depth );
783+
680784 if (raw ) {
681785 lua_rawgeti (l , -1 , i );
682786 } else {
@@ -698,6 +802,9 @@ static void json_append_array(lua_State *l, json_config_t *cfg, int current_dept
698802 lua_pop (l , 1 );
699803 }
700804
805+ if (has_items && cfg -> encode_pretty_enabled )
806+ json_append_item_separator_and_indent (json , cfg , current_depth - 1 );
807+
701808 strbuf_append_char (json , ']' );
702809}
703810
@@ -752,6 +859,7 @@ static void json_append_object(lua_State *l, json_config_t *cfg,
752859 int current_depth , strbuf_t * json )
753860{
754861 int comma , keytype , json_pos , err ;
862+ int has_items = 0 ;
755863
756864 /* Object */
757865 strbuf_append_char (json , '{' );
@@ -760,10 +868,15 @@ static void json_append_object(lua_State *l, json_config_t *cfg,
760868 /* table, startkey */
761869 comma = 0 ;
762870 while (lua_next (l , -2 ) != 0 ) {
871+ has_items = 1 ;
872+
763873 json_pos = strbuf_length (json );
764874 if (comma ++ > 0 )
765875 strbuf_append_char (json , ',' );
766876
877+ if (cfg -> encode_pretty_enabled )
878+ json_append_item_separator_and_indent (json , cfg , current_depth );
879+
767880 /* table, key, value */
768881 keytype = lua_type (l , -2 );
769882 if (keytype == LUA_TNUMBER ) {
@@ -778,6 +891,8 @@ static void json_append_object(lua_State *l, json_config_t *cfg,
778891 "table key must be a number or string" );
779892 /* never returns */
780893 }
894+ if (cfg -> encode_pretty_enabled )
895+ strbuf_append_string (json , cfg -> encode_pretty_key_separator );
781896
782897 /* table, key, value */
783898 err = json_append_data (l , cfg , current_depth , json );
@@ -792,6 +907,9 @@ static void json_append_object(lua_State *l, json_config_t *cfg,
792907 /* table, key */
793908 }
794909
910+ if (has_items && cfg -> encode_pretty_enabled )
911+ json_append_item_separator_and_indent (json , cfg , current_depth - 1 );
912+
795913 strbuf_append_char (json , '}' );
796914}
797915
@@ -1571,6 +1689,7 @@ static int lua_cjson_new(lua_State *l)
15711689 { "decode_invalid_numbers" , json_cfg_decode_invalid_numbers },
15721690 { "encode_escape_forward_slash" , json_cfg_encode_escape_forward_slash },
15731691 { "encode_skip_unsupported_value_types" , json_cfg_encode_skip_unsupported_value_types },
1692+ { "encode_pretty" , json_cfg_encode_pretty },
15741693 { "new" , lua_cjson_new },
15751694 { NULL , NULL }
15761695 };
0 commit comments