Implementation of JSON Streaming Parser

This change will introduce a streaming parser capability to Haiku.  The
existing functionality of writing the JSON data to a BMessage in-memory
model is retained.  The new parser implements a SAX-style listener based
interface where the listener accepts parse events.  Unit tests have been
supplied for the JSON parser as well.
This commit is contained in:
Andrew Lindesay 2017-05-13 18:50:39 +12:00
parent c72855f6c0
commit 1f6b57a5d2
18 changed files with 4150 additions and 386 deletions

View File

@ -39,6 +39,11 @@ for architectureObject in [ MultiArchSubDirSetup ] {
HashString.cpp
IconButton.cpp
IconView.cpp
JsonWriter.cpp
JsonEventListener.cpp
JsonMessageWriter.cpp
JsonTextWriter.cpp
JsonEvent.cpp
Json.cpp
Keymap.cpp
LongAndDragTrackingFilter.cpp

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,127 @@
/*
* Copyright 2017, Andrew Lindesay <apl@lindesay.co.nz>
* Distributed under the terms of the MIT License.
*/
#include "JsonEvent.h"
#include <stdlib.h>
#include <stdio.h>
#include <String.h>
BJsonEvent::BJsonEvent(json_event_type eventType, const char* content)
:
fEventType(eventType),
fContent(content),
fOwnedContent(NULL)
{
}
BJsonEvent::BJsonEvent(const char* content)
:
fEventType(B_JSON_STRING),
fContent(content),
fOwnedContent(NULL)
{
}
BJsonEvent::BJsonEvent(double content) {
fEventType = B_JSON_NUMBER;
fContent = NULL;
int actualLength = snprintf(0, 0, "%f", content) + 1;
char* buffer = (char*) malloc(sizeof(char) * actualLength);
if (buffer == NULL) {
fprintf(stderr, "memory exhaustion\n");
// given the risk, this is the only sensible thing to do here.
exit(EXIT_FAILURE);
}
sprintf(buffer, "%f", content);
fOwnedContent = buffer;
}
BJsonEvent::BJsonEvent(int64 content) {
fEventType = B_JSON_NUMBER;
fContent = NULL;
fOwnedContent = NULL;
static const char* zeroValue = "0";
static const char* oneValue = "1";
switch (content) {
case 0:
fContent = zeroValue;
break;
case 1:
fContent = oneValue;
break;
default:
{
int actualLength = snprintf(0, 0, "%" B_PRId64, content) + 1;
char* buffer = (char*) malloc(sizeof(char) * actualLength);
if (buffer == NULL) {
fprintf(stderr, "memory exhaustion\n");
// given the risk, this is the only sensible thing to do
// here.
exit(EXIT_FAILURE);
}
sprintf(buffer, "%" B_PRId64, content);
fOwnedContent = buffer;
break;
}
}
}
BJsonEvent::BJsonEvent(json_event_type eventType)
:
fEventType(eventType),
fContent(NULL),
fOwnedContent(NULL)
{
}
BJsonEvent::~BJsonEvent()
{
if (NULL != fOwnedContent)
free(fOwnedContent);
}
json_event_type
BJsonEvent::EventType() const
{
return fEventType;
}
const char*
BJsonEvent::Content() const
{
if (NULL != fOwnedContent)
return fOwnedContent;
return fContent;
}
double
BJsonEvent::ContentDouble() const
{
return strtod(Content(), NULL);
}
int64
BJsonEvent::ContentInteger() const
{
return strtoll(Content(), NULL, 10);
}

View File

@ -0,0 +1,16 @@
/*
* Copyright 2017, Andrew Lindesay <apl@lindesay.co.nz>
* Distributed under the terms of the MIT License.
*/
#include "JsonEventListener.h"
BJsonEventListener::BJsonEventListener()
{
}
BJsonEventListener::~BJsonEventListener()
{
}

View File

@ -0,0 +1,520 @@
/*
* Copyright 2017, Andrew Lindesay <apl@lindesay.co.nz>
* Distributed under the terms of the MIT License.
*/
#include "JsonMessageWriter.h"
#include <stdio.h>
namespace BPrivate {
/*! The class and sub-classes of it are used as a stack internal to the
BJsonMessageWriter class. As the JSON is parsed, the stack of these
internal listeners follows the stack of the JSON parsing in terms of
containers; arrays and objects.
*/
class BStackedMessageEventListener : public BJsonEventListener {
public:
BStackedMessageEventListener(
BJsonMessageWriter* writer,
BStackedMessageEventListener* parent,
uint32 messageWhat);
BStackedMessageEventListener(
BJsonMessageWriter* writer,
BStackedMessageEventListener* parent,
BMessage* message);
~BStackedMessageEventListener();
bool Handle(const BJsonEvent& event);
void HandleError(status_t status, int32 line,
const char* message);
void Complete();
void AddMessage(BMessage* value);
status_t ErrorStatus();
virtual const char* NextItemName() = 0;
BStackedMessageEventListener*
Parent();
protected:
void AddBool(bool value);
void AddNull();
void AddDouble(double value);
void AddString(const char* value);
virtual bool WillAdd();
virtual void DidAdd();
void SetStackedListenerOnWriter(
BStackedMessageEventListener*
stackedListener);
BJsonMessageWriter* fWriter;
bool fOwnsMessage;
BStackedMessageEventListener
*fParent;
BMessage* fMessage;
};
class BStackedArrayMessageEventListener : public BStackedMessageEventListener {
public:
BStackedArrayMessageEventListener(
BJsonMessageWriter* writer,
BStackedMessageEventListener* parent);
BStackedArrayMessageEventListener(
BJsonMessageWriter* writer,
BStackedMessageEventListener* parent,
BMessage* message);
~BStackedArrayMessageEventListener();
bool Handle(const BJsonEvent& event);
const char* NextItemName();
protected:
void DidAdd();
private:
uint32 fCount;
BString fNextItemName;
};
class BStackedObjectMessageEventListener : public BStackedMessageEventListener {
public:
BStackedObjectMessageEventListener(
BJsonMessageWriter* writer,
BStackedMessageEventListener* parent);
BStackedObjectMessageEventListener(
BJsonMessageWriter* writer,
BStackedMessageEventListener* parent,
BMessage* message);
~BStackedObjectMessageEventListener();
bool Handle(const BJsonEvent& event);
const char* NextItemName();
protected:
bool WillAdd();
void DidAdd();
private:
BString fNextItemName;
};
} // namespace BPrivate
using BPrivate::BStackedMessageEventListener;
using BPrivate::BStackedArrayMessageEventListener;
using BPrivate::BStackedObjectMessageEventListener;
// #pragma mark - BStackedMessageEventListener
BStackedMessageEventListener::BStackedMessageEventListener(
BJsonMessageWriter* writer,
BStackedMessageEventListener* parent,
uint32 messageWhat)
{
fWriter = writer;
fParent = parent;
fOwnsMessage = true;
fMessage = new BMessage(messageWhat);
}
BStackedMessageEventListener::BStackedMessageEventListener(
BJsonMessageWriter* writer,
BStackedMessageEventListener* parent,
BMessage* message)
{
fWriter = writer;
fParent = parent;
fOwnsMessage = false;
fMessage = message;
}
BStackedMessageEventListener::~BStackedMessageEventListener()
{
if (fOwnsMessage)
delete fMessage;
}
bool
BStackedMessageEventListener::Handle(const BJsonEvent& event)
{
if (fWriter->ErrorStatus() != B_OK)
return false;
switch (event.EventType()) {
case B_JSON_NUMBER:
AddDouble(event.ContentDouble());
break;
case B_JSON_STRING:
AddString(event.Content());
break;
case B_JSON_TRUE:
AddBool(true);
break;
case B_JSON_FALSE:
AddBool(false);
break;
case B_JSON_NULL:
AddNull();
break;
case B_JSON_OBJECT_START:
SetStackedListenerOnWriter(new BStackedObjectMessageEventListener(
fWriter, this));
break;
case B_JSON_ARRAY_START:
SetStackedListenerOnWriter(new BStackedArrayMessageEventListener(
fWriter, this));
break;
default:
HandleError(B_NOT_ALLOWED, JSON_EVENT_LISTENER_ANY_LINE,
"unexpected type of json item to add to container");
return false;
}
return ErrorStatus() == B_OK;
}
void
BStackedMessageEventListener::HandleError(status_t status, int32 line,
const char* message)
{
fWriter->HandleError(status, line, message);
}
void
BStackedMessageEventListener::Complete()
{
// illegal state.
HandleError(JSON_EVENT_LISTENER_ANY_LINE, B_NOT_ALLOWED,
"Complete() called on stacked message listener");
}
void
BStackedMessageEventListener::AddMessage(BMessage* message)
{
if (WillAdd()) {
fMessage->AddMessage(NextItemName(), message);
DidAdd();
}
}
status_t
BStackedMessageEventListener::ErrorStatus()
{
return fWriter->ErrorStatus();
}
BStackedMessageEventListener*
BStackedMessageEventListener::Parent()
{
return fParent;
}
void
BStackedMessageEventListener::AddBool(bool value)
{
if (WillAdd()) {
fMessage->AddBool(NextItemName(), value);
DidAdd();
}
}
void
BStackedMessageEventListener::AddNull()
{
if (WillAdd()) {
fMessage->AddPointer(NextItemName(), (void*)NULL);
DidAdd();
}
}
void
BStackedMessageEventListener::AddDouble(double value)
{
if (WillAdd()) {
fMessage->AddDouble(NextItemName(), value);
DidAdd();
}
}
void
BStackedMessageEventListener::AddString(const char* value)
{
if (WillAdd()) {
fMessage->AddString(NextItemName(), value);
DidAdd();
}
}
bool
BStackedMessageEventListener::WillAdd()
{
return true;
}
void
BStackedMessageEventListener::DidAdd()
{
// noop - present for overriding
}
void
BStackedMessageEventListener::SetStackedListenerOnWriter(
BStackedMessageEventListener* stackedListener)
{
fWriter->SetStackedListener(stackedListener);
}
// #pragma mark - BStackedArrayMessageEventListener
BStackedArrayMessageEventListener::BStackedArrayMessageEventListener(
BJsonMessageWriter* writer,
BStackedMessageEventListener* parent)
: BStackedMessageEventListener(writer, parent, B_JSON_MESSAGE_WHAT_ARRAY)
{
fCount = 0;
}
BStackedArrayMessageEventListener::BStackedArrayMessageEventListener(
BJsonMessageWriter* writer,
BStackedMessageEventListener* parent,
BMessage* message)
: BStackedMessageEventListener(writer, parent, message)
{
message->what = B_JSON_MESSAGE_WHAT_ARRAY;
fCount = 0;
}
BStackedArrayMessageEventListener::~BStackedArrayMessageEventListener()
{
}
bool
BStackedArrayMessageEventListener::Handle(const BJsonEvent& event)
{
if (fWriter->ErrorStatus() != B_OK)
return false;
switch (event.EventType()) {
case B_JSON_ARRAY_END:
if (fParent != NULL)
fParent->AddMessage(fMessage);
SetStackedListenerOnWriter(fParent);
delete this;
break;
default:
return BStackedMessageEventListener::Handle(event);
}
return true;
}
const char*
BStackedArrayMessageEventListener::NextItemName()
{
fNextItemName.SetToFormat("%" B_PRIu32, fCount);
return fNextItemName.String();
}
void
BStackedArrayMessageEventListener::DidAdd()
{
BStackedMessageEventListener::DidAdd();
fCount++;
}
// #pragma mark - BStackedObjectMessageEventListener
BStackedObjectMessageEventListener::BStackedObjectMessageEventListener(
BJsonMessageWriter* writer,
BStackedMessageEventListener* parent)
: BStackedMessageEventListener(writer, parent, B_JSON_MESSAGE_WHAT_OBJECT)
{
}
BStackedObjectMessageEventListener::BStackedObjectMessageEventListener(
BJsonMessageWriter* writer,
BStackedMessageEventListener* parent,
BMessage* message)
: BStackedMessageEventListener(writer, parent, message)
{
message->what = B_JSON_MESSAGE_WHAT_OBJECT;
}
BStackedObjectMessageEventListener::~BStackedObjectMessageEventListener()
{
}
bool
BStackedObjectMessageEventListener::Handle(const BJsonEvent& event)
{
if (fWriter->ErrorStatus() != B_OK)
return false;
switch (event.EventType()) {
case B_JSON_OBJECT_END:
if (fParent != NULL)
fParent->AddMessage(fMessage);
SetStackedListenerOnWriter(fParent);
delete this;
break;
case B_JSON_OBJECT_NAME:
fNextItemName.SetTo(event.Content());
break;
default:
return BStackedMessageEventListener::Handle(event);
}
return true;
}
const char*
BStackedObjectMessageEventListener::NextItemName()
{
return fNextItemName.String();
}
bool
BStackedObjectMessageEventListener::WillAdd()
{
if (0 == fNextItemName.Length()) {
HandleError(B_NOT_ALLOWED, JSON_EVENT_LISTENER_ANY_LINE,
"missing name for adding value into an object");
return false;
}
return true;
}
void
BStackedObjectMessageEventListener::DidAdd()
{
BStackedMessageEventListener::DidAdd();
fNextItemName.SetTo("", 0);
}
// #pragma mark - BJsonMessageWriter
BJsonMessageWriter::BJsonMessageWriter(BMessage& message)
{
fTopLevelMessage = &message;
fStackedListener = NULL;
}
BJsonMessageWriter::~BJsonMessageWriter()
{
BStackedMessageEventListener* listener = fStackedListener;
while (listener != NULL) {
BStackedMessageEventListener* nextListener = listener->Parent();
delete listener;
listener = nextListener;
}
fStackedListener = NULL;
}
bool
BJsonMessageWriter::Handle(const BJsonEvent& event)
{
if (fErrorStatus != B_OK)
return false;
if (fStackedListener != NULL)
return fStackedListener->Handle(event);
else {
switch(event.EventType()) {
case B_JSON_OBJECT_START:
SetStackedListener(new BStackedObjectMessageEventListener(
this, NULL, fTopLevelMessage));
break;
case B_JSON_ARRAY_START:
fTopLevelMessage->what = B_JSON_MESSAGE_WHAT_ARRAY;
SetStackedListener(new BStackedArrayMessageEventListener(
this, NULL, fTopLevelMessage));
break;
default:
HandleError(B_NOT_ALLOWED, JSON_EVENT_LISTENER_ANY_LINE,
"a message object can only handle an object or an array"
"at the top level");
return false;
}
}
return true; // keep going
}
void
BJsonMessageWriter::Complete()
{
if (fStackedListener != NULL) {
HandleError(B_BAD_DATA, JSON_EVENT_LISTENER_ANY_LINE,
"unexpected end of input data processing structure");
}
}
void
BJsonMessageWriter::SetStackedListener(
BStackedMessageEventListener* listener)
{
fStackedListener = listener;
}

View File

@ -0,0 +1,697 @@
/*
* Copyright 2017, Andrew Lindesay <apl@lindesay.co.nz>
* Distributed under the terms of the MIT License.
*/
#include "JsonTextWriter.h"
#include <stdio.h>
#include <stdlib.h>
#include <UnicodeChar.h>
namespace BPrivate {
static bool
b_json_is_7bit_clean(uint8 c)
{
return c >= 0x20 && c < 0x7f;
}
static bool
b_json_is_illegal(uint8 c) {
return c < 0x20 || c == 0x7f;
}
static const char*
b_json_simple_esc_sequence(char c)
{
switch (c) {
case '"':
return "\\\"";
case '\\':
return "\\\\";
case '/':
return "\\/";
case '\b':
return "\\b";
case '\f':
return "\\f";
case '\n':
return "\\n";
case '\r':
return "\\r";
case '\t':
return "\\t";
default:
return NULL;
}
}
/*! 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
internal listeners follows the stack of the JSON parsing in terms of
containers; arrays and objects.
*/
class BJsonTextWriterStackedEventListener : public BJsonEventListener {
public:
BJsonTextWriterStackedEventListener(
BJsonTextWriter* writer,
BJsonTextWriterStackedEventListener* parent);
~BJsonTextWriterStackedEventListener();
bool Handle(const BJsonEvent& event);
void HandleError(status_t status, int32 line,
const char* message);
void Complete();
status_t ErrorStatus();
BJsonTextWriterStackedEventListener*
Parent();
protected:
status_t StreamNumberNode(const BJsonEvent& event);
status_t StreamStringVerbatim(const char* string);
status_t StreamStringVerbatim(const char* string,
off_t offset, size_t length);
status_t StreamStringEncoded(const char* string);
status_t StreamStringEncoded(const char* string,
off_t offset, size_t length);
status_t StreamQuotedEncodedString(const char* string);
status_t StreamQuotedEncodedString(const char* string,
off_t offset, size_t length);
status_t StreamChar(char c);
virtual bool WillAdd();
virtual void DidAdd();
void SetStackedListenerOnWriter(
BJsonTextWriterStackedEventListener*
stackedListener);
BJsonTextWriter*
fWriter;
BJsonTextWriterStackedEventListener*
fParent;
uint32 fCount;
};
class BJsonTextWriterArrayStackedEventListener
: public BJsonTextWriterStackedEventListener {
public:
BJsonTextWriterArrayStackedEventListener(
BJsonTextWriter* writer,
BJsonTextWriterStackedEventListener* parent);
~BJsonTextWriterArrayStackedEventListener();
bool Handle(const BJsonEvent& event);
protected:
bool WillAdd();
};
class BJsonTextWriterObjectStackedEventListener
: public BJsonTextWriterStackedEventListener {
public:
BJsonTextWriterObjectStackedEventListener(
BJsonTextWriter* writer,
BJsonTextWriterStackedEventListener* parent);
~BJsonTextWriterObjectStackedEventListener();
bool Handle(const BJsonEvent& event);
};
} // namespace BPrivate
using BPrivate::BJsonTextWriterStackedEventListener;
using BPrivate::BJsonTextWriterArrayStackedEventListener;
using BPrivate::BJsonTextWriterObjectStackedEventListener;
// #pragma mark - BJsonTextWriterStackedEventListener
BJsonTextWriterStackedEventListener::BJsonTextWriterStackedEventListener(
BJsonTextWriter* writer,
BJsonTextWriterStackedEventListener* parent)
{
fWriter = writer;
fParent = parent;
fCount = 0;
}
BJsonTextWriterStackedEventListener::~BJsonTextWriterStackedEventListener()
{
}
bool
BJsonTextWriterStackedEventListener::Handle(const BJsonEvent& event)
{
status_t writeResult = B_OK;
if (fWriter->ErrorStatus() != B_OK)
return false;
switch (event.EventType()) {
case B_JSON_NUMBER:
case B_JSON_STRING:
case B_JSON_TRUE:
case B_JSON_FALSE:
case B_JSON_NULL:
case B_JSON_OBJECT_START:
case B_JSON_ARRAY_START:
if (!WillAdd())
return false;
break;
default:
break;
}
switch (event.EventType()) {
case B_JSON_NUMBER:
writeResult = StreamNumberNode(event);
break;
case B_JSON_STRING:
writeResult = StreamQuotedEncodedString(event.Content());
break;
case B_JSON_TRUE:
writeResult = StreamStringVerbatim("true", 0, 4);
break;
case B_JSON_FALSE:
writeResult = StreamStringVerbatim("false", 0, 5);
break;
case B_JSON_NULL:
writeResult = StreamStringVerbatim("null", 0, 4);
break;
case B_JSON_OBJECT_START:
writeResult = StreamChar('{');
if (writeResult == B_OK) {
SetStackedListenerOnWriter(
new BJsonTextWriterObjectStackedEventListener(fWriter, this));
}
break;
case B_JSON_ARRAY_START:
writeResult = StreamChar('[');
if (writeResult == B_OK) {
SetStackedListenerOnWriter(
new BJsonTextWriterArrayStackedEventListener(fWriter, this));
}
break;
default:
HandleError(B_NOT_ALLOWED, JSON_EVENT_LISTENER_ANY_LINE,
"unexpected type of json item to add to container");
return false;
}
if (writeResult == B_OK)
DidAdd();
else {
HandleError(writeResult, JSON_EVENT_LISTENER_ANY_LINE,
"error writing output");
}
return ErrorStatus() == B_OK;
}
void
BJsonTextWriterStackedEventListener::HandleError(status_t status, int32 line,
const char* message)
{
fWriter->HandleError(status, line, message);
}
void
BJsonTextWriterStackedEventListener::Complete()
{
// illegal state.
HandleError(JSON_EVENT_LISTENER_ANY_LINE, B_NOT_ALLOWED,
"Complete() called on stacked message listener");
}
status_t
BJsonTextWriterStackedEventListener::ErrorStatus()
{
return fWriter->ErrorStatus();
}
BJsonTextWriterStackedEventListener*
BJsonTextWriterStackedEventListener::Parent()
{
return fParent;
}
status_t
BJsonTextWriterStackedEventListener::StreamNumberNode(const BJsonEvent& event)
{
return fWriter->StreamNumberNode(event);
}
status_t
BJsonTextWriterStackedEventListener::StreamStringVerbatim(const char* string)
{
return fWriter->StreamStringVerbatim(string);
}
status_t
BJsonTextWriterStackedEventListener::StreamStringVerbatim(const char* string,
off_t offset, size_t length)
{
return fWriter->StreamStringVerbatim(string, offset, length);
}
status_t
BJsonTextWriterStackedEventListener::StreamStringEncoded(const char* string)
{
return fWriter->StreamStringEncoded(string);
}
status_t
BJsonTextWriterStackedEventListener::StreamStringEncoded(const char* string,
off_t offset, size_t length)
{
return fWriter->StreamStringEncoded(string, offset, length);
}
status_t
BJsonTextWriterStackedEventListener::StreamQuotedEncodedString(const char* string)
{
return fWriter->StreamQuotedEncodedString(string);
}
status_t
BJsonTextWriterStackedEventListener::StreamQuotedEncodedString(const char* string,
off_t offset, size_t length)
{
return fWriter->StreamQuotedEncodedString(string, offset, length);
}
status_t
BJsonTextWriterStackedEventListener::StreamChar(char c)
{
return fWriter->StreamChar(c);
}
bool
BJsonTextWriterStackedEventListener::WillAdd()
{
return true; // carry on
}
void
BJsonTextWriterStackedEventListener::DidAdd()
{
fCount++;
}
void
BJsonTextWriterStackedEventListener::SetStackedListenerOnWriter(
BJsonTextWriterStackedEventListener* stackedListener)
{
fWriter->SetStackedListener(stackedListener);
}
// #pragma mark - BJsonTextWriterArrayStackedEventListener
BJsonTextWriterArrayStackedEventListener::BJsonTextWriterArrayStackedEventListener(
BJsonTextWriter* writer,
BJsonTextWriterStackedEventListener* parent)
: BJsonTextWriterStackedEventListener(writer, parent)
{
}
BJsonTextWriterArrayStackedEventListener
::~BJsonTextWriterArrayStackedEventListener()
{
}
bool
BJsonTextWriterArrayStackedEventListener::Handle(const BJsonEvent& event)
{
status_t writeResult = B_OK;
if (fWriter->ErrorStatus() != B_OK)
return false;
switch (event.EventType()) {
case B_JSON_ARRAY_END:
{
writeResult = StreamChar(']');
if (writeResult == B_OK) {
SetStackedListenerOnWriter(fParent);
delete this;
return true; // must exit immediately after delete this.
}
break;
}
default:
return BJsonTextWriterStackedEventListener::Handle(event);
}
if(writeResult != B_OK) {
HandleError(writeResult, JSON_EVENT_LISTENER_ANY_LINE,
"error writing output");
}
return ErrorStatus() == B_OK;
}
bool
BJsonTextWriterArrayStackedEventListener::WillAdd()
{
status_t writeResult = B_OK;
if (writeResult == B_OK && fCount > 0)
writeResult = StreamChar(',');
if (writeResult != B_OK) {
HandleError(B_IO_ERROR, JSON_EVENT_LISTENER_ANY_LINE,
"error writing data");
return false;
}
return BJsonTextWriterStackedEventListener::WillAdd();
}
// #pragma mark - BJsonTextWriterObjectStackedEventListener
BJsonTextWriterObjectStackedEventListener::BJsonTextWriterObjectStackedEventListener(
BJsonTextWriter* writer,
BJsonTextWriterStackedEventListener* parent)
: BJsonTextWriterStackedEventListener(writer, parent)
{
}
BJsonTextWriterObjectStackedEventListener
::~BJsonTextWriterObjectStackedEventListener()
{
}
bool
BJsonTextWriterObjectStackedEventListener::Handle(const BJsonEvent& event)
{
status_t writeResult = B_OK;
if (fWriter->ErrorStatus() != B_OK)
return false;
switch (event.EventType()) {
case B_JSON_OBJECT_END:
{
writeResult = StreamChar('}');
if (writeResult == B_OK) {
SetStackedListenerOnWriter(fParent);
delete this;
return true; // just exit after delete this.
}
break;
}
case B_JSON_OBJECT_NAME:
{
if (writeResult == B_OK && fCount > 0)
writeResult = StreamChar(',');
if (writeResult == B_OK)
writeResult = StreamQuotedEncodedString(event.Content());
if (writeResult == B_OK)
writeResult = StreamChar(':');
break;
}
default:
return BJsonTextWriterStackedEventListener::Handle(event);
}
if (writeResult != B_OK) {
HandleError(writeResult, JSON_EVENT_LISTENER_ANY_LINE,
"error writing data");
}
return ErrorStatus() == B_OK;
}
// #pragma mark - BJsonTextWriter
BJsonTextWriter::BJsonTextWriter(
BDataIO* dataIO)
:
fDataIO(dataIO)
{
// this is a preparation for this buffer to easily be used later
// to efficiently output encoded unicode characters.
fUnicodeAssemblyBuffer[0] = '\\';
fUnicodeAssemblyBuffer[1] = 'u';
fStackedListener = new BJsonTextWriterStackedEventListener(this, NULL);
}
BJsonTextWriter::~BJsonTextWriter()
{
BJsonTextWriterStackedEventListener* listener = fStackedListener;
while (listener != NULL) {
BJsonTextWriterStackedEventListener* nextListener = listener->Parent();
delete listener;
listener = nextListener;
}
fStackedListener = NULL;
}
bool
BJsonTextWriter::Handle(const BJsonEvent& event)
{
return fStackedListener->Handle(event);
}
void
BJsonTextWriter::Complete()
{
// upon construction, this object will add one listener to the
// stack. On complete, this listener should still be there;
// otherwise this implies an unterminated structure such as array
// / object.
if (fStackedListener->Parent() != NULL) {
HandleError(B_BAD_DATA, JSON_EVENT_LISTENER_ANY_LINE,
"unexpected end of input data");
}
}
void
BJsonTextWriter::SetStackedListener(
BJsonTextWriterStackedEventListener* stackedListener)
{
fStackedListener = stackedListener;
}
status_t
BJsonTextWriter::StreamNumberNode(const BJsonEvent& event)
{
return StreamStringVerbatim(event.Content());
}
status_t
BJsonTextWriter::StreamStringVerbatim(const char* string)
{
return StreamStringVerbatim(string, 0, strlen(string));
}
status_t
BJsonTextWriter::StreamStringVerbatim(const char* string,
off_t offset, size_t length)
{
return fDataIO->WriteExactly(&string[offset], length);
}
status_t
BJsonTextWriter::StreamStringEncoded(const char* string)
{
return StreamStringEncoded(string, 0, strlen(string));
}
/*! Note that this method will expect a UTF-8 encoded string. */
status_t
BJsonTextWriter::StreamStringEncoded(const char* string,
off_t offset, size_t length)
{
status_t writeResult = B_OK;
uint8* string8bit = (uint8*)string;
while (writeResult == B_OK && 0 != length) {
uint8 c = string8bit[offset];
const char* simpleEsc = b_json_simple_esc_sequence(c);
// simple escape sequence involving the backslash + one character.
if (simpleEsc != NULL) {
writeResult = StreamStringVerbatim(simpleEsc, 0, 2);
if (writeResult == B_OK) {
offset++;
length--;
}
} else {
if (b_json_is_7bit_clean(c)) {
// roll forward while the characters are simple and then
// output them at as a block verbatim.
uint32 count7BitClean = 1;
while (count7BitClean < length
&& b_json_is_7bit_clean(
string8bit[offset + count7BitClean])) {
count7BitClean++;
}
writeResult = StreamStringVerbatim(&string[offset], 0,
count7BitClean);
if (writeResult == B_OK) {
offset += count7BitClean;
length -= count7BitClean;
}
} else {
if (b_json_is_illegal(c)) {
fprintf(stderr, "! string encoding error - illegal "
"character [%" B_PRIu32 "]\n", static_cast<uint32>(c));
offset++;
length--;
} else {
// if the character is < 128 then it can be rendered
// verbatim - check how many are like this and then
// render those verbatim.
const char* stringInitial = &string[offset];
uint32 unicodeCharacter = BUnicodeChar::FromUTF8(
&stringInitial);
sprintf(&fUnicodeAssemblyBuffer[2], "%04" B_PRIx32,
unicodeCharacter);
writeResult = StreamStringVerbatim(fUnicodeAssemblyBuffer,
0, 6);
if (writeResult == B_OK) {
uint32 sequence_length
= (uint32)(stringInitial - &string[offset]);
offset += sequence_length;
length -= sequence_length;
}
}
}
}
}
return writeResult;
}
status_t
BJsonTextWriter::StreamQuotedEncodedString(const char* string)
{
return StreamQuotedEncodedString(string, 0, strlen(string));
}
status_t
BJsonTextWriter::StreamQuotedEncodedString(const char* string,
off_t offset, size_t length)
{
status_t write_result = B_OK;
if (write_result == B_OK)
write_result = StreamChar('\"');
if (write_result == B_OK)
write_result = StreamStringEncoded(string, offset, length);
if (write_result == B_OK)
write_result = StreamChar('\"');
return write_result;
}
status_t
BJsonTextWriter::StreamChar(char c)
{
return fDataIO->WriteExactly(&c, 1);
}

View File

@ -0,0 +1,144 @@
/*
* Copyright 2017, Andrew Lindesay <apl@lindesay.co.nz>
* Distributed under the terms of the MIT License.
*/
#include "JsonWriter.h"
#include <stdio.h>
#include <stdlib.h>
#include <UnicodeChar.h>
BJsonWriter::BJsonWriter()
:
fErrorStatus(B_OK)
{
}
BJsonWriter::~BJsonWriter()
{
}
void
BJsonWriter::HandleError(status_t status, int32 line,
const char* message)
{
if(fErrorStatus == B_OK) {
if (message == NULL)
message = "?";
fErrorStatus = status;
fprintf(stderr, "! json err @line %" B_PRIi32 " - %s : %s\n", line,
strerror(status), message);
}
}
status_t
BJsonWriter::ErrorStatus()
{
return fErrorStatus;
}
status_t
BJsonWriter::WriteBoolean(bool value)
{
if (value)
return WriteTrue();
return WriteFalse();
}
status_t
BJsonWriter::WriteTrue()
{
Handle(BJsonEvent(B_JSON_TRUE));
return fErrorStatus;
}
status_t
BJsonWriter::WriteFalse()
{
Handle(BJsonEvent(B_JSON_FALSE));
return fErrorStatus;
}
status_t
BJsonWriter::WriteNull()
{
Handle(BJsonEvent(B_JSON_NULL));
return fErrorStatus;
}
status_t
BJsonWriter::WriteInteger(int64 value)
{
Handle(BJsonEvent(value));
return fErrorStatus;
}
status_t
BJsonWriter::WriteDouble(double value)
{
Handle(BJsonEvent(value));
return fErrorStatus;
}
status_t
BJsonWriter::WriteString(const char* value)
{
Handle(BJsonEvent(value));
return fErrorStatus;
}
status_t
BJsonWriter::WriteObjectStart()
{
Handle(BJsonEvent(B_JSON_OBJECT_START));
return fErrorStatus;
}
status_t
BJsonWriter::WriteObjectName(const char* value)
{
Handle(BJsonEvent(B_JSON_OBJECT_NAME, value));
return fErrorStatus;
}
status_t
BJsonWriter::WriteObjectEnd()
{
Handle(BJsonEvent(B_JSON_OBJECT_END));
return fErrorStatus;
}
status_t
BJsonWriter::WriteArrayStart()
{
Handle(BJsonEvent(B_JSON_ARRAY_START));
return fErrorStatus;
}
status_t
BJsonWriter::WriteArrayEnd()
{
Handle(BJsonEvent(B_JSON_ARRAY_END));
return fErrorStatus;
}

View File

@ -10,6 +10,10 @@ UnitTestLib libsharedtest.so :
CalendarViewTest.cpp
DriverSettingsMessageAdapterTest.cpp
JsonEndToEndTest.cpp
JsonErrorHandlingTest.cpp
JsonTextWriterTest.cpp
JsonToMessageTest.cpp
GeolocationTest.cpp
NaturalCompareTest.cpp

View File

@ -0,0 +1,215 @@
/*
* Copyright 2017, Andrew Lindesay <apl@lindesay.co.nz>
* Distributed under the terms of the MIT License.
*/
#include "JsonEndToEndTest.h"
#include <AutoDeleter.h>
#include <Json.h>
#include <JsonTextWriter.h>
#include <cppunit/TestCaller.h>
#include <cppunit/TestSuite.h>
#include "JsonSamples.h"
using namespace BPrivate;
JsonEndToEndTest::JsonEndToEndTest()
{
}
JsonEndToEndTest::~JsonEndToEndTest()
{
}
void
JsonEndToEndTest::TestParseAndWrite(char* input, char* expectedOutput)
{
BDataIO* inputData = new BMemoryIO(input, strlen(input));
ObjectDeleter<BDataIO> inputDataDeleter(inputData);
BMallocIO* outputData = new BMallocIO();
ObjectDeleter<BMallocIO> outputDataDeleter(outputData);
BPrivate::BJsonTextWriter* listener
= new BJsonTextWriter(outputData);
ObjectDeleter<BPrivate::BJsonTextWriter> listenerDeleter(listener);
// ----------------------
BPrivate::BJson::Parse(inputData, listener);
// ----------------------
CPPUNIT_ASSERT_EQUAL(B_OK, listener->ErrorStatus());
fprintf(stderr, "in >%s<\n", input);
fprintf(stderr, "expected out >%s<\n", expectedOutput);
fprintf(stderr, "actual out >%s<\n", (char*)outputData->Buffer());
CPPUNIT_ASSERT_MESSAGE("expected did no equal actual output",
0 == strncmp(expectedOutput, (char*)outputData->Buffer(),
strlen(expectedOutput)));
}
void
JsonEndToEndTest::TestNullA()
{
TestParseAndWrite(JSON_SAMPLE_NULL_A_IN,
JSON_SAMPLE_NULL_A_EXPECTED_OUT);
}
void
JsonEndToEndTest::TestTrueA()
{
TestParseAndWrite(JSON_SAMPLE_TRUE_A_IN,
JSON_SAMPLE_TRUE_A_EXPECTED_OUT);
}
void
JsonEndToEndTest::TestFalseA()
{
TestParseAndWrite(JSON_SAMPLE_FALSE_A_IN,
JSON_SAMPLE_FALSE_A_EXPECTED_OUT);
}
void
JsonEndToEndTest::TestNumberA()
{
TestParseAndWrite(JSON_SAMPLE_NUMBER_A_IN,
JSON_SAMPLE_NUMBER_A_EXPECTED_OUT);
}
void
JsonEndToEndTest::TestStringA()
{
TestParseAndWrite(JSON_SAMPLE_STRING_A_IN,
JSON_SAMPLE_STRING_A_EXPECTED_OUT);
}
void
JsonEndToEndTest::TestStringB()
{
TestParseAndWrite(JSON_SAMPLE_STRING_B_IN,
JSON_SAMPLE_STRING_B_EXPECTED_OUT);
}
/* In this test, there are some UTF-8 characters. */
void
JsonEndToEndTest::TestStringA2()
{
TestParseAndWrite(JSON_SAMPLE_STRING_A2_IN,
JSON_SAMPLE_STRING_A_EXPECTED_OUT);
}
void
JsonEndToEndTest::TestArrayA()
{
TestParseAndWrite(JSON_SAMPLE_ARRAY_A_IN, JSON_SAMPLE_ARRAY_A_EXPECTED_OUT);
}
void
JsonEndToEndTest::TestArrayB()
{
TestParseAndWrite(JSON_SAMPLE_ARRAY_B_IN, JSON_SAMPLE_ARRAY_B_EXPECTED_OUT);
}
void
JsonEndToEndTest::TestObjectA()
{
TestParseAndWrite(JSON_SAMPLE_OBJECT_A_IN,
JSON_SAMPLE_OBJECT_A_EXPECTED_OUT);
}
/*! This method will test an element being unterminated; such an object that
is missing the terminating "}" symbol or a string that has no closing
quote. This is tested here because the writer
*/
void
JsonEndToEndTest::TestUnterminated(const char *input)
{
BDataIO* inputData = new BMemoryIO(input, strlen(input));
ObjectDeleter<BDataIO> inputDataDeleter(inputData);
BMallocIO* outputData = new BMallocIO();
ObjectDeleter<BMallocIO> outputDataDeleter(outputData);
BPrivate::BJsonTextWriter* listener
= new BJsonTextWriter(outputData);
ObjectDeleter<BPrivate::BJsonTextWriter> listenerDeleter(listener);
// ----------------------
BPrivate::BJson::Parse(inputData, listener);
// ----------------------
CPPUNIT_ASSERT_EQUAL(B_BAD_DATA, listener->ErrorStatus());
}
void
JsonEndToEndTest::TestStringUnterminated()
{
TestUnterminated(JSON_SAMPLE_BROKEN_UNTERMINATED_STRING);
}
void
JsonEndToEndTest::TestArrayUnterminated()
{
TestUnterminated(JSON_SAMPLE_BROKEN_UNTERMINATED_ARRAY);
}
void
JsonEndToEndTest::TestObjectUnterminated()
{
TestUnterminated(JSON_SAMPLE_BROKEN_UNTERMINATED_OBJECT);
}
/*static*/ void
JsonEndToEndTest::AddTests(BTestSuite& parent)
{
CppUnit::TestSuite& suite = *new CppUnit::TestSuite("JsonEndToEndTest");
suite.addTest(new CppUnit::TestCaller<JsonEndToEndTest>(
"JsonEndToEndTest::TestNullA", &JsonEndToEndTest::TestNullA));
suite.addTest(new CppUnit::TestCaller<JsonEndToEndTest>(
"JsonEndToEndTest::TestTrueA", &JsonEndToEndTest::TestTrueA));
suite.addTest(new CppUnit::TestCaller<JsonEndToEndTest>(
"JsonEndToEndTest::TestFalseA", &JsonEndToEndTest::TestFalseA));
suite.addTest(new CppUnit::TestCaller<JsonEndToEndTest>(
"JsonEndToEndTest::TestNumberA", &JsonEndToEndTest::TestNumberA));
suite.addTest(new CppUnit::TestCaller<JsonEndToEndTest>(
"JsonEndToEndTest::TestStringA", &JsonEndToEndTest::TestStringA));
suite.addTest(new CppUnit::TestCaller<JsonEndToEndTest>(
"JsonEndToEndTest::TestStringA2", &JsonEndToEndTest::TestStringA2));
suite.addTest(new CppUnit::TestCaller<JsonEndToEndTest>(
"JsonEndToEndTest::TestStringB", &JsonEndToEndTest::TestStringB));
suite.addTest(new CppUnit::TestCaller<JsonEndToEndTest>(
"JsonEndToEndTest::TestArrayA", &JsonEndToEndTest::TestArrayA));
suite.addTest(new CppUnit::TestCaller<JsonEndToEndTest>(
"JsonEndToEndTest::TestArrayB", &JsonEndToEndTest::TestArrayB));
suite.addTest(new CppUnit::TestCaller<JsonEndToEndTest>(
"JsonEndToEndTest::TestObjectA", &JsonEndToEndTest::TestObjectA));
suite.addTest(new CppUnit::TestCaller<JsonEndToEndTest>(
"JsonEndToEndTest::TestStringUnterminated",
&JsonEndToEndTest::TestStringUnterminated));
suite.addTest(new CppUnit::TestCaller<JsonEndToEndTest>(
"JsonEndToEndTest::TestArrayUnterminated",
&JsonEndToEndTest::TestArrayUnterminated));
suite.addTest(new CppUnit::TestCaller<JsonEndToEndTest>(
"JsonEndToEndTest::TestObjectUnterminated",
&JsonEndToEndTest::TestObjectUnterminated));
parent.addTest("JsonEndToEndTest", &suite);
}

View File

@ -0,0 +1,45 @@
/*
* Copyright 2017, Andrew Lindesay <apl@lindesay.co.nz>
* Distributed under the terms of the MIT License.
*/
#ifndef JSON_END_TO_END_TEST_H
#define JSON_END_TO_END_TEST_H
#include <TestCase.h>
#include <TestSuite.h>
class Sample;
class JsonEndToEndTest : public CppUnit::TestCase {
public:
JsonEndToEndTest();
virtual ~JsonEndToEndTest();
void TestNullA();
void TestTrueA();
void TestFalseA();
void TestNumberA();
void TestStringA();
void TestStringA2();
void TestStringB();
void TestArrayA();
void TestArrayB();
void TestObjectA();
void TestStringUnterminated();
void TestArrayUnterminated();
void TestObjectUnterminated();
static void AddTests(BTestSuite& suite);
private:
void TestUnterminated(const char* input);
void TestParseAndWrite(char* input,
char* expectedOutput);
};
#endif // JSON_END_TO_END_TEST_H

View File

@ -0,0 +1,354 @@
/*
* Copyright 2017, Andrew Lindesay <apl@lindesay.co.nz>
* Distributed under the terms of the MIT License.
*/
#include "JsonErrorHandlingTest.h"
#include <AutoDeleter.h>
#include <Json.h>
#include <JsonEventListener.h>
#include <cppunit/TestCaller.h>
#include <cppunit/TestSuite.h>
#include "JsonSamples.h"
using namespace BPrivate;
class ErrorCapturingListener : public BJsonEventListener {
public:
ErrorCapturingListener();
virtual ~ErrorCapturingListener();
bool Handle(const BJsonEvent& event);
void HandleError(status_t status, int32 line,
const char* message);
void Complete() {};
status_t ErrorStatus();
int32 GetErrorLine();
BString GetErrorMessage();
bool HasEventsAfterError();
json_event_type FirstEventTypeAfterError();
private:
status_t fErrorStatus;
int32 fErrorLine;
BString fErrorMessage;
json_event_type fFirstEventTypeAfterError;
int32 fEventCountAfterError;
};
/*! This DataIO concrete implementation is designed to open and then to fail
in order to simulate what might happen if there were an IO problem when
parsing some JSON.
*/
class FailingDataIO : public BDataIO {
public:
FailingDataIO();
virtual ~FailingDataIO();
ssize_t Read(void* buffer, size_t size);
ssize_t Write(const void* buffer, size_t size);
status_t Flush();
};
// #pragma mark - FailingDataIO
FailingDataIO::FailingDataIO()
{
}
FailingDataIO::~FailingDataIO()
{
}
ssize_t
FailingDataIO::Read(void* buffer, size_t size)
{
return B_IO_ERROR;
}
ssize_t
FailingDataIO::Write(const void* buffer, size_t size)
{
fprintf(stdout, "attempt to write");
return B_IO_ERROR;
}
status_t
FailingDataIO::Flush()
{
return B_IO_ERROR;
}
// #pragma mark - ErrorCapturingListener
ErrorCapturingListener::ErrorCapturingListener()
{
fErrorStatus = B_OK;
fFirstEventTypeAfterError = B_JSON_NULL; // least likely
fEventCountAfterError = 0;
}
ErrorCapturingListener::~ErrorCapturingListener()
{
}
bool
ErrorCapturingListener::Handle(const BJsonEvent& event)
{
if (fErrorStatus != B_OK) {
if (fEventCountAfterError == 0)
fFirstEventTypeAfterError = event.EventType();
fEventCountAfterError++;
}
return true; // keep going.
}
void
ErrorCapturingListener::HandleError(status_t status, int32 line,
const char *message)
{
fErrorStatus = status;
fErrorLine = line;
if (message != NULL)
fErrorMessage = BString(message);
else
fErrorMessage = BString();
}
status_t
ErrorCapturingListener::ErrorStatus()
{
return fErrorStatus;
}
int32
ErrorCapturingListener::GetErrorLine()
{
return fErrorLine;
}
BString
ErrorCapturingListener::GetErrorMessage()
{
return fErrorMessage;
}
json_event_type
ErrorCapturingListener::FirstEventTypeAfterError()
{
return fFirstEventTypeAfterError;
}
bool
ErrorCapturingListener::HasEventsAfterError()
{
return fEventCountAfterError > 0;
}
JsonErrorHandlingTest::JsonErrorHandlingTest()
{
}
JsonErrorHandlingTest::~JsonErrorHandlingTest()
{
}
void
JsonErrorHandlingTest::TestParseWithBadStringEscape(const char* input,
int32 line, status_t expectedStatus, char expectedBadEscapeChar)
{
BString expectedMessage;
expectedMessage.SetToFormat("unexpected escaped character [%c] "
"in string parsing", expectedBadEscapeChar);
TestParseWithErrorMessage(input, line, expectedStatus,
expectedMessage.String());
}
void
JsonErrorHandlingTest::TestParseWithUnterminatedElement(const char* input,
int32 line, status_t expectedStatus)
{
BString expectedMessage;
expectedMessage.SetToFormat("unterminated element");
TestParseWithErrorMessage(input, line, expectedStatus,
expectedMessage.String());
}
void
JsonErrorHandlingTest::TestParseWithUnexpectedCharacter(const char* input,
int32 line, status_t expectedStatus, char expectedChar)
{
BString expectedMessage;
expectedMessage.SetToFormat("unexpected character [%" B_PRIu8
"] (%c) when parsing element", static_cast<uint8>(expectedChar),
expectedChar);
TestParseWithErrorMessage(input, line, expectedStatus,
expectedMessage.String());
}
void
JsonErrorHandlingTest::TestParseWithErrorMessage(const char* input, int32 line,
status_t expectedStatus, const char* expectedMessage)
{
fprintf(stderr, "in >%s<\n", input);
BDataIO *inputData = new BMemoryIO(input, strlen(input));
ObjectDeleter<BDataIO> inputDataDeleter(inputData);
TestParseWithErrorMessage(inputData, line, expectedStatus, expectedMessage);
}
void
JsonErrorHandlingTest::TestParseWithErrorMessage(BDataIO* inputData, int32 line,
status_t expectedStatus, const char* expectedMessage)
{
ErrorCapturingListener* listener = new ErrorCapturingListener();
ObjectDeleter<ErrorCapturingListener> listenerDeleter(listener);
// ----------------------
BPrivate::BJson::Parse(inputData, listener);
// ----------------------
fprintf(stderr, "expected error at line %" B_PRIi32 " - %s : %s\n",
line,
strerror(expectedStatus),
expectedMessage);
fprintf(stderr, "actual error at line %" B_PRIi32 " - %s : %s\n",
listener->GetErrorLine(),
strerror(listener->ErrorStatus()),
listener->GetErrorMessage().String());
if (listener->HasEventsAfterError()) {
fprintf(stderr, "first event after error [%d]\n",
listener->FirstEventTypeAfterError());
}
CPPUNIT_ASSERT(!listener->HasEventsAfterError());
CPPUNIT_ASSERT_EQUAL(expectedStatus, listener->ErrorStatus());
CPPUNIT_ASSERT_EQUAL(line, listener->GetErrorLine());
CPPUNIT_ASSERT(0 == strcmp(expectedMessage,
listener->GetErrorMessage().String()));
}
void
JsonErrorHandlingTest::TestCompletelyUnknown()
{
TestParseWithUnexpectedCharacter(
JSON_SAMPLE_BROKEN_COMPLETELY_UNKNOWN, 1, B_BAD_DATA, 'z');
}
void
JsonErrorHandlingTest::TestObjectWithPrematureSeparator()
{
TestParseWithErrorMessage(JSON_SAMPLE_BROKEN_OBJECT_PREMATURE_SEPARATOR, 1,
B_BAD_DATA, "unexpected item separator when parsing start of object");
}
void
JsonErrorHandlingTest::TestStringUnterminated()
{
TestParseWithErrorMessage(JSON_SAMPLE_BROKEN_UNTERMINATED_STRING, 1,
B_BAD_DATA, "unexpected end of input");
}
void
JsonErrorHandlingTest::TestBadStringEscape()
{
TestParseWithBadStringEscape(
JSON_SAMPLE_BROKEN_BAD_STRING_ESCAPE, 1, B_BAD_DATA, 'v');
}
void
JsonErrorHandlingTest::TestBadNumber()
{
BString expectedMessage;
expectedMessage.SetToFormat("malformed number",
JSON_SAMPLE_BROKEN_NUMBER);
TestParseWithErrorMessage(JSON_SAMPLE_BROKEN_NUMBER, 1, B_BAD_DATA,
expectedMessage.String());
}
void
JsonErrorHandlingTest::TestIOIssue()
{
BDataIO *inputData = new FailingDataIO();
ObjectDeleter<BDataIO> inputDataDeleter(inputData);
TestParseWithErrorMessage(inputData, -1, B_IO_ERROR,
"io related read error");
}
/*static*/ void
JsonErrorHandlingTest::AddTests(BTestSuite& parent)
{
CppUnit::TestSuite& suite = *new CppUnit::TestSuite(
"JsonErrorHandlingTest");
suite.addTest(new CppUnit::TestCaller<JsonErrorHandlingTest>(
"JsonErrorHandlingTest::TestCompletelyUnknown",
&JsonErrorHandlingTest::TestCompletelyUnknown));
suite.addTest(new CppUnit::TestCaller<JsonErrorHandlingTest>(
"JsonErrorHandlingTest::TestObjectWithPrematureSeparator",
&JsonErrorHandlingTest::TestObjectWithPrematureSeparator));
suite.addTest(new CppUnit::TestCaller<JsonErrorHandlingTest>(
"JsonErrorHandlingTest::TestStringUnterminated",
&JsonErrorHandlingTest::TestStringUnterminated));
suite.addTest(new CppUnit::TestCaller<JsonErrorHandlingTest>(
"JsonErrorHandlingTest::TestBadStringEscape",
&JsonErrorHandlingTest::TestBadStringEscape));
suite.addTest(new CppUnit::TestCaller<JsonErrorHandlingTest>(
"JsonErrorHandlingTest::TestBadNumber",
&JsonErrorHandlingTest::TestBadNumber));
suite.addTest(new CppUnit::TestCaller<JsonErrorHandlingTest>(
"JsonErrorHandlingTest::TestIOIssue",
&JsonErrorHandlingTest::TestIOIssue));
parent.addTest("JsonErrorHandlingTest", &suite);
}

View File

@ -0,0 +1,61 @@
/*
* Copyright 2017, Andrew Lindesay <apl@lindesay.co.nz>
* Distributed under the terms of the MIT License.
*/
#ifndef JSON_ERROR_HANDLING_TEST_H
#define JSON_ERROR_HANDLING_TEST_H
#include <TestCase.h>
#include <TestSuite.h>
#include <DataIO.h>
class Sample;
class JsonErrorHandlingTest : public CppUnit::TestCase {
public:
JsonErrorHandlingTest();
virtual ~JsonErrorHandlingTest();
void TestCompletelyUnknown();
void TestObjectWithPrematureSeparator();
void TestStringUnterminated();
void TestBadStringEscape();
void TestBadNumber();
void TestIOIssue();
static void AddTests(BTestSuite& suite);
private:
void TestParseWithUnexpectedCharacter(
const char* input, int32 line,
status_t expectedStatus, char expectedChar);
void TestParseWithUnterminatedElement(
const char* input, int32 line,
status_t expectedStatus);
void TestParseWithBadStringEscape(
const char* input,
int32 line, status_t expectedStatus,
char expectedBadEscapeChar);
void TestParseWithErrorMessage(
const char* input, int32 line,
status_t expectedStatus,
const char* expectedMessage);
void TestParseWithErrorMessage(
BDataIO* data, int32 line,
status_t expectedStatus,
const char* expectedMessage);
};
#endif // JSON_ERROR_HANDLING_TEST_H

View File

@ -0,0 +1,707 @@
/*
* Copyright 2017, Andrew Lindesay <apl@lindesay.co.nz>
* Distributed under the terms of the MIT License.
*/
#ifndef JSON_SAMPLES_H
#define JSON_SAMPLES_H
#define JSON_SAMPLE_FALSE_A_IN "false"
#define JSON_SAMPLE_FALSE_A_EXPECTED_OUT JSON_SAMPLE_FALSE_A_IN
#define JSON_SAMPLE_TRUE_A_IN "true"
#define JSON_SAMPLE_TRUE_A_EXPECTED_OUT JSON_SAMPLE_TRUE_A_IN
#define JSON_SAMPLE_NULL_A_IN "null"
#define JSON_SAMPLE_NULL_A_EXPECTED_OUT JSON_SAMPLE_NULL_A_IN
#define JSON_SAMPLE_NUMBER_A_LITERAL 3.142857142857143
#define JSON_SAMPLE_NUMBER_A_IN "3.142857142857143"
#define JSON_SAMPLE_NUMBER_A_EXPECTED_OUT JSON_SAMPLE_NUMBER_A_IN
#define JSON_SAMPLE_NUMBER_B_LITERAL 1122334455
#define JSON_SAMPLE_NUMBER_B_IN "1122334455"
#define JSON_SAMPLE_NUMBER_B_EXPECTED_OUT JSON_SAMPLE_NUMBER_B_IN
/*! This sample is a string that contains a newline in the middle as well as a
unicode character escaped with an "\x..." sequence. The second version 'A2'
has the same unicode character (non US-ASCII) represented as UTF-8 directly.
*/
#define JSON_SAMPLE_STRING_A_IN "\"Lake\\nTaup\\u014d\""
#define JSON_SAMPLE_STRING_A2_IN "\"Lake\\nTaup\xc5\x8d\""
#define JSON_SAMPLE_STRING_A_EXPECTED_OUT JSON_SAMPLE_STRING_A_IN
#define JSON_SAMPLE_STRING_B_IN "\"\\u21c8\""
#define JSON_SAMPLE_STRING_B_EXPECTED_OUT JSON_SAMPLE_STRING_B_IN
/*! This sample is an array containing other elements. */
#define JSON_SAMPLE_ARRAY_A_IN "[\"1234\", 4567 , \n" \
" [ \"A\" ,\"b\", \"C\\u00e9zanne\"] ]"
#define JSON_SAMPLE_ARRAY_A_EXPECTED_OUT "[\"1234\",4567," \
"[\"A\",\"b\",\"C\\u00e9zanne\"]]"
#define JSON_SAMPLE_ARRAY_B_IN "[\"Whirinaki\", \"Wellington\" , \n" \
" { \"key\" : [ \"value\" ] }\n" \
"]"
#define JSON_SAMPLE_ARRAY_B_EXPECTED_OUT "[\"Whirinaki\",\"Wellington\"," \
"{\"key\":[\"value\"]}]"
/*! This sample is an object type containing other elements. */
#define JSON_SAMPLE_OBJECT_A_IN "{\"weather\":\"raining\", \"humidity\" : " \
"\"too-high\", \"daysOfWeek\": [\"MON\", \"TUE\", \"WED\", \"THR\"," \
"\"FRI\"]}"
#define JSON_SAMPLE_OBJECT_A_EXPECTED_OUT "{\"weather\":\"raining\"," \
"\"humidity\":\"too-high\",\"daysOfWeek\":[\"MON\",\"TUE\",\"WED\",\"THR\"," \
"\"FRI\"]}"
/*! This sample contains some other types of JSON nodes such as true, false
and null.
*/
#define JSON_SAMPLE_OBJECT_B_IN "{\"testTrue\":true, \"testFalse\" : " \
"false, \"testNull\": null }"
/*! A very simple object that can be used to test basic functionality. */
#define JSON_SAMPLE_OBJECT_C_IN "{\"key\":\"value\"}"
/*! This sample has a key-value separator as the first character after
opening the object.
*/
#define JSON_SAMPLE_BROKEN_OBJECT_PREMATURE_SEPARATOR "{,\"key\":\"value\"}"
#define JSON_SAMPLE_BROKEN_COMPLETELY_UNKNOWN "zzz"
#define JSON_SAMPLE_BROKEN_UNTERMINATED_OBJECT "[ 123, {\"key\":\"value\""
#define JSON_SAMPLE_BROKEN_UNTERMINATED_ARRAY "{\"key\":\"value\",\n" \
"\"key2\":\n" \
"["
#define JSON_SAMPLE_BROKEN_UNTERMINATED_STRING "\"without end"
#define JSON_SAMPLE_BROKEN_BAD_STRING_ESCAPE "\"a dastardly \\v escape sequence\""
#define JSON_SAMPLE_BROKEN_NUMBER "-123e123E456e"
/*! This is a larger example and comes from the request-response
cycle of a JSON-RPC request from the HaikuDepot desktop application
into the HaikuDepotServer system.
*/
#define JSON_SAMPLE_HDS_FETCH_BATCH_PKGS { \
0x7b, 0x22, 0x6a, 0x73, 0x6f, 0x6e, 0x72, 0x70, 0x63, 0x22, 0x3a, 0x22, \
0x32, 0x2e, 0x30, 0x22, 0x2c, 0x22, 0x69, 0x64, 0x22, 0x3a, 0x36, 0x2c, \
0x22, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x3a, 0x7b, 0x22, 0x70, \
0x6b, 0x67, 0x73, 0x22, 0x3a, 0x5b, 0x7b, 0x22, 0x6e, 0x61, 0x6d, 0x65, \
0x22, 0x3a, 0x22, 0x62, 0x65, 0x73, 0x68, 0x61, 0x72, 0x65, 0x5f, 0x73, \
0x65, 0x72, 0x76, 0x65, 0x72, 0x22, 0x2c, 0x22, 0x6d, 0x6f, 0x64, 0x69, \
0x66, 0x79, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, \
0x3a, 0x31, 0x34, 0x37, 0x36, 0x31, 0x39, 0x39, 0x33, 0x36, 0x34, 0x30, \
0x37, 0x30, 0x2c, 0x22, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73, \
0x22, 0x3a, 0x5b, 0x7b, 0x22, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x22, 0x3a, \
0x22, 0x32, 0x22, 0x2c, 0x22, 0x6d, 0x69, 0x6e, 0x6f, 0x72, 0x22, 0x3a, \
0x22, 0x34, 0x32, 0x22, 0x2c, 0x22, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x22, \
0x3a, 0x6e, 0x75, 0x6c, 0x6c, 0x2c, 0x22, 0x70, 0x72, 0x65, 0x52, 0x65, \
0x6c, 0x65, 0x61, 0x73, 0x65, 0x22, 0x3a, 0x6e, 0x75, 0x6c, 0x6c, 0x2c, \
0x22, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x3a, 0x33, \
0x2c, 0x22, 0x61, 0x72, 0x63, 0x68, 0x69, 0x74, 0x65, 0x63, 0x74, 0x75, \
0x72, 0x65, 0x43, 0x6f, 0x64, 0x65, 0x22, 0x3a, 0x22, 0x78, 0x38, 0x36, \
0x5f, 0x67, 0x63, 0x63, 0x32, 0x22, 0x2c, 0x22, 0x74, 0x69, 0x74, 0x6c, \
0x65, 0x22, 0x3a, 0x6e, 0x75, 0x6c, 0x6c, 0x2c, 0x22, 0x72, 0x65, 0x70, \
0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x6f, 0x75, 0x72, 0x63, \
0x65, 0x43, 0x6f, 0x64, 0x65, 0x22, 0x3a, 0x22, 0x66, 0x61, 0x74, 0x65, \
0x6c, 0x6b, 0x5f, 0x78, 0x38, 0x36, 0x5f, 0x67, 0x63, 0x63, 0x32, 0x22, \
0x2c, 0x22, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, \
0x43, 0x6f, 0x64, 0x65, 0x22, 0x3a, 0x22, 0x66, 0x61, 0x74, 0x65, 0x6c, \
0x6b, 0x22, 0x2c, 0x22, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x22, \
0x3a, 0x22, 0x42, 0x65, 0x53, 0x68, 0x61, 0x72, 0x65, 0x20, 0x53, 0x65, \
0x72, 0x76, 0x65, 0x72, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x41, 0x74, 0x72, \
0x75, 0x73, 0x20, 0x43, 0x68, 0x61, 0x74, 0x62, 0x6f, 0x74, 0x22, 0x2c, \
0x22, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, \
0x22, 0x3a, 0x22, 0x54, 0x68, 0x69, 0x73, 0x20, 0x70, 0x61, 0x63, 0x6b, \
0x61, 0x67, 0x65, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x73, \
0x20, 0x74, 0x68, 0x65, 0x20, 0x5c, 0x22, 0x6d, 0x75, 0x73, 0x63, 0x6c, \
0x65, 0x64, 0x5c, 0x22, 0x20, 0x28, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x20, \
0x55, 0x73, 0x65, 0x72, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, \
0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x20, 0x4c, 0x69, 0x6e, 0x6b, 0x69, \
0x6e, 0x67, 0x20, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, \
0x6e, 0x74, 0x20, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x29, 0x20, 0x73, \
0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x28, 0x76, 0x65, 0x72, 0x73, 0x69, \
0x6f, 0x6e, 0x20, 0x33, 0x2e, 0x32, 0x30, 0x20, 0x70, 0x6f, 0x72, 0x74, \
0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x48, 0x61, 0x69, 0x6b, 0x75, 0x20, \
0x47, 0x43, 0x43, 0x32, 0x20, 0x62, 0x79, 0x20, 0x41, 0x47, 0x4d, 0x53, \
0x20, 0x69, 0x6e, 0x20, 0x32, 0x30, 0x31, 0x36, 0x29, 0x20, 0x61, 0x6e, \
0x64, 0x20, 0x74, 0x68, 0x65, 0x20, 0x41, 0x74, 0x72, 0x75, 0x73, 0x20, \
0x63, 0x68, 0x61, 0x74, 0x20, 0x72, 0x6f, 0x62, 0x6f, 0x74, 0x20, 0x32, \
0x2e, 0x34, 0x32, 0x20, 0x77, 0x68, 0x69, 0x63, 0x68, 0x20, 0x6d, 0x6f, \
0x6e, 0x69, 0x74, 0x6f, 0x72, 0x73, 0x20, 0x74, 0x65, 0x78, 0x74, 0x20, \
0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, \
0x73, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x72, \
0x76, 0x65, 0x72, 0x2e, 0x20, 0x20, 0x54, 0x68, 0x61, 0x74, 0x20, 0x70, \
0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x73, 0x20, 0x65, 0x76, 0x65, 0x72, \
0x79, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x20, 0x79, 0x6f, 0x75, 0x20, 0x6e, \
0x65, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x73, 0x65, 0x74, 0x20, 0x75, \
0x70, 0x20, 0x79, 0x6f, 0x75, 0x72, 0x20, 0x6f, 0x77, 0x6e, 0x20, 0x42, \
0x65, 0x53, 0x68, 0x61, 0x72, 0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, \
0x72, 0x2e, 0x5c, 0x6e, 0x5c, 0x6e, 0x41, 0x66, 0x74, 0x65, 0x72, 0x20, \
0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x2c, 0x20, \
0x79, 0x6f, 0x75, 0x20, 0x6e, 0x65, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, \
0x64, 0x6f, 0x20, 0x61, 0x20, 0x66, 0x65, 0x77, 0x20, 0x74, 0x68, 0x69, \
0x6e, 0x67, 0x73, 0x3a, 0x5c, 0x6e, 0x5c, 0x6e, 0x31, 0x29, 0x20, 0x53, \
0x65, 0x65, 0x20, 0x69, 0x66, 0x20, 0x69, 0x74, 0x20, 0x69, 0x6e, 0x73, \
0x74, 0x61, 0x6c, 0x6c, 0x65, 0x64, 0x2e, 0x20, 0x20, 0x49, 0x74, 0x20, \
0x73, 0x68, 0x6f, 0x75, 0x6c, 0x64, 0x20, 0x62, 0x65, 0x20, 0x72, 0x75, \
0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x72, 0x69, 0x67, 0x68, 0x74, 0x20, \
0x61, 0x66, 0x74, 0x65, 0x72, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, \
0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, \
0x61, 0x75, 0x74, 0x6f, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x6c, \
0x79, 0x20, 0x73, 0x74, 0x61, 0x72, 0x74, 0x20, 0x75, 0x70, 0x20, 0x61, \
0x66, 0x74, 0x65, 0x72, 0x20, 0x79, 0x6f, 0x75, 0x20, 0x72, 0x65, 0x62, \
0x6f, 0x6f, 0x74, 0x2e, 0x20, 0x20, 0x49, 0x6e, 0x20, 0x50, 0x72, 0x6f, \
0x63, 0x65, 0x73, 0x73, 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, \
0x6c, 0x65, 0x72, 0x20, 0x75, 0x6e, 0x64, 0x65, 0x72, 0x20, 0x74, 0x68, \
0x65, 0x20, 0x5c, 0x22, 0x54, 0x68, 0x72, 0x65, 0x61, 0x64, 0x73, 0x20, \
0x61, 0x6e, 0x64, 0x20, 0x43, 0x50, 0x55, 0x20, 0x75, 0x73, 0x61, 0x67, \
0x65, 0x5c, 0x22, 0x20, 0x6d, 0x65, 0x6e, 0x75, 0x20, 0x79, 0x6f, 0x75, \
0x20, 0x73, 0x68, 0x6f, 0x75, 0x6c, 0x64, 0x20, 0x73, 0x65, 0x65, 0x20, \
0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x20, 0x66, 0x6f, 0x72, 0x20, \
0x5c, 0x22, 0x2f, 0x62, 0x69, 0x6e, 0x2f, 0x6d, 0x75, 0x73, 0x63, 0x6c, \
0x65, 0x64, 0x5c, 0x22, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x5c, 0x22, 0x2f, \
0x62, 0x69, 0x6e, 0x41, 0x74, 0x72, 0x75, 0x73, 0x5c, 0x22, 0x2e, 0x5c, \
0x6e, 0x5c, 0x6e, 0x32, 0x29, 0x20, 0x54, 0x65, 0x73, 0x74, 0x20, 0x69, \
0x74, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x42, 0x65, 0x53, 0x68, 0x61, \
0x72, 0x65, 0x2e, 0x20, 0x20, 0x55, 0x73, 0x65, 0x20, 0x79, 0x6f, 0x75, \
0x72, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x72, 0x27, 0x73, \
0x20, 0x49, 0x50, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x20, \
0x6f, 0x72, 0x20, 0x6c, 0x65, 0x73, 0x73, 0x20, 0x72, 0x65, 0x6c, 0x69, \
0x61, 0x62, 0x6c, 0x79, 0x20, 0x5c, 0x22, 0x6c, 0x6f, 0x63, 0x61, 0x6c, \
0x68, 0x6f, 0x73, 0x74, 0x5c, 0x22, 0x20, 0x61, 0x73, 0x20, 0x74, 0x68, \
0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, \
0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x74, 0x6f, 0x20, \
0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x20, 0x74, 0x6f, 0x2e, 0x5c, \
0x6e, 0x5c, 0x6e, 0x33, 0x29, 0x20, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, \
0x64, 0x20, 0x70, 0x6f, 0x72, 0x74, 0x20, 0x32, 0x39, 0x36, 0x30, 0x20, \
0x66, 0x72, 0x6f, 0x6d, 0x20, 0x79, 0x6f, 0x75, 0x72, 0x20, 0x72, 0x6f, \
0x75, 0x74, 0x65, 0x72, 0x20, 0x74, 0x6f, 0x20, 0x79, 0x6f, 0x75, 0x72, \
0x20, 0x63, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x72, 0x20, 0x28, 0x6f, \
0x74, 0x68, 0x65, 0x72, 0x77, 0x69, 0x73, 0x65, 0x20, 0x6e, 0x6f, 0x62, \
0x6f, 0x64, 0x79, 0x20, 0x6f, 0x75, 0x74, 0x73, 0x69, 0x64, 0x65, 0x20, \
0x63, 0x61, 0x6e, 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x79, \
0x6f, 0x75, 0x72, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x29, 0x2e, \
0x5c, 0x6e, 0x5c, 0x6e, 0x34, 0x29, 0x20, 0x55, 0x70, 0x6f, 0x6e, 0x20, \
0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, \
0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x6f, 0x6f, 0x74, 0x20, 0x73, 0x63, \
0x72, 0x69, 0x70, 0x74, 0x20, 0x69, 0x73, 0x20, 0x63, 0x6f, 0x70, 0x69, \
0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x2f, 0x62, 0x6f, 0x6f, 0x74, 0x2f, \
0x68, 0x6f, 0x6d, 0x65, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2f, \
0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x2f, 0x62, 0x6f, 0x6f, \
0x74, 0x2f, 0x6c, 0x61, 0x75, 0x6e, 0x63, 0x68, 0x2f, 0x62, 0x65, 0x73, \
0x68, 0x61, 0x72, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, \
0x73, 0x68, 0x20, 0x77, 0x68, 0x65, 0x72, 0x65, 0x20, 0x79, 0x6f, 0x75, \
0x20, 0x63, 0x61, 0x6e, 0x20, 0x65, 0x64, 0x69, 0x74, 0x20, 0x74, 0x68, \
0x65, 0x20, 0x73, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x20, 0x6f, 0x70, \
0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2c, 0x20, 0x69, 0x6e, 0x63, 0x6c, 0x75, \
0x64, 0x69, 0x6e, 0x67, 0x20, 0x79, 0x6f, 0x75, 0x72, 0x20, 0x27, 0x62, \
0x6f, 0x74, 0x73, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x61, 0x6e, 0x64, \
0x20, 0x74, 0x68, 0x65, 0x20, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, \
0x6c, 0x20, 0x49, 0x50, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, \
0x20, 0x66, 0x6f, 0x72, 0x20, 0x4d, 0x75, 0x73, 0x63, 0x6c, 0x65, 0x20, \
0x74, 0x6f, 0x20, 0x75, 0x73, 0x65, 0x20, 0x28, 0x6f, 0x74, 0x68, 0x65, \
0x72, 0x77, 0x69, 0x73, 0x65, 0x20, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, \
0x61, 0x6c, 0x20, 0x75, 0x73, 0x65, 0x72, 0x73, 0x20, 0x77, 0x69, 0x6c, \
0x6c, 0x20, 0x68, 0x61, 0x76, 0x65, 0x20, 0x74, 0x72, 0x6f, 0x75, 0x62, \
0x6c, 0x65, 0x20, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6e, \
0x67, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x64, 0x6f, 0x69, 0x6e, 0x67, 0x20, \
0x66, 0x69, 0x6c, 0x65, 0x20, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, \
0x72, 0x73, 0x29, 0x2e, 0x20, 0x20, 0x54, 0x68, 0x65, 0x20, 0x6c, 0x6f, \
0x67, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x20, 0x61, 0x72, 0x65, 0x20, \
0x61, 0x6c, 0x73, 0x6f, 0x20, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, \
0x65, 0x64, 0x20, 0x74, 0x68, 0x65, 0x72, 0x65, 0x2c, 0x20, 0x72, 0x65, \
0x6d, 0x6f, 0x76, 0x65, 0x20, 0x74, 0x68, 0x65, 0x6d, 0x20, 0x69, 0x66, \
0x20, 0x79, 0x6f, 0x75, 0x27, 0x72, 0x65, 0x20, 0x75, 0x73, 0x69, 0x6e, \
0x67, 0x20, 0x61, 0x20, 0x72, 0x65, 0x61, 0x64, 0x2d, 0x6f, 0x6e, 0x6c, \
0x79, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x20, 0x73, 0x79, 0x73, 0x74, 0x65, \
0x6d, 0x2e, 0x5c, 0x6e, 0x5c, 0x6e, 0x35, 0x29, 0x20, 0x45, 0x64, 0x69, \
0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x42, 0x6f, 0x74, 0x44, 0x61, 0x74, \
0x61, 0x20, 0x74, 0x65, 0x78, 0x74, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x73, \
0x20, 0x69, 0x6e, 0x20, 0x2f, 0x62, 0x6f, 0x6f, 0x74, 0x2f, 0x68, 0x6f, \
0x6d, 0x65, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2f, 0x73, 0x65, \
0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x2f, 0x42, 0x6f, 0x74, 0x44, 0x61, \
0x74, 0x61, 0x20, 0x74, 0x6f, 0x20, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, \
0x69, 0x7a, 0x65, 0x20, 0x79, 0x6f, 0x75, 0x72, 0x20, 0x63, 0x68, 0x61, \
0x74, 0x62, 0x6f, 0x74, 0x27, 0x73, 0x20, 0x74, 0x65, 0x78, 0x74, 0x2e, \
0x5c, 0x6e, 0x5c, 0x6e, 0x48, 0x61, 0x70, 0x70, 0x79, 0x20, 0x42, 0x65, \
0x73, 0x68, 0x61, 0x72, 0x69, 0x6e, 0x67, 0x21, 0x22, 0x2c, 0x22, 0x70, \
0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, \
0x22, 0x3a, 0x34, 0x30, 0x33, 0x36, 0x33, 0x37, 0x33, 0x7d, 0x5d, 0x2c, \
0x22, 0x70, 0x6b, 0x67, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, \
0x43, 0x6f, 0x64, 0x65, 0x73, 0x22, 0x3a, 0x5b, 0x22, 0x69, 0x6e, 0x74, \
0x65, 0x72, 0x6e, 0x65, 0x74, 0x61, 0x6e, 0x64, 0x6e, 0x65, 0x74, 0x77, \
0x6f, 0x72, 0x6b, 0x22, 0x5d, 0x2c, 0x22, 0x70, 0x6b, 0x67, 0x53, 0x63, \
0x72, 0x65, 0x65, 0x6e, 0x73, 0x68, 0x6f, 0x74, 0x73, 0x22, 0x3a, 0x5b, \
0x5d, 0x2c, 0x22, 0x70, 0x6b, 0x67, 0x49, 0x63, 0x6f, 0x6e, 0x73, 0x22, \
0x3a, 0x6e, 0x75, 0x6c, 0x6c, 0x2c, 0x22, 0x64, 0x65, 0x72, 0x69, 0x76, \
0x65, 0x64, 0x52, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x22, 0x3a, 0x6e, 0x75, \
0x6c, 0x6c, 0x2c, 0x22, 0x70, 0x72, 0x6f, 0x6d, 0x69, 0x6e, 0x65, 0x6e, \
0x63, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x22, 0x3a, \
0x31, 0x30, 0x30, 0x30, 0x2c, 0x22, 0x70, 0x6b, 0x67, 0x43, 0x68, 0x61, \
0x6e, 0x67, 0x65, 0x6c, 0x6f, 0x67, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, \
0x74, 0x22, 0x3a, 0x6e, 0x75, 0x6c, 0x6c, 0x7d, 0x2c, 0x7b, 0x22, 0x6e, \
0x61, 0x6d, 0x65, 0x22, 0x3a, 0x22, 0x62, 0x6f, 0x6f, 0x6b, 0x6d, 0x61, \
0x72, 0x6b, 0x65, 0x72, 0x22, 0x2c, 0x22, 0x6d, 0x6f, 0x64, 0x69, 0x66, \
0x79, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x3a, \
0x31, 0x34, 0x38, 0x38, 0x37, 0x38, 0x35, 0x33, 0x33, 0x31, 0x36, 0x33, \
0x31, 0x2c, 0x22, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x22, \
0x3a, 0x5b, 0x7b, 0x22, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x22, 0x3a, 0x22, \
0x31, 0x22, 0x2c, 0x22, 0x6d, 0x69, 0x6e, 0x6f, 0x72, 0x22, 0x3a, 0x22, \
0x33, 0x22, 0x2c, 0x22, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x22, 0x3a, 0x6e, \
0x75, 0x6c, 0x6c, 0x2c, 0x22, 0x70, 0x72, 0x65, 0x52, 0x65, 0x6c, 0x65, \
0x61, 0x73, 0x65, 0x22, 0x3a, 0x6e, 0x75, 0x6c, 0x6c, 0x2c, 0x22, 0x72, \
0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x3a, 0x32, 0x2c, 0x22, \
0x61, 0x72, 0x63, 0x68, 0x69, 0x74, 0x65, 0x63, 0x74, 0x75, 0x72, 0x65, \
0x43, 0x6f, 0x64, 0x65, 0x22, 0x3a, 0x22, 0x78, 0x38, 0x36, 0x5f, 0x67, \
0x63, 0x63, 0x32, 0x22, 0x2c, 0x22, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x22, \
0x3a, 0x22, 0x42, 0x6f, 0x6f, 0x6b, 0x6d, 0x61, 0x72, 0x6b, 0x65, 0x72, \
0x22, 0x2c, 0x22, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, \
0x79, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x64, 0x65, 0x22, \
0x3a, 0x22, 0x66, 0x61, 0x74, 0x65, 0x6c, 0x6b, 0x5f, 0x78, 0x38, 0x36, \
0x5f, 0x67, 0x63, 0x63, 0x32, 0x22, 0x2c, 0x22, 0x72, 0x65, 0x70, 0x6f, \
0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x43, 0x6f, 0x64, 0x65, 0x22, 0x3a, \
0x22, 0x66, 0x61, 0x74, 0x65, 0x6c, 0x6b, 0x22, 0x2c, 0x22, 0x73, 0x75, \
0x6d, 0x6d, 0x61, 0x72, 0x79, 0x22, 0x3a, 0x22, 0x42, 0x6f, 0x6f, 0x6b, \
0x6d, 0x61, 0x72, 0x6b, 0x20, 0x6d, 0x61, 0x6b, 0x65, 0x72, 0x22, 0x2c, \
0x22, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, \
0x22, 0x3a, 0x22, 0x42, 0x6f, 0x6f, 0x6b, 0x6d, 0x61, 0x72, 0x6b, 0x65, \
0x72, 0x20, 0x6d, 0x61, 0x6b, 0x65, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, \
0x73, 0x74, 0x6f, 0x72, 0x65, 0x73, 0x20, 0x62, 0x6f, 0x6f, 0x6b, 0x6d, \
0x61, 0x72, 0x6b, 0x73, 0x20, 0x28, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, \
0x20, 0x77, 0x69, 0x64, 0x65, 0x20, 0x6c, 0x69, 0x6e, 0x6b, 0x73, 0x20, \
0x74, 0x6f, 0x20, 0x77, 0x65, 0x62, 0x20, 0x73, 0x69, 0x74, 0x65, 0x73, \
0x29, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, 0x48, 0x61, \
0x69, 0x6b, 0x75, 0x20, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6e, \
0x67, 0x20, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x2e, 0x20, 0x49, 0x74, \
0x61, 0x6c, 0x73, 0x6f, 0x20, 0x61, 0x64, 0x64, 0x73, 0x20, 0x74, 0x68, \
0x65, 0x20, 0x62, 0x6f, 0x6f, 0x6b, 0x6d, 0x61, 0x72, 0x6b, 0x73, 0x20, \
0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x44, 0x65, 0x73, 0x6b, 0x62, \
0x61, 0x72, 0x20, 0x6d, 0x65, 0x6e, 0x75, 0x2e, 0x22, 0x2c, 0x22, 0x70, \
0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, \
0x22, 0x3a, 0x36, 0x34, 0x34, 0x31, 0x35, 0x7d, 0x5d, 0x2c, 0x22, 0x70, \
0x6b, 0x67, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x43, 0x6f, \
0x64, 0x65, 0x73, 0x22, 0x3a, 0x5b, 0x22, 0x70, 0x72, 0x6f, 0x64, 0x75, \
0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x22, 0x2c, 0x22, 0x73, 0x79, \
0x73, 0x74, 0x65, 0x6d, 0x61, 0x6e, 0x64, 0x75, 0x74, 0x69, 0x6c, 0x69, \
0x74, 0x69, 0x65, 0x73, 0x22, 0x2c, 0x22, 0x69, 0x6e, 0x74, 0x65, 0x72, \
0x6e, 0x65, 0x74, 0x61, 0x6e, 0x64, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, \
0x6b, 0x22, 0x5d, 0x2c, 0x22, 0x70, 0x6b, 0x67, 0x53, 0x63, 0x72, 0x65, \
0x65, 0x6e, 0x73, 0x68, 0x6f, 0x74, 0x73, 0x22, 0x3a, 0x5b, 0x7b, 0x22, \
0x63, 0x6f, 0x64, 0x65, 0x22, 0x3a, 0x22, 0x37, 0x36, 0x35, 0x30, 0x39, \
0x64, 0x64, 0x36, 0x2d, 0x32, 0x35, 0x39, 0x36, 0x2d, 0x34, 0x32, 0x34, \
0x37, 0x2d, 0x61, 0x38, 0x65, 0x34, 0x2d, 0x34, 0x37, 0x36, 0x39, 0x65, \
0x34, 0x35, 0x61, 0x36, 0x66, 0x37, 0x35, 0x22, 0x2c, 0x22, 0x6c, 0x65, \
0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x32, 0x32, 0x31, 0x32, 0x35, 0x2c, \
0x22, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0x3a, 0x34, 0x33, 0x32, \
0x2c, 0x22, 0x77, 0x69, 0x64, 0x74, 0x68, 0x22, 0x3a, 0x35, 0x31, 0x31, \
0x7d, 0x2c, 0x7b, 0x22, 0x63, 0x6f, 0x64, 0x65, 0x22, 0x3a, 0x22, 0x33, \
0x32, 0x32, 0x61, 0x62, 0x39, 0x66, 0x33, 0x2d, 0x65, 0x35, 0x36, 0x66, \
0x2d, 0x34, 0x64, 0x31, 0x61, 0x2d, 0x39, 0x37, 0x34, 0x33, 0x2d, 0x35, \
0x63, 0x65, 0x66, 0x65, 0x39, 0x39, 0x32, 0x38, 0x32, 0x37, 0x66, 0x22, \
0x2c, 0x22, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x35, 0x34, \
0x31, 0x30, 0x34, 0x2c, 0x22, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, \
0x3a, 0x33, 0x30, 0x39, 0x2c, 0x22, 0x77, 0x69, 0x64, 0x74, 0x68, 0x22, \
0x3a, 0x33, 0x32, 0x36, 0x7d, 0x5d, 0x2c, 0x22, 0x70, 0x6b, 0x67, 0x49, \
0x63, 0x6f, 0x6e, 0x73, 0x22, 0x3a, 0x6e, 0x75, 0x6c, 0x6c, 0x2c, 0x22, \
0x64, 0x65, 0x72, 0x69, 0x76, 0x65, 0x64, 0x52, 0x61, 0x74, 0x69, 0x6e, \
0x67, 0x22, 0x3a, 0x32, 0x2e, 0x30, 0x2c, 0x22, 0x70, 0x72, 0x6f, 0x6d, \
0x69, 0x6e, 0x65, 0x6e, 0x63, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x69, \
0x6e, 0x67, 0x22, 0x3a, 0x31, 0x30, 0x30, 0x30, 0x2c, 0x22, 0x70, 0x6b, \
0x67, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x6c, 0x6f, 0x67, 0x43, 0x6f, \
0x6e, 0x74, 0x65, 0x6e, 0x74, 0x22, 0x3a, 0x6e, 0x75, 0x6c, 0x6c, 0x7d, \
0x2c, 0x7b, 0x22, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x3a, 0x22, 0x66, 0x61, \
0x6c, 0x6c, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x73, 0x22, 0x2c, 0x22, 0x6d, \
0x6f, 0x64, 0x69, 0x66, 0x79, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, \
0x6d, 0x70, 0x22, 0x3a, 0x31, 0x34, 0x36, 0x33, 0x36, 0x38, 0x34, 0x36, \
0x35, 0x38, 0x33, 0x39, 0x38, 0x2c, 0x22, 0x76, 0x65, 0x72, 0x73, 0x69, \
0x6f, 0x6e, 0x73, 0x22, 0x3a, 0x5b, 0x7b, 0x22, 0x6d, 0x61, 0x6a, 0x6f, \
0x72, 0x22, 0x3a, 0x22, 0x31, 0x22, 0x2c, 0x22, 0x6d, 0x69, 0x6e, 0x6f, \
0x72, 0x22, 0x3a, 0x22, 0x32, 0x22, 0x2c, 0x22, 0x6d, 0x69, 0x63, 0x72, \
0x6f, 0x22, 0x3a, 0x6e, 0x75, 0x6c, 0x6c, 0x2c, 0x22, 0x70, 0x72, 0x65, \
0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x22, 0x3a, 0x6e, 0x75, 0x6c, \
0x6c, 0x2c, 0x22, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x22, \
0x3a, 0x31, 0x2c, 0x22, 0x61, 0x72, 0x63, 0x68, 0x69, 0x74, 0x65, 0x63, \
0x74, 0x75, 0x72, 0x65, 0x43, 0x6f, 0x64, 0x65, 0x22, 0x3a, 0x22, 0x78, \
0x38, 0x36, 0x5f, 0x67, 0x63, 0x63, 0x32, 0x22, 0x2c, 0x22, 0x74, 0x69, \
0x74, 0x6c, 0x65, 0x22, 0x3a, 0x22, 0x46, 0x61, 0x6c, 0x6c, 0x20, 0x4c, \
0x65, 0x61, 0x76, 0x65, 0x73, 0x22, 0x2c, 0x22, 0x72, 0x65, 0x70, 0x6f, \
0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, \
0x43, 0x6f, 0x64, 0x65, 0x22, 0x3a, 0x22, 0x66, 0x61, 0x74, 0x65, 0x6c, \
0x6b, 0x5f, 0x78, 0x38, 0x36, 0x5f, 0x67, 0x63, 0x63, 0x32, 0x22, 0x2c, \
0x22, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x43, \
0x6f, 0x64, 0x65, 0x22, 0x3a, 0x22, 0x66, 0x61, 0x74, 0x65, 0x6c, 0x6b, \
0x22, 0x2c, 0x22, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x22, 0x3a, \
0x22, 0x46, 0x61, 0x6c, 0x6c, 0x20, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x73, \
0x20, 0x73, 0x63, 0x72, 0x65, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x72, \
0x22, 0x2c, 0x22, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, \
0x6f, 0x6e, 0x22, 0x3a, 0x22, 0x41, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x6c, \
0x79, 0x20, 0x73, 0x63, 0x72, 0x65, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, \
0x72, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x66, 0x61, 0x6c, 0x6c, 0x69, \
0x6e, 0x67, 0x20, 0x48, 0x61, 0x69, 0x6b, 0x75, 0x20, 0x6c, 0x65, 0x61, \
0x76, 0x65, 0x73, 0x2e, 0x22, 0x2c, 0x22, 0x70, 0x61, 0x79, 0x6c, 0x6f, \
0x61, 0x64, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x32, \
0x34, 0x31, 0x32, 0x7d, 0x5d, 0x2c, 0x22, 0x70, 0x6b, 0x67, 0x43, 0x61, \
0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x43, 0x6f, 0x64, 0x65, 0x73, 0x22, \
0x3a, 0x5b, 0x22, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x61, 0x6e, 0x64, \
0x75, 0x74, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x22, 0x5d, 0x2c, \
0x22, 0x70, 0x6b, 0x67, 0x53, 0x63, 0x72, 0x65, 0x65, 0x6e, 0x73, 0x68, \
0x6f, 0x74, 0x73, 0x22, 0x3a, 0x5b, 0x7b, 0x22, 0x63, 0x6f, 0x64, 0x65, \
0x22, 0x3a, 0x22, 0x64, 0x64, 0x62, 0x37, 0x62, 0x63, 0x66, 0x63, 0x2d, \
0x64, 0x35, 0x32, 0x31, 0x2d, 0x34, 0x61, 0x66, 0x36, 0x2d, 0x38, 0x32, \
0x38, 0x33, 0x2d, 0x34, 0x62, 0x30, 0x31, 0x66, 0x39, 0x36, 0x63, 0x63, \
0x31, 0x34, 0x39, 0x22, 0x2c, 0x22, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, \
0x22, 0x3a, 0x32, 0x36, 0x31, 0x33, 0x36, 0x35, 0x2c, 0x22, 0x68, 0x65, \
0x69, 0x67, 0x68, 0x74, 0x22, 0x3a, 0x31, 0x30, 0x32, 0x34, 0x2c, 0x22, \
0x77, 0x69, 0x64, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x32, 0x38, 0x30, 0x7d, \
0x5d, 0x2c, 0x22, 0x70, 0x6b, 0x67, 0x49, 0x63, 0x6f, 0x6e, 0x73, 0x22, \
0x3a, 0x6e, 0x75, 0x6c, 0x6c, 0x2c, 0x22, 0x64, 0x65, 0x72, 0x69, 0x76, \
0x65, 0x64, 0x52, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x22, 0x3a, 0x6e, 0x75, \
0x6c, 0x6c, 0x2c, 0x22, 0x70, 0x72, 0x6f, 0x6d, 0x69, 0x6e, 0x65, 0x6e, \
0x63, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x22, 0x3a, \
0x31, 0x30, 0x30, 0x30, 0x2c, 0x22, 0x70, 0x6b, 0x67, 0x43, 0x68, 0x61, \
0x6e, 0x67, 0x65, 0x6c, 0x6f, 0x67, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, \
0x74, 0x22, 0x3a, 0x22, 0x31, 0x2e, 0x32, 0x20, 0x63, 0x68, 0x61, 0x6e, \
0x67, 0x65, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x20, \
0x62, 0x79, 0x20, 0x68, 0x72, 0x65, 0x76, 0x35, 0x30, 0x32, 0x32, 0x36, \
0x20, 0x20, 0x74, 0x6f, 0x20, 0x75, 0x73, 0x65, 0x20, 0x42, 0x75, 0x69, \
0x6c, 0x64, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x53, 0x65, 0x74, \
0x74, 0x69, 0x6e, 0x67, 0x73, 0x56, 0x69, 0x65, 0x77, 0x5c, 0x6e, 0x5c, \
0x6e, 0x31, 0x2e, 0x31, 0x20, 0x6d, 0x61, 0x6b, 0x65, 0x20, 0x73, 0x6f, \
0x6d, 0x65, 0x20, 0x6c, 0x65, 0x76, 0x65, 0x73, 0x20, 0x63, 0x68, 0x61, \
0x6e, 0x67, 0x65, 0x20, 0x6f, 0x72, 0x69, 0x65, 0x6e, 0x74, 0x61, 0x74, \
0x69, 0x6f, 0x6e, 0x5c, 0x6e, 0x5c, 0x6e, 0x31, 0x2e, 0x30, 0x20, 0x6f, \
0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x20, 0x72, 0x65, 0x6c, 0x65, \
0x61, 0x73, 0x65, 0x22, 0x7d, 0x2c, 0x7b, 0x22, 0x6e, 0x61, 0x6d, 0x65, \
0x22, 0x3a, 0x22, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x62, 0x75, \
0x69, 0x6c, 0x64, 0x65, 0x72, 0x22, 0x2c, 0x22, 0x6d, 0x6f, 0x64, 0x69, \
0x66, 0x79, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, \
0x3a, 0x31, 0x34, 0x38, 0x38, 0x38, 0x30, 0x34, 0x37, 0x30, 0x30, 0x36, \
0x33, 0x30, 0x2c, 0x22, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73, \
0x22, 0x3a, 0x5b, 0x7b, 0x22, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x22, 0x3a, \
0x22, 0x34, 0x22, 0x2c, 0x22, 0x6d, 0x69, 0x6e, 0x6f, 0x72, 0x22, 0x3a, \
0x22, 0x35, 0x22, 0x2c, 0x22, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x22, 0x3a, \
0x6e, 0x75, 0x6c, 0x6c, 0x2c, 0x22, 0x70, 0x72, 0x65, 0x52, 0x65, 0x6c, \
0x65, 0x61, 0x73, 0x65, 0x22, 0x3a, 0x6e, 0x75, 0x6c, 0x6c, 0x2c, 0x22, \
0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x3a, 0x31, 0x2c, \
0x22, 0x61, 0x72, 0x63, 0x68, 0x69, 0x74, 0x65, 0x63, 0x74, 0x75, 0x72, \
0x65, 0x43, 0x6f, 0x64, 0x65, 0x22, 0x3a, 0x22, 0x78, 0x38, 0x36, 0x5f, \
0x67, 0x63, 0x63, 0x32, 0x22, 0x2c, 0x22, 0x74, 0x69, 0x74, 0x6c, 0x65, \
0x22, 0x3a, 0x22, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x42, 0x75, \
0x69, 0x6c, 0x64, 0x65, 0x72, 0x22, 0x2c, 0x22, 0x72, 0x65, 0x70, 0x6f, \
0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, \
0x43, 0x6f, 0x64, 0x65, 0x22, 0x3a, 0x22, 0x66, 0x61, 0x74, 0x65, 0x6c, \
0x6b, 0x5f, 0x78, 0x38, 0x36, 0x5f, 0x67, 0x63, 0x63, 0x32, 0x22, 0x2c, \
0x22, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x43, \
0x6f, 0x64, 0x65, 0x22, 0x3a, 0x22, 0x66, 0x61, 0x74, 0x65, 0x6c, 0x6b, \
0x22, 0x2c, 0x22, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x22, 0x3a, \
0x22, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x20, 0x74, 0x6f, 0x20, \
0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x6f, \
0x70, 0x65, 0x6e, 0x20, 0x2e, 0x70, 0x6b, 0x67, 0x20, 0x66, 0x69, 0x6c, \
0x65, 0x73, 0x22, 0x2c, 0x22, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, \
0x74, 0x69, 0x6f, 0x6e, 0x22, 0x3a, 0x22, 0x54, 0x68, 0x69, 0x73, 0x20, \
0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, \
0x77, 0x61, 0x73, 0x20, 0x42, 0x65, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x27, \
0x73, 0x20, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x20, 0x6f, 0x66, 0x20, \
0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x2e, 0x70, 0x6b, \
0x67, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, \
0x6f, 0x6e, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x2e, 0x20, 0x49, 0x74, \
0x20, 0x77, 0x61, 0x73, 0x20, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, \
0x64, 0x20, 0x73, 0x65, 0x70, 0x61, 0x72, 0x61, 0x74, 0x65, 0x6c, 0x79, \
0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x42, 0x65, 0x4f, 0x53, 0x20, 0x61, \
0x73, 0x20, 0x70, 0x61, 0x72, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, \
0x65, 0x20, 0x64, 0x65, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x72, 0x20, \
0x74, 0x6f, 0x6f, 0x6c, 0x73, 0x2e, 0x20, 0x49, 0x74, 0x20, 0x69, 0x73, \
0x20, 0x75, 0x6e, 0x6c, 0x69, 0x6b, 0x65, 0x6c, 0x79, 0x20, 0x74, 0x68, \
0x61, 0x74, 0x20, 0x61, 0x6e, 0x79, 0x6f, 0x6e, 0x65, 0x20, 0x77, 0x69, \
0x6c, 0x6c, 0x20, 0x73, 0x74, 0x69, 0x6c, 0x6c, 0x20, 0x77, 0x61, 0x6e, \
0x74, 0x20, 0x74, 0x6f, 0x20, 0x6d, 0x61, 0x6b, 0x65, 0x20, 0x74, 0x68, \
0x65, 0x73, 0x65, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, \
0x20, 0x6e, 0x6f, 0x77, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x77, 0x65, \
0x20, 0x61, 0x72, 0x65, 0x20, 0x6d, 0x6f, 0x76, 0x69, 0x6e, 0x67, 0x20, \
0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x27, 0x68, 0x70, 0x6b, 0x67, \
0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x2e, 0x20, 0x42, 0x75, 0x74, \
0x20, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x42, 0x75, 0x69, 0x6c, \
0x64, 0x65, 0x72, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6f, \
0x6e, 0x6c, 0x79, 0x20, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x20, 0x77, 0x61, \
0x79, 0x20, 0x74, 0x6f, 0x20, 0x72, 0x69, 0x70, 0x20, 0x61, 0x20, 0x2e, \
0x70, 0x6b, 0x67, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x20, 0x61, 0x70, 0x61, \
0x72, 0x74, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x67, 0x65, 0x74, 0x20, 0x61, \
0x74, 0x20, 0x69, 0x74, 0x73, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, \
0x74, 0x73, 0x2e, 0x20, 0x49, 0x74, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, \
0x65, 0x72, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x20, 0x73, 0x74, 0x69, 0x6c, \
0x6c, 0x20, 0x6f, 0x66, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x65, 0x73, \
0x74, 0x20, 0x74, 0x6f, 0x20, 0x64, 0x65, 0x76, 0x65, 0x6c, 0x6f, 0x70, \
0x65, 0x72, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x70, 0x6f, 0x72, 0x74, \
0x65, 0x72, 0x73, 0x2e, 0x22, 0x2c, 0x22, 0x70, 0x61, 0x79, 0x6c, 0x6f, \
0x61, 0x64, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x34, 0x39, \
0x38, 0x38, 0x38, 0x36, 0x7d, 0x5d, 0x2c, 0x22, 0x70, 0x6b, 0x67, 0x43, \
0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x43, 0x6f, 0x64, 0x65, 0x73, \
0x22, 0x3a, 0x5b, 0x22, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x61, 0x6e, \
0x64, 0x75, 0x74, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x22, 0x2c, \
0x22, 0x64, 0x65, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x6d, 0x65, 0x6e, 0x74, \
0x22, 0x5d, 0x2c, 0x22, 0x70, 0x6b, 0x67, 0x53, 0x63, 0x72, 0x65, 0x65, \
0x6e, 0x73, 0x68, 0x6f, 0x74, 0x73, 0x22, 0x3a, 0x5b, 0x7b, 0x22, 0x63, \
0x6f, 0x64, 0x65, 0x22, 0x3a, 0x22, 0x65, 0x66, 0x62, 0x34, 0x32, 0x32, \
0x31, 0x34, 0x2d, 0x30, 0x31, 0x66, 0x36, 0x2d, 0x34, 0x38, 0x37, 0x63, \
0x2d, 0x61, 0x33, 0x39, 0x33, 0x2d, 0x36, 0x32, 0x61, 0x33, 0x61, 0x31, \
0x34, 0x64, 0x31, 0x36, 0x30, 0x31, 0x22, 0x2c, 0x22, 0x6c, 0x65, 0x6e, \
0x67, 0x74, 0x68, 0x22, 0x3a, 0x35, 0x34, 0x32, 0x31, 0x33, 0x2c, 0x22, \
0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0x3a, 0x33, 0x36, 0x30, 0x2c, \
0x22, 0x77, 0x69, 0x64, 0x74, 0x68, 0x22, 0x3a, 0x33, 0x32, 0x37, 0x7d, \
0x5d, 0x2c, 0x22, 0x70, 0x6b, 0x67, 0x49, 0x63, 0x6f, 0x6e, 0x73, 0x22, \
0x3a, 0x6e, 0x75, 0x6c, 0x6c, 0x2c, 0x22, 0x64, 0x65, 0x72, 0x69, 0x76, \
0x65, 0x64, 0x52, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x22, 0x3a, 0x34, 0x2e, \
0x30, 0x2c, 0x22, 0x70, 0x72, 0x6f, 0x6d, 0x69, 0x6e, 0x65, 0x6e, 0x63, \
0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x22, 0x3a, 0x31, \
0x30, 0x30, 0x2c, 0x22, 0x70, 0x6b, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x67, \
0x65, 0x6c, 0x6f, 0x67, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x22, \
0x3a, 0x6e, 0x75, 0x6c, 0x6c, 0x7d, 0x2c, 0x7b, 0x22, 0x6e, 0x61, 0x6d, \
0x65, 0x22, 0x3a, 0x22, 0x72, 0x61, 0x77, 0x61, 0x65, 0x73, 0x22, 0x2c, \
0x22, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x54, 0x69, 0x6d, 0x65, 0x73, \
0x74, 0x61, 0x6d, 0x70, 0x22, 0x3a, 0x31, 0x34, 0x39, 0x31, 0x36, 0x35, \
0x32, 0x30, 0x33, 0x39, 0x30, 0x38, 0x39, 0x2c, 0x22, 0x76, 0x65, 0x72, \
0x73, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x3a, 0x5b, 0x7b, 0x22, 0x6d, 0x61, \
0x6a, 0x6f, 0x72, 0x22, 0x3a, 0x22, 0x31, 0x22, 0x2c, 0x22, 0x6d, 0x69, \
0x6e, 0x6f, 0x72, 0x22, 0x3a, 0x22, 0x31, 0x22, 0x2c, 0x22, 0x6d, 0x69, \
0x63, 0x72, 0x6f, 0x22, 0x3a, 0x6e, 0x75, 0x6c, 0x6c, 0x2c, 0x22, 0x70, \
0x72, 0x65, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x22, 0x3a, 0x6e, \
0x75, 0x6c, 0x6c, 0x2c, 0x22, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, \
0x6e, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x61, 0x72, 0x63, 0x68, 0x69, 0x74, \
0x65, 0x63, 0x74, 0x75, 0x72, 0x65, 0x43, 0x6f, 0x64, 0x65, 0x22, 0x3a, \
0x22, 0x78, 0x38, 0x36, 0x5f, 0x67, 0x63, 0x63, 0x32, 0x22, 0x2c, 0x22, \
0x74, 0x69, 0x74, 0x6c, 0x65, 0x22, 0x3a, 0x6e, 0x75, 0x6c, 0x6c, 0x2c, \
0x22, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x53, \
0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x64, 0x65, 0x22, 0x3a, 0x22, \
0x66, 0x61, 0x74, 0x65, 0x6c, 0x6b, 0x5f, 0x78, 0x38, 0x36, 0x5f, 0x67, \
0x63, 0x63, 0x32, 0x22, 0x2c, 0x22, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, \
0x74, 0x6f, 0x72, 0x79, 0x43, 0x6f, 0x64, 0x65, 0x22, 0x3a, 0x22, 0x66, \
0x61, 0x74, 0x65, 0x6c, 0x6b, 0x22, 0x2c, 0x22, 0x73, 0x75, 0x6d, 0x6d, \
0x61, 0x72, 0x79, 0x22, 0x3a, 0x22, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, \
0x64, 0x2d, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x65, 0x6e, 0x63, 0x72, 0x79, \
0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x75, 0x74, 0x69, 0x6c, 0x69, 0x74, \
0x79, 0x22, 0x2c, 0x22, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, \
0x69, 0x6f, 0x6e, 0x22, 0x3a, 0x22, 0x52, 0x61, 0x77, 0x61, 0x65, 0x73, \
0x20, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x73, 0x20, 0x61, 0x20, \
0x66, 0x69, 0x6c, 0x65, 0x20, 0x75, 0x73, 0x69, 0x6e, 0x67, 0x20, 0x74, \
0x68, 0x65, 0x20, 0x41, 0x64, 0x76, 0x61, 0x6e, 0x64, 0x63, 0x65, 0x64, \
0x20, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, \
0x53, 0x74, 0x61, 0x6e, 0x64, 0x61, 0x72, 0x64, 0x20, 0x28, 0x41, 0x45, \
0x53, 0x29, 0x2e, 0x20, 0x20, 0x41, 0x45, 0x53, 0x20, 0x75, 0x73, 0x65, \
0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x52, 0x69, 0x6a, 0x6e, 0x64, 0x61, \
0x65, 0x6c, 0x20, 0x41, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, \
0x2c, 0x20, 0x61, 0x20, 0x31, 0x32, 0x38, 0x2d, 0x62, 0x69, 0x74, 0x20, \
0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, \
0x2c, 0x20, 0x74, 0x6f, 0x20, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, \
0x2e, 0x20, 0x20, 0x53, 0x69, 0x6e, 0x63, 0x65, 0x20, 0x74, 0x68, 0x65, \
0x20, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, \
0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x73, 0x20, 0x31, 0x36, 0x2d, \
0x62, 0x79, 0x74, 0x65, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x2c, \
0x20, 0x72, 0x61, 0x77, 0x61, 0x65, 0x73, 0x20, 0x77, 0x69, 0x6c, 0x6c, \
0x20, 0x70, 0x61, 0x64, 0x20, 0x61, 0x6e, 0x79, 0x20, 0x75, 0x6e, 0x65, \
0x76, 0x65, 0x6e, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x20, 0x77, \
0x69, 0x74, 0x68, 0x20, 0x30, 0x73, 0x2c, 0x20, 0x70, 0x6f, 0x73, 0x73, \
0x69, 0x62, 0x6c, 0x79, 0x20, 0x69, 0x6e, 0x63, 0x72, 0x65, 0x61, 0x73, \
0x69, 0x6e, 0x67, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x69, 0x7a, 0x65, \
0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, 0x69, 0x6c, 0x65, \
0x20, 0x75, 0x70, 0x20, 0x74, 0x6f, 0x20, 0x31, 0x35, 0x20, 0x62, 0x79, \
0x74, 0x65, 0x73, 0x2e, 0x22, 0x2c, 0x22, 0x70, 0x61, 0x79, 0x6c, 0x6f, \
0x61, 0x64, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x36, 0x32, \
0x37, 0x39, 0x33, 0x7d, 0x5d, 0x2c, 0x22, 0x70, 0x6b, 0x67, 0x43, 0x61, \
0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x43, 0x6f, 0x64, 0x65, 0x73, 0x22, \
0x3a, 0x5b, 0x22, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x61, 0x6e, 0x64, \
0x75, 0x74, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x22, 0x2c, 0x22, \
0x73, 0x63, 0x69, 0x65, 0x6e, 0x63, 0x65, 0x61, 0x6e, 0x64, 0x6d, 0x61, \
0x74, 0x68, 0x65, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x73, 0x22, 0x5d, 0x2c, \
0x22, 0x70, 0x6b, 0x67, 0x53, 0x63, 0x72, 0x65, 0x65, 0x6e, 0x73, 0x68, \
0x6f, 0x74, 0x73, 0x22, 0x3a, 0x5b, 0x5d, 0x2c, 0x22, 0x70, 0x6b, 0x67, \
0x49, 0x63, 0x6f, 0x6e, 0x73, 0x22, 0x3a, 0x6e, 0x75, 0x6c, 0x6c, 0x2c, \
0x22, 0x64, 0x65, 0x72, 0x69, 0x76, 0x65, 0x64, 0x52, 0x61, 0x74, 0x69, \
0x6e, 0x67, 0x22, 0x3a, 0x6e, 0x75, 0x6c, 0x6c, 0x2c, 0x22, 0x70, 0x72, \
0x6f, 0x6d, 0x69, 0x6e, 0x65, 0x6e, 0x63, 0x65, 0x4f, 0x72, 0x64, 0x65, \
0x72, 0x69, 0x6e, 0x67, 0x22, 0x3a, 0x31, 0x30, 0x30, 0x30, 0x2c, 0x22, \
0x70, 0x6b, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x6c, 0x6f, 0x67, \
0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x22, 0x3a, 0x6e, 0x75, 0x6c, \
0x6c, 0x7d, 0x2c, 0x7b, 0x22, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x3a, 0x22, \
0x74, 0x65, 0x78, 0x74, 0x73, 0x61, 0x76, 0x65, 0x72, 0x22, 0x2c, 0x22, \
0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, \
0x61, 0x6d, 0x70, 0x22, 0x3a, 0x31, 0x34, 0x39, 0x31, 0x36, 0x35, 0x32, \
0x30, 0x33, 0x39, 0x38, 0x30, 0x39, 0x2c, 0x22, 0x76, 0x65, 0x72, 0x73, \
0x69, 0x6f, 0x6e, 0x73, 0x22, 0x3a, 0x5b, 0x7b, 0x22, 0x6d, 0x61, 0x6a, \
0x6f, 0x72, 0x22, 0x3a, 0x22, 0x31, 0x22, 0x2c, 0x22, 0x6d, 0x69, 0x6e, \
0x6f, 0x72, 0x22, 0x3a, 0x22, 0x30, 0x22, 0x2c, 0x22, 0x6d, 0x69, 0x63, \
0x72, 0x6f, 0x22, 0x3a, 0x6e, 0x75, 0x6c, 0x6c, 0x2c, 0x22, 0x70, 0x72, \
0x65, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x22, 0x3a, 0x6e, 0x75, \
0x6c, 0x6c, 0x2c, 0x22, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, \
0x22, 0x3a, 0x31, 0x2c, 0x22, 0x61, 0x72, 0x63, 0x68, 0x69, 0x74, 0x65, \
0x63, 0x74, 0x75, 0x72, 0x65, 0x43, 0x6f, 0x64, 0x65, 0x22, 0x3a, 0x22, \
0x78, 0x38, 0x36, 0x5f, 0x67, 0x63, 0x63, 0x32, 0x22, 0x2c, 0x22, 0x74, \
0x69, 0x74, 0x6c, 0x65, 0x22, 0x3a, 0x22, 0x54, 0x65, 0x78, 0x74, 0x53, \
0x61, 0x76, 0x65, 0x72, 0x22, 0x2c, 0x22, 0x72, 0x65, 0x70, 0x6f, 0x73, \
0x69, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, \
0x6f, 0x64, 0x65, 0x22, 0x3a, 0x22, 0x66, 0x61, 0x74, 0x65, 0x6c, 0x6b, \
0x5f, 0x78, 0x38, 0x36, 0x5f, 0x67, 0x63, 0x63, 0x32, 0x22, 0x2c, 0x22, \
0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x43, 0x6f, \
0x64, 0x65, 0x22, 0x3a, 0x22, 0x66, 0x61, 0x74, 0x65, 0x6c, 0x6b, 0x22, \
0x2c, 0x22, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x22, 0x3a, 0x22, \
0x54, 0x65, 0x78, 0x74, 0x53, 0x61, 0x76, 0x65, 0x72, 0x20, 0x73, 0x63, \
0x72, 0x65, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x72, 0x22, 0x2c, 0x22, \
0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x22, \
0x3a, 0x22, 0x44, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x73, 0x20, 0x79, \
0x6f, 0x75, 0x72, 0x20, 0x74, 0x65, 0x78, 0x74, 0x20, 0x61, 0x74, 0x20, \
0x72, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x74, \
0x69, 0x6f, 0x6e, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x72, 0x61, 0x6e, 0x64, \
0x6f, 0x6d, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x73, 0x2e, 0x22, 0x2c, \
0x22, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x4c, 0x65, 0x6e, 0x67, \
0x74, 0x68, 0x22, 0x3a, 0x31, 0x32, 0x31, 0x36, 0x38, 0x7d, 0x5d, 0x2c, \
0x22, 0x70, 0x6b, 0x67, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, \
0x43, 0x6f, 0x64, 0x65, 0x73, 0x22, 0x3a, 0x5b, 0x22, 0x73, 0x79, 0x73, \
0x74, 0x65, 0x6d, 0x61, 0x6e, 0x64, 0x75, 0x74, 0x69, 0x6c, 0x69, 0x74, \
0x69, 0x65, 0x73, 0x22, 0x5d, 0x2c, 0x22, 0x70, 0x6b, 0x67, 0x53, 0x63, \
0x72, 0x65, 0x65, 0x6e, 0x73, 0x68, 0x6f, 0x74, 0x73, 0x22, 0x3a, 0x5b, \
0x5d, 0x2c, 0x22, 0x70, 0x6b, 0x67, 0x49, 0x63, 0x6f, 0x6e, 0x73, 0x22, \
0x3a, 0x6e, 0x75, 0x6c, 0x6c, 0x2c, 0x22, 0x64, 0x65, 0x72, 0x69, 0x76, \
0x65, 0x64, 0x52, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x22, 0x3a, 0x6e, 0x75, \
0x6c, 0x6c, 0x2c, 0x22, 0x70, 0x72, 0x6f, 0x6d, 0x69, 0x6e, 0x65, 0x6e, \
0x63, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x22, 0x3a, \
0x31, 0x30, 0x30, 0x30, 0x2c, 0x22, 0x70, 0x6b, 0x67, 0x43, 0x68, 0x61, \
0x6e, 0x67, 0x65, 0x6c, 0x6f, 0x67, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, \
0x74, 0x22, 0x3a, 0x6e, 0x75, 0x6c, 0x6c, 0x7d, 0x2c, 0x7b, 0x22, 0x6e, \
0x61, 0x6d, 0x65, 0x22, 0x3a, 0x22, 0x76, 0x6e, 0x63, 0x73, 0x65, 0x72, \
0x76, 0x65, 0x72, 0x22, 0x2c, 0x22, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, \
0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x3a, 0x31, \
0x34, 0x39, 0x31, 0x36, 0x35, 0x32, 0x30, 0x34, 0x30, 0x32, 0x34, 0x34, \
0x2c, 0x22, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x3a, \
0x5b, 0x7b, 0x22, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x22, 0x3a, 0x22, 0x76, \
0x6e, 0x63, 0x34, 0x5f, 0x30, 0x22, 0x2c, 0x22, 0x6d, 0x69, 0x6e, 0x6f, \
0x72, 0x22, 0x3a, 0x22, 0x61, 0x67, 0x6d, 0x73, 0x5f, 0x31, 0x22, 0x2c, \
0x22, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x22, 0x3a, 0x22, 0x32, 0x37, 0x22, \
0x2c, 0x22, 0x70, 0x72, 0x65, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, \
0x22, 0x3a, 0x6e, 0x75, 0x6c, 0x6c, 0x2c, 0x22, 0x72, 0x65, 0x76, 0x69, \
0x73, 0x69, 0x6f, 0x6e, 0x22, 0x3a, 0x32, 0x2c, 0x22, 0x61, 0x72, 0x63, \
0x68, 0x69, 0x74, 0x65, 0x63, 0x74, 0x75, 0x72, 0x65, 0x43, 0x6f, 0x64, \
0x65, 0x22, 0x3a, 0x22, 0x78, 0x38, 0x36, 0x5f, 0x67, 0x63, 0x63, 0x32, \
0x22, 0x2c, 0x22, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x22, 0x3a, 0x22, 0x56, \
0x4e, 0x43, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x22, 0x2c, 0x22, 0x72, \
0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x6f, 0x75, \
0x72, 0x63, 0x65, 0x43, 0x6f, 0x64, 0x65, 0x22, 0x3a, 0x22, 0x66, 0x61, \
0x74, 0x65, 0x6c, 0x6b, 0x5f, 0x78, 0x38, 0x36, 0x5f, 0x67, 0x63, 0x63, \
0x32, 0x22, 0x2c, 0x22, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, \
0x72, 0x79, 0x43, 0x6f, 0x64, 0x65, 0x22, 0x3a, 0x22, 0x66, 0x61, 0x74, \
0x65, 0x6c, 0x6b, 0x22, 0x2c, 0x22, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, \
0x79, 0x22, 0x3a, 0x22, 0x56, 0x4e, 0x43, 0x53, 0x65, 0x72, 0x76, 0x65, \
0x72, 0x20, 0x6c, 0x65, 0x74, 0x73, 0x20, 0x79, 0x6f, 0x75, 0x20, 0x75, \
0x73, 0x65, 0x20, 0x79, 0x6f, 0x75, 0x72, 0x20, 0x42, 0x65, 0x4f, 0x53, \
0x20, 0x2f, 0x20, 0x48, 0x61, 0x69, 0x6b, 0x75, 0x20, 0x63, 0x6f, 0x6d, \
0x70, 0x75, 0x74, 0x65, 0x72, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x61, \
0x6e, 0x79, 0x77, 0x68, 0x65, 0x72, 0x65, 0x20, 0x74, 0x68, 0x65, 0x72, \
0x65, 0x20, 0x69, 0x73, 0x20, 0x61, 0x6e, 0x20, 0x49, 0x6e, 0x74, 0x65, \
0x72, 0x6e, 0x65, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, \
0x69, 0x6f, 0x6e, 0x2e, 0x20, 0x20, 0x59, 0x6f, 0x75, 0x20, 0x63, 0x61, \
0x6e, 0x20, 0x74, 0x68, 0x69, 0x6e, 0x6b, 0x20, 0x6f, 0x66, 0x20, 0x69, \
0x74, 0x20, 0x61, 0x73, 0x20, 0x61, 0x20, 0x72, 0x65, 0x61, 0x6c, 0x6c, \
0x79, 0x20, 0x6c, 0x6f, 0x6e, 0x67, 0x20, 0x6b, 0x65, 0x79, 0x62, 0x6f, \
0x61, 0x72, 0x64, 0x2c, 0x20, 0x6d, 0x6f, 0x75, 0x73, 0x65, 0x20, 0x61, \
0x6e, 0x64, 0x20, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x20, 0x63, 0x61, 0x62, \
0x6c, 0x65, 0x2e, 0x22, 0x2c, 0x22, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, \
0x70, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x3a, 0x22, 0x41, 0x20, 0x56, 0x4e, \
0x43, 0x20, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x20, 0x28, 0x61, 0x76, \
0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x65, 0x6c, 0x73, 0x65, \
0x77, 0x68, 0x65, 0x72, 0x65, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x57, 0x69, \
0x6e, 0x64, 0x6f, 0x77, 0x73, 0x2c, 0x20, 0x4d, 0x61, 0x63, 0x2c, 0x20, \
0x4c, 0x69, 0x6e, 0x75, 0x78, 0x2c, 0x20, 0x6f, 0x74, 0x68, 0x65, 0x72, \
0x73, 0x29, 0x20, 0x73, 0x68, 0x6f, 0x77, 0x73, 0x20, 0x79, 0x6f, 0x75, \
0x20, 0x77, 0x68, 0x61, 0x74, 0x27, 0x73, 0x20, 0x6f, 0x6e, 0x20, 0x74, \
0x68, 0x65, 0x20, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x20, 0x42, 0x65, \
0x4f, 0x53, 0x20, 0x2f, 0x20, 0x48, 0x61, 0x69, 0x6b, 0x75, 0x20, 0x63, \
0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x72, 0x27, 0x73, 0x20, 0x73, 0x63, \
0x72, 0x65, 0x65, 0x6e, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x73, 0x65, 0x6e, \
0x64, 0x73, 0x20, 0x6b, 0x65, 0x79, 0x73, 0x74, 0x72, 0x6f, 0x6b, 0x65, \
0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x6d, 0x6f, 0x75, 0x73, 0x65, 0x20, \
0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x6f, 0x76, 0x65, 0x72, \
0x20, 0x74, 0x68, 0x65, 0x20, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, \
0x74, 0x2e, 0x20, 0x20, 0x54, 0x68, 0x65, 0x20, 0x56, 0x4e, 0x43, 0x53, \
0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x6f, 0x66, 0x74, 0x77, 0x61, \
0x72, 0x65, 0x20, 0x72, 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x6f, \
0x6e, 0x20, 0x42, 0x65, 0x4f, 0x53, 0x20, 0x6f, 0x72, 0x20, 0x48, 0x61, \
0x69, 0x6b, 0x75, 0x20, 0x75, 0x73, 0x65, 0x73, 0x20, 0x74, 0x68, 0x61, \
0x74, 0x20, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x20, 0x64, 0x61, 0x74, \
0x61, 0x20, 0x74, 0x6f, 0x20, 0x73, 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, \
0x65, 0x20, 0x62, 0x75, 0x74, 0x74, 0x6f, 0x6e, 0x20, 0x70, 0x72, 0x65, \
0x73, 0x73, 0x65, 0x73, 0x20, 0x6f, 0x6e, 0x20, 0x61, 0x20, 0x66, 0x61, \
0x6b, 0x65, 0x20, 0x6b, 0x65, 0x79, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x20, \
0x61, 0x6e, 0x64, 0x20, 0x6d, 0x6f, 0x76, 0x65, 0x6d, 0x65, 0x6e, 0x74, \
0x73, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x6e, 0x20, 0x69, 0x6d, 0x61, 0x67, \
0x69, 0x6e, 0x61, 0x72, 0x79, 0x20, 0x6d, 0x6f, 0x75, 0x73, 0x65, 0x2e, \
0x20, 0x20, 0x49, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6f, 0x70, 0x70, \
0x6f, 0x73, 0x69, 0x74, 0x65, 0x20, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, \
0x69, 0x6f, 0x6e, 0x2c, 0x20, 0x56, 0x4e, 0x43, 0x53, 0x65, 0x72, 0x76, \
0x65, 0x72, 0x20, 0x73, 0x63, 0x61, 0x6e, 0x73, 0x20, 0x79, 0x6f, 0x75, \
0x72, 0x20, 0x73, 0x63, 0x72, 0x65, 0x65, 0x6e, 0x20, 0x66, 0x6f, 0x72, \
0x20, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x2c, 0x20, 0x63, 0x6f, \
0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x20, 0x74, 0x68, 0x65, \
0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x67, \
0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x20, 0x64, 0x61, 0x74, 0x61, \
0x20, 0x61, 0x6e, 0x64, 0x20, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x6d, 0x69, \
0x74, 0x73, 0x20, 0x69, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, \
0x20, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2e, 0x5c, 0x6e, 0x5c, 0x6e, \
0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x70, 0x6f, \
0x72, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x56, 0x4e, 0x43, 0x20, 0x75, 0x73, \
0x69, 0x6e, 0x67, 0x20, 0x52, 0x65, 0x61, 0x6c, 0x56, 0x4e, 0x43, 0x27, \
0x73, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x34, 0x2e, \
0x30, 0x20, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x20, 0x73, 0x6f, 0x75, 0x72, \
0x63, 0x65, 0x20, 0x63, 0x6f, 0x64, 0x65, 0x20, 0x28, 0x77, 0x68, 0x69, \
0x63, 0x68, 0x20, 0x68, 0x61, 0x73, 0x20, 0x61, 0x6e, 0x20, 0x65, 0x78, \
0x74, 0x72, 0x65, 0x6d, 0x65, 0x6c, 0x79, 0x20, 0x77, 0x65, 0x6c, 0x6c, \
0x20, 0x64, 0x65, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x20, 0x63, 0x6c, \
0x61, 0x73, 0x73, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x75, 0x72, \
0x65, 0x2c, 0x20, 0x6d, 0x61, 0x6b, 0x69, 0x6e, 0x67, 0x20, 0x69, 0x74, \
0x20, 0x65, 0x61, 0x73, 0x79, 0x20, 0x74, 0x6f, 0x20, 0x64, 0x6f, 0x20, \
0x74, 0x68, 0x69, 0x73, 0x20, 0x70, 0x6f, 0x72, 0x74, 0x29, 0x2e, 0x20, \
0x20, 0x54, 0x68, 0x65, 0x72, 0x65, 0x20, 0x61, 0x72, 0x65, 0x20, 0x6c, \
0x6f, 0x74, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x56, 0x4e, 0x43, 0x20, 0x63, \
0x6c, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x6f, 0x75, 0x74, 0x20, 0x74, \
0x68, 0x65, 0x72, 0x65, 0x2c, 0x20, 0x62, 0x75, 0x74, 0x20, 0x49, 0x20, \
0x63, 0x61, 0x6e, 0x20, 0x72, 0x65, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, \
0x64, 0x20, 0x74, 0x68, 0x65, 0x20, 0x52, 0x65, 0x61, 0x6c, 0x56, 0x4e, \
0x43, 0x20, 0x6f, 0x6e, 0x65, 0x73, 0x20, 0x61, 0x73, 0x20, 0x77, 0x6f, \
0x72, 0x6b, 0x69, 0x6e, 0x67, 0x20, 0x76, 0x65, 0x72, 0x79, 0x20, 0x77, \
0x65, 0x6c, 0x6c, 0x20, 0x75, 0x6e, 0x64, 0x65, 0x72, 0x20, 0x57, 0x69, \
0x6e, 0x64, 0x6f, 0x77, 0x73, 0x2e, 0x20, 0x20, 0x59, 0x6f, 0x75, 0x20, \
0x63, 0x61, 0x6e, 0x20, 0x67, 0x65, 0x74, 0x20, 0x74, 0x68, 0x65, 0x69, \
0x72, 0x20, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x2c, 0x20, 0x73, \
0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x73, \
0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, 0x63, 0x6f, 0x64, 0x65, 0x20, 0x61, \
0x74, 0x20, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, \
0x2e, 0x72, 0x65, 0x61, 0x6c, 0x76, 0x6e, 0x63, 0x2e, 0x63, 0x6f, 0x6d, \
0x2f, 0x22, 0x2c, 0x22, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x4c, \
0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x32, 0x32, 0x34, 0x35, 0x37, \
0x32, 0x7d, 0x5d, 0x2c, 0x22, 0x70, 0x6b, 0x67, 0x43, 0x61, 0x74, 0x65, \
0x67, 0x6f, 0x72, 0x79, 0x43, 0x6f, 0x64, 0x65, 0x73, 0x22, 0x3a, 0x5b, \
0x22, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x61, 0x6e, 0x64, \
0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x22, 0x5d, 0x2c, 0x22, 0x70, \
0x6b, 0x67, 0x53, 0x63, 0x72, 0x65, 0x65, 0x6e, 0x73, 0x68, 0x6f, 0x74, \
0x73, 0x22, 0x3a, 0x5b, 0x5d, 0x2c, 0x22, 0x70, 0x6b, 0x67, 0x49, 0x63, \
0x6f, 0x6e, 0x73, 0x22, 0x3a, 0x6e, 0x75, 0x6c, 0x6c, 0x2c, 0x22, 0x64, \
0x65, 0x72, 0x69, 0x76, 0x65, 0x64, 0x52, 0x61, 0x74, 0x69, 0x6e, 0x67, \
0x22, 0x3a, 0x6e, 0x75, 0x6c, 0x6c, 0x2c, 0x22, 0x70, 0x72, 0x6f, 0x6d, \
0x69, 0x6e, 0x65, 0x6e, 0x63, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x69, \
0x6e, 0x67, 0x22, 0x3a, 0x31, 0x30, 0x30, 0x30, 0x2c, 0x22, 0x70, 0x6b, \
0x67, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x6c, 0x6f, 0x67, 0x43, 0x6f, \
0x6e, 0x74, 0x65, 0x6e, 0x74, 0x22, 0x3a, 0x6e, 0x75, 0x6c, 0x6c, 0x7d, \
0x5d, 0x7d, 0x7d, 0x0a, 0x00 \
}
#endif // JSON_SAMPLES_H

View File

@ -0,0 +1,207 @@
/*
* Copyright 2017, Andrew Lindesay <apl@lindesay.co.nz>
* Distributed under the terms of the MIT License.
*/
#include "JsonTextWriterTest.h"
#include <AutoDeleter.h>
#include <Json.h>
#include <JsonTextWriter.h>
#include <cppunit/TestCaller.h>
#include <cppunit/TestSuite.h>
#include "JsonSamples.h"
#define LOOPS_TEST_OBJECT_A_FOR_PERFORMANCE 100000
using namespace BPrivate;
JsonTextWriterTest::JsonTextWriterTest()
{
}
JsonTextWriterTest::~JsonTextWriterTest()
{
}
void
JsonTextWriterTest::TestArrayA()
{
BMallocIO* outputData = new BMallocIO();
ObjectDeleter<BMallocIO> outputDataDeleter(outputData);
BJsonTextWriter writer(outputData);
CPPUNIT_ASSERT_EQUAL(B_OK, writer.WriteArrayStart());
CPPUNIT_ASSERT_EQUAL(B_OK, writer.WriteString("1234"));
CPPUNIT_ASSERT_EQUAL(B_OK, writer.WriteInteger(4567));
CPPUNIT_ASSERT_EQUAL(B_OK, writer.WriteArrayStart());
CPPUNIT_ASSERT_EQUAL(B_OK, writer.WriteString("A"));
CPPUNIT_ASSERT_EQUAL(B_OK, writer.WriteString("b"));
CPPUNIT_ASSERT_EQUAL(B_OK, writer.WriteString("C\xc3\xa9zanne"));
CPPUNIT_ASSERT_EQUAL(B_OK, writer.WriteArrayEnd());
CPPUNIT_ASSERT_EQUAL(B_OK, writer.WriteArrayEnd());
writer.Complete();
BString outputString((char*)outputData->Buffer(),
outputData->BufferLength());
fprintf(stderr, "expected out >%s<\n", JSON_SAMPLE_ARRAY_A_EXPECTED_OUT);
fprintf(stderr, "actual out >%s< (%" B_PRIuSIZE ")\n", outputString.String(),
outputData->BufferLength());
CPPUNIT_ASSERT_EQUAL(BString(JSON_SAMPLE_ARRAY_A_EXPECTED_OUT),
outputString);
}
void
JsonTextWriterTest::TestObjectA()
{
BMallocIO* outputData = new BMallocIO();
ObjectDeleter<BMallocIO> outputDataDeleter(outputData);
BJsonTextWriter writer(outputData);
CPPUNIT_ASSERT_EQUAL(B_OK, writer.WriteObjectStart());
CPPUNIT_ASSERT_EQUAL(B_OK, writer.WriteObjectName("weather"));
CPPUNIT_ASSERT_EQUAL(B_OK, writer.WriteString("raining"));
CPPUNIT_ASSERT_EQUAL(B_OK, writer.WriteObjectName("humidity"));
CPPUNIT_ASSERT_EQUAL(B_OK, writer.WriteString("too-high"));
CPPUNIT_ASSERT_EQUAL(B_OK, writer.WriteObjectName("daysOfWeek"));
CPPUNIT_ASSERT_EQUAL(B_OK, writer.WriteArrayStart());
CPPUNIT_ASSERT_EQUAL(B_OK, writer.WriteString("MON"));
CPPUNIT_ASSERT_EQUAL(B_OK, writer.WriteString("TUE"));
CPPUNIT_ASSERT_EQUAL(B_OK, writer.WriteString("WED"));
CPPUNIT_ASSERT_EQUAL(B_OK, writer.WriteString("THR"));
CPPUNIT_ASSERT_EQUAL(B_OK, writer.WriteString("FRI"));
CPPUNIT_ASSERT_EQUAL(B_OK, writer.WriteArrayEnd());
CPPUNIT_ASSERT_EQUAL(B_OK, writer.WriteObjectEnd());
writer.Complete();
BString outputString((char*)outputData->Buffer(),
outputData->BufferLength());
fprintf(stderr, "expected out >%s<\n", JSON_SAMPLE_OBJECT_A_EXPECTED_OUT);
fprintf(stderr, "actual out >%s< (%" B_PRIuSIZE ")\n", outputString.String(),
outputData->BufferLength());
CPPUNIT_ASSERT_EQUAL(BString(JSON_SAMPLE_OBJECT_A_EXPECTED_OUT),
outputString);
}
void
JsonTextWriterTest::TestObjectAForPerformance()
{
int i;
for (i = 0; i < LOOPS_TEST_OBJECT_A_FOR_PERFORMANCE; i++) {
TestObjectA();
}
}
void
JsonTextWriterTest::TestInteger()
{
BMallocIO* outputData = new BMallocIO();
ObjectDeleter<BMallocIO> outputDataDeleter(outputData);
BJsonTextWriter writer(outputData);
static char* expectedOut = JSON_SAMPLE_NUMBER_B_EXPECTED_OUT;
CPPUNIT_ASSERT_EQUAL(B_OK,
writer.WriteInteger(JSON_SAMPLE_NUMBER_B_LITERAL));
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::TestDouble()
{
BMallocIO* outputData = new BMallocIO();
ObjectDeleter<BMallocIO> outputDataDeleter(outputData);
BJsonTextWriter writer(outputData);
static char* expectedOut = "3.142857";
CPPUNIT_ASSERT_EQUAL(B_OK,
writer.WriteDouble(JSON_SAMPLE_NUMBER_A_LITERAL));
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::TestFalse()
{
BMallocIO* outputData = new BMallocIO();
ObjectDeleter<BMallocIO> outputDataDeleter(outputData);
BJsonTextWriter writer(outputData);
static char* expectedOut = "false";
CPPUNIT_ASSERT_EQUAL(B_OK, writer.WriteFalse());
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);
}
/*static*/ void
JsonTextWriterTest::AddTests(BTestSuite& parent)
{
CppUnit::TestSuite& suite = *new CppUnit::TestSuite(
"JsonTextWriterTest");
suite.addTest(new CppUnit::TestCaller<JsonTextWriterTest>(
"JsonTextWriterTest::TestDouble",
&JsonTextWriterTest::TestDouble));
suite.addTest(new CppUnit::TestCaller<JsonTextWriterTest>(
"JsonTextWriterTest::TestInteger",
&JsonTextWriterTest::TestInteger));
suite.addTest(new CppUnit::TestCaller<JsonTextWriterTest>(
"JsonTextWriterTest::TestFalse",
&JsonTextWriterTest::TestFalse));
suite.addTest(new CppUnit::TestCaller<JsonTextWriterTest>(
"JsonTextWriterTest::TestArrayA",
&JsonTextWriterTest::TestArrayA));
suite.addTest(new CppUnit::TestCaller<JsonTextWriterTest>(
"JsonTextWriterTest::TestObjectA",
&JsonTextWriterTest::TestObjectA));
// suite.addTest(new CppUnit::TestCaller<JsonTextWriterTest>(
// "JsonTextWriterTest::TestObjectAForPerformance",
// &JsonTextWriterTest::TestObjectAForPerformance));
parent.addTest("JsonTextWriterTest", &suite);
}

View File

@ -0,0 +1,35 @@
/*
* Copyright 2017, Andrew Lindesay <apl@lindesay.co.nz>
* Distributed under the terms of the MIT License.
*/
#ifndef JSON_WRITER_TEST_H
#define JSON_WRITER_TEST_H
#include <TestCase.h>
#include <TestSuite.h>
#include <DataIO.h>
class Sample;
class JsonTextWriterTest : public CppUnit::TestCase {
public:
JsonTextWriterTest();
virtual ~JsonTextWriterTest();
void TestArrayA();
void TestObjectA();
void TestObjectAForPerformance();
void TestDouble();
void TestInteger();
void TestFalse();
static void AddTests(BTestSuite& suite);
private:
};
#endif // JSON_WRITER_TEST_H

View File

@ -0,0 +1,292 @@
/*
* Copyright 2017, Andrew Lindesay <apl@lindesay.co.nz>
* Distributed under the terms of the MIT License.
*/
#include "JsonToMessageTest.h"
#include <Json.h>
#include <cppunit/TestCaller.h>
#include <cppunit/TestSuite.h>
#include "JsonSamples.h"
#define LOOPS_TEST_OBJECT_A_FOR_PERFORMANCE 100000
using namespace BPrivate;
JsonToMessageTest::JsonToMessageTest()
{
}
JsonToMessageTest::~JsonToMessageTest()
{
}
void
JsonToMessageTest::TestArrayA()
{
BMessage message;
BMessage subMessage;
BString stringValue;
double doubleValue;
// ----------------------
BJson::Parse(JSON_SAMPLE_ARRAY_A_IN, message);
// ----------------------
CPPUNIT_ASSERT_EQUAL_MESSAGE("!find [0]", B_OK,
message.FindString("0", &stringValue));
CPPUNIT_ASSERT_EQUAL_MESSAGE("!eq [0]", BString("1234"), stringValue);
CPPUNIT_ASSERT_EQUAL_MESSAGE("!find [1]", B_OK,
message.FindDouble("1", &doubleValue));
CPPUNIT_ASSERT_EQUAL_MESSAGE("!eq [1]", 4567, doubleValue);
CPPUNIT_ASSERT_EQUAL_MESSAGE("!find [2]", B_OK,
message.FindMessage("2", &subMessage));
CPPUNIT_ASSERT_EQUAL_MESSAGE("!find [2.0]", B_OK,
subMessage.FindString("0", &stringValue));
CPPUNIT_ASSERT_EQUAL_MESSAGE("!eq [2.0]", BString("A"), stringValue);
CPPUNIT_ASSERT_EQUAL_MESSAGE("!find [2.1]", B_OK,
subMessage.FindString("1", &stringValue));
CPPUNIT_ASSERT_EQUAL_MESSAGE("!eq [2.1]", BString("b"), stringValue);
CPPUNIT_ASSERT_EQUAL_MESSAGE("!find [2.2]", B_OK,
subMessage.FindString("2", &stringValue));
CPPUNIT_ASSERT_EQUAL_MESSAGE("!eq [2.2]", BString("C\xc3\xa9zanne"),
stringValue);
}
void
JsonToMessageTest::TestArrayB()
{
BMessage message;
BMessage subMessage;
BMessage subSubMessage;
BString stringValue;
// ----------------------
BJson::Parse(JSON_SAMPLE_ARRAY_B_IN, message);
// ----------------------
CPPUNIT_ASSERT_EQUAL_MESSAGE("!find [0]", B_OK,
message.FindString("0", &stringValue));
CPPUNIT_ASSERT_EQUAL_MESSAGE("!eq [0]", BString("Whirinaki"), stringValue);
CPPUNIT_ASSERT_EQUAL_MESSAGE("!find [1]", B_OK,
message.FindString("1", &stringValue));
CPPUNIT_ASSERT_EQUAL_MESSAGE("!eq [1]", BString("Wellington"), stringValue);
CPPUNIT_ASSERT_EQUAL_MESSAGE("!find [2]", B_OK,
message.FindMessage("2", &subMessage));
CPPUNIT_ASSERT_EQUAL_MESSAGE("!find [2.0]", B_OK,
subMessage.FindMessage("key", &subSubMessage));
CPPUNIT_ASSERT_EQUAL_MESSAGE("!find [2.0.0]", B_OK,
subSubMessage.FindString("0", &stringValue));
CPPUNIT_ASSERT_EQUAL_MESSAGE("!eq [2.0.0]", BString("value"), stringValue);
}
void
JsonToMessageTest::TestObjectA()
{
BMessage message;
BMessage subMessage;
BString stringValue;
// ----------------------
BJson::Parse(JSON_SAMPLE_OBJECT_A_IN, message);
// ----------------------
CPPUNIT_ASSERT_EQUAL(B_OK, message.FindString("weather", &stringValue));
CPPUNIT_ASSERT_EQUAL(BString("raining"), stringValue);
CPPUNIT_ASSERT_EQUAL(B_OK, message.FindString("humidity", &stringValue));
CPPUNIT_ASSERT_EQUAL(BString("too-high"), stringValue);
CPPUNIT_ASSERT_EQUAL(B_OK, message.FindMessage("daysOfWeek", &subMessage));
CPPUNIT_ASSERT_EQUAL(B_OK, subMessage.FindString("0", &stringValue));
CPPUNIT_ASSERT_EQUAL(BString("MON"), stringValue);
CPPUNIT_ASSERT_EQUAL(B_OK, subMessage.FindString("1", &stringValue));
CPPUNIT_ASSERT_EQUAL(BString("TUE"), stringValue);
CPPUNIT_ASSERT_EQUAL(B_OK, subMessage.FindString("2", &stringValue));
CPPUNIT_ASSERT_EQUAL(BString("WED"), stringValue);
CPPUNIT_ASSERT_EQUAL(B_OK, subMessage.FindString("3", &stringValue));
CPPUNIT_ASSERT_EQUAL(BString("THR"), stringValue);
CPPUNIT_ASSERT_EQUAL(B_OK, subMessage.FindString("4", &stringValue));
CPPUNIT_ASSERT_EQUAL(BString("FRI"), stringValue);
}
/*! This is not a real test, but is a convenient point at which to implement a
performance test.
*/
void
JsonToMessageTest::TestObjectAForPerformance()
{
int i;
for (i=0; i < LOOPS_TEST_OBJECT_A_FOR_PERFORMANCE; i++)
TestObjectA();
}
void
JsonToMessageTest::TestObjectB()
{
BMessage message;
bool boolValue;
void *ptrValue; // this is how NULL is represented.
// ----------------------
BJson::Parse(JSON_SAMPLE_OBJECT_B_IN, message);
// ----------------------
CPPUNIT_ASSERT_EQUAL(B_OK, message.FindBool("testTrue", &boolValue));
CPPUNIT_ASSERT_EQUAL(true, boolValue);
CPPUNIT_ASSERT_EQUAL(B_OK, message.FindBool("testFalse", &boolValue));
CPPUNIT_ASSERT_EQUAL(false, boolValue);
CPPUNIT_ASSERT_EQUAL(B_OK, message.FindPointer("testNull", &ptrValue));
CPPUNIT_ASSERT_EQUAL(0, (uint32) ptrValue);
}
void
JsonToMessageTest::TestObjectC()
{
BMessage message;
BMessage subMessage;
BString stringValue;
// ----------------------
BJson::Parse(JSON_SAMPLE_OBJECT_C_IN, message);
// ----------------------
CPPUNIT_ASSERT_EQUAL(B_OK, message.FindString("key", &stringValue));
CPPUNIT_ASSERT_EQUAL(BString("value"), stringValue);
}
void
JsonToMessageTest::TestUnterminatedObject()
{
BMessage message;
// ----------------------
status_t result = BJson::Parse(JSON_SAMPLE_BROKEN_UNTERMINATED_OBJECT,
message);
// ----------------------
CPPUNIT_ASSERT_EQUAL(B_BAD_DATA, result);
}
void
JsonToMessageTest::TestUnterminatedArray()
{
BMessage message;
// ----------------------
status_t result = BJson::Parse(JSON_SAMPLE_BROKEN_UNTERMINATED_ARRAY,
message);
// ----------------------
CPPUNIT_ASSERT_EQUAL(B_BAD_DATA, result);
}
void JsonToMessageTest::TestHaikuDepotFetchBatch()
{
const unsigned char input[] = JSON_SAMPLE_HDS_FETCH_BATCH_PKGS;
BMessage message;
BMessage resultMessage;
BMessage pkgsMessage;
BMessage pkgMessage1;
double modifyTimestampDouble;
double expectedModifyTimestampDouble = 1488785331631.0;
// ----------------------
status_t result = BJson::Parse((char*)input,
message);
// ----------------------
CPPUNIT_ASSERT_EQUAL(B_OK, result);
// this is quite a large test input so a random sample "thing to
// check" is chosen to indicate that the parse was successful.
CPPUNIT_ASSERT_EQUAL_MESSAGE("result", B_OK,
message.FindMessage("result", &resultMessage));
CPPUNIT_ASSERT_EQUAL_MESSAGE("pkgs", B_OK,
resultMessage.FindMessage("pkgs", &pkgsMessage));
CPPUNIT_ASSERT_EQUAL_MESSAGE("1", B_OK,
pkgsMessage.FindMessage("1", &pkgMessage1));
CPPUNIT_ASSERT_EQUAL_MESSAGE("modifyTimestamp", B_OK,
pkgMessage1.FindDouble("modifyTimestamp", &modifyTimestampDouble));
CPPUNIT_ASSERT_EQUAL(expectedModifyTimestampDouble, modifyTimestampDouble);
}
/*static*/ void
JsonToMessageTest::AddTests(BTestSuite& parent)
{
CppUnit::TestSuite& suite = *new CppUnit::TestSuite(
"JsonToMessageTest");
suite.addTest(new CppUnit::TestCaller<JsonToMessageTest>(
"JsonToMessageTest::TestArrayA",
&JsonToMessageTest::TestArrayA));
suite.addTest(new CppUnit::TestCaller<JsonToMessageTest>(
"JsonToMessageTest::TestArrayB",
&JsonToMessageTest::TestArrayB));
suite.addTest(new CppUnit::TestCaller<JsonToMessageTest>(
"JsonToMessageTest::TestObjectA",
&JsonToMessageTest::TestObjectA));
suite.addTest(new CppUnit::TestCaller<JsonToMessageTest>(
"JsonToMessageTest::TestObjectB",
&JsonToMessageTest::TestObjectB));
suite.addTest(new CppUnit::TestCaller<JsonToMessageTest>(
"JsonToMessageTest::TestObjectC",
&JsonToMessageTest::TestObjectC));
suite.addTest(new CppUnit::TestCaller<JsonToMessageTest>(
"JsonToMessageTest::TestUnterminatedObject",
&JsonToMessageTest::TestUnterminatedObject));
suite.addTest(new CppUnit::TestCaller<JsonToMessageTest>(
"JsonToMessageTest::TestUnterminatedArray",
&JsonToMessageTest::TestUnterminatedArray));
suite.addTest(new CppUnit::TestCaller<JsonToMessageTest>(
"JsonToMessageTest::TestHaikuDepotFetchBatch",
&JsonToMessageTest::TestHaikuDepotFetchBatch));
// suite.addTest(new CppUnit::TestCaller<JsonToMessageTest>(
// "JsonToMessageTest::TestObjectAForPerformance",
// &JsonToMessageTest::TestObjectAForPerformance));
parent.addTest("JsonToMessageTest", &suite);
}

View File

@ -0,0 +1,38 @@
/*
* Copyright 2017, Andrew Lindesay <apl@lindesay.co.nz>
* Distributed under the terms of the MIT License.
*/
#ifndef JSON_TO_MESSAGE_TEST_H
#define JSON_TO_MESSAGE_TEST_H
#include <TestCase.h>
#include <TestSuite.h>
#include <DataIO.h>
class Sample;
class JsonToMessageTest : public CppUnit::TestCase {
public:
JsonToMessageTest();
virtual ~JsonToMessageTest();
void TestArrayA();
void TestArrayB();
void TestObjectAForPerformance();
void TestObjectA();
void TestObjectB();
void TestObjectC();
void TestUnterminatedObject();
void TestUnterminatedArray();
void TestHaikuDepotFetchBatch();
static void AddTests(BTestSuite& suite);
private:
};
#endif // JSON_TO_MESSAGE_TEST_H

View File

@ -1,4 +1,5 @@
/*
* Copyright 2017, Andrew Lindesay, apl@lindesay.co.nz
* Copyright 2012-2015, Axel Dörfler, axeld@pinc-software.de.
* Distributed under the terms of the MIT License.
*/
@ -11,6 +12,10 @@
#include "DriverSettingsMessageAdapterTest.h"
#include "GeolocationTest.h"
#include "NaturalCompareTest.h"
#include "JsonEndToEndTest.h"
#include "JsonErrorHandlingTest.h"
#include "JsonTextWriterTest.h"
#include "JsonToMessageTest.h"
BTestSuite*
@ -22,6 +27,10 @@ getTestSuite()
DriverSettingsMessageAdapterTest::AddTests(*suite);
GeolocationTest::AddTests(*suite);
NaturalCompareTest::AddTests(*suite);
JsonEndToEndTest::AddTests(*suite);
JsonErrorHandlingTest::AddTests(*suite);
JsonTextWriterTest::AddTests(*suite);
JsonToMessageTest::AddTests(*suite);
return suite;
}