# HG changeset patch # User jbe # Date 1406827649 -7200 # Node ID 581878c613d664ad7ee31056362d18226c311427 # Parent 3b8c1e2aef9cb257db28262f9cd5e21d6ae3fcad Always sort keys in JSON objects when exporting diff -r 3b8c1e2aef9c -r 581878c613d6 libraries/json/json.c --- a/libraries/json/json.c Thu Jul 31 16:24:39 2014 +0200 +++ b/libraries/json/json.c Thu Jul 31 19:27:29 2014 +0200 @@ -678,6 +678,35 @@ return 3; } +typedef struct { + size_t length; + const char *data; +} json_key_t; + +static int json_key_cmp(json_key_t *key1, json_key_t *key2) { + size_t pos = 0; + unsigned char c1, c2; + while (1) { + if (key1->length > pos) { + if (key2->length > pos) { + c1 = key1->data[pos]; + c2 = key2->data[pos]; + if (c1 < c2) return -1; + else if (c1 > c2) return 1; + } else { + return 1; + } + } else { + if (key2->length > pos) { + return -1; + } else { + return 0; + } + } + pos++; + } +} + #define JSON_TABLETYPE_UNKNOWN 0 #define JSON_TABLETYPE_OBJECT 1 #define JSON_TABLETYPE_ARRAY 2 @@ -693,6 +722,9 @@ int luatype; int tabletype = JSON_TABLETYPE_UNKNOWN; int needsep = 0; + size_t keycount = 0; + size_t keypos = 0; + json_key_t *keybuf; lua_Integer idx; lua_settop(L, 1); if (json_isnullmark(L, 1)) { @@ -768,33 +800,43 @@ } switch (tabletype) { case JSON_TABLETYPE_OBJECT: - lua_settop(L, 3); + for (lua_pushnil(L); lua_next(L, 1); lua_pop(L, 1)) { + if (lua_type(L, -2) == LUA_TSTRING) keycount++; + } + if (keycount) { + keybuf = calloc(keycount, sizeof(json_key_t)); + if (!keybuf) return luaL_error(L, "Memory allocation failed in JSON library"); + for (lua_pushnil(L); lua_next(L, 1); lua_pop(L, 1)) { + if (lua_type(L, -2) == LUA_TSTRING) { + json_key_t *key = keybuf + (keypos++); + key->data = lua_tolstring(L, -2, &key->length); + } + } + qsort(keybuf, keycount, sizeof(json_key_t), (void *)json_key_cmp); + } luaL_buffinit(L, &buf); luaL_addchar(&buf, '{'); - for (lua_pushnil(L); lua_next(L, 1); ) { - if (lua_type(L, -2) == LUA_TSTRING) { - lua_replace(L, 3); - lua_replace(L, 2); - if (needsep) luaL_addchar(&buf, ','); - else needsep = 1; - lua_pushcfunction(L, json_export); - lua_pushvalue(L, 2); - lua_call(L, 1, 1); - luaL_addvalue(&buf); - luaL_addchar(&buf, ':'); - if (json_isnullmark(L, 3)) { - luaL_addstring(&buf, "null"); - } else { - lua_pushcfunction(L, json_export); - lua_pushvalue(L, 3); - lua_call(L, 1, 1); - luaL_addvalue(&buf); - } - lua_pushvalue(L, 2); - } else { - lua_pop(L, 1); + for (keypos=0; keyposdata, key->length); + if (lua_pcall(L, 1, 1, 0)) { + if (keybuf) free(keybuf); + return lua_error(L); } + luaL_addvalue(&buf); + luaL_addchar(&buf, ':'); + lua_pushcfunction(L, json_export); + lua_pushlstring(L, key->data, key->length); + lua_rawget(L, 1); + if (lua_pcall(L, 1, 1, 0)) { + if (keybuf) free(keybuf); + return lua_error(L); + } + luaL_addvalue(&buf); } + if (keybuf) free(keybuf); luaL_addchar(&buf, '}'); luaL_pushresult(&buf); return 1;