PageRenderTime 46ms CodeModel.GetById 14ms RepoModel.GetById 1ms app.codeStats 0ms

/mordor/streams/buffered.cpp

http://github.com/mozy/mordor
C++ | 364 lines | 312 code | 32 blank | 20 comment | 78 complexity | 392cbb87c17f6903e315c24c4f0e9af9 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. // Copyright (c) 2009 - Mozy, Inc.
  2. #include "buffered.h"
  3. #include "mordor/config.h"
  4. #include "mordor/exception.h"
  5. #include "mordor/log.h"
  6. namespace Mordor {
  7. static ConfigVar<size_t>::ptr g_defaultBufferSize =
  8. Config::lookup<size_t>("stream.buffered.defaultbuffersize", 65536,
  9. "Default buffer size for new BufferedStreams");
  10. static Logger::ptr g_log = Log::lookup("mordor:streams:buffered");
  11. BufferedStream::BufferedStream(Stream::ptr parent, bool own)
  12. : FilterStream(parent, own)
  13. {
  14. m_bufferSize = g_defaultBufferSize->val();
  15. m_allowPartialReads = false;
  16. m_flushMultiplesOfBuffer = false;
  17. }
  18. void
  19. BufferedStream::close(CloseType type)
  20. {
  21. MORDOR_LOG_VERBOSE(g_log) << this << " close(" << type << ")";
  22. if (type & READ)
  23. m_readBuffer.clear();
  24. try {
  25. if ((type & WRITE) && m_writeBuffer.readAvailable())
  26. flush(false);
  27. } catch (...) {
  28. if (ownsParent())
  29. parent()->close(type);
  30. throw;
  31. }
  32. if (ownsParent())
  33. parent()->close(type);
  34. }
  35. size_t
  36. BufferedStream::read(Buffer &buffer, size_t length)
  37. {
  38. return readInternal(buffer, length);
  39. }
  40. size_t
  41. BufferedStream::read(void *buffer, size_t length)
  42. {
  43. return readInternal(buffer, length);
  44. }
  45. // Buffer keeps track of its position automatically
  46. static void advance(Buffer &buffer, size_t amount)
  47. {}
  48. // void * does not
  49. static void advance(void *&buffer, size_t amount)
  50. {
  51. (unsigned char *&)buffer += amount;
  52. }
  53. template <class T>
  54. size_t
  55. BufferedStream::readInternal(T &buffer, size_t length)
  56. {
  57. if (supportsSeek())
  58. flush(false);
  59. size_t remaining = length;
  60. size_t buffered = (std::min)(m_readBuffer.readAvailable(), remaining);
  61. m_readBuffer.copyOut(buffer, buffered);
  62. m_readBuffer.consume(buffered);
  63. advance(buffer, buffered);
  64. remaining -= buffered;
  65. MORDOR_LOG_VERBOSE(g_log) << this << " read(" << length << "): "
  66. << buffered << " read from buffer";
  67. if (remaining == 0)
  68. return length;
  69. if (buffered == 0 || !m_allowPartialReads) {
  70. size_t result;
  71. do {
  72. // Read enough to satisfy this request, plus up to a multiple of
  73. // the buffer size
  74. size_t todo = ((remaining - 1) / m_bufferSize + 1) * m_bufferSize;
  75. try {
  76. MORDOR_LOG_TRACE(g_log) << this << " parent()->read(" << todo
  77. << ")";
  78. result = parent()->read(m_readBuffer, todo);
  79. MORDOR_LOG_DEBUG(g_log) << this << " parent()->read(" << todo
  80. << "): " << result;
  81. } catch (...) {
  82. if (remaining == length) {
  83. MORDOR_LOG_VERBOSE(g_log) << this << " forwarding exception";
  84. throw;
  85. } else {
  86. MORDOR_LOG_VERBOSE(g_log) << this << " swallowing exception";
  87. // Swallow the exception
  88. return length - remaining;
  89. }
  90. }
  91. buffered = (std::min)(m_readBuffer.readAvailable(), remaining);
  92. m_readBuffer.copyOut(buffer, buffered);
  93. m_readBuffer.consume(buffered);
  94. advance(buffer, buffered);
  95. remaining -= buffered;
  96. } while (remaining > 0 && !m_allowPartialReads && result != 0);
  97. }
  98. return length - remaining;
  99. }
  100. size_t
  101. BufferedStream::write(const Buffer &buffer, size_t length)
  102. {
  103. m_writeBuffer.copyIn(buffer, length);
  104. size_t result = flushWrite(length);
  105. // Partial writes not allowed
  106. MORDOR_ASSERT(result == length);
  107. return result;
  108. }
  109. size_t
  110. BufferedStream::write(const void *buffer, size_t length)
  111. {
  112. m_writeBuffer.reserve((std::max)(m_bufferSize, length));
  113. m_writeBuffer.copyIn(buffer, length);
  114. size_t result = flushWrite(length);
  115. // Partial writes not allowed
  116. MORDOR_ASSERT(result == length);
  117. return result;
  118. }
  119. size_t
  120. BufferedStream::flushWrite(size_t length)
  121. {
  122. while (m_writeBuffer.readAvailable() >= m_bufferSize)
  123. {
  124. size_t result;
  125. try {
  126. if (supportsSeek() && m_readBuffer.readAvailable()) {
  127. parent()->seek(-(long long)m_readBuffer.readAvailable(), CURRENT);
  128. m_readBuffer.clear();
  129. }
  130. size_t toWrite = m_writeBuffer.readAvailable();
  131. if (m_flushMultiplesOfBuffer)
  132. toWrite = toWrite / m_bufferSize * m_bufferSize;
  133. MORDOR_LOG_TRACE(g_log) << this << " parent()->write("
  134. << toWrite << ")";
  135. result = parent()->write(m_writeBuffer, toWrite);
  136. MORDOR_LOG_DEBUG(g_log) << this << " parent()->write("
  137. << toWrite << "): " << result;
  138. m_writeBuffer.consume(result);
  139. } catch (...) {
  140. // If this entire write is still in our buffer,
  141. // back it out and report the error
  142. if (m_writeBuffer.readAvailable() >= length) {
  143. MORDOR_LOG_VERBOSE(g_log) << this << " forwarding exception";
  144. Buffer tempBuffer;
  145. tempBuffer.copyIn(m_writeBuffer, m_writeBuffer.readAvailable()
  146. - length);
  147. m_writeBuffer.clear();
  148. m_writeBuffer.copyIn(tempBuffer);
  149. throw;
  150. } else {
  151. // Otherwise we have to say we succeeded,
  152. // because we're not allowed to have a partial
  153. // write, and we can't report an error because
  154. // the caller will think he needs to repeat
  155. // the entire write
  156. MORDOR_LOG_VERBOSE(g_log) << this << " swallowing exception";
  157. return length;
  158. }
  159. }
  160. }
  161. return length;
  162. }
  163. long long
  164. BufferedStream::seek(long long offset, Anchor anchor)
  165. {
  166. MORDOR_ASSERT(parent()->supportsTell());
  167. long long parentPos = parent()->tell();
  168. long long bufferedPos = parentPos - m_readBuffer.readAvailable()
  169. + m_writeBuffer.readAvailable();
  170. long long parentSize = parent()->supportsSize() ? parent()->size() : -1ll;
  171. // Check for no change in position
  172. if ((offset == 0 && anchor == CURRENT) ||
  173. (offset == bufferedPos && anchor == BEGIN) ||
  174. (parentSize != -1ll && offset + parentSize == bufferedPos &&
  175. anchor == END))
  176. return bufferedPos;
  177. MORDOR_ASSERT(supportsSeek());
  178. flush(false);
  179. MORDOR_ASSERT(m_writeBuffer.readAvailable() == 0u);
  180. switch (anchor) {
  181. case BEGIN:
  182. MORDOR_ASSERT(offset >= 0);
  183. if (offset >= bufferedPos && offset <= parentPos) {
  184. m_readBuffer.consume((size_t)(offset - bufferedPos));
  185. return offset;
  186. }
  187. m_readBuffer.clear();
  188. break;
  189. case CURRENT:
  190. if (offset > 0 && offset <= (long long)m_readBuffer.readAvailable()) {
  191. m_readBuffer.consume((size_t)offset);
  192. return bufferedPos + offset;
  193. }
  194. offset -= m_readBuffer.readAvailable();
  195. m_readBuffer.clear();
  196. break;
  197. case END:
  198. if (parentSize == -1ll)
  199. throw std::invalid_argument("Can't seek from end without known size");
  200. if (parentSize + offset >= bufferedPos && parentSize + offset <= parentPos) {
  201. m_readBuffer.consume((size_t)(parentSize + offset - bufferedPos));
  202. return parentSize + offset;
  203. }
  204. m_readBuffer.clear();
  205. break;
  206. default:
  207. MORDOR_NOTREACHED();
  208. }
  209. return parent()->seek(offset, anchor);
  210. }
  211. long long
  212. BufferedStream::size()
  213. {
  214. long long size = parent()->size();
  215. if (parent()->supportsTell()) {
  216. return (std::max)(size, tell());
  217. } else {
  218. // not a seekable stream; we can only write to the end
  219. size += m_writeBuffer.readAvailable();
  220. }
  221. return size;
  222. }
  223. void
  224. BufferedStream::truncate(long long size)
  225. {
  226. if (!parent()->supportsTell() ||
  227. parent()->tell() + (long long)m_writeBuffer.readAvailable() >= size)
  228. flush(false);
  229. // TODO: truncate/clear m_readBuffer only if necessary
  230. m_readBuffer.clear();
  231. parent()->truncate(size);
  232. }
  233. void
  234. BufferedStream::flush(bool flushParent)
  235. {
  236. while (m_writeBuffer.readAvailable()) {
  237. if (supportsSeek() && m_readBuffer.readAvailable()) {
  238. parent()->seek(-(long long)m_readBuffer.readAvailable(), CURRENT);
  239. m_readBuffer.clear();
  240. }
  241. MORDOR_LOG_TRACE(g_log) << this << " parent()->write("
  242. << m_writeBuffer.readAvailable() << ")";
  243. size_t result = parent()->write(m_writeBuffer, m_writeBuffer.readAvailable());
  244. MORDOR_LOG_DEBUG(g_log) << this << " parent()->write("
  245. << m_writeBuffer.readAvailable() << "): " << result;
  246. MORDOR_ASSERT(result > 0);
  247. m_writeBuffer.consume(result);
  248. }
  249. if (flushParent)
  250. parent()->flush();
  251. }
  252. ptrdiff_t
  253. BufferedStream::find(char delim, size_t sanitySize, bool throwIfNotFound)
  254. {
  255. if (supportsSeek())
  256. flush(false);
  257. if (sanitySize == (size_t)~0)
  258. sanitySize = 2 * m_bufferSize;
  259. ++sanitySize;
  260. while (true) {
  261. size_t readAvailable = m_readBuffer.readAvailable();
  262. if (readAvailable > 0) {
  263. ptrdiff_t result = m_readBuffer.find(delim,
  264. (std::min)(sanitySize, readAvailable));
  265. if (result != -1) {
  266. return result;
  267. }
  268. }
  269. if (readAvailable >= sanitySize) {
  270. if (throwIfNotFound)
  271. MORDOR_THROW_EXCEPTION(BufferOverflowException());
  272. return -(ptrdiff_t)m_readBuffer.readAvailable() - 1;
  273. }
  274. MORDOR_LOG_TRACE(g_log) << this << " parent()->read(" << m_bufferSize
  275. << ")";
  276. size_t result = parent()->read(m_readBuffer, m_bufferSize);
  277. MORDOR_LOG_DEBUG(g_log) << this << " parent()->read(" << m_bufferSize
  278. << "): " << result;
  279. if (result == 0) {
  280. // EOF
  281. if (throwIfNotFound)
  282. MORDOR_THROW_EXCEPTION(UnexpectedEofException());
  283. return -(ptrdiff_t)m_readBuffer.readAvailable() - 1;
  284. }
  285. }
  286. }
  287. ptrdiff_t
  288. BufferedStream::find(const std::string &str, size_t sanitySize, bool throwIfNotFound)
  289. {
  290. if (supportsSeek())
  291. flush(false);
  292. if (sanitySize == (size_t)~0)
  293. sanitySize = 2 * m_bufferSize;
  294. sanitySize += str.size();
  295. while (true) {
  296. size_t readAvailable = m_readBuffer.readAvailable();
  297. if (readAvailable > 0) {
  298. ptrdiff_t result = m_readBuffer.find(str,
  299. (std::min)(sanitySize, readAvailable));
  300. if (result != -1) {
  301. return result;
  302. }
  303. }
  304. if (readAvailable >= sanitySize) {
  305. if (throwIfNotFound)
  306. MORDOR_THROW_EXCEPTION(BufferOverflowException());
  307. return -(ptrdiff_t)m_readBuffer.readAvailable() - 1;
  308. }
  309. MORDOR_LOG_TRACE(g_log) << this << " parent()->read(" << m_bufferSize
  310. << ")";
  311. size_t result = parent()->read(m_readBuffer, m_bufferSize);
  312. MORDOR_LOG_DEBUG(g_log) << this << " parent()->read(" << m_bufferSize
  313. << "): " << result;
  314. if (result == 0) {
  315. // EOF
  316. if (throwIfNotFound)
  317. MORDOR_THROW_EXCEPTION(UnexpectedEofException());
  318. return -(ptrdiff_t)m_readBuffer.readAvailable() - 1;
  319. }
  320. }
  321. }
  322. void
  323. BufferedStream::unread(const Buffer &b, size_t len)
  324. {
  325. MORDOR_ASSERT(supportsUnread());
  326. Buffer tempBuffer;
  327. tempBuffer.copyIn(b, len);
  328. tempBuffer.copyIn(m_readBuffer);
  329. m_readBuffer.clear();
  330. m_readBuffer.copyIn(tempBuffer);
  331. }
  332. }