aboutsummaryrefslogtreecommitdiff
path: root/webrtc/base/buffer.h
blob: 7b9402b5113904ea184d1df50826679750ca9c91 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
/*
 *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#ifndef WEBRTC_BASE_BUFFER_H_
#define WEBRTC_BASE_BUFFER_H_

#include <algorithm>  // std::swap (pre-C++11)
#include <cassert>
#include <cstring>
#include <utility>  // std::swap (C++11 and later)
#include "webrtc/base/scoped_ptr.h"

namespace rtc {

namespace internal {

// (Internal; please don't use outside this file.) ByteType<T>::t is int if T
// is uint8_t, int8_t, or char; otherwise, it's a compilation error. Use like
// this:
//
//   template <typename T, typename ByteType<T>::t = 0>
//   void foo(T* x);
//
// to let foo<T> be defined only for byte-sized integers.
template <typename T>
struct ByteType {
 private:
  static int F(uint8_t*);
  static int F(int8_t*);
  static int F(char*);

 public:
  using t = decltype(F(static_cast<T*>(nullptr)));
};

}  // namespace internal

// Basic buffer class, can be grown and shrunk dynamically.
// Unlike std::string/vector, does not initialize data when expanding capacity.
class Buffer {
 public:
  Buffer();                   // An empty buffer.
  Buffer(const Buffer& buf);  // Copy size and contents of an existing buffer.
  Buffer(Buffer&& buf);       // Move contents from an existing buffer.

  // Construct a buffer with the specified number of uninitialized bytes.
  explicit Buffer(size_t size);
  Buffer(size_t size, size_t capacity);

  // Construct a buffer and copy the specified number of bytes into it. The
  // source array may be (const) uint8_t*, int8_t*, or char*.
  template <typename T, typename internal::ByteType<T>::t = 0>
  Buffer(const T* data, size_t size)
      : Buffer(data, size, size) {}
  template <typename T, typename internal::ByteType<T>::t = 0>
  Buffer(const T* data, size_t size, size_t capacity)
      : Buffer(size, capacity) {
    std::memcpy(data_.get(), data, size);
  }

  // Construct a buffer from the contents of an array.
  template <typename T, size_t N, typename internal::ByteType<T>::t = 0>
  Buffer(const T(&array)[N])
      : Buffer(array, N) {}

  ~Buffer();

  // Get a pointer to the data. Just .data() will give you a (const) uint8_t*,
  // but you may also use .data<int8_t>() and .data<char>().
  template <typename T = uint8_t, typename internal::ByteType<T>::t = 0>
  const T* data() const {
    assert(IsConsistent());
    return reinterpret_cast<T*>(data_.get());
  }
  template <typename T = uint8_t, typename internal::ByteType<T>::t = 0>
  T* data() {
    assert(IsConsistent());
    return reinterpret_cast<T*>(data_.get());
  }

  size_t size() const {
    assert(IsConsistent());
    return size_;
  }
  size_t capacity() const {
    assert(IsConsistent());
    return capacity_;
  }

  Buffer& operator=(const Buffer& buf) {
    if (&buf != this)
      SetData(buf.data(), buf.size());
    return *this;
  }
  Buffer& operator=(Buffer&& buf) {
    assert(IsConsistent());
    assert(buf.IsConsistent());
    size_ = buf.size_;
    capacity_ = buf.capacity_;
    data_ = buf.data_.Pass();
    buf.OnMovedFrom();
    return *this;
  }

  bool operator==(const Buffer& buf) const {
    assert(IsConsistent());
    return size_ == buf.size() && memcmp(data_.get(), buf.data(), size_) == 0;
  }

  bool operator!=(const Buffer& buf) const { return !(*this == buf); }

  // Replace the contents of the buffer. Accepts the same types as the
  // constructors.
  template <typename T, typename internal::ByteType<T>::t = 0>
  void SetData(const T* data, size_t size) {
    assert(IsConsistent());
    size_ = 0;
    AppendData(data, size);
  }
  template <typename T, size_t N, typename internal::ByteType<T>::t = 0>
  void SetData(const T(&array)[N]) {
    SetData(array, N);
  }
  void SetData(const Buffer& buf) { SetData(buf.data(), buf.size()); }

  // Append data to the buffer. Accepts the same types as the constructors.
  template <typename T, typename internal::ByteType<T>::t = 0>
  void AppendData(const T* data, size_t size) {
    assert(IsConsistent());
    const size_t new_size = size_ + size;
    EnsureCapacity(new_size);
    std::memcpy(data_.get() + size_, data, size);
    size_ = new_size;
    assert(IsConsistent());
  }
  template <typename T, size_t N, typename internal::ByteType<T>::t = 0>
  void AppendData(const T(&array)[N]) {
    AppendData(array, N);
  }
  void AppendData(const Buffer& buf) { AppendData(buf.data(), buf.size()); }

  // Sets the size of the buffer. If the new size is smaller than the old, the
  // buffer contents will be kept but truncated; if the new size is greater,
  // the existing contents will be kept and the new space will be
  // uninitialized.
  void SetSize(size_t size) {
    EnsureCapacity(size);
    size_ = size;
  }

  // Ensure that the buffer size can be increased to at least capacity without
  // further reallocation. (Of course, this operation might need to reallocate
  // the buffer.)
  void EnsureCapacity(size_t capacity) {
    assert(IsConsistent());
    if (capacity <= capacity_)
      return;
    scoped_ptr<uint8_t[]> new_data(new uint8_t[capacity]);
    std::memcpy(new_data.get(), data_.get(), size_);
    data_ = new_data.Pass();
    capacity_ = capacity;
    assert(IsConsistent());
  }

  // We can't call std::move(b), so call b.Pass() instead to do the same job.
  Buffer&& Pass() {
    assert(IsConsistent());
    return static_cast<Buffer&&>(*this);
  }

  // Resets the buffer to zero size and capacity. Works even if the buffer has
  // been moved from.
  void Clear() {
    data_.reset();
    size_ = 0;
    capacity_ = 0;
    assert(IsConsistent());
  }

  // Swaps two buffers. Also works for buffers that have been moved from.
  friend void swap(Buffer& a, Buffer& b) {
    using std::swap;
    swap(a.size_, b.size_);
    swap(a.capacity_, b.capacity_);
    swap(a.data_, b.data_);
  }

 private:
  // Precondition for all methods except Clear and the destructor.
  // Postcondition for all methods except move construction and move
  // assignment, which leave the moved-from object in a possibly inconsistent
  // state.
  bool IsConsistent() const {
    return (data_ || capacity_ == 0) && capacity_ >= size_;
  }

  // Called when *this has been moved from. Conceptually it's a no-op, but we
  // can mutate the state slightly to help subsequent sanity checks catch bugs.
  void OnMovedFrom() {
#ifdef NDEBUG
    // Make *this consistent and empty. Shouldn't be necessary, but better safe
    // than sorry.
    size_ = 0;
    capacity_ = 0;
#else
    // Ensure that *this is always inconsistent, to provoke bugs.
    size_ = 1;
    capacity_ = 0;
#endif
  }

  size_t size_;
  size_t capacity_;
  scoped_ptr<uint8_t[]> data_;
};

}  // namespace rtc

#endif  // WEBRTC_BASE_BUFFER_H_