1 | /* |
2 | * Copyright 2013-present Facebook, Inc. |
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 | #pragma once |
18 | |
19 | #include <algorithm> |
20 | #include <iterator> |
21 | #include <type_traits> |
22 | |
23 | #include <folly/io/IOBuf.h> |
24 | #include <folly/memory/Malloc.h> |
25 | |
26 | namespace folly { |
27 | |
28 | /** |
29 | * Wrapper class to handle a IOBuf as a typed buffer (to a standard layout |
30 | * class). |
31 | * |
32 | * This class punts on alignment, and assumes that you know what you're doing. |
33 | * |
34 | * All methods are wrappers around the corresponding IOBuf methods. The |
35 | * TypedIOBuf object is stateless, so it's perfectly okay to access the |
36 | * underlying IOBuf in between TypedIOBuf method calls. |
37 | */ |
38 | template <class T> |
39 | class TypedIOBuf { |
40 | static_assert(std::is_standard_layout<T>::value, "must be standard layout" ); |
41 | |
42 | public: |
43 | typedef T value_type; |
44 | typedef value_type& reference; |
45 | typedef const value_type& const_reference; |
46 | typedef uint32_t size_type; |
47 | typedef value_type* iterator; |
48 | typedef const value_type* const_iterator; |
49 | |
50 | explicit TypedIOBuf(IOBuf* buf) : buf_(buf) {} |
51 | |
52 | IOBuf* ioBuf() { |
53 | return buf_; |
54 | } |
55 | const IOBuf* ioBuf() const { |
56 | return buf_; |
57 | } |
58 | |
59 | bool empty() const { |
60 | return buf_->empty(); |
61 | } |
62 | const T* data() const { |
63 | return cast(buf_->data()); |
64 | } |
65 | T* writableData() { |
66 | return cast(buf_->writableData()); |
67 | } |
68 | const T* tail() const { |
69 | return cast(buf_->tail()); |
70 | } |
71 | T* writableTail() { |
72 | return cast(buf_->writableTail()); |
73 | } |
74 | uint32_t length() const { |
75 | return sdiv(buf_->length()); |
76 | } |
77 | uint32_t size() const { |
78 | return length(); |
79 | } |
80 | |
81 | uint32_t headroom() const { |
82 | return sdiv(buf_->headroom()); |
83 | } |
84 | uint32_t tailroom() const { |
85 | return sdiv(buf_->tailroom()); |
86 | } |
87 | const T* buffer() const { |
88 | return cast(buf_->buffer()); |
89 | } |
90 | T* writableBuffer() { |
91 | return cast(buf_->writableBuffer()); |
92 | } |
93 | const T* bufferEnd() const { |
94 | return cast(buf_->bufferEnd()); |
95 | } |
96 | uint32_t capacity() const { |
97 | return sdiv(buf_->capacity()); |
98 | } |
99 | void advance(uint32_t n) { |
100 | buf_->advance(smul(n)); |
101 | } |
102 | void retreat(uint32_t n) { |
103 | buf_->retreat(smul(n)); |
104 | } |
105 | void prepend(uint32_t n) { |
106 | buf_->prepend(smul(n)); |
107 | } |
108 | void append(uint32_t n) { |
109 | buf_->append(smul(n)); |
110 | } |
111 | void trimStart(uint32_t n) { |
112 | buf_->trimStart(smul(n)); |
113 | } |
114 | void trimEnd(uint32_t n) { |
115 | buf_->trimEnd(smul(n)); |
116 | } |
117 | void clear() { |
118 | buf_->clear(); |
119 | } |
120 | void reserve(uint32_t minHeadroom, uint32_t minTailroom) { |
121 | buf_->reserve(smul(minHeadroom), smul(minTailroom)); |
122 | } |
123 | void reserve(uint32_t minTailroom) { |
124 | reserve(0, minTailroom); |
125 | } |
126 | |
127 | const T* cbegin() const { |
128 | return data(); |
129 | } |
130 | const T* cend() const { |
131 | return tail(); |
132 | } |
133 | const T* begin() const { |
134 | return cbegin(); |
135 | } |
136 | const T* end() const { |
137 | return cend(); |
138 | } |
139 | T* begin() { |
140 | return writableData(); |
141 | } |
142 | T* end() { |
143 | return writableTail(); |
144 | } |
145 | |
146 | const T& front() const { |
147 | assert(!empty()); |
148 | return *begin(); |
149 | } |
150 | T& front() { |
151 | assert(!empty()); |
152 | return *begin(); |
153 | } |
154 | const T& back() const { |
155 | assert(!empty()); |
156 | return end()[-1]; |
157 | } |
158 | T& back() { |
159 | assert(!empty()); |
160 | return end()[-1]; |
161 | } |
162 | |
163 | /** |
164 | * Simple wrapper to make it easier to treat this TypedIOBuf as an array of |
165 | * T. |
166 | */ |
167 | const T& operator[](ssize_t idx) const { |
168 | assert(idx >= 0 && idx < length()); |
169 | return data()[idx]; |
170 | } |
171 | |
172 | T& operator[](ssize_t idx) { |
173 | assert(idx >= 0 && idx < length()); |
174 | return writableData()[idx]; |
175 | } |
176 | |
177 | /** |
178 | * Append one element. |
179 | */ |
180 | void push(const T& data) { |
181 | push(&data, &data + 1); |
182 | } |
183 | void push_back(const T& data) { |
184 | push(data); |
185 | } |
186 | |
187 | /** |
188 | * Append multiple elements in a sequence; will call distance(). |
189 | */ |
190 | template <class IT> |
191 | void push(IT begin, IT end) { |
192 | uint32_t n = std::distance(begin, end); |
193 | if (usingJEMalloc()) { |
194 | // Rely on xallocx() and avoid exponential growth to limit |
195 | // amount of memory wasted. |
196 | reserve(headroom(), n); |
197 | } else if (tailroom() < n) { |
198 | reserve(headroom(), std::max(n, 3 + size() / 2)); |
199 | } |
200 | std::copy(begin, end, writableTail()); |
201 | append(n); |
202 | } |
203 | |
204 | // Movable |
205 | TypedIOBuf(TypedIOBuf&&) = default; |
206 | TypedIOBuf& operator=(TypedIOBuf&&) = default; |
207 | |
208 | private: |
209 | // Non-copyable |
210 | TypedIOBuf(const TypedIOBuf&) = delete; |
211 | TypedIOBuf& operator=(const TypedIOBuf&) = delete; |
212 | |
213 | // cast to T* |
214 | static T* cast(uint8_t* p) { |
215 | return reinterpret_cast<T*>(p); |
216 | } |
217 | static const T* cast(const uint8_t* p) { |
218 | return reinterpret_cast<const T*>(p); |
219 | } |
220 | // divide by size |
221 | static uint32_t sdiv(uint32_t n) { |
222 | return n / sizeof(T); |
223 | } |
224 | // multiply by size |
225 | static uint32_t smul(uint32_t n) { |
226 | // In debug mode, check for overflow |
227 | assert((uint64_t(n) * sizeof(T)) < (uint64_t(1) << 32)); |
228 | return n * sizeof(T); |
229 | } |
230 | |
231 | IOBuf* buf_; |
232 | }; |
233 | |
234 | } // namespace folly |
235 | |