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