| // Licensed to the Apache Software Foundation (ASF) under one | |
| // or more contributor license agreements. See the NOTICE file | |
| // distributed with this work for additional information | |
| // regarding copyright ownership. The ASF licenses this file | |
| // to you under the Apache License, Version 2.0 (the | |
| // "License"); you may not use this file except in compliance | |
| // with the License. You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, | |
| // software distributed under the License is distributed on an | |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
| // KIND, either express or implied. See the License for the | |
| // specific language governing permissions and limitations | |
| // under the License. | |
| #pragma once | |
| #include <assert.h> | |
| #include <stddef.h> | |
| #include <stdint.h> | |
| #include <string.h> | |
| #include <iostream> | |
| #include <map> | |
| #include <string> | |
| #include <utility> | |
| #include <vector> | |
| #include "core/allocator.h" | |
| #include "core/allocator_fwd.h" | |
| namespace doris { | |
| class faststring; | |
| /// @brief A wrapper around externally allocated data. | |
| /// | |
| /// Slice is a simple structure containing a pointer into some external | |
| /// storage and a size. The user of a Slice must ensure that the slice | |
| /// is not used after the corresponding external storage has been | |
| /// deallocated. | |
| /// | |
| /// Multiple threads can invoke const methods on a Slice without | |
| /// external synchronization, but if any of the threads may call a | |
| /// non-const method, all threads accessing the same Slice must use | |
| /// external synchronization. | |
| struct Slice { | |
| public: | |
| char* data = nullptr; | |
| size_t size; | |
| // Intentionally copyable | |
| /// Create an empty slice. | |
| Slice() : data(const_cast<char*>("")), size(0) {} | |
| /// Create a slice that refers to a @c char byte array. | |
| Slice(const char* d, size_t n) : data(const_cast<char*>(d)), size(n) {} | |
| // Create a slice that refers to a @c uint8_t byte array. | |
| // | |
| // @param [in] d | |
| // The input array. | |
| // @param [in] n | |
| // Number of bytes in the array. | |
| Slice(const uint8_t* s, size_t n) | |
| : data(const_cast<char*>(reinterpret_cast<const char*>(s))), size(n) {} | |
| /// Create a slice that refers to the contents of the given string. | |
| Slice(const std::string& s) | |
| : // NOLINT(runtime/explicit) | |
| data(const_cast<char*>(s.data())), | |
| size(s.size()) {} | |
| Slice(const faststring& s); | |
| /// Create a slice that refers to a C-string s[0,strlen(s)-1]. | |
| Slice(const char* s) | |
| : // NOLINT(runtime/explicit) | |
| data(const_cast<char*>(s)), | |
| size(strlen(s)) {} | |
| /// default copy/move constructor and assignment | |
| Slice(const Slice&) = default; | |
| Slice& operator=(const Slice&) = default; | |
| Slice(Slice&&) noexcept = default; | |
| Slice& operator=(Slice&&) noexcept = default; | |
| /// @return A pointer to the beginning of the referenced data. | |
| const char* get_data() const { return data; } | |
| /// @return A mutable pointer to the beginning of the referenced data. | |
| char* mutable_data() { return data; } | |
| /// @return The length (in bytes) of the referenced data. | |
| size_t get_size() const { return size; } | |
| /// @return @c true iff the length of the referenced data is zero. | |
| bool empty() const { return size == 0; } | |
| /// @return the n-th byte in the referenced data. | |
| const char& operator[](size_t n) const { | |
| assert(n < size); | |
| return data[n]; | |
| } | |
| /// Change this slice to refer to an empty array. | |
| void clear() { | |
| data = const_cast<char*>(""); | |
| size = 0; | |
| } | |
| /// Drop the first "n" bytes from this slice. | |
| /// | |
| /// @pre n <= size | |
| /// | |
| /// @note Only the base and bounds of the slice are changed; | |
| /// the data is not modified. | |
| /// | |
| /// @param [in] n | |
| /// Number of bytes that should be dropped from the beginning. | |
| void remove_prefix(size_t n) { | |
| assert(n <= size); | |
| data += n; | |
| size -= n; | |
| } | |
| /// Drop the last "n" bytes from this slice. | |
| /// | |
| /// @pre n <= size | |
| /// | |
| /// @note Only the base and bounds of the slice are changed; | |
| /// the data is not modified. | |
| /// | |
| /// @param [in] n | |
| /// Number of bytes that should be dropped from the last. | |
| void remove_suffix(size_t n) { | |
| assert(n <= size); | |
| size -= n; | |
| } | |
| /// Remove leading spaces. | |
| /// | |
| /// @pre n <= size | |
| /// | |
| /// @note Only the base and bounds of the slice are changed; | |
| /// the data is not modified. | |
| /// | |
| /// @param [in] n | |
| /// Number of bytes of space that should be dropped from the beginning. | |
| void trim_prefix() { | |
| int32_t begin = 0; | |
| while (begin < size && data[begin] == ' ') { | |
| data += 1; | |
| size -= 1; | |
| } | |
| } | |
| /// Remove quote char '"' or ''' which should exist as first and last char. | |
| /// | |
| /// @pre n <= size | |
| /// | |
| /// @note Only the base and bounds of the slice are changed; | |
| /// the data is not modified. | |
| /// | |
| /// @param [in] n | |
| /// Number of bytes of space that should be dropped from the beginning. | |
| bool trim_quote() { | |
| int32_t begin = 0; | |
| bool change = false; | |
| if (size >= 2 && ((data[begin] == '"' && data[size - 1] == '"') || | |
| (data[begin] == '\'' && data[size - 1] == '\''))) { | |
| data += 1; | |
| size -= 2; | |
| change = true; | |
| } | |
| return change; | |
| } | |
| /// Remove quote char '"' which should exist as first and last char. | |
| /// | |
| /// @pre n <= size | |
| /// | |
| /// @note Only the base and bounds of the slice are changed; | |
| /// the data is not modified. | |
| /// | |
| /// @param [in] n | |
| /// Number of bytes of space that should be dropped from the beginning. | |
| bool trim_double_quotes() { | |
| int32_t begin = 0; | |
| if (size >= 2 && (data[begin] == '"' && data[size - 1] == '"')) { | |
| data += 1; | |
| size -= 2; | |
| return true; | |
| } | |
| return false; | |
| } | |
| /// Truncate the slice to the given number of bytes. | |
| /// | |
| /// @pre n <= size | |
| /// | |
| /// @note Only the base and bounds of the slice are changed; | |
| /// the data is not modified. | |
| /// | |
| /// @param [in] n | |
| /// The new size of the slice. | |
| void truncate(size_t n) { | |
| assert(n <= size); | |
| size = n; | |
| } | |
| /// @return A string that contains a copy of the referenced data. | |
| std::string to_string() const { return std::string(data, size); } | |
| /// Do a three-way comparison of the slice's data. | |
| int compare(const Slice& b) const; | |
| /// Check whether the slice starts with the given prefix. | |
| bool starts_with(const Slice& x) const { | |
| return ((size >= x.size) && (mem_equal(data, x.data, x.size))); | |
| } | |
| bool ends_with(const Slice& x) const { | |
| return ((size >= x.size) && mem_equal(data + (size - x.size), x.data, x.size)); | |
| } | |
| /// @brief Comparator struct, useful for ordered collections (like STL maps). | |
| struct Comparator { | |
| /// Compare two slices using Slice::compare() | |
| /// | |
| /// @param [in] a | |
| /// The slice to call Slice::compare() at. | |
| /// @param [in] b | |
| /// The slice to use as a parameter for Slice::compare(). | |
| /// @return @c true iff @c a is less than @c b by Slice::compare(). | |
| bool operator()(const Slice& a, const Slice& b) const { return a.compare(b) < 0; } | |
| }; | |
| /// Relocate/copy the slice's data into a new location. | |
| /// | |
| /// @param [in] d | |
| /// The new location for the data. If it's the same location, then no | |
| /// relocation is done. It is assumed that the new location is | |
| /// large enough to fit the data. | |
| void relocate(char* d) { | |
| if (data != d) { | |
| memcpy(d, data, size); | |
| data = d; | |
| } | |
| } | |
| friend bool operator==(const Slice& x, const Slice& y); | |
| friend std::ostream& operator<<(std::ostream& os, const Slice& slice); | |
| static bool mem_equal(const void* a, const void* b, size_t n) { return memcmp(a, b, n) == 0; } | |
| static int mem_compare(const void* a, const void* b, size_t n) { return memcmp(a, b, n); } | |
| static size_t compute_total_size(const std::vector<Slice>& slices) { | |
| size_t total_size = 0; | |
| for (auto& slice : slices) { | |
| total_size += slice.size; | |
| } | |
| return total_size; | |
| } | |
| static std::string to_string(const std::vector<Slice>& slices) { | |
| std::string buf; | |
| for (auto& slice : slices) { | |
| buf.append(slice.data, slice.size); | |
| } | |
| return buf; | |
| } | |
| // X is (maybe) a truncated prefix of string X' | |
| // Y is (maybe) a truncated prefix of string Y' | |
| // return true only if we can determine that X' is strictly less than Y' | |
| // based on these maybe truncated prefixes | |
| static bool lhs_is_strictly_less_than_rhs(Slice X, bool X_is_truncated, Slice Y, | |
| bool Y_is_truncated); | |
| }; | |
| inline std::ostream& operator<<(std::ostream& os, const Slice& slice) { | |
| os << slice.to_string(); | |
| return os; | |
| } | |
| /// Check whether two slices are identical. | |
| inline bool operator==(const Slice& x, const Slice& y) { | |
| return ((x.size == y.size) && (Slice::mem_equal(x.data, y.data, x.size))); | |
| } | |
| /// Check whether two slices are not identical. | |
| inline bool operator!=(const Slice& x, const Slice& y) { | |
| return !(x == y); | |
| } | |
| inline int Slice::compare(const Slice& b) const { | |
| const auto min_len = (size < b.size) ? size : b.size; | |
| int r = mem_compare(data, b.data, min_len); | |
| if (r == 0) { | |
| if (size < b.size) | |
| r = -1; | |
| else if (size > b.size) | |
| r = +1; | |
| } | |
| return r; | |
| } | |
| // A move-only type which manage the lifecycle of externally allocated data. | |
| // Unlike DorisUniqueBufferPtr<uint8_t>, OwnedSlice remembers the size of data so that clients can access | |
| // the underlying buffer as a Slice. | |
| // | |
| // Usage example: | |
| // OwnedSlice read_page(PagePointer pp); | |
| // { | |
| // OwnedSlice page_data(new uint8_t[pp.size], pp.size); | |
| // Status s = _file.read_at(pp.offset, owned.slice()); | |
| // if (!s.ok()) { | |
| // return s; // `page_data` destructs, deallocate underlying buffer | |
| // } | |
| // return page_data; // transfer ownership of buffer into the caller | |
| // } | |
| // | |
| // only receive the memory allocated by Allocator and disables mmap, | |
| // otherwise the memory may not be freed correctly, currently only be constructed by faststring. | |
| class OwnedSlice : private Allocator<false, false, false, DefaultMemoryAllocator> { | |
| public: | |
| OwnedSlice() : _slice((uint8_t*)nullptr, 0) {} | |
| OwnedSlice(size_t length) | |
| : _slice(reinterpret_cast<char*>(Allocator::alloc(length)), length), | |
| _capacity(length) {} | |
| OwnedSlice(OwnedSlice&& src) : _slice(src._slice), _capacity(src._capacity) { | |
| src._slice.data = nullptr; | |
| src._slice.size = 0; | |
| src._capacity = 0; | |
| } | |
| OwnedSlice& operator=(OwnedSlice&& src) { | |
| if (this != &src) { | |
| std::swap(_slice, src._slice); | |
| std::swap(_capacity, src._capacity); | |
| } | |
| return *this; | |
| } | |
| // disable copy constructor and copy assignment | |
| OwnedSlice(const OwnedSlice&) = delete; | |
| void operator=(const OwnedSlice&) = delete; | |
| ~OwnedSlice() { | |
| if (_slice.data != nullptr) { | |
| DCHECK(_capacity != 0); | |
| Allocator::free(_slice.data, _capacity); | |
| } | |
| } | |
| char* data() const { return _slice.data; } | |
| const Slice& slice() const { return _slice; } | |
| private: | |
| // faststring also inherits Allocator and disables mmap. | |
| friend class faststring; | |
| OwnedSlice(uint8_t* _data, size_t size, size_t capacity) | |
| : _slice(_data, size), _capacity(capacity) {} | |
| Slice _slice; | |
| size_t _capacity = 0; | |
| }; | |
| } // namespace doris |