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
26namespace 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 */
38template <class T>
39class 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