RemoteDrawingEngine: Reduce RP_READ_BITMAP result timeout.
[haiku.git] / src / kits / shared / JsonTextWriter.cpp
blob35c95122ff01678c816523c4ab24f8750a392dc6
1 /*
2 * Copyright 2017, Andrew Lindesay <apl@lindesay.co.nz>
3 * Distributed under the terms of the MIT License.
4 */
7 #include "JsonTextWriter.h"
9 #include <stdio.h>
10 #include <stdlib.h>
12 #include <UnicodeChar.h>
15 namespace BPrivate {
18 static bool
19 b_json_is_7bit_clean(uint8 c)
21 return c >= 0x20 && c < 0x7f;
25 static bool
26 b_json_is_illegal(uint8 c)
28 return c < 0x20 || c == 0x7f;
32 static const char*
33 b_json_simple_esc_sequence(char c)
35 switch (c) {
36 case '"':
37 return "\\\"";
38 case '\\':
39 return "\\\\";
40 case '/':
41 return "\\/";
42 case '\b':
43 return "\\b";
44 case '\f':
45 return "\\f";
46 case '\n':
47 return "\\n";
48 case '\r':
49 return "\\r";
50 case '\t':
51 return "\\t";
52 default:
53 return NULL;
58 /*! The class and sub-classes of it are used as a stack internal to the
59 BJsonTextWriter class. As the JSON is parsed, the stack of these
60 internal listeners follows the stack of the JSON parsing in terms of
61 containers; arrays and objects.
64 class BJsonTextWriterStackedEventListener : public BJsonEventListener {
65 public:
66 BJsonTextWriterStackedEventListener(
67 BJsonTextWriter* writer,
68 BJsonTextWriterStackedEventListener* parent);
69 ~BJsonTextWriterStackedEventListener();
71 bool Handle(const BJsonEvent& event);
72 void HandleError(status_t status, int32 line,
73 const char* message);
74 void Complete();
76 status_t ErrorStatus();
78 BJsonTextWriterStackedEventListener*
79 Parent();
81 protected:
83 status_t StreamNumberNode(const BJsonEvent& event);
85 status_t StreamStringVerbatim(const char* string);
86 status_t StreamStringVerbatim(const char* string,
87 off_t offset, size_t length);
89 status_t StreamStringEncoded(const char* string);
90 status_t StreamStringEncoded(const char* string,
91 off_t offset, size_t length);
93 status_t StreamQuotedEncodedString(const char* string);
94 status_t StreamQuotedEncodedString(const char* string,
95 off_t offset, size_t length);
97 status_t StreamChar(char c);
99 virtual bool WillAdd();
100 virtual void DidAdd();
102 void SetStackedListenerOnWriter(
103 BJsonTextWriterStackedEventListener*
104 stackedListener);
106 BJsonTextWriter*
107 fWriter;
108 BJsonTextWriterStackedEventListener*
109 fParent;
110 uint32 fCount;
115 class BJsonTextWriterArrayStackedEventListener
116 : public BJsonTextWriterStackedEventListener {
117 public:
118 BJsonTextWriterArrayStackedEventListener(
119 BJsonTextWriter* writer,
120 BJsonTextWriterStackedEventListener* parent);
121 ~BJsonTextWriterArrayStackedEventListener();
123 bool Handle(const BJsonEvent& event);
125 protected:
126 bool WillAdd();
130 class BJsonTextWriterObjectStackedEventListener
131 : public BJsonTextWriterStackedEventListener {
132 public:
133 BJsonTextWriterObjectStackedEventListener(
134 BJsonTextWriter* writer,
135 BJsonTextWriterStackedEventListener* parent);
136 ~BJsonTextWriterObjectStackedEventListener();
138 bool Handle(const BJsonEvent& event);
141 } // namespace BPrivate
144 using BPrivate::BJsonTextWriterStackedEventListener;
145 using BPrivate::BJsonTextWriterArrayStackedEventListener;
146 using BPrivate::BJsonTextWriterObjectStackedEventListener;
149 // #pragma mark - BJsonTextWriterStackedEventListener
152 BJsonTextWriterStackedEventListener::BJsonTextWriterStackedEventListener(
153 BJsonTextWriter* writer,
154 BJsonTextWriterStackedEventListener* parent)
156 fWriter = writer;
157 fParent = parent;
158 fCount = 0;
162 BJsonTextWriterStackedEventListener::~BJsonTextWriterStackedEventListener()
167 bool
168 BJsonTextWriterStackedEventListener::Handle(const BJsonEvent& event)
170 status_t writeResult = B_OK;
172 if (fWriter->ErrorStatus() != B_OK)
173 return false;
175 switch (event.EventType()) {
177 case B_JSON_NUMBER:
178 case B_JSON_STRING:
179 case B_JSON_TRUE:
180 case B_JSON_FALSE:
181 case B_JSON_NULL:
182 case B_JSON_OBJECT_START:
183 case B_JSON_ARRAY_START:
184 if (!WillAdd())
185 return false;
186 break;
188 default:
189 break;
192 switch (event.EventType()) {
194 case B_JSON_NUMBER:
195 writeResult = StreamNumberNode(event);
196 break;
198 case B_JSON_STRING:
199 writeResult = StreamQuotedEncodedString(event.Content());
200 break;
202 case B_JSON_TRUE:
203 writeResult = StreamStringVerbatim("true", 0, 4);
204 break;
206 case B_JSON_FALSE:
207 writeResult = StreamStringVerbatim("false", 0, 5);
208 break;
210 case B_JSON_NULL:
211 writeResult = StreamStringVerbatim("null", 0, 4);
212 break;
214 case B_JSON_OBJECT_START:
216 writeResult = StreamChar('{');
218 if (writeResult == B_OK) {
219 SetStackedListenerOnWriter(
220 new BJsonTextWriterObjectStackedEventListener(
221 fWriter, this));
223 break;
226 case B_JSON_ARRAY_START:
228 writeResult = StreamChar('[');
230 if (writeResult == B_OK) {
231 SetStackedListenerOnWriter(
232 new BJsonTextWriterArrayStackedEventListener(
233 fWriter, this));
235 break;
238 default:
240 HandleError(B_NOT_ALLOWED, JSON_EVENT_LISTENER_ANY_LINE,
241 "unexpected type of json item to add to container");
242 return false;
246 if (writeResult == B_OK)
247 DidAdd();
248 else {
249 HandleError(writeResult, JSON_EVENT_LISTENER_ANY_LINE,
250 "error writing output");
253 return ErrorStatus() == B_OK;
257 void
258 BJsonTextWriterStackedEventListener::HandleError(status_t status, int32 line,
259 const char* message)
261 fWriter->HandleError(status, line, message);
265 void
266 BJsonTextWriterStackedEventListener::Complete()
268 // illegal state.
269 HandleError(JSON_EVENT_LISTENER_ANY_LINE, B_NOT_ALLOWED,
270 "Complete() called on stacked message listener");
274 status_t
275 BJsonTextWriterStackedEventListener::ErrorStatus()
277 return fWriter->ErrorStatus();
281 BJsonTextWriterStackedEventListener*
282 BJsonTextWriterStackedEventListener::Parent()
284 return fParent;
288 status_t
289 BJsonTextWriterStackedEventListener::StreamNumberNode(const BJsonEvent& event)
291 return fWriter->StreamNumberNode(event);
295 status_t
296 BJsonTextWriterStackedEventListener::StreamStringVerbatim(const char* string)
298 return fWriter->StreamStringVerbatim(string);
302 status_t
303 BJsonTextWriterStackedEventListener::StreamStringVerbatim(const char* string,
304 off_t offset, size_t length)
306 return fWriter->StreamStringVerbatim(string, offset, length);
310 status_t
311 BJsonTextWriterStackedEventListener::StreamStringEncoded(const char* string)
313 return fWriter->StreamStringEncoded(string);
317 status_t
318 BJsonTextWriterStackedEventListener::StreamStringEncoded(const char* string,
319 off_t offset, size_t length)
321 return fWriter->StreamStringEncoded(string, offset, length);
325 status_t
326 BJsonTextWriterStackedEventListener::StreamQuotedEncodedString(
327 const char* string)
329 return fWriter->StreamQuotedEncodedString(string);
333 status_t
334 BJsonTextWriterStackedEventListener::StreamQuotedEncodedString(
335 const char* string, off_t offset, size_t length)
337 return fWriter->StreamQuotedEncodedString(string, offset, length);
341 status_t
342 BJsonTextWriterStackedEventListener::StreamChar(char c)
344 return fWriter->StreamChar(c);
348 bool
349 BJsonTextWriterStackedEventListener::WillAdd()
351 return true; // carry on
355 void
356 BJsonTextWriterStackedEventListener::DidAdd()
358 fCount++;
362 void
363 BJsonTextWriterStackedEventListener::SetStackedListenerOnWriter(
364 BJsonTextWriterStackedEventListener* stackedListener)
366 fWriter->SetStackedListener(stackedListener);
370 // #pragma mark - BJsonTextWriterArrayStackedEventListener
373 BJsonTextWriterArrayStackedEventListener::BJsonTextWriterArrayStackedEventListener(
374 BJsonTextWriter* writer,
375 BJsonTextWriterStackedEventListener* parent)
377 BJsonTextWriterStackedEventListener(writer, parent)
382 BJsonTextWriterArrayStackedEventListener
383 ::~BJsonTextWriterArrayStackedEventListener()
388 bool
389 BJsonTextWriterArrayStackedEventListener::Handle(const BJsonEvent& event)
391 status_t writeResult = B_OK;
393 if (fWriter->ErrorStatus() != B_OK)
394 return false;
396 switch (event.EventType()) {
397 case B_JSON_ARRAY_END:
399 writeResult = StreamChar(']');
401 if (writeResult == B_OK) {
402 SetStackedListenerOnWriter(fParent);
403 delete this;
404 return true; // must exit immediately after delete this.
406 break;
409 default:
410 return BJsonTextWriterStackedEventListener::Handle(event);
413 if(writeResult != B_OK) {
414 HandleError(writeResult, JSON_EVENT_LISTENER_ANY_LINE,
415 "error writing output");
418 return ErrorStatus() == B_OK;
422 bool
423 BJsonTextWriterArrayStackedEventListener::WillAdd()
425 status_t writeResult = B_OK;
427 if (writeResult == B_OK && fCount > 0)
428 writeResult = StreamChar(',');
430 if (writeResult != B_OK) {
431 HandleError(B_IO_ERROR, JSON_EVENT_LISTENER_ANY_LINE,
432 "error writing data");
433 return false;
436 return BJsonTextWriterStackedEventListener::WillAdd();
440 // #pragma mark - BJsonTextWriterObjectStackedEventListener
443 BJsonTextWriterObjectStackedEventListener::BJsonTextWriterObjectStackedEventListener(
444 BJsonTextWriter* writer,
445 BJsonTextWriterStackedEventListener* parent)
447 BJsonTextWriterStackedEventListener(writer, parent)
452 BJsonTextWriterObjectStackedEventListener
453 ::~BJsonTextWriterObjectStackedEventListener()
458 bool
459 BJsonTextWriterObjectStackedEventListener::Handle(const BJsonEvent& event)
461 status_t writeResult = B_OK;
463 if (fWriter->ErrorStatus() != B_OK)
464 return false;
466 switch (event.EventType()) {
467 case B_JSON_OBJECT_END:
469 writeResult = StreamChar('}');
471 if (writeResult == B_OK) {
472 SetStackedListenerOnWriter(fParent);
473 delete this;
474 return true; // just exit after delete this.
476 break;
479 case B_JSON_OBJECT_NAME:
481 if (writeResult == B_OK && fCount > 0)
482 writeResult = StreamChar(',');
484 if (writeResult == B_OK)
485 writeResult = StreamQuotedEncodedString(event.Content());
487 if (writeResult == B_OK)
488 writeResult = StreamChar(':');
490 break;
493 default:
494 return BJsonTextWriterStackedEventListener::Handle(event);
497 if (writeResult != B_OK) {
498 HandleError(writeResult, JSON_EVENT_LISTENER_ANY_LINE,
499 "error writing data");
502 return ErrorStatus() == B_OK;
506 // #pragma mark - BJsonTextWriter
509 BJsonTextWriter::BJsonTextWriter(
510 BDataIO* dataIO)
512 fDataIO(dataIO)
515 // this is a preparation for this buffer to easily be used later
516 // to efficiently output encoded unicode characters.
518 fUnicodeAssemblyBuffer[0] = '\\';
519 fUnicodeAssemblyBuffer[1] = 'u';
521 fStackedListener = new BJsonTextWriterStackedEventListener(this, NULL);
525 BJsonTextWriter::~BJsonTextWriter()
527 BJsonTextWriterStackedEventListener* listener = fStackedListener;
529 while (listener != NULL) {
530 BJsonTextWriterStackedEventListener* nextListener = listener->Parent();
531 delete listener;
532 listener = nextListener;
535 fStackedListener = NULL;
539 bool
540 BJsonTextWriter::Handle(const BJsonEvent& event)
542 return fStackedListener->Handle(event);
546 void
547 BJsonTextWriter::Complete()
549 // upon construction, this object will add one listener to the
550 // stack. On complete, this listener should still be there;
551 // otherwise this implies an unterminated structure such as array
552 // / object.
554 if (fStackedListener->Parent() != NULL) {
555 HandleError(B_BAD_DATA, JSON_EVENT_LISTENER_ANY_LINE,
556 "unexpected end of input data");
561 void
562 BJsonTextWriter::SetStackedListener(
563 BJsonTextWriterStackedEventListener* stackedListener)
565 fStackedListener = stackedListener;
569 status_t
570 BJsonTextWriter::StreamNumberNode(const BJsonEvent& event)
572 return StreamStringVerbatim(event.Content());
576 status_t
577 BJsonTextWriter::StreamStringVerbatim(const char* string)
579 return StreamStringVerbatim(string, 0, strlen(string));
583 status_t
584 BJsonTextWriter::StreamStringVerbatim(const char* string,
585 off_t offset, size_t length)
587 return fDataIO->WriteExactly(&string[offset], length);
591 status_t
592 BJsonTextWriter::StreamStringEncoded(const char* string)
594 return StreamStringEncoded(string, 0, strlen(string));
598 /*! Note that this method will expect a UTF-8 encoded string. */
600 status_t
601 BJsonTextWriter::StreamStringEncoded(const char* string,
602 off_t offset, size_t length)
604 status_t writeResult = B_OK;
605 uint8* string8bit = (uint8*)string;
607 while (writeResult == B_OK && length != 0) {
608 uint8 c = string8bit[offset];
609 const char* simpleEsc = b_json_simple_esc_sequence(c);
611 // simple escape sequence involving the backslash + one character.
613 if (simpleEsc != NULL) {
614 writeResult = StreamStringVerbatim(simpleEsc, 0, 2);
616 if (writeResult == B_OK) {
617 offset++;
618 length--;
620 } else {
622 if (b_json_is_7bit_clean(c)) {
624 // roll forward while the characters are simple and then
625 // output them at as a block verbatim.
627 uint32 count7BitClean = 1;
629 while (count7BitClean < length
630 && b_json_is_7bit_clean(
631 string8bit[offset + count7BitClean])) {
632 count7BitClean++;
635 writeResult = StreamStringVerbatim(&string[offset], 0,
636 count7BitClean);
638 if (writeResult == B_OK) {
639 offset += count7BitClean;
640 length -= count7BitClean;
642 } else {
643 if (b_json_is_illegal(c)) {
644 fprintf(stderr, "! string encoding error - illegal "
645 "character [%" B_PRIu32 "]\n", static_cast<uint32>(c));
646 offset++;
647 length--;
648 } else {
649 // if the character is < 128 then it can be rendered
650 // verbatim - check how many are like this and then
651 // render those verbatim.
652 const char* stringInitial = &string[offset];
653 uint32 unicodeCharacter = BUnicodeChar::FromUTF8(
654 &stringInitial);
656 sprintf(&fUnicodeAssemblyBuffer[2], "%04" B_PRIx32,
657 unicodeCharacter);
658 writeResult = StreamStringVerbatim(fUnicodeAssemblyBuffer,
659 0, 6);
661 if (writeResult == B_OK) {
662 uint32 sequence_length
663 = (uint32)(stringInitial - &string[offset]);
664 offset += sequence_length;
665 length -= sequence_length;
672 return writeResult;
676 status_t
677 BJsonTextWriter::StreamQuotedEncodedString(const char* string)
679 return StreamQuotedEncodedString(string, 0, strlen(string));
683 status_t
684 BJsonTextWriter::StreamQuotedEncodedString(const char* string,
685 off_t offset, size_t length)
687 status_t write_result = B_OK;
689 if (write_result == B_OK)
690 write_result = StreamChar('\"');
692 if (write_result == B_OK)
693 write_result = StreamStringEncoded(string, offset, length);
695 if (write_result == B_OK)
696 write_result = StreamChar('\"');
698 return write_result;
702 status_t
703 BJsonTextWriter::StreamChar(char c)
705 return fDataIO->WriteExactly(&c, 1);