aligned_buffer.h 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. // Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
  2. // This source code is licensed under both the GPLv2 (found in the
  3. // COPYING file in the root directory) and Apache 2.0 License
  4. // (found in the LICENSE.Apache file in the root directory).
  5. //
  6. // Copyright (c) 2011 The LevelDB Authors. All rights reserved.
  7. // Use of this source code is governed by a BSD-style license that can be
  8. // found in the LICENSE file. See the AUTHORS file for names of contributors.
  9. #pragma once
  10. #include <algorithm>
  11. #include "port/port.h"
  12. namespace ROCKSDB_NAMESPACE {
  13. // This file contains utilities to handle the alignment of pages and buffers.
  14. // Truncate to a multiple of page_size, which is also a page boundary. This
  15. // helps to figuring out the right alignment.
  16. // Example:
  17. // TruncateToPageBoundary(5000, 4096) => 4096
  18. // TruncateToPageBoundary(10000, 4096) => 8192
  19. inline size_t TruncateToPageBoundary(size_t page_size, size_t s) {
  20. s -= (s & (page_size - 1));
  21. assert((s % page_size) == 0);
  22. return s;
  23. }
  24. // Round up x to a multiple of y.
  25. // Example:
  26. // Roundup(13, 5) => 15
  27. // Roundup(201, 16) => 208
  28. inline size_t Roundup(size_t x, size_t y) {
  29. return ((x + y - 1) / y) * y;
  30. }
  31. // Round down x to a multiple of y.
  32. // Example:
  33. // Rounddown(13, 5) => 10
  34. // Rounddown(201, 16) => 192
  35. inline size_t Rounddown(size_t x, size_t y) { return (x / y) * y; }
  36. // AlignedBuffer manages a buffer by taking alignment into consideration, and
  37. // aligns the buffer start and end positions. It is mainly used for direct I/O,
  38. // though it can be used other purposes as well.
  39. // It also supports expanding the managed buffer, and copying whole or part of
  40. // the data from old buffer into the new expanded buffer. Such a copy especially
  41. // helps in cases avoiding an IO to re-fetch the data from disk.
  42. //
  43. // Example:
  44. // AlignedBuffer buf;
  45. // buf.Alignment(alignment);
  46. // buf.AllocateNewBuffer(user_requested_buf_size);
  47. // ...
  48. // buf.AllocateNewBuffer(2*user_requested_buf_size, /*copy_data*/ true,
  49. // copy_offset, copy_len);
  50. class AlignedBuffer {
  51. size_t alignment_;
  52. std::unique_ptr<char[]> buf_;
  53. size_t capacity_;
  54. size_t cursize_;
  55. char* bufstart_;
  56. public:
  57. AlignedBuffer()
  58. : alignment_(),
  59. capacity_(0),
  60. cursize_(0),
  61. bufstart_(nullptr) {
  62. }
  63. AlignedBuffer(AlignedBuffer&& o) ROCKSDB_NOEXCEPT {
  64. *this = std::move(o);
  65. }
  66. AlignedBuffer& operator=(AlignedBuffer&& o) ROCKSDB_NOEXCEPT {
  67. alignment_ = std::move(o.alignment_);
  68. buf_ = std::move(o.buf_);
  69. capacity_ = std::move(o.capacity_);
  70. cursize_ = std::move(o.cursize_);
  71. bufstart_ = std::move(o.bufstart_);
  72. return *this;
  73. }
  74. AlignedBuffer(const AlignedBuffer&) = delete;
  75. AlignedBuffer& operator=(const AlignedBuffer&) = delete;
  76. static bool isAligned(const void* ptr, size_t alignment) {
  77. return reinterpret_cast<uintptr_t>(ptr) % alignment == 0;
  78. }
  79. static bool isAligned(size_t n, size_t alignment) {
  80. return n % alignment == 0;
  81. }
  82. size_t Alignment() const {
  83. return alignment_;
  84. }
  85. size_t Capacity() const {
  86. return capacity_;
  87. }
  88. size_t CurrentSize() const {
  89. return cursize_;
  90. }
  91. const char* BufferStart() const {
  92. return bufstart_;
  93. }
  94. char* BufferStart() { return bufstart_; }
  95. void Clear() {
  96. cursize_ = 0;
  97. }
  98. void Alignment(size_t alignment) {
  99. assert(alignment > 0);
  100. assert((alignment & (alignment - 1)) == 0);
  101. alignment_ = alignment;
  102. }
  103. // Allocates a new buffer and sets the start position to the first aligned
  104. // byte.
  105. //
  106. // requested_capacity: requested new buffer capacity. This capacity will be
  107. // rounded up based on alignment.
  108. // copy_data: Copy data from old buffer to new buffer. If copy_offset and
  109. // copy_len are not passed in and the new requested capacity is bigger
  110. // than the existing buffer's capacity, the data in the exising buffer is
  111. // fully copied over to the new buffer.
  112. // copy_offset: Copy data from this offset in old buffer.
  113. // copy_len: Number of bytes to copy.
  114. //
  115. // The function does nothing if the new requested_capacity is smaller than
  116. // the current buffer capacity and copy_data is true i.e. the old buffer is
  117. // retained as is.
  118. void AllocateNewBuffer(size_t requested_capacity, bool copy_data = false,
  119. uint64_t copy_offset = 0, size_t copy_len = 0) {
  120. assert(alignment_ > 0);
  121. assert((alignment_ & (alignment_ - 1)) == 0);
  122. copy_len = copy_len > 0 ? copy_len : cursize_;
  123. if (copy_data && requested_capacity < copy_len) {
  124. // If we are downsizing to a capacity that is smaller than the current
  125. // data in the buffer -- Ignore the request.
  126. return;
  127. }
  128. size_t new_capacity = Roundup(requested_capacity, alignment_);
  129. char* new_buf = new char[new_capacity + alignment_];
  130. char* new_bufstart = reinterpret_cast<char*>(
  131. (reinterpret_cast<uintptr_t>(new_buf) + (alignment_ - 1)) &
  132. ~static_cast<uintptr_t>(alignment_ - 1));
  133. if (copy_data) {
  134. assert(bufstart_ + copy_offset + copy_len <= bufstart_ + cursize_);
  135. memcpy(new_bufstart, bufstart_ + copy_offset, copy_len);
  136. cursize_ = copy_len;
  137. } else {
  138. cursize_ = 0;
  139. }
  140. bufstart_ = new_bufstart;
  141. capacity_ = new_capacity;
  142. buf_.reset(new_buf);
  143. }
  144. // Append to the buffer.
  145. //
  146. // src : source to copy the data from.
  147. // append_size : number of bytes to copy from src.
  148. // Returns the number of bytes appended.
  149. //
  150. // If append_size is more than the remaining buffer size only the
  151. // remaining-size worth of bytes are copied.
  152. size_t Append(const char* src, size_t append_size) {
  153. size_t buffer_remaining = capacity_ - cursize_;
  154. size_t to_copy = std::min(append_size, buffer_remaining);
  155. if (to_copy > 0) {
  156. memcpy(bufstart_ + cursize_, src, to_copy);
  157. cursize_ += to_copy;
  158. }
  159. return to_copy;
  160. }
  161. // Read from the buffer.
  162. //
  163. // dest : destination buffer to copy the data to.
  164. // offset : the buffer offset to start reading from.
  165. // read_size : the number of bytes to copy from the buffer to dest.
  166. // Returns the number of bytes read/copied to dest.
  167. size_t Read(char* dest, size_t offset, size_t read_size) const {
  168. assert(offset < cursize_);
  169. size_t to_read = 0;
  170. if(offset < cursize_) {
  171. to_read = std::min(cursize_ - offset, read_size);
  172. }
  173. if (to_read > 0) {
  174. memcpy(dest, bufstart_ + offset, to_read);
  175. }
  176. return to_read;
  177. }
  178. // Pad to the end of alignment with "padding"
  179. void PadToAlignmentWith(int padding) {
  180. size_t total_size = Roundup(cursize_, alignment_);
  181. size_t pad_size = total_size - cursize_;
  182. if (pad_size > 0) {
  183. assert((pad_size + cursize_) <= capacity_);
  184. memset(bufstart_ + cursize_, padding, pad_size);
  185. cursize_ += pad_size;
  186. }
  187. }
  188. void PadWith(size_t pad_size, int padding) {
  189. assert((pad_size + cursize_) <= capacity_);
  190. memset(bufstart_ + cursize_, padding, pad_size);
  191. cursize_ += pad_size;
  192. }
  193. // After a partial flush move the tail to the beginning of the buffer.
  194. void RefitTail(size_t tail_offset, size_t tail_size) {
  195. if (tail_size > 0) {
  196. memmove(bufstart_, bufstart_ + tail_offset, tail_size);
  197. }
  198. cursize_ = tail_size;
  199. }
  200. // Returns a place to start appending.
  201. // WARNING: Note that it is possible to write past the end of the buffer if
  202. // the buffer is modified without using the write APIs or encapsulation
  203. // offered by AlignedBuffer. It is up to the user to guard against such
  204. // errors.
  205. char* Destination() {
  206. return bufstart_ + cursize_;
  207. }
  208. void Size(size_t cursize) {
  209. cursize_ = cursize;
  210. }
  211. };
  212. } // namespace ROCKSDB_NAMESPACE