llcorebufferarray.cpp 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. /**
  2. * @file llcorebufferarray.cpp
  3. * @brief Implements the BufferArray scatter/gather buffer
  4. *
  5. * $LicenseInfo:firstyear=2012&license=viewerlgpl$
  6. *
  7. * Copyright (C) 2012, Linden Research, Inc.
  8. *
  9. * Second Life Viewer Source Code
  10. * The source code in this file ("Source Code") is provided by Linden Lab
  11. * to you under the terms of the GNU General Public License, version 2.0
  12. * ("GPL"), unless you have obtained a separate licensing agreement
  13. * ("Other License"), formally executed by you and Linden Lab. Terms of
  14. * the GPL can be found in doc/GPL-license.txt in this distribution, or
  15. * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
  16. *
  17. * There are special exceptions to the terms and conditions of the GPL as
  18. * it is applied to this Source Code. View the full text of the exception
  19. * in the file doc/FLOSS-exception.txt in this software distribution, or
  20. * online at
  21. * http://secondlifegrid.net/programs/open_source/licensing/flossexception
  22. *
  23. * By copying, modifying or distributing this software, you acknowledge
  24. * that you have read and understood your obligations described above,
  25. * and agree to abide by those obligations.
  26. *
  27. * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
  28. * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
  29. * COMPLETENESS OR PERFORMANCE.
  30. * $/LicenseInfo$
  31. */
  32. #include "linden_common.h"
  33. #include "llcorebufferarray.h"
  34. // BufferArray is a list of chunks, each a BufferArray::Block, of contiguous
  35. // data presented as a single array. Chunks are at least BLOCK_ALLOC_SIZE in
  36. // length and can be larger. Any chunk may be partially filled or even empty.
  37. //
  38. // The BufferArray itself is sharable as a RefCounted entity. As shared reads
  39. // do not work with the concept of a current position/seek value, none is kept
  40. // with the object. Instead, the read and write operations all take position
  41. // arguments. Single write/shared read is not supported directly and any such
  42. // attempts have to be serialized outside of this implementation.
  43. namespace LLCore
  44. {
  45. ///////////////////////////////////////////////////////////////////////////////
  46. // BufferArray::Block sub-class
  47. ///////////////////////////////////////////////////////////////////////////////
  48. class BufferArray::Block
  49. {
  50. public:
  51. Block(const Block&) = delete;
  52. void operator=(const Block&) = delete;
  53. Block(size_t len)
  54. : mAllocated(len),
  55. mUsed(0)
  56. {
  57. mDataPtr = (char*)malloc(len);
  58. if (!mDataPtr)
  59. {
  60. llerrs << "Failure to allocate a new memory block of "
  61. << len << " bytes !" << llendl;
  62. }
  63. memset(mDataPtr, 0, len);
  64. }
  65. LL_INLINE ~Block() noexcept
  66. {
  67. free((void*)mDataPtr);
  68. }
  69. public:
  70. char* mDataPtr;
  71. size_t mAllocated;
  72. size_t mUsed;
  73. };
  74. ///////////////////////////////////////////////////////////////////////////////
  75. // BufferArray class
  76. ///////////////////////////////////////////////////////////////////////////////
  77. constexpr size_t BLOCK_ALLOC_SIZE = 131072;
  78. BufferArray::BufferArray()
  79. : LLCoreInt::RefCounted(true),
  80. mLen(0)
  81. {
  82. // Allow to allocate up to 4 Mb before having to grow mBlocks
  83. mBlocks.reserve(32);
  84. }
  85. BufferArray::~BufferArray()
  86. {
  87. for (container_t::iterator it = mBlocks.begin(), end = mBlocks.end();
  88. it != end; ++it)
  89. {
  90. delete *it;
  91. *it = NULL;
  92. }
  93. mBlocks.clear();
  94. }
  95. size_t BufferArray::append(const void* src, size_t len)
  96. {
  97. const size_t ret = len;
  98. const char* c_src = (const char*)src;
  99. // First, try to copy into the last block
  100. if (len && !mBlocks.empty())
  101. {
  102. Block& last = *mBlocks.back();
  103. if (last.mUsed < last.mAllocated)
  104. {
  105. // Some will fit...
  106. const size_t copy_len = llmin(len, last.mAllocated - last.mUsed);
  107. memcpy(last.mDataPtr + last.mUsed, c_src, copy_len);
  108. last.mUsed += copy_len;
  109. mLen += copy_len;
  110. c_src += copy_len;
  111. len -= copy_len;
  112. }
  113. }
  114. // Then get new blocks as needed
  115. while (len)
  116. {
  117. const size_t copy_len = llmin(len, BLOCK_ALLOC_SIZE);
  118. Block* block = new Block(BLOCK_ALLOC_SIZE);
  119. memcpy(block->mDataPtr, c_src, copy_len);
  120. block->mUsed = copy_len;
  121. mBlocks.push_back(block);
  122. mLen += copy_len;
  123. c_src += copy_len;
  124. len -= copy_len;
  125. }
  126. return ret;
  127. }
  128. // If someone asks for zero-length, we give them a valid pointer.
  129. void* BufferArray::appendBufferAlloc(size_t len)
  130. {
  131. Block* block = new Block(llmax(BLOCK_ALLOC_SIZE, len));
  132. block->mUsed = len;
  133. mBlocks.push_back(block);
  134. mLen += len;
  135. return block->mDataPtr;
  136. }
  137. size_t BufferArray::read(size_t pos, void* dst, size_t len)
  138. {
  139. if (pos >= mLen)
  140. {
  141. return 0;
  142. }
  143. len = llmin(len, mLen - pos);
  144. if (!len)
  145. {
  146. return 0;
  147. }
  148. size_t offset = 0;
  149. S32 block_start = findBlock(pos, offset);
  150. if (block_start < 0)
  151. {
  152. return 0;
  153. }
  154. size_t result = 0;
  155. char* c_dst = (char*)dst;
  156. S32 block_limit = (S32)mBlocks.size();
  157. do
  158. {
  159. Block& block = *mBlocks[block_start++];
  160. size_t block_len = llmin(block.mUsed - offset, len);
  161. memcpy(c_dst, block.mDataPtr + offset, block_len);
  162. result += block_len;
  163. len -= block_len;
  164. c_dst += block_len;
  165. offset = 0;
  166. }
  167. while (len > 0 && block_start < block_limit);
  168. return result;
  169. }
  170. size_t BufferArray::write(size_t pos, const void* src, size_t len)
  171. {
  172. if (pos > mLen || len == 0)
  173. {
  174. return 0;
  175. }
  176. size_t result = 0;
  177. const char* c_src = (const char*)src;
  178. size_t offset = 0;
  179. S32 block_start = findBlock(pos, offset);
  180. if (block_start >= 0)
  181. {
  182. S32 block_limit = (S32)mBlocks.size();
  183. // Some or all of the write will be on top of existing data.
  184. do
  185. {
  186. Block& block = *mBlocks[block_start++];
  187. size_t block_len = llmin(block.mUsed - offset, len);
  188. memcpy(block.mDataPtr + offset, c_src, block_len);
  189. result += block_len;
  190. c_src += block_len;
  191. len -= block_len;
  192. offset = 0;
  193. }
  194. while (len > 0 && block_start < block_limit);
  195. }
  196. // Something left, see if it will fit in the free space of the last block.
  197. if (len && !mBlocks.empty())
  198. {
  199. Block& last = *mBlocks.back();
  200. if (last.mUsed < last.mAllocated)
  201. {
  202. // Some will fit...
  203. const size_t copy_len = llmin(len, last.mAllocated - last.mUsed);
  204. memcpy(last.mDataPtr + last.mUsed, c_src, copy_len);
  205. last.mUsed += copy_len;
  206. result += copy_len;
  207. mLen += copy_len;
  208. c_src += copy_len;
  209. len -= copy_len;
  210. }
  211. }
  212. if (len)
  213. {
  214. // Some or all of the remaining write data will be an append.
  215. result += append(c_src, len);
  216. }
  217. return result;
  218. }
  219. // Note: returns a signed integer and not a size_t (which is usually unsigned),
  220. // so that we can return -1 when no block is found.
  221. S32 BufferArray::findBlock(size_t pos, size_t& ret_offset) const
  222. {
  223. ret_offset = 0;
  224. if (pos >= mLen)
  225. {
  226. return -1; // Does not exist
  227. }
  228. for (size_t i = 0, block_limit = mBlocks.size(); i < block_limit; ++i)
  229. {
  230. if (pos < mBlocks[i]->mUsed)
  231. {
  232. ret_offset = pos;
  233. return i;
  234. }
  235. pos -= mBlocks[i]->mUsed;
  236. }
  237. // Should not get here but...
  238. return -1;
  239. }
  240. bool BufferArray::getBlockStartEnd(S32 block, const char** start,
  241. const char** end)
  242. {
  243. if (block < 0 || (size_t)block >= mBlocks.size())
  244. {
  245. return false;
  246. }
  247. const Block& b = *mBlocks[block];
  248. *start = b.mDataPtr;
  249. *end = b.mDataPtr + b.mUsed;
  250. return true;
  251. }
  252. } // End namespace LLCore