NCBI C++ Toolkit Cross Reference

C++/src/util/strbuffer.cpp


  1 /*  $Id: strbuffer.cpp 171185 2009-09-22 15:43:51Z gouriano $
  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: Eugene Vasilchenko
 27 *
 28 * File Description:
 29 *   Input buffer
 30 */
 31 
 32 #include <ncbi_pch.hpp>
 33 #include <corelib/ncbistre.hpp>
 34 #include <corelib/ncbi_limits.hpp>
 35 #include <util/strbuffer.hpp>
 36 #include <util/bytesrc.hpp>
 37 #include <util/error_codes.hpp>
 38 #include <algorithm>
 39 
 40 
 41 #define NCBI_USE_ERRCODE_X   Util_Stream
 42 
 43 BEGIN_NCBI_SCOPE
 44 
 45 static const size_t KInitialBufferSize = 4096;
 46 
 47 static inline
 48 size_t BiggerBufferSize(size_t size) THROWS1_NONE
 49 {
 50     return size * 2;
 51 }
 52 
 53 CIStreamBuffer::CIStreamBuffer(void)
 54     THROWS1((bad_alloc))
 55     : m_Error(0), m_BufferPos(0),
 56       m_BufferSize(0), m_Buffer(0),
 57       m_CurrentPos(0), m_DataEndPos(0),
 58       m_Line(1),
 59       m_CollectPos(0)
 60 {
 61 }
 62 
 63 CIStreamBuffer::CIStreamBuffer(const char* buffer, size_t size)
 64     : m_Error(0), m_BufferPos(0),
 65       m_BufferSize(0), m_Buffer(const_cast<char*>(buffer)),
 66       m_CurrentPos(buffer), m_DataEndPos(buffer+size),
 67       m_Line(1),
 68       m_CollectPos(0)
 69 {
 70 }
 71 
 72 CIStreamBuffer::~CIStreamBuffer(void)
 73 {
 74     try {
 75         Close();
 76     }
 77     catch ( exception& exc ) {
 78         ERR_POST_X(1, Warning <<
 79                       "~CIStreamBuffer: exception while closing: " << exc.what());
 80     }
 81     if ( m_BufferSize ) {
 82         delete[] m_Buffer;
 83     }
 84 }
 85 
 86 void CIStreamBuffer::Open(CByteSourceReader& reader)
 87 {
 88     Close();
 89     if ( !m_BufferSize ) {
 90         m_BufferSize = KInitialBufferSize;
 91         m_CurrentPos = m_DataEndPos = m_Buffer = new char[m_BufferSize];
 92     }
 93     m_Input = &reader;
 94     m_Error = 0;
 95 }
 96 
 97 void CIStreamBuffer::Open(const char* buffer, size_t size)
 98 {
 99     Close();
100     if ( m_BufferSize ) {
101         delete[] m_Buffer;
102     }
103     m_BufferSize = 0;
104     m_Buffer = const_cast<char*>(buffer);
105     m_CurrentPos = buffer;
106     m_DataEndPos = buffer + size;
107     m_Error = 0;
108 }
109 
110 void CIStreamBuffer::Close(void)
111 {
112     if ( m_Input ) {
113         size_t unused = m_DataEndPos - m_CurrentPos;
114         if ( unused ) {
115             m_Input->Pushback(m_CurrentPos, unused);
116         }
117         m_Input.Reset();
118     }
119     m_BufferPos = 0;
120     m_CurrentPos = m_Buffer;
121     m_DataEndPos = m_Buffer;
122     m_Line = 1;
123     m_Error = 0;
124 }
125 
126 void CIStreamBuffer::StartSubSource(void)
127 {
128     if ( m_Collector ) {
129         // update current collector data
130         _ASSERT(m_CollectPos);
131         size_t count = m_CurrentPos - m_CollectPos;
132         if ( count )
133             m_Collector->AddChunk(m_CollectPos, count);
134     }
135     m_CollectPos = m_CurrentPos;
136     if ( m_Input ) {
137         m_Collector =
138             m_Input->SubSource(m_DataEndPos - m_CurrentPos, m_Collector);
139     }
140     else {
141         m_Collector =
142             new CMemorySourceCollector(m_Collector);
143     }
144 }
145 
146 CRef<CByteSource> CIStreamBuffer::EndSubSource(void)
147 {
148     _ASSERT(m_Collector);
149     _ASSERT(m_CollectPos);
150 
151     _ASSERT(m_CollectPos <= m_CurrentPos);
152     size_t count = m_CurrentPos - m_CollectPos;
153     if ( count )
154         m_Collector->AddChunk(m_CollectPos, count);
155 
156     CRef<CByteSource> source = m_Collector->GetSource();
157 
158     CRef<CSubSourceCollector> parent = m_Collector->GetParentCollector();
159     if ( parent ) {
160         m_Collector = parent;
161         m_CollectPos = m_CurrentPos;
162     }
163     else {
164         m_Collector = null;
165         m_CollectPos = 0;
166     }
167 
168     return source;
169 }
170 
171 // this method is highly optimized
172 char CIStreamBuffer::SkipSpaces(void)
173     THROWS1((CIOException))
174 {
175     // cache pointers
176     const char* pos = m_CurrentPos;
177     const char* end = m_DataEndPos;
178     // make sure thire is at least one char in buffer
179     if ( pos == end ) {
180         // fill buffer
181         pos = FillBuffer(pos);
182         // cache m_DataEndPos
183         end = m_DataEndPos;
184     }
185     // main cycle
186     // at the beginning:
187     //     pos == m_CurrentPos
188     //     end == m_DataEndPos
189     //     pos < end
190     for (;;) {
191         // we use do{}while() cycle because
192         // condition is true at the beginning ( pos < end )
193         do {
194             // cache current char
195             char c = *pos;
196             if ( c != ' ' ) { // it's not space (' ')
197                 // point m_CurrentPos to first non space char
198                 m_CurrentPos = pos;
199                 // return char value
200                 return c;
201             }
202             // skip space char
203         } while ( ++pos < end );
204         // here pos == end == m_DataEndPos
205         // point m_CurrentPos to end of buffer
206         m_CurrentPos = pos;
207         // fill next portion
208         pos = FillBuffer(pos);
209         // cache m_DataEndPos
210         end = m_DataEndPos;
211     }
212 }
213 
214 // this method is highly optimized
215 void CIStreamBuffer::FindChar(char c)
216     THROWS1((CIOException))
217 {
218     // cache pointers
219     const char* pos = m_CurrentPos;
220     const char* end = m_DataEndPos;
221     // make sure thire is at least one char in buffer
222     if ( pos == end ) {
223         // fill buffer
224         pos = FillBuffer(pos);
225         // cache m_DataEndPos
226         end = m_DataEndPos;
227     }
228     // main cycle
229     // at the beginning:
230     //     pos == m_CurrentPos
231     //     end == m_DataEndPos
232     //     pos < end
233     for (;;) {
234         const void* found = memchr(pos, c, end - pos);
235         if ( found ) {
236             m_CurrentPos = static_cast<const char*>(found);
237             return;
238         }
239         // point m_CurrentPos to end of buffer
240         m_CurrentPos = end;
241         // fill next portion
242         pos = FillBuffer(end);
243         // cache m_DataEndPos
244         end = m_DataEndPos;
245     }
246 }
247 
248 // this method is highly optimized
249 size_t CIStreamBuffer::PeekFindChar(char c, size_t limit)
250     THROWS1((CIOException))
251 {
252     _ASSERT(limit > 0);
253     PeekCharNoEOF(limit - 1);
254     // cache pointers
255     const char* pos = m_CurrentPos;
256     size_t bufferSize = m_DataEndPos - pos;
257     if ( bufferSize != 0 ) {
258         const void* found = memchr(pos, c, min(limit, bufferSize));
259         if ( found )
260             return static_cast<const char*>(found) - pos;
261     }
262     return limit;
263 }
264 
265 const char* CIStreamBuffer::FillBuffer(const char* pos, bool noEOF)
266     THROWS1((CIOException, bad_alloc))
267 {
268     _ASSERT(pos >= m_DataEndPos);
269     // remove unused portion of buffer at the beginning
270     _ASSERT(m_CurrentPos >= m_Buffer);
271     if ( m_BufferSize == 0 ) {
272         // buffer is external -> no more data
273         if ( noEOF ) {
274             return pos;
275         }
276         else {
277             m_Error = "end of file";
278             NCBI_THROW(CEofException,eEof,m_Error);
279         }
280     }
281     size_t newPosOffset = pos - m_Buffer;
282     if ( newPosOffset >= m_BufferSize || m_DataEndPos == m_CurrentPos ) {
283         // if new position is out of buffer, or if there is no data left
284         // move pointers to the beginning
285         size_t erase = m_CurrentPos - m_Buffer;
286         if ( erase > 0 ) {
287             const char* newPos = m_CurrentPos - erase;
288             if ( m_Collector ) {
289                 _ASSERT(m_CollectPos);
290                 size_t count = m_CurrentPos - m_CollectPos;
291                 if ( count > 0 )
292                     m_Collector->AddChunk(m_CollectPos, count);
293                 m_CollectPos = newPos;
294             }
295             size_t copy_count = m_DataEndPos - m_CurrentPos;
296             if ( copy_count )
297                 memmove(const_cast<char*>(newPos), m_CurrentPos, copy_count);
298             m_CurrentPos = newPos;
299             m_DataEndPos -= erase;
300             m_BufferPos += CT_OFF_TYPE(erase);
301             pos -= erase;
302             newPosOffset -= erase;
303         }
304     }
305     size_t dataSize = m_DataEndPos - m_Buffer;
306     if ( newPosOffset >= m_BufferSize ) {
307         // reallocate buffer
308         size_t newSize = BiggerBufferSize(m_BufferSize);
309         while ( newPosOffset >= newSize ) {
310             newSize = BiggerBufferSize(newSize);
311         }
312         char* newBuffer = new char[newSize];
313         memcpy(newBuffer, m_Buffer, dataSize);
314         m_CurrentPos = newBuffer + (m_CurrentPos - m_Buffer);
315         if ( m_CollectPos )
316             m_CollectPos = newBuffer + (m_CollectPos - m_Buffer);
317         pos = newBuffer + newPosOffset;
318         m_DataEndPos = newBuffer + dataSize;
319         delete[] m_Buffer;
320         m_Buffer = newBuffer;
321         m_BufferSize = newSize;
322     }
323     size_t load = m_BufferSize - dataSize;
324     while ( load > 0  &&  pos >= m_DataEndPos ) {
325         if ( !m_Input ) {
326             if ( noEOF ) {
327                 return pos;
328             }
329             m_Error = "end of file";
330             NCBI_THROW(CEofException,eEof,m_Error);
331         }
332         size_t count = m_Input->Read(const_cast<char*>(m_DataEndPos), load);
333         if ( count == 0 ) {
334             if ( pos < m_DataEndPos )
335                 return pos;
336             if ( m_Input->EndOfData() ) {
337                 if ( noEOF ) {
338                     // ignore EOF
339                     _ASSERT(m_Buffer <= m_CurrentPos);
340                     _ASSERT(m_CurrentPos <= pos);
341                     _ASSERT(m_DataEndPos <= m_Buffer + m_BufferSize);
342                     _ASSERT(!m_CollectPos ||
343                             (m_CollectPos>=m_Buffer &&
344                              m_CollectPos<=m_CurrentPos));
345                     return pos;
346                 }
347                 m_Error = "end of file";
348                 NCBI_THROW(CEofException,eEof,m_Error);
349             }
350             else {
351                 m_Error = "read fault";
352                 NCBI_THROW(CIOException,eRead,m_Error);
353             }
354         }
355         m_DataEndPos += count;
356         load -= count;
357     }
358     _ASSERT(m_Buffer <= m_CurrentPos);
359     _ASSERT(m_CurrentPos <= pos);
360     _ASSERT(pos < m_DataEndPos);
361     _ASSERT(m_DataEndPos <= m_Buffer + m_BufferSize);
362     _ASSERT(!m_CollectPos ||
363             (m_CollectPos>=m_Buffer && m_CollectPos<=m_CurrentPos));
364     return pos;
365 }
366 
367 char CIStreamBuffer::FillBufferNoEOF(const char* pos)
368 {
369     pos = FillBuffer(pos, true);
370     if ( pos >= m_DataEndPos )
371         return 0;
372     else
373         return *pos;
374 }
375 
376 bool CIStreamBuffer::TryToFillBuffer(void)
377 {
378     return FillBuffer(m_CurrentPos, true) < m_DataEndPos;
379 }
380 
381 void CIStreamBuffer::GetChars(char* buffer, size_t count)
382     THROWS1((CIOException))
383 {
384     // cache pos
385     const char* pos = m_CurrentPos;
386     for ( ;; ) {
387         size_t c = m_DataEndPos - pos;
388         if ( c >= count ) {
389             // all data is already in buffer -> copy it
390             memcpy(buffer, pos, count);
391             m_CurrentPos = pos + count;
392             return;
393         }
394         else {
395             memcpy(buffer, pos, c);
396             buffer += c;
397             count -= c;
398             m_CurrentPos = pos += c;
399             pos = FillBuffer(pos);
400         }
401     }
402 }
403 
404 void CIStreamBuffer::GetChars(string& str, size_t count)
405     THROWS1((CIOException))
406 {
407     // cache pos
408     const char* pos = m_CurrentPos;
409     size_t in_buffer = m_DataEndPos - pos;
410     if ( in_buffer >= count ) {
411         // simplest case - plain copy
412         str.assign(pos, count);
413         m_CurrentPos = pos + count;
414         return;
415     }
416     str.reserve(count);
417     str.assign(pos, in_buffer);
418     for ( ;; ) {
419         count -= in_buffer;
420         m_CurrentPos = pos += in_buffer;
421         pos = FillBuffer(pos);
422         in_buffer = m_DataEndPos - pos;
423         if ( in_buffer >= count ) {
424             // all data is already in buffer -> copy it
425             str.append(pos, count);
426             m_CurrentPos = pos + count;
427             return;
428         }
429         str.append(pos, in_buffer);
430     }
431 }
432 
433 void CIStreamBuffer::GetChars(size_t count)
434     THROWS1((CIOException))
435 {
436     // cache pos
437     const char* pos = m_CurrentPos;
438     for ( ;; ) {
439         size_t c = m_DataEndPos - pos;
440         if ( c >= count ) {
441             // all data is already in buffer -> skip it
442             m_CurrentPos = pos + count;
443             return;
444         }
445         else {
446             count -= c;
447             m_CurrentPos = pos += c;
448             pos = FillBuffer(pos);
449         }
450     }
451 }
452 
453 void CIStreamBuffer::SkipEndOfLine(char lastChar)
454     THROWS1((CIOException))
455 {
456     _ASSERT(lastChar == '\n' || lastChar == '\r');
457     _ASSERT(m_CurrentPos > m_Buffer && m_CurrentPos[-1] == lastChar);
458     m_Line++;
459     char nextChar = PeekCharNoEOF();
460     // lastChar either '\r' or \n'
461     // if nextChar is compliment, skip it
462     if ( (lastChar + nextChar) == ('\r' + '\n') )
463         SkipChar();
464 }
465 
466 size_t CIStreamBuffer::ReadLine(char* buff, size_t size)
467     THROWS1((CIOException))
468 {
469     size_t count = 0;
470     try {
471         while ( size > 0 ) {
472             char c = *buff++ = GetChar();
473             count++;
474             size--;
475             switch ( c ) {
476             case '\r':
477                 // replace leading '\r' by '\n'
478                 buff[-1] = '\n';
479                 if ( PeekChar() == '\n' )
480                     SkipChar();
481                 return count;
482             case '\n':
483                 if ( PeekChar() == '\r' )
484                     SkipChar();
485                 return count;
486             }
487         }
488         return count;
489     }
490     catch ( CEofException& /*ignored*/ ) {
491         return count;
492     }
493 }
494 
495 void CIStreamBuffer::BadNumber(void)
496     THROWS1((CUtilException))
497 {
498     m_Error = "bad number";
499     NCBI_THROW_FMT(CUtilException, eWrongData,
500                    "bad number in line " << GetLine());
501 }
502 
503 void CIStreamBuffer::NumberOverflow(void)
504     THROWS1((CUtilException))
505 {
506     m_Error = "number overflow";
507     NCBI_THROW_FMT(CUtilException, eWrongData,
508                    "number overflow in line " << GetLine());
509 }
510 
511 void CIStreamBuffer::SetStreamOffset(CNcbiStreampos pos)
512 {
513     SetStreamPos(pos);
514 }
515 
516 void CIStreamBuffer::SetStreamPos(CNcbiStreampos pos)
517 {
518     if ( m_Input ) {
519         m_Input->Seekg(pos);
520         m_BufferPos = pos;
521         m_DataEndPos = m_Buffer;
522         m_CurrentPos = m_Buffer;
523         m_Line = 1;
524     }
525     else {
526         if ( pos < 0 || pos > m_DataEndPos - m_Buffer ) {
527             NCBI_THROW(CIOException,eRead,"stream position is out of buffer");
528         }
529         m_BufferPos = pos;
530         m_CurrentPos = m_Buffer + pos;
531         m_Line = 1;
532     }
533 }
534 
535 char CIStreamBuffer::SkipWs(void)
536 {
537     char c;
538     while (isspace((unsigned char)(c = GetChar())))
539     ;
540     return c;
541 }
542 
543 Int4 CIStreamBuffer::GetInt4(void)
544     THROWS1((CIOException,CUtilException))
545 {
546     bool sign;
547     char c = SkipWs();
548     switch ( c ) {
549     case '-':
550         sign = true;
551         c = GetChar();
552         break;
553     case '+':
554         sign = false;
555         c = GetChar();
556         break;
557     default:
558         sign = false;
559         break;
560     }
561     
562     Uint4 n = c - '0';
563     if ( n > 9 )
564         BadNumber();
565     
566     // overflow limits
567     const Uint4 kMaxBeforeMul = kMax_I4/10;
568     const Uint1 kMaxLimitAdd = Uint1(kMax_I4%10 + sign);
569     
570     for ( ;; ) {
571         Uint1 d = PeekCharNoEOF() - '0';
572         if  ( d > 9 )
573             break;
574         SkipChar();
575         
576         // check multiplication overflow
577         if ( n > kMaxBeforeMul || n == kMaxBeforeMul && d > kMaxLimitAdd )
578             NumberOverflow();
579         
580         n = n * 10 + d;
581     }
582     if ( sign )
583         return -Int4(n);
584     else
585         return n;
586 }
587 
588 Uint4 CIStreamBuffer::GetUint4(void)
589     THROWS1((CIOException,CUtilException))
590 {
591     char c = SkipWs();
592     if ( c == '+' )
593         c = GetChar();
594 
595     Uint4 n = c - '0';
596     if ( n > 9 )
597         BadNumber();
598     
599     // overflow limits
600     const Uint4 kMaxBeforeMul = kMax_UI4/10;
601     const Uint1 kMaxLimitAdd = Uint1(kMax_UI4%10);
602     
603     for ( ;; ) {
604         Uint1 d = PeekCharNoEOF() - '0';
605         if  ( d > 9 )
606             break;
607         SkipChar();
608 
609         // check multiplication overflow
610         if ( n > kMaxBeforeMul || n == kMaxBeforeMul && d > kMaxLimitAdd )
611             NumberOverflow();
612 
613         n = n * 10 + d;
614     }
615     return n;
616 }
617 
618 Int8 CIStreamBuffer::GetInt8(void)
619     THROWS1((CIOException,CUtilException))
620 {
621     bool sign;
622     char c = SkipWs();
623     switch ( c ) {
624     case '-':
625         sign = true;
626         c = GetChar();
627         break;
628     case '+':
629         sign = false;
630         c = GetChar();
631         break;
632     default:
633         sign = false;
634         break;
635     }
636 
637     Uint8 n = c - '0';
638     if ( n > 9 )
639         BadNumber();
640     
641     // overflow limits
642     const Uint8 kMaxBeforeMul = kMax_I8/10;
643     const Uint1 kMaxLimitAdd = Uint1(kMax_I8%10 + sign);
644 
645     for ( ;; ) {
646         Uint1 d = PeekCharNoEOF() - '0';
647         if  ( d > 9 )
648             break;
649         SkipChar();
650 
651         // check multiplication overflow
652         if ( n > kMaxBeforeMul || n == kMaxBeforeMul && d > kMaxLimitAdd )
653             NumberOverflow();
654 
655         n = n * 10 + d;
656     }
657     if ( sign )
658         return -Int8(n);
659     else
660         return n;
661 }
662 
663 Uint8 CIStreamBuffer::GetUint8(void)
664     THROWS1((CIOException))
665 {
666     char c = SkipWs();
667     if ( c == '+' )
668         c = GetChar();
669     Uint1 d = c - '0';
670     if ( d > 9 )
671         BadNumber();
672     
673     Uint8 n = d;
674     
675     // overflow limits
676     const Uint8 kMaxBeforeMul = kMax_UI8/10;
677     
678     for ( ;; ) {
679         d = PeekCharNoEOF() - '0';
680         if  ( d > 9 )
681             break;
682         SkipChar();
683 
684         // check multiplication overflow
685         if ( n > kMaxBeforeMul )
686             NumberOverflow();
687         
688         n = n * 10 + d;
689         
690         if ( n < d )
691             NumberOverflow();
692     }
693     return n;
694 }
695 
696 COStreamBuffer::COStreamBuffer(CNcbiOstream& out, bool deleteOut)
697     THROWS1((bad_alloc))
698     : m_Output(out), m_DeleteOutput(deleteOut), m_Error(0),
699       m_IndentLevel(0), m_BufferPos(0),
700       m_Buffer(new char[KInitialBufferSize]),
701       m_CurrentPos(m_Buffer),
702       m_BufferEnd(m_Buffer + KInitialBufferSize),
703       m_Line(1), m_LineLength(0),
704       m_BackLimit(0), m_UseIndentation(true)
705 {
706 }
707 
708 COStreamBuffer::~COStreamBuffer(void)
709 {
710     try {
711         Close();
712     }
713     catch ( exception& exc ) {
714         ERR_POST_X(2, Warning <<
715                       "~COStreamBuffer: exception while closing: " << exc.what());
716     }
717     delete[] m_Buffer;
718 }
719 
720 void COStreamBuffer::Close(void)
721 {
722     if ( m_Output ) {
723         FlushBuffer();
724         if ( m_DeleteOutput ) {
725             Flush();
726             delete &m_Output;
727             m_DeleteOutput = false;
728         }
729     }
730     m_Error = 0;
731     m_IndentLevel = 0;
732     m_CurrentPos = m_Buffer;
733     m_Line = 1;
734     m_LineLength = 0;
735 }
736 
737 void COStreamBuffer::FlushBuffer(bool fullBuffer)
738     THROWS1((CIOException))
739 {
740     size_t used = GetUsedSpace();
741     size_t count;
742     size_t leave;
743     if ( fullBuffer ) {
744         count = used;
745         leave = 0;
746     }
747     else {
748         leave = m_BackLimit;
749         if ( used < leave )
750             return; // none to flush
751         count = used - leave;
752     }
753     if ( count != 0 ) {
754         if ( !m_Output.write(m_Buffer, count) ) {
755             m_Error = "write fault";
756             NCBI_THROW(CIOException,eWrite,m_Error);
757         }
758         if ( leave != 0 ) {
759             memmove(m_Buffer, m_Buffer + count, leave);
760             m_CurrentPos -= count;
761         }
762         else {
763             m_CurrentPos = m_Buffer;
764         }
765         m_BufferPos += CT_OFF_TYPE(count);
766     }
767 }
768 
769 
770 void COStreamBuffer::Flush(void)
771     THROWS1((CIOException))
772 {
773     FlushBuffer();
774     IOS_BASE::iostate state = m_Output.rdstate();
775     m_Output.clear();
776     try {
777         if ( !m_Output.flush() ) {
778             NCBI_THROW(CIOException,eFlush,"COStreamBuffer::Flush: failed");
779         }
780     }
781     catch (...) {
782         m_Output.clear(state);
783         throw;
784     }
785     m_Output.clear(state);
786 }
787 
788 
789 char* COStreamBuffer::DoReserve(size_t count)
790     THROWS1((CIOException, bad_alloc))
791 {
792     FlushBuffer(false);
793     size_t usedSize = m_CurrentPos - m_Buffer;
794     size_t needSize = usedSize + count;
795     size_t bufferSize = m_BufferEnd - m_Buffer;
796     if ( bufferSize < needSize ) {
797         // realloc too small buffer
798         do {
799             bufferSize = BiggerBufferSize(bufferSize);
800         } while ( bufferSize < needSize );
801         if ( usedSize == 0 ) {
802             delete[] m_Buffer;
803             m_CurrentPos = m_Buffer = new char[bufferSize];
804             m_BufferEnd = m_Buffer + bufferSize;
805         }
806         else {
807             char* oldBuffer = m_Buffer;
808             m_Buffer = new char[bufferSize];
809             m_BufferEnd = m_Buffer + bufferSize;
810             memcpy(m_Buffer, oldBuffer, usedSize);
811             delete[] oldBuffer;
812             m_CurrentPos = m_Buffer + usedSize;
813         }
814     }
815     return m_CurrentPos;
816 }
817 
818 void COStreamBuffer::PutInt4(Int4 v)
819     THROWS1((CIOException, bad_alloc))
820 {
821     const size_t BSIZE = (sizeof(v)*CHAR_BIT) / 3 + 2;
822     char b[BSIZE];
823     Int4 n = v;
824     if ( n < 0 ) {
825         n = -n;
826     }
827     char* pos = b + BSIZE;
828     do {
829         Int4 a = '0'+n;
830         *--pos = a-10*(n/=Uint4(10));
831     } while ( n );
832     if ( v < 0 ) {
833         *--pos = '-';
834     }
835     int len = b + BSIZE - pos;
836     char* dst = Skip(len);
837     for ( int i = 0; i < len; ++i ) {
838         dst[i] = pos[i];
839     }
840 }
841 
842 void COStreamBuffer::PutUint4(Uint4 v)
843     THROWS1((CIOException, bad_alloc))
844 {
845     const size_t BSIZE = (sizeof(v)*CHAR_BIT) / 3 + 2;
846     char b[BSIZE];
847     Uint4 n = v;
848     char* pos = b + BSIZE;
849     do {
850         Uint4 a = '0'+n;
851         *--pos = a-10*(n/=Uint4(10));
852     } while ( n );
853     int len = b + BSIZE - pos;
854     char* dst = Skip(len);
855     for ( int i = 0; i < len; ++i ) {
856         dst[i] = pos[i];
857     }
858 }
859 
860 // On some platforms division of Int8 is very slow,
861 // so will try to optimize it working with chunks.
862 // Works only for radix base == 10.
863 
864 #define PRINT_INT8_CHUNK 1000000000
865 #define PRINT_INT8_CHUNK_SIZE 9
866 
867 void COStreamBuffer::PutInt8(Int8 v)
868     THROWS1((CIOException, bad_alloc))
869 {
870     const size_t BSIZE = (sizeof(v)*CHAR_BIT) / 3 + 2;
871     char b[BSIZE];
872     Int8 n = v;
873     if ( n < 0 ) {
874         n = -n;
875     }
876     char* pos = b + BSIZE;
877 #ifdef PRINT_INT8_CHUNK
878     // while n doesn't fit in Int4 process it by 9-digit chunks with 32 bits
879     while ( n & ~Uint8(Uint4(~0)) ) {
880         Uint4 m = Uint4(n);
881         m -= PRINT_INT8_CHUNK*Uint4(n/=Uint8(PRINT_INT8_CHUNK));
882         char* end = pos - PRINT_INT8_CHUNK_SIZE;
883         do {
884             Uint4 a = '0'+m;
885             *--pos = a-10*(m/=10);
886         } while ( pos != end );
887     }
888     // process all remaining digits in 32-bit number
889     Uint4 m = Uint4(n);
890     do {
891         Uint4 a = '0'+m;
892         *--pos = a-10*(m/=10);
893     } while ( m );
894 #else
895     do {
896         Uint8 a = '0'+n;
897         *--pos = char(a-10*(n/=Uint8(10)));
898     } while ( n );
899 #endif
900     if ( v < 0 ) {
901         *--pos = '-';
902     }
903     int len = b + BSIZE - pos;
904     char* dst = Skip(len);
905     for ( int i = 0; i < len; ++i ) {
906         dst[i] = pos[i];
907     }
908 }
909 
910 void COStreamBuffer::PutUint8(Uint8 v)
911     THROWS1((CIOException, bad_alloc))
912 {
913     const size_t BSIZE = (sizeof(v)*CHAR_BIT) / 3 + 2;
914     char b[BSIZE];
915     Uint8 n = v;
916     char* pos = b + BSIZE;
917 #ifdef PRINT_INT8_CHUNK
918     // while n doesn't fit in Uint4 process it by 9-digit chunks with 32 bits
919     while ( n & ~Uint8(Uint4(~0)) ) {
920         Uint4 m = Uint4(n);
921         m -= PRINT_INT8_CHUNK*Uint4(n/=Uint8(PRINT_INT8_CHUNK));
922         char* end = pos - PRINT_INT8_CHUNK_SIZE;
923         do {
924             Uint4 a = '0'+m;
925             *--pos = a-10*(m/=10);
926         } while ( pos != end );
927     }
928     // process all remaining digits in 32-bit number
929     Uint4 m = Uint4(n);
930     do {
931         Uint4 a = '0'+m;
932         *--pos = a-10*(m/=10);
933     } while ( m );
934 #else
935     do {
936         Uint8 a = '0'+n;
937         *--pos = char(a-10*(n/=10));
938     } while ( n );
939 #endif
940     int len = b + BSIZE - pos;
941     char* dst = Skip(len);
942     for ( int i = 0; i < len; ++i ) {
943         dst[i] = pos[i];
944     }
945 }
946 
947 void COStreamBuffer::PutEolAtWordEnd(size_t lineLength)
948     THROWS1((CIOException, bad_alloc))
949 {
950     Reserve(1);
951     size_t linePos = m_LineLength;
952     char* pos = m_CurrentPos;
953     bool goodPlace = false;
954     while ( pos > m_Buffer && linePos > 0 ) {
955         --pos;
956         --linePos;
957         if ( linePos <= lineLength && (isspace((unsigned char) (*pos)) ||
958                                        *pos == '\'') ) {
959             goodPlace = true;
960             break;
961         }
962         else if ( *pos == '\n' || *pos == '"' ) {
963             // no suitable space found
964             break;
965         }
966     }
967 
968     // Prevent insertion of more than one '\n'
969     if (pos > m_Buffer  &&  *(pos-1) == '\n') {
970         goodPlace = false;
971     }
972 
973     if ( !goodPlace ) {
974         // no suitable space found
975         if ( linePos < lineLength ) {
976             pos += lineLength - linePos;
977             linePos = lineLength;
978         }
979         // assure we will not break double ""
980         while ( pos > m_Buffer && *(pos - 1) == '"' ) {
981             --pos;
982             --linePos;
983         }
984         if ( pos == m_Buffer ) {
985             // it's possible that we already put some " before...
986             while ( pos < m_CurrentPos && *pos == '"' ) {
987                 ++pos;
988                 ++linePos;
989             }
990         }
991     }
992     // split there
993     // insert '\n'
994     size_t count = m_CurrentPos - pos;
995     memmove(pos + 1, pos, count);
996     m_LineLength = count;
997     ++m_CurrentPos;
998     *pos = '\n';
999     ++m_Line;
1000 }
1001 
1002 void COStreamBuffer::Write(const char* data, size_t dataLength)
1003     THROWS1((CIOException, bad_alloc))
1004 {
1005     while ( dataLength > 0 ) {
1006         size_t available = GetAvailableSpace();
1007         if ( available == 0 ) {
1008             FlushBuffer(false);
1009             available = GetAvailableSpace();
1010         }
1011         if ( available >= dataLength )
1012             break; // current chunk will fit in buffer
1013         memcpy(m_CurrentPos, data, available);
1014         m_CurrentPos += available;
1015         data += available;
1016         dataLength -= available;
1017     }
1018     memcpy(m_CurrentPos, data, dataLength);
1019     m_CurrentPos += dataLength;
1020 }
1021 
1022 void COStreamBuffer::Write(CByteSourceReader& reader)
1023     THROWS1((CIOException, bad_alloc))
1024 {
1025     for ( ;; ) {
1026         size_t available = GetAvailableSpace();
1027         if ( available == 0 ) {
1028             FlushBuffer(false);
1029             available = GetAvailableSpace();
1030         }
1031         size_t count = reader.Read(m_CurrentPos, available);
1032         if ( count == 0 ) {
1033             if ( reader.EndOfData() )
1034                 return;
1035             else
1036                 NCBI_THROW(CIOException,eRead,"buffer read fault");
1037         }
1038         m_CurrentPos += count;
1039     }
1040 }
1041 
1042 END_NCBI_SCOPE
1043 

source navigation ]   [ diff markup ]   [ identifier search ]   [ freetext search ]   [ file search ]  

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.