CmDataStream.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652
  1. #include "CmDataStream.h"
  2. #include "CmDebug.h"
  3. #include "CmException.h"
  4. namespace CamelotFramework
  5. {
  6. template <typename T> DataStream& DataStream::operator >>(T& val)
  7. {
  8. read(static_cast<void*>(&val), sizeof(T));
  9. return *this;
  10. }
  11. String DataStream::getLine(bool trimAfter)
  12. {
  13. char tmpBuf[OGRE_STREAM_TEMP_SIZE];
  14. String retString;
  15. size_t readCount;
  16. // Keep looping while not hitting delimiter
  17. while ((readCount = read(tmpBuf, OGRE_STREAM_TEMP_SIZE-1)) != 0)
  18. {
  19. // Terminate string
  20. tmpBuf[readCount] = '\0';
  21. char* p = strchr(tmpBuf, '\n');
  22. if (p != 0)
  23. {
  24. // Reposition backwards
  25. skip((long)(p + 1 - tmpBuf - readCount));
  26. *p = '\0';
  27. }
  28. retString += tmpBuf;
  29. if (p != 0)
  30. {
  31. // Trim off trailing CR if this was a CR/LF entry
  32. if (retString.length() && retString[retString.length()-1] == '\r')
  33. {
  34. retString.erase(retString.length()-1, 1);
  35. }
  36. // Found terminator, break out
  37. break;
  38. }
  39. }
  40. if (trimAfter)
  41. {
  42. StringUtil::trim(retString);
  43. }
  44. return retString;
  45. }
  46. size_t DataStream::readLine(char* buf, size_t maxCount, const String& delim)
  47. {
  48. // Deal with both Unix & Windows LFs
  49. bool trimCR = false;
  50. if (delim.find_first_of('\n') != String::npos)
  51. {
  52. trimCR = true;
  53. }
  54. char tmpBuf[OGRE_STREAM_TEMP_SIZE];
  55. size_t chunkSize = std::min(maxCount, (size_t)OGRE_STREAM_TEMP_SIZE-1);
  56. size_t totalCount = 0;
  57. size_t readCount;
  58. while (chunkSize && (readCount = read(tmpBuf, chunkSize)) != 0)
  59. {
  60. // Terminate
  61. tmpBuf[readCount] = '\0';
  62. // Find first delimiter
  63. size_t pos = strcspn(tmpBuf, delim.c_str());
  64. if (pos < readCount)
  65. {
  66. // Found terminator, reposition backwards
  67. skip((long)(pos + 1 - readCount));
  68. }
  69. // Are we genuinely copying?
  70. if (buf)
  71. {
  72. memcpy(buf+totalCount, tmpBuf, pos);
  73. }
  74. totalCount += pos;
  75. if (pos < readCount)
  76. {
  77. // Trim off trailing CR if this was a CR/LF entry
  78. if (trimCR && totalCount && buf[totalCount-1] == '\r')
  79. {
  80. --totalCount;
  81. }
  82. // Found terminator, break out
  83. break;
  84. }
  85. // Adjust chunkSize for next time
  86. chunkSize = std::min(maxCount-totalCount, (size_t)OGRE_STREAM_TEMP_SIZE-1);
  87. }
  88. // Terminate
  89. buf[totalCount] = '\0';
  90. return totalCount;
  91. }
  92. size_t DataStream::skipLine(const String& delim)
  93. {
  94. char tmpBuf[OGRE_STREAM_TEMP_SIZE];
  95. size_t total = 0;
  96. size_t readCount;
  97. // Keep looping while not hitting delimiter
  98. while ((readCount = read(tmpBuf, OGRE_STREAM_TEMP_SIZE-1)) != 0)
  99. {
  100. // Terminate string
  101. tmpBuf[readCount] = '\0';
  102. // Find first delimiter
  103. size_t pos = strcspn(tmpBuf, delim.c_str());
  104. if (pos < readCount)
  105. {
  106. // Found terminator, reposition backwards
  107. skip((long)(pos + 1 - readCount));
  108. total += pos + 1;
  109. // break out
  110. break;
  111. }
  112. total += readCount;
  113. }
  114. return total;
  115. }
  116. String DataStream::getAsString()
  117. {
  118. // Read the entire buffer - ideally in one read, but if the size of
  119. // the buffer is unknown, do multiple fixed size reads.
  120. size_t bufSize = (mSize > 0 ? mSize : 4096);
  121. char* pBuf = (char*)malloc(sizeof(char) * bufSize);
  122. // Ensure read from begin of stream
  123. seek(0);
  124. String result;
  125. while (!eof())
  126. {
  127. size_t nr = read(pBuf, bufSize);
  128. result.append(pBuf, nr);
  129. }
  130. free(pBuf);
  131. return result;
  132. }
  133. MemoryDataStream::MemoryDataStream(void* pMem, size_t inSize, bool freeOnClose, bool readOnly)
  134. : DataStream(static_cast<UINT16>(readOnly ? READ : (READ | WRITE)))
  135. {
  136. mData = mPos = static_cast<UINT8*>(pMem);
  137. mSize = inSize;
  138. mEnd = mData + mSize;
  139. mFreeOnClose = freeOnClose;
  140. assert(mEnd >= mPos);
  141. }
  142. MemoryDataStream::MemoryDataStream(const String& name, void* pMem, size_t inSize,
  143. bool freeOnClose, bool readOnly)
  144. : DataStream(name, static_cast<UINT16>(readOnly ? READ : (READ | WRITE)))
  145. {
  146. mData = mPos = static_cast<UINT8*>(pMem);
  147. mSize = inSize;
  148. mEnd = mData + mSize;
  149. mFreeOnClose = freeOnClose;
  150. assert(mEnd >= mPos);
  151. }
  152. MemoryDataStream::MemoryDataStream(DataStream& sourceStream,
  153. bool freeOnClose, bool readOnly)
  154. : DataStream(static_cast<UINT16>(readOnly ? READ : (READ | WRITE)))
  155. {
  156. // Copy data from incoming stream
  157. mSize = sourceStream.size();
  158. if (mSize == 0 && !sourceStream.eof())
  159. {
  160. // size of source is unknown, read all of it into memory
  161. String contents = sourceStream.getAsString();
  162. mSize = contents.size();
  163. mData = (UINT8*)malloc(sizeof(UINT8) * mSize);
  164. mPos = mData;
  165. memcpy(mData, contents.data(), mSize);
  166. mEnd = mData + mSize;
  167. }
  168. else
  169. {
  170. mData = (UINT8*)malloc(sizeof(UINT8) * mSize);
  171. mPos = mData;
  172. mEnd = mData + sourceStream.read(mData, mSize);
  173. mFreeOnClose = freeOnClose;
  174. }
  175. assert(mEnd >= mPos);
  176. }
  177. MemoryDataStream::MemoryDataStream(DataStreamPtr& sourceStream,
  178. bool freeOnClose, bool readOnly)
  179. : DataStream(static_cast<UINT16>(readOnly ? READ : (READ | WRITE)))
  180. {
  181. // Copy data from incoming stream
  182. mSize = sourceStream->size();
  183. if (mSize == 0 && !sourceStream->eof())
  184. {
  185. // size of source is unknown, read all of it into memory
  186. String contents = sourceStream->getAsString();
  187. mSize = contents.size();
  188. mData = (UINT8*)malloc(sizeof(UINT8) * mSize);
  189. mPos = mData;
  190. memcpy(mData, contents.data(), mSize);
  191. mEnd = mData + mSize;
  192. }
  193. else
  194. {
  195. mData = (UINT8*)malloc(sizeof(UINT8) * mSize);
  196. mPos = mData;
  197. mEnd = mData + sourceStream->read(mData, mSize);
  198. mFreeOnClose = freeOnClose;
  199. }
  200. assert(mEnd >= mPos);
  201. }
  202. MemoryDataStream::MemoryDataStream(const String& name, DataStream& sourceStream,
  203. bool freeOnClose, bool readOnly)
  204. : DataStream(name, static_cast<UINT16>(readOnly ? READ : (READ | WRITE)))
  205. {
  206. // Copy data from incoming stream
  207. mSize = sourceStream.size();
  208. if (mSize == 0 && !sourceStream.eof())
  209. {
  210. // size of source is unknown, read all of it into memory
  211. String contents = sourceStream.getAsString();
  212. mSize = contents.size();
  213. mData = (UINT8*)malloc(sizeof(UINT8) * mSize);
  214. mPos = mData;
  215. memcpy(mData, contents.data(), mSize);
  216. mEnd = mData + mSize;
  217. }
  218. else
  219. {
  220. mData = (UINT8*)malloc(sizeof(UINT8) * mSize);
  221. mPos = mData;
  222. mEnd = mData + sourceStream.read(mData, mSize);
  223. mFreeOnClose = freeOnClose;
  224. }
  225. assert(mEnd >= mPos);
  226. }
  227. MemoryDataStream::MemoryDataStream(const String& name, const DataStreamPtr& sourceStream,
  228. bool freeOnClose, bool readOnly)
  229. : DataStream(name, static_cast<UINT16>(readOnly ? READ : (READ | WRITE)))
  230. {
  231. // Copy data from incoming stream
  232. mSize = sourceStream->size();
  233. if (mSize == 0 && !sourceStream->eof())
  234. {
  235. // size of source is unknown, read all of it into memory
  236. String contents = sourceStream->getAsString();
  237. mSize = contents.size();
  238. mData = (UINT8*)malloc(sizeof(UINT8) * mSize);
  239. mPos = mData;
  240. memcpy(mData, contents.data(), mSize);
  241. mEnd = mData + mSize;
  242. }
  243. else
  244. {
  245. mData = (UINT8*)malloc(sizeof(UINT8) * mSize);
  246. mPos = mData;
  247. mEnd = mData + sourceStream->read(mData, mSize);
  248. mFreeOnClose = freeOnClose;
  249. }
  250. assert(mEnd >= mPos);
  251. }
  252. MemoryDataStream::MemoryDataStream(size_t inSize, bool freeOnClose, bool readOnly)
  253. : DataStream(static_cast<UINT16>(readOnly ? READ : (READ | WRITE)))
  254. {
  255. mSize = inSize;
  256. mFreeOnClose = freeOnClose;
  257. mData = (UINT8*)malloc(sizeof(UINT8) * mSize);
  258. mPos = mData;
  259. mEnd = mData + mSize;
  260. assert(mEnd >= mPos);
  261. }
  262. MemoryDataStream::MemoryDataStream(const String& name, size_t inSize,
  263. bool freeOnClose, bool readOnly)
  264. : DataStream(name, static_cast<UINT16>(readOnly ? READ : (READ | WRITE)))
  265. {
  266. mSize = inSize;
  267. mFreeOnClose = freeOnClose;
  268. mData = (UINT8*)malloc(sizeof(UINT8) * mSize);
  269. mPos = mData;
  270. mEnd = mData + mSize;
  271. assert(mEnd >= mPos);
  272. }
  273. MemoryDataStream::~MemoryDataStream()
  274. {
  275. close();
  276. }
  277. size_t MemoryDataStream::read(void* buf, size_t count)
  278. {
  279. size_t cnt = count;
  280. // Read over end of memory?
  281. if (mPos + cnt > mEnd)
  282. cnt = mEnd - mPos;
  283. if (cnt == 0)
  284. return 0;
  285. assert (cnt<=count);
  286. memcpy(buf, mPos, cnt);
  287. mPos += cnt;
  288. return cnt;
  289. }
  290. size_t MemoryDataStream::write(const void* buf, size_t count)
  291. {
  292. size_t written = 0;
  293. if (isWriteable())
  294. {
  295. written = count;
  296. // we only allow writing within the extents of allocated memory
  297. // check for buffer overrun & disallow
  298. if (mPos + written > mEnd)
  299. written = mEnd - mPos;
  300. if (written == 0)
  301. return 0;
  302. memcpy(mPos, buf, written);
  303. mPos += written;
  304. }
  305. return written;
  306. }
  307. size_t MemoryDataStream::readLine(char* buf, size_t maxCount, const String& delim)
  308. {
  309. // Deal with both Unix & Windows LFs
  310. bool trimCR = false;
  311. if (delim.find_first_of('\n') != String::npos)
  312. {
  313. trimCR = true;
  314. }
  315. size_t pos = 0;
  316. // Make sure pos can never go past the end of the data
  317. while (pos < maxCount && mPos < mEnd)
  318. {
  319. if (delim.find(*mPos) != String::npos)
  320. {
  321. // Trim off trailing CR if this was a CR/LF entry
  322. if (trimCR && pos && buf[pos-1] == '\r')
  323. {
  324. // terminate 1 character early
  325. --pos;
  326. }
  327. // Found terminator, skip and break out
  328. ++mPos;
  329. break;
  330. }
  331. buf[pos++] = *mPos++;
  332. }
  333. // terminate
  334. buf[pos] = '\0';
  335. return pos;
  336. }
  337. size_t MemoryDataStream::skipLine(const String& delim)
  338. {
  339. size_t pos = 0;
  340. // Make sure pos can never go past the end of the data
  341. while (mPos < mEnd)
  342. {
  343. ++pos;
  344. if (delim.find(*mPos++) != String::npos)
  345. {
  346. // Found terminator, break out
  347. break;
  348. }
  349. }
  350. return pos;
  351. }
  352. void MemoryDataStream::skip(long count)
  353. {
  354. size_t newpos = (size_t)( ( mPos - mData ) + count );
  355. assert( mData + newpos <= mEnd );
  356. mPos = mData + newpos;
  357. }
  358. void MemoryDataStream::seek( size_t pos )
  359. {
  360. assert( mData + pos <= mEnd );
  361. mPos = mData + pos;
  362. }
  363. size_t MemoryDataStream::tell(void) const
  364. {
  365. //mData is start, mPos is current location
  366. return mPos - mData;
  367. }
  368. bool MemoryDataStream::eof(void) const
  369. {
  370. return mPos >= mEnd;
  371. }
  372. void MemoryDataStream::close(void)
  373. {
  374. if (mFreeOnClose && mData)
  375. {
  376. free(mData);
  377. mData = 0;
  378. }
  379. }
  380. FileDataStream::FileDataStream(std::shared_ptr<std::ifstream> s, bool freeOnClose)
  381. : DataStream(), mpInStream(s), mpFStreamRO(s), mpFStream(0), mFreeOnClose(freeOnClose)
  382. {
  383. // calculate the size
  384. mpInStream->seekg(0, std::ios_base::end);
  385. mSize = (size_t)mpInStream->tellg();
  386. mpInStream->seekg(0, std::ios_base::beg);
  387. determineAccess();
  388. }
  389. FileDataStream::FileDataStream(const String& name,
  390. std::shared_ptr<std::ifstream> s, bool freeOnClose)
  391. : DataStream(name), mpInStream(s), mpFStreamRO(s), mpFStream(0), mFreeOnClose(freeOnClose)
  392. {
  393. // calculate the size
  394. mpInStream->seekg(0, std::ios_base::end);
  395. mSize = (size_t)mpInStream->tellg();
  396. mpInStream->seekg(0, std::ios_base::beg);
  397. determineAccess();
  398. }
  399. FileDataStream::FileDataStream(const String& name,
  400. std::shared_ptr<std::ifstream> s, size_t inSize, bool freeOnClose)
  401. : DataStream(name), mpInStream(s), mpFStreamRO(s), mpFStream(0), mFreeOnClose(freeOnClose)
  402. {
  403. // Size is passed in
  404. mSize = inSize;
  405. determineAccess();
  406. }
  407. FileDataStream::FileDataStream(std::shared_ptr<std::fstream> s, bool freeOnClose)
  408. : DataStream(false), mpInStream(s), mpFStreamRO(0), mpFStream(s), mFreeOnClose(freeOnClose)
  409. {
  410. // writeable!
  411. // calculate the size
  412. mpInStream->seekg(0, std::ios_base::end);
  413. mSize = (size_t)mpInStream->tellg();
  414. mpInStream->seekg(0, std::ios_base::beg);
  415. determineAccess();
  416. }
  417. FileDataStream::FileDataStream(const String& name,
  418. std::shared_ptr<std::fstream> s, bool freeOnClose)
  419. : DataStream(name, false), mpInStream(s), mpFStreamRO(0), mpFStream(s), mFreeOnClose(freeOnClose)
  420. {
  421. // writeable!
  422. // calculate the size
  423. mpInStream->seekg(0, std::ios_base::end);
  424. mSize = (size_t)mpInStream->tellg();
  425. mpInStream->seekg(0, std::ios_base::beg);
  426. determineAccess();
  427. }
  428. FileDataStream::FileDataStream(const String& name,
  429. std::shared_ptr<std::fstream> s, size_t inSize, bool freeOnClose)
  430. : DataStream(name, false), mpInStream(s), mpFStreamRO(0), mpFStream(s), mFreeOnClose(freeOnClose)
  431. {
  432. // writeable!
  433. // Size is passed in
  434. mSize = inSize;
  435. determineAccess();
  436. }
  437. void FileDataStream::determineAccess()
  438. {
  439. mAccess = 0;
  440. if (mpInStream)
  441. mAccess |= READ;
  442. if (mpFStream)
  443. mAccess |= WRITE;
  444. }
  445. FileDataStream::~FileDataStream()
  446. {
  447. close();
  448. }
  449. size_t FileDataStream::read(void* buf, size_t count)
  450. {
  451. mpInStream->read(static_cast<char*>(buf), static_cast<std::streamsize>(count));
  452. return (size_t)mpInStream->gcount();
  453. }
  454. size_t FileDataStream::write(const void* buf, size_t count)
  455. {
  456. size_t written = 0;
  457. if (isWriteable() && mpFStream)
  458. {
  459. mpFStream->write(static_cast<const char*>(buf), static_cast<std::streamsize>(count));
  460. written = count;
  461. }
  462. return written;
  463. }
  464. size_t FileDataStream::readLine(char* buf, size_t maxCount, const String& delim)
  465. {
  466. if (delim.empty())
  467. {
  468. CM_EXCEPT(InvalidParametersException, "No delimiter provided");
  469. }
  470. if (delim.size() > 1)
  471. {
  472. gDebug().log("WARNING: FileStreamDataStream::readLine - using only first delimeter", "DataStream");
  473. }
  474. // Deal with both Unix & Windows LFs
  475. bool trimCR = false;
  476. if (delim.at(0) == '\n')
  477. {
  478. trimCR = true;
  479. }
  480. // maxCount + 1 since count excludes terminator in getline
  481. mpInStream->getline(buf, static_cast<std::streamsize>(maxCount+1), delim.at(0));
  482. size_t ret = (size_t)mpInStream->gcount();
  483. // three options
  484. // 1) we had an eof before we read a whole line
  485. // 2) we ran out of buffer space
  486. // 3) we read a whole line - in this case the delim character is taken from the stream but not written in the buffer so the read data is of length ret-1 and thus ends at index ret-2
  487. // in all cases the buffer will be null terminated for us
  488. if (mpInStream->eof())
  489. {
  490. // no problem
  491. }
  492. else if (mpInStream->fail())
  493. {
  494. // Did we fail because of maxCount hit? No - no terminating character
  495. // in included in the count in this case
  496. if (ret == maxCount)
  497. {
  498. // clear failbit for next time
  499. mpInStream->clear();
  500. }
  501. else
  502. {
  503. CM_EXCEPT(InternalErrorException, "Streaming error occurred");
  504. }
  505. }
  506. else
  507. {
  508. // we need to adjust ret because we want to use it as a
  509. // pointer to the terminating null character and it is
  510. // currently the length of the data read from the stream
  511. // i.e. 1 more than the length of the data in the buffer and
  512. // hence 1 more than the _index_ of the NULL character
  513. --ret;
  514. }
  515. // trim off CR if we found CR/LF
  516. if (trimCR && buf[ret-1] == '\r')
  517. {
  518. --ret;
  519. buf[ret] = '\0';
  520. }
  521. return ret;
  522. }
  523. void FileDataStream::skip(long count)
  524. {
  525. #if defined(STLPORT)
  526. // Workaround for STLport issues: After reached eof of file stream,
  527. // it's seems the stream was putted in intermediate state, and will be
  528. // fail if try to repositioning relative to current position.
  529. // Note: tellg() fail in this case too.
  530. if (mpInStream->eof())
  531. {
  532. mpInStream->clear();
  533. // Use seek relative to either begin or end to bring the stream
  534. // back to normal state.
  535. mpInStream->seekg(0, std::ios::end);
  536. }
  537. #endif
  538. mpInStream->clear(); //Clear fail status in case eof was set
  539. mpInStream->seekg(static_cast<std::ifstream::pos_type>(count), std::ios::cur);
  540. }
  541. void FileDataStream::seek( size_t pos )
  542. {
  543. mpInStream->clear(); //Clear fail status in case eof was set
  544. mpInStream->seekg(static_cast<std::streamoff>(pos), std::ios::beg);
  545. }
  546. size_t FileDataStream::tell(void) const
  547. {
  548. mpInStream->clear(); //Clear fail status in case eof was set
  549. return (size_t)mpInStream->tellg();
  550. }
  551. bool FileDataStream::eof(void) const
  552. {
  553. return mpInStream->eof();
  554. }
  555. void FileDataStream::close(void)
  556. {
  557. if (mpInStream)
  558. {
  559. // Unfortunately, there is no file-specific shared class hierarchy between fstream and ifstream (!!)
  560. if (mpFStreamRO)
  561. mpFStreamRO->close();
  562. if (mpFStream)
  563. {
  564. mpFStream->flush();
  565. mpFStream->close();
  566. }
  567. if (mFreeOnClose)
  568. {
  569. mpInStream = nullptr;
  570. mpFStreamRO = nullptr;
  571. mpFStream = nullptr;
  572. }
  573. }
  574. }
  575. }