benchmark silo added
[c11concurrency-benchmarks.git] / silo / masstree / jsontest.cc
diff --git a/silo/masstree/jsontest.cc b/silo/masstree/jsontest.cc
new file mode 100644 (file)
index 0000000..e579939
--- /dev/null
@@ -0,0 +1,603 @@
+// -*- c-basic-offset: 4 -*-
+/*
+ * jsontest.{cc,hh} -- regression tests for Json
+ * Eddie Kohler
+ *
+ * Copyright (c) 2012-2014 Eddie Kohler
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Click LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Click LICENSE file; the license in that file is
+ * legally binding.
+ */
+
+#include "json.hh"
+#include <unordered_map>
+using namespace lcdf;
+
+#define CHECK(x) do { if (!(x)) { std::cerr << __FILE__ << ":" << __LINE__ << ": test '" << #x << "' failed\n"; exit(1); } } while (0)
+#define CHECK_JUP(x, str) do { if ((x).unparse() != (str)) { std::cerr << __FILE__ << ":" << __LINE__ << ": '" #x "' is '" << (x) << "', not '" << (str) << "'\n"; exit(1); } } while (0)
+
+
+#if 0
+template <typename T> void incr(T& x) __attribute__((noinline));
+template <typename T>
+void incr(T& x) {
+    ++x;
+}
+#endif
+
+void benchmark_parse() {
+    std::vector<String> parse_examples;
+    parse_examples.push_back("{}");
+    parse_examples.push_back("{\"foo\":\"bar\",\"baz\":\"flim\"}");
+    parse_examples.push_back("[1,2,3,4,5]");
+    parse_examples.push_back("[]");
+    parse_examples.push_back("[{},{\"b\":[]}]");
+    Json j;
+#if 0
+    for (int i = 0; i < 10000000; ++i)
+        j.assign_parse(parse_examples[rand() % parse_examples.size()]);
+#else
+    using std::swap;
+    Json::streaming_parser jsp;
+    for (int i = 0; i < 10000000; ++i) {
+        jsp.reset();
+        swap(jsp.result(), j);
+        const String& str = parse_examples[rand() % parse_examples.size()];
+        jsp.consume(str.begin(), str.end(), str, true);
+    }
+#endif
+    exit(0);
+}
+
+int main(int argc, char** argv) {
+    (void) argc, (void) argv;
+    //benchmark_parse();
+
+    Json j;
+    CHECK(j.empty());
+    CHECK(!j);
+
+    j = Json::make_object();
+    CHECK(j.empty());
+    CHECK(j);
+
+    j.set("foo", "bar");
+    CHECK(j["foo"]);
+    CHECK(j["foo"].to_s() == "bar");
+    CHECK(j.size() == 1);
+
+    j.set("baz", "flim");
+    CHECK(j.size() == 2);
+    CHECK(j.unparse() == "{\"foo\":\"bar\",\"baz\":\"flim\"}");
+
+    j.erase("foo");
+    CHECK(j.size() == 1);
+
+    j.assign_parse("2");
+    CHECK(j == 2);
+
+    j.assign_parse("null");
+    CHECK(j == Json());
+
+    j.assign_parse("\"a\"");
+    CHECK(j == "a");
+
+    j.assign_parse("[1,2]");
+    CHECK(j.unparse() == "[1,2]");
+
+    j.assign_parse("[[[]],{\"a\":{}}]");
+    CHECK(j.unparse() == "[[[]],{\"a\":{}}]");
+
+    j = Json::parse("{\"x22\":{\n\
+      \"git-revision\":\"ebbd3d4767847300f552b181a10bda57a926f554M\",\n\
+      \"time\":\"Tue Feb  7 20:20:33 2012\",\n\
+      \"machine\":\"rtshanks-laptop\",\n\
+      \"cores\":2,\n\
+      \"runs\":[\"x22\\/rw2\\/mb\\/0\"]\n\
+    },\n\
+    \"x23\":{\n\
+      \"git-revision\":\"ebbd3d4767847300f552b181a10bda57a926f554M\",\n\
+      \"time\":\"Tue Feb  7 20:31:05 2012\",\n\
+      \"machine\":\"rtshanks-laptop\",\n\
+      \"cores\":2,\n\
+      \"runs\":[\"x23\\/rw2\\/mb\\/0\",\"x23\\/rw1\\/mb\\/0\",\"x23\\/rw3\\/mb\\/0\"]\n\
+    },\n\
+    \"x24\":{\n\
+      \"git-revision\":\"62e9970ca8ae9c6eebf2d71b7065ea694fb25282M\",\n\
+      \"time\":\"Sat Feb 11 15:54:01 2012\",\n\
+      \"machine\":\"rtshanks-laptop\",\n\
+      \"cores\":2,\n\
+      \"runs\":[\"x24\\/rw1\\/b\\/0\"]\n\
+    },\"b\":\"c\",\"c\":\"d\"}");
+    CHECK(j["x22"]["time"] == "Tue Feb  7 20:20:33 2012");
+    CHECK(j["x22"]["cores"] == 2);
+    {
+        Json::object_iterator it = j.obegin();
+        CHECK(it.key() == "x22");
+        ++it;
+        CHECK(it.key() == "x23");
+        ++it;
+        CHECK(it.key() == "x24");
+        ++it;
+        CHECK(it.key() == "b");
+        ++it;
+        CHECK(it.key() == "c");
+    }
+
+    {
+        Json jcopy = j;
+        CHECK(j.size() == 5);
+        int count = 0;
+        for (Json::iterator it = jcopy.begin(); it != jcopy.end(); ++it) {
+            it->second = Json();
+            ++count;
+        }
+        CHECK(!jcopy["x22"]);
+        CHECK(j["x22"]["cores"] == 2);
+        CHECK(count == jcopy.size());
+    }
+
+    CHECK(!j["x49"]);
+    CHECK(j.size() == 5);
+    CHECK(!j["x49"]["45"][2]["a"]);
+    CHECK(j.size() == 5);
+    j["x49"]["45"][2]["a"] = 1;
+    CHECK(j.size() == 6);
+    CHECK(j["x22"].is_object() && j.get("x22").is_object());
+    CHECK(j["x23"].is_object() && j.get("x23").is_object());
+    CHECK(j["b"].is_string() && j.get("b").is_string());
+    CHECK(j["x49"].is_object() && j.get("x49").is_object());
+    CHECK(j["x49"]["45"].is_array());
+    CHECK(j["x49"]["45"].size() == 3 && j["x49"]["45"][2].is_object());
+
+    j = Json::make_object();
+    j["a"] = j["b"];
+    CHECK(j.size() == 1);
+    CHECK(j["a"].is_null());
+    CHECK(j.count("a") == 1);
+
+    Json k = Json::make_array();
+    {
+        CHECK(k.size() == 0);
+        Json::array_iterator it = k.abegin();
+        CHECK(!it.live());
+    }
+    j = Json::make_object();
+    j["a"] = k[2];
+    CHECK(j.size() == 1);
+    CHECK(k.size() == 0);
+    CHECK(j["a"].is_null());
+    CHECK(j.count("a") == 1);
+
+    j = Json(1);
+    CHECK(j.get("a").is_null());
+
+    j.assign_parse("{\"a\":1,\"b\":true,\"c\":\"\"}");
+    {
+        int i = 0;
+        bool b = false;
+        String s;
+        CHECK(j.get("a", i));
+        CHECK(i == 1);
+        CHECK(j.get("a", i).get("b", b));
+        CHECK(b == true);
+        CHECK(!j.get("a", s).status());
+        CHECK(!j.get("a", s).status(b));
+        CHECK(b == false);
+        CHECK(j.get("a", k));
+        CHECK(k == Json(1));
+        CHECK(!j.get("cc", k));
+    }
+
+    j["a"] = Json(5);
+    j.set("a", Json(5));
+    j["b"] = Json::parse("[]");
+
+    {
+        Json j1 = Json::make_object(), j2 = Json::make_object();
+        j1.set("a", j2); // stores a COPY of j2 in j1
+        j2.set("b", 1);
+        CHECK(j1.unparse() == "{\"a\":{}}");
+        CHECK(j2.unparse() == "{\"b\":1}");
+    }
+
+    {
+        Json j = Json::parse("{\"a\":true}");
+        if (j["foo"]["bar"])
+            CHECK(false);
+        CHECK(j.unparse() == "{\"a\":true}");
+        j["foo"]["bar"] = true;
+        CHECK(j.unparse() == "{\"a\":true,\"foo\":{\"bar\":true}}");
+        j["a"]["2"] = false;
+        CHECK(j.unparse() == "{\"a\":{\"2\":false},\"foo\":{\"bar\":true}}");
+        j[3] = true;
+        CHECK(j.unparse() == "{\"a\":{\"2\":false},\"foo\":{\"bar\":true},\"3\":true}");
+        CHECK(j[3] == Json(true));
+        j = Json::parse("[1,2,3,4]");
+        CHECK(j["2"] == Json(3));
+        CHECK(j.unparse() == "[1,2,3,4]");
+        j["a"] = true;
+        CHECK(j.unparse() == "{\"0\":1,\"1\":2,\"2\":3,\"3\":4,\"a\":true}");
+        CHECK(j["2"] == Json(3));
+    }
+
+    {
+        Json j = Json::parse("{}");
+        j.set("foo", String::make_out_of_memory()).set(String::make_out_of_memory(), 2);
+        CHECK(j.unparse() == "{\"foo\":\"\360\237\222\243ENOMEM\360\237\222\243\",\"\360\237\222\243ENOMEM\360\237\222\243\":2}");
+        j.clear();
+        CHECK(j.unparse() == "{}");
+        CHECK(j.get("foo") == Json());
+    }
+
+    {
+        Json j = Json::array(1, 2, 3, 4, 5, 6, 7, 8);
+        CHECK(j.unparse() == "[1,2,3,4,5,6,7,8]");
+        Json jcopy = j;
+        CHECK(j.unparse() == "[1,2,3,4,5,6,7,8]");
+        CHECK(jcopy.unparse() == "[1,2,3,4,5,6,7,8]");
+        j.push_back(9);
+        CHECK(j.unparse() == "[1,2,3,4,5,6,7,8,9]");
+        CHECK(jcopy.unparse() == "[1,2,3,4,5,6,7,8]");
+        jcopy.push_back(10);
+        CHECK(j.unparse() == "[1,2,3,4,5,6,7,8,9]");
+        CHECK(jcopy.unparse() == "[1,2,3,4,5,6,7,8,10]");
+    }
+
+    {
+        unsigned long s = 77;
+        Json j = Json::array(0, 0, 0);
+        Json k = Json::array(0, s, "foo");
+        CHECK(j[1].is_i());
+        CHECK(k[1].is_u());
+        j[1] = k[1];
+        CHECK(j[1].is_u());
+    }
+
+#if 0
+    {
+        Json j = Json::array(1, 2, 3);
+        //int j[3] = {1, 2, 3};
+        for (int i = 0; i < 1000000; ++i)
+            incr(j[1].value());
+        std::cout << j << "\n";
+    }
+#endif
+
+    {
+        Json::streaming_parser jsp;
+        const uint8_t* x = reinterpret_cast<const uint8_t*>("\"abcdef\"");
+
+        const uint8_t* r = jsp.consume(x, x + 8, String());
+        CHECK(r == x + 8);
+        CHECK(jsp.success());
+        CHECK(jsp.result().unparse() == "\"abcdef\"");
+
+        x = reinterpret_cast<const uint8_t*>("\"\\\"\\\\fartbox\" amksdnsndfa");
+        jsp.reset();
+        r = jsp.consume(x, x + 9, String());
+        CHECK(r == x + 9);
+        CHECK(!jsp.done());
+        r = jsp.consume(x + 9, x + 17, String());
+        CHECK(r == x + 13);
+        CHECK(jsp.success());
+        CHECK(jsp.result().unparse() == "\"\\\"\\\\fartbox\"");
+
+        jsp.reset();
+        r = jsp.consume(x, x + 3, String());
+        CHECK(r == x + 3);
+        r = jsp.consume(x + 3, x + 6, String());
+        CHECK(r == x + 6);
+        r = jsp.consume(x + 6, x + 9, String());
+        CHECK(r == x + 9);
+        r = jsp.consume(x + 9, x + 13, String());
+        CHECK(r == x + 13);
+        CHECK(jsp.success());
+        CHECK(jsp.result().unparse() == "\"\\\"\\\\fartbox\"");
+
+        jsp.reset();
+        x = reinterpret_cast<const uint8_t*>("\"\\uD834\\uDD1E\"f");
+        r = jsp.consume(x, x + 15, String());
+        CHECK(r == x + 14);
+        CHECK(jsp.success());
+        CHECK(jsp.result().unparse() == "\"\xF0\x9D\x84\x9E\"");
+
+        jsp.reset();
+        r = jsp.consume(x, x + 2, String());
+        CHECK(r == x + 2);
+        r = jsp.consume(x + 2, x + 4, String());
+        CHECK(r == x + 4);
+        r = jsp.consume(x + 4, x + 6, String());
+        CHECK(r == x + 6);
+        r = jsp.consume(x + 6, x + 8, String());
+        CHECK(r == x + 8);
+        r = jsp.consume(x + 8, x + 10, String());
+        CHECK(r == x + 10);
+        r = jsp.consume(x + 10, x + 15, String());
+        CHECK(r == x + 14);
+        CHECK(jsp.success());
+        CHECK(jsp.result().unparse() == "\"\xF0\x9D\x84\x9E\"");
+
+        x = reinterpret_cast<const uint8_t*>("\"\xF0\x9D\x84\x9E\" fart");
+        jsp.reset();
+        r = jsp.consume(x, x + 3, String());
+        CHECK(r == x + 3);
+        r = jsp.consume(x + 5, x + 7, String());
+        CHECK(r == x + 5);
+        CHECK(jsp.error());
+
+        jsp.reset();
+        r = jsp.consume(x, x + 7, String());
+        CHECK(r == x + 6);
+        CHECK(jsp.success());
+        CHECK(jsp.result().unparse() == "\"\xF0\x9D\x84\x9E\"");
+
+        jsp.reset();
+        r = jsp.consume(x, x + 2, String());
+        CHECK(r == x + 2);
+        r = jsp.consume(x + 2, x + 4, String());
+        CHECK(r == x + 4);
+        r = jsp.consume(x + 4, x + 7, String());
+        CHECK(r == x + 6);
+        CHECK(jsp.success());
+        CHECK(jsp.result().unparse() == "\"\xF0\x9D\x84\x9E\"");
+
+        jsp.reset();
+        x = reinterpret_cast<const uint8_t*>("{}");
+        r = jsp.consume(x, x + 2, String());
+        CHECK(r == x + 2);
+        CHECK(jsp.success());
+        CHECK(jsp.result().unparse() == "{}");
+
+        jsp.reset();
+        x = reinterpret_cast<const uint8_t*>("\"ab cde\" ");
+        r = jsp.consume(x, x + 9, String());
+        CHECK(r == x + 8);
+        CHECK(jsp.success());
+        CHECK(jsp.result().unparse() == "\"ab cde\"");
+
+        jsp.reset();
+        x = reinterpret_cast<const uint8_t*>("{\"a\":\"b\"}");
+        r = jsp.consume(x, x + 9, String());
+        CHECK(r == x + 9);
+        CHECK(jsp.success());
+        CHECK(jsp.result().unparse() == "{\"a\":\"b\"}");
+
+        jsp.reset();
+        x = reinterpret_cast<const uint8_t*>("[]");
+        r = jsp.consume(x, x + 2, String());
+        CHECK(r == x + 2);
+        CHECK(jsp.success());
+        CHECK(jsp.result().unparse() == "[]");
+
+        jsp.reset();
+        x = reinterpret_cast<const uint8_t*>("[\"a\",\"b\"]");
+        r = jsp.consume(x, x + 9, String());
+        CHECK(r == x + 9);
+        CHECK(jsp.success());
+        CHECK(jsp.result().unparse() == "[\"a\",\"b\"]");
+
+        jsp.reset();
+        x = reinterpret_cast<const uint8_t*>("[[\"a\"],[[\"b\"]]]");
+        r = jsp.consume(x, x + 15, String());
+        CHECK(r == x + 15);
+        CHECK(jsp.success());
+        CHECK(jsp.result().unparse() == "[[\"a\"],[[\"b\"]]]");
+
+        jsp.reset();
+        x = reinterpret_cast<const uint8_t*>("[[],[[]]]");
+        r = jsp.consume(x, x + 9, String());
+        CHECK(r == x + 9);
+        CHECK(jsp.success());
+        CHECK(jsp.result().unparse() == "[[],[[]]]");
+
+        jsp.reset();
+        x = reinterpret_cast<const uint8_t*>("{\"abc\":\"def\"}");
+        r = jsp.consume(x, x + 3, String());
+        CHECK(r == x + 3);
+        r = jsp.consume(x + 3, x + 6, String());
+        CHECK(r == x + 6);
+        r = jsp.consume(x + 6, x + 9, String());
+        CHECK(r == x + 9);
+        r = jsp.consume(x + 9, x + 12, String());
+        CHECK(r == x + 12);
+        r = jsp.consume(x + 12, x + 13, String());
+        CHECK(r == x + 13);
+        CHECK(jsp.success());
+        CHECK(jsp.result().unparse() == "{\"abc\":\"def\"}");
+
+        jsp.reset();
+        x = reinterpret_cast<const uint8_t*>("{\"abc\":true }");
+        r = jsp.consume(x, x + 3, String());
+        CHECK(r == x + 3);
+        r = jsp.consume(x + 3, x + 6, String());
+        CHECK(r == x + 6);
+        r = jsp.consume(x + 6, x + 9, String());
+        CHECK(r == x + 9);
+        r = jsp.consume(x + 9, x + 12, String());
+        CHECK(r == x + 12);
+        r = jsp.consume(x + 12, x + 13, String());
+        CHECK(r == x + 13);
+        CHECK(jsp.success());
+        CHECK(jsp.result().unparse() == "{\"abc\":true}");
+
+        jsp.reset();
+        x = reinterpret_cast<const uint8_t*>("{\"abc\": null}");
+        r = jsp.consume(x, x + 3, String());
+        CHECK(r == x + 3);
+        r = jsp.consume(x + 3, x + 6, String());
+        CHECK(r == x + 6);
+        r = jsp.consume(x + 6, x + 9, String());
+        CHECK(r == x + 9);
+        r = jsp.consume(x + 9, x + 12, String());
+        CHECK(r == x + 12);
+        r = jsp.consume(x + 12, x + 13, String());
+        CHECK(r == x + 13);
+        CHECK(jsp.success());
+        CHECK(jsp.result().unparse() == "{\"abc\":null}");
+
+        jsp.reset();
+        x = reinterpret_cast<const uint8_t*>("{\"abc\":false}");
+        r = jsp.consume(x, x + 3, String());
+        CHECK(r == x + 3);
+        r = jsp.consume(x + 3, x + 6, String());
+        CHECK(r == x + 6);
+        r = jsp.consume(x + 6, x + 9, String());
+        CHECK(r == x + 9);
+        r = jsp.consume(x + 9, x + 12, String());
+        CHECK(r == x + 12);
+        r = jsp.consume(x + 12, x + 13, String());
+        CHECK(r == x + 13);
+        CHECK(jsp.success());
+        CHECK(jsp.result().unparse() == "{\"abc\":false}");
+
+        jsp.reset();
+        x = reinterpret_cast<const uint8_t*>("{\"abc\": -13 }");
+        r = jsp.consume(x, x + 3, String());
+        CHECK(r == x + 3);
+        r = jsp.consume(x + 3, x + 6, String());
+        CHECK(r == x + 6);
+        r = jsp.consume(x + 6, x + 9, String());
+        CHECK(r == x + 9);
+        r = jsp.consume(x + 9, x + 12, String());
+        CHECK(r == x + 12);
+        r = jsp.consume(x + 12, x + 13, String());
+        CHECK(r == x + 13);
+        CHECK(jsp.success());
+        CHECK(jsp.result().unparse() == "{\"abc\":-13}");
+
+        jsp.reset();
+        x = reinterpret_cast<const uint8_t*>("{\"abc\":0    }");
+        r = jsp.consume(x, x + 3, String());
+        CHECK(r == x + 3);
+        r = jsp.consume(x + 3, x + 6, String());
+        CHECK(r == x + 6);
+        r = jsp.consume(x + 6, x + 9, String());
+        CHECK(r == x + 9);
+        r = jsp.consume(x + 9, x + 12, String());
+        CHECK(r == x + 12);
+        r = jsp.consume(x + 12, x + 13, String());
+        CHECK(r == x + 13);
+        CHECK(jsp.success());
+        CHECK(jsp.result().unparse() == "{\"abc\":0}");
+
+        jsp.reset();
+        x = reinterpret_cast<const uint8_t*>("[0,1,2,3,4e5,10.2]");
+        r = jsp.consume(x, x + 3, String());
+        CHECK(r == x + 3);
+        r = jsp.consume(x + 3, x + 6, String());
+        CHECK(r == x + 6);
+        r = jsp.consume(x + 6, x + 9, String());
+        CHECK(r == x + 9);
+        r = jsp.consume(x + 9, x + 12, String());
+        CHECK(r == x + 12);
+        r = jsp.consume(x + 12, x + 18, String());
+        CHECK(r == x + 18);
+        CHECK(jsp.success());
+        CHECK(jsp.result().unparse() == "[0,1,2,3,400000,10.2]");
+    }
+
+    {
+        unsigned long s = 77;
+        Json j = Json::array(0, s, "foo");
+        CHECK(j.is_a());
+        CHECK(j[1].is_u());
+        CHECK(j[1] == s);
+        CHECK(j[1] == 77);
+
+        j = Json::array((uint64_t) -1, (int64_t) -1,
+                        (uint64_t) 1, (int64_t) 1,
+                        (uint64_t) 2, (int64_t) 2);
+        CHECK(j[0] != j[1]);
+        CHECK(j[2] == j[3]);
+        CHECK(j[4] == j[5]);
+        CHECK(j[0] != j[2]);
+        CHECK(j[2] != j[4]);
+        CHECK_JUP(j, "[18446744073709551615,-1,1,1,2,2]");
+    }
+
+    {
+        std::vector<int> v = {1, 2, 3, 4, 5};
+        Json j(v.begin(), v.end());
+        CHECK(j.unparse() == "[1,2,3,4,5]");
+    }
+
+    {
+        std::unordered_map<String, String> h;
+        h["a"] = "b";
+        h["c"] = "d";
+        h["x"] = "e";
+        Json j(h.begin(), h.end());
+        CHECK(j.is_o());
+        CHECK(j.size() == 3);
+        CHECK(j["a"] == "b");
+        CHECK(j["c"] == "d");
+        CHECK(j["x"] == "e");
+    }
+
+    {
+        Json j = Json::array(1, 2, 3, 4, 5, 6, 7, 8);
+        CHECK(j.unparse() == "[1,2,3,4,5,6,7,8]");
+        Json jcopy = j;
+        CHECK(j.unparse() == "[1,2,3,4,5,6,7,8]");
+        CHECK(jcopy.unparse() == "[1,2,3,4,5,6,7,8]");
+        auto it = j.erase(j.abegin() + 4);
+        CHECK_JUP(j, "[1,2,3,4,6,7,8]");
+        CHECK_JUP(jcopy, "[1,2,3,4,5,6,7,8]");
+        CHECK(it == j.abegin() + 4);
+        it = j.erase(j.aend(), j.aend());
+        CHECK_JUP(j, "[1,2,3,4,6,7,8]");
+        CHECK(it == j.aend());
+        it = j.erase(j.abegin(), j.abegin());
+        CHECK_JUP(j, "[1,2,3,4,6,7,8]");
+        CHECK(it == j.abegin());
+        it = j.erase(j.abegin(), j.abegin() + 3);
+        CHECK_JUP(j, "[4,6,7,8]");
+        CHECK(it == j.abegin());
+    }
+
+    {
+        Json j((uint64_t) 1 << 63);
+        CHECK(j.is_u());
+        CHECK(j.unparse() == "9223372036854775808");
+        j = Json::parse("9223372036854775808");
+        CHECK(j.is_u());
+        CHECK(j.to_u() == (uint64_t) 1 << 63);
+    }
+
+    {
+        Json j = Json::object("a", 1, "b", Json(2), "c", "9137471");
+        CHECK(j.unparse() == "{\"a\":1,\"b\":2,\"c\":\"9137471\"}");
+    }
+
+    {
+        Json a = Json::parse("[{\"a\":1},{\"b\":2},{\"c\":3},{\"d\":4}]");
+        Json b = Json::array(a[1], a[3], a[2], a[0]);
+        CHECK(&a[0]["a"].cvalue() == &b[3]["a"].cvalue());
+        b.push_back(a[5]);
+        CHECK(b[4] == Json());
+        CHECK(a.size() == 4);
+        b = Json::object("a5", a[5], "a3", a[3]);
+        CHECK(b.unparse() == "{\"a5\":null,\"a3\":{\"d\":4}}");
+        CHECK(a.size() == 4);
+        b.set_list("a6", a[6], "a2", a[2]);
+        CHECK(b.unparse() == "{\"a5\":null,\"a3\":{\"d\":4},\"a6\":null,\"a2\":{\"c\":3}}");
+        CHECK(a.size() == 4);
+    }
+
+    {
+        Json a = Json::parse("[{\"a\":\"\\\"\\\\\\/\"}]");
+        CHECK(a[0]["a"] == "\"\\/");
+        CHECK(a.unparse() == "[{\"a\":\"\\\"\\\\\\/\"}]");
+    }
+
+    std::cout << "All tests pass!\n";
+    return 0;
+}