1 /*  $Id: objostrjson.cpp 608769 2020-05-20 19:18:29Z vasilche $
2 * ===========================================================================
3 *
4 *                            PUBLIC DOMAIN NOTICE
5 *               National Center for Biotechnology Information
6 *
7 *  This software/database is a "United States Government Work" under the
8 *  terms of the United States Copyright Act.  It was written as part of
9 *  the author's official duties as a United States Government employee and
10 *  thus cannot be copyrighted.  This software/database is freely available
11 *  to the public for use. The National Library of Medicine and the U.S.
12 *  Government have not placed any restriction on its use or reproduction.
13 *
14 *  Although all reasonable efforts have been taken to ensure the accuracy
15 *  and reliability of the software and data, the NLM and the U.S.
16 *  Government do not and cannot warrant the performance or results that
17 *  may be obtained by using this software or data. The NLM and the U.S.
18 *  Government disclaim all warranties, express or implied, including
19 *  warranties of performance, merchantability or fitness for any particular
20 *  purpose.
21 *
22 *  Please cite the author in any work or product based on this material.
23 *
24 * ===========================================================================
25 *
26 * Author: Andrei Gourianov
27 *
28 * File Description:
29 *   JSON object output stream
30 *
31 */
32 
33 #include <ncbi_pch.hpp>
34 #include <corelib/ncbistd.hpp>
35 #include <corelib/ncbi_limits.h>
36 
37 #include <serial/objostrjson.hpp>
38 #include <serial/objistr.hpp>
39 #include <serial/objcopy.hpp>
40 #include <serial/impl/memberid.hpp>
41 #include <serial/impl/memberlist.hpp>
42 #include <serial/enumvalues.hpp>
43 #include <serial/objhook.hpp>
44 #include <serial/impl/classinfo.hpp>
45 #include <serial/impl/choice.hpp>
46 #include <serial/impl/continfo.hpp>
47 #include <serial/delaybuf.hpp>
48 #include <serial/impl/ptrinfo.hpp>
49 #include <serial/error_codes.hpp>
50 
51 #include <stdio.h>
52 #include <math.h>
53 
54 
55 #define NCBI_USE_ERRCODE_X   Serial_OStream
56 
57 BEGIN_NCBI_SCOPE
58 
59 
OpenObjectOStreamJson(CNcbiOstream & out,EOwnership deleteOut)60 CObjectOStream* CObjectOStream::OpenObjectOStreamJson(CNcbiOstream& out,
61                                                      EOwnership deleteOut)
62 {
63     return new CObjectOStreamJson(out, deleteOut);
64 }
65 
66 
67 
CObjectOStreamJson(CNcbiOstream & out,bool deleteOut)68 CObjectOStreamJson::CObjectOStreamJson(CNcbiOstream& out, bool deleteOut)
69     : CObjectOStream(eSerial_Json, out, deleteOut ? eTakeOwnership : eNoOwnership),
70     m_FileHeader(false),
71     m_BlockStart(false),
72     m_ExpectValue(false),
73     m_StringEncoding(eEncoding_UTF8),
74     m_BinaryFormat(eDefault),
75     m_WrapAt(0)
76 {
77     m_WriteNamedIntegersByValue = true;
78     m_EnforceWritingDefaults = true;
79 }
80 
CObjectOStreamJson(CNcbiOstream & out,EOwnership deleteOut)81 CObjectOStreamJson::CObjectOStreamJson(CNcbiOstream& out, EOwnership deleteOut)
82     : CObjectOStream(eSerial_Json, out, deleteOut),
83     m_FileHeader(false),
84     m_BlockStart(false),
85     m_ExpectValue(false),
86     m_StringEncoding(eEncoding_UTF8),
87     m_BinaryFormat(eDefault),
88     m_WrapAt(0)
89 {
90     m_WriteNamedIntegersByValue = true;
91     m_EnforceWritingDefaults = true;
92 }
93 
~CObjectOStreamJson(void)94 CObjectOStreamJson::~CObjectOStreamJson(void)
95 {
96 }
97 
SetDefaultStringEncoding(EEncoding enc)98 void CObjectOStreamJson::SetDefaultStringEncoding(EEncoding enc)
99 {
100     m_StringEncoding = enc;
101 }
102 
GetDefaultStringEncoding(void) const103 EEncoding CObjectOStreamJson::GetDefaultStringEncoding(void) const
104 {
105     return m_StringEncoding;
106 }
107 
108 CObjectOStreamJson::EBinaryDataFormat
GetBinaryDataFormat(void) const109 CObjectOStreamJson::GetBinaryDataFormat(void) const
110 {
111     return m_BinaryFormat;
112 }
SetBinaryDataFormat(CObjectOStreamJson::EBinaryDataFormat fmt)113 void CObjectOStreamJson::SetBinaryDataFormat(CObjectOStreamJson::EBinaryDataFormat fmt)
114 {
115     m_BinaryFormat = fmt;
116 }
117 
SetJsonpMode(const string & function_name)118 void CObjectOStreamJson::SetJsonpMode(const string& function_name)
119 {
120     m_JsonpPrefix = function_name + "(";
121     m_JsonpSuffix = ");";
122 }
123 
GetJsonpPadding(string * prefix,string * suffix) const124 void CObjectOStreamJson::GetJsonpPadding(string* prefix, string* suffix) const
125 {
126     if (prefix) {*prefix = m_JsonpPrefix;}
127     if (suffix) {*suffix = m_JsonpSuffix;}
128 }
129 
GetPosition(void) const130 string CObjectOStreamJson::GetPosition(void) const
131 {
132     return "line "+NStr::SizetToString(m_Output.GetLine());
133 }
134 
WriteFileHeader(TTypeInfo type)135 void CObjectOStreamJson::WriteFileHeader(TTypeInfo type)
136 {
137     if (!m_JsonpPrefix.empty() || !m_JsonpSuffix.empty()) {
138         m_Output.PutString(m_JsonpPrefix);
139     }
140     if (type->GetDataSpec() != EDataSpec::eJSON) {
141         m_FileHeader = true;
142         StartBlock();
143         if (!type->GetName().empty()) {
144             m_Output.PutEol();
145             WriteKey(type->GetName());
146         }
147     }
148 }
149 
EndOfWrite(void)150 void CObjectOStreamJson::EndOfWrite(void)
151 {
152     if (m_FileHeader) {
153         EndBlock();
154         m_FileHeader = false;
155     } else {
156         m_BlockStart = false;
157         m_ExpectValue = false;
158     }
159     if (!m_JsonpPrefix.empty() || !m_JsonpSuffix.empty()) {
160         m_Output.PutString(m_JsonpSuffix);
161     }
162     m_Output.PutEol();
163     CObjectOStream::EndOfWrite();
164 }
165 
WriteBool(bool data)166 void CObjectOStreamJson::WriteBool(bool data)
167 {
168     WriteKeywordValue( data ? "true" : "false");
169 }
170 
WriteChar(char data)171 void CObjectOStreamJson::WriteChar(char data)
172 {
173     string s;
174     s += data;
175     WriteString(s);
176 }
177 
WriteInt4(Int4 data)178 void CObjectOStreamJson::WriteInt4(Int4 data)
179 {
180     WriteKeywordValue(NStr::IntToString(data));
181 }
182 
WriteUint4(Uint4 data)183 void CObjectOStreamJson::WriteUint4(Uint4 data)
184 {
185     WriteKeywordValue(NStr::UIntToString(data));
186 }
187 
WriteInt8(Int8 data)188 void CObjectOStreamJson::WriteInt8(Int8 data)
189 {
190     WriteKeywordValue(NStr::Int8ToString(data));
191 }
192 
WriteUint8(Uint8 data)193 void CObjectOStreamJson::WriteUint8(Uint8 data)
194 {
195     WriteKeywordValue(NStr::UInt8ToString(data));
196 }
197 
WriteFloat(float data)198 void CObjectOStreamJson::WriteFloat(float data)
199 {
200     WriteDouble2(data,FLT_DIG);
201 }
202 
WriteDouble(double data)203 void CObjectOStreamJson::WriteDouble(double data)
204 {
205     WriteDouble2(data,DBL_DIG);
206 }
207 
WriteDouble2(double data,unsigned digits)208 void CObjectOStreamJson::WriteDouble2(double data, unsigned digits)
209 {
210     if (isnan(data)) {
211         ThrowError(fInvalidData, "invalid double: not a number");
212     }
213     if (!finite(data)) {
214         ThrowError(fInvalidData, "invalid double: infinite");
215     }
216     if (m_FastWriteDouble) {
217         char buffer[64];
218         WriteKeywordValue( string(buffer,
219             NStr::DoubleToStringPosix(data, digits, buffer, sizeof(buffer))));
220     } else {
221         WriteKeywordValue(NStr::DoubleToString(data,digits, NStr::fDoublePosix));
222     }
223 }
224 
WriteCString(const char * str)225 void CObjectOStreamJson::WriteCString(const char* str)
226 {
227     WriteValue(str);
228 }
229 
WriteString(const string & str,EStringType type)230 void CObjectOStreamJson::WriteString(const string& str,
231                             EStringType type)
232 {
233     WriteValue(str,type);
234 }
235 
WriteStringStore(const string & s)236 void CObjectOStreamJson::WriteStringStore(const string& s)
237 {
238     WriteString(s);
239 }
240 
CopyString(CObjectIStream & in,EStringType type)241 void CObjectOStreamJson::CopyString(CObjectIStream& in,
242                                     EStringType type)
243 {
244     string s;
245     in.ReadString(s, type);
246     WriteString(s, type);
247 }
248 
CopyStringStore(CObjectIStream & in)249 void CObjectOStreamJson::CopyStringStore(CObjectIStream& in)
250 {
251     string s;
252     in.ReadStringStore(s);
253     WriteStringStore(s);
254 }
255 
WriteNullPointer(void)256 void CObjectOStreamJson::WriteNullPointer(void)
257 {
258     CObjectStackFrame::EFrameType ftype = TopFrame().GetFrameType();
259     if (m_ExpectValue ||
260         ftype == CObjectStackFrame::eFrameArrayElement ||
261         ftype == CObjectStackFrame::eFrameClassMember ||
262         ftype == CObjectStackFrame::eFrameChoiceVariant) {
263         WriteKeywordValue("null");
264     }
265 }
266 
WriteObjectReference(TObjectIndex)267 void CObjectOStreamJson::WriteObjectReference(TObjectIndex /*index*/)
268 {
269     ThrowError(fNotImplemented, "Not Implemented");
270 }
271 
WriteOtherBegin(TTypeInfo)272 void CObjectOStreamJson::WriteOtherBegin(TTypeInfo /*typeInfo*/)
273 {
274     ThrowError(fNotImplemented, "Not Implemented");
275 }
276 
WriteOtherEnd(TTypeInfo)277 void CObjectOStreamJson::WriteOtherEnd(TTypeInfo /*typeInfo*/)
278 {
279     ThrowError(fNotImplemented, "Not Implemented");
280 }
281 
WriteOther(TConstObjectPtr,TTypeInfo)282 void CObjectOStreamJson::WriteOther(TConstObjectPtr /*object*/, TTypeInfo /*typeInfo*/)
283 {
284     ThrowError(fNotImplemented, "Not Implemented");
285 }
286 
WriteNull(void)287 void CObjectOStreamJson::WriteNull(void)
288 {
289     if (!m_ExpectValue && !m_SkippedMemberId.empty()) {
290         m_SkippedMemberId.erase();
291     }
292     if (m_ExpectValue) {
293         WriteKeywordValue("null");
294     }
295 }
296 
WriteAnyContentObject(const CAnyContentObject & obj)297 void CObjectOStreamJson::WriteAnyContentObject(const CAnyContentObject& obj)
298 {
299     string obj_name = obj.GetName();
300     if (obj_name.empty()) {
301         if (!StackIsEmpty() && TopFrame().HasMemberId()) {
302             obj_name = TopFrame().GetMemberId().GetName();
303         }
304         if (obj_name.empty()) {
305             ThrowError(fInvalidData, "AnyContent object must have name");
306         }
307     }
308     WriteKey(obj_name);
309     const vector<CSerialAttribInfoItem>& attlist = obj.GetAttributes();
310     if (attlist.empty()) {
311         WriteValue(obj.GetValue(),eStringTypeUTF8);
312         return;
313     }
314     StartBlock();
315     vector<CSerialAttribInfoItem>::const_iterator it;
316     for ( it = attlist.begin(); it != attlist.end(); ++it) {
317         NextElement();
318         WriteKey(it->GetName());
319         WriteValue(it->GetValue(),eStringTypeUTF8);
320     }
321     m_SkippedMemberId = obj_name;
322     WriteValue(obj.GetValue(),eStringTypeUTF8);
323     EndBlock();
324 }
325 
CopyAnyContentObject(CObjectIStream & in)326 void CObjectOStreamJson::CopyAnyContentObject(CObjectIStream& in)
327 {
328     CAnyContentObject obj;
329     in.ReadAnyContentObject(obj);
330     WriteAnyContentObject(obj);
331 }
332 
333 
WriteBitString(const CBitString & obj)334 void CObjectOStreamJson::WriteBitString(const CBitString& obj)
335 {
336     m_Output.PutChar('\"');
337 #if BITSTRING_AS_VECTOR
338     static const char ToHex[] = "0123456789ABCDEF";
339     Uint1 data, mask;
340     bool done = false;
341     for ( CBitString::const_iterator i = obj.begin(); !done; ) {
342         for (data=0, mask=0x8; !done && mask!=0; mask = Uint1(mask >> 1)) {
343             if (*i) {
344                 data |= mask;
345             }
346             done = (++i == obj.end());
347         }
348         m_Output.PutChar(ToHex[data]);
349     }
350 #else
351     if (IsCompressed()) {
352         bm::word_t* tmp_block = (bm::word_t*)bm::aligned_new_malloc(bm::set_block_alloc_size);
353         CBitString::statistics st;
354         obj.calc_stat(&st);
355         char* buf = (char*)malloc(st.max_serialize_mem);
356         size_t len = bm::serialize(obj, (unsigned char*)buf, tmp_block);
357         WriteBytes(buf,len);
358         free(buf);
359         bm::aligned_free(tmp_block);
360     } else {
361         CBitString::size_type i=0;
362         CBitString::size_type ilast = obj.size();
363         CBitString::enumerator e = obj.first();
364         for (; i < ilast; ++i) {
365             m_Output.PutChar( (i == *e) ? '1' : '0');
366             if (i == *e) {
367                 ++e;
368             }
369         }
370     }
371 #endif
372     m_Output.PutString("B\"");
373 }
374 
CopyBitString(CObjectIStream &)375 void CObjectOStreamJson::CopyBitString(CObjectIStream& /*in*/)
376 {
377     ThrowError(fNotImplemented, "Not Implemented");
378 }
379 
WriteEnum(const CEnumeratedTypeValues & values,TEnumValueType value)380 void CObjectOStreamJson::WriteEnum(const CEnumeratedTypeValues& values,
381                         TEnumValueType value)
382 {
383     string value_str;
384     if (values.IsInteger()) {
385         value_str = NStr::IntToString(value);
386         const string& name = values.FindNameEx(value, values.IsInteger());
387         if (name.empty() || GetWriteNamedIntegersByValue()) {
388             WriteKeywordValue(value_str);
389         } else {
390             WriteValue(name);
391         }
392     } else {
393         value_str = values.FindNameEx(value, values.IsInteger());
394         WriteValue(value_str);
395     }
396 }
397 
CopyEnum(const CEnumeratedTypeValues & values,CObjectIStream & in)398 void CObjectOStreamJson::CopyEnum(const CEnumeratedTypeValues& values,
399                         CObjectIStream& in)
400 {
401     TEnumValueType value = in.ReadEnum(values);
402     WriteEnum(values, value);
403 }
404 
WriteClassMember(const CMemberId & memberId,TTypeInfo memberType,TConstObjectPtr memberPtr)405 void CObjectOStreamJson::WriteClassMember(const CMemberId& memberId,
406                                           TTypeInfo memberType,
407                                           TConstObjectPtr memberPtr)
408 {
409     CObjectOStream::WriteClassMember(memberId,memberType,memberPtr);
410 }
411 
WriteClassMember(const CMemberId & memberId,const CDelayBuffer & buffer)412 bool CObjectOStreamJson::WriteClassMember(const CMemberId& memberId,
413                                           const CDelayBuffer& buffer)
414 {
415     return CObjectOStream::WriteClassMember(memberId,buffer);
416 }
417 
WriteClassMemberSpecialCase(const CMemberId & memberId,TTypeInfo memberType,TConstObjectPtr memberPtr,ESpecialCaseWrite how)418 void CObjectOStreamJson::WriteClassMemberSpecialCase(
419     const CMemberId& memberId, TTypeInfo memberType,
420     TConstObjectPtr memberPtr, ESpecialCaseWrite how)
421 {
422     if (how == eWriteAsNil) {
423         BeginClassMember(memberId);
424         WriteKeywordValue("null");
425         EndClassMember();
426     }
427 }
428 
BeginNamedType(TTypeInfo namedTypeInfo)429 void CObjectOStreamJson::BeginNamedType(TTypeInfo namedTypeInfo)
430 {
431     CObjectOStream::BeginNamedType(namedTypeInfo);
432 }
433 
EndNamedType(void)434 void CObjectOStreamJson::EndNamedType(void)
435 {
436     CObjectOStream::EndNamedType();
437 }
438 
439 
BeginContainer(const CContainerTypeInfo * containerType)440 void CObjectOStreamJson::BeginContainer(const CContainerTypeInfo* containerType)
441 {
442     CObjectTypeInfo type(GetRealTypeInfo(containerType->GetElementType()));
443     if (type.GetTypeFamily() == eTypeFamilyPrimitive && type.GetPrimitiveValueType() == ePrimitiveValueAny) {
444         TopFrame().SetNotag();
445         m_BlockStart = true;
446         m_ExpectValue = false;
447         return;
448     }
449     BeginArray();
450 }
451 
EndContainer(void)452 void CObjectOStreamJson::EndContainer(void)
453 {
454     if (TopFrame().GetNotag()) {
455         TopFrame().SetNotag(false);
456         return;
457     }
458     EndArray();
459 }
460 
BeginContainerElement(TTypeInfo)461 void CObjectOStreamJson::BeginContainerElement(TTypeInfo /*elementType*/)
462 {
463     NextElement();
464 }
465 
EndContainerElement(void)466 void CObjectOStreamJson::EndContainerElement(void)
467 {
468 }
469 
470 
BeginClass(const CClassTypeInfo *)471 void CObjectOStreamJson::BeginClass(const CClassTypeInfo* /*classInfo*/)
472 {
473     if (GetStackDepth() > 1 && FetchFrameFromTop(1).GetNotag()) {
474         return;
475     }
476     StartBlock();
477 }
478 
479 
EndClass(void)480 void CObjectOStreamJson::EndClass(void)
481 {
482     if (GetStackDepth() > 1 && FetchFrameFromTop(1).GetNotag()) {
483         return;
484     }
485     EndBlock();
486 }
487 
BeginClassMember(const CMemberId & id)488 void CObjectOStreamJson::BeginClassMember(const CMemberId& id)
489 {
490     if (m_ExpectValue) {
491         return;
492     }
493     if (id.HasNotag() || id.IsAttlist()) {
494         TopFrame().SetNotag();
495         if (id.HasAnyContent()) {
496 #if 1
497             if ( m_BlockStart ) {
498                 m_BlockStart = false;
499             } else {
500                 m_Output.PutChar(',');
501             }
502 #else
503             NextElement();
504 #endif
505         } else {
506             auto tn = [this]()->const string& {
507                 const string& r(m_TypeAlias->GetName());
508                 m_TypeAlias = nullptr;
509                 return r;
510             };
511             m_SkippedMemberId = (m_TypeAlias && id.HasNotag()) ? tn() : id.GetName();
512         }
513         return;
514     }
515     if (id.HasAnyContent()) {
516         return;
517     }
518     NextElement();
519     WriteMemberId(id);
520 }
521 
EndClassMember(void)522 void CObjectOStreamJson::EndClassMember(void)
523 {
524     if (TopFrame().GetNotag()) {
525         TopFrame().SetNotag(false);
526     }
527     m_ExpectValue = false;
528 }
529 
530 
BeginChoice(const CChoiceTypeInfo *)531 void CObjectOStreamJson::BeginChoice(const CChoiceTypeInfo* /*choiceType*/)
532 {
533     if (GetStackDepth() > 1 && FetchFrameFromTop(1).GetNotag()) {
534         return;
535     }
536     StartBlock();
537 }
538 
EndChoice(void)539 void CObjectOStreamJson::EndChoice(void)
540 {
541     if (GetStackDepth() > 1 && FetchFrameFromTop(1).GetNotag()) {
542         return;
543     }
544     EndBlock();
545 }
546 
BeginChoiceVariant(const CChoiceTypeInfo *,const CMemberId & id)547 void CObjectOStreamJson::BeginChoiceVariant(const CChoiceTypeInfo* /*choiceType*/,
548                                             const CMemberId& id)
549 {
550     if (id.HasNotag() || id.IsAttlist()) {
551         m_SkippedMemberId = id.GetName();
552         TopFrame().SetNotag();
553         return;
554     }
555     NextElement();
556     WriteMemberId(id);
557 }
558 
EndChoiceVariant(void)559 void CObjectOStreamJson::EndChoiceVariant(void)
560 {
561     if (TopFrame().GetNotag()) {
562         TopFrame().SetNotag(false);
563     }
564     m_ExpectValue = false;
565 }
566 
567 
568 static const char* const HEX = "0123456789ABCDEF";
569 
BeginBytes(const ByteBlock &)570 void CObjectOStreamJson::BeginBytes(const ByteBlock& )
571 {
572     if (m_BinaryFormat == eArray_Bool ||
573         m_BinaryFormat == eArray_01 ||
574         m_BinaryFormat == eArray_Uint) {
575         m_Output.PutChar('[');
576         m_WrapAt = 78;
577     } else {
578         m_Output.PutChar('\"');
579         m_WrapAt = 0;
580     }
581 }
582 
WriteBytes(const ByteBlock &,const char * bytes,size_t length)583 void CObjectOStreamJson::WriteBytes(const ByteBlock& /*block*/,
584                                     const char* bytes, size_t length)
585 {
586     if (m_BinaryFormat != CObjectOStreamJson::eDefault) {
587         WriteCustomBytes(bytes,length);
588         return;
589     }
590     if (IsCompressed()) {
591         WriteBase64Bytes(bytes,length);
592         return;
593     }
594     WriteBytes(bytes,length);
595 }
596 
EndBytes(const ByteBlock &)597 void CObjectOStreamJson::EndBytes(const ByteBlock& )
598 {
599     if (m_BinaryFormat == eArray_Bool ||
600         m_BinaryFormat == eArray_01 ||
601         m_BinaryFormat == eArray_Uint) {
602         m_Output.BackChar(',');
603         m_Output.PutEol();
604         m_Output.PutChar(']');
605     } else {
606         if (m_BinaryFormat == eString_01B) {
607            m_Output.PutChar('B');
608         }
609         m_Output.PutChar('\"');
610     }
611 }
612 
WriteBase64Bytes(const char * bytes,size_t length)613 void CObjectOStreamJson::WriteBase64Bytes(const char* bytes, size_t length)
614 {
615     const size_t chunk_in  = 57;
616     const size_t chunk_out = 80;
617     if (length > chunk_in && m_WrapAt != 0) {
618         m_Output.PutEol(false);
619     }
620     char dst_buf[chunk_out];
621     size_t bytes_left = length;
622     size_t  src_read=0, dst_written=0, line_len=0;
623     while (bytes_left > 0 && bytes_left <= length) {
624         BASE64_Encode(bytes,  min(bytes_left,chunk_in),  &src_read,
625                         dst_buf, chunk_out, &dst_written, &line_len);
626         m_Output.PutString(dst_buf,dst_written);
627         bytes_left -= src_read;
628         bytes += src_read;
629         if (bytes_left > 0 && m_WrapAt != 0) {
630             m_Output.PutEol(false);
631         }
632     }
633     if (length > chunk_in && m_WrapAt != 0) {
634         m_Output.PutEol(false);
635     }
636 }
637 
WriteBytes(const char * bytes,size_t length)638 void CObjectOStreamJson::WriteBytes(const char* bytes, size_t length)
639 {
640     while ( length-- > 0 ) {
641         char c = *bytes++;
642         m_Output.PutChar(HEX[(c >> 4) & 0xf]);
643         m_Output.PutChar(HEX[c & 0xf]);
644     }
645 }
646 
WriteCustomBytes(const char * bytes,size_t length)647 void CObjectOStreamJson::WriteCustomBytes(const char* bytes, size_t length)
648 {
649     if (m_BinaryFormat == eString_Base64) {
650         WriteBase64Bytes(bytes, length);
651         return;
652     } else if (m_BinaryFormat == eString_Hex) {
653         WriteBytes(bytes, length);
654         return;
655     }
656     if (m_WrapAt != 0 &&
657         m_BinaryFormat != eString_Hex &&
658         m_BinaryFormat != eString_01 &&
659         m_BinaryFormat != eString_01B) {
660         m_Output.PutEol(false);
661     }
662     while ( length-- > 0 ) {
663         Uint1 c = *bytes++;
664         Uint1 mask=0x80;
665         switch (m_BinaryFormat) {
666         case eArray_Bool:
667             for (; mask!=0; mask = Uint1(mask >> 1)) {
668                 if (m_WrapAt != 0) {
669                     m_Output.WrapAt(m_WrapAt, false);
670                 }
671                 m_Output.PutString( (mask & c) ? "true" : "false");
672                 m_Output.PutChar(',');
673             }
674             break;
675         case eArray_01:
676             for (; mask!=0; mask = Uint1(mask >> 1)) {
677                 if (m_WrapAt != 0) {
678                     m_Output.WrapAt(m_WrapAt, false);
679                 }
680                 m_Output.PutChar( (mask & c) ? '1' : '0');
681                 m_Output.PutChar(',');
682             }
683             break;
684         default:
685         case eArray_Uint:
686             if (m_WrapAt != 0) {
687                 m_Output.WrapAt(m_WrapAt, false);
688             }
689             m_Output.PutString( NStr::UIntToString((unsigned int)c));
690             m_Output.PutChar(',');
691             break;
692         case eString_01:
693         case eString_01B:
694             for (; mask!=0; mask = Uint1(mask >> 1)) {
695                 m_Output.PutChar( (mask & c) ? '1' : '0');
696             }
697             break;
698         }
699     }
700 }
701 
WriteChars(const CharBlock &,const char *,size_t)702 void CObjectOStreamJson::WriteChars(const CharBlock& /*block*/,
703                         const char* /*chars*/, size_t /*length*/)
704 {
705     ThrowError(fNotImplemented, "Not Implemented");
706 }
707 
708 
WriteSeparator(void)709 void CObjectOStreamJson::WriteSeparator(void)
710 {
711 }
712 
WriteMemberId(const CMemberId & id)713 void CObjectOStreamJson::WriteMemberId(const CMemberId& id)
714 {
715     WriteKey(id.GetName());
716     m_SkippedMemberId.erase();
717 }
718 
WriteSkippedMember(void)719 void CObjectOStreamJson::WriteSkippedMember(void)
720 {
721     string name("#");
722     name += m_SkippedMemberId;
723     NextElement();
724     WriteKey(name);
725     m_SkippedMemberId.erase();
726 }
727 
728 
WriteEscapedChar(char c,EEncoding enc_in)729 void CObjectOStreamJson::WriteEscapedChar(char c, EEncoding enc_in)
730 {
731     switch ( c ) {
732     case '"':
733         m_Output.PutString("\\\"");
734         break;
735     case '\\':
736         m_Output.PutString("\\\\");
737         break;
738     default:
739         if ( (unsigned int)c <  0x20 ||
740             ((unsigned int)c >= 0x80 && enc_in != eEncoding_UTF8) ) {
741             m_Output.PutString("\\u00");
742             Uint1 ch = c;
743             unsigned hi = ch >> 4;
744             unsigned lo = ch & 0xF;
745             m_Output.PutChar(HEX[hi]);
746             m_Output.PutChar(HEX[lo]);
747         } else {
748             m_Output.PutChar(c);
749         }
750         break;
751     }
752 }
753 
WriteEncodedChar(const char * & src,EStringType type)754 void CObjectOStreamJson::WriteEncodedChar(const char*& src, EStringType type)
755 {
756     EEncoding enc_in( type == eStringTypeUTF8 ? eEncoding_UTF8 : m_StringEncoding);
757     EEncoding enc_out(eEncoding_UTF8);
758 
759     if (enc_in == enc_out || enc_in == eEncoding_Unknown || (*src & 0x80) == 0) {
760         WriteEscapedChar(*src, enc_in);
761     } else {
762         CStringUTF8 tmp( CUtf8::AsUTF8( CTempString(src,1),enc_in));
763         for ( string::const_iterator t = tmp.begin(); t != tmp.end(); ++t ) {
764             m_Output.PutChar(*t);
765         }
766     }
767 }
768 
x_WriteString(const string & value,EStringType type)769 void CObjectOStreamJson::x_WriteString(const string& value, EStringType type)
770 {
771     m_Output.PutChar('\"');
772     for (const char* src = value.c_str(); *src; ++src) {
773         WriteEncodedChar(src,type);
774     }
775     m_Output.PutChar('\"');
776 }
777 
WriteKey(const string & key)778 void CObjectOStreamJson::WriteKey(const string& key)
779 {
780     string s(key);
781     NStr::ReplaceInPlace(s,"-","_");
782     x_WriteString(s);
783     NameSeparator();
784 }
785 
BeginValue(void)786 void CObjectOStreamJson::BeginValue(void)
787 {
788     if (!m_ExpectValue && !m_SkippedMemberId.empty()) {
789         WriteSkippedMember();
790     }
791 }
792 
WriteValue(const string & value,EStringType type)793 void CObjectOStreamJson::WriteValue(const string& value, EStringType type)
794 {
795     BeginValue();
796     x_WriteString(value,type);
797     m_ExpectValue = false;
798 }
799 
WriteKeywordValue(const string & value)800 void CObjectOStreamJson::WriteKeywordValue(const string& value)
801 {
802     BeginValue();
803     m_Output.PutString(value);
804     m_ExpectValue = false;
805 }
806 
StartBlock(void)807 void CObjectOStreamJson::StartBlock(void)
808 {
809     BeginValue();
810     m_Output.PutChar('{');
811     m_Output.IncIndentLevel();
812     m_BlockStart = true;
813     m_ExpectValue = false;
814 }
815 
EndBlock(void)816 void CObjectOStreamJson::EndBlock(void)
817 {
818     m_Output.DecIndentLevel();
819     m_Output.PutEol();
820     m_Output.PutChar('}');
821     m_BlockStart = false;
822     m_ExpectValue = false;
823 }
824 
NextElement(void)825 void CObjectOStreamJson::NextElement(void)
826 {
827     if ( m_BlockStart ) {
828         m_BlockStart = false;
829     } else {
830         m_Output.PutChar(',');
831     }
832     m_Output.PutEol();
833     m_ExpectValue = true;
834 }
835 
BeginArray(void)836 void CObjectOStreamJson::BeginArray(void)
837 {
838     BeginValue();
839     m_Output.PutChar('[');
840     m_Output.IncIndentLevel();
841     m_BlockStart = true;
842     m_ExpectValue = false;
843 }
844 
EndArray(void)845 void CObjectOStreamJson::EndArray(void)
846 {
847     m_Output.DecIndentLevel();
848     m_Output.PutEol();
849     m_Output.PutChar(']');
850     m_BlockStart = false;
851     m_ExpectValue = false;
852 }
853 
NameSeparator(void)854 void CObjectOStreamJson::NameSeparator(void)
855 {
856     m_Output.PutChar(':');
857     if (m_Output.GetUseIndentation()) {
858         m_Output.PutChar(' ');
859     }
860     m_ExpectValue = true;
861 }
862 
863 END_NCBI_SCOPE
864