/indra/llmessage/llbufferstream.cpp

https://bitbucket.org/lindenlab/viewer-beta/ · C++ · 352 lines · 228 code · 23 blank · 101 comment · 41 complexity · b30836f76442528922091a36097a2703 MD5 · raw file

  1. /**
  2. * @file llbufferstream.cpp
  3. * @author Phoenix
  4. * @date 2005-10-10
  5. * @brief Implementation of the buffer iostream classes
  6. *
  7. * $LicenseInfo:firstyear=2005&license=viewerlgpl$
  8. * Second Life Viewer Source Code
  9. * Copyright (C) 2010, Linden Research, Inc.
  10. *
  11. * This library is free software; you can redistribute it and/or
  12. * modify it under the terms of the GNU Lesser General Public
  13. * License as published by the Free Software Foundation;
  14. * version 2.1 of the License only.
  15. *
  16. * This library is distributed in the hope that it will be useful,
  17. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  19. * Lesser General Public License for more details.
  20. *
  21. * You should have received a copy of the GNU Lesser General Public
  22. * License along with this library; if not, write to the Free Software
  23. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  24. *
  25. * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
  26. * $/LicenseInfo$
  27. */
  28. #include "linden_common.h"
  29. #include "llbufferstream.h"
  30. #include "llbuffer.h"
  31. #include "llmemtype.h"
  32. #include "llthread.h"
  33. static const S32 DEFAULT_OUTPUT_SEGMENT_SIZE = 1024 * 4;
  34. /*
  35. * LLBufferStreamBuf
  36. */
  37. LLBufferStreamBuf::LLBufferStreamBuf(
  38. const LLChannelDescriptors& channels,
  39. LLBufferArray* buffer) :
  40. mChannels(channels),
  41. mBuffer(buffer)
  42. {
  43. LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
  44. }
  45. LLBufferStreamBuf::~LLBufferStreamBuf()
  46. {
  47. LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
  48. sync();
  49. }
  50. // virtual
  51. int LLBufferStreamBuf::underflow()
  52. {
  53. LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
  54. //lldebugs << "LLBufferStreamBuf::underflow()" << llendl;
  55. if(!mBuffer)
  56. {
  57. return EOF;
  58. }
  59. LLMutexLock lock(mBuffer->getMutex());
  60. LLBufferArray::segment_iterator_t iter;
  61. LLBufferArray::segment_iterator_t end = mBuffer->endSegment();
  62. U8* last_pos = (U8*)gptr();
  63. LLSegment segment;
  64. if(last_pos)
  65. {
  66. // Back up into a piece of memory we know that we have
  67. // allocated so that calls for the next segment based on
  68. // 'after' will succeed.
  69. --last_pos;
  70. iter = mBuffer->splitAfter(last_pos);
  71. if(iter != end)
  72. {
  73. // We need to clear the read segment just in case we have
  74. // an early exit in the function and never collect the
  75. // next segment. Calling eraseSegment() with the same
  76. // segment twice is just like double deleting -- nothing
  77. // good comes from it.
  78. mBuffer->eraseSegment(iter++);
  79. if(iter != end) segment = (*iter);
  80. }
  81. else
  82. {
  83. // This should never really happen, but somehow, the
  84. // istream is telling the buf that it just finished
  85. // reading memory that is not in the buf. I think this
  86. // would only happen if there were a bug in the c++ stream
  87. // class. Just bail.
  88. // *TODO: can we set the fail bit on the stream somehow?
  89. return EOF;
  90. }
  91. }
  92. else
  93. {
  94. // Get iterator to full segment containing last_pos
  95. // and construct sub-segment starting at last_pos.
  96. // Note: segment may != *it at this point
  97. iter = mBuffer->constructSegmentAfter(last_pos, segment);
  98. }
  99. if(iter == end)
  100. {
  101. return EOF;
  102. }
  103. // Iterate through segments to find a non-empty segment on input channel.
  104. while((!segment.isOnChannel(mChannels.in()) || (segment.size() == 0)))
  105. {
  106. ++iter;
  107. if(iter == end)
  108. {
  109. return EOF;
  110. }
  111. segment = *(iter);
  112. }
  113. // set up the stream to read from the next segment.
  114. char* start = (char*)segment.data();
  115. setg(start, start, start + segment.size());
  116. return *gptr();
  117. }
  118. // virtual
  119. int LLBufferStreamBuf::overflow(int c)
  120. {
  121. LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
  122. if(!mBuffer)
  123. {
  124. return EOF;
  125. }
  126. if(EOF == c)
  127. {
  128. // if someone puts an EOF, I suppose we should sync and return
  129. // success.
  130. if(0 == sync())
  131. {
  132. return 1;
  133. }
  134. else
  135. {
  136. return EOF;
  137. }
  138. }
  139. // since we got here, we have a buffer, and we have a character to
  140. // put on it.
  141. LLBufferArray::segment_iterator_t it;
  142. LLMutexLock lock(mBuffer->getMutex());
  143. it = mBuffer->makeSegment(mChannels.out(), DEFAULT_OUTPUT_SEGMENT_SIZE);
  144. if(it != mBuffer->endSegment())
  145. {
  146. char* start = (char*)(*it).data();
  147. (*start) = (char)(c);
  148. setp(start + 1, start + (*it).size());
  149. return c;
  150. }
  151. else
  152. {
  153. return EOF;
  154. }
  155. }
  156. // virtual
  157. int LLBufferStreamBuf::sync()
  158. {
  159. LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
  160. int return_value = -1;
  161. if(!mBuffer)
  162. {
  163. return return_value;
  164. }
  165. // This chunk of code is not necessary because typically, users of
  166. // the stream will read until EOF. Therefore, underflow was called
  167. // and the segment was discarded before the sync() was called in
  168. // the destructor. Theoretically, we could keep some more data
  169. // around and detect the rare case where an istream was deleted
  170. // before reading to the end, but that will only leave behind some
  171. // unavailable but still referenced memory. Also, if another
  172. // istream is constructed, it will re-read that segment, and then
  173. // discard it.
  174. //U8* last_pos = (U8*)gptr();
  175. //if(last_pos)
  176. //{
  177. // // Looks like we read something. Discard what we have read.
  178. // // gptr() actually returns the currrent position, but we call
  179. // // it last_pos because of how it is used in the split call
  180. // // below.
  181. // --last_pos;
  182. // LLBufferArray::segment_iterator_t iter;
  183. // iter = mBuffer->splitAfter(last_pos);
  184. // if(iter != mBuffer->endSegment())
  185. // {
  186. // // We need to clear the read segment just in case we have
  187. // // an early exit in the function and never collect the
  188. // // next segment. Calling eraseSegment() with the same
  189. // // segment twice is just like double deleting -- nothing
  190. // // good comes from it.
  191. // mBuffer->eraseSegment(iter);
  192. // }
  193. //}
  194. // set the put pointer so that we force an overflow on the next
  195. // write.
  196. U8* address = (U8*)pptr();
  197. setp(NULL, NULL);
  198. // *NOTE: I bet we could just --address if address is not NULL.
  199. // Need to think about that.
  200. LLMutexLock lock(mBuffer->getMutex());
  201. address = mBuffer->seek(mChannels.out(), address, -1);
  202. if(address)
  203. {
  204. LLBufferArray::segment_iterator_t it;
  205. it = mBuffer->splitAfter(address);
  206. LLBufferArray::segment_iterator_t end = mBuffer->endSegment();
  207. if(it != end)
  208. {
  209. ++it;
  210. if(it != end)
  211. {
  212. mBuffer->eraseSegment(it);
  213. }
  214. return_value = 0;
  215. }
  216. }
  217. else
  218. {
  219. // nothing was put on the buffer, so the sync() is a no-op.
  220. return_value = 0;
  221. }
  222. return return_value;
  223. }
  224. // virtual
  225. #if( LL_WINDOWS || __GNUC__ > 2)
  226. LLBufferStreamBuf::pos_type LLBufferStreamBuf::seekoff(
  227. LLBufferStreamBuf::off_type off,
  228. std::ios::seekdir way,
  229. std::ios::openmode which)
  230. #else
  231. streampos LLBufferStreamBuf::seekoff(
  232. streamoff off,
  233. std::ios::seekdir way,
  234. std::ios::openmode which)
  235. #endif
  236. {
  237. LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
  238. if(!mBuffer
  239. || ((way == std::ios::beg) && (off < 0))
  240. || ((way == std::ios::end) && (off > 0)))
  241. {
  242. return -1;
  243. }
  244. U8* address = NULL;
  245. if(which & std::ios::in)
  246. {
  247. U8* base_addr = NULL;
  248. switch(way)
  249. {
  250. case std::ios::end:
  251. base_addr = (U8*)LLBufferArray::npos;
  252. break;
  253. case std::ios::cur:
  254. // get the current get pointer and adjust it for buffer
  255. // array semantics.
  256. base_addr = (U8*)gptr();
  257. break;
  258. case std::ios::beg:
  259. default:
  260. // NULL is fine
  261. break;
  262. }
  263. LLMutexLock lock(mBuffer->getMutex());
  264. address = mBuffer->seek(mChannels.in(), base_addr, off);
  265. if(address)
  266. {
  267. LLBufferArray::segment_iterator_t iter;
  268. iter = mBuffer->getSegment(address);
  269. char* start = (char*)(*iter).data();
  270. setg(start, (char*)address, start + (*iter).size());
  271. }
  272. else
  273. {
  274. address = (U8*)(-1);
  275. }
  276. }
  277. if(which & std::ios::out)
  278. {
  279. U8* base_addr = NULL;
  280. switch(way)
  281. {
  282. case std::ios::end:
  283. base_addr = (U8*)LLBufferArray::npos;
  284. break;
  285. case std::ios::cur:
  286. // get the current put pointer and adjust it for buffer
  287. // array semantics.
  288. base_addr = (U8*)pptr();
  289. break;
  290. case std::ios::beg:
  291. default:
  292. // NULL is fine
  293. break;
  294. }
  295. LLMutexLock lock(mBuffer->getMutex());
  296. address = mBuffer->seek(mChannels.out(), base_addr, off);
  297. if(address)
  298. {
  299. LLBufferArray::segment_iterator_t iter;
  300. iter = mBuffer->getSegment(address);
  301. setp((char*)address, (char*)(*iter).data() + (*iter).size());
  302. }
  303. else
  304. {
  305. address = (U8*)(-1);
  306. }
  307. }
  308. #if( LL_WINDOWS || __GNUC__ > 2 )
  309. S32 rv = (S32)(intptr_t)address;
  310. return (pos_type)rv;
  311. #else
  312. return (streampos)address;
  313. #endif
  314. }
  315. /*
  316. * LLBufferStream
  317. */
  318. LLBufferStream::LLBufferStream(
  319. const LLChannelDescriptors& channels,
  320. LLBufferArray* buffer) :
  321. std::iostream(&mStreamBuf),
  322. mStreamBuf(channels, buffer)
  323. {
  324. LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
  325. }
  326. LLBufferStream::~LLBufferStream()
  327. {
  328. LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
  329. }