1/*
2 *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
3 *
4 *  Use of this source code is governed by a BSD-style license
5 *  that can be found in the LICENSE file in the root of the source
6 *  tree. An additional intellectual property rights grant can be found
7 *  in the file PATENTS.  All contributing project authors may
8 *  be found in the AUTHORS file in the root of the source tree.
9 */
10
11#ifndef WEBRTC_BASE_BUFFER_H_
12#define WEBRTC_BASE_BUFFER_H_
13
14#include <algorithm>  // std::swap (pre-C++11)
15#include <cassert>
16#include <cstring>
17#include <utility>  // std::swap (C++11 and later)
18
19#include "webrtc/base/deprecation.h"
20#include "webrtc/base/scoped_ptr.h"
21
22namespace rtc {
23
24namespace internal {
25
26// (Internal; please don't use outside this file.) ByteType<T>::t is int if T
27// is uint8_t, int8_t, or char; otherwise, it's a compilation error. Use like
28// this:
29//
30//   template <typename T, typename ByteType<T>::t = 0>
31//   void foo(T* x);
32//
33// to let foo<T> be defined only for byte-sized integers.
34template <typename T>
35struct ByteType {
36 private:
37  static int F(uint8_t*);
38  static int F(int8_t*);
39  static int F(char*);
40
41 public:
42  using t = decltype(F(static_cast<T*>(nullptr)));
43};
44
45}  // namespace internal
46
47// Basic buffer class, can be grown and shrunk dynamically.
48// Unlike std::string/vector, does not initialize data when expanding capacity.
49class Buffer {
50 public:
51  Buffer();                   // An empty buffer.
52  Buffer(const Buffer& buf);  // Copy size and contents of an existing buffer.
53  Buffer(Buffer&& buf);       // Move contents from an existing buffer.
54
55  // Construct a buffer with the specified number of uninitialized bytes.
56  explicit Buffer(size_t size);
57  Buffer(size_t size, size_t capacity);
58
59  // Construct a buffer and copy the specified number of bytes into it. The
60  // source array may be (const) uint8_t*, int8_t*, or char*.
61  template <typename T, typename internal::ByteType<T>::t = 0>
62  Buffer(const T* data, size_t size)
63      : Buffer(data, size, size) {}
64  template <typename T, typename internal::ByteType<T>::t = 0>
65  Buffer(const T* data, size_t size, size_t capacity)
66      : Buffer(size, capacity) {
67    std::memcpy(data_.get(), data, size);
68  }
69
70  // Construct a buffer from the contents of an array.
71  template <typename T, size_t N, typename internal::ByteType<T>::t = 0>
72  Buffer(const T(&array)[N])
73      : Buffer(array, N) {}
74
75  ~Buffer();
76
77  // Get a pointer to the data. Just .data() will give you a (const) uint8_t*,
78  // but you may also use .data<int8_t>() and .data<char>().
79  template <typename T = uint8_t, typename internal::ByteType<T>::t = 0>
80  const T* data() const {
81    assert(IsConsistent());
82    return reinterpret_cast<T*>(data_.get());
83  }
84  template <typename T = uint8_t, typename internal::ByteType<T>::t = 0>
85  T* data() {
86    assert(IsConsistent());
87    return reinterpret_cast<T*>(data_.get());
88  }
89
90  size_t size() const {
91    assert(IsConsistent());
92    return size_;
93  }
94  size_t capacity() const {
95    assert(IsConsistent());
96    return capacity_;
97  }
98
99  Buffer& operator=(const Buffer& buf) {
100    if (&buf != this)
101      SetData(buf.data(), buf.size());
102    return *this;
103  }
104  Buffer& operator=(Buffer&& buf) {
105    assert(IsConsistent());
106    assert(buf.IsConsistent());
107    size_ = buf.size_;
108    capacity_ = buf.capacity_;
109    data_ = std::move(buf.data_);
110    buf.OnMovedFrom();
111    return *this;
112  }
113
114  bool operator==(const Buffer& buf) const {
115    assert(IsConsistent());
116    return size_ == buf.size() && memcmp(data_.get(), buf.data(), size_) == 0;
117  }
118
119  bool operator!=(const Buffer& buf) const { return !(*this == buf); }
120
121  // Replace the contents of the buffer. Accepts the same types as the
122  // constructors.
123  template <typename T, typename internal::ByteType<T>::t = 0>
124  void SetData(const T* data, size_t size) {
125    assert(IsConsistent());
126    size_ = 0;
127    AppendData(data, size);
128  }
129  template <typename T, size_t N, typename internal::ByteType<T>::t = 0>
130  void SetData(const T(&array)[N]) {
131    SetData(array, N);
132  }
133  void SetData(const Buffer& buf) { SetData(buf.data(), buf.size()); }
134
135  // Append data to the buffer. Accepts the same types as the constructors.
136  template <typename T, typename internal::ByteType<T>::t = 0>
137  void AppendData(const T* data, size_t size) {
138    assert(IsConsistent());
139    const size_t new_size = size_ + size;
140    EnsureCapacity(new_size);
141    std::memcpy(data_.get() + size_, data, size);
142    size_ = new_size;
143    assert(IsConsistent());
144  }
145  template <typename T, size_t N, typename internal::ByteType<T>::t = 0>
146  void AppendData(const T(&array)[N]) {
147    AppendData(array, N);
148  }
149  void AppendData(const Buffer& buf) { AppendData(buf.data(), buf.size()); }
150
151  // Sets the size of the buffer. If the new size is smaller than the old, the
152  // buffer contents will be kept but truncated; if the new size is greater,
153  // the existing contents will be kept and the new space will be
154  // uninitialized.
155  void SetSize(size_t size) {
156    EnsureCapacity(size);
157    size_ = size;
158  }
159
160  // Ensure that the buffer size can be increased to at least capacity without
161  // further reallocation. (Of course, this operation might need to reallocate
162  // the buffer.)
163  void EnsureCapacity(size_t capacity) {
164    assert(IsConsistent());
165    if (capacity <= capacity_)
166      return;
167    scoped_ptr<uint8_t[]> new_data(new uint8_t[capacity]);
168    std::memcpy(new_data.get(), data_.get(), size_);
169    data_ = std::move(new_data);
170    capacity_ = capacity;
171    assert(IsConsistent());
172  }
173
174  // b.Pass() does the same thing as std::move(b).
175  // Deprecated; remove in March 2016 (bug 5373).
176  RTC_DEPRECATED Buffer&& Pass() { return DEPRECATED_Pass(); }
177  Buffer&& DEPRECATED_Pass() {
178    assert(IsConsistent());
179    return std::move(*this);
180  }
181
182  // Resets the buffer to zero size and capacity. Works even if the buffer has
183  // been moved from.
184  void Clear() {
185    data_.reset();
186    size_ = 0;
187    capacity_ = 0;
188    assert(IsConsistent());
189  }
190
191  // Swaps two buffers. Also works for buffers that have been moved from.
192  friend void swap(Buffer& a, Buffer& b) {
193    using std::swap;
194    swap(a.size_, b.size_);
195    swap(a.capacity_, b.capacity_);
196    swap(a.data_, b.data_);
197  }
198
199 private:
200  // Precondition for all methods except Clear and the destructor.
201  // Postcondition for all methods except move construction and move
202  // assignment, which leave the moved-from object in a possibly inconsistent
203  // state.
204  bool IsConsistent() const {
205    return (data_ || capacity_ == 0) && capacity_ >= size_;
206  }
207
208  // Called when *this has been moved from. Conceptually it's a no-op, but we
209  // can mutate the state slightly to help subsequent sanity checks catch bugs.
210  void OnMovedFrom() {
211#ifdef NDEBUG
212    // Make *this consistent and empty. Shouldn't be necessary, but better safe
213    // than sorry.
214    size_ = 0;
215    capacity_ = 0;
216#else
217    // Ensure that *this is always inconsistent, to provoke bugs.
218    size_ = 1;
219    capacity_ = 0;
220#endif
221  }
222
223  size_t size_;
224  size_t capacity_;
225  scoped_ptr<uint8_t[]> data_;
226};
227
228}  // namespace rtc
229
230#endif  // WEBRTC_BASE_BUFFER_H_
231