Json : Fix String Writer
Tidy-up and correct logic around writing JSON encoded strings. related #13832 Change-Id: I1eca33e11dff4457f85a896c02331c1cd9ae1110 Reviewed-on: https://review.haiku-os.org/617 Reviewed-by: waddlesplash <[email protected]>
This commit is contained in:
committed by
waddlesplash
parent
f1962303a3
commit
15fed7905d
@@ -39,6 +39,7 @@ private:
|
|||||||
status_t StreamStringEncoded(const char* string);
|
status_t StreamStringEncoded(const char* string);
|
||||||
status_t StreamStringEncoded(const char* string,
|
status_t StreamStringEncoded(const char* string,
|
||||||
off_t offset, size_t length);
|
off_t offset, size_t length);
|
||||||
|
status_t StreamStringUnicodeCharacter(uint32 c);
|
||||||
|
|
||||||
status_t StreamQuotedEncodedString(const char* string);
|
status_t StreamQuotedEncodedString(const char* string);
|
||||||
status_t StreamQuotedEncodedString(const char* string,
|
status_t StreamQuotedEncodedString(const char* string,
|
||||||
|
|||||||
@@ -55,6 +55,20 @@ b_json_simple_esc_sequence(char c)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static size_t
|
||||||
|
b_json_len_7bit_clean_non_esc(uint8* c, size_t length) {
|
||||||
|
size_t result = 0;
|
||||||
|
|
||||||
|
while (result < length
|
||||||
|
&& b_json_is_7bit_clean(c[result])
|
||||||
|
&& b_json_simple_esc_sequence(c[result]) == NULL) {
|
||||||
|
result++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*! The class and sub-classes of it are used as a stack internal to the
|
/*! The class and sub-classes of it are used as a stack internal to the
|
||||||
BJsonTextWriter class. As the JSON is parsed, the stack of these
|
BJsonTextWriter class. As the JSON is parsed, the stack of these
|
||||||
internal listeners follows the stack of the JSON parsing in terms of
|
internal listeners follows the stack of the JSON parsing in terms of
|
||||||
@@ -595,6 +609,16 @@ BJsonTextWriter::StreamStringEncoded(const char* string)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
BJsonTextWriter::StreamStringUnicodeCharacter(uint32 c)
|
||||||
|
{
|
||||||
|
sprintf(&fUnicodeAssemblyBuffer[2], "%04" B_PRIx32, c);
|
||||||
|
// note that the buffer's first two bytes are populated with the JSON
|
||||||
|
// prefix for a unicode char.
|
||||||
|
return StreamStringVerbatim(fUnicodeAssemblyBuffer, 0, 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*! Note that this method will expect a UTF-8 encoded string. */
|
/*! Note that this method will expect a UTF-8 encoded string. */
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
@@ -603,67 +627,43 @@ BJsonTextWriter::StreamStringEncoded(const char* string,
|
|||||||
{
|
{
|
||||||
status_t writeResult = B_OK;
|
status_t writeResult = B_OK;
|
||||||
uint8* string8bit = (uint8*)string;
|
uint8* string8bit = (uint8*)string;
|
||||||
|
off_t i = 0;
|
||||||
|
|
||||||
while (writeResult == B_OK && length != 0) {
|
while (i < length && writeResult == B_OK) {
|
||||||
uint8 c = string8bit[offset];
|
uint8 c = string8bit[offset + i];
|
||||||
const char* simpleEsc = b_json_simple_esc_sequence(c);
|
const char* simpleEsc = b_json_simple_esc_sequence(c);
|
||||||
|
|
||||||
// simple escape sequence involving the backslash + one character.
|
|
||||||
|
|
||||||
if (simpleEsc != NULL) {
|
if (simpleEsc != NULL) {
|
||||||
|
// here the character to emit is something like a tab or a quote
|
||||||
|
// in this case the output JSON should escape it so that it looks
|
||||||
|
// like \t or \n in the output.
|
||||||
writeResult = StreamStringVerbatim(simpleEsc, 0, 2);
|
writeResult = StreamStringVerbatim(simpleEsc, 0, 2);
|
||||||
|
i++;
|
||||||
if (writeResult == B_OK) {
|
|
||||||
offset++;
|
|
||||||
length--;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
if (b_json_is_7bit_clean(c)) {
|
if (b_json_is_7bit_clean(c)) {
|
||||||
|
// in this case the first character is a simple one that can be
|
||||||
// roll forward while the characters are simple and then
|
// output without any special handling. Find the sequence of
|
||||||
// output them at as a block verbatim.
|
// such characters and output them as a sequence so that it's
|
||||||
|
// included as one write operation.
|
||||||
uint32 count7BitClean = 1;
|
size_t l = 1 + b_json_len_7bit_clean_non_esc(
|
||||||
|
&string8bit[offset + i + 1], length - (offset + i + 1));
|
||||||
while (count7BitClean < length
|
writeResult = StreamStringVerbatim(&string[offset + i], 0, l);
|
||||||
&& b_json_is_7bit_clean(
|
i += static_cast<off_t>(l);
|
||||||
string8bit[offset + count7BitClean])) {
|
|
||||||
count7BitClean++;
|
|
||||||
}
|
|
||||||
|
|
||||||
writeResult = StreamStringVerbatim(&string[offset], 0,
|
|
||||||
count7BitClean);
|
|
||||||
|
|
||||||
if (writeResult == B_OK) {
|
|
||||||
offset += count7BitClean;
|
|
||||||
length -= count7BitClean;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if (b_json_is_illegal(c)) {
|
if (b_json_is_illegal(c)) {
|
||||||
fprintf(stderr, "! string encoding error - illegal "
|
fprintf(stderr, "! string encoding error - illegal "
|
||||||
"character [%" B_PRIu32 "]\n", static_cast<uint32>(c));
|
"character [%" B_PRIu32 "]\n", static_cast<uint32>(c));
|
||||||
offset++;
|
i++;
|
||||||
length--;
|
|
||||||
} else {
|
} else {
|
||||||
// if the character is < 128 then it can be rendered
|
// now we have a UTF-8 sequence. Read the UTF-8 sequence
|
||||||
// verbatim - check how many are like this and then
|
// to get the unicode character and then encode that as
|
||||||
// render those verbatim.
|
// JSON.
|
||||||
const char* stringInitial = &string[offset];
|
const char* unicodeStr = &string[offset + i];
|
||||||
uint32 unicodeCharacter = BUnicodeChar::FromUTF8(
|
uint32 unicodeCharacter = BUnicodeChar::FromUTF8(
|
||||||
&stringInitial);
|
&unicodeStr);
|
||||||
|
writeResult = StreamStringUnicodeCharacter(
|
||||||
sprintf(&fUnicodeAssemblyBuffer[2], "%04" B_PRIx32,
|
|
||||||
unicodeCharacter);
|
unicodeCharacter);
|
||||||
writeResult = StreamStringVerbatim(fUnicodeAssemblyBuffer,
|
i += static_cast<off_t>(unicodeStr - &string[offset + i]);
|
||||||
0, 6);
|
|
||||||
|
|
||||||
if (writeResult == B_OK) {
|
|
||||||
uint32 sequence_length
|
|
||||||
= (uint32)(stringInitial - &string[offset]);
|
|
||||||
offset += sequence_length;
|
|
||||||
length -= sequence_length;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -173,6 +173,52 @@ JsonTextWriterTest::TestFalse()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
JsonTextWriterTest::TestStringGeneric(const char *input,
|
||||||
|
const char *expectedOut)
|
||||||
|
{
|
||||||
|
BMallocIO* outputData = new BMallocIO();
|
||||||
|
ObjectDeleter<BMallocIO> outputDataDeleter(outputData);
|
||||||
|
BJsonTextWriter writer(outputData);
|
||||||
|
|
||||||
|
CPPUNIT_ASSERT_EQUAL(B_OK, writer.WriteString(input));
|
||||||
|
writer.Complete();
|
||||||
|
|
||||||
|
BString outputString((char*)outputData->Buffer(),
|
||||||
|
outputData->BufferLength());
|
||||||
|
fprintf(stderr, "expected out >%s<\n", expectedOut);
|
||||||
|
fprintf(stderr, "actual out >%s< (%" B_PRIuSIZE ")\n",
|
||||||
|
outputString.String(), outputData->BufferLength());
|
||||||
|
|
||||||
|
CPPUNIT_ASSERT_EQUAL(BString(expectedOut),
|
||||||
|
outputString);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
JsonTextWriterTest::TestString()
|
||||||
|
{
|
||||||
|
TestStringGeneric(
|
||||||
|
"\"Eichh\xc3\xb6rnchen\"\nsind\nTiere.",
|
||||||
|
"\"\\\"Eichh\\u00f6rnchen\\\"\\nsind\\nTiere.\"");
|
||||||
|
// complex example with unicode, escapes and simple sequences.
|
||||||
|
TestStringGeneric("", "\"\"");
|
||||||
|
TestStringGeneric("Press \"C\" to continue",
|
||||||
|
"\"Press \\\"C\\\" to continue\"");
|
||||||
|
// test of a simple string of one character enclosed with escape
|
||||||
|
// characters to check handling of one character simple sub-sequences.
|
||||||
|
TestStringGeneric("\xc3\xb6", "\"\\u00f6\"");
|
||||||
|
// test of a unicode character on its own.
|
||||||
|
TestStringGeneric("simple", "\"simple\"");
|
||||||
|
// test of a simple string that contains no escapes or anything complex.
|
||||||
|
TestStringGeneric("\t", "\"\\t\"");
|
||||||
|
// test of a single escape character.
|
||||||
|
TestStringGeneric("\007B", "\"B\"");
|
||||||
|
// contains an illegal character which should be ignored.
|
||||||
|
TestStringGeneric("X", "\"X\"");
|
||||||
|
// a simple string with a single character
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*static*/ void
|
/*static*/ void
|
||||||
JsonTextWriterTest::AddTests(BTestSuite& parent)
|
JsonTextWriterTest::AddTests(BTestSuite& parent)
|
||||||
{
|
{
|
||||||
@@ -191,6 +237,10 @@ JsonTextWriterTest::AddTests(BTestSuite& parent)
|
|||||||
"JsonTextWriterTest::TestFalse",
|
"JsonTextWriterTest::TestFalse",
|
||||||
&JsonTextWriterTest::TestFalse));
|
&JsonTextWriterTest::TestFalse));
|
||||||
|
|
||||||
|
suite.addTest(new CppUnit::TestCaller<JsonTextWriterTest>(
|
||||||
|
"JsonTextWriterTest::TestString",
|
||||||
|
&JsonTextWriterTest::TestString));
|
||||||
|
|
||||||
suite.addTest(new CppUnit::TestCaller<JsonTextWriterTest>(
|
suite.addTest(new CppUnit::TestCaller<JsonTextWriterTest>(
|
||||||
"JsonTextWriterTest::TestArrayA",
|
"JsonTextWriterTest::TestArrayA",
|
||||||
&JsonTextWriterTest::TestArrayA));
|
&JsonTextWriterTest::TestArrayA));
|
||||||
|
|||||||
@@ -25,9 +25,12 @@ public:
|
|||||||
void TestDouble();
|
void TestDouble();
|
||||||
void TestInteger();
|
void TestInteger();
|
||||||
void TestFalse();
|
void TestFalse();
|
||||||
|
void TestString();
|
||||||
|
|
||||||
static void AddTests(BTestSuite& suite);
|
static void AddTests(BTestSuite& suite);
|
||||||
private:
|
private:
|
||||||
|
void TestStringGeneric(const char *input,
|
||||||
|
const char *expectedOut);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user