Bug 1074591 (part 1) - Allow JSON collections to be printed on a single line. r=froydnj.

--HG--
extra : rebase_source : d576a70d2d1ff83311ab8cce849ec0b5a6b3fa80
This commit is contained in:
Nicholas Nethercote 2014-10-02 18:01:19 -07:00
parent b6419e9f3a
commit 56daec2fe6
2 changed files with 215 additions and 27 deletions

View File

@ -33,6 +33,10 @@
// pretty-printing, which are (a) correctly escaping strings, and (b) adding
// appropriate indentation and commas between items.
//
// By default, every property is placed on its own line. However, it is
// possible to request that objects and arrays be placed entirely on a single
// line, which can reduce output size significantly in some cases.
//
// Strings used (for property names and string property values) are |const
// char*| throughout, and can be ASCII or UTF-8.
//
@ -54,6 +58,13 @@
// w.StartObjectElement();
// {
// w.PointerProperty("ptr", (void*)0x12345678);
// w.StartArrayProperty("single-line array", w.SingleLineStyle);
// {
// w.IntElement(1);
// w.StartObjectElement(); // SingleLineStyle is inherited from
// w.EndObjectElement(); // above for this collection
// }
// w.EndArray();
// }
// w.EndObjectElement();
// }
@ -71,7 +82,8 @@
// "array": [
// 3.4,
// {
// "ptr": "0x12345678"
// "ptr": "0x12345678",
// "single-line array": [1, {}]
// }
// ]
// }
@ -217,8 +229,21 @@ class JSONWriter
}
};
public:
// Collections (objects and arrays) are printed in a multi-line style by
// default. This can be changed to a single-line style if SingleLineStyle is
// specified. If a collection is printed in single-line style, every nested
// collection within it is also printed in single-line style, even if
// multi-line style is requested.
enum CollectionStyle {
MultiLineStyle, // the default
SingleLineStyle
};
private:
const UniquePtr<JSONWriteFunc> mWriter;
Vector<bool, 8> mNeedComma; // do we need a comma at depth N?
Vector<bool, 8> mNeedNewlines; // do we need newlines at depth N?
size_t mDepth; // the current nesting depth
void Indent()
@ -236,10 +261,12 @@ class JSONWriter
if (mNeedComma[mDepth]) {
mWriter->Write(",");
}
if (mDepth > 0) {
if (mDepth > 0 && mNeedNewlines[mDepth]) {
mWriter->Write("\n");
Indent();
} else if (mNeedComma[mDepth]) {
mWriter->Write(" ");
}
Indent();
}
void PropertyNameAndColon(const char* aName)
@ -272,15 +299,18 @@ class JSONWriter
mNeedComma[mDepth] = true;
}
void NewCommaEntry()
void NewVectorEntries()
{
// If this tiny allocation OOMs we might as well just crash because we must
// be in serious memory trouble.
// If these tiny allocations OOM we might as well just crash because we
// must be in serious memory trouble.
MOZ_RELEASE_ASSERT(mNeedComma.growByUninitialized(1));
MOZ_RELEASE_ASSERT(mNeedNewlines.growByUninitialized(1));
mNeedComma[mDepth] = false;
mNeedNewlines[mDepth] = true;
}
void StartCollection(const char* aMaybePropertyName, const char* aStartChar)
void StartCollection(const char* aMaybePropertyName, const char* aStartChar,
CollectionStyle aStyle = MultiLineStyle)
{
Separator();
if (aMaybePropertyName) {
@ -291,15 +321,21 @@ class JSONWriter
mWriter->Write(aStartChar);
mNeedComma[mDepth] = true;
mDepth++;
NewCommaEntry();
NewVectorEntries();
mNeedNewlines[mDepth] =
mNeedNewlines[mDepth - 1] && aStyle == MultiLineStyle;
}
// Adds the whitespace and closing char necessary to end a collection.
void EndCollection(const char* aEndChar)
{
mDepth--;
mWriter->Write("\n");
Indent();
if (mNeedNewlines[mDepth]) {
mWriter->Write("\n");
mDepth--;
Indent();
} else {
mDepth--;
}
mWriter->Write(aEndChar);
}
@ -307,9 +343,10 @@ public:
explicit JSONWriter(UniquePtr<JSONWriteFunc> aWriter)
: mWriter(Move(aWriter))
, mNeedComma()
, mNeedNewlines()
, mDepth(0)
{
NewCommaEntry();
NewVectorEntries();
}
// Returns the JSONWriteFunc passed in at creation, for temporary use. The
@ -317,15 +354,18 @@ public:
JSONWriteFunc* WriteFunc() const { return mWriter.get(); }
// For all the following functions, the "Prints:" comment indicates what the
// basic output looks like. However, it doesn't indicate the indentation and
// basic output looks like. However, it doesn't indicate the whitespace and
// trailing commas, which are automatically added as required.
//
// All property names and string properties are escaped as necessary.
// Prints: {
void Start() { StartCollection(nullptr, "{"); }
void Start(CollectionStyle aStyle = MultiLineStyle)
{
StartCollection(nullptr, "{", aStyle);
}
// Prints: }\n
// Prints: }
void End() { EndCollection("}\n"); }
// Prints: "<aName>": null
@ -396,19 +436,33 @@ public:
void PointerElement(const void* aPtr) { PointerProperty(nullptr, aPtr); }
// Prints: "<aName>": [
void StartArrayProperty(const char* aName) { StartCollection(aName, "["); }
void StartArrayProperty(const char* aName,
CollectionStyle aStyle = MultiLineStyle)
{
StartCollection(aName, "[", aStyle);
}
// Prints: [
void StartArrayElement() { StartArrayProperty(nullptr); }
void StartArrayElement(CollectionStyle aStyle = MultiLineStyle)
{
StartArrayProperty(nullptr, aStyle);
}
// Prints: ]
void EndArray() { EndCollection("]"); }
// Prints: "<aName>": {
void StartObjectProperty(const char* aName) { StartCollection(aName, "{"); }
void StartObjectProperty(const char* aName,
CollectionStyle aStyle = MultiLineStyle)
{
StartCollection(aName, "{", aStyle);
}
// Prints: {
void StartObjectElement() { StartObjectProperty(nullptr); }
void StartObjectElement(CollectionStyle aStyle = MultiLineStyle)
{
StartObjectProperty(nullptr, aStyle);
}
// Prints: }
void EndObject() { EndCollection("}"); }

View File

@ -73,20 +73,23 @@ void TestBasicProperties()
\"ptr1\": \"0x0\",\n\
\"ptr2\": \"0xdeadbeef\",\n\
\"ptr3\": \"0xfacade\",\n\
\"len 0 array\": [\n\
\"len 0 array, multi-line\": [\n\
],\n\
\"len 0 array, single-line\": [],\n\
\"len 1 array\": [\n\
1\n\
],\n\
\"len 5 array\": [\n\
\"len 5 array, multi-line\": [\n\
1,\n\
2,\n\
3,\n\
4,\n\
5\n\
],\n\
\"len 0 object\": {\n\
\"len 3 array, single-line\": [1, [{}, 2, []], 3],\n\
\"len 0 object, multi-line\": {\n\
},\n\
\"len 0 object, single-line\": {},\n\
\"len 1 object\": {\n\
\"one\": 1\n\
},\n\
@ -96,7 +99,8 @@ void TestBasicProperties()
\"three\": 3,\n\
\"four\": 4,\n\
\"five\": 5\n\
}\n\
},\n\
\"len 3 object, single-line\": {\"a\": 1, \"b\": [{}, 2, []], \"c\": 3}\n\
}\n\
";
@ -127,7 +131,10 @@ void TestBasicProperties()
w.PointerProperty("ptr2", (void*)0xdeadbeef);
w.PointerProperty("ptr3", (void*)0xFaCaDe);
w.StartArrayProperty("len 0 array");
w.StartArrayProperty("len 0 array, multi-line", w.MultiLineStyle);
w.EndArray();
w.StartArrayProperty("len 0 array, single-line", w.SingleLineStyle);
w.EndArray();
w.StartArrayProperty("len 1 array");
@ -136,7 +143,7 @@ void TestBasicProperties()
}
w.EndArray();
w.StartArrayProperty("len 5 array");
w.StartArrayProperty("len 5 array, multi-line", w.MultiLineStyle);
{
w.IntElement(1);
w.IntElement(2);
@ -146,7 +153,28 @@ void TestBasicProperties()
}
w.EndArray();
w.StartObjectProperty("len 0 object");
w.StartArrayProperty("len 3 array, single-line", w.SingleLineStyle);
{
w.IntElement(1);
w.StartArrayElement();
{
w.StartObjectElement(w.SingleLineStyle);
w.EndObject();
w.IntElement(2);
w.StartArrayElement(w.MultiLineStyle); // style overridden from above
w.EndArray();
}
w.EndArray();
w.IntElement(3);
}
w.EndArray();
w.StartObjectProperty("len 0 object, multi-line");
w.EndObject();
w.StartObjectProperty("len 0 object, single-line", w.SingleLineStyle);
w.EndObject();
w.StartObjectProperty("len 1 object");
@ -164,6 +192,24 @@ void TestBasicProperties()
w.IntProperty("five", 5);
}
w.EndObject();
w.StartObjectProperty("len 3 object, single-line", w.SingleLineStyle);
{
w.IntProperty("a", 1);
w.StartArrayProperty("b");
{
w.StartObjectElement();
w.EndObject();
w.IntElement(2);
w.StartArrayElement(w.SingleLineStyle);
w.EndArray();
}
w.EndArray();
w.IntProperty("c", 3);
}
w.EndObject();
}
w.End();
@ -194,6 +240,7 @@ void TestBasicElements()
\"0xfacade\",\n\
[\n\
],\n\
[],\n\
[\n\
1\n\
],\n\
@ -204,8 +251,10 @@ void TestBasicElements()
4,\n\
5\n\
],\n\
[1, [{}, 2, []], 3],\n\
{\n\
},\n\
{},\n\
{\n\
\"one\": 1\n\
},\n\
@ -215,7 +264,8 @@ void TestBasicElements()
\"three\": 3,\n\
\"four\": 4,\n\
\"five\": 5\n\
}\n\
},\n\
{\"a\": 1, \"b\": [{}, 2, []], \"c\": 3}\n\
]\n\
}\n\
";
@ -251,6 +301,9 @@ void TestBasicElements()
w.StartArrayElement();
w.EndArray();
w.StartArrayElement(w.SingleLineStyle);
w.EndArray();
w.StartArrayElement();
{
w.IntElement(1);
@ -267,9 +320,30 @@ void TestBasicElements()
}
w.EndArray();
w.StartArrayElement(w.SingleLineStyle);
{
w.IntElement(1);
w.StartArrayElement();
{
w.StartObjectElement(w.SingleLineStyle);
w.EndObject();
w.IntElement(2);
w.StartArrayElement(w.MultiLineStyle); // style overridden from above
w.EndArray();
}
w.EndArray();
w.IntElement(3);
}
w.EndArray();
w.StartObjectElement();
w.EndObject();
w.StartObjectElement(w.SingleLineStyle);
w.EndObject();
w.StartObjectElement();
{
w.IntProperty("one", 1);
@ -285,6 +359,24 @@ void TestBasicElements()
w.IntProperty("five", 5);
}
w.EndObject();
w.StartObjectElement(w.SingleLineStyle);
{
w.IntProperty("a", 1);
w.StartArrayProperty("b");
{
w.StartObjectElement();
w.EndObject();
w.IntElement(2);
w.StartArrayElement(w.SingleLineStyle);
w.EndArray();
}
w.EndArray();
w.IntProperty("c", 3);
}
w.EndObject();
}
w.EndArray();
w.End();
@ -292,6 +384,47 @@ void TestBasicElements()
Check(w.WriteFunc(), expected);
}
void TestOneLineObject()
{
const char* expected = "\
{\"i\": 1, \"array\": [null, [{}], {\"o\": {}}, \"s\"], \"d\": 3.33}\n\
";
JSONWriter w(MakeUnique<StringWriteFunc>());
w.Start(w.SingleLineStyle);
w.IntProperty("i", 1);
w.StartArrayProperty("array");
{
w.NullElement();
w.StartArrayElement(w.MultiLineStyle); // style overridden from above
{
w.StartObjectElement();
w.EndObject();
}
w.EndArray();
w.StartObjectElement();
{
w.StartObjectProperty("o");
w.EndObject();
}
w.EndObject();
w.StringElement("s");
}
w.EndArray();
w.DoubleProperty("d", 3.33);
w.End();
Check(w.WriteFunc(), expected);
}
void TestStringEscaping()
{
// This test uses hexadecimal character escapes because UTF8 literals cause
@ -412,6 +545,7 @@ int main(void)
{
TestBasicProperties();
TestBasicElements();
TestOneLineObject();
TestStringEscaping();
TestDeepNesting();