aboutsummaryrefslogtreecommitdiff
path: root/webrtc/base/array_view.h
blob: a7ca66cc95deb5a0c503bf3db6c2d53a9c34f941 (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
/*
 *  Copyright 2015 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_ARRAY_VIEW_H_
#define WEBRTC_BASE_ARRAY_VIEW_H_

#include "webrtc/base/checks.h"

namespace rtc {

// Many functions read from or write to arrays. The obvious way to do this is
// to use two arguments, a pointer to the first element and an element count:
//
//   bool Contains17(const int* arr, size_t size) {
//     for (size_t i = 0; i < size; ++i) {
//       if (arr[i] == 17)
//         return true;
//     }
//     return false;
//   }
//
// This is flexible, since it doesn't matter how the array is stored (C array,
// std::vector, rtc::Buffer, ...), but it's error-prone because the caller has
// to correctly specify the array length:
//
//   Contains17(arr, arraysize(arr));  // C array
//   Contains17(&arr[0], arr.size());  // std::vector
//   Contains17(arr, size);            // pointer + size
//   ...
//
// It's also kind of messy to have two separate arguments for what is
// conceptually a single thing.
//
// Enter rtc::ArrayView<T>. It contains a T pointer (to an array it doesn't
// own) and a count, and supports the basic things you'd expect, such as
// indexing and iteration. It allows us to write our function like this:
//
//   bool Contains17(rtc::ArrayView<const int> arr) {
//     for (auto e : arr) {
//       if (e == 17)
//         return true;
//     }
//     return false;
//   }
//
// And even better, because a bunch of things will implicitly convert to
// ArrayView, we can call it like this:
//
//   Contains17(arr);                             // C array
//   Contains17(arr);                             // std::vector
//   Contains17(rtc::ArrayView<int>(arr, size));  // pointer + size
//   ...
//
// One important point is that ArrayView<T> and ArrayView<const T> are
// different types, which allow and don't allow mutation of the array elements,
// respectively. The implicit conversions work just like you'd hope, so that
// e.g. vector<int> will convert to either ArrayView<int> or ArrayView<const
// int>, but const vector<int> will convert only to ArrayView<const int>.
// (ArrayView itself can be the source type in such conversions, so
// ArrayView<int> will convert to ArrayView<const int>.)
//
// Note: ArrayView is tiny (just a pointer and a count) and trivially copyable,
// so it's probably cheaper to pass it by value than by const reference.
template <typename T>
class ArrayView final {
 public:
  // Construct an empty ArrayView.
  ArrayView() : ArrayView(static_cast<T*>(nullptr), 0) {}

  // Construct an ArrayView for a (pointer,size) pair.
  template <typename U>
  ArrayView(U* data, size_t size)
      : data_(size == 0 ? nullptr : data), size_(size) {
    CheckInvariant();
  }

  // Construct an ArrayView for an array.
  template <typename U, size_t N>
  ArrayView(U (&array)[N]) : ArrayView(&array[0], N) {}

  // Construct an ArrayView for any type U that has a size() method whose
  // return value converts implicitly to size_t, and a data() method whose
  // return value converts implicitly to T*. In particular, this means we allow
  // conversion from ArrayView<T> to ArrayView<const T>, but not the other way
  // around. Other allowed conversions include std::vector<T> to ArrayView<T>
  // or ArrayView<const T>, const std::vector<T> to ArrayView<const T>, and
  // rtc::Buffer to ArrayView<uint8_t> (with the same const behavior as
  // std::vector).
  template <typename U>
  ArrayView(U& u) : ArrayView(u.data(), u.size()) {}

  // Indexing, size, and iteration. These allow mutation even if the ArrayView
  // is const, because the ArrayView doesn't own the array. (To prevent
  // mutation, use ArrayView<const T>.)
  size_t size() const { return size_; }
  bool empty() const { return size_ == 0; }
  T* data() const { return data_; }
  T& operator[](size_t idx) const {
    RTC_DCHECK_LT(idx, size_);
    RTC_DCHECK(data_);  // Follows from size_ > idx and the class invariant.
    return data_[idx];
  }
  T* begin() const { return data_; }
  T* end() const { return data_ + size_; }
  const T* cbegin() const { return data_; }
  const T* cend() const { return data_ + size_; }

  // Comparing two ArrayViews compares their (pointer,size) pairs; it does
  // *not* dereference the pointers.
  friend bool operator==(const ArrayView& a, const ArrayView& b) {
    return a.data_ == b.data_ && a.size_ == b.size_;
  }
  friend bool operator!=(const ArrayView& a, const ArrayView& b) {
    return !(a == b);
  }

 private:
  // Invariant: !data_ iff size_ == 0.
  void CheckInvariant() const { RTC_DCHECK_EQ(!data_, size_ == 0); }
  T* data_;
  size_t size_;
};

}  // namespace rtc

#endif  // WEBRTC_BASE_ARRAY_VIEW_H_