86df07c44135d9e367812ce0584182e470de9ff6
[folly.git] / folly / io / TypedIOBuf.h
1 /*
2  * Copyright 2017 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/Malloc.h>
24 #include <folly/io/IOBuf.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  public:
42   typedef T value_type;
43   typedef value_type& reference;
44   typedef const value_type& const_reference;
45   typedef uint32_t size_type;
46   typedef value_type* iterator;
47   typedef const value_type* const_iterator;
48
49   explicit TypedIOBuf(IOBuf* buf) : buf_(buf) { }
50
51   IOBuf* ioBuf() {
52     return buf_;
53   }
54   const IOBuf* ioBuf() const {
55     return buf_;
56   }
57
58   bool empty() const {
59     return buf_->empty();
60   }
61   const T* data() const {
62     return cast(buf_->data());
63   }
64   T* writableData() {
65     return cast(buf_->writableData());
66   }
67   const T* tail() const {
68     return cast(buf_->tail());
69   }
70   T* writableTail() {
71     return cast(buf_->writableTail());
72   }
73   uint32_t length() const {
74     return sdiv(buf_->length());
75   }
76   uint32_t size() const { return length(); }
77
78   uint32_t headroom() const {
79     return sdiv(buf_->headroom());
80   }
81   uint32_t tailroom() const {
82     return sdiv(buf_->tailroom());
83   }
84   const T* buffer() const {
85     return cast(buf_->buffer());
86   }
87   T* writableBuffer() {
88     return cast(buf_->writableBuffer());
89   }
90   const T* bufferEnd() const {
91     return cast(buf_->bufferEnd());
92   }
93   uint32_t capacity() const {
94     return sdiv(buf_->capacity());
95   }
96   void advance(uint32_t n) {
97     buf_->advance(smul(n));
98   }
99   void retreat(uint32_t n) {
100     buf_->retreat(smul(n));
101   }
102   void prepend(uint32_t n) {
103     buf_->prepend(smul(n));
104   }
105   void append(uint32_t n) {
106     buf_->append(smul(n));
107   }
108   void trimStart(uint32_t n) {
109     buf_->trimStart(smul(n));
110   }
111   void trimEnd(uint32_t n) {
112     buf_->trimEnd(smul(n));
113   }
114   void clear() {
115     buf_->clear();
116   }
117   void reserve(uint32_t minHeadroom, uint32_t minTailroom) {
118     buf_->reserve(smul(minHeadroom), smul(minTailroom));
119   }
120   void reserve(uint32_t minTailroom) { reserve(0, minTailroom); }
121
122   const T* cbegin() const { return data(); }
123   const T* cend() const { return tail(); }
124   const T* begin() const { return cbegin(); }
125   const T* end() const { return cend(); }
126   T* begin() { return writableData(); }
127   T* end() { return writableTail(); }
128
129   const T& front() const {
130     assert(!empty());
131     return *begin();
132   }
133   T& front() {
134     assert(!empty());
135     return *begin();
136   }
137   const T& back() const {
138     assert(!empty());
139     return end()[-1];
140   }
141   T& back() {
142     assert(!empty());
143     return end()[-1];
144   }
145
146   /**
147    * Simple wrapper to make it easier to treat this TypedIOBuf as an array of
148    * T.
149    */
150   const T& operator[](ssize_t idx) const {
151     assert(idx >= 0 && idx < length());
152     return data()[idx];
153   }
154
155   T& operator[](ssize_t idx) {
156     assert(idx >= 0 && idx < length());
157     return writableData()[idx];
158   }
159
160   /**
161    * Append one element.
162    */
163   void push(const T& data) {
164     push(&data, &data + 1);
165   }
166   void push_back(const T& data) { push(data); }
167
168   /**
169    * Append multiple elements in a sequence; will call distance().
170    */
171   template <class IT>
172   void push(IT begin, IT end) {
173     uint32_t n = std::distance(begin, end);
174     if (usingJEMalloc()) {
175       // Rely on xallocx() and avoid exponential growth to limit
176       // amount of memory wasted.
177       reserve(headroom(), n);
178     } else if (tailroom() < n) {
179       reserve(headroom(), std::max(n, 3 + size() / 2));
180     }
181     std::copy(begin, end, writableTail());
182     append(n);
183   }
184
185   // Movable
186   TypedIOBuf(TypedIOBuf&&) = default;
187   TypedIOBuf& operator=(TypedIOBuf&&) = default;
188
189  private:
190   // Non-copyable
191   TypedIOBuf(const TypedIOBuf&) = delete;
192   TypedIOBuf& operator=(const TypedIOBuf&) = delete;
193
194   // cast to T*
195   static T* cast(uint8_t* p) {
196     return reinterpret_cast<T*>(p);
197   }
198   static const T* cast(const uint8_t* p) {
199     return reinterpret_cast<const T*>(p);
200   }
201   // divide by size
202   static uint32_t sdiv(uint32_t n) {
203     return n / sizeof(T);
204   }
205   // multiply by size
206   static uint32_t smul(uint32_t n) {
207     // In debug mode, check for overflow
208     assert((uint64_t(n) * sizeof(T)) < (uint64_t(1) << 32));
209     return n * sizeof(T);
210   }
211
212   IOBuf* buf_;
213 };
214
215 } // namespace folly