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,74 @@ 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 , "number must be non-negative" );
453+
454+ /* Overflow check */
455+ luaL_argcheck (l , (unsigned long long )n <= SIZE_MAX , optindex , "number too large" );
456+ size_t len = (size_t )n ;
457+
458+ strbuf_t b ;
459+ strbuf_init (& b , len );
460+ for (size_t i = 0 ; i < len ; i ++ )
461+ strbuf_append_char_unsafe (& b , ' ' );
462+
463+ cfg -> encode_pretty_indent = strbuf_free_to_string (& b , & len );
464+ } else if (lua_isstring (l , optindex )) {
465+ const char * indent = luaL_checkstring (l , optindex );
466+ if (!is_allowed_ws (indent ))
467+ luaL_argerror (l , optindex , "indent may only contain space, tab, LF, or CR" );
468+ cfg -> encode_pretty_indent = indent ;
469+ } else {
470+ luaL_argerror (l , optindex , "string or number expected" );
471+ }
472+ }
473+ lua_pushstring (l , cfg -> encode_pretty_indent ? cfg -> encode_pretty_indent : "" );
474+
475+
476+ const char * item_separator = cfg -> encode_pretty_item_separator ;
477+ json_string_option (l , 3 , & item_separator );
478+ if (!is_allowed_ws (item_separator ))
479+ luaL_argerror (l , optindex , "item_separator may only contain space, tab, LF, or CR" );
480+ cfg -> encode_pretty_item_separator = item_separator ;
481+
482+ const char * key_separator = cfg -> encode_pretty_key_separator ;
483+ json_string_option (l , 4 , & key_separator );
484+ if (!is_allowed_ws (key_separator ))
485+ luaL_argerror (l , optindex , "key_separator may only contain space, tab, LF, or CR" );
486+ cfg -> encode_pretty_key_separator = key_separator ;
487+
488+ return 4 ;
489+ }
490+
403491#if defined(DISABLE_INVALID_NUMBERS ) && !defined(USE_INTERNAL_FPCONV )
404492void json_verify_invalid_number_setting (lua_State * l , int * setting )
405493{
@@ -491,6 +579,10 @@ static void json_create_config(lua_State *l)
491579 cfg -> decode_array_with_array_mt = DEFAULT_DECODE_ARRAY_WITH_ARRAY_MT ;
492580 cfg -> encode_escape_forward_slash = DEFAULT_ENCODE_ESCAPE_FORWARD_SLASH ;
493581 cfg -> encode_skip_unsupported_value_types = DEFAULT_ENCODE_SKIP_UNSUPPORTED_VALUE_TYPES ;
582+ cfg -> encode_pretty_enabled = DEFAULT_ENCODE_PRETTY_ENABLED ;
583+ cfg -> encode_pretty_indent = DEFAULT_ENCODE_PRETTY_INDENT ;
584+ cfg -> encode_pretty_item_separator = DEFAULT_ENCODE_PRETTY_ITEM_SEPARATOR ;
585+ cfg -> encode_pretty_key_separator = DEFAULT_ENCODE_PRETTY_KEY_SEPARATOR ;
494586
495587#if DEFAULT_ENCODE_KEEP_BUFFER > 0
496588 strbuf_init (& cfg -> encode_buf , 0 );
@@ -660,6 +752,13 @@ static void json_check_encode_depth(lua_State *l, json_config_t *cfg,
660752static int json_append_data (lua_State * l , json_config_t * cfg ,
661753 int current_depth , strbuf_t * json );
662754
755+ static void json_append_item_separator_and_indent (strbuf_t * json , json_config_t * cfg , int depth )
756+ {
757+ strbuf_append_string (json , cfg -> encode_pretty_item_separator );
758+ for (int i = 0 ; i < depth ; i ++ )
759+ strbuf_append_string (json , cfg -> encode_pretty_indent );
760+ }
761+
663762/* json_append_array args:
664763 * - lua_State
665764 * - JSON strbuf
@@ -668,15 +767,21 @@ static void json_append_array(lua_State *l, json_config_t *cfg, int current_dept
668767 strbuf_t * json , int array_length , int raw )
669768{
670769 int comma , i , json_pos , err ;
770+ int has_items = 0 ;
671771
672772 strbuf_append_char (json , '[' );
673773
674774 comma = 0 ;
675775 for (i = 1 ; i <= array_length ; i ++ ) {
776+ has_items = 1 ;
777+
676778 json_pos = strbuf_length (json );
677779 if (comma ++ > 0 )
678780 strbuf_append_char (json , ',' );
679781
782+ if (cfg -> encode_pretty_enabled )
783+ json_append_item_separator_and_indent (json , cfg , current_depth );
784+
680785 if (raw ) {
681786 lua_rawgeti (l , -1 , i );
682787 } else {
@@ -698,6 +803,9 @@ static void json_append_array(lua_State *l, json_config_t *cfg, int current_dept
698803 lua_pop (l , 1 );
699804 }
700805
806+ if (has_items && cfg -> encode_pretty_enabled )
807+ json_append_item_separator_and_indent (json , cfg , current_depth - 1 );
808+
701809 strbuf_append_char (json , ']' );
702810}
703811
@@ -752,6 +860,7 @@ static void json_append_object(lua_State *l, json_config_t *cfg,
752860 int current_depth , strbuf_t * json )
753861{
754862 int comma , keytype , json_pos , err ;
863+ int has_items = 0 ;
755864
756865 /* Object */
757866 strbuf_append_char (json , '{' );
@@ -760,10 +869,15 @@ static void json_append_object(lua_State *l, json_config_t *cfg,
760869 /* table, startkey */
761870 comma = 0 ;
762871 while (lua_next (l , -2 ) != 0 ) {
872+ has_items = 1 ;
873+
763874 json_pos = strbuf_length (json );
764875 if (comma ++ > 0 )
765876 strbuf_append_char (json , ',' );
766877
878+ if (cfg -> encode_pretty_enabled )
879+ json_append_item_separator_and_indent (json , cfg , current_depth );
880+
767881 /* table, key, value */
768882 keytype = lua_type (l , -2 );
769883 if (keytype == LUA_TNUMBER ) {
@@ -778,6 +892,8 @@ static void json_append_object(lua_State *l, json_config_t *cfg,
778892 "table key must be a number or string" );
779893 /* never returns */
780894 }
895+ if (cfg -> encode_pretty_enabled )
896+ strbuf_append_string (json , cfg -> encode_pretty_key_separator );
781897
782898 /* table, key, value */
783899 err = json_append_data (l , cfg , current_depth , json );
@@ -792,6 +908,9 @@ static void json_append_object(lua_State *l, json_config_t *cfg,
792908 /* table, key */
793909 }
794910
911+ if (has_items && cfg -> encode_pretty_enabled )
912+ json_append_item_separator_and_indent (json , cfg , current_depth - 1 );
913+
795914 strbuf_append_char (json , '}' );
796915}
797916
@@ -1571,6 +1690,7 @@ static int lua_cjson_new(lua_State *l)
15711690 { "decode_invalid_numbers" , json_cfg_decode_invalid_numbers },
15721691 { "encode_escape_forward_slash" , json_cfg_encode_escape_forward_slash },
15731692 { "encode_skip_unsupported_value_types" , json_cfg_encode_skip_unsupported_value_types },
1693+ { "encode_pretty" , json_cfg_encode_pretty },
15741694 { "new" , lua_cjson_new },
15751695 { NULL , NULL }
15761696 };
0 commit comments