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_SORT_KEYS 0
9495
9596#ifdef DISABLE_INVALID_NUMBERS
9697#undef DEFAULT_DECODE_INVALID_NUMBERS
@@ -155,6 +156,32 @@ static const char *json_token_type_name[] = {
155156 NULL
156157};
157158
159+ typedef struct {
160+ strbuf_t * buf ;
161+ size_t offset ;
162+ size_t length ;
163+ int raw_typ ;
164+ union {
165+ lua_Number number ;
166+ const char * string ;
167+ } raw ;
168+ } key_entry_t ;
169+
170+ /* Stores all keys for a table when key sorting is enabled.
171+ * - buf: buffer holding serialized key strings
172+ * - keys: array of key_entry_t pointing into buf
173+ * - size: number of keys stored
174+ * - capacity: allocated capacity of keys array
175+ */
176+ typedef struct {
177+ strbuf_t buf ;
178+ key_entry_t * keys ;
179+ size_t size ;
180+ size_t capacity ;
181+ } keybuf_t ;
182+
183+ #define KEYBUF_DEFAULT_CAPACITY 32
184+
158185typedef struct {
159186 json_token_type_t ch2token [256 ];
160187 char escape2char [256 ]; /* Decoding */
@@ -163,6 +190,10 @@ typedef struct {
163190 * encode_keep_buffer is set */
164191 strbuf_t encode_buf ;
165192
193+ /* encode_keybuf is only allocated and used when
194+ * sort_keys is set */
195+ keybuf_t encode_keybuf ;
196+
166197 int encode_sparse_convert ;
167198 int encode_sparse_ratio ;
168199 int encode_sparse_safe ;
@@ -172,6 +203,7 @@ typedef struct {
172203 int encode_keep_buffer ;
173204 int encode_empty_table_as_object ;
174205 int encode_escape_forward_slash ;
206+ int encode_sort_keys ;
175207
176208 int decode_invalid_numbers ;
177209 int decode_max_depth ;
@@ -449,6 +481,15 @@ static int json_cfg_encode_escape_forward_slash(lua_State *l)
449481 return ret ;
450482}
451483
484+ static int json_cfg_encode_sort_keys (lua_State * l )
485+ {
486+ json_config_t * cfg = json_arg_init (l , 1 );
487+
488+ json_enum_option (l , 1 , & cfg -> encode_sort_keys , NULL , 1 );
489+
490+ return 1 ;
491+ }
492+
452493static int json_destroy_config (lua_State * l )
453494{
454495 json_config_t * cfg ;
@@ -491,6 +532,7 @@ static void json_create_config(lua_State *l)
491532 cfg -> decode_array_with_array_mt = DEFAULT_DECODE_ARRAY_WITH_ARRAY_MT ;
492533 cfg -> encode_escape_forward_slash = DEFAULT_ENCODE_ESCAPE_FORWARD_SLASH ;
493534 cfg -> encode_skip_unsupported_value_types = DEFAULT_ENCODE_SKIP_UNSUPPORTED_VALUE_TYPES ;
535+ cfg -> encode_sort_keys = DEFAULT_ENCODE_SORT_KEYS ;
494536
495537#if DEFAULT_ENCODE_KEEP_BUFFER > 0
496538 strbuf_init (& cfg -> encode_buf , 0 );
@@ -549,17 +591,17 @@ static void json_encode_exception(lua_State *l, json_config_t *cfg, strbuf_t *js
549591{
550592 if (!cfg -> encode_keep_buffer )
551593 strbuf_free (json );
594+
595+ if (cfg -> encode_sort_keys ) {
596+ strbuf_free (& cfg -> encode_keybuf .buf );
597+ free (cfg -> encode_keybuf .keys );
598+ }
599+
552600 luaL_error (l , "Cannot serialise %s: %s" ,
553601 lua_typename (l , lua_type (l , lindex )), reason );
554602}
555603
556- /* json_append_string args:
557- * - lua_State
558- * - JSON strbuf
559- * - String (Lua stack index)
560- *
561- * Returns nothing. Doesn't remove string from Lua stack */
562- static void json_append_string (lua_State * l , strbuf_t * json , int lindex )
604+ static void json_append_string_contents (lua_State * l , strbuf_t * json , int lindex )
563605{
564606 const char * escstr ;
565607 const char * str ;
@@ -572,19 +614,30 @@ static void json_append_string(lua_State *l, strbuf_t *json, int lindex)
572614 * This buffer is reused constantly for small strings
573615 * If there are any excess pages, they won't be hit anyway.
574616 * This gains ~5% speedup. */
575- if (len > SIZE_MAX / 6 - 3 )
617+ if (len >= SIZE_MAX / 6 )
576618 abort (); /* Overflow check */
577- strbuf_ensure_empty_length (json , len * 6 + 2 );
619+ strbuf_ensure_empty_length (json , len * 6 );
578620
579- strbuf_append_char_unsafe (json , '\"' );
580621 for (i = 0 ; i < len ; i ++ ) {
581622 escstr = char2escape [(unsigned char )str [i ]];
582623 if (escstr )
583624 strbuf_append_string (json , escstr );
584625 else
585626 strbuf_append_char_unsafe (json , str [i ]);
586627 }
587- strbuf_append_char_unsafe (json , '\"' );
628+ }
629+
630+ /* json_append_string args:
631+ * - lua_State
632+ * - JSON strbuf
633+ * - String (Lua stack index)
634+ *
635+ * Returns nothing. Doesn't remove string from Lua stack */
636+ static void json_append_string (lua_State * l , strbuf_t * json , int lindex )
637+ {
638+ strbuf_append_char (json , '\"' );
639+ json_append_string_contents (l , json , lindex );
640+ strbuf_append_char (json , '\"' );
588641}
589642
590643/* Find the size of the array on the top of the Lua stack
@@ -748,6 +801,19 @@ static void json_append_number(lua_State *l, json_config_t *cfg,
748801 strbuf_extend_length (json , len );
749802}
750803
804+ /* Compare key_entry_t for qsort. */
805+ static int cmp_key_entries (const void * a , const void * b ) {
806+ const key_entry_t * ka = a ;
807+ const key_entry_t * kb = b ;
808+ int res = memcmp (ka -> buf -> buf + ka -> offset ,
809+ kb -> buf -> buf + kb -> offset ,
810+ ka -> length < kb -> length ? ka -> length : kb -> length );
811+ if (res == 0 )
812+ return (ka -> length - kb -> length );
813+ return res ;
814+
815+ }
816+
751817static void json_append_object (lua_State * l , json_config_t * cfg ,
752818 int current_depth , strbuf_t * json )
753819{
@@ -756,40 +822,118 @@ static void json_append_object(lua_State *l, json_config_t *cfg,
756822 /* Object */
757823 strbuf_append_char (json , '{' );
758824
759- lua_pushnil (l );
760- /* table, startkey */
761825 comma = 0 ;
762- while ( lua_next ( l , -2 ) != 0 ) {
763- json_pos = strbuf_length ( json ) ;
764- if ( comma ++ > 0 )
765- strbuf_append_char ( json , ',' );
826+ if ( cfg -> encode_sort_keys ) {
827+ keybuf_t * keybuf = & cfg -> encode_keybuf ;
828+ size_t init_keybuf_size = cfg -> encode_keybuf . size ;
829+ size_t init_keybuf_length = strbuf_length ( & cfg -> encode_keybuf . buf );
766830
767- /* table, key, value */
768- keytype = lua_type (l , -2 );
769- if (keytype == LUA_TNUMBER ) {
770- strbuf_append_char (json , '"' );
771- json_append_number (l , cfg , json , -2 );
772- strbuf_append_mem (json , "\":" , 2 );
773- } else if (keytype == LUA_TSTRING ) {
774- json_append_string (l , json , -2 );
775- strbuf_append_char (json , ':' );
776- } else {
777- json_encode_exception (l , cfg , json , -2 ,
778- "table key must be a number or string" );
779- /* never returns */
831+ lua_pushnil (l );
832+ while (lua_next (l , -2 ) != 0 ) {
833+ if (keybuf -> size == keybuf -> capacity ){
834+ if (!keybuf -> capacity ) {
835+ keybuf -> capacity = KEYBUF_DEFAULT_CAPACITY ;
836+ keybuf -> keys = malloc (keybuf -> capacity * sizeof (key_entry_t ));
837+ if (!keybuf -> keys )
838+ json_encode_exception (l , cfg , json , -1 , "out of memory" );
839+ } else {
840+ keybuf -> capacity *= 2 ;
841+ key_entry_t * tmp = realloc (keybuf -> keys ,
842+ keybuf -> capacity * sizeof (key_entry_t ));
843+ if (!tmp )
844+ json_encode_exception (l , cfg , json , -1 , "out of memory" );
845+ keybuf -> keys = tmp ;
846+ }
847+ }
848+
849+ keytype = lua_type (l , -2 );
850+ key_entry_t key_entry = {
851+ .buf = & keybuf -> buf ,
852+ .offset = strbuf_length (& keybuf -> buf ),
853+ .raw_typ = keytype ,
854+ };
855+ if (keytype == LUA_TSTRING ) {
856+ json_append_string_contents (l , & keybuf -> buf , -2 );
857+ key_entry .raw .string = lua_tostring (l , -2 );
858+ } else if (keytype == LUA_TNUMBER ) {
859+ json_append_number (l , cfg , & keybuf -> buf , -2 );
860+ key_entry .raw .number = lua_tointeger (l , -2 );
861+ } else {
862+ json_encode_exception (l , cfg , json , -2 ,
863+ "table key must be number or string" );
864+ }
865+ key_entry .length = strbuf_length (& keybuf -> buf ) - key_entry .offset ;
866+ keybuf -> keys [keybuf -> size ++ ] = key_entry ;
867+ lua_pop (l , 1 );
780868 }
781869
782- /* table, key, value */
783- err = json_append_data (l , cfg , current_depth , json );
784- if (err ) {
785- strbuf_set_length (json , json_pos );
786- if (comma == 1 ) {
787- comma = 0 ;
870+ size_t keys_count = keybuf -> size - init_keybuf_size ;
871+ qsort (keybuf -> keys + init_keybuf_size , keys_count ,
872+ sizeof (key_entry_t ), cmp_key_entries );
873+
874+ for (size_t i = init_keybuf_size ; i < init_keybuf_size + keys_count ; i ++ ) {
875+ key_entry_t * current_key = & keybuf -> keys [i ];
876+ json_pos = strbuf_length (json );
877+ if (comma ++ > 0 )
878+ strbuf_append_char (json , ',' );
879+
880+ strbuf_ensure_empty_length (json , current_key -> length + 3 );
881+ strbuf_append_char_unsafe (json , '"' );
882+ strbuf_append_mem_unsafe (json , keybuf -> buf .buf + current_key -> offset ,
883+ current_key -> length );
884+ strbuf_append_mem (json , "\":" , 2 );
885+
886+ if (current_key -> raw_typ == LUA_TSTRING )
887+ lua_pushstring (l , current_key -> raw .string );
888+ else
889+ lua_pushnumber (l , current_key -> raw .number );
890+
891+ lua_gettable (l , -2 );
892+ err = json_append_data (l , cfg , current_depth , json );
893+ if (err ) {
894+ strbuf_set_length (json , json_pos );
895+ if (comma == 1 )
896+ comma = 0 ;
788897 }
898+ lua_pop (l , 1 );
789899 }
900+ /* resize encode_keybuf to reuse allocated memory for forward keys */
901+ strbuf_set_length (& keybuf -> buf , init_keybuf_length );
902+ keybuf -> size = init_keybuf_size ;
903+ } else {
904+ lua_pushnil (l );
905+ /* table, startkey */
906+ while (lua_next (l , -2 ) != 0 ) {
907+ json_pos = strbuf_length (json );
908+ if (comma ++ > 0 )
909+ strbuf_append_char (json , ',' );
910+
911+ /* table, key, value */
912+ keytype = lua_type (l , -2 );
913+ if (keytype == LUA_TNUMBER ) {
914+ strbuf_append_char (json , '"' );
915+ json_append_number (l , cfg , json , -2 );
916+ strbuf_append_mem (json , "\":" , 2 );
917+ } else if (keytype == LUA_TSTRING ) {
918+ json_append_string (l , json , -2 );
919+ strbuf_append_char (json , ':' );
920+ } else {
921+ json_encode_exception (l , cfg , json , -2 ,
922+ "table key must be a number or string" );
923+ /* never returns */
924+ }
790925
791- lua_pop (l , 1 );
792- /* table, key */
926+ /* table, key, value */
927+ err = json_append_data (l , cfg , current_depth , json );
928+ if (err ) {
929+ strbuf_set_length (json , json_pos );
930+ if (comma == 1 )
931+ comma = 0 ;
932+ }
933+
934+ lua_pop (l , 1 );
935+ /* table, key */
936+ }
793937 }
794938
795939 strbuf_append_char (json , '}' );
@@ -914,6 +1058,12 @@ static int json_encode(lua_State *l)
9141058 strbuf_reset (encode_buf );
9151059 }
9161060
1061+ if (cfg -> encode_sort_keys ) {
1062+ strbuf_init (& cfg -> encode_keybuf .buf , 0 );
1063+ cfg -> encode_keybuf .size = 0 ;
1064+ cfg -> encode_keybuf .capacity = 0 ;
1065+ }
1066+
9171067 json_append_data (l , cfg , 0 , encode_buf );
9181068 json = strbuf_string (encode_buf , & len );
9191069
@@ -922,6 +1072,11 @@ static int json_encode(lua_State *l)
9221072 if (!cfg -> encode_keep_buffer )
9231073 strbuf_free (encode_buf );
9241074
1075+ if (cfg -> encode_sort_keys ) {
1076+ strbuf_free (& cfg -> encode_keybuf .buf );
1077+ free (cfg -> encode_keybuf .keys );
1078+ }
1079+
9251080 return 1 ;
9261081}
9271082
@@ -1571,6 +1726,7 @@ static int lua_cjson_new(lua_State *l)
15711726 { "decode_invalid_numbers" , json_cfg_decode_invalid_numbers },
15721727 { "encode_escape_forward_slash" , json_cfg_encode_escape_forward_slash },
15731728 { "encode_skip_unsupported_value_types" , json_cfg_encode_skip_unsupported_value_types },
1729+ { "encode_sort_keys" , json_cfg_encode_sort_keys },
15741730 { "new" , lua_cjson_new },
15751731 { NULL , NULL }
15761732 };
0 commit comments