Vault  4.1
vtextiostream.cpp
Go to the documentation of this file.
00001 /*
00002 Copyright c1997-2014 Trygve Isaacson. All rights reserved.
00003 This file is part of the Code Vault version 4.1
00004 http://www.bombaydigital.com/
00005 License: MIT. See LICENSE.md in the Vault top level directory.
00006 */
00007 
00010 #include "vtextiostream.h"
00011 
00012 #include "vchar.h"
00013 #include "vexception.h"
00014 #include "vassert.h"
00015 
00016 VTextIOStream::VTextIOStream(VStream& rawStream, int lineEndingsWriteKind) :
00017     VIOStream(rawStream),
00018     mLineEndingsReadKind(kLineEndingsUnknown),
00019     mLineEndingsWriteKind(lineEndingsWriteKind),
00020     mPendingCharacter(0),
00021     mReadState(kReadStateReady),
00022     mLineBuffer(),
00023 #ifndef VCOMPILER_MSVC /* GCC EffC++ warnings want this initializer, but then VC++ warns about default initialization. */
00024     mLineEndingChars(),
00025 #endif
00026     mLineEndingCharsLength(0) {
00027     mLineBuffer.preflight(80); // allocate a reasonable buffer size up front to avoid repeated re-allocation
00028     this->setLineEndingsKind(lineEndingsWriteKind); // install the line ending data to be written
00029 }
00030 
00031 void VTextIOStream::readLine(VString& s, bool includeLineEnding) {
00032     // Note: We append char-by-char, but VString should already be optimized to
00033     // avoid actually re-allocating its buffer for each single-char expansion.
00034 
00035     mLineBuffer = VString::EMPTY();
00036 
00037     bool        readFirstByteOfLine = false;
00038     Vs64        numBytesToRead = 1;
00039     Vs64        numBytesRead;
00040     VCodePoint  c(0);
00041     bool        done = false;
00042 
00043     do {
00044         if (mPendingCharacter.isNotNull()) {
00045             c = mPendingCharacter;
00046             mPendingCharacter = VCodePoint(0);
00047             numBytesRead = 1;
00048         } else {
00049             if (this->available() == 0) {
00050                 numBytesRead = 0;
00051             } else {
00052                 c = VCodePoint(*this);
00053                 numBytesRead = c.getUTF8Length();
00054             }
00055 
00056             // Throw EOF if we fail reading very first byte of line.
00057             // Otherwise, we'll return whatever we read, and throw next time.
00058             if (numBytesRead == 0) {
00059                 if (readFirstByteOfLine) {
00060                     break; // this line is done
00061                 } else {
00062                     throw VEOFException("EOF");
00063                 }
00064             }
00065 
00066             readFirstByteOfLine = true;
00067         }
00068 
00069         switch (mReadState) {
00070             case kReadStateReady:
00071 
00072                 if (c == 0x0A) {  // found a Unix line end
00073                     if (includeLineEnding) {
00074                         mLineBuffer += c;
00075                     }
00076 
00077                     done = true;    // done, bail out and return the string
00078 
00079                     this->_updateLineEndingsReadKind(kLineEndingsUnix);
00080                 } else if (c == 0x0D) { // found a Mac line end, or 1st byte of a DOS line end
00081                     mReadState = kReadStateGot0x0D;
00082                 } else { // found a normal character
00083                     mLineBuffer += c;
00084                 }
00085 
00086                 break;
00087 
00088             case kReadStateGot0x0D:
00089 
00090                 if (c == 0x0A) {  // found a DOS line end
00091                     if (includeLineEnding) {
00092                         mLineBuffer += VCodePoint(0x0D);
00093                         mLineBuffer += VCodePoint(0x0A);
00094                     }
00095 
00096                     mReadState = kReadStateReady;
00097                     done = true;    // done, bail out and return the string
00098 
00099                     this->_updateLineEndingsReadKind(kLineEndingsDOS);
00100                 } else { // found a normal character, so we have a Mac line end pending
00101                     if (includeLineEnding) {
00102                         mLineBuffer += VCodePoint(0x0D);
00103                     }
00104 
00105                     mPendingCharacter = c;
00106 
00107                     mReadState = kReadStateReady;
00108                     done = true;
00109 
00110                     this->_updateLineEndingsReadKind(kLineEndingsMac);
00111                 }
00112 
00113                 break;
00114         }
00115 
00116     } while ((numBytesRead == numBytesToRead) && !done);
00117 
00118     s = mLineBuffer;
00119 }
00120 
00121 VCodePoint VTextIOStream::readUTF8CodePoint() {
00122     VCodePoint cp(*this);
00123     return cp;
00124 }
00125 
00126 VChar VTextIOStream::readCharacterByte() {
00127     char c;
00128 
00129     this->readGuaranteed(reinterpret_cast<Vu8*>(&c), 1);
00130 
00131     return c;
00132 }
00133 
00134 void VTextIOStream::readAll(VString& s, bool includeLineEndings) {
00135     try {
00136         VString line;
00137         for (;;) {
00138             this->readLine(line, includeLineEndings);
00139             s += line;
00140         }
00141     } catch (const VEOFException&) {}
00142 }
00143 
00144 void VTextIOStream::readAll(VStringVector& lines) {
00145     try {
00146         VString line;
00147         for (;;) {
00148             this->readLine(line);
00149             lines.push_back(line);
00150         }
00151     } catch (const VEOFException&) {}
00152 }
00153 
00154 void VTextIOStream::writeLine(const VString& s) {
00155     this->writeString(s);
00156     this->writeLineEnd();
00157 }
00158 
00159 void VTextIOStream::writeString(const VString& s) {
00160     (void) this->write(s.getDataBufferConst(), static_cast<Vs64>(s.length()));
00161 }
00162 
00163 void VTextIOStream::writeLineEnd() {
00164     if (mLineEndingCharsLength != 0) {
00165         (void) this->write(mLineEndingChars, static_cast<Vs64>(mLineEndingCharsLength));
00166     }
00167 }
00168 
00169 void VTextIOStream::_updateLineEndingsReadKind(int lineEndingKind) {
00170     switch (mLineEndingsReadKind) {
00171         case kLineEndingsUnknown:
00172             switch (lineEndingKind) {
00173                 case kLineEndingsUnknown:
00174                 case kLineEndingsMixed:
00175                     VASSERT(false);    // invalid input value
00176                     break;
00177 
00178                 case kLineEndingsUnix:
00179                 case kLineEndingsDOS:
00180                 case kLineEndingsMac:
00181                     mLineEndingsReadKind = lineEndingKind;
00182                     break;
00183             }
00184             break;
00185 
00186         case kLineEndingsUnix:
00187             switch (lineEndingKind) {
00188                 case kLineEndingsUnknown:
00189                 case kLineEndingsMixed:
00190                     VASSERT(false);    // invalid input value
00191                     break;
00192 
00193                 case kLineEndingsUnix:
00194                     // no change
00195                     break;
00196 
00197                 case kLineEndingsDOS:
00198                 case kLineEndingsMac:
00199                     mLineEndingsReadKind = kLineEndingsMixed;
00200                     break;
00201 
00202             }
00203             break;
00204 
00205         case kLineEndingsDOS:
00206             switch (lineEndingKind) {
00207                 case kLineEndingsUnknown:
00208                 case kLineEndingsMixed:
00209                     VASSERT(false);    // invalid input value
00210                     break;
00211 
00212                 case kLineEndingsUnix:
00213                 case kLineEndingsMac:
00214                     mLineEndingsReadKind = kLineEndingsMixed;
00215                     break;
00216 
00217                 case kLineEndingsDOS:
00218                     // no change
00219                     break;
00220 
00221             }
00222             break;
00223 
00224         case kLineEndingsMac:
00225             switch (lineEndingKind) {
00226                 case kLineEndingsUnknown:
00227                 case kLineEndingsMixed:
00228                     VASSERT(false);    // invalid input value
00229                     break;
00230 
00231                 case kLineEndingsUnix:
00232                 case kLineEndingsDOS:
00233                     mLineEndingsReadKind = kLineEndingsMixed;
00234                     break;
00235 
00236                 case kLineEndingsMac:
00237                     // no change
00238                     break;
00239 
00240             }
00241             break;
00242 
00243         case kLineEndingsMixed:
00244             // Once we detect mixed input, it stays that way.
00245             break;
00246     }
00247 
00248 }
00249 
00250 int VTextIOStream::getLineEndingsReadKindForWrite() const {
00251     int writeKind = kUseNativeLineEndings;
00252 
00253     switch (mLineEndingsReadKind) {
00254         case kLineEndingsUnix:
00255             writeKind = kUseUnixLineEndings;
00256             break;
00257 
00258         case kLineEndingsDOS:
00259             writeKind = kUseDOSLineEndings;
00260             break;
00261 
00262         case kLineEndingsMac:
00263             writeKind = kUseMacLineEndings;
00264             break;
00265 
00266     }
00267 
00268     return writeKind;
00269 }
00270 
00271 void VTextIOStream::setLineEndingsKind(int kind) {
00272     switch (kind) {
00273         case kUseUnixLineEndings:
00274             mLineEndingChars[0] = 0x0A;
00275             mLineEndingChars[1] = 0x00; // will not be referenced nor used
00276             mLineEndingCharsLength = 1;
00277             break;
00278         case kUseDOSLineEndings:
00279             mLineEndingChars[0] = 0x0D;
00280             mLineEndingChars[1] = 0x0A;
00281             mLineEndingCharsLength = 2;
00282             break;
00283         case kUseMacLineEndings:
00284             mLineEndingChars[0] = 0x0D;
00285             mLineEndingChars[1] = 0x00; // will not be referenced nor used
00286             mLineEndingCharsLength = 1;
00287             break;
00288         case kUseNativeLineEndings: {
00289             const Vu8* lineEndingChars = vault::VgetNativeLineEnding(mLineEndingCharsLength);
00290             for (int i = 0; i < mLineEndingCharsLength; ++i) {
00291                 mLineEndingChars[i] = lineEndingChars[i];
00292             }
00293         }
00294         break;
00295         case kUseSuppliedLineEndings:
00296             // Line endings will be supplied by caller and already written into the line data.
00297             mLineEndingChars[0] = 0x00; // will not be referenced nor used
00298             mLineEndingChars[1] = 0x00; // will not be referenced nor used
00299             mLineEndingCharsLength = 0;
00300             break;
00301         default:
00302             throw VStackTraceException(VSTRING_FORMAT("VTextIOStream::writeLine using invalid line ending mode %d.", kind));
00303             break;
00304     }
00305 
00306     mLineEndingsWriteKind = kind;
00307 }
00308 
00309 

Copyright ©1997-2014 Trygve Isaacson. All rights reserved. This documentation was generated with Doxygen.