Vault
4.1
|
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