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,15 @@ 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 = (const key_entry_t * )a ;
807+ const key_entry_t * kb = (const key_entry_t * )b ;
808+ return memcmp (ka -> buf -> buf + ka -> offset ,
809+ kb -> buf -> buf + kb -> offset ,
810+ (ka -> length < kb -> length ? ka -> length : kb -> length ));
811+ }
812+
751813static void json_append_object (lua_State * l , json_config_t * cfg ,
752814 int current_depth , strbuf_t * json )
753815{
@@ -756,40 +818,118 @@ static void json_append_object(lua_State *l, json_config_t *cfg,
756818 /* Object */
757819 strbuf_append_char (json , '{' );
758820
759- lua_pushnil (l );
760- /* table, startkey */
761821 comma = 0 ;
762- while ( lua_next ( l , -2 ) != 0 ) {
763- json_pos = strbuf_length ( json ) ;
764- if ( comma ++ > 0 )
765- strbuf_append_char ( json , ',' );
822+ if ( cfg -> encode_sort_keys ) {
823+ keybuf_t * keybuf = & cfg -> encode_keybuf ;
824+ size_t init_keybuf_size = cfg -> encode_keybuf . size ;
825+ size_t init_keybuf_length = strbuf_length ( & cfg -> encode_keybuf . buf );
766826
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 */
827+ lua_pushnil (l );
828+ while (lua_next (l , -2 ) != 0 ) {
829+ if (keybuf -> size == keybuf -> capacity ){
830+ if (!keybuf -> capacity ) {
831+ keybuf -> capacity = KEYBUF_DEFAULT_CAPACITY ;
832+ keybuf -> keys = malloc (keybuf -> capacity * sizeof (key_entry_t ));
833+ if (!keybuf -> keys )
834+ json_encode_exception (l , cfg , json , -1 , "out of memory" );
835+ } else {
836+ keybuf -> capacity *= 2 ;
837+ key_entry_t * tmp = realloc (keybuf -> keys ,
838+ keybuf -> capacity * sizeof (key_entry_t ));
839+ if (!tmp )
840+ json_encode_exception (l , cfg , json , -1 , "out of memory" );
841+ keybuf -> keys = tmp ;
842+ }
843+ }
844+
845+ keytype = lua_type (l , -2 );
846+ key_entry_t key_entry = {
847+ .buf = & keybuf -> buf ,
848+ .offset = strbuf_length (& keybuf -> buf ),
849+ .raw_typ = keytype ,
850+ };
851+ if (keytype == LUA_TSTRING ) {
852+ json_append_string_contents (l , & keybuf -> buf , -2 );
853+ key_entry .raw .string = lua_tostring (l , -2 );
854+ } else if (keytype == LUA_TNUMBER ) {
855+ json_append_number (l , cfg , & keybuf -> buf , -2 );
856+ key_entry .raw .number = lua_tointeger (l , -2 );
857+ } else {
858+ json_encode_exception (l , cfg , json , -2 ,
859+ "table key must be number or string" );
860+ }
861+ key_entry .length = strbuf_length (& keybuf -> buf ) - key_entry .offset ;
862+ keybuf -> keys [keybuf -> size ++ ] = key_entry ;
863+ lua_pop (l , 1 );
780864 }
781865
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 ;
866+ size_t keys_count = keybuf -> size - init_keybuf_size ;
867+ qsort (keybuf -> keys + init_keybuf_size , keys_count ,
868+ sizeof (key_entry_t ), cmp_key_entries );
869+
870+ for (size_t i = init_keybuf_size ; i < init_keybuf_size + keys_count ; i ++ ) {
871+ key_entry_t * current_key = & keybuf -> keys [i ];
872+ json_pos = strbuf_length (json );
873+ if (comma ++ > 0 )
874+ strbuf_append_char (json , ',' );
875+
876+ strbuf_ensure_empty_length (json , current_key -> length + 3 );
877+ strbuf_append_char_unsafe (json , '"' );
878+ strbuf_append_mem_unsafe (json , keybuf -> buf .buf + current_key -> offset ,
879+ current_key -> length );
880+ strbuf_append_mem (json , "\":" , 2 );
881+
882+ if (current_key -> raw_typ == LUA_TSTRING )
883+ lua_pushstring (l , current_key -> raw .string );
884+ else
885+ lua_pushnumber (l , current_key -> raw .number );
886+
887+ lua_gettable (l , -2 );
888+ err = json_append_data (l , cfg , current_depth , json );
889+ if (err ) {
890+ strbuf_set_length (json , json_pos );
891+ if (comma == 1 )
892+ comma = 0 ;
788893 }
894+ lua_pop (l , 1 );
789895 }
896+ /* resize encode_keybuf to reuse allocated memory for forward keys */
897+ strbuf_set_length (& keybuf -> buf , init_keybuf_length );
898+ keybuf -> size = init_keybuf_size ;
899+ } else {
900+ lua_pushnil (l );
901+ /* table, startkey */
902+ while (lua_next (l , -2 ) != 0 ) {
903+ json_pos = strbuf_length (json );
904+ if (comma ++ > 0 )
905+ strbuf_append_char (json , ',' );
906+
907+ /* table, key, value */
908+ keytype = lua_type (l , -2 );
909+ if (keytype == LUA_TNUMBER ) {
910+ strbuf_append_char (json , '"' );
911+ json_append_number (l , cfg , json , -2 );
912+ strbuf_append_mem (json , "\":" , 2 );
913+ } else if (keytype == LUA_TSTRING ) {
914+ json_append_string (l , json , -2 );
915+ strbuf_append_char (json , ':' );
916+ } else {
917+ json_encode_exception (l , cfg , json , -2 ,
918+ "table key must be a number or string" );
919+ /* never returns */
920+ }
790921
791- lua_pop (l , 1 );
792- /* table, key */
922+ /* table, key, value */
923+ err = json_append_data (l , cfg , current_depth , json );
924+ if (err ) {
925+ strbuf_set_length (json , json_pos );
926+ if (comma == 1 )
927+ comma = 0 ;
928+ }
929+
930+ lua_pop (l , 1 );
931+ /* table, key */
932+ }
793933 }
794934
795935 strbuf_append_char (json , '}' );
@@ -914,6 +1054,12 @@ static int json_encode(lua_State *l)
9141054 strbuf_reset (encode_buf );
9151055 }
9161056
1057+ if (cfg -> encode_sort_keys ) {
1058+ strbuf_init (& cfg -> encode_keybuf .buf , 0 );
1059+ cfg -> encode_keybuf .size = 0 ;
1060+ cfg -> encode_keybuf .capacity = 0 ;
1061+ }
1062+
9171063 json_append_data (l , cfg , 0 , encode_buf );
9181064 json = strbuf_string (encode_buf , & len );
9191065
@@ -922,6 +1068,11 @@ static int json_encode(lua_State *l)
9221068 if (!cfg -> encode_keep_buffer )
9231069 strbuf_free (encode_buf );
9241070
1071+ if (cfg -> encode_sort_keys ) {
1072+ strbuf_free (& cfg -> encode_keybuf .buf );
1073+ free (cfg -> encode_keybuf .keys );
1074+ }
1075+
9251076 return 1 ;
9261077}
9271078
@@ -1571,6 +1722,7 @@ static int lua_cjson_new(lua_State *l)
15711722 { "decode_invalid_numbers" , json_cfg_decode_invalid_numbers },
15721723 { "encode_escape_forward_slash" , json_cfg_encode_escape_forward_slash },
15731724 { "encode_skip_unsupported_value_types" , json_cfg_encode_skip_unsupported_value_types },
1725+ { "encode_sort_keys" , json_cfg_encode_sort_keys },
15741726 { "new" , lua_cjson_new },
15751727 { NULL , NULL }
15761728 };
0 commit comments