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 // Copyright (c) 2011 The LevelDB Authors. All rights reserved.
6 // Use of this source code is governed by a BSD-style license that can be
7 // found in the LICENSE file. See the AUTHORS file for names of contributors.
8 //
9 // Slice is a simple structure containing a pointer into some external
10 // storage and a size. The user of a Slice must ensure that the slice
11 // is not used after the corresponding external storage has been
12 // deallocated.
13 //
14 // Multiple threads can invoke const methods on a Slice without
15 // external synchronization, but if any of the threads may call a
16 // non-const method, all threads accessing the same Slice must use
17 // external synchronization.
18
19 #pragma once
20
21 #include <assert.h>
22 #include <stddef.h>
23 #include <string.h>
24 #include <cstdio>
25 #include <string>
26
27 #ifdef __cpp_lib_string_view
28 #include <string_view>
29 #endif
30
31 #include "rocksdb/cleanable.h"
32
33 namespace ROCKSDB_NAMESPACE {
34
35 class Slice {
36 public:
37 // Create an empty slice.
Slice()38 Slice() : data_(""), size_(0) {}
39
40 // Create a slice that refers to d[0,n-1].
Slice(const char * d,size_t n)41 Slice(const char* d, size_t n) : data_(d), size_(n) {}
42
43 // Create a slice that refers to the contents of "s"
44 /* implicit */
Slice(const std::string & s)45 Slice(const std::string& s) : data_(s.data()), size_(s.size()) {}
46
47 #ifdef __cpp_lib_string_view
48 // Create a slice that refers to the same contents as "sv"
49 /* implicit */
Slice(std::string_view sv)50 Slice(std::string_view sv) : data_(sv.data()), size_(sv.size()) {}
51 #endif
52
53 // Create a slice that refers to s[0,strlen(s)-1]
54 /* implicit */
Slice(const char * s)55 Slice(const char* s) : data_(s) { size_ = (s == nullptr) ? 0 : strlen(s); }
56
57 // Create a single slice from SliceParts using buf as storage.
58 // buf must exist as long as the returned Slice exists.
59 Slice(const struct SliceParts& parts, std::string* buf);
60
61 // Return a pointer to the beginning of the referenced data
data()62 const char* data() const { return data_; }
63
64 // Return the length (in bytes) of the referenced data
size()65 size_t size() const { return size_; }
66
67 // Return true iff the length of the referenced data is zero
empty()68 bool empty() const { return size_ == 0; }
69
70 // Return the ith byte in the referenced data.
71 // REQUIRES: n < size()
72 char operator[](size_t n) const {
73 assert(n < size());
74 return data_[n];
75 }
76
77 // Change this slice to refer to an empty array
clear()78 void clear() {
79 data_ = "";
80 size_ = 0;
81 }
82
83 // Drop the first "n" bytes from this slice.
remove_prefix(size_t n)84 void remove_prefix(size_t n) {
85 assert(n <= size());
86 data_ += n;
87 size_ -= n;
88 }
89
remove_suffix(size_t n)90 void remove_suffix(size_t n) {
91 assert(n <= size());
92 size_ -= n;
93 }
94
95 // Return a string that contains the copy of the referenced data.
96 // when hex is true, returns a string of twice the length hex encoded (0-9A-F)
97 std::string ToString(bool hex = false) const;
98
99 #ifdef __cpp_lib_string_view
100 // Return a string_view that references the same data as this slice.
ToStringView()101 std::string_view ToStringView() const {
102 return std::string_view(data_, size_);
103 }
104 #endif
105
106 // Decodes the current slice interpreted as an hexadecimal string into result,
107 // if successful returns true, if this isn't a valid hex string
108 // (e.g not coming from Slice::ToString(true)) DecodeHex returns false.
109 // This slice is expected to have an even number of 0-9A-F characters
110 // also accepts lowercase (a-f)
111 bool DecodeHex(std::string* result) const;
112
113 // Three-way comparison. Returns value:
114 // < 0 iff "*this" < "b",
115 // == 0 iff "*this" == "b",
116 // > 0 iff "*this" > "b"
117 int compare(const Slice& b) const;
118
119 // Return true iff "x" is a prefix of "*this"
starts_with(const Slice & x)120 bool starts_with(const Slice& x) const {
121 return ((size_ >= x.size_) && (memcmp(data_, x.data_, x.size_) == 0));
122 }
123
ends_with(const Slice & x)124 bool ends_with(const Slice& x) const {
125 return ((size_ >= x.size_) &&
126 (memcmp(data_ + size_ - x.size_, x.data_, x.size_) == 0));
127 }
128
129 // Compare two slices and returns the first byte where they differ
130 size_t difference_offset(const Slice& b) const;
131
132 // private: make these public for rocksdbjni access
133 const char* data_;
134 size_t size_;
135
136 // Intentionally copyable
137 };
138
139 /**
140 * A Slice that can be pinned with some cleanup tasks, which will be run upon
141 * ::Reset() or object destruction, whichever is invoked first. This can be used
142 * to avoid memcpy by having the PinnableSlice object referring to the data
143 * that is locked in the memory and release them after the data is consumed.
144 */
145 class PinnableSlice : public Slice, public Cleanable {
146 public:
PinnableSlice()147 PinnableSlice() { buf_ = &self_space_; }
PinnableSlice(std::string * buf)148 explicit PinnableSlice(std::string* buf) { buf_ = buf; }
149
150 PinnableSlice(PinnableSlice&& other);
151 PinnableSlice& operator=(PinnableSlice&& other);
152
153 // No copy constructor and copy assignment allowed.
154 PinnableSlice(PinnableSlice&) = delete;
155 PinnableSlice& operator=(PinnableSlice&) = delete;
156
PinSlice(const Slice & s,CleanupFunction f,void * arg1,void * arg2)157 inline void PinSlice(const Slice& s, CleanupFunction f, void* arg1,
158 void* arg2) {
159 assert(!pinned_);
160 pinned_ = true;
161 data_ = s.data();
162 size_ = s.size();
163 RegisterCleanup(f, arg1, arg2);
164 assert(pinned_);
165 }
166
PinSlice(const Slice & s,Cleanable * cleanable)167 inline void PinSlice(const Slice& s, Cleanable* cleanable) {
168 assert(!pinned_);
169 pinned_ = true;
170 data_ = s.data();
171 size_ = s.size();
172 cleanable->DelegateCleanupsTo(this);
173 assert(pinned_);
174 }
175
PinSelf(const Slice & slice)176 inline void PinSelf(const Slice& slice) {
177 assert(!pinned_);
178 buf_->assign(slice.data(), slice.size());
179 data_ = buf_->data();
180 size_ = buf_->size();
181 assert(!pinned_);
182 }
183
PinSelf()184 inline void PinSelf() {
185 assert(!pinned_);
186 data_ = buf_->data();
187 size_ = buf_->size();
188 assert(!pinned_);
189 }
190
remove_suffix(size_t n)191 void remove_suffix(size_t n) {
192 assert(n <= size());
193 if (pinned_) {
194 size_ -= n;
195 } else {
196 buf_->erase(size() - n, n);
197 PinSelf();
198 }
199 }
200
remove_prefix(size_t n)201 void remove_prefix(size_t n) {
202 assert(n <= size());
203 if (pinned_) {
204 data_ += n;
205 size_ -= n;
206 } else {
207 buf_->erase(0, n);
208 PinSelf();
209 }
210 }
211
Reset()212 void Reset() {
213 Cleanable::Reset();
214 pinned_ = false;
215 size_ = 0;
216 }
217
GetSelf()218 inline std::string* GetSelf() { return buf_; }
219
IsPinned()220 inline bool IsPinned() const { return pinned_; }
221
222 private:
223 friend class PinnableSlice4Test;
224 std::string self_space_;
225 std::string* buf_;
226 bool pinned_ = false;
227 };
228
229 // A set of Slices that are virtually concatenated together. 'parts' points
230 // to an array of Slices. The number of elements in the array is 'num_parts'.
231 struct SliceParts {
SlicePartsSliceParts232 SliceParts(const Slice* _parts, int _num_parts)
233 : parts(_parts), num_parts(_num_parts) {}
SlicePartsSliceParts234 SliceParts() : parts(nullptr), num_parts(0) {}
235
236 const Slice* parts;
237 int num_parts;
238 };
239
240 inline bool operator==(const Slice& x, const Slice& y) {
241 return ((x.size() == y.size()) &&
242 (memcmp(x.data(), y.data(), x.size()) == 0));
243 }
244
245 inline bool operator!=(const Slice& x, const Slice& y) { return !(x == y); }
246
compare(const Slice & b)247 inline int Slice::compare(const Slice& b) const {
248 assert(data_ != nullptr && b.data_ != nullptr);
249 const size_t min_len = (size_ < b.size_) ? size_ : b.size_;
250 int r = memcmp(data_, b.data_, min_len);
251 if (r == 0) {
252 if (size_ < b.size_)
253 r = -1;
254 else if (size_ > b.size_)
255 r = +1;
256 }
257 return r;
258 }
259
difference_offset(const Slice & b)260 inline size_t Slice::difference_offset(const Slice& b) const {
261 size_t off = 0;
262 const size_t len = (size_ < b.size_) ? size_ : b.size_;
263 for (; off < len; off++) {
264 if (data_[off] != b.data_[off]) break;
265 }
266 return off;
267 }
268
269 } // namespace ROCKSDB_NAMESPACE
270