Joel Scherpelz | f3fa5cc | 2017-05-22 12:30:03 +0900 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2017 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | #ifndef NETUTILS_SLICE_H |
| 18 | #define NETUTILS_SLICE_H |
| 19 | |
| 20 | #include <algorithm> |
| 21 | #include <array> |
| 22 | #include <cstring> |
| 23 | #include <ostream> |
| 24 | #include <tuple> |
| 25 | #include <vector> |
| 26 | |
| 27 | namespace android { |
| 28 | namespace netdutils { |
| 29 | |
| 30 | // Immutable wrapper for a linear region of unowned bytes. |
| 31 | // Slice represents memory as a half-closed interval [base, limit). |
| 32 | // |
| 33 | // Note that without manually invoking the Slice() constructor, it is |
| 34 | // impossible to increase the size of a slice. This guarantees that |
| 35 | // applications that properly use the slice API will never access |
| 36 | // memory outside of a slice. |
| 37 | // |
| 38 | // Note that const Slice still wraps mutable memory, however copy |
| 39 | // assignment and move assignment to slice are disabled. |
| 40 | class Slice { |
| 41 | public: |
| 42 | Slice() = default; |
| 43 | |
| 44 | // Create a slice beginning at base and continuing to but not including limit |
| 45 | Slice(void* base, void* limit) : mBase(toUint8(base)), mLimit(toUint8(limit)) {} |
| 46 | |
| 47 | // Create a slice beginning at base and continuing for size bytes |
| 48 | Slice(void* base, size_t size) : Slice(base, toUint8(base) + size) {} |
| 49 | |
| 50 | // Return the address of the first byte in this slice |
| 51 | uint8_t* base() const { return mBase; } |
| 52 | |
| 53 | // Return the address of the first byte following this slice |
| 54 | uint8_t* limit() const { return mLimit; } |
| 55 | |
| 56 | // Return the size of this slice in bytes |
| 57 | size_t size() const { return limit() - base(); } |
| 58 | |
| 59 | // Return true if size() == 0 |
| 60 | bool empty() const { return base() == limit(); } |
| 61 | |
| 62 | private: |
| 63 | static uint8_t* toUint8(void* ptr) { return reinterpret_cast<uint8_t*>(ptr); } |
| 64 | |
| 65 | uint8_t* mBase = nullptr; |
| 66 | uint8_t* mLimit = nullptr; |
| 67 | }; |
| 68 | |
| 69 | // Return slice representation of ref which must be a POD type |
| 70 | template <typename T> |
| 71 | inline const Slice makeSlice(const T& ref) { |
| 72 | static_assert(std::is_pod<T>::value, "value must be a POD type"); |
| 73 | static_assert(!std::is_pointer<T>::value, "value must not be a pointer type"); |
| 74 | return {const_cast<T*>(&ref), sizeof(ref)}; |
| 75 | } |
| 76 | |
Joel Scherpelz | de93796 | 2017-06-01 13:20:21 +0900 | [diff] [blame] | 77 | // Return slice representation of string data() |
| 78 | inline const Slice makeSlice(const std::string& s) { |
| 79 | using ValueT = std::string::value_type; |
| 80 | return {const_cast<ValueT*>(s.data()), s.size() * sizeof(ValueT)}; |
| 81 | } |
| 82 | |
Joel Scherpelz | f3fa5cc | 2017-05-22 12:30:03 +0900 | [diff] [blame] | 83 | // Return slice representation of vector data() |
| 84 | template <typename T> |
| 85 | inline const Slice makeSlice(const std::vector<T>& v) { |
| 86 | return {const_cast<T*>(v.data()), v.size() * sizeof(T)}; |
| 87 | } |
| 88 | |
| 89 | // Return slice representation of array data() |
| 90 | template <typename U, size_t V> |
| 91 | inline const Slice makeSlice(const std::array<U, V>& a) { |
| 92 | return {const_cast<U*>(a.data()), a.size() * sizeof(U)}; |
| 93 | } |
| 94 | |
| 95 | // Return prefix and suffix of Slice s ending and starting at position cut |
| 96 | inline std::pair<const Slice, const Slice> split(const Slice s, size_t cut) { |
| 97 | const size_t tmp = std::min(cut, s.size()); |
| 98 | return {{s.base(), s.base() + tmp}, {s.base() + tmp, s.limit()}}; |
| 99 | } |
| 100 | |
| 101 | // Return prefix of Slice s ending at position cut |
| 102 | inline const Slice take(const Slice s, size_t cut) { |
| 103 | return std::get<0>(split(s, cut)); |
| 104 | } |
| 105 | |
| 106 | // Return suffix of Slice s starting at position cut |
| 107 | inline const Slice drop(const Slice s, size_t cut) { |
| 108 | return std::get<1>(split(s, cut)); |
| 109 | } |
| 110 | |
| 111 | // Copy from src into dst. Bytes copied is the lesser of dst.size() and src.size() |
| 112 | inline size_t copy(const Slice dst, const Slice src) { |
| 113 | const auto min = std::min(dst.size(), src.size()); |
| 114 | memcpy(dst.base(), src.base(), min); |
| 115 | return min; |
| 116 | } |
| 117 | |
| 118 | // Base case for variadic extract below |
| 119 | template <typename Head> |
| 120 | inline size_t extract(const Slice src, Head& head) { |
| 121 | return copy(makeSlice(head), src); |
| 122 | } |
| 123 | |
| 124 | // Copy from src into one or more pointers to POD data. If src.size() |
| 125 | // is less than the sum of all data pointers a suffix of data will be |
| 126 | // left unmodified. Return the number of bytes copied. |
| 127 | template <typename Head, typename... Tail> |
Jonathan Basseri | 288745d | 2017-10-04 16:41:56 -0700 | [diff] [blame] | 128 | inline size_t extract(const Slice src, Head& head, Tail&... tail) { |
Joel Scherpelz | f3fa5cc | 2017-05-22 12:30:03 +0900 | [diff] [blame] | 129 | const auto extracted = extract(src, head); |
| 130 | return extracted + extract(drop(src, extracted), tail...); |
| 131 | } |
| 132 | |
| 133 | // Return a string containing a copy of the contents of s |
| 134 | std::string toString(const Slice s); |
| 135 | |
| 136 | // Return a string containing a hexadecimal representation of the contents of s. |
| 137 | // This function inserts a newline into its output every wrap bytes. |
| 138 | std::string toHex(const Slice s, int wrap); |
| 139 | |
| 140 | inline bool operator==(const Slice& lhs, const Slice& rhs) { |
| 141 | return (lhs.base() == rhs.base()) && (lhs.limit() == rhs.limit()); |
| 142 | } |
| 143 | |
| 144 | inline bool operator!=(const Slice& lhs, const Slice& rhs) { |
| 145 | return !(lhs == rhs); |
| 146 | } |
| 147 | |
| 148 | std::ostream& operator<<(std::ostream& os, const Slice& slice); |
| 149 | |
Chenbo Feng | dbbc000 | 2018-04-12 19:05:19 -0700 | [diff] [blame] | 150 | // Return suffix of Slice s starting at the first match of byte c. If no matched |
| 151 | // byte, return an empty Slice. |
| 152 | inline const Slice findFirstMatching(const Slice s, uint8_t c) { |
| 153 | uint8_t* match = (uint8_t*)memchr(s.base(), c, s.size()); |
| 154 | if (!match) return Slice(); |
| 155 | return drop(s, match - s.base()); |
| 156 | } |
| 157 | |
Joel Scherpelz | f3fa5cc | 2017-05-22 12:30:03 +0900 | [diff] [blame] | 158 | } // namespace netdutils |
| 159 | } // namespace android |
| 160 | |
| 161 | #endif /* NETUTILS_SLICE_H */ |