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 <type_traits>
26 #include <vector>
27 
28 namespace android {
29 namespace netdutils {
30 
31 // Immutable wrapper for a linear region of unowned bytes.
32 // Slice represents memory as a half-closed interval [base, limit).
33 //
34 // Note that without manually invoking the Slice() constructor, it is
35 // impossible to increase the size of a slice. This guarantees that
36 // applications that properly use the slice API will never access
37 // memory outside of a slice.
38 //
39 // Note that const Slice still wraps mutable memory, however copy
40 // assignment and move assignment to slice are disabled.
41 class Slice {
42   public:
43     Slice() = default;
44 
45     // Create a slice beginning at base and continuing to but not including limit
Slice(void * base,void * limit)46     Slice(void* base, void* limit) : mBase(toUint8(base)), mLimit(toUint8(limit)) {}
47 
48     // Create a slice beginning at base and continuing for size bytes
Slice(void * base,size_t size)49     Slice(void* base, size_t size) : Slice(base, toUint8(base) + size) {}
50 
51     // Return the address of the first byte in this slice
base()52     uint8_t* base() const { return mBase; }
53 
54     // Return the address of the first byte following this slice
limit()55     uint8_t* limit() const { return mLimit; }
56 
57     // Return the size of this slice in bytes
size()58     size_t size() const { return limit() - base(); }
59 
60     // Return true if size() == 0
empty()61     bool empty() const { return base() == limit(); }
62 
63   private:
toUint8(void * ptr)64     static uint8_t* toUint8(void* ptr) { return reinterpret_cast<uint8_t*>(ptr); }
65 
66     uint8_t* mBase = nullptr;
67     uint8_t* mLimit = nullptr;
68 };
69 
70 // Return slice representation of ref which must be a POD type
71 template <typename T>
makeSlice(const T & ref)72 inline const Slice makeSlice(const T& ref) {
73     static_assert(std::is_pod<T>::value, "value must be a POD type");
74     static_assert(!std::is_pointer<T>::value, "value must not be a pointer type");
75     return {const_cast<T*>(&ref), sizeof(ref)};
76 }
77 
78 // Return slice representation of string data()
makeSlice(const std::string & s)79 inline const Slice makeSlice(const std::string& s) {
80     using ValueT = std::string::value_type;
81     return {const_cast<ValueT*>(s.data()), s.size() * sizeof(ValueT)};
82 }
83 
84 // Return slice representation of vector data()
85 template <typename T>
makeSlice(const std::vector<T> & v)86 inline const Slice makeSlice(const std::vector<T>& v) {
87     return {const_cast<T*>(v.data()), v.size() * sizeof(T)};
88 }
89 
90 // Return slice representation of array data()
91 template <typename U, size_t V>
makeSlice(const std::array<U,V> & a)92 inline const Slice makeSlice(const std::array<U, V>& a) {
93     return {const_cast<U*>(a.data()), a.size() * sizeof(U)};
94 }
95 
96 // Return prefix and suffix of Slice s ending and starting at position cut
split(const Slice s,size_t cut)97 inline std::pair<const Slice, const Slice> split(const Slice s, size_t cut) {
98     const size_t tmp = std::min(cut, s.size());
99     return {{s.base(), s.base() + tmp}, {s.base() + tmp, s.limit()}};
100 }
101 
102 // Return prefix of Slice s ending at position cut
take(const Slice s,size_t cut)103 inline const Slice take(const Slice s, size_t cut) {
104     return std::get<0>(split(s, cut));
105 }
106 
107 // Return suffix of Slice s starting at position cut
drop(const Slice s,size_t cut)108 inline const Slice drop(const Slice s, size_t cut) {
109     return std::get<1>(split(s, cut));
110 }
111 
112 // Copy from src into dst. Bytes copied is the lesser of dst.size() and src.size()
copy(const Slice dst,const Slice src)113 inline size_t copy(const Slice dst, const Slice src) {
114     const auto min = std::min(dst.size(), src.size());
115     memcpy(dst.base(), src.base(), min);
116     return min;
117 }
118 
119 // Base case for variadic extract below
120 template <typename Head>
extract(const Slice src,Head & head)121 inline size_t extract(const Slice src, Head& head) {
122     return copy(makeSlice(head), src);
123 }
124 
125 // Copy from src into one or more pointers to POD data.  If src.size()
126 // is less than the sum of all data pointers a suffix of data will be
127 // left unmodified. Return the number of bytes copied.
128 template <typename Head, typename... Tail>
extract(const Slice src,Head & head,Tail &...tail)129 inline size_t extract(const Slice src, Head& head, Tail&... tail) {
130     const auto extracted = extract(src, head);
131     return extracted + extract(drop(src, extracted), tail...);
132 }
133 
134 // Return a string containing a copy of the contents of s
135 std::string toString(const Slice s);
136 
137 // Return a string containing a hexadecimal representation of the contents of s.
138 // This function inserts a newline into its output every wrap bytes.
139 std::string toHex(const Slice s, int wrap = INT_MAX);
140 
141 inline bool operator==(const Slice& lhs, const Slice& rhs) {
142     return (lhs.base() == rhs.base()) && (lhs.limit() == rhs.limit());
143 }
144 
145 inline bool operator!=(const Slice& lhs, const Slice& rhs) {
146     return !(lhs == rhs);
147 }
148 
149 std::ostream& operator<<(std::ostream& os, const Slice& slice);
150 
151 // Return suffix of Slice s starting at the first match of byte c. If no matched
152 // byte, return an empty Slice.
findFirstMatching(const Slice s,uint8_t c)153 inline const Slice findFirstMatching(const Slice s, uint8_t c) {
154     uint8_t* match = (uint8_t*)memchr(s.base(), c, s.size());
155     if (!match) return Slice();
156     return drop(s, match - s.base());
157 }
158 
159 }  // namespace netdutils
160 }  // namespace android
161 
162 #endif /* NETUTILS_SLICE_H */
163