Reland "Move webrtc/{base => rtc_base}" (https://codereview.webrtc.org/2877023002)

Reland the base->rtc_base without adding stub headers (will be
done in follow-up CL). This preserves git blame history of all files.

BUG=webrtc:7634
NOTRY=True
TBR=kwiberg@webrtc.org

Change-Id: Iea3bb6f3f67b8374c96337b63e8f5aa3e6181012
Reviewed-on: https://chromium-review.googlesource.com/554611
Reviewed-by: Henrik Kjellander <kjellander@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#18821}
This commit is contained in:
Henrik Kjellander
2017-06-29 08:03:04 +02:00
parent ec78f1cebc
commit c03627683f
383 changed files with 1607 additions and 1467 deletions

File diff suppressed because it is too large Load Diff

15
webrtc/rtc_base/DEPS Normal file
View File

@ -0,0 +1,15 @@
include_rules = [
"+base/third_party/libevent",
"+json",
"+third_party/jsoncpp",
"+webrtc/system_wrappers",
]
specific_include_rules = {
"gunit_prod.h": [
"+gtest",
],
"protobuf_utils.h": [
"+third_party/protobuf",
],
}

19
webrtc/rtc_base/OWNERS Normal file
View File

@ -0,0 +1,19 @@
henrika@webrtc.org
henrikg@webrtc.org
hta@webrtc.org
juberti@webrtc.org
mflodman@webrtc.org
perkj@webrtc.org
pthatcher@webrtc.org
sergeyu@chromium.org
tommi@webrtc.org
deadbeef@webrtc.org
kwiberg@webrtc.org
# These are for the common case of adding or renaming files. If you're doing
# structural changes, please get a review from a reviewer in this file.
per-file *.gn=*
per-file *.gni=*
per-file rate_statistics*=sprang@webrtc.org
per-file rate_statistics*=stefan@webrtc.org

View File

@ -0,0 +1,53 @@
/*
* Copyright 2014 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.
*/
// This file only exists because various iOS and macOS system APIs are only
// available from Objective-C. See unixfilesystem.cc for the only use
// (enforced by a lack of a header file).
#import <Foundation/NSPathUtilities.h>
#import <Foundation/NSProcessInfo.h>
#include <string.h>
#include "webrtc/base/checks.h"
#include "webrtc/base/pathutils.h"
// Return a new[]'d |char*| copy of the UTF8 representation of |s|.
// Caller owns the returned memory and must use delete[] on it.
static char* copyString(NSString* s) {
const char* utf8 = [s UTF8String];
size_t len = strlen(utf8) + 1;
char* copy = new char[len];
// This uses a new[] + strcpy (instead of strdup) because the
// receiver expects to be able to delete[] the returned pointer
// (instead of free()ing it).
strcpy(copy, utf8);
return copy;
}
// Return a (leaked) copy of a directory name suitable for application data.
char* AppleDataDirectory() {
NSArray* paths = NSSearchPathForDirectoriesInDomains(
NSApplicationSupportDirectory, NSUserDomainMask, YES);
RTC_DCHECK([paths count] == 1);
return copyString([paths objectAtIndex:0]);
}
// Return a (leaked) copy of a directory name suitable for use as a $TEMP.
char* AppleTempDirectory() {
return copyString(NSTemporaryDirectory());
}
// Return the binary's path.
void AppleAppName(rtc::Pathname* path) {
NSProcessInfo* pInfo = [NSProcessInfo processInfo];
NSString* argv0 = [[pInfo arguments] objectAtIndex:0];
path->SetPathname([argv0 UTF8String]);
}

View File

@ -0,0 +1,253 @@
/*
* 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_RTC_BASE_ARRAY_VIEW_H_
#define WEBRTC_RTC_BASE_ARRAY_VIEW_H_
#include "webrtc/base/checks.h"
#include "webrtc/base/type_traits.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.data(), 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
// Contains17(nullptr); // nullptr -> empty ArrayView
// ...
//
// ArrayView<T> stores both a pointer and a size, but you may also use
// ArrayView<T, N>, which has a size that's fixed at compile time (which means
// it only has to store the pointer).
//
// 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 if variable-sized, just
// a pointer if fix-sized) and trivially copyable, so it's probably cheaper to
// pass it by value than by const reference.
namespace impl {
// Magic constant for indicating that the size of an ArrayView is variable
// instead of fixed.
enum : std::ptrdiff_t { kArrayViewVarSize = -4711 };
// Base class for ArrayViews of fixed nonzero size.
template <typename T, std::ptrdiff_t Size>
class ArrayViewBase {
static_assert(Size > 0, "ArrayView size must be variable or non-negative");
public:
ArrayViewBase(T* data, size_t size) : data_(data) {}
static constexpr size_t size() { return Size; }
static constexpr bool empty() { return false; }
T* data() const { return data_; }
protected:
static constexpr bool fixed_size() { return true; }
private:
T* data_;
};
// Specialized base class for ArrayViews of fixed zero size.
template <typename T>
class ArrayViewBase<T, 0> {
public:
explicit ArrayViewBase(T* data, size_t size) {}
static constexpr size_t size() { return 0; }
static constexpr bool empty() { return true; }
T* data() const { return nullptr; }
protected:
static constexpr bool fixed_size() { return true; }
};
// Specialized base class for ArrayViews of variable size.
template <typename T>
class ArrayViewBase<T, impl::kArrayViewVarSize> {
public:
ArrayViewBase(T* data, size_t size)
: data_(size == 0 ? nullptr : data), size_(size) {}
size_t size() const { return size_; }
bool empty() const { return size_ == 0; }
T* data() const { return data_; }
protected:
static constexpr bool fixed_size() { return false; }
private:
T* data_;
size_t size_;
};
} // namespace impl
template <typename T, std::ptrdiff_t Size = impl::kArrayViewVarSize>
class ArrayView final : public impl::ArrayViewBase<T, Size> {
public:
using value_type = T;
using const_iterator = const T*;
// Construct an ArrayView from a pointer and a length.
template <typename U>
ArrayView(U* data, size_t size)
: impl::ArrayViewBase<T, Size>::ArrayViewBase(data, size) {
RTC_DCHECK_EQ(size == 0 ? nullptr : data, this->data());
RTC_DCHECK_EQ(size, this->size());
RTC_DCHECK_EQ(!this->data(),
this->size() == 0); // data is null iff size == 0.
}
// Construct an empty ArrayView. Note that fixed-size ArrayViews of size > 0
// cannot be empty.
ArrayView() : ArrayView(nullptr, 0) {}
ArrayView(std::nullptr_t) : ArrayView() {}
ArrayView(std::nullptr_t, size_t size)
: ArrayView(static_cast<T*>(nullptr), size) {
static_assert(Size == 0 || Size == impl::kArrayViewVarSize, "");
RTC_DCHECK_EQ(0, size);
}
// Construct an ArrayView from an array.
template <typename U, size_t N>
ArrayView(U (&array)[N]) : ArrayView(array, N) {
static_assert(Size == N || Size == impl::kArrayViewVarSize,
"Array size must match ArrayView size");
}
// (Only if size is fixed.) Construct an ArrayView from any type U that has a
// static constexpr size() method whose return value is equal to Size, and a
// data() method whose return value converts implicitly to T*. In particular,
// this means we allow conversion from ArrayView<T, N> to ArrayView<const T,
// N>, but not the other way around. We also don't allow conversion from
// ArrayView<T> to ArrayView<T, N>, or from ArrayView<T, M> to ArrayView<T,
// N> when M != N.
template <typename U,
typename std::enable_if<
Size != impl::kArrayViewVarSize &&
HasDataAndSize<U, T>::value>::type* = nullptr>
ArrayView(U& u) : ArrayView(u.data(), u.size()) {
static_assert(U::size() == Size, "Sizes must match exactly");
}
// (Only if size is variable.) Construct an ArrayView from 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
// ArrayView<T, N> to ArrayView<T> or ArrayView<const T>,
// std::vector<T> to ArrayView<T> or ArrayView<const T>,
// const std::vector<T> to ArrayView<const T>,
// rtc::Buffer to ArrayView<uint8_t> or ArrayView<const uint8_t>, and
// const rtc::Buffer to ArrayView<const uint8_t>.
template <
typename U,
typename std::enable_if<Size == impl::kArrayViewVarSize &&
HasDataAndSize<U, T>::value>::type* = nullptr>
ArrayView(U& u) : ArrayView(u.data(), u.size()) {}
// Indexing and iteration. These allow mutation even if the ArrayView is
// const, because the ArrayView doesn't own the array. (To prevent mutation,
// use a const element type.)
T& operator[](size_t idx) const {
RTC_DCHECK_LT(idx, this->size());
RTC_DCHECK(this->data());
return this->data()[idx];
}
T* begin() const { return this->data(); }
T* end() const { return this->data() + this->size(); }
const T* cbegin() const { return this->data(); }
const T* cend() const { return this->data() + this->size(); }
ArrayView<T> subview(size_t offset, size_t size) const {
return offset < this->size()
? ArrayView<T>(this->data() + offset,
std::min(size, this->size() - offset))
: ArrayView<T>();
}
ArrayView<T> subview(size_t offset) const {
return subview(offset, this->size());
}
};
// Comparing two ArrayViews compares their (pointer,size) pairs; it does *not*
// dereference the pointers.
template <typename T, std::ptrdiff_t Size1, std::ptrdiff_t Size2>
bool operator==(const ArrayView<T, Size1>& a, const ArrayView<T, Size2>& b) {
return a.data() == b.data() && a.size() == b.size();
}
template <typename T, std::ptrdiff_t Size1, std::ptrdiff_t Size2>
bool operator!=(const ArrayView<T, Size1>& a, const ArrayView<T, Size2>& b) {
return !(a == b);
}
// Variable-size ArrayViews are the size of two pointers; fixed-size ArrayViews
// are the size of one pointer. (And as a special case, fixed-size ArrayViews
// of size 0 require no storage.)
static_assert(sizeof(ArrayView<int>) == 2 * sizeof(int*), "");
static_assert(sizeof(ArrayView<int, 17>) == sizeof(int*), "");
static_assert(std::is_empty<ArrayView<int, 0>>::value, "");
template <typename T>
inline ArrayView<T> MakeArrayView(T* data, size_t size) {
return ArrayView<T>(data, size);
}
} // namespace rtc
#endif // WEBRTC_RTC_BASE_ARRAY_VIEW_H_

View File

@ -0,0 +1,411 @@
/*
* 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.
*/
#include <algorithm>
#include <string>
#include <vector>
#include "webrtc/base/array_view.h"
#include "webrtc/base/buffer.h"
#include "webrtc/base/checks.h"
#include "webrtc/base/gunit.h"
#include "webrtc/test/gmock.h"
namespace rtc {
namespace {
using ::testing::ElementsAre;
using ::testing::IsEmpty;
template <typename T>
void Call(ArrayView<T>) {}
} // namespace
TEST(ArrayViewTest, TestConstructFromPtrAndArray) {
char arr[] = "Arrr!";
const char carr[] = "Carrr!";
Call<const char>(arr);
Call<const char>(carr);
Call<char>(arr);
// Call<char>(carr); // Compile error, because can't drop const.
// Call<int>(arr); // Compile error, because incompatible types.
ArrayView<int*> x;
EXPECT_EQ(0u, x.size());
EXPECT_EQ(nullptr, x.data());
ArrayView<char> y = arr;
EXPECT_EQ(6u, y.size());
EXPECT_EQ(arr, y.data());
ArrayView<char, 6> yf = arr;
static_assert(yf.size() == 6, "");
EXPECT_EQ(arr, yf.data());
ArrayView<const char> z(arr + 1, 3);
EXPECT_EQ(3u, z.size());
EXPECT_EQ(arr + 1, z.data());
ArrayView<const char, 3> zf(arr + 1, 3);
static_assert(zf.size() == 3, "");
EXPECT_EQ(arr + 1, zf.data());
ArrayView<const char> w(arr, 2);
EXPECT_EQ(2u, w.size());
EXPECT_EQ(arr, w.data());
ArrayView<const char, 2> wf(arr, 2);
static_assert(wf.size() == 2, "");
EXPECT_EQ(arr, wf.data());
ArrayView<char> q(arr, 0);
EXPECT_EQ(0u, q.size());
EXPECT_EQ(nullptr, q.data());
ArrayView<char, 0> qf(arr, 0);
static_assert(qf.size() == 0, "");
EXPECT_EQ(nullptr, qf.data());
#if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
// DCHECK error (nullptr with nonzero size).
EXPECT_DEATH(ArrayView<int>(static_cast<int*>(nullptr), 5), "");
#endif
// These are compile errors, because incompatible types.
// ArrayView<int> m = arr;
// ArrayView<float> n(arr + 2, 2);
}
TEST(ArrayViewTest, TestCopyConstructorVariable) {
char arr[] = "Arrr!";
ArrayView<char> x = arr;
EXPECT_EQ(6u, x.size());
EXPECT_EQ(arr, x.data());
ArrayView<char> y = x; // Copy non-const -> non-const.
EXPECT_EQ(6u, y.size());
EXPECT_EQ(arr, y.data());
ArrayView<const char> z = x; // Copy non-const -> const.
EXPECT_EQ(6u, z.size());
EXPECT_EQ(arr, z.data());
ArrayView<const char> w = z; // Copy const -> const.
EXPECT_EQ(6u, w.size());
EXPECT_EQ(arr, w.data());
// ArrayView<char> v = z; // Compile error, because can't drop const.
}
TEST(ArrayViewTest, TestCopyConstructorFixed) {
char arr[] = "Arrr!";
ArrayView<char, 6> x = arr;
static_assert(x.size() == 6, "");
EXPECT_EQ(arr, x.data());
// Copy fixed -> fixed.
ArrayView<char, 6> y = x; // Copy non-const -> non-const.
static_assert(y.size() == 6, "");
EXPECT_EQ(arr, y.data());
ArrayView<const char, 6> z = x; // Copy non-const -> const.
static_assert(z.size() == 6, "");
EXPECT_EQ(arr, z.data());
ArrayView<const char, 6> w = z; // Copy const -> const.
static_assert(w.size() == 6, "");
EXPECT_EQ(arr, w.data());
// ArrayView<char, 6> v = z; // Compile error, because can't drop const.
// Copy fixed -> variable.
ArrayView<char> yv = x; // Copy non-const -> non-const.
EXPECT_EQ(6u, yv.size());
EXPECT_EQ(arr, yv.data());
ArrayView<const char> zv = x; // Copy non-const -> const.
EXPECT_EQ(6u, zv.size());
EXPECT_EQ(arr, zv.data());
ArrayView<const char> wv = z; // Copy const -> const.
EXPECT_EQ(6u, wv.size());
EXPECT_EQ(arr, wv.data());
// ArrayView<char> vv = z; // Compile error, because can't drop const.
}
TEST(ArrayViewTest, TestCopyAssignmentVariable) {
char arr[] = "Arrr!";
ArrayView<char> x(arr);
EXPECT_EQ(6u, x.size());
EXPECT_EQ(arr, x.data());
ArrayView<char> y;
y = x; // Copy non-const -> non-const.
EXPECT_EQ(6u, y.size());
EXPECT_EQ(arr, y.data());
ArrayView<const char> z;
z = x; // Copy non-const -> const.
EXPECT_EQ(6u, z.size());
EXPECT_EQ(arr, z.data());
ArrayView<const char> w;
w = z; // Copy const -> const.
EXPECT_EQ(6u, w.size());
EXPECT_EQ(arr, w.data());
// ArrayView<char> v;
// v = z; // Compile error, because can't drop const.
}
TEST(ArrayViewTest, TestCopyAssignmentFixed) {
char arr[] = "Arrr!";
char init[] = "Init!";
ArrayView<char, 6> x(arr);
EXPECT_EQ(arr, x.data());
// Copy fixed -> fixed.
ArrayView<char, 6> y(init);
y = x; // Copy non-const -> non-const.
EXPECT_EQ(arr, y.data());
ArrayView<const char, 6> z(init);
z = x; // Copy non-const -> const.
EXPECT_EQ(arr, z.data());
ArrayView<const char, 6> w(init);
w = z; // Copy const -> const.
EXPECT_EQ(arr, w.data());
// ArrayView<char, 6> v(init);
// v = z; // Compile error, because can't drop const.
// Copy fixed -> variable.
ArrayView<char> yv;
yv = x; // Copy non-const -> non-const.
EXPECT_EQ(6u, yv.size());
EXPECT_EQ(arr, yv.data());
ArrayView<const char> zv;
zv = x; // Copy non-const -> const.
EXPECT_EQ(6u, zv.size());
EXPECT_EQ(arr, zv.data());
ArrayView<const char> wv;
wv = z; // Copy const -> const.
EXPECT_EQ(6u, wv.size());
EXPECT_EQ(arr, wv.data());
// ArrayView<char> v;
// v = z; // Compile error, because can't drop const.
}
TEST(ArrayViewTest, TestStdVector) {
std::vector<int> v;
v.push_back(3);
v.push_back(11);
Call<const int>(v);
Call<int>(v);
// Call<unsigned int>(v); // Compile error, because incompatible types.
ArrayView<int> x = v;
EXPECT_EQ(2u, x.size());
EXPECT_EQ(v.data(), x.data());
ArrayView<const int> y;
y = v;
EXPECT_EQ(2u, y.size());
EXPECT_EQ(v.data(), y.data());
// ArrayView<double> d = v; // Compile error, because incompatible types.
const std::vector<int> cv;
Call<const int>(cv);
// Call<int>(cv); // Compile error, because can't drop const.
ArrayView<const int> z = cv;
EXPECT_EQ(0u, z.size());
EXPECT_EQ(nullptr, z.data());
// ArrayView<int> w = cv; // Compile error, because can't drop const.
}
TEST(ArrayViewTest, TestRtcBuffer) {
rtc::Buffer b = "so buffer";
Call<const uint8_t>(b);
Call<uint8_t>(b);
// Call<int8_t>(b); // Compile error, because incompatible types.
ArrayView<uint8_t> x = b;
EXPECT_EQ(10u, x.size());
EXPECT_EQ(b.data(), x.data());
ArrayView<const uint8_t> y;
y = b;
EXPECT_EQ(10u, y.size());
EXPECT_EQ(b.data(), y.data());
// ArrayView<char> d = b; // Compile error, because incompatible types.
const rtc::Buffer cb = "very const";
Call<const uint8_t>(cb);
// Call<uint8_t>(cb); // Compile error, because can't drop const.
ArrayView<const uint8_t> z = cb;
EXPECT_EQ(11u, z.size());
EXPECT_EQ(cb.data(), z.data());
// ArrayView<uint8_t> w = cb; // Compile error, because can't drop const.
}
TEST(ArrayViewTest, TestSwapVariable) {
const char arr[] = "Arrr!";
const char aye[] = "Aye, Cap'n!";
ArrayView<const char> x(arr);
EXPECT_EQ(6u, x.size());
EXPECT_EQ(arr, x.data());
ArrayView<const char> y(aye);
EXPECT_EQ(12u, y.size());
EXPECT_EQ(aye, y.data());
using std::swap;
swap(x, y);
EXPECT_EQ(12u, x.size());
EXPECT_EQ(aye, x.data());
EXPECT_EQ(6u, y.size());
EXPECT_EQ(arr, y.data());
// ArrayView<char> z;
// swap(x, z); // Compile error, because can't drop const.
}
TEST(FixArrayViewTest, TestSwapFixed) {
const char arr[] = "Arr!";
char aye[] = "Aye!";
ArrayView<const char, 5> x(arr);
EXPECT_EQ(arr, x.data());
ArrayView<const char, 5> y(aye);
EXPECT_EQ(aye, y.data());
using std::swap;
swap(x, y);
EXPECT_EQ(aye, x.data());
EXPECT_EQ(arr, y.data());
// ArrayView<char, 5> z(aye);
// swap(x, z); // Compile error, because can't drop const.
// ArrayView<const char, 4> w(aye, 4);
// swap(x, w); // Compile error, because different sizes.
}
TEST(ArrayViewTest, TestIndexing) {
char arr[] = "abcdefg";
ArrayView<char> x(arr);
const ArrayView<char> y(arr);
ArrayView<const char, 8> z(arr);
EXPECT_EQ(8u, x.size());
EXPECT_EQ(8u, y.size());
EXPECT_EQ(8u, z.size());
EXPECT_EQ('b', x[1]);
EXPECT_EQ('c', y[2]);
EXPECT_EQ('d', z[3]);
x[3] = 'X';
y[2] = 'Y';
// z[1] = 'Z'; // Compile error, because z's element type is const char.
EXPECT_EQ('b', x[1]);
EXPECT_EQ('Y', y[2]);
EXPECT_EQ('X', z[3]);
#if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
EXPECT_DEATH(z[8], ""); // DCHECK error (index out of bounds).
#endif
}
TEST(ArrayViewTest, TestIterationEmpty) {
// Variable-size.
ArrayView<std::vector<std::vector<std::vector<std::string>>>> av;
EXPECT_EQ(av.begin(), av.end());
EXPECT_EQ(av.cbegin(), av.cend());
for (auto& e : av) {
EXPECT_TRUE(false);
EXPECT_EQ(42u, e.size()); // Dummy use of e to prevent unused var warning.
}
// Fixed-size.
ArrayView<std::vector<std::vector<std::vector<std::string>>>, 0> af;
EXPECT_EQ(af.begin(), af.end());
EXPECT_EQ(af.cbegin(), af.cend());
for (auto& e : af) {
EXPECT_TRUE(false);
EXPECT_EQ(42u, e.size()); // Dummy use of e to prevent unused var warning.
}
}
TEST(ArrayViewTest, TestIterationVariable) {
char arr[] = "Arrr!";
ArrayView<char> av(arr);
EXPECT_EQ('A', *av.begin());
EXPECT_EQ('A', *av.cbegin());
EXPECT_EQ('\0', *(av.end() - 1));
EXPECT_EQ('\0', *(av.cend() - 1));
char i = 0;
for (auto& e : av) {
EXPECT_EQ(arr + i, &e);
e = 's' + i;
++i;
}
i = 0;
for (auto& e : ArrayView<const char>(av)) {
EXPECT_EQ(arr + i, &e);
// e = 'q' + i; // Compile error, because e is a const char&.
++i;
}
}
TEST(ArrayViewTest, TestIterationFixed) {
char arr[] = "Arrr!";
ArrayView<char, 6> av(arr);
EXPECT_EQ('A', *av.begin());
EXPECT_EQ('A', *av.cbegin());
EXPECT_EQ('\0', *(av.end() - 1));
EXPECT_EQ('\0', *(av.cend() - 1));
char i = 0;
for (auto& e : av) {
EXPECT_EQ(arr + i, &e);
e = 's' + i;
++i;
}
i = 0;
for (auto& e : ArrayView<const char, 6>(av)) {
EXPECT_EQ(arr + i, &e);
// e = 'q' + i; // Compile error, because e is a const char&.
++i;
}
}
TEST(ArrayViewTest, TestEmpty) {
EXPECT_TRUE(ArrayView<int>().empty());
const int a[] = {1, 2, 3};
EXPECT_FALSE(ArrayView<const int>(a).empty());
static_assert(ArrayView<int, 0>::empty(), "");
static_assert(!ArrayView<int, 3>::empty(), "");
}
TEST(ArrayViewTest, TestCompare) {
int a[] = {1, 2, 3};
int b[] = {1, 2, 3};
EXPECT_EQ(ArrayView<int>(a), ArrayView<int>(a));
EXPECT_EQ((ArrayView<int, 3>(a)), (ArrayView<int, 3>(a)));
EXPECT_EQ(ArrayView<int>(a), (ArrayView<int, 3>(a)));
EXPECT_EQ(ArrayView<int>(), ArrayView<int>());
EXPECT_EQ(ArrayView<int>(), ArrayView<int>(a, 0));
EXPECT_EQ(ArrayView<int>(a, 0), ArrayView<int>(b, 0));
EXPECT_EQ((ArrayView<int, 0>(a, 0)), ArrayView<int>());
EXPECT_NE(ArrayView<int>(a), ArrayView<int>(b));
EXPECT_NE((ArrayView<int, 3>(a)), (ArrayView<int, 3>(b)));
EXPECT_NE((ArrayView<int, 3>(a)), ArrayView<int>(b));
EXPECT_NE(ArrayView<int>(a), ArrayView<int>());
EXPECT_NE(ArrayView<int>(a), ArrayView<int>(a, 2));
EXPECT_NE((ArrayView<int, 3>(a)), (ArrayView<int, 2>(a, 2)));
}
TEST(ArrayViewTest, TestSubViewVariable) {
int a[] = {1, 2, 3};
ArrayView<int> av(a);
EXPECT_EQ(av.subview(0), av);
EXPECT_THAT(av.subview(1), ElementsAre(2, 3));
EXPECT_THAT(av.subview(2), ElementsAre(3));
EXPECT_THAT(av.subview(3), IsEmpty());
EXPECT_THAT(av.subview(4), IsEmpty());
EXPECT_THAT(av.subview(1, 0), IsEmpty());
EXPECT_THAT(av.subview(1, 1), ElementsAre(2));
EXPECT_THAT(av.subview(1, 2), ElementsAre(2, 3));
EXPECT_THAT(av.subview(1, 3), ElementsAre(2, 3));
}
TEST(ArrayViewTest, TestSubViewFixed) {
int a[] = {1, 2, 3};
ArrayView<int, 3> av(a);
EXPECT_EQ(av.subview(0), av);
EXPECT_THAT(av.subview(1), ElementsAre(2, 3));
EXPECT_THAT(av.subview(2), ElementsAre(3));
EXPECT_THAT(av.subview(3), IsEmpty());
EXPECT_THAT(av.subview(4), IsEmpty());
EXPECT_THAT(av.subview(1, 0), IsEmpty());
EXPECT_THAT(av.subview(1, 1), ElementsAre(2));
EXPECT_THAT(av.subview(1, 2), ElementsAre(2, 3));
EXPECT_THAT(av.subview(1, 3), ElementsAre(2, 3));
}
} // namespace rtc

View File

@ -0,0 +1,31 @@
/*
* Copyright (c) 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_RTC_BASE_ARRAYSIZE_H_
#define WEBRTC_RTC_BASE_ARRAYSIZE_H_
#include <stddef.h>
// This file defines the arraysize() macro and is derived from Chromium's
// base/macros.h.
// The arraysize(arr) macro returns the # of elements in an array arr.
// The expression is a compile-time constant, and therefore can be
// used in defining new arrays, for example. If you use arraysize on
// a pointer by mistake, you will get a compile-time error.
// This template function declaration is used in defining arraysize.
// Note that the function doesn't need an implementation, as we only
// use its type.
template <typename T, size_t N> char (&ArraySizeHelper(T (&array)[N]))[N];
#define arraysize(array) (sizeof(ArraySizeHelper(array)))
#endif // WEBRTC_RTC_BASE_ARRAYSIZE_H_

View File

@ -0,0 +1,56 @@
/*
* Copyright 2014 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_RTC_BASE_ASYNCINVOKER_INL_H_
#define WEBRTC_RTC_BASE_ASYNCINVOKER_INL_H_
#include "webrtc/base/atomicops.h"
#include "webrtc/base/bind.h"
#include "webrtc/base/criticalsection.h"
#include "webrtc/base/messagehandler.h"
#include "webrtc/base/sigslot.h"
#include "webrtc/base/thread.h"
#include "webrtc/base/thread_annotations.h"
namespace rtc {
class AsyncInvoker;
// Helper class for AsyncInvoker. Runs a task and triggers a callback
// on the calling thread if necessary.
class AsyncClosure {
public:
explicit AsyncClosure(AsyncInvoker* invoker) : invoker_(invoker) {}
virtual ~AsyncClosure();
// Runs the asynchronous task, and triggers a callback to the calling
// thread if needed. Should be called from the target thread.
virtual void Execute() = 0;
protected:
AsyncInvoker* invoker_;
};
// Simple closure that doesn't trigger a callback for the calling thread.
template <class FunctorT>
class FireAndForgetAsyncClosure : public AsyncClosure {
public:
explicit FireAndForgetAsyncClosure(AsyncInvoker* invoker,
const FunctorT& functor)
: AsyncClosure(invoker), functor_(functor) {}
virtual void Execute() {
functor_();
}
private:
FunctorT functor_;
};
} // namespace rtc
#endif // WEBRTC_RTC_BASE_ASYNCINVOKER_INL_H_

View File

@ -0,0 +1,119 @@
/*
* Copyright 2014 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.
*/
#include "webrtc/base/asyncinvoker.h"
#include "webrtc/base/atomicops.h"
#include "webrtc/base/checks.h"
#include "webrtc/base/logging.h"
namespace rtc {
AsyncInvoker::AsyncInvoker() : invocation_complete_(false, false) {}
AsyncInvoker::~AsyncInvoker() {
destroying_ = true;
// Messages for this need to be cleared *before* our destructor is complete.
MessageQueueManager::Clear(this);
// And we need to wait for any invocations that are still in progress on
// other threads.
while (AtomicOps::AcquireLoad(&pending_invocations_)) {
// If the destructor was called while AsyncInvoke was being called by
// another thread, WITHIN an AsyncInvoked functor, it may do another
// Thread::Post even after we called MessageQueueManager::Clear(this). So
// we need to keep calling Clear to discard these posts.
MessageQueueManager::Clear(this);
invocation_complete_.Wait(Event::kForever);
}
}
void AsyncInvoker::OnMessage(Message* msg) {
// Get the AsyncClosure shared ptr from this message's data.
ScopedMessageData<AsyncClosure>* data =
static_cast<ScopedMessageData<AsyncClosure>*>(msg->pdata);
// Execute the closure and trigger the return message if needed.
data->inner_data().Execute();
delete data;
}
void AsyncInvoker::Flush(Thread* thread, uint32_t id /*= MQID_ANY*/) {
if (destroying_) return;
// Run this on |thread| to reduce the number of context switches.
if (Thread::Current() != thread) {
thread->Invoke<void>(RTC_FROM_HERE,
Bind(&AsyncInvoker::Flush, this, thread, id));
return;
}
MessageList removed;
thread->Clear(this, id, &removed);
for (MessageList::iterator it = removed.begin(); it != removed.end(); ++it) {
// This message was pending on this thread, so run it now.
thread->Send(it->posted_from, it->phandler, it->message_id, it->pdata);
}
}
void AsyncInvoker::DoInvoke(const Location& posted_from,
Thread* thread,
std::unique_ptr<AsyncClosure> closure,
uint32_t id) {
if (destroying_) {
LOG(LS_WARNING) << "Tried to invoke while destroying the invoker.";
return;
}
AtomicOps::Increment(&pending_invocations_);
thread->Post(posted_from, this, id,
new ScopedMessageData<AsyncClosure>(std::move(closure)));
}
void AsyncInvoker::DoInvokeDelayed(const Location& posted_from,
Thread* thread,
std::unique_ptr<AsyncClosure> closure,
uint32_t delay_ms,
uint32_t id) {
if (destroying_) {
LOG(LS_WARNING) << "Tried to invoke while destroying the invoker.";
return;
}
AtomicOps::Increment(&pending_invocations_);
thread->PostDelayed(posted_from, delay_ms, this, id,
new ScopedMessageData<AsyncClosure>(std::move(closure)));
}
GuardedAsyncInvoker::GuardedAsyncInvoker() : thread_(Thread::Current()) {
thread_->SignalQueueDestroyed.connect(this,
&GuardedAsyncInvoker::ThreadDestroyed);
}
GuardedAsyncInvoker::~GuardedAsyncInvoker() {
}
bool GuardedAsyncInvoker::Flush(uint32_t id) {
rtc::CritScope cs(&crit_);
if (thread_ == nullptr)
return false;
invoker_.Flush(thread_, id);
return true;
}
void GuardedAsyncInvoker::ThreadDestroyed() {
rtc::CritScope cs(&crit_);
// We should never get more than one notification about the thread dying.
RTC_DCHECK(thread_ != nullptr);
thread_ = nullptr;
}
AsyncClosure::~AsyncClosure() {
AtomicOps::Decrement(&invoker_->pending_invocations_);
invoker_->invocation_complete_.Set();
}
} // namespace rtc

View File

@ -0,0 +1,221 @@
/*
* Copyright 2014 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_RTC_BASE_ASYNCINVOKER_H_
#define WEBRTC_RTC_BASE_ASYNCINVOKER_H_
#include <memory>
#include <utility>
#include "webrtc/base/asyncinvoker-inl.h"
#include "webrtc/base/bind.h"
#include "webrtc/base/constructormagic.h"
#include "webrtc/base/event.h"
#include "webrtc/base/sigslot.h"
#include "webrtc/base/thread.h"
namespace rtc {
// Invokes function objects (aka functors) asynchronously on a Thread, and
// owns the lifetime of calls (ie, when this object is destroyed, calls in
// flight are cancelled). AsyncInvoker can optionally execute a user-specified
// function when the asynchronous call is complete, or operates in
// fire-and-forget mode otherwise.
//
// AsyncInvoker does not own the thread it calls functors on.
//
// A note about async calls and object lifetimes: users should
// be mindful of object lifetimes when calling functions asynchronously and
// ensure objects used by the function _cannot_ be deleted between the
// invocation and execution of the functor. AsyncInvoker is designed to
// help: any calls in flight will be cancelled when the AsyncInvoker used to
// make the call is destructed, and any calls executing will be allowed to
// complete before AsyncInvoker destructs.
//
// The easiest way to ensure lifetimes are handled correctly is to create a
// class that owns the Thread and AsyncInvoker objects, and then call its
// methods asynchronously as needed.
//
// Example:
// class MyClass {
// public:
// void FireAsyncTaskWithResult(Thread* thread, int x) {
// // Specify a callback to get the result upon completion.
// invoker_.AsyncInvoke<int>(RTC_FROM_HERE,
// thread, Bind(&MyClass::AsyncTaskWithResult, this, x),
// &MyClass::OnTaskComplete, this);
// }
// void FireAnotherAsyncTask(Thread* thread) {
// // No callback specified means fire-and-forget.
// invoker_.AsyncInvoke<void>(RTC_FROM_HERE,
// thread, Bind(&MyClass::AnotherAsyncTask, this));
//
// private:
// int AsyncTaskWithResult(int x) {
// // Some long running process...
// return x * x;
// }
// void AnotherAsyncTask() {
// // Some other long running process...
// }
// void OnTaskComplete(int result) { result_ = result; }
//
// AsyncInvoker invoker_;
// int result_;
// };
class AsyncInvoker : public MessageHandler {
public:
AsyncInvoker();
~AsyncInvoker() override;
// Call |functor| asynchronously on |thread|, with no callback upon
// completion. Returns immediately.
template <class ReturnT, class FunctorT>
void AsyncInvoke(const Location& posted_from,
Thread* thread,
const FunctorT& functor,
uint32_t id = 0) {
std::unique_ptr<AsyncClosure> closure(
new FireAndForgetAsyncClosure<FunctorT>(this, functor));
DoInvoke(posted_from, thread, std::move(closure), id);
}
// Call |functor| asynchronously on |thread| with |delay_ms|, with no callback
// upon completion. Returns immediately.
template <class ReturnT, class FunctorT>
void AsyncInvokeDelayed(const Location& posted_from,
Thread* thread,
const FunctorT& functor,
uint32_t delay_ms,
uint32_t id = 0) {
std::unique_ptr<AsyncClosure> closure(
new FireAndForgetAsyncClosure<FunctorT>(this, functor));
DoInvokeDelayed(posted_from, thread, std::move(closure), delay_ms, id);
}
// Synchronously execute on |thread| all outstanding calls we own
// that are pending on |thread|, and wait for calls to complete
// before returning. Optionally filter by message id.
// The destructor will not wait for outstanding calls, so if that
// behavior is desired, call Flush() before destroying this object.
void Flush(Thread* thread, uint32_t id = MQID_ANY);
private:
void OnMessage(Message* msg) override;
void DoInvoke(const Location& posted_from,
Thread* thread,
std::unique_ptr<AsyncClosure> closure,
uint32_t id);
void DoInvokeDelayed(const Location& posted_from,
Thread* thread,
std::unique_ptr<AsyncClosure> closure,
uint32_t delay_ms,
uint32_t id);
volatile int pending_invocations_ = 0;
Event invocation_complete_;
bool destroying_ = false;
friend class AsyncClosure;
RTC_DISALLOW_COPY_AND_ASSIGN(AsyncInvoker);
};
// Similar to AsyncInvoker, but guards against the Thread being destroyed while
// there are outstanding dangling pointers to it. It will connect to the current
// thread in the constructor, and will get notified when that thread is
// destroyed. After GuardedAsyncInvoker is constructed, it can be used from
// other threads to post functors to the thread it was constructed on. If that
// thread dies, any further calls to AsyncInvoke() will be safely ignored.
class GuardedAsyncInvoker : public sigslot::has_slots<> {
public:
GuardedAsyncInvoker();
~GuardedAsyncInvoker() override;
// Synchronously execute all outstanding calls we own, and wait for calls to
// complete before returning. Optionally filter by message id. The destructor
// will not wait for outstanding calls, so if that behavior is desired, call
// Flush() first. Returns false if the thread has died.
bool Flush(uint32_t id = MQID_ANY);
// Call |functor| asynchronously with no callback upon completion. Returns
// immediately. Returns false if the thread has died.
template <class ReturnT, class FunctorT>
bool AsyncInvoke(const Location& posted_from,
const FunctorT& functor,
uint32_t id = 0) {
rtc::CritScope cs(&crit_);
if (thread_ == nullptr)
return false;
invoker_.AsyncInvoke<ReturnT, FunctorT>(posted_from, thread_, functor, id);
return true;
}
// Call |functor| asynchronously with |delay_ms|, with no callback upon
// completion. Returns immediately. Returns false if the thread has died.
template <class ReturnT, class FunctorT>
bool AsyncInvokeDelayed(const Location& posted_from,
const FunctorT& functor,
uint32_t delay_ms,
uint32_t id = 0) {
rtc::CritScope cs(&crit_);
if (thread_ == nullptr)
return false;
invoker_.AsyncInvokeDelayed<ReturnT, FunctorT>(posted_from, thread_,
functor, delay_ms, id);
return true;
}
// Call |functor| asynchronously, calling |callback| when done. Returns false
// if the thread has died.
template <class ReturnT, class FunctorT, class HostT>
bool AsyncInvoke(const Location& posted_from,
const Location& callback_posted_from,
const FunctorT& functor,
void (HostT::*callback)(ReturnT),
HostT* callback_host,
uint32_t id = 0) {
rtc::CritScope cs(&crit_);
if (thread_ == nullptr)
return false;
invoker_.AsyncInvoke<ReturnT, FunctorT, HostT>(
posted_from, callback_posted_from, thread_, functor, callback,
callback_host, id);
return true;
}
// Call |functor| asynchronously calling |callback| when done. Overloaded for
// void return. Returns false if the thread has died.
template <class ReturnT, class FunctorT, class HostT>
bool AsyncInvoke(const Location& posted_from,
const Location& callback_posted_from,
const FunctorT& functor,
void (HostT::*callback)(),
HostT* callback_host,
uint32_t id = 0) {
rtc::CritScope cs(&crit_);
if (thread_ == nullptr)
return false;
invoker_.AsyncInvoke<ReturnT, FunctorT, HostT>(
posted_from, callback_posted_from, thread_, functor, callback,
callback_host, id);
return true;
}
private:
// Callback when |thread_| is destroyed.
void ThreadDestroyed();
CriticalSection crit_;
Thread* thread_ GUARDED_BY(crit_);
AsyncInvoker invoker_ GUARDED_BY(crit_);
};
} // namespace rtc
#endif // WEBRTC_RTC_BASE_ASYNCINVOKER_H_

View File

@ -0,0 +1,29 @@
/*
* 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.
*/
#include "webrtc/base/asyncpacketsocket.h"
namespace rtc {
PacketTimeUpdateParams::PacketTimeUpdateParams()
: rtp_sendtime_extension_id(-1),
srtp_auth_tag_len(-1),
srtp_packet_index(-1) {
}
PacketTimeUpdateParams::~PacketTimeUpdateParams() = default;
AsyncPacketSocket::AsyncPacketSocket() {
}
AsyncPacketSocket::~AsyncPacketSocket() {
}
}; // namespace rtc

View File

@ -0,0 +1,143 @@
/*
* 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_RTC_BASE_ASYNCPACKETSOCKET_H_
#define WEBRTC_RTC_BASE_ASYNCPACKETSOCKET_H_
#include "webrtc/base/constructormagic.h"
#include "webrtc/base/dscp.h"
#include "webrtc/base/sigslot.h"
#include "webrtc/base/socket.h"
#include "webrtc/base/timeutils.h"
namespace rtc {
// This structure holds the info needed to update the packet send time header
// extension, including the information needed to update the authentication tag
// after changing the value.
struct PacketTimeUpdateParams {
PacketTimeUpdateParams();
~PacketTimeUpdateParams();
int rtp_sendtime_extension_id; // extension header id present in packet.
std::vector<char> srtp_auth_key; // Authentication key.
int srtp_auth_tag_len; // Authentication tag length.
int64_t srtp_packet_index; // Required for Rtp Packet authentication.
};
// This structure holds meta information for the packet which is about to send
// over network.
struct PacketOptions {
PacketOptions() : dscp(DSCP_NO_CHANGE), packet_id(-1) {}
explicit PacketOptions(DiffServCodePoint dscp) : dscp(dscp), packet_id(-1) {}
DiffServCodePoint dscp;
int packet_id; // 16 bits, -1 represents "not set".
PacketTimeUpdateParams packet_time_params;
};
// This structure will have the information about when packet is actually
// received by socket.
struct PacketTime {
PacketTime() : timestamp(-1), not_before(-1) {}
PacketTime(int64_t timestamp, int64_t not_before)
: timestamp(timestamp), not_before(not_before) {}
int64_t timestamp; // Receive time after socket delivers the data.
// Earliest possible time the data could have arrived, indicating the
// potential error in the |timestamp| value, in case the system, is busy. For
// example, the time of the last select() call.
// If unknown, this value will be set to zero.
int64_t not_before;
};
inline PacketTime CreatePacketTime(int64_t not_before) {
return PacketTime(TimeMicros(), not_before);
}
// Provides the ability to receive packets asynchronously. Sends are not
// buffered since it is acceptable to drop packets under high load.
class AsyncPacketSocket : public sigslot::has_slots<> {
public:
enum State {
STATE_CLOSED,
STATE_BINDING,
STATE_BOUND,
STATE_CONNECTING,
STATE_CONNECTED
};
AsyncPacketSocket();
~AsyncPacketSocket() override;
// Returns current local address. Address may be set to null if the
// socket is not bound yet (GetState() returns STATE_BINDING).
virtual SocketAddress GetLocalAddress() const = 0;
// Returns remote address. Returns zeroes if this is not a client TCP socket.
virtual SocketAddress GetRemoteAddress() const = 0;
// Send a packet.
virtual int Send(const void *pv, size_t cb, const PacketOptions& options) = 0;
virtual int SendTo(const void *pv, size_t cb, const SocketAddress& addr,
const PacketOptions& options) = 0;
// Close the socket.
virtual int Close() = 0;
// Returns current state of the socket.
virtual State GetState() const = 0;
// Get/set options.
virtual int GetOption(Socket::Option opt, int* value) = 0;
virtual int SetOption(Socket::Option opt, int value) = 0;
// Get/Set current error.
// TODO: Remove SetError().
virtual int GetError() const = 0;
virtual void SetError(int error) = 0;
// Emitted each time a packet is read. Used only for UDP and
// connected TCP sockets.
sigslot::signal5<AsyncPacketSocket*, const char*, size_t,
const SocketAddress&,
const PacketTime&> SignalReadPacket;
// Emitted each time a packet is sent.
sigslot::signal2<AsyncPacketSocket*, const SentPacket&> SignalSentPacket;
// Emitted when the socket is currently able to send.
sigslot::signal1<AsyncPacketSocket*> SignalReadyToSend;
// Emitted after address for the socket is allocated, i.e. binding
// is finished. State of the socket is changed from BINDING to BOUND
// (for UDP and server TCP sockets) or CONNECTING (for client TCP
// sockets).
sigslot::signal2<AsyncPacketSocket*, const SocketAddress&> SignalAddressReady;
// Emitted for client TCP sockets when state is changed from
// CONNECTING to CONNECTED.
sigslot::signal1<AsyncPacketSocket*> SignalConnect;
// Emitted for client TCP sockets when state is changed from
// CONNECTED to CLOSED.
sigslot::signal2<AsyncPacketSocket*, int> SignalClose;
// Used only for listening TCP sockets.
sigslot::signal2<AsyncPacketSocket*, AsyncPacketSocket*> SignalNewConnection;
private:
RTC_DISALLOW_COPY_AND_ASSIGN(AsyncPacketSocket);
};
} // namespace rtc
#endif // WEBRTC_RTC_BASE_ASYNCPACKETSOCKET_H_

View File

@ -0,0 +1,20 @@
/*
* 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.
*/
#include "webrtc/base/asyncresolverinterface.h"
namespace rtc {
AsyncResolverInterface::AsyncResolverInterface() {
}
AsyncResolverInterface::~AsyncResolverInterface() = default;
}; // namespace rtc

View File

@ -0,0 +1,47 @@
/*
* Copyright 2013 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_RTC_BASE_ASYNCRESOLVERINTERFACE_H_
#define WEBRTC_RTC_BASE_ASYNCRESOLVERINTERFACE_H_
#include "webrtc/base/sigslot.h"
#include "webrtc/base/socketaddress.h"
namespace rtc {
// This interface defines the methods to resolve the address asynchronously.
class AsyncResolverInterface {
public:
AsyncResolverInterface();
virtual ~AsyncResolverInterface();
// Start address resolve process.
virtual void Start(const SocketAddress& addr) = 0;
// Returns top most resolved address of |family|
virtual bool GetResolvedAddress(int family, SocketAddress* addr) const = 0;
// Returns error from resolver.
virtual int GetError() const = 0;
// Delete the resolver.
virtual void Destroy(bool wait) = 0;
// Returns top most resolved IPv4 address if address is resolved successfully.
// Otherwise returns address set in SetAddress.
SocketAddress address() const {
SocketAddress addr;
GetResolvedAddress(AF_INET, &addr);
return addr;
}
// This signal is fired when address resolve process is completed.
sigslot::signal1<AsyncResolverInterface*> SignalDone;
};
} // namespace rtc
#endif

View File

@ -0,0 +1,127 @@
/*
* Copyright 2010 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.
*/
#include "webrtc/base/asyncsocket.h"
#include "webrtc/base/checks.h"
namespace rtc {
AsyncSocket::AsyncSocket() {
}
AsyncSocket::~AsyncSocket() {
}
AsyncSocketAdapter::AsyncSocketAdapter(AsyncSocket* socket) : socket_(nullptr) {
Attach(socket);
}
AsyncSocketAdapter::~AsyncSocketAdapter() {
delete socket_;
}
void AsyncSocketAdapter::Attach(AsyncSocket* socket) {
RTC_DCHECK(!socket_);
socket_ = socket;
if (socket_) {
socket_->SignalConnectEvent.connect(this,
&AsyncSocketAdapter::OnConnectEvent);
socket_->SignalReadEvent.connect(this, &AsyncSocketAdapter::OnReadEvent);
socket_->SignalWriteEvent.connect(this, &AsyncSocketAdapter::OnWriteEvent);
socket_->SignalCloseEvent.connect(this, &AsyncSocketAdapter::OnCloseEvent);
}
}
SocketAddress AsyncSocketAdapter::GetLocalAddress() const {
return socket_->GetLocalAddress();
}
SocketAddress AsyncSocketAdapter::GetRemoteAddress() const {
return socket_->GetRemoteAddress();
}
int AsyncSocketAdapter::Bind(const SocketAddress& addr) {
return socket_->Bind(addr);
}
int AsyncSocketAdapter::Connect(const SocketAddress& addr) {
return socket_->Connect(addr);
}
int AsyncSocketAdapter::Send(const void* pv, size_t cb) {
return socket_->Send(pv, cb);
}
int AsyncSocketAdapter::SendTo(const void* pv,
size_t cb,
const SocketAddress& addr) {
return socket_->SendTo(pv, cb, addr);
}
int AsyncSocketAdapter::Recv(void* pv, size_t cb, int64_t* timestamp) {
return socket_->Recv(pv, cb, timestamp);
}
int AsyncSocketAdapter::RecvFrom(void* pv,
size_t cb,
SocketAddress* paddr,
int64_t* timestamp) {
return socket_->RecvFrom(pv, cb, paddr, timestamp);
}
int AsyncSocketAdapter::Listen(int backlog) {
return socket_->Listen(backlog);
}
AsyncSocket* AsyncSocketAdapter::Accept(SocketAddress* paddr) {
return socket_->Accept(paddr);
}
int AsyncSocketAdapter::Close() {
return socket_->Close();
}
int AsyncSocketAdapter::GetError() const {
return socket_->GetError();
}
void AsyncSocketAdapter::SetError(int error) {
return socket_->SetError(error);
}
AsyncSocket::ConnState AsyncSocketAdapter::GetState() const {
return socket_->GetState();
}
int AsyncSocketAdapter::GetOption(Option opt, int* value) {
return socket_->GetOption(opt, value);
}
int AsyncSocketAdapter::SetOption(Option opt, int value) {
return socket_->SetOption(opt, value);
}
void AsyncSocketAdapter::OnConnectEvent(AsyncSocket* socket) {
SignalConnectEvent(this);
}
void AsyncSocketAdapter::OnReadEvent(AsyncSocket* socket) {
SignalReadEvent(this);
}
void AsyncSocketAdapter::OnWriteEvent(AsyncSocket* socket) {
SignalWriteEvent(this);
}
void AsyncSocketAdapter::OnCloseEvent(AsyncSocket* socket, int err) {
SignalCloseEvent(this, err);
}
} // namespace rtc

View File

@ -0,0 +1,83 @@
/*
* 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_RTC_BASE_ASYNCSOCKET_H_
#define WEBRTC_RTC_BASE_ASYNCSOCKET_H_
#include "webrtc/base/sigslot.h"
#include "webrtc/base/socket.h"
namespace rtc {
// TODO: Remove Socket and rename AsyncSocket to Socket.
// Provides the ability to perform socket I/O asynchronously.
class AsyncSocket : public Socket {
public:
AsyncSocket();
~AsyncSocket() override;
AsyncSocket* Accept(SocketAddress* paddr) override = 0;
// SignalReadEvent and SignalWriteEvent use multi_threaded_local to allow
// access concurrently from different thread.
// For example SignalReadEvent::connect will be called in AsyncUDPSocket ctor
// but at the same time the SocketDispatcher maybe signaling the read event.
// ready to read
sigslot::signal1<AsyncSocket*,
sigslot::multi_threaded_local> SignalReadEvent;
// ready to write
sigslot::signal1<AsyncSocket*,
sigslot::multi_threaded_local> SignalWriteEvent;
sigslot::signal1<AsyncSocket*> SignalConnectEvent; // connected
sigslot::signal2<AsyncSocket*, int> SignalCloseEvent; // closed
};
class AsyncSocketAdapter : public AsyncSocket, public sigslot::has_slots<> {
public:
// The adapted socket may explicitly be null, and later assigned using Attach.
// However, subclasses which support detached mode must override any methods
// that will be called during the detached period (usually GetState()), to
// avoid dereferencing a null pointer.
explicit AsyncSocketAdapter(AsyncSocket* socket);
~AsyncSocketAdapter() override;
void Attach(AsyncSocket* socket);
SocketAddress GetLocalAddress() const override;
SocketAddress GetRemoteAddress() const override;
int Bind(const SocketAddress& addr) override;
int Connect(const SocketAddress& addr) override;
int Send(const void* pv, size_t cb) override;
int SendTo(const void* pv, size_t cb, const SocketAddress& addr) override;
int Recv(void* pv, size_t cb, int64_t* timestamp) override;
int RecvFrom(void* pv,
size_t cb,
SocketAddress* paddr,
int64_t* timestamp) override;
int Listen(int backlog) override;
AsyncSocket* Accept(SocketAddress* paddr) override;
int Close() override;
int GetError() const override;
void SetError(int error) override;
ConnState GetState() const override;
int GetOption(Option opt, int* value) override;
int SetOption(Option opt, int value) override;
protected:
virtual void OnConnectEvent(AsyncSocket* socket);
virtual void OnReadEvent(AsyncSocket* socket);
virtual void OnWriteEvent(AsyncSocket* socket);
virtual void OnCloseEvent(AsyncSocket* socket, int err);
AsyncSocket* socket_;
};
} // namespace rtc
#endif // WEBRTC_RTC_BASE_ASYNCSOCKET_H_

View File

@ -0,0 +1,331 @@
/*
* 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.
*/
#include "webrtc/base/asynctcpsocket.h"
#include <string.h>
#include <algorithm>
#include <memory>
#include "webrtc/base/byteorder.h"
#include "webrtc/base/checks.h"
#include "webrtc/base/logging.h"
#if defined(WEBRTC_POSIX)
#include <errno.h>
#endif // WEBRTC_POSIX
namespace rtc {
static const size_t kMaxPacketSize = 64 * 1024;
typedef uint16_t PacketLength;
static const size_t kPacketLenSize = sizeof(PacketLength);
static const size_t kBufSize = kMaxPacketSize + kPacketLenSize;
// The input buffer will be resized so that at least kMinimumRecvSize bytes can
// be received (but it will not grow above the maximum size passed to the
// constructor).
static const size_t kMinimumRecvSize = 128;
static const int kListenBacklog = 5;
// Binds and connects |socket|
AsyncSocket* AsyncTCPSocketBase::ConnectSocket(
rtc::AsyncSocket* socket,
const rtc::SocketAddress& bind_address,
const rtc::SocketAddress& remote_address) {
std::unique_ptr<rtc::AsyncSocket> owned_socket(socket);
if (socket->Bind(bind_address) < 0) {
LOG(LS_ERROR) << "Bind() failed with error " << socket->GetError();
return nullptr;
}
if (socket->Connect(remote_address) < 0) {
LOG(LS_ERROR) << "Connect() failed with error " << socket->GetError();
return nullptr;
}
return owned_socket.release();
}
AsyncTCPSocketBase::AsyncTCPSocketBase(AsyncSocket* socket, bool listen,
size_t max_packet_size)
: socket_(socket),
listen_(listen),
max_insize_(max_packet_size),
max_outsize_(max_packet_size) {
if (!listen_) {
// Listening sockets don't send/receive data, so they don't need buffers.
inbuf_.EnsureCapacity(kMinimumRecvSize);
}
RTC_DCHECK(socket_.get() != nullptr);
socket_->SignalConnectEvent.connect(
this, &AsyncTCPSocketBase::OnConnectEvent);
socket_->SignalReadEvent.connect(this, &AsyncTCPSocketBase::OnReadEvent);
socket_->SignalWriteEvent.connect(this, &AsyncTCPSocketBase::OnWriteEvent);
socket_->SignalCloseEvent.connect(this, &AsyncTCPSocketBase::OnCloseEvent);
if (listen_) {
if (socket_->Listen(kListenBacklog) < 0) {
LOG(LS_ERROR) << "Listen() failed with error " << socket_->GetError();
}
}
}
AsyncTCPSocketBase::~AsyncTCPSocketBase() {}
SocketAddress AsyncTCPSocketBase::GetLocalAddress() const {
return socket_->GetLocalAddress();
}
SocketAddress AsyncTCPSocketBase::GetRemoteAddress() const {
return socket_->GetRemoteAddress();
}
int AsyncTCPSocketBase::Close() {
return socket_->Close();
}
AsyncTCPSocket::State AsyncTCPSocketBase::GetState() const {
switch (socket_->GetState()) {
case Socket::CS_CLOSED:
return STATE_CLOSED;
case Socket::CS_CONNECTING:
if (listen_) {
return STATE_BOUND;
} else {
return STATE_CONNECTING;
}
case Socket::CS_CONNECTED:
return STATE_CONNECTED;
default:
RTC_NOTREACHED();
return STATE_CLOSED;
}
}
int AsyncTCPSocketBase::GetOption(Socket::Option opt, int* value) {
return socket_->GetOption(opt, value);
}
int AsyncTCPSocketBase::SetOption(Socket::Option opt, int value) {
return socket_->SetOption(opt, value);
}
int AsyncTCPSocketBase::GetError() const {
return socket_->GetError();
}
void AsyncTCPSocketBase::SetError(int error) {
return socket_->SetError(error);
}
int AsyncTCPSocketBase::SendTo(const void *pv, size_t cb,
const SocketAddress& addr,
const rtc::PacketOptions& options) {
const SocketAddress& remote_address = GetRemoteAddress();
if (addr == remote_address)
return Send(pv, cb, options);
// Remote address may be empty if there is a sudden network change.
RTC_DCHECK(remote_address.IsNil());
socket_->SetError(ENOTCONN);
return -1;
}
int AsyncTCPSocketBase::SendRaw(const void * pv, size_t cb) {
if (outbuf_.size() + cb > max_outsize_) {
socket_->SetError(EMSGSIZE);
return -1;
}
RTC_DCHECK(!listen_);
outbuf_.AppendData(static_cast<const uint8_t*>(pv), cb);
return FlushOutBuffer();
}
int AsyncTCPSocketBase::FlushOutBuffer() {
RTC_DCHECK(!listen_);
int res = socket_->Send(outbuf_.data(), outbuf_.size());
if (res <= 0) {
return res;
}
if (static_cast<size_t>(res) > outbuf_.size()) {
RTC_NOTREACHED();
return -1;
}
size_t new_size = outbuf_.size() - res;
if (new_size > 0) {
memmove(outbuf_.data(), outbuf_.data() + res, new_size);
}
outbuf_.SetSize(new_size);
return res;
}
void AsyncTCPSocketBase::AppendToOutBuffer(const void* pv, size_t cb) {
RTC_DCHECK(outbuf_.size() + cb <= max_outsize_);
RTC_DCHECK(!listen_);
outbuf_.AppendData(static_cast<const uint8_t*>(pv), cb);
}
void AsyncTCPSocketBase::OnConnectEvent(AsyncSocket* socket) {
SignalConnect(this);
}
void AsyncTCPSocketBase::OnReadEvent(AsyncSocket* socket) {
RTC_DCHECK(socket_.get() == socket);
if (listen_) {
rtc::SocketAddress address;
rtc::AsyncSocket* new_socket = socket->Accept(&address);
if (!new_socket) {
// TODO(stefan): Do something better like forwarding the error
// to the user.
LOG(LS_ERROR) << "TCP accept failed with error " << socket_->GetError();
return;
}
HandleIncomingConnection(new_socket);
// Prime a read event in case data is waiting.
new_socket->SignalReadEvent(new_socket);
} else {
size_t total_recv = 0;
while (true) {
size_t free_size = inbuf_.capacity() - inbuf_.size();
if (free_size < kMinimumRecvSize && inbuf_.capacity() < max_insize_) {
inbuf_.EnsureCapacity(std::min(max_insize_, inbuf_.capacity() * 2));
free_size = inbuf_.capacity() - inbuf_.size();
}
int len =
socket_->Recv(inbuf_.data() + inbuf_.size(), free_size, nullptr);
if (len < 0) {
// TODO(stefan): Do something better like forwarding the error to the
// user.
if (!socket_->IsBlocking()) {
LOG(LS_ERROR) << "Recv() returned error: " << socket_->GetError();
}
break;
}
total_recv += len;
inbuf_.SetSize(inbuf_.size() + len);
if (!len || static_cast<size_t>(len) < free_size) {
break;
}
}
if (!total_recv) {
return;
}
size_t size = inbuf_.size();
ProcessInput(inbuf_.data<char>(), &size);
if (size > inbuf_.size()) {
LOG(LS_ERROR) << "input buffer overflow";
RTC_NOTREACHED();
inbuf_.Clear();
} else {
inbuf_.SetSize(size);
}
}
}
void AsyncTCPSocketBase::OnWriteEvent(AsyncSocket* socket) {
RTC_DCHECK(socket_.get() == socket);
if (outbuf_.size() > 0) {
FlushOutBuffer();
}
if (outbuf_.size() == 0) {
SignalReadyToSend(this);
}
}
void AsyncTCPSocketBase::OnCloseEvent(AsyncSocket* socket, int error) {
SignalClose(this, error);
}
// AsyncTCPSocket
// Binds and connects |socket| and creates AsyncTCPSocket for
// it. Takes ownership of |socket|. Returns null if bind() or
// connect() fail (|socket| is destroyed in that case).
AsyncTCPSocket* AsyncTCPSocket::Create(
AsyncSocket* socket,
const SocketAddress& bind_address,
const SocketAddress& remote_address) {
return new AsyncTCPSocket(AsyncTCPSocketBase::ConnectSocket(
socket, bind_address, remote_address), false);
}
AsyncTCPSocket::AsyncTCPSocket(AsyncSocket* socket, bool listen)
: AsyncTCPSocketBase(socket, listen, kBufSize) {
}
int AsyncTCPSocket::Send(const void *pv, size_t cb,
const rtc::PacketOptions& options) {
if (cb > kBufSize) {
SetError(EMSGSIZE);
return -1;
}
// If we are blocking on send, then silently drop this packet
if (!IsOutBufferEmpty())
return static_cast<int>(cb);
PacketLength pkt_len = HostToNetwork16(static_cast<PacketLength>(cb));
AppendToOutBuffer(&pkt_len, kPacketLenSize);
AppendToOutBuffer(pv, cb);
int res = FlushOutBuffer();
if (res <= 0) {
// drop packet if we made no progress
ClearOutBuffer();
return res;
}
rtc::SentPacket sent_packet(options.packet_id, rtc::TimeMillis());
SignalSentPacket(this, sent_packet);
// We claim to have sent the whole thing, even if we only sent partial
return static_cast<int>(cb);
}
void AsyncTCPSocket::ProcessInput(char * data, size_t* len) {
SocketAddress remote_addr(GetRemoteAddress());
while (true) {
if (*len < kPacketLenSize)
return;
PacketLength pkt_len = rtc::GetBE16(data);
if (*len < kPacketLenSize + pkt_len)
return;
SignalReadPacket(this, data + kPacketLenSize, pkt_len, remote_addr,
CreatePacketTime(0));
*len -= kPacketLenSize + pkt_len;
if (*len > 0) {
memmove(data, data + kPacketLenSize + pkt_len, *len);
}
}
}
void AsyncTCPSocket::HandleIncomingConnection(AsyncSocket* socket) {
SignalNewConnection(this, new AsyncTCPSocket(socket, false));
}
} // namespace rtc

View File

@ -0,0 +1,108 @@
/*
* 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_RTC_BASE_ASYNCTCPSOCKET_H_
#define WEBRTC_RTC_BASE_ASYNCTCPSOCKET_H_
#include <memory>
#include "webrtc/base/asyncpacketsocket.h"
#include "webrtc/base/buffer.h"
#include "webrtc/base/constructormagic.h"
#include "webrtc/base/socketfactory.h"
namespace rtc {
// Simulates UDP semantics over TCP. Send and Recv packet sizes
// are preserved, and drops packets silently on Send, rather than
// buffer them in user space.
class AsyncTCPSocketBase : public AsyncPacketSocket {
public:
AsyncTCPSocketBase(AsyncSocket* socket, bool listen, size_t max_packet_size);
~AsyncTCPSocketBase() override;
// Pure virtual methods to send and recv data.
int Send(const void *pv, size_t cb,
const rtc::PacketOptions& options) override = 0;
virtual void ProcessInput(char* data, size_t* len) = 0;
// Signals incoming connection.
virtual void HandleIncomingConnection(AsyncSocket* socket) = 0;
SocketAddress GetLocalAddress() const override;
SocketAddress GetRemoteAddress() const override;
int SendTo(const void* pv,
size_t cb,
const SocketAddress& addr,
const rtc::PacketOptions& options) override;
int Close() override;
State GetState() const override;
int GetOption(Socket::Option opt, int* value) override;
int SetOption(Socket::Option opt, int value) override;
int GetError() const override;
void SetError(int error) override;
protected:
// Binds and connects |socket| and creates AsyncTCPSocket for
// it. Takes ownership of |socket|. Returns null if bind() or
// connect() fail (|socket| is destroyed in that case).
static AsyncSocket* ConnectSocket(AsyncSocket* socket,
const SocketAddress& bind_address,
const SocketAddress& remote_address);
virtual int SendRaw(const void* pv, size_t cb);
int FlushOutBuffer();
// Add data to |outbuf_|.
void AppendToOutBuffer(const void* pv, size_t cb);
// Helper methods for |outpos_|.
bool IsOutBufferEmpty() const { return outbuf_.size() == 0; }
void ClearOutBuffer() { outbuf_.Clear(); }
private:
// Called by the underlying socket
void OnConnectEvent(AsyncSocket* socket);
void OnReadEvent(AsyncSocket* socket);
void OnWriteEvent(AsyncSocket* socket);
void OnCloseEvent(AsyncSocket* socket, int error);
std::unique_ptr<AsyncSocket> socket_;
bool listen_;
Buffer inbuf_;
Buffer outbuf_;
size_t max_insize_;
size_t max_outsize_;
RTC_DISALLOW_COPY_AND_ASSIGN(AsyncTCPSocketBase);
};
class AsyncTCPSocket : public AsyncTCPSocketBase {
public:
// Binds and connects |socket| and creates AsyncTCPSocket for
// it. Takes ownership of |socket|. Returns null if bind() or
// connect() fail (|socket| is destroyed in that case).
static AsyncTCPSocket* Create(AsyncSocket* socket,
const SocketAddress& bind_address,
const SocketAddress& remote_address);
AsyncTCPSocket(AsyncSocket* socket, bool listen);
~AsyncTCPSocket() override {}
int Send(const void* pv,
size_t cb,
const rtc::PacketOptions& options) override;
void ProcessInput(char* data, size_t* len) override;
void HandleIncomingConnection(AsyncSocket* socket) override;
private:
RTC_DISALLOW_COPY_AND_ASSIGN(AsyncTCPSocket);
};
} // namespace rtc
#endif // WEBRTC_RTC_BASE_ASYNCTCPSOCKET_H_

View File

@ -0,0 +1,50 @@
/*
* 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.
*/
#include <memory>
#include <string>
#include "webrtc/base/asynctcpsocket.h"
#include "webrtc/base/gunit.h"
#include "webrtc/base/virtualsocketserver.h"
namespace rtc {
class AsyncTCPSocketTest
: public testing::Test,
public sigslot::has_slots<> {
public:
AsyncTCPSocketTest()
: vss_(new rtc::VirtualSocketServer()),
socket_(vss_->CreateAsyncSocket(SOCK_STREAM)),
tcp_socket_(new AsyncTCPSocket(socket_, true)),
ready_to_send_(false) {
tcp_socket_->SignalReadyToSend.connect(this,
&AsyncTCPSocketTest::OnReadyToSend);
}
void OnReadyToSend(rtc::AsyncPacketSocket* socket) {
ready_to_send_ = true;
}
protected:
std::unique_ptr<VirtualSocketServer> vss_;
AsyncSocket* socket_;
std::unique_ptr<AsyncTCPSocket> tcp_socket_;
bool ready_to_send_;
};
TEST_F(AsyncTCPSocketTest, OnWriteEvent) {
EXPECT_FALSE(ready_to_send_);
socket_->SignalWriteEvent(socket_);
EXPECT_TRUE(ready_to_send_);
}
} // namespace rtc

View File

@ -0,0 +1,130 @@
/*
* 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.
*/
#include "webrtc/base/asyncudpsocket.h"
#include "webrtc/base/checks.h"
#include "webrtc/base/logging.h"
namespace rtc {
static const int BUF_SIZE = 64 * 1024;
AsyncUDPSocket* AsyncUDPSocket::Create(
AsyncSocket* socket,
const SocketAddress& bind_address) {
std::unique_ptr<AsyncSocket> owned_socket(socket);
if (socket->Bind(bind_address) < 0) {
LOG(LS_ERROR) << "Bind() failed with error " << socket->GetError();
return nullptr;
}
return new AsyncUDPSocket(owned_socket.release());
}
AsyncUDPSocket* AsyncUDPSocket::Create(SocketFactory* factory,
const SocketAddress& bind_address) {
AsyncSocket* socket =
factory->CreateAsyncSocket(bind_address.family(), SOCK_DGRAM);
if (!socket)
return nullptr;
return Create(socket, bind_address);
}
AsyncUDPSocket::AsyncUDPSocket(AsyncSocket* socket)
: socket_(socket) {
size_ = BUF_SIZE;
buf_ = new char[size_];
// The socket should start out readable but not writable.
socket_->SignalReadEvent.connect(this, &AsyncUDPSocket::OnReadEvent);
socket_->SignalWriteEvent.connect(this, &AsyncUDPSocket::OnWriteEvent);
}
AsyncUDPSocket::~AsyncUDPSocket() {
delete [] buf_;
}
SocketAddress AsyncUDPSocket::GetLocalAddress() const {
return socket_->GetLocalAddress();
}
SocketAddress AsyncUDPSocket::GetRemoteAddress() const {
return socket_->GetRemoteAddress();
}
int AsyncUDPSocket::Send(const void *pv, size_t cb,
const rtc::PacketOptions& options) {
rtc::SentPacket sent_packet(options.packet_id, rtc::TimeMillis());
int ret = socket_->Send(pv, cb);
SignalSentPacket(this, sent_packet);
return ret;
}
int AsyncUDPSocket::SendTo(const void *pv, size_t cb,
const SocketAddress& addr,
const rtc::PacketOptions& options) {
rtc::SentPacket sent_packet(options.packet_id, rtc::TimeMillis());
int ret = socket_->SendTo(pv, cb, addr);
SignalSentPacket(this, sent_packet);
return ret;
}
int AsyncUDPSocket::Close() {
return socket_->Close();
}
AsyncUDPSocket::State AsyncUDPSocket::GetState() const {
return STATE_BOUND;
}
int AsyncUDPSocket::GetOption(Socket::Option opt, int* value) {
return socket_->GetOption(opt, value);
}
int AsyncUDPSocket::SetOption(Socket::Option opt, int value) {
return socket_->SetOption(opt, value);
}
int AsyncUDPSocket::GetError() const {
return socket_->GetError();
}
void AsyncUDPSocket::SetError(int error) {
return socket_->SetError(error);
}
void AsyncUDPSocket::OnReadEvent(AsyncSocket* socket) {
RTC_DCHECK(socket_.get() == socket);
SocketAddress remote_addr;
int64_t timestamp;
int len = socket_->RecvFrom(buf_, size_, &remote_addr, &timestamp);
if (len < 0) {
// An error here typically means we got an ICMP error in response to our
// send datagram, indicating the remote address was unreachable.
// When doing ICE, this kind of thing will often happen.
// TODO: Do something better like forwarding the error to the user.
SocketAddress local_addr = socket_->GetLocalAddress();
LOG(LS_INFO) << "AsyncUDPSocket[" << local_addr.ToSensitiveString() << "] "
<< "receive failed with error " << socket_->GetError();
return;
}
// TODO: Make sure that we got all of the packet.
// If we did not, then we should resize our buffer to be large enough.
SignalReadPacket(
this, buf_, static_cast<size_t>(len), remote_addr,
(timestamp > -1 ? PacketTime(timestamp, 0) : CreatePacketTime(0)));
}
void AsyncUDPSocket::OnWriteEvent(AsyncSocket* socket) {
SignalReadyToSend(this);
}
} // namespace rtc

View File

@ -0,0 +1,67 @@
/*
* 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_RTC_BASE_ASYNCUDPSOCKET_H_
#define WEBRTC_RTC_BASE_ASYNCUDPSOCKET_H_
#include <memory>
#include "webrtc/base/asyncpacketsocket.h"
#include "webrtc/base/socketfactory.h"
namespace rtc {
// Provides the ability to receive packets asynchronously. Sends are not
// buffered since it is acceptable to drop packets under high load.
class AsyncUDPSocket : public AsyncPacketSocket {
public:
// Binds |socket| and creates AsyncUDPSocket for it. Takes ownership
// of |socket|. Returns null if bind() fails (|socket| is destroyed
// in that case).
static AsyncUDPSocket* Create(AsyncSocket* socket,
const SocketAddress& bind_address);
// Creates a new socket for sending asynchronous UDP packets using an
// asynchronous socket from the given factory.
static AsyncUDPSocket* Create(SocketFactory* factory,
const SocketAddress& bind_address);
explicit AsyncUDPSocket(AsyncSocket* socket);
~AsyncUDPSocket() override;
SocketAddress GetLocalAddress() const override;
SocketAddress GetRemoteAddress() const override;
int Send(const void* pv,
size_t cb,
const rtc::PacketOptions& options) override;
int SendTo(const void* pv,
size_t cb,
const SocketAddress& addr,
const rtc::PacketOptions& options) override;
int Close() override;
State GetState() const override;
int GetOption(Socket::Option opt, int* value) override;
int SetOption(Socket::Option opt, int value) override;
int GetError() const override;
void SetError(int error) override;
private:
// Called when the underlying socket is ready to be read from.
void OnReadEvent(AsyncSocket* socket);
// Called when the underlying socket is ready to send.
void OnWriteEvent(AsyncSocket* socket);
std::unique_ptr<AsyncSocket> socket_;
char* buf_;
size_t size_;
};
} // namespace rtc
#endif // WEBRTC_RTC_BASE_ASYNCUDPSOCKET_H_

View File

@ -0,0 +1,53 @@
/*
* 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.
*/
#include <memory>
#include <string>
#include "webrtc/base/asyncudpsocket.h"
#include "webrtc/base/gunit.h"
#include "webrtc/base/physicalsocketserver.h"
#include "webrtc/base/virtualsocketserver.h"
namespace rtc {
class AsyncUdpSocketTest
: public testing::Test,
public sigslot::has_slots<> {
public:
AsyncUdpSocketTest()
: pss_(new rtc::PhysicalSocketServer),
vss_(new rtc::VirtualSocketServer(pss_.get())),
socket_(vss_->CreateAsyncSocket(SOCK_DGRAM)),
udp_socket_(new AsyncUDPSocket(socket_)),
ready_to_send_(false) {
udp_socket_->SignalReadyToSend.connect(this,
&AsyncUdpSocketTest::OnReadyToSend);
}
void OnReadyToSend(rtc::AsyncPacketSocket* socket) {
ready_to_send_ = true;
}
protected:
std::unique_ptr<PhysicalSocketServer> pss_;
std::unique_ptr<VirtualSocketServer> vss_;
AsyncSocket* socket_;
std::unique_ptr<AsyncUDPSocket> udp_socket_;
bool ready_to_send_;
};
TEST_F(AsyncUdpSocketTest, OnWriteEvent) {
EXPECT_FALSE(ready_to_send_);
socket_->SignalWriteEvent(socket_);
EXPECT_TRUE(ready_to_send_);
}
} // namespace rtc

View File

@ -0,0 +1,87 @@
/*
* Copyright 2011 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_RTC_BASE_ATOMICOPS_H_
#define WEBRTC_RTC_BASE_ATOMICOPS_H_
#if defined(WEBRTC_WIN)
// Include winsock2.h before including <windows.h> to maintain consistency with
// win32.h. We can't include win32.h directly here since it pulls in
// headers such as basictypes.h which causes problems in Chromium where webrtc
// exists as two separate projects, webrtc and libjingle.
#include <winsock2.h>
#include <windows.h>
#endif // defined(WEBRTC_WIN)
namespace rtc {
class AtomicOps {
public:
#if defined(WEBRTC_WIN)
// Assumes sizeof(int) == sizeof(LONG), which it is on Win32 and Win64.
static int Increment(volatile int* i) {
return ::InterlockedIncrement(reinterpret_cast<volatile LONG*>(i));
}
static int Decrement(volatile int* i) {
return ::InterlockedDecrement(reinterpret_cast<volatile LONG*>(i));
}
static int AcquireLoad(volatile const int* i) {
return *i;
}
static void ReleaseStore(volatile int* i, int value) {
*i = value;
}
static int CompareAndSwap(volatile int* i, int old_value, int new_value) {
return ::InterlockedCompareExchange(reinterpret_cast<volatile LONG*>(i),
new_value,
old_value);
}
// Pointer variants.
template <typename T>
static T* AcquireLoadPtr(T* volatile* ptr) {
return *ptr;
}
template <typename T>
static T* CompareAndSwapPtr(T* volatile* ptr, T* old_value, T* new_value) {
return static_cast<T*>(::InterlockedCompareExchangePointer(
reinterpret_cast<PVOID volatile*>(ptr), new_value, old_value));
}
#else
static int Increment(volatile int* i) {
return __sync_add_and_fetch(i, 1);
}
static int Decrement(volatile int* i) {
return __sync_sub_and_fetch(i, 1);
}
static int AcquireLoad(volatile const int* i) {
return __atomic_load_n(i, __ATOMIC_ACQUIRE);
}
static void ReleaseStore(volatile int* i, int value) {
__atomic_store_n(i, value, __ATOMIC_RELEASE);
}
static int CompareAndSwap(volatile int* i, int old_value, int new_value) {
return __sync_val_compare_and_swap(i, old_value, new_value);
}
// Pointer variants.
template <typename T>
static T* AcquireLoadPtr(T* volatile* ptr) {
return __atomic_load_n(ptr, __ATOMIC_ACQUIRE);
}
template <typename T>
static T* CompareAndSwapPtr(T* volatile* ptr, T* old_value, T* new_value) {
return __sync_val_compare_and_swap(ptr, old_value, new_value);
}
#endif
};
}
#endif // WEBRTC_RTC_BASE_ATOMICOPS_H_

View File

@ -0,0 +1,12 @@
/*
* Copyright 2011 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.
*/
// TODO(pbos): Move AtomicOps tests to here from
// webrtc/base/criticalsection_unittest.cc.

278
webrtc/rtc_base/base64.cc Normal file
View File

@ -0,0 +1,278 @@
//*********************************************************************
//* Base64 - a simple base64 encoder and decoder.
//*
//* Copyright (c) 1999, Bob Withers - bwit@pobox.com
//*
//* This code may be freely used for any purpose, either personal
//* or commercial, provided the authors copyright notice remains
//* intact.
//*
//* Enhancements by Stanley Yamane:
//* o reverse lookup table for the decode function
//* o reserve string buffer space in advance
//*
//*********************************************************************
#include "webrtc/base/base64.h"
#include <string.h>
#include "webrtc/base/checks.h"
using std::vector;
namespace rtc {
static const char kPad = '=';
static const unsigned char pd = 0xFD; // Padding
static const unsigned char sp = 0xFE; // Whitespace
static const unsigned char il = 0xFF; // Illegal base64 character
const char Base64::Base64Table[] =
// 0000000000111111111122222222223333333333444444444455555555556666
// 0123456789012345678901234567890123456789012345678901234567890123
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
// Decode Table gives the index of any valid base64 character in the
// Base64 table
// 65 == A, 97 == a, 48 == 0, 43 == +, 47 == /
const unsigned char Base64::DecodeTable[] = {
// 0 1 2 3 4 5 6 7 8 9
il, il, il, il, il, il, il, il, il, sp, // 0 - 9
sp, sp, sp, sp, il, il, il, il, il, il, // 10 - 19
il, il, il, il, il, il, il, il, il, il, // 20 - 29
il, il, sp, il, il, il, il, il, il, il, // 30 - 39
il, il, il, 62, il, il, il, 63, 52, 53, // 40 - 49
54, 55, 56, 57, 58, 59, 60, 61, il, il, // 50 - 59
il, pd, il, il, il, 0, 1, 2, 3, 4, // 60 - 69
5, 6, 7, 8, 9, 10, 11, 12, 13, 14, // 70 - 79
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, // 80 - 89
25, il, il, il, il, il, il, 26, 27, 28, // 90 - 99
29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // 100 - 109
39, 40, 41, 42, 43, 44, 45, 46, 47, 48, // 110 - 119
49, 50, 51, il, il, il, il, il, il, il, // 120 - 129
il, il, il, il, il, il, il, il, il, il, // 130 - 139
il, il, il, il, il, il, il, il, il, il, // 140 - 149
il, il, il, il, il, il, il, il, il, il, // 150 - 159
il, il, il, il, il, il, il, il, il, il, // 160 - 169
il, il, il, il, il, il, il, il, il, il, // 170 - 179
il, il, il, il, il, il, il, il, il, il, // 180 - 189
il, il, il, il, il, il, il, il, il, il, // 190 - 199
il, il, il, il, il, il, il, il, il, il, // 200 - 209
il, il, il, il, il, il, il, il, il, il, // 210 - 219
il, il, il, il, il, il, il, il, il, il, // 220 - 229
il, il, il, il, il, il, il, il, il, il, // 230 - 239
il, il, il, il, il, il, il, il, il, il, // 240 - 249
il, il, il, il, il, il // 250 - 255
};
bool Base64::IsBase64Char(char ch) {
return (('A' <= ch) && (ch <= 'Z')) || (('a' <= ch) && (ch <= 'z')) ||
(('0' <= ch) && (ch <= '9')) || (ch == '+') || (ch == '/');
}
bool Base64::GetNextBase64Char(char ch, char* next_ch) {
if (next_ch == nullptr) {
return false;
}
const char* p = strchr(Base64Table, ch);
if (!p)
return false;
++p;
*next_ch = (*p) ? *p : Base64Table[0];
return true;
}
bool Base64::IsBase64Encoded(const std::string& str) {
for (size_t i = 0; i < str.size(); ++i) {
if (!IsBase64Char(str.at(i)))
return false;
}
return true;
}
void Base64::EncodeFromArray(const void* data,
size_t len,
std::string* result) {
RTC_DCHECK(nullptr != result);
result->clear();
result->resize(((len + 2) / 3) * 4);
const unsigned char* byte_data = static_cast<const unsigned char*>(data);
unsigned char c;
size_t i = 0;
size_t dest_ix = 0;
while (i < len) {
c = (byte_data[i] >> 2) & 0x3f;
(*result)[dest_ix++] = Base64Table[c];
c = (byte_data[i] << 4) & 0x3f;
if (++i < len) {
c |= (byte_data[i] >> 4) & 0x0f;
}
(*result)[dest_ix++] = Base64Table[c];
if (i < len) {
c = (byte_data[i] << 2) & 0x3f;
if (++i < len) {
c |= (byte_data[i] >> 6) & 0x03;
}
(*result)[dest_ix++] = Base64Table[c];
} else {
(*result)[dest_ix++] = kPad;
}
if (i < len) {
c = byte_data[i] & 0x3f;
(*result)[dest_ix++] = Base64Table[c];
++i;
} else {
(*result)[dest_ix++] = kPad;
}
}
}
size_t Base64::GetNextQuantum(DecodeFlags parse_flags,
bool illegal_pads,
const char* data,
size_t len,
size_t* dpos,
unsigned char qbuf[4],
bool* padded) {
size_t byte_len = 0, pad_len = 0, pad_start = 0;
for (; (byte_len < 4) && (*dpos < len); ++*dpos) {
qbuf[byte_len] = DecodeTable[static_cast<unsigned char>(data[*dpos])];
if ((il == qbuf[byte_len]) || (illegal_pads && (pd == qbuf[byte_len]))) {
if (parse_flags != DO_PARSE_ANY)
break;
// Ignore illegal characters
} else if (sp == qbuf[byte_len]) {
if (parse_flags == DO_PARSE_STRICT)
break;
// Ignore spaces
} else if (pd == qbuf[byte_len]) {
if (byte_len < 2) {
if (parse_flags != DO_PARSE_ANY)
break;
// Ignore unexpected padding
} else if (byte_len + pad_len >= 4) {
if (parse_flags != DO_PARSE_ANY)
break;
// Ignore extra pads
} else {
if (1 == ++pad_len) {
pad_start = *dpos;
}
}
} else {
if (pad_len > 0) {
if (parse_flags != DO_PARSE_ANY)
break;
// Ignore pads which are followed by data
pad_len = 0;
}
++byte_len;
}
}
for (size_t i = byte_len; i < 4; ++i) {
qbuf[i] = 0;
}
if (4 == byte_len + pad_len) {
*padded = true;
} else {
*padded = false;
if (pad_len) {
// Roll back illegal padding
*dpos = pad_start;
}
}
return byte_len;
}
bool Base64::DecodeFromArray(const char* data,
size_t len,
DecodeFlags flags,
std::string* result,
size_t* data_used) {
return DecodeFromArrayTemplate<std::string>(data, len, flags, result,
data_used);
}
bool Base64::DecodeFromArray(const char* data,
size_t len,
DecodeFlags flags,
vector<char>* result,
size_t* data_used) {
return DecodeFromArrayTemplate<vector<char>>(data, len, flags, result,
data_used);
}
bool Base64::DecodeFromArray(const char* data,
size_t len,
DecodeFlags flags,
vector<uint8_t>* result,
size_t* data_used) {
return DecodeFromArrayTemplate<vector<uint8_t>>(data, len, flags, result,
data_used);
}
template <typename T>
bool Base64::DecodeFromArrayTemplate(const char* data,
size_t len,
DecodeFlags flags,
T* result,
size_t* data_used) {
RTC_DCHECK(nullptr != result);
RTC_DCHECK(flags <= (DO_PARSE_MASK | DO_PAD_MASK | DO_TERM_MASK));
const DecodeFlags parse_flags = flags & DO_PARSE_MASK;
const DecodeFlags pad_flags = flags & DO_PAD_MASK;
const DecodeFlags term_flags = flags & DO_TERM_MASK;
RTC_DCHECK(0 != parse_flags);
RTC_DCHECK(0 != pad_flags);
RTC_DCHECK(0 != term_flags);
result->clear();
result->reserve(len);
size_t dpos = 0;
bool success = true, padded;
unsigned char c, qbuf[4];
while (dpos < len) {
size_t qlen = GetNextQuantum(parse_flags, (DO_PAD_NO == pad_flags), data,
len, &dpos, qbuf, &padded);
c = (qbuf[0] << 2) | ((qbuf[1] >> 4) & 0x3);
if (qlen >= 2) {
result->push_back(c);
c = ((qbuf[1] << 4) & 0xf0) | ((qbuf[2] >> 2) & 0xf);
if (qlen >= 3) {
result->push_back(c);
c = ((qbuf[2] << 6) & 0xc0) | qbuf[3];
if (qlen >= 4) {
result->push_back(c);
c = 0;
}
}
}
if (qlen < 4) {
if ((DO_TERM_ANY != term_flags) && (0 != c)) {
success = false; // unused bits
}
if ((DO_PAD_YES == pad_flags) && !padded) {
success = false; // expected padding
}
break;
}
}
if ((DO_TERM_BUFFER == term_flags) && (dpos != len)) {
success = false; // unused chars
}
if (data_used) {
*data_used = dpos;
}
return success;
}
} // namespace rtc

123
webrtc/rtc_base/base64.h Normal file
View File

@ -0,0 +1,123 @@
//*********************************************************************
//* C_Base64 - a simple base64 encoder and decoder.
//*
//* Copyright (c) 1999, Bob Withers - bwit@pobox.com
//*
//* This code may be freely used for any purpose, either personal
//* or commercial, provided the authors copyright notice remains
//* intact.
//*********************************************************************
#ifndef WEBRTC_RTC_BASE_BASE64_H_
#define WEBRTC_RTC_BASE_BASE64_H_
#include <string>
#include <vector>
namespace rtc {
class Base64 {
public:
enum DecodeOption {
DO_PARSE_STRICT = 1, // Parse only base64 characters
DO_PARSE_WHITE = 2, // Parse only base64 and whitespace characters
DO_PARSE_ANY = 3, // Parse all characters
DO_PARSE_MASK = 3,
DO_PAD_YES = 4, // Padding is required
DO_PAD_ANY = 8, // Padding is optional
DO_PAD_NO = 12, // Padding is disallowed
DO_PAD_MASK = 12,
DO_TERM_BUFFER = 16, // Must termiante at end of buffer
DO_TERM_CHAR = 32, // May terminate at any character boundary
DO_TERM_ANY = 48, // May terminate at a sub-character bit offset
DO_TERM_MASK = 48,
// Strictest interpretation
DO_STRICT = DO_PARSE_STRICT | DO_PAD_YES | DO_TERM_BUFFER,
DO_LAX = DO_PARSE_ANY | DO_PAD_ANY | DO_TERM_CHAR,
};
typedef int DecodeFlags;
static bool IsBase64Char(char ch);
// Get the char next to the |ch| from the Base64Table.
// If the |ch| is the last one in the Base64Table then returns
// the first one from the table.
// Expects the |ch| be a base64 char.
// The result will be saved in |next_ch|.
// Returns true on success.
static bool GetNextBase64Char(char ch, char* next_ch);
// Determines whether the given string consists entirely of valid base64
// encoded characters.
static bool IsBase64Encoded(const std::string& str);
static void EncodeFromArray(const void* data,
size_t len,
std::string* result);
static bool DecodeFromArray(const char* data,
size_t len,
DecodeFlags flags,
std::string* result,
size_t* data_used);
static bool DecodeFromArray(const char* data,
size_t len,
DecodeFlags flags,
std::vector<char>* result,
size_t* data_used);
static bool DecodeFromArray(const char* data,
size_t len,
DecodeFlags flags,
std::vector<uint8_t>* result,
size_t* data_used);
// Convenience Methods
static inline std::string Encode(const std::string& data) {
std::string result;
EncodeFromArray(data.data(), data.size(), &result);
return result;
}
static inline std::string Decode(const std::string& data, DecodeFlags flags) {
std::string result;
DecodeFromArray(data.data(), data.size(), flags, &result, nullptr);
return result;
}
static inline bool Decode(const std::string& data,
DecodeFlags flags,
std::string* result,
size_t* data_used) {
return DecodeFromArray(data.data(), data.size(), flags, result, data_used);
}
static inline bool Decode(const std::string& data,
DecodeFlags flags,
std::vector<char>* result,
size_t* data_used) {
return DecodeFromArray(data.data(), data.size(), flags, result, data_used);
}
private:
static const char Base64Table[];
static const unsigned char DecodeTable[];
static size_t GetNextQuantum(DecodeFlags parse_flags,
bool illegal_pads,
const char* data,
size_t len,
size_t* dpos,
unsigned char qbuf[4],
bool* padded);
template <typename T>
static bool DecodeFromArrayTemplate(const char* data,
size_t len,
DecodeFlags flags,
T* result,
size_t* data_used);
};
} // namespace rtc
#endif // WEBRTC_RTC_BASE_BASE64_H_

View File

@ -0,0 +1,999 @@
/*
* Copyright 2011 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.
*/
#include "webrtc/base/base64.h"
#include "webrtc/base/gunit.h"
#include "webrtc/base/logging.h"
#include "webrtc/base/stringutils.h"
#include "webrtc/base/testbase64.h"
using namespace std;
using namespace rtc;
static struct {
size_t plain_length;
const char* plaintext;
const char* cyphertext;
} base64_tests[] = {
// Basic bit patterns;
// values obtained with "echo -n '...' | uuencode -m test"
{ 1, "\000", "AA==" },
{ 1, "\001", "AQ==" },
{ 1, "\002", "Ag==" },
{ 1, "\004", "BA==" },
{ 1, "\010", "CA==" },
{ 1, "\020", "EA==" },
{ 1, "\040", "IA==" },
{ 1, "\100", "QA==" },
{ 1, "\200", "gA==" },
{ 1, "\377", "/w==" },
{ 1, "\376", "/g==" },
{ 1, "\375", "/Q==" },
{ 1, "\373", "+w==" },
{ 1, "\367", "9w==" },
{ 1, "\357", "7w==" },
{ 1, "\337", "3w==" },
{ 1, "\277", "vw==" },
{ 1, "\177", "fw==" },
{ 2, "\000\000", "AAA=" },
{ 2, "\000\001", "AAE=" },
{ 2, "\000\002", "AAI=" },
{ 2, "\000\004", "AAQ=" },
{ 2, "\000\010", "AAg=" },
{ 2, "\000\020", "ABA=" },
{ 2, "\000\040", "ACA=" },
{ 2, "\000\100", "AEA=" },
{ 2, "\000\200", "AIA=" },
{ 2, "\001\000", "AQA=" },
{ 2, "\002\000", "AgA=" },
{ 2, "\004\000", "BAA=" },
{ 2, "\010\000", "CAA=" },
{ 2, "\020\000", "EAA=" },
{ 2, "\040\000", "IAA=" },
{ 2, "\100\000", "QAA=" },
{ 2, "\200\000", "gAA=" },
{ 2, "\377\377", "//8=" },
{ 2, "\377\376", "//4=" },
{ 2, "\377\375", "//0=" },
{ 2, "\377\373", "//s=" },
{ 2, "\377\367", "//c=" },
{ 2, "\377\357", "/+8=" },
{ 2, "\377\337", "/98=" },
{ 2, "\377\277", "/78=" },
{ 2, "\377\177", "/38=" },
{ 2, "\376\377", "/v8=" },
{ 2, "\375\377", "/f8=" },
{ 2, "\373\377", "+/8=" },
{ 2, "\367\377", "9/8=" },
{ 2, "\357\377", "7/8=" },
{ 2, "\337\377", "3/8=" },
{ 2, "\277\377", "v/8=" },
{ 2, "\177\377", "f/8=" },
{ 3, "\000\000\000", "AAAA" },
{ 3, "\000\000\001", "AAAB" },
{ 3, "\000\000\002", "AAAC" },
{ 3, "\000\000\004", "AAAE" },
{ 3, "\000\000\010", "AAAI" },
{ 3, "\000\000\020", "AAAQ" },
{ 3, "\000\000\040", "AAAg" },
{ 3, "\000\000\100", "AABA" },
{ 3, "\000\000\200", "AACA" },
{ 3, "\000\001\000", "AAEA" },
{ 3, "\000\002\000", "AAIA" },
{ 3, "\000\004\000", "AAQA" },
{ 3, "\000\010\000", "AAgA" },
{ 3, "\000\020\000", "ABAA" },
{ 3, "\000\040\000", "ACAA" },
{ 3, "\000\100\000", "AEAA" },
{ 3, "\000\200\000", "AIAA" },
{ 3, "\001\000\000", "AQAA" },
{ 3, "\002\000\000", "AgAA" },
{ 3, "\004\000\000", "BAAA" },
{ 3, "\010\000\000", "CAAA" },
{ 3, "\020\000\000", "EAAA" },
{ 3, "\040\000\000", "IAAA" },
{ 3, "\100\000\000", "QAAA" },
{ 3, "\200\000\000", "gAAA" },
{ 3, "\377\377\377", "////" },
{ 3, "\377\377\376", "///+" },
{ 3, "\377\377\375", "///9" },
{ 3, "\377\377\373", "///7" },
{ 3, "\377\377\367", "///3" },
{ 3, "\377\377\357", "///v" },
{ 3, "\377\377\337", "///f" },
{ 3, "\377\377\277", "//+/" },
{ 3, "\377\377\177", "//9/" },
{ 3, "\377\376\377", "//7/" },
{ 3, "\377\375\377", "//3/" },
{ 3, "\377\373\377", "//v/" },
{ 3, "\377\367\377", "//f/" },
{ 3, "\377\357\377", "/+//" },
{ 3, "\377\337\377", "/9//" },
{ 3, "\377\277\377", "/7//" },
{ 3, "\377\177\377", "/3//" },
{ 3, "\376\377\377", "/v//" },
{ 3, "\375\377\377", "/f//" },
{ 3, "\373\377\377", "+///" },
{ 3, "\367\377\377", "9///" },
{ 3, "\357\377\377", "7///" },
{ 3, "\337\377\377", "3///" },
{ 3, "\277\377\377", "v///" },
{ 3, "\177\377\377", "f///" },
// Random numbers: values obtained with
//
// #! /bin/bash
// dd bs=$1 count=1 if=/dev/random of=/tmp/bar.random
// od -N $1 -t o1 /tmp/bar.random
// uuencode -m test < /tmp/bar.random
//
// where $1 is the number of bytes (2, 3)
{ 2, "\243\361", "o/E=" },
{ 2, "\024\167", "FHc=" },
{ 2, "\313\252", "y6o=" },
{ 2, "\046\041", "JiE=" },
{ 2, "\145\236", "ZZ4=" },
{ 2, "\254\325", "rNU=" },
{ 2, "\061\330", "Mdg=" },
{ 2, "\245\032", "pRo=" },
{ 2, "\006\000", "BgA=" },
{ 2, "\375\131", "/Vk=" },
{ 2, "\303\210", "w4g=" },
{ 2, "\040\037", "IB8=" },
{ 2, "\261\372", "sfo=" },
{ 2, "\335\014", "3Qw=" },
{ 2, "\233\217", "m48=" },
{ 2, "\373\056", "+y4=" },
{ 2, "\247\232", "p5o=" },
{ 2, "\107\053", "Rys=" },
{ 2, "\204\077", "hD8=" },
{ 2, "\276\211", "vok=" },
{ 2, "\313\110", "y0g=" },
{ 2, "\363\376", "8/4=" },
{ 2, "\251\234", "qZw=" },
{ 2, "\103\262", "Q7I=" },
{ 2, "\142\312", "Yso=" },
{ 2, "\067\211", "N4k=" },
{ 2, "\220\001", "kAE=" },
{ 2, "\152\240", "aqA=" },
{ 2, "\367\061", "9zE=" },
{ 2, "\133\255", "W60=" },
{ 2, "\176\035", "fh0=" },
{ 2, "\032\231", "Gpk=" },
{ 3, "\013\007\144", "Cwdk" },
{ 3, "\030\112\106", "GEpG" },
{ 3, "\047\325\046", "J9Um" },
{ 3, "\310\160\022", "yHAS" },
{ 3, "\131\100\237", "WUCf" },
{ 3, "\064\342\134", "NOJc" },
{ 3, "\010\177\004", "CH8E" },
{ 3, "\345\147\205", "5WeF" },
{ 3, "\300\343\360", "wOPw" },
{ 3, "\061\240\201", "MaCB" },
{ 3, "\225\333\044", "ldsk" },
{ 3, "\215\137\352", "jV/q" },
{ 3, "\371\147\160", "+Wdw" },
{ 3, "\030\320\051", "GNAp" },
{ 3, "\044\174\241", "JHyh" },
{ 3, "\260\127\037", "sFcf" },
{ 3, "\111\045\033", "SSUb" },
{ 3, "\202\114\107", "gkxH" },
{ 3, "\057\371\042", "L/ki" },
{ 3, "\223\247\244", "k6ek" },
{ 3, "\047\216\144", "J45k" },
{ 3, "\203\070\327", "gzjX" },
{ 3, "\247\140\072", "p2A6" },
{ 3, "\124\115\116", "VE1O" },
{ 3, "\157\162\050", "b3Io" },
{ 3, "\357\223\004", "75ME" },
{ 3, "\052\117\156", "Kk9u" },
{ 3, "\347\154\000", "52wA" },
{ 3, "\303\012\142", "wwpi" },
{ 3, "\060\035\362", "MB3y" },
{ 3, "\130\226\361", "WJbx" },
{ 3, "\173\013\071", "ews5" },
{ 3, "\336\004\027", "3gQX" },
{ 3, "\357\366\234", "7/ac" },
{ 3, "\353\304\111", "68RJ" },
{ 3, "\024\264\131", "FLRZ" },
{ 3, "\075\114\251", "PUyp" },
{ 3, "\315\031\225", "zRmV" },
{ 3, "\154\201\276", "bIG+" },
{ 3, "\200\066\072", "gDY6" },
{ 3, "\142\350\267", "Yui3" },
{ 3, "\033\000\166", "GwB2" },
{ 3, "\210\055\077", "iC0/" },
{ 3, "\341\037\124", "4R9U" },
{ 3, "\161\103\152", "cUNq" },
{ 3, "\270\142\131", "uGJZ" },
{ 3, "\337\076\074", "3z48" },
{ 3, "\375\106\362", "/Uby" },
{ 3, "\227\301\127", "l8FX" },
{ 3, "\340\002\234", "4AKc" },
{ 3, "\121\064\033", "UTQb" },
{ 3, "\157\134\143", "b1xj" },
{ 3, "\247\055\327", "py3X" },
{ 3, "\340\142\005", "4GIF" },
{ 3, "\060\260\143", "MLBj" },
{ 3, "\075\203\170", "PYN4" },
{ 3, "\143\160\016", "Y3AO" },
{ 3, "\313\013\063", "ywsz" },
{ 3, "\174\236\135", "fJ5d" },
{ 3, "\103\047\026", "QycW" },
{ 3, "\365\005\343", "9QXj" },
{ 3, "\271\160\223", "uXCT" },
{ 3, "\362\255\172", "8q16" },
{ 3, "\113\012\015", "SwoN" },
// various lengths, generated by this python script:
//
// from string import lowercase as lc
// for i in range(27):
// print '{ %2d, "%s",%s "%s" },' % (i, lc[:i], ' ' * (26-i),
// lc[:i].encode('base64').strip())
{ 0, "abcdefghijklmnopqrstuvwxyz", "" },
{ 1, "abcdefghijklmnopqrstuvwxyz", "YQ==" },
{ 2, "abcdefghijklmnopqrstuvwxyz", "YWI=" },
{ 3, "abcdefghijklmnopqrstuvwxyz", "YWJj" },
{ 4, "abcdefghijklmnopqrstuvwxyz", "YWJjZA==" },
{ 5, "abcdefghijklmnopqrstuvwxyz", "YWJjZGU=" },
{ 6, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVm" },
{ 7, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZw==" },
{ 8, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2g=" },
{ 9, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hp" },
{ 10, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpag==" },
{ 11, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpams=" },
{ 12, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamts" },
{ 13, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbQ==" },
{ 14, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW4=" },
{ 15, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW5v" },
{ 16, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW5vcA==" },
{ 17, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW5vcHE=" },
{ 18, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW5vcHFy" },
{ 19, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW5vcHFycw==" },
{ 20, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW5vcHFyc3Q=" },
{ 21, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW5vcHFyc3R1" },
{ 22, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dg==" },
{ 23, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnc=" },
{ 24, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4" },
{ 25, "abcdefghijklmnopqrstuvwxy", "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eQ==" },
{ 26, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXo=" },
};
#if 0
static struct {
const char* plaintext;
const char* cyphertext;
} base64_strings[] = {
// The first few Google quotes
// Cyphertext created with "uuencode - GNU sharutils 4.2.1"
{
"Everyone! We're teetering on the brink of disaster."
" - Sergey Brin, 6/24/99, regarding the company's state "
"after the unleashing of Netscape/Google search",
"RXZlcnlvbmUhICBXZSdyZSB0ZWV0ZXJpbmcgb24gdGhlIGJyaW5rIG9mIGRp"
"c2FzdGVyLiAtIFNlcmdleSBCcmluLCA2LzI0Lzk5LCByZWdhcmRpbmcgdGhl"
"IGNvbXBhbnkncyBzdGF0ZSBhZnRlciB0aGUgdW5sZWFzaGluZyBvZiBOZXRz"
"Y2FwZS9Hb29nbGUgc2VhcmNo" },
{
"I'm not sure why we're still alive, but we seem to be."
" - Larry Page, 6/24/99, while hiding in the kitchenette "
"during the Netscape traffic overflow",
"SSdtIG5vdCBzdXJlIHdoeSB3ZSdyZSBzdGlsbCBhbGl2ZSwgYnV0IHdlIHNl"
"ZW0gdG8gYmUuIC0gTGFycnkgUGFnZSwgNi8yNC85OSwgd2hpbGUgaGlkaW5n"
"IGluIHRoZSBraXRjaGVuZXR0ZSBkdXJpbmcgdGhlIE5ldHNjYXBlIHRyYWZm"
"aWMgb3ZlcmZsb3c" },
{
"I think kids want porn."
" - Sergey Brin, 6/99, on why Google shouldn't prioritize a "
"filtered search for children and families",
"SSB0aGluayBraWRzIHdhbnQgcG9ybi4gLSBTZXJnZXkgQnJpbiwgNi85OSwg"
"b24gd2h5IEdvb2dsZSBzaG91bGRuJ3QgcHJpb3JpdGl6ZSBhIGZpbHRlcmVk"
"IHNlYXJjaCBmb3IgY2hpbGRyZW4gYW5kIGZhbWlsaWVz" },
};
#endif
// Compare bytes 0..len-1 of x and y. If not equal, abort with verbose error
// message showing position and numeric value that differed.
// Handles embedded nulls just like any other byte.
// Only added because string.compare() in gcc-3.3.3 seems to misbehave with
// embedded nulls.
// TODO: switch back to string.compare() if/when gcc is fixed
#define EXPECT_EQ_ARRAY(len, x, y, msg) \
for (size_t j = 0; j < len; ++j) { \
if (x[j] != y[j]) { \
LOG(LS_ERROR) << "" # x << " != " # y \
<< " byte " << j << " msg: " << msg; \
} \
}
size_t Base64Escape(const unsigned char *src, size_t szsrc, char *dest,
size_t szdest) {
std::string escaped;
Base64::EncodeFromArray((const char *)src, szsrc, &escaped);
memcpy(dest, escaped.data(), min(escaped.size(), szdest));
return escaped.size();
}
size_t Base64Unescape(const char *src, size_t szsrc, char *dest,
size_t szdest) {
std::string unescaped;
EXPECT_TRUE(
Base64::DecodeFromArray(src, szsrc, Base64::DO_LAX, &unescaped, nullptr));
memcpy(dest, unescaped.data(), min(unescaped.size(), szdest));
return unescaped.size();
}
size_t Base64Unescape(const char *src, size_t szsrc, std::string *s) {
EXPECT_TRUE(Base64::DecodeFromArray(src, szsrc, Base64::DO_LAX, s, nullptr));
return s->size();
}
TEST(Base64, EncodeDecodeBattery) {
LOG(LS_VERBOSE) << "Testing base-64";
size_t i;
// Check the short strings; this tests the math (and boundaries)
for( i = 0; i < sizeof(base64_tests) / sizeof(base64_tests[0]); ++i ) {
char encode_buffer[100];
size_t encode_length;
char decode_buffer[100];
size_t decode_length;
size_t cypher_length;
LOG(LS_VERBOSE) << "B64: " << base64_tests[i].cyphertext;
const unsigned char* unsigned_plaintext =
reinterpret_cast<const unsigned char*>(base64_tests[i].plaintext);
cypher_length = strlen(base64_tests[i].cyphertext);
// The basic escape function:
memset(encode_buffer, 0, sizeof(encode_buffer));
encode_length = Base64Escape(unsigned_plaintext,
base64_tests[i].plain_length,
encode_buffer,
sizeof(encode_buffer));
// Is it of the expected length?
EXPECT_EQ(encode_length, cypher_length);
// Is it the expected encoded value?
EXPECT_STREQ(encode_buffer, base64_tests[i].cyphertext);
// If we encode it into a buffer of exactly the right length...
memset(encode_buffer, 0, sizeof(encode_buffer));
encode_length = Base64Escape(unsigned_plaintext,
base64_tests[i].plain_length,
encode_buffer,
cypher_length);
// Is it still of the expected length?
EXPECT_EQ(encode_length, cypher_length);
// And is the value still correct? (i.e., not losing the last byte)
EXPECT_STREQ(encode_buffer, base64_tests[i].cyphertext);
// If we decode it back:
memset(decode_buffer, 0, sizeof(decode_buffer));
decode_length = Base64Unescape(encode_buffer,
cypher_length,
decode_buffer,
sizeof(decode_buffer));
// Is it of the expected length?
EXPECT_EQ(decode_length, base64_tests[i].plain_length);
// Is it the expected decoded value?
EXPECT_EQ(0, memcmp(decode_buffer, base64_tests[i].plaintext, decode_length));
// Our decoder treats the padding '=' characters at the end as
// optional. If encode_buffer has any, run some additional
// tests that fiddle with them.
char* first_equals = strchr(encode_buffer, '=');
if (first_equals) {
// How many equals signs does the string start with?
int equals = (*(first_equals+1) == '=') ? 2 : 1;
// Try chopping off the equals sign(s) entirely. The decoder
// should still be okay with this.
std::string decoded2("this junk should also be ignored");
*first_equals = '\0';
EXPECT_NE(0U, Base64Unescape(encode_buffer, first_equals-encode_buffer,
&decoded2));
EXPECT_EQ(decoded2.size(), base64_tests[i].plain_length);
EXPECT_EQ_ARRAY(decoded2.size(), decoded2.data(), base64_tests[i].plaintext, i);
size_t len;
// try putting some extra stuff after the equals signs, or in between them
if (equals == 2) {
sprintfn(first_equals, 6, " = = ");
len = first_equals - encode_buffer + 5;
} else {
sprintfn(first_equals, 6, " = ");
len = first_equals - encode_buffer + 3;
}
decoded2.assign("this junk should be ignored");
EXPECT_NE(0U, Base64Unescape(encode_buffer, len, &decoded2));
EXPECT_EQ(decoded2.size(), base64_tests[i].plain_length);
EXPECT_EQ_ARRAY(decoded2.size(), decoded2, base64_tests[i].plaintext, i);
}
}
}
// here's a weird case: a giant base64 encoded stream which broke our base64
// decoding. Let's test it explicitly.
const char SpecificTest[] =
"/9j/4AAQSkZJRgABAgEASABIAAD/4Q0HRXhpZgAATU0AKgAAAAgADAEOAAIAAAAgAAAAngEPAAI\n"
"AAAAFAAAAvgEQAAIAAAAJAAAAwwESAAMAAAABAAEAAAEaAAUAAAABAAAAzAEbAAUAAAABAAAA1A\n"
"EoAAMAAAABAAIAAAExAAIAAAAUAAAA3AEyAAIAAAAUAAAA8AE8AAIAAAAQAAABBAITAAMAAAABA\n"
"AIAAIdpAAQAAAABAAABFAAAAsQgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgAFNPTlkA\n"
"RFNDLVAyMDAAAAAASAAAAAEAAABIAAAAAUFkb2JlIFBob3Rvc2hvcCA3LjAAMjAwNzowMTozMCA\n"
"yMzoxMDowNABNYWMgT1MgWCAxMC40LjgAAByCmgAFAAAAAQAAAmqCnQAFAAAAAQAAAnKIIgADAA\n"
"AAAQACAACIJwADAAAAAQBkAACQAAAHAAAABDAyMjCQAwACAAAAFAAAAnqQBAACAAAAFAAAAo6RA\n"
"QAHAAAABAECAwCRAgAFAAAAAQAAAqKSBAAKAAAAAQAAAqqSBQAFAAAAAQAAArKSBwADAAAAAQAF\n"
"AACSCAADAAAAAQAAAACSCQADAAAAAQAPAACSCgAFAAAAAQAAArqgAAAHAAAABDAxMDCgAQADAAA\n"
"AAf//AACgAgAEAAAAAQAAAGSgAwAEAAAAAQAAAGSjAAAHAAAAAQMAAACjAQAHAAAAAQEAAACkAQ\n"
"ADAAAAAQAAAACkAgADAAAAAQAAAACkAwADAAAAAQAAAACkBgADAAAAAQAAAACkCAADAAAAAQAAA\n"
"ACkCQADAAAAAQAAAACkCgADAAAAAQAAAAAAAAAAAAAACgAAAZAAAAAcAAAACjIwMDc6MDE6MjAg\n"
"MjM6MDU6NTIAMjAwNzowMToyMCAyMzowNTo1MgAAAAAIAAAAAQAAAAAAAAAKAAAAMAAAABAAAAB\n"
"PAAAACgAAAAYBAwADAAAAAQAGAAABGgAFAAAAAQAAAxIBGwAFAAAAAQAAAxoBKAADAAAAAQACAA\n"
"ACAQAEAAAAAQAAAyICAgAEAAAAAQAACd0AAAAAAAAASAAAAAEAAABIAAAAAf/Y/+AAEEpGSUYAA\n"
"QIBAEgASAAA/+0ADEFkb2JlX0NNAAL/7gAOQWRvYmUAZIAAAAAB/9sAhAAMCAgICQgMCQkMEQsK\n"
"CxEVDwwMDxUYExMVExMYEQwMDAwMDBEMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMAQ0LCw0\n"
"ODRAODhAUDg4OFBQODg4OFBEMDAwMDBERDAwMDAwMEQwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA\n"
"wMDAz/wAARCABkAGQDASIAAhEBAxEB/90ABAAH/8QBPwAAAQUBAQEBAQEAAAAAAAAAAwABAgQFB\n"
"gcICQoLAQABBQEBAQEBAQAAAAAAAAABAAIDBAUGBwgJCgsQAAEEAQMCBAIFBwYIBQMMMwEAAhED\n"
"BCESMQVBUWETInGBMgYUkaGxQiMkFVLBYjM0coLRQwclklPw4fFjczUWorKDJkSTVGRFwqN0Nhf\n"
"SVeJl8rOEw9N14/NGJ5SkhbSVxNTk9KW1xdXl9VZmdoaWprbG1ub2N0dXZ3eHl6e3x9fn9xEAAg\n"
"IBAgQEAwQFBgcHBgU1AQACEQMhMRIEQVFhcSITBTKBkRShsUIjwVLR8DMkYuFygpJDUxVjczTxJ\n"
"QYWorKDByY1wtJEk1SjF2RFVTZ0ZeLys4TD03Xj80aUpIW0lcTU5PSltcXV5fVWZnaGlqa2xtbm\n"
"9ic3R1dnd4eXp7fH/9oADAMBAAIRAxEAPwDy7bKNTUXNLz9EaJPDWMjxH4ozhtpYwaACT8ShaaW\n"
"bW0uEc9/JFfjj0Q4Hk/PRDxwX7y47W9z/AN9Cv4+O3ILK2DcRqT2CaSvEbcl1Jbz37KG1dBldLo\n"
"qaS4l9xGjG9v6yoDAdYIaIjUk+AREgo4y5sapirb8Yl0NHHdKvBNm4yA1o5Pc+SPEFvCWqB3HZF\n"
"Hj2SbWQ/afGFP0bHP8ATY0uc4w1o1JPkkimGiS2KvqlnmBkOZQTyydzgPMM9v8A0lp4v1Nx9gF1\n"
"tpdqJaGtH/S3I0i3lISXW/8AMqnd/O2bfg2eUkqVYf/Q8zuncO4Bj7lZ+n7f5Mj5KsJcY8NUZ4d\n"
"uEDVo1HkeU0rg3Om4H2rabCWUN7DQuK1n5FWKW4uCwG92gDRJBS6exhxmMboQI+Cv4WFTQ42Bs2\n"
"fvnkkqEmy2YxoMMbpVzaz6jt+RbpHZs8lzkHqrasKkYOKP0jgDfZ4N/wDM1tNrcWfSPmRyq9uNV\n"
"DnFg2s97i7UkjxKVrq0eVz3spZsja+ASDzwsh9jnOk/JFzb3XZD3v1c4yT8UACTCniKDUnKz5Nj\n"
"G33XV1DV73BrT8dF23SejV4zg9g33cOsPb+SxVvqv9ViwNy8vS0iWs/daf8A0Y5dpTi1sADGxCR\n"
"K1o0YBEmInlXWYbDBcDLdPJXa8f71Yrx2jnUoAqLnfZK5hJaW2vdwEk5a/wD/0fN6Ia/e76IiVf\n"
"xavUL7CPpnT4LNbYXAVjuQt/AqDmNYO/Kjnoy4hr5J8SwMhrRMaeSvbsxrfUazcOw4UX0Cisem2\n"
"SBoD4+Kz8nC6llbSLCRrubJA8kwUWbUDa29X1PMa7aQWjuDC0MXMdbDbhI7eazBiUfZ6GOYRe1s\n"
"WvGgJ8Vbw2+m4Bx9s6JpNHuuGo1FF53r/SHYua61gLse0lzXeBP5rkvqx0o5vVWz7WY49QkiQSP\n"
"oN/tLoevW/ogxv0HA7tJ0AnhT+pdDGYVl/wCdcTPkGn2NU0JWNWvlgAbHV6fEqdu2gR/r2WlWwt\n"
"AA5VXAEsLXTqJafArQY5rRr9LiPBJiZsZCI1pJjxCi0j4oncSICSkWwzwkjeaSch//0vO7sP7Lm\n"
"enO9ogtd5FbPT3Q5pCpZVc4ld3Lmn3O8j9EI2BYdunKjOobMQIyI+rusc2wx4d0eutwGnHh/uQc\n"
"Ha7ladj6mVANGvcqOgz0Go7HJ12/GEHcwvB/dPY6ImbbaMaASGuIBjkN7qofs9Ubg9g7OI9p/t/\n"
"RTSmhTHr0v6eSz6UgCPP2/wAVu9Ex2V49dVY2iACB4BZeVXQ/AJ3gzGnnOi2+kACpru8flUsNmt\n"
"zHRf6xfWCnoeAfTh2ZaQKazx/Ke7+QxcKz61fWA2uuObaC4zGhaPJrXBL64ZFmR124O09ENraPK\n"
"N3/AH5GqxIrZVUyp2K2vfdkENsDnxuex9m4Ox9n82xSgNd9D+p/XR1npgseR9ppOy4Dx/NfH/CL\n"
"oQJGunmvMv8AFq3KHVcq3HkYQbD2nuSf0I/rMavSg6TLjLigQhJ7Z58v9QkmlsTOqSCn/9PzL7R\n"
"d6Qq3n0wZ2zotXpT9xLfFYvkr/S7jXeB8E0jRkhKpC3q8LcJ/kmCrTnkuAPCq4do9Q/ytVbuAeY\n"
"Gg5lQybQK+82GBqEQUA1kOHPYf3LLsoyN36G5w8iUfHxepbXE2l0cApALgLHzBq9UxhTXU5hMC1\n"
"ktnSCup6S4Ctk+C5XqVGcaHPfuiuHkeTTuWz0+9zaKiH6CC0/yXBSQ2a/MxojV57634rq+v2PLY\n"
"be1r2nsYG13/AFKxbfCBMcr0brGAzrGEwCG31ncx0SfBzf7S4+zoHUWWsJq3hz9oLfcBH77R9H+\n"
"0pA13u/qPgDp/Q6ri39JlfpXkDx+h/msWn1L6wdO6bSbcrIbU2Q0xLnSe21kuVejJspbVS5+4bd\n"
"ocBAkD/orG+tP1ar67Wy7GtZTm1SCXfRsb+a18fRe38x6SG3/44H1Z3f0y2I+l6DoSXD/8xPrDs\n"
"3enVu3bdnqN3R+//USSVo//1PLohhce+gRWS0Nsby3lRgFkKxQyW7SgUh3em5Tbq2uB9wWw1wey\n"
"J1XGV2XYdm5k7e4WzidXY9oMwo5RZ4T6Hd1ixwfp96PWbAJBVTHzK7O6Ky5oJB1HZMqmUEFlkGy\n"
"xpa4zI1Hkq31dy7bMN9BAc3HeWAnnbyxEycmuup1jiAGglZ31PyrmZ9tQg1WtNj54EHR3/S2qTH\n"
"1Yc5GgD1FFtzPdWGkd2AyflogZmRmsz6PSrbXbdo+txOrP337f3fzVo15DK2uyrTtqpBOnBKx6b\n"
"7MjJsz7tHWOAYP3WD6LU6cqGjFCNl1MmvLcxv6YtDTLSAqP27LrdtYHXFnJZI+Tp3MWg68OpDPv\n"
"UMUM2lkQBoouKQ6swjE9Nml+1sz1PW+z6xt27zuj+skrX2ZvqR5z8kkuOfdPt43/1fMm/grFG6f\n"
"Lss9JA7JG7tnZs/SfJUrfS3foJ9TvHCopJsV8nWx/t24bJn8Fo/5TjWJXMJIS+i+G36TsZ/7Q9P\n"
"8ATfzfeOFofVSZv2/zvt+O3X/v65dJPjt/BiyfN1/wn0zre79nVej/ADG8ep4x2/6Srjd6TdviF\n"
"52ko8m6/Ht9X1KnftEo+POwxzK8mSTF46vrH6T1/OEl5Okkl//Z/+0uHFBob3Rvc2hvcCAzLjAA\n"
"OEJJTQQEAAAAAAArHAIAAAIAAhwCeAAfICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAA\n"
"4QklNBCUAAAAAABD7Caa9B0wqNp2P4sxXqayFOEJJTQPqAAAAAB2wPD94bWwgdmVyc2lvbj0iMS\n"
"4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPCFET0NUWVBFIHBsaXN0IFBVQkxJQyAiLS8vQXBwbGUgQ\n"
"29tcHV0ZXIvL0RURCBQTElTVCAxLjAvL0VOIiAiaHR0cDovL3d3dy5hcHBsZS5jb20vRFREcy9Q\n"
"cm9wZXJ0eUxpc3QtMS4wLmR0ZCI+CjxwbGlzdCB2ZXJzaW9uPSIxLjAiPgo8ZGljdD4KCTxrZXk\n"
"+Y29tLmFwcGxlLnByaW50LlBhZ2VGb3JtYXQuUE1Ib3Jpem9udGFsUmVzPC9rZXk+Cgk8ZGljdD\n"
"4KCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuY3JlYXRvcjwva2V5PgoJCTxzdHJpbmc+Y\n"
"29tLmFwcGxlLnByaW50aW5nbWFuYWdlcjwvc3RyaW5nPgoJCTxrZXk+Y29tLmFwcGxlLnByaW50\n"
"LnRpY2tldC5pdGVtQXJyYXk8L2tleT4KCQk8YXJyYXk+CgkJCTxkaWN0PgoJCQkJPGtleT5jb20\n"
"uYXBwbGUucHJpbnQuUGFnZUZvcm1hdC5QTUhvcml6b250YWxSZXM8L2tleT4KCQkJCTxyZWFsPj\n"
"cyPC9yZWFsPgoJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LmNsaWVudDwva2V5PgoJC\n"
"QkJPHN0cmluZz5jb20uYXBwbGUucHJpbnRpbmdtYW5hZ2VyPC9zdHJpbmc+CgkJCQk8a2V5PmNv\n"
"bS5hcHBsZS5wcmludC50aWNrZXQubW9kRGF0ZTwva2V5PgoJCQkJPGRhdGU+MjAwNy0wMS0zMFQ\n"
"yMjowODo0MVo8L2RhdGU+CgkJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuc3RhdGVGbG\n"
"FnPC9rZXk+CgkJCQk8aW50ZWdlcj4wPC9pbnRlZ2VyPgoJCQk8L2RpY3Q+CgkJPC9hcnJheT4KC\n"
"TwvZGljdD4KCTxrZXk+Y29tLmFwcGxlLnByaW50LlBhZ2VGb3JtYXQuUE1PcmllbnRhdGlvbjwv\n"
"a2V5PgoJPGRpY3Q+CgkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LmNyZWF0b3I8L2tleT4\n"
"KCQk8c3RyaW5nPmNvbS5hcHBsZS5wcmludGluZ21hbmFnZXI8L3N0cmluZz4KCQk8a2V5PmNvbS\n"
"5hcHBsZS5wcmludC50aWNrZXQuaXRlbUFycmF5PC9rZXk+CgkJPGFycmF5PgoJCQk8ZGljdD4KC\n"
"QkJCTxrZXk+Y29tLmFwcGxlLnByaW50LlBhZ2VGb3JtYXQuUE1PcmllbnRhdGlvbjwva2V5PgoJ\n"
"CQkJPGludGVnZXI+MTwvaW50ZWdlcj4KCQkJCTxrZXk+Y29tLmFwcGxlLnByaW50LnRpY2tldC5\n"
"jbGllbnQ8L2tleT4KCQkJCTxzdHJpbmc+Y29tLmFwcGxlLnByaW50aW5nbWFuYWdlcjwvc3RyaW\n"
"5nPgoJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0Lm1vZERhdGU8L2tleT4KCQkJCTxkY\n"
"XRlPjIwMDctMDEtMzBUMjI6MDg6NDFaPC9kYXRlPgoJCQkJPGtleT5jb20uYXBwbGUucHJpbnQu\n"
"dGlja2V0LnN0YXRlRmxhZzwva2V5PgoJCQkJPGludGVnZXI+MDwvaW50ZWdlcj4KCQkJPC9kaWN\n"
"0PgoJCTwvYXJyYXk+Cgk8L2RpY3Q+Cgk8a2V5PmNvbS5hcHBsZS5wcmludC5QYWdlRm9ybWF0Ll\n"
"BNU2NhbGluZzwva2V5PgoJPGRpY3Q+CgkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LmNyZ\n"
"WF0b3I8L2tleT4KCQk8c3RyaW5nPmNvbS5hcHBsZS5wcmludGluZ21hbmFnZXI8L3N0cmluZz4K\n"
"CQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuaXRlbUFycmF5PC9rZXk+CgkJPGFycmF5Pgo\n"
"JCQk8ZGljdD4KCQkJCTxrZXk+Y29tLmFwcGxlLnByaW50LlBhZ2VGb3JtYXQuUE1TY2FsaW5nPC\n"
"9rZXk+CgkJCQk8cmVhbD4xPC9yZWFsPgoJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0L\n"
"mNsaWVudDwva2V5PgoJCQkJPHN0cmluZz5jb20uYXBwbGUucHJpbnRpbmdtYW5hZ2VyPC9zdHJp\n"
"bmc+CgkJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQubW9kRGF0ZTwva2V5PgoJCQkJPGR\n"
"hdGU+MjAwNy0wMS0zMFQyMjowODo0MVo8L2RhdGU+CgkJCQk8a2V5PmNvbS5hcHBsZS5wcmludC\n"
"50aWNrZXQuc3RhdGVGbGFnPC9rZXk+CgkJCQk8aW50ZWdlcj4wPC9pbnRlZ2VyPgoJCQk8L2RpY\n"
"3Q+CgkJPC9hcnJheT4KCTwvZGljdD4KCTxrZXk+Y29tLmFwcGxlLnByaW50LlBhZ2VGb3JtYXQu\n"
"UE1WZXJ0aWNhbFJlczwva2V5PgoJPGRpY3Q+CgkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V\n"
"0LmNyZWF0b3I8L2tleT4KCQk8c3RyaW5nPmNvbS5hcHBsZS5wcmludGluZ21hbmFnZXI8L3N0cm\n"
"luZz4KCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuaXRlbUFycmF5PC9rZXk+CgkJPGFyc\n"
"mF5PgoJCQk8ZGljdD4KCQkJCTxrZXk+Y29tLmFwcGxlLnByaW50LlBhZ2VGb3JtYXQuUE1WZXJ0\n"
"aWNhbFJlczwva2V5PgoJCQkJPHJlYWw+NzI8L3JlYWw+CgkJCQk8a2V5PmNvbS5hcHBsZS5wcml\n"
"udC50aWNrZXQuY2xpZW50PC9rZXk+CgkJCQk8c3RyaW5nPmNvbS5hcHBsZS5wcmludGluZ21hbm\n"
"FnZXI8L3N0cmluZz4KCQkJCTxrZXk+Y29tLmFwcGxlLnByaW50LnRpY2tldC5tb2REYXRlPC9rZ\n"
"Xk+CgkJCQk8ZGF0ZT4yMDA3LTAxLTMwVDIyOjA4OjQxWjwvZGF0ZT4KCQkJCTxrZXk+Y29tLmFw\n"
"cGxlLnByaW50LnRpY2tldC5zdGF0ZUZsYWc8L2tleT4KCQkJCTxpbnRlZ2VyPjA8L2ludGVnZXI\n"
"+CgkJCTwvZGljdD4KCQk8L2FycmF5PgoJPC9kaWN0PgoJPGtleT5jb20uYXBwbGUucHJpbnQuUG\n"
"FnZUZvcm1hdC5QTVZlcnRpY2FsU2NhbGluZzwva2V5PgoJPGRpY3Q+CgkJPGtleT5jb20uYXBwb\n"
"GUucHJpbnQudGlja2V0LmNyZWF0b3I8L2tleT4KCQk8c3RyaW5nPmNvbS5hcHBsZS5wcmludGlu\n"
"Z21hbmFnZXI8L3N0cmluZz4KCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuaXRlbUFycmF\n"
"5PC9rZXk+CgkJPGFycmF5PgoJCQk8ZGljdD4KCQkJCTxrZXk+Y29tLmFwcGxlLnByaW50LlBhZ2\n"
"VGb3JtYXQuUE1WZXJ0aWNhbFNjYWxpbmc8L2tleT4KCQkJCTxyZWFsPjE8L3JlYWw+CgkJCQk8a\n"
"2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuY2xpZW50PC9rZXk+CgkJCQk8c3RyaW5nPmNvbS5h\n"
"cHBsZS5wcmludGluZ21hbmFnZXI8L3N0cmluZz4KCQkJCTxrZXk+Y29tLmFwcGxlLnByaW50LnR\n"
"pY2tldC5tb2REYXRlPC9rZXk+CgkJCQk8ZGF0ZT4yMDA3LTAxLTMwVDIyOjA4OjQxWjwvZGF0ZT\n"
"4KCQkJCTxrZXk+Y29tLmFwcGxlLnByaW50LnRpY2tldC5zdGF0ZUZsYWc8L2tleT4KCQkJCTxpb\n"
"nRlZ2VyPjA8L2ludGVnZXI+CgkJCTwvZGljdD4KCQk8L2FycmF5PgoJPC9kaWN0PgoJPGtleT5j\n"
"b20uYXBwbGUucHJpbnQuc3ViVGlja2V0LnBhcGVyX2luZm9fdGlja2V0PC9rZXk+Cgk8ZGljdD4\n"
"KCQk8a2V5PmNvbS5hcHBsZS5wcmludC5QYWdlRm9ybWF0LlBNQWRqdXN0ZWRQYWdlUmVjdDwva2\n"
"V5PgoJCTxkaWN0PgoJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuY3JlYXRvcjwva2V5P\n"
"goJCQk8c3RyaW5nPmNvbS5hcHBsZS5wcmludGluZ21hbmFnZXI8L3N0cmluZz4KCQkJPGtleT5j\n"
"b20uYXBwbGUucHJpbnQudGlja2V0Lml0ZW1BcnJheTwva2V5PgoJCQk8YXJyYXk+CgkJCQk8ZGl\n"
"jdD4KCQkJCQk8a2V5PmNvbS5hcHBsZS5wcmludC5QYWdlRm9ybWF0LlBNQWRqdXN0ZWRQYWdlUm\n"
"VjdDwva2V5PgoJCQkJCTxhcnJheT4KCQkJCQkJPHJlYWw+MC4wPC9yZWFsPgoJCQkJCQk8cmVhb\n"
"D4wLjA8L3JlYWw+CgkJCQkJCTxyZWFsPjczNDwvcmVhbD4KCQkJCQkJPHJlYWw+NTc2PC9yZWFs\n"
"PgoJCQkJCTwvYXJyYXk+CgkJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LmNsaWVudDw\n"
"va2V5PgoJCQkJCTxzdHJpbmc+Y29tLmFwcGxlLnByaW50aW5nbWFuYWdlcjwvc3RyaW5nPgoJCQ\n"
"kJCTxrZXk+Y29tLmFwcGxlLnByaW50LnRpY2tldC5tb2REYXRlPC9rZXk+CgkJCQkJPGRhdGU+M\n"
"jAwNy0wMS0zMFQyMjowODo0MVo8L2RhdGU+CgkJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlj\n"
"a2V0LnN0YXRlRmxhZzwva2V5PgoJCQkJCTxpbnRlZ2VyPjA8L2ludGVnZXI+CgkJCQk8L2RpY3Q\n"
"+CgkJCTwvYXJyYXk+CgkJPC9kaWN0PgoJCTxrZXk+Y29tLmFwcGxlLnByaW50LlBhZ2VGb3JtYX\n"
"QuUE1BZGp1c3RlZFBhcGVyUmVjdDwva2V5PgoJCTxkaWN0PgoJCQk8a2V5PmNvbS5hcHBsZS5wc\n"
"mludC50aWNrZXQuY3JlYXRvcjwva2V5PgoJCQk8c3RyaW5nPmNvbS5hcHBsZS5wcmludGluZ21h\n"
"bmFnZXI8L3N0cmluZz4KCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0Lml0ZW1BcnJheTw\n"
"va2V5PgoJCQk8YXJyYXk+CgkJCQk8ZGljdD4KCQkJCQk8a2V5PmNvbS5hcHBsZS5wcmludC5QYW\n"
"dlRm9ybWF0LlBNQWRqdXN0ZWRQYXBlclJlY3Q8L2tleT4KCQkJCQk8YXJyYXk+CgkJCQkJCTxyZ\n"
"WFsPi0xODwvcmVhbD4KCQkJCQkJPHJlYWw+LTE4PC9yZWFsPgoJCQkJCQk8cmVhbD43NzQ8L3Jl\n"
"YWw+CgkJCQkJCTxyZWFsPjU5NDwvcmVhbD4KCQkJCQk8L2FycmF5PgoJCQkJCTxrZXk+Y29tLmF\n"
"wcGxlLnByaW50LnRpY2tldC5jbGllbnQ8L2tleT4KCQkJCQk8c3RyaW5nPmNvbS5hcHBsZS5wcm\n"
"ludGluZ21hbmFnZXI8L3N0cmluZz4KCQkJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQub\n"
"W9kRGF0ZTwva2V5PgoJCQkJCTxkYXRlPjIwMDctMDEtMzBUMjI6MDg6NDFaPC9kYXRlPgoJCQkJ\n"
"CTxrZXk+Y29tLmFwcGxlLnByaW50LnRpY2tldC5zdGF0ZUZsYWc8L2tleT4KCQkJCQk8aW50ZWd\n"
"lcj4wPC9pbnRlZ2VyPgoJCQkJPC9kaWN0PgoJCQk8L2FycmF5PgoJCTwvZGljdD4KCQk8a2V5Pm\n"
"NvbS5hcHBsZS5wcmludC5QYXBlckluZm8uUE1QYXBlck5hbWU8L2tleT4KCQk8ZGljdD4KCQkJP\n"
"GtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LmNyZWF0b3I8L2tleT4KCQkJPHN0cmluZz5jb20u\n"
"YXBwbGUucHJpbnQucG0uUG9zdFNjcmlwdDwvc3RyaW5nPgoJCQk8a2V5PmNvbS5hcHBsZS5wcml\n"
"udC50aWNrZXQuaXRlbUFycmF5PC9rZXk+CgkJCTxhcnJheT4KCQkJCTxkaWN0PgoJCQkJCTxrZX\n"
"k+Y29tLmFwcGxlLnByaW50LlBhcGVySW5mby5QTVBhcGVyTmFtZTwva2V5PgoJCQkJCTxzdHJpb\n"
"mc+bmEtbGV0dGVyPC9zdHJpbmc+CgkJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LmNs\n"
"aWVudDwva2V5PgoJCQkJCTxzdHJpbmc+Y29tLmFwcGxlLnByaW50LnBtLlBvc3RTY3JpcHQ8L3N\n"
"0cmluZz4KCQkJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQubW9kRGF0ZTwva2V5PgoJCQ\n"
"kJCTxkYXRlPjIwMDMtMDctMDFUMTc6NDk6MzZaPC9kYXRlPgoJCQkJCTxrZXk+Y29tLmFwcGxlL\n"
"nByaW50LnRpY2tldC5zdGF0ZUZsYWc8L2tleT4KCQkJCQk8aW50ZWdlcj4xPC9pbnRlZ2VyPgoJ\n"
"CQkJPC9kaWN0PgoJCQk8L2FycmF5PgoJCTwvZGljdD4KCQk8a2V5PmNvbS5hcHBsZS5wcmludC5\n"
"QYXBlckluZm8uUE1VbmFkanVzdGVkUGFnZVJlY3Q8L2tleT4KCQk8ZGljdD4KCQkJPGtleT5jb2\n"
"0uYXBwbGUucHJpbnQudGlja2V0LmNyZWF0b3I8L2tleT4KCQkJPHN0cmluZz5jb20uYXBwbGUuc\n"
"HJpbnQucG0uUG9zdFNjcmlwdDwvc3RyaW5nPgoJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNr\n"
"ZXQuaXRlbUFycmF5PC9rZXk+CgkJCTxhcnJheT4KCQkJCTxkaWN0PgoJCQkJCTxrZXk+Y29tLmF\n"
"wcGxlLnByaW50LlBhcGVySW5mby5QTVVuYWRqdXN0ZWRQYWdlUmVjdDwva2V5PgoJCQkJCTxhcn\n"
"JheT4KCQkJCQkJPHJlYWw+MC4wPC9yZWFsPgoJCQkJCQk8cmVhbD4wLjA8L3JlYWw+CgkJCQkJC\n"
"TxyZWFsPjczNDwvcmVhbD4KCQkJCQkJPHJlYWw+NTc2PC9yZWFsPgoJCQkJCTwvYXJyYXk+CgkJ\n"
"CQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LmNsaWVudDwva2V5PgoJCQkJCTxzdHJpbmc\n"
"+Y29tLmFwcGxlLnByaW50aW5nbWFuYWdlcjwvc3RyaW5nPgoJCQkJCTxrZXk+Y29tLmFwcGxlLn\n"
"ByaW50LnRpY2tldC5tb2REYXRlPC9rZXk+CgkJCQkJPGRhdGU+MjAwNy0wMS0zMFQyMjowODo0M\n"
"Vo8L2RhdGU+CgkJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LnN0YXRlRmxhZzwva2V5\n"
"PgoJCQkJCTxpbnRlZ2VyPjA8L2ludGVnZXI+CgkJCQk8L2RpY3Q+CgkJCTwvYXJyYXk+CgkJPC9\n"
"kaWN0PgoJCTxrZXk+Y29tLmFwcGxlLnByaW50LlBhcGVySW5mby5QTVVuYWRqdXN0ZWRQYXBlcl\n"
"JlY3Q8L2tleT4KCQk8ZGljdD4KCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LmNyZWF0b\n"
"3I8L2tleT4KCQkJPHN0cmluZz5jb20uYXBwbGUucHJpbnQucG0uUG9zdFNjcmlwdDwvc3RyaW5n\n"
"PgoJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuaXRlbUFycmF5PC9rZXk+CgkJCTxhcnJ\n"
"heT4KCQkJCTxkaWN0PgoJCQkJCTxrZXk+Y29tLmFwcGxlLnByaW50LlBhcGVySW5mby5QTVVuYW\n"
"RqdXN0ZWRQYXBlclJlY3Q8L2tleT4KCQkJCQk8YXJyYXk+CgkJCQkJCTxyZWFsPi0xODwvcmVhb\n"
"D4KCQkJCQkJPHJlYWw+LTE4PC9yZWFsPgoJCQkJCQk8cmVhbD43NzQ8L3JlYWw+CgkJCQkJCTxy\n"
"ZWFsPjU5NDwvcmVhbD4KCQkJCQk8L2FycmF5PgoJCQkJCTxrZXk+Y29tLmFwcGxlLnByaW50LnR\n"
"pY2tldC5jbGllbnQ8L2tleT4KCQkJCQk8c3RyaW5nPmNvbS5hcHBsZS5wcmludGluZ21hbmFnZX\n"
"I8L3N0cmluZz4KCQkJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQubW9kRGF0ZTwva2V5P\n"
"goJCQkJCTxkYXRlPjIwMDctMDEtMzBUMjI6MDg6NDFaPC9kYXRlPgoJCQkJCTxrZXk+Y29tLmFw\n"
"cGxlLnByaW50LnRpY2tldC5zdGF0ZUZsYWc8L2tleT4KCQkJCQk8aW50ZWdlcj4wPC9pbnRlZ2V\n"
"yPgoJCQkJPC9kaWN0PgoJCQk8L2FycmF5PgoJCTwvZGljdD4KCQk8a2V5PmNvbS5hcHBsZS5wcm\n"
"ludC5QYXBlckluZm8ucHBkLlBNUGFwZXJOYW1lPC9rZXk+CgkJPGRpY3Q+CgkJCTxrZXk+Y29tL\n"
"mFwcGxlLnByaW50LnRpY2tldC5jcmVhdG9yPC9rZXk+CgkJCTxzdHJpbmc+Y29tLmFwcGxlLnBy\n"
"aW50LnBtLlBvc3RTY3JpcHQ8L3N0cmluZz4KCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V\n"
"0Lml0ZW1BcnJheTwva2V5PgoJCQk8YXJyYXk+CgkJCQk8ZGljdD4KCQkJCQk8a2V5PmNvbS5hcH\n"
"BsZS5wcmludC5QYXBlckluZm8ucHBkLlBNUGFwZXJOYW1lPC9rZXk+CgkJCQkJPHN0cmluZz5VU\n"
"yBMZXR0ZXI8L3N0cmluZz4KCQkJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuY2xpZW50\n"
"PC9rZXk+CgkJCQkJPHN0cmluZz5jb20uYXBwbGUucHJpbnQucG0uUG9zdFNjcmlwdDwvc3RyaW5\n"
"nPgoJCQkJCTxrZXk+Y29tLmFwcGxlLnByaW50LnRpY2tldC5tb2REYXRlPC9rZXk+CgkJCQkJPG\n"
"RhdGU+MjAwMy0wNy0wMVQxNzo0OTozNlo8L2RhdGU+CgkJCQkJPGtleT5jb20uYXBwbGUucHJpb\n"
"nQudGlja2V0LnN0YXRlRmxhZzwva2V5PgoJCQkJCTxpbnRlZ2VyPjE8L2ludGVnZXI+CgkJCQk8\n"
"L2RpY3Q+CgkJCTwvYXJyYXk+CgkJPC9kaWN0PgoJCTxrZXk+Y29tLmFwcGxlLnByaW50LnRpY2t\n"
"ldC5BUElWZXJzaW9uPC9rZXk+CgkJPHN0cmluZz4wMC4yMDwvc3RyaW5nPgoJCTxrZXk+Y29tLm\n"
"FwcGxlLnByaW50LnRpY2tldC5wcml2YXRlTG9jazwva2V5PgoJCTxmYWxzZS8+CgkJPGtleT5jb\n"
"20uYXBwbGUucHJpbnQudGlja2V0LnR5cGU8L2tleT4KCQk8c3RyaW5nPmNvbS5hcHBsZS5wcmlu\n"
"dC5QYXBlckluZm9UaWNrZXQ8L3N0cmluZz4KCTwvZGljdD4KCTxrZXk+Y29tLmFwcGxlLnByaW5\n"
"0LnRpY2tldC5BUElWZXJzaW9uPC9rZXk+Cgk8c3RyaW5nPjAwLjIwPC9zdHJpbmc+Cgk8a2V5Pm\n"
"NvbS5hcHBsZS5wcmludC50aWNrZXQucHJpdmF0ZUxvY2s8L2tleT4KCTxmYWxzZS8+Cgk8a2V5P\n"
"mNvbS5hcHBsZS5wcmludC50aWNrZXQudHlwZTwva2V5PgoJPHN0cmluZz5jb20uYXBwbGUucHJp\n"
"bnQuUGFnZUZvcm1hdFRpY2tldDwvc3RyaW5nPgo8L2RpY3Q+CjwvcGxpc3Q+CjhCSU0D6QAAAAA\n"
"AeAADAAAASABIAAAAAALeAkD/7v/uAwYCUgNnBSgD/AACAAAASABIAAAAAALYAigAAQAAAGQAAA\n"
"ABAAMDAwAAAAF//wABAAEAAAAAAAAAAAAAAABoCAAZAZAAAAAAACAAAAAAAAAAAAAAAAAAAAAAA\n"
"AAAAAAAAAAAADhCSU0D7QAAAAAAEABIAAAAAQABAEgAAAABAAE4QklNBCYAAAAAAA4AAAAAAAAA\n"
"AAAAP4AAADhCSU0EDQAAAAAABAAAAB44QklNBBkAAAAAAAQAAAAeOEJJTQPzAAAAAAAJAAAAAAA\n"
"AAAABADhCSU0ECgAAAAAAAQAAOEJJTScQAAAAAAAKAAEAAAAAAAAAAThCSU0D9QAAAAAASAAvZm\n"
"YAAQBsZmYABgAAAAAAAQAvZmYAAQChmZoABgAAAAAAAQAyAAAAAQBaAAAABgAAAAAAAQA1AAAAA\n"
"QAtAAAABgAAAAAAAThCSU0D+AAAAAAAcAAA/////////////////////////////wPoAAAAAP//\n"
"//////////////////////////8D6AAAAAD/////////////////////////////A+gAAAAA///\n"
"//////////////////////////wPoAAA4QklNBAgAAAAAABAAAAABAAACQAAAAkAAAAAAOEJJTQ\n"
"QeAAAAAAAEAAAAADhCSU0EGgAAAAADRQAAAAYAAAAAAAAAAAAAAGQAAABkAAAACABEAFMAQwAwA\n"
"DIAMwAyADUAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAGQAAABkAAAAAAAAAAAA\n"
"AAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAEAAAAAAABudWxsAAAAAgAAAAZib3VuZHN\n"
"PYmpjAAAAAQAAAAAAAFJjdDEAAAAEAAAAAFRvcCBsb25nAAAAAAAAAABMZWZ0bG9uZwAAAAAAAA\n"
"AAQnRvbWxvbmcAAABkAAAAAFJnaHRsb25nAAAAZAAAAAZzbGljZXNWbExzAAAAAU9iamMAAAABA\n"
"AAAAAAFc2xpY2UAAAASAAAAB3NsaWNlSURsb25nAAAAAAAAAAdncm91cElEbG9uZwAAAAAAAAAG\n"
"b3JpZ2luZW51bQAAAAxFU2xpY2VPcmlnaW4AAAANYXV0b0dlbmVyYXRlZAAAAABUeXBlZW51bQA\n"
"AAApFU2xpY2VUeXBlAAAAAEltZyAAAAAGYm91bmRzT2JqYwAAAAEAAAAAAABSY3QxAAAABAAAAA\n"
"BUb3AgbG9uZwAAAAAAAAAATGVmdGxvbmcAAAAAAAAAAEJ0b21sb25nAAAAZAAAAABSZ2h0bG9uZ\n"
"wAAAGQAAAADdXJsVEVYVAAAAAEAAAAAAABudWxsVEVYVAAAAAEAAAAAAABNc2dlVEVYVAAAAAEA\n"
"AAAAAAZhbHRUYWdURVhUAAAAAQAAAAAADmNlbGxUZXh0SXNIVE1MYm9vbAEAAAAIY2VsbFRleHR\n"
"URVhUAAAAAQAAAAAACWhvcnpBbGlnbmVudW0AAAAPRVNsaWNlSG9yekFsaWduAAAAB2RlZmF1bH\n"
"QAAAAJdmVydEFsaWduZW51bQAAAA9FU2xpY2VWZXJ0QWxpZ24AAAAHZGVmYXVsdAAAAAtiZ0Nvb\n"
"G9yVHlwZWVudW0AAAARRVNsaWNlQkdDb2xvclR5cGUAAAAATm9uZQAAAAl0b3BPdXRzZXRsb25n\n"
"AAAAAAAAAApsZWZ0T3V0c2V0bG9uZwAAAAAAAAAMYm90dG9tT3V0c2V0bG9uZwAAAAAAAAALcml\n"
"naHRPdXRzZXRsb25nAAAAAAA4QklNBBEAAAAAAAEBADhCSU0EFAAAAAAABAAAAAE4QklNBAwAAA\n"
"AACfkAAAABAAAAZAAAAGQAAAEsAAB1MAAACd0AGAAB/9j/4AAQSkZJRgABAgEASABIAAD/7QAMQ\n"
"WRvYmVfQ00AAv/uAA5BZG9iZQBkgAAAAAH/2wCEAAwICAgJCAwJCQwRCwoLERUPDAwPFRgTExUT\n"
"ExgRDAwMDAwMEQwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwBDQsLDQ4NEA4OEBQODg4UFA4\n"
"ODg4UEQwMDAwMEREMDAwMDAwRDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDP/AABEIAGQAZA\n"
"MBIgACEQEDEQH/3QAEAAf/xAE/AAABBQEBAQEBAQAAAAAAAAADAAECBAUGBwgJCgsBAAEFAQEBA\n"
"QEBAAAAAAAAAAEAAgMEBQYHCAkKCxAAAQQBAwIEAgUHBggFAwwzAQACEQMEIRIxBUFRYRMicYEy\n"
"BhSRobFCIyQVUsFiMzRygtFDByWSU/Dh8WNzNRaisoMmRJNUZEXCo3Q2F9JV4mXys4TD03Xj80Y\n"
"nlKSFtJXE1OT0pbXF1eX1VmZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3EQACAgECBAQDBAUGBwcGBT\n"
"UBAAIRAyExEgRBUWFxIhMFMoGRFKGxQiPBUtHwMyRi4XKCkkNTFWNzNPElBhaisoMHJjXC0kSTV\n"
"KMXZEVVNnRl4vKzhMPTdePzRpSkhbSVxNTk9KW1xdXl9VZmdoaWprbG1ub2JzdHV2d3h5ent8f/\n"
"2gAMAwEAAhEDEQA/APLtso1NRc0vP0Rok8NYyPEfijOG2ljBoAJPxKFppZtbS4Rz38kV+OPRDge\n"
"T89EPHBfvLjtb3P8A30K/j47cgsrYNxGpPYJpK8RtyXUlvPfsobV0GV0uippLiX3EaMb2/rKgMB\n"
"1ghoiNST4BESCjjLmxqmKtvxiXQ0cd0q8E2bjIDWjk9z5I8QW8JaoHcdkUePZJtZD9p8YU/Rsc/\n"
"wBNjS5zjDWjUk+SSKYaJLYq+qWeYGQ5lBPLJ3OA8wz2/wDSWni/U3H2AXW2l2oloa0f9LcjSLeU\n"
"hJdb/wAyqd387Zt+DZ5SSpVh/9DzO6dw7gGPuVn6ft/kyPkqwlxjw1Rnh24QNWjUeR5TSuDc6bg\n"
"fatpsJZQ3sNC4rWfkVYpbi4LAb3aANEkFLp7GHGYxuhAj4K/hYVNDjYGzZ++eSSoSbLZjGgwxul\n"
"XNrPqO35FukdmzyXOQeqtqwqRg4o/SOAN9ng3/AMzW02txZ9I+ZHKr241UOcWDaz3uLtSSPEpWu\n"
"rR5XPeylmyNr4BIPPCyH2Oc6T8kXNvddkPe/VzjJPxQAJMKeIoNScrPk2MbfddXUNXvcGtPx0Xb\n"
"dJ6NXjOD2Dfdw6w9v5LFW+q/1WLA3Ly9LSJaz91p/wDRjl2lOLWwAMbEJErWjRgESYieVdZhsMF\n"
"wMt08ldrx/vVivHaOdSgCoud9krmElpba93ASTlr/AP/R83ohr97voiJV/Fq9QvsI+mdPgs1thc\n"
"BWO5C38CoOY1g78qOejLiGvknxLAyGtExp5K9uzGt9RrNw7DhRfQKKx6bZIGgPj4rPycLqWVtIs\n"
"JGu5skDyTBRZtQNrb1fU8xrtpBaO4MLQxcx1sNuEjt5rMGJR9noY5hF7Wxa8aAnxVvDb6bgHH2z\n"
"omk0e64ajUUXnev9Idi5rrWAux7SXNd4E/muS+rHSjm9VbPtZjj1CSJBI+g3+0uh69b+iDG/QcD\n"
"u0nQCeFP6l0MZhWX/AJ1xM+QafY1TQlY1a+WABsdXp8Sp27aBH+vZaVbC0ADlVcASwtdOolp8Ct\n"
"BjmtGv0uI8EmJmxkIjWkmPEKLSPiidxIgJKRbDPCSN5pJyH//S87uw/suZ6c72iC13kVs9PdDmk\n"
"KllVziV3cuafc7yP0QjYFh26cqM6hsxAjIj6u6xzbDHh3R663AaceH+5BwdruVp2PqZUA0a9yo6\n"
"DPQajscnXb8YQdzC8H909joiZttoxoBIa4gGOQ3uqh+z1RuD2Ds4j2n+39FNKaFMevS/p5LPpSA\n"
"I8/b/ABW70THZXj11VjaIAIHgFl5VdD8AneDMaec6Lb6QAKmu7x+VSw2a3MdF/rF9YKeh4B9OHZ\n"
"lpAprPH8p7v5DFwrPrV9YDa645toLjMaFo8mtcEvrhkWZHXbg7T0Q2to8o3f8AfkarEitlVTKnY\n"
"ra992QQ2wOfG57H2bg7H2fzbFKA130P6n9dHWemCx5H2mk7LgPH818f8IuhAka6ea8y/wAWrcod\n"
"VyrceRhBsPae5J/Qj+sxq9KDpMuMuKBCEntnny/1CSaWxM6pIKf/0/MvtF3pCrefTBnbOi1elP3\n"
"Et8Vi+Sv9LuNd4HwTSNGSEqkLerwtwn+SYKtOeS4A8Krh2j1D/K1Vu4B5gaDmVDJtAr7zYYGoRB\n"
"QDWQ4c9h/csuyjI3fobnDyJR8fF6ltcTaXRwCkAuAsfMGr1TGFNdTmEwLWS2dIK6npLgK2T4Lle\n"
"pUZxoc9+6K4eR5NO5bPT73NoqIfoILT/JcFJDZr8zGiNXnvrfiur6/Y8tht7WvaexgbXf8AUrFt\n"
"8IExyvRusYDOsYTAIbfWdzHRJ8HN/tLj7OgdRZawmreHP2gt9wEfvtH0f7SkDXe7+o+AOn9DquL\n"
"f0mV+leQPH6H+axafUvrB07ptJtyshtTZDTEudJ7bWS5V6MmyltVLn7ht2hwECQP+isb60/Vqvr\n"
"tbLsa1lObVIJd9Gxv5rXx9F7fzHpIbf/jgfVnd/TLYj6XoOhJcP/zE+sOzd6dW7dt2eo3dH7/9R\n"
"JJWj//U8uiGFx76BFZLQ2xvLeVGAWQrFDJbtKBSHd6blNura4H3BbDXB7InVcZXZdh2bmTt7hbO\n"
"J1dj2gzCjlFnhPod3WLHB+n3o9ZsAkFVMfMrs7orLmgkHUdkyqZQQWWQbLGlrjMjUeSrfV3Ltsw\n"
"30EBzcd5YCedvLETJya66nWOIAaCVnfU/KuZn21CDVa02PngQdHf9LapMfVhzkaAPUUW3M91YaR\n"
"3YDJ+WiBmZGazPo9Kttdt2j63E6s/fft/d/NWjXkMra7KtO2qkE6cErHpvsyMmzPu0dY4Bg/dYP\n"
"otTpyoaMUI2XUya8tzG/pi0NMtICo/bsut21gdcWclkj5OncxaDrw6kM+9QxQzaWRAGii4pDqzC\n"
"MT02aX7WzPU9b7PrG3bvO6P6yStfZm+pHnPySS4590+3jf/V8yb+CsUbp8uyz0kDskbu2dmz9J8\n"
"lSt9Ld+gn1O8cKikmxXydbH+3bhsmfwWj/lONYlcwkhL6L4bfpOxn/tD0/wBN/N944Wh9VJm/b/\n"
"O+347df+/rl0k+O38GLJ83X/CfTOt7v2dV6P8AMbx6njHb/pKuN3pN2+IXnaSjybr8e31fUqd+0\n"
"Sj487DHMryZJMXjq+sfpPX84SXk6SSX/9kAOEJJTQQhAAAAAABVAAAAAQEAAAAPAEEAZABvAGIA\n"
"ZQAgAFAAaABvAHQAbwBzAGgAbwBwAAAAEwBBAGQAbwBiAGUAIABQAGgAbwB0AG8AcwBoAG8AcAA\n"
"gADcALgAwAAAAAQA4QklNBAYAAAAAAAcABQAAAAEBAP/hFWdodHRwOi8vbnMuYWRvYmUuY29tL3\n"
"hhcC8xLjAvADw/eHBhY2tldCBiZWdpbj0n77u/JyBpZD0nVzVNME1wQ2VoaUh6cmVTek5UY3prY\n"
"zlkJz8+Cjw/YWRvYmUteGFwLWZpbHRlcnMgZXNjPSJDUiI/Pgo8eDp4YXBtZXRhIHhtbG5zOng9\n"
"J2Fkb2JlOm5zOm1ldGEvJyB4OnhhcHRrPSdYTVAgdG9vbGtpdCAyLjguMi0zMywgZnJhbWV3b3J\n"
"rIDEuNSc+CjxyZGY6UkRGIHhtbG5zOnJkZj0naHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi\n"
"1yZGYtc3ludGF4LW5zIycgeG1sbnM6aVg9J2h0dHA6Ly9ucy5hZG9iZS5jb20vaVgvMS4wLyc+C\n"
"gogPHJkZjpEZXNjcmlwdGlvbiBhYm91dD0ndXVpZDoyMmQwMmIwYS1iMjQ5LTExZGItOGFmOC05\n"
"MWQ1NDAzZjkyZjknCiAgeG1sbnM6cGRmPSdodHRwOi8vbnMuYWRvYmUuY29tL3BkZi8xLjMvJz4\n"
"KICA8IS0tIHBkZjpTdWJqZWN0IGlzIGFsaWFzZWQgLS0+CiA8L3JkZjpEZXNjcmlwdGlvbj4KCi\n"
"A8cmRmOkRlc2NyaXB0aW9uIGFib3V0PSd1dWlkOjIyZDAyYjBhLWIyNDktMTFkYi04YWY4LTkxZ\n"
"DU0MDNmOTJmOScKICB4bWxuczpwaG90b3Nob3A9J2h0dHA6Ly9ucy5hZG9iZS5jb20vcGhvdG9z\n"
"aG9wLzEuMC8nPgogIDwhLS0gcGhvdG9zaG9wOkNhcHRpb24gaXMgYWxpYXNlZCAtLT4KIDwvcmR\n"
"mOkRlc2NyaXB0aW9uPgoKIDxyZGY6RGVzY3JpcHRpb24gYWJvdXQ9J3V1aWQ6MjJkMDJiMGEtYj\n"
"I0OS0xMWRiLThhZjgtOTFkNTQwM2Y5MmY5JwogIHhtbG5zOnhhcD0naHR0cDovL25zLmFkb2JlL\n"
"mNvbS94YXAvMS4wLyc+CiAgPCEtLSB4YXA6RGVzY3JpcHRpb24gaXMgYWxpYXNlZCAtLT4KIDwv\n"
"cmRmOkRlc2NyaXB0aW9uPgoKIDxyZGY6RGVzY3JpcHRpb24gYWJvdXQ9J3V1aWQ6MjJkMDJiMGE\n"
"tYjI0OS0xMWRiLThhZjgtOTFkNTQwM2Y5MmY5JwogIHhtbG5zOnhhcE1NPSdodHRwOi8vbnMuYW\n"
"RvYmUuY29tL3hhcC8xLjAvbW0vJz4KICA8eGFwTU06RG9jdW1lbnRJRD5hZG9iZTpkb2NpZDpwa\n"
"G90b3Nob3A6MjJkMDJiMDYtYjI0OS0xMWRiLThhZjgtOTFkNTQwM2Y5MmY5PC94YXBNTTpEb2N1\n"
"bWVudElEPgogPC9yZGY6RGVzY3JpcHRpb24+CgogPHJkZjpEZXNjcmlwdGlvbiBhYm91dD0ndXV\n"
"pZDoyMmQwMmIwYS1iMjQ5LTExZGItOGFmOC05MWQ1NDAzZjkyZjknCiAgeG1sbnM6ZGM9J2h0dH\n"
"A6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvJz4KICA8ZGM6ZGVzY3JpcHRpb24+CiAgIDxyZ\n"
"GY6QWx0PgogICAgPHJkZjpsaSB4bWw6bGFuZz0neC1kZWZhdWx0Jz4gICAgICAgICAgICAgICAg\n"
"ICAgICAgICAgICAgICAgPC9yZGY6bGk+CiAgIDwvcmRmOkFsdD4KICA8L2RjOmRlc2NyaXB0aW9\n"
"uPgogPC9yZGY6RGVzY3JpcHRpb24+Cgo8L3JkZjpSREY+CjwveDp4YXBtZXRhPgogICAgICAgIC\n"
"AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI\n"
"CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAg\n"
"ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA\n"
"gICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC\n"
"AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI\n"
"CAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg\n"
"ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA\n"
"gCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC\n"
"AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgI\n"
"CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg\n"
"ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICA\n"
"gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC\n"
"AgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI\n"
"CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg\n"
"ICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA\n"
"gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIA\n"
"ogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI\n"
"CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAg\n"
"ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA\n"
"gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgIC\n"
"AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI\n"
"CAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg\n"
"ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA\n"
"gICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC\n"
"AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKI\n"
"CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg\n"
"ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICA\n"
"gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC\n"
"AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgI\n"
"CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg\n"
"ICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA\n"
"gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC\n"
"AgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI\n"
"CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAg\n"
"ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA\n"
"gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgIC\n"
"AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI\n"
"CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAg\n"
"ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA\n"
"gICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC\n"
"AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI\n"
"CAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg\n"
"ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICA\n"
"gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC\n"
"AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgI\n"
"CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg\n"
"ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICA\n"
"gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC\n"
"AgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI\n"
"CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg\n"
"ICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA\n"
"gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgIC\n"
"AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI\n"
"CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAg\n"
"ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA\n"
"gICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIC\n"
"AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI\n"
"CAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg\n"
"ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA\n"
"gICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC\n"
"AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgI\n"
"CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg\n"
"ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICA\n"
"gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC\n"
"AgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgI\n"
"CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg\n"
"ICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA\n"
"gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC\n"
"AgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI\n"
"CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAg\n"
"ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA\n"
"gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgIC\n"
"AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKPD94cGFja2V0IGVuZD0ndyc/P\n"
"v/uAA5BZG9iZQBkQAAAAAH/2wCEAAQDAwMDAwQDAwQGBAMEBgcFBAQFBwgGBgcGBggKCAkJCQkI\n"
"CgoMDAwMDAoMDAwMDAwMDAwMDAwMDAwMDAwMDAwBBAUFCAcIDwoKDxQODg4UFA4ODg4UEQwMDAw\n"
"MEREMDAwMDAwRDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDP/AABEIAGQAZAMBEQACEQEDEQ\n"
"H/3QAEAA3/xAGiAAAABwEBAQEBAAAAAAAAAAAEBQMCBgEABwgJCgsBAAICAwEBAQEBAAAAAAAAA\n"
"AEAAgMEBQYHCAkKCxAAAgEDAwIEAgYHAwQCBgJzAQIDEQQABSESMUFRBhNhInGBFDKRoQcVsUIj\n"
"wVLR4TMWYvAkcoLxJUM0U5KismNzwjVEJ5OjszYXVGR0w9LiCCaDCQoYGYSURUaktFbTVSga8uP\n"
"zxNTk9GV1hZWltcXV5fVmdoaWprbG1ub2N0dXZ3eHl6e3x9fn9zhIWGh4iJiouMjY6PgpOUlZaX\n"
"mJmam5ydnp+So6SlpqeoqaqrrK2ur6EQACAgECAwUFBAUGBAgDA20BAAIRAwQhEjFBBVETYSIGc\n"
"YGRMqGx8BTB0eEjQhVSYnLxMyQ0Q4IWklMlomOywgdz0jXiRIMXVJMICQoYGSY2RRonZHRVN/Kj\n"
"s8MoKdPj84SUpLTE1OT0ZXWFlaW1xdXl9UZWZnaGlqa2xtbm9kdXZ3eHl6e3x9fn9zhIWGh4iJi\n"
"ouMjY6Pg5SVlpeYmZqbnJ2en5KjpKWmp6ipqqusra6vr/2gAMAwEAAhEDEQA/APBnplwPAdR+GB\n"
"KY6dYtNG1w39yh4+xb+zIksgEfFaRSSoIx8f7RPRRkSWQimM+lRmwWVXFWYigHxUUVoMiJM+Fj0\n"
"tg0RBegLE0Wu+3c+GTBazFCGI7HtSp9slbFYYzyoBsegw2hY1Afl3wqqRqahk+0tDgKpgu4DAUU\n"
"+HY+GRS2ePiMKtUB3G+KGuONq//Q8OzpFbW5WnxMop4k9crG5ZnZNJkEOn21utVRYw7HxZtz+OR\n"
"vdsrZ2lRtci4aVxFEQA0neg/ZXxJpTITNNuOFss0vSotYNvZ2qGRkPKSTqiU8Sdqk5SZU5Ix8XJ\n"
"NNZ8k6bp8TtM73OputUtYq0Unux/hkRkJOzZLCAN2KR+VpbtSkCBaDnIzdlWu59u+XeJTjeASk8\n"
"+juZOESEAVqx8BvU/PJibScTrTy09560hkWOGFd2YgFnPQKD19zhOSkxw2l8Vm6XAiYb8gg+k5O\n"
"9mnhoon9H3cs5s7WF5pp29OGGMFndyaAKBuTiEEPQLD8h/NDmNdYlttNkYjlbFjcXCr3LLH8II8\n"
"C2WUGviZvon/OPWkm3RNSv72SYllMkKxQRV67CQMSKYQAxMkR/wBC56d61P0heel4cYuVOXWvTp\n"
"h4Qjjf/9Hw5qBYyISaqjBV+QpvkAzKcki4HomnIxck/wBhtlR2bhunvlDywddMUl4zW+kQ9FQ8X\n"
"nfuSewrtmPkycPvc/DhMhvyegXOrWWhmLQPKlsj6xIAiLCoZkY96nv7npmJvI2XOjQFMl0fyRqM\n"
"NoxvZvrGt33wlATwiMnVnY1LEdSfuyXF3KIDmUu88w2XlnTl8raAlb2ZFfVL0jdYRtQnxc7BfDC\n"
"OaJR7nm3me5tdOtjbMvp3ZRXkV6chVQRX79hmVjgZG+jgZ5jHGhzecXF5LPL6jEjstSSaDM51Ka\n"
"6MZ9S1C0sEBe8uZo4YCBXdjxGw60wEWyEqfUHkT8vLXRJFuLdTcaqfhlvWUErtukZ3ABPUjIXTE\n"
"m3rGmeV2Tk5UKz/AG/E/wAcgZKya20C3b02kjYtH8AqCygbkUH0nLYlgUb+gbWtPbpXt/n2ybB/\n"
"/9Lw4oaVxGd+PxH3qBkGaY3KyiSP01IkiUclH8sg+LKydm6INvZvKsFu+kWtvD8LRoFNRup6moO\n"
"aqd277HsGW+XPLmn6XM17FF6l7vW4fd2Zuu+RFls2tmUNrLJb7TSBertGQGqetDkxE0na0pvtHs\n"
"QkszWyiGAG5laYlnkeMVHJj8sA5rPk+SvMepTalqlxd3B5zTOXdj/MxqafLpm5xioh5nPK5kpRG\n"
"pkcKAST0A6k5NpfUP5K/ki1ssHmHzF+71KRQ8Nud/Qibb/kYw6/yjbrXISlSH07YaHbWyxx2kXE\n"
"KACB2zHJtLI7XSelBRvH2xCpvaaTDHXkOTVBPcUG2479RlsdmJVPRtvV+ylenQ0y62FP/9PxRpo\n"
"WG5FxKKxKFDA+GVS5NsebLdFsRePc3siVW4f4QR0QVAGYeSXR2unhtZ6s60K6jt+MMSFwtF2+xX\n"
"wr7eGUGLlRPQMsE2vxQm7itxKg3VCfT2+nb8cDYaCDtfOXmCCcROrQrUhkkCHYn6emRMqZxjbLd\n"
"F1+W/4xajHzjNCtQKMffETWUdngX5p+QZ9A8xS6hbo0ui37NNDPT7DOalHpsCD08Rmyw5ARTpdV\n"
"gIPEF35MeRn80ed4S5EdrpKm9kZ15K0iH92hB7Me/tmS60vt/QrCYyekiBdgSTXcjqV9q9MokFD\n"
"N7S3aFVVR8RoK9zldqndvAY6nffr/AGYQqLhjdpCoIAZW22HavU/LJBUP9WblX0xTw7fOmWsX/9\n"
"Tw7FdvMqWkQ3Z1qfED+mQIbI77PX/LFis9vBajZm2Y+x65rMh3t30Bsze400aVaIbSLk6r8CMRT\n"
"l/NmOcllnGDD9Y8uecNfEEiXrMgDGWAyGOOu5WlB+vMrHODTlxZCdjsyFdB006VpVtLasurQxBL\n"
"64WiLI4/aFT1ANOXemV5piR2b9NiljB4yyHy9CLOVI5GJhB+CvXY9R8xmINzs5HNZ+Z96BZpbxA\n"
"fVJo39UFefwopYgL4nMiMd2qZoIn/AJx00u3t/Lt7qpp9Yv5GLf5MUTERqfbvmzBeezjd9H+VlL\n"
"wSQzBqsvOGQD7L12rXsemPNxmXQSxxIPU2nFV4HYqR1xEUWj4ZAxBryr2G+J2VGDZlLrxUH6KZA\n"
"Fkqb15VFelfwy+2FP8A/9Xxlf6AdA182Yk9eFeLxSjoVfcfSMo4uIOfkweFOnpvlWYrLEwNFAA+\n"
"nMOYdrhFvQLeSO7coBXiK8iKiv07Zj8Ac4QtNrW1njUcKcT+yAR/xGmR4WcsStLpTuPU9IFaEsV\n"
"BP3k4m2AgBzSwyQNcIwNTE1aI3wnam9O2Ug7s5Ckk/NDndeVXa2H78MqqV6jmeBp9+ZWKXqDjZ4\n"
"+gvVvy30qCy0qzsLRBCnBI2VdgUTqPvOZ7y+Q7pz+bn5q6d+VflZxZlJ/NN4ypptk5qtB9qRwDX\n"
"gn/AAx2y2ItpfKFv+eH5qNeTajJ5ovVaVywSqvEtTUKqupAA6D2y0BNPtv/AJx//M5PzL8mJeXT\n"
"L+ndPf6rqarSpkAqsnEAAeoN6DpkJRYci9lROSgSUUH9o9K5Tw0ztfSHnXkOtK9q+PHwydq//9b\n"
"yxrVoZNBtNSA5zRMPXmH8j0CLXuBmHE+qneamHpEuqYeV7pzFVTRgQK5XMNmnlb1vyyY5QA1OwJ\n"
"+eUF2seTOLu5s7azVIVAkpVn/hhnIALG73Yz5jvb1dICqzpDNIqyFD8SxH7R28cxibZCiWOsdJs\n"
"PTM6XNstPhnkjIhcHuJBVfvOCiUSn0TfWrTTLjyw8guA/PifTO3xcxxA8a5ZAbimvJP0m3p/kFF\n"
"WxhmpWQJ9NW3zZPHz5vlb/nIDVbrWfzO1RJhxGnpDaRL/khA1T7ktmSOTAJhZaAUtLawsbayl8v\n"
"xWi3Gpay0cF3HPcFRJJHJMXVrcJ8UaAFG5LWjF8tAYW9H/wCcOo9bTzxrt/owkTyksZW5gkIKvI\n"
"7k26nvyReRJHyyBWT7dWQyOWlbnK2526e1O1MqIUFE84uPLkOdK9RXI0E2/wD/1/DA1bURZLY/W\n"
"ZDZqwb0eXw7dMgIi7bjllVXsz7yNcfWC0Vd3Ip92Y2UOz0cnsPlwyx8xQ/u24sMxCadoJp9LOXk\n"
"VX/uwRUE0BI8cokbLMyoKouHu2MaKGXw7fLDwgoGSkbHpaNZyLLHRSKcFFQQRvUdMlwUFOQyLzr\n"
"ztpCaba6fPau4ijv4OURY8AjVFKV7ZZiO+7Vnh6XvXkSWNbW2WTb92KDxIFMzwHlZc3zX+fuizW\n"
"f5p3ty8XGDU4YLmCQiisyII3+4rvl8UB5ffEghRGvOm7AbnvWvjk1fen/ONPldPKP5aWOpPCfr2\n"
"uE31y6q2wbaMEn+VAMDSdyzrzj+avlHyTp0l/r2rxWFuHWJuIeacu4qFCRgsajfBwsty89/6Gr/\n"
"ACa9an+JL/hSnrfoubhXwpXpjwhaL//Q8E1AqtcAZMs8l6i1nqMa1oSVP0VynKLDmaWdSfQXl69\n"
"jF1Jv8MhDb5rpB3AO7INRRLhhGp4R05FgaGvTMU8200xS70zVDMRp2pTIOvBmB3PgQP15kxIcnD\n"
"LH/EEz0rRvOJhldr9pQtCqyd6VrShGTqw5d4ARv9jHfOGl+ZJNMluLkyenaFbiRdqFYW5nrWuwO\n"
"MKB5MdSMRxnhlu9N8p6lLFpti63FUjCtFJTrDKvse2bEDZ4XJ9RZB+YPli2/Mjy5bxoUi1a0YS2\n"
"85UOwIXiy9jRu+TBppfOF1+V3m22vrdpNPM8cs/oo0VJlUqQPjValR3+IZNNvtLS9Yu9Mi0/TJr\n"
"kyp6QhWVVCIWRATsKBemwwFrDzT87fybs/wA1bW21PRb+DTvNlgGSRp6iC8i3KJJx+y6n7D0Pwm\n"
"hxBZXT55/6Fi/Nf0PW+qWXq+t6X1X67F6vD/ftK04V/wBl344U8b//0fBapxheVh9ocV+nviqY2\n"
"/qQJDew/bioWHiuQ8m0bbvaPKGtQ6jaxSo9JloCK75gZI0Xb4sgkHo8MouoAvP94BsRmGY7uWJU\n"
"gzbypOQpNOvIdK4Nw2WCE2tXulTkjEEbdafgclxMhFBas93dwyQzsWDghlJFONKHJCZtjOFBJfy\n"
"j1y9vPL9zpbIs0WkXL2sUjA8hDXlGCRXtt07ZuYvL5KJeo6bfajbkzWkcToR8dqshZ6in2fhNK/\n"
"PDTUlXmHVvMdr5o0v9H2kdrqGpfu7m0nkY87Uf7tkKAU4/s03ynLkEBbfihx7dGT6va67LbRMNR\n"
"aKOBuUTKgIBXoK1BOYR1M3aQ0mOt9yxUeZNdtJhFapLqMluSXkg5oxJrUMW5KevQ9MmNXXNqOiH\n"
"Rr/Hmv8A1r9I/oj95w+r+j9Yf1+NP5+nXtTD+dF8tkfkOlv/0vC3ph7f0/alcVTbS4A8QibuKb5\n"
"RI05EBYRFpdX3ly79a2qYCavH/EY7TCYyMD5PSdD8+wXUSn1ArDqOhBzFlipz4ZwWbaV5htbsgF\n"
"qg9crMXKErGyYwajFGzxyHlGSePbbwyqg5UZlCaxrFpaWU95LIqrEjMAT4Dp9OShGy1ZslBhv/A\n"
"Dj9rd/a+aL+xUK+m38L3d0HrxRo2HFtu5D8c27y8t30raarbWkU+u6g4gsNORn+EcUaSh2Pc0/4\n"
"lgtAjezzbT9SutY1i782al8Nxdyotqh6xWybIg+jc5q8s+I27bFDgFPQp9RE+nrag70+L6crrZu\n"
"4jajokdv6LW/Dii1Wo61PXKQN3KPK0L+h4/rnD/K5V78a5LhXxd3/0/DMXXtwxVNtL9Xkaf3f7N\n"
"etfbKMjdjtkZ9D6ufrlK0+HpX8coF9HJ26sXvfqXrf7i/U+uften/d/wCyrmQL6uOav0pvpP8Ai\n"
"b1F+rV59+vH6a5XLhcjH4nRmY/xpxHP0/UptWvT6Mx/RbmjxWK+aP8AFf1M/pCv1Kvxen9inavf\n"
"MrFwXtzcLUeLXq5Mv/I3nz1b0v8AjofuKVry9KrUpTanOlf9jmQ68va/zH9b/COn/o7/AI431mP\n"
"65SvLh+zWvbl9rMfNfC34K4kmj9T6lD6FKclp/DNYXZx5srsPrHor6nXvkgxTPS/U+rv6dPU5mt\n"
"fngFN5ulv+l/pL/Lp/scerHo//2Q==\n";
static std::string gCommandLine;
TEST(Base64, LargeSample) {
LOG(LS_VERBOSE) << "Testing specific base64 file";
char unescaped[64 * 1024];
// unescape that massive blob above
size_t size = Base64Unescape(SpecificTest,
sizeof(SpecificTest),
unescaped,
sizeof(unescaped));
EXPECT_EQ(size, sizeof(testbase64));
EXPECT_EQ(0, memcmp(testbase64, unescaped, sizeof(testbase64)));
}
bool DecodeTest(const char* encoded, size_t expect_unparsed,
const char* decoded, Base64::DecodeFlags flags)
{
std::string result;
size_t consumed = 0, encoded_len = strlen(encoded);
bool success = Base64::DecodeFromArray(encoded, encoded_len, flags,
&result, &consumed);
size_t unparsed = encoded_len - consumed;
EXPECT_EQ(expect_unparsed, unparsed) << "\"" << encoded
<< "\" -> \"" << decoded
<< "\"";
EXPECT_STREQ(decoded, result.c_str());
return success;
}
#define Flags(x,y,z) \
Base64::DO_PARSE_##x | Base64::DO_PAD_##y | Base64::DO_TERM_##z
TEST(Base64, DecodeParseOptions) {
// Trailing whitespace
EXPECT_TRUE (DecodeTest("YWJjZA== ", 1, "abcd", Flags(STRICT, YES, CHAR)));
EXPECT_TRUE (DecodeTest("YWJjZA== ", 0, "abcd", Flags(WHITE, YES, CHAR)));
EXPECT_TRUE (DecodeTest("YWJjZA== ", 0, "abcd", Flags(ANY, YES, CHAR)));
// Embedded whitespace
EXPECT_FALSE(DecodeTest("YWJjZA= =", 3, "abcd", Flags(STRICT, YES, CHAR)));
EXPECT_TRUE (DecodeTest("YWJjZA= =", 0, "abcd", Flags(WHITE, YES, CHAR)));
EXPECT_TRUE (DecodeTest("YWJjZA= =", 0, "abcd", Flags(ANY, YES, CHAR)));
// Embedded non-base64 characters
EXPECT_FALSE(DecodeTest("YWJjZA=*=", 3, "abcd", Flags(STRICT, YES, CHAR)));
EXPECT_FALSE(DecodeTest("YWJjZA=*=", 3, "abcd", Flags(WHITE, YES, CHAR)));
EXPECT_TRUE (DecodeTest("YWJjZA=*=", 0, "abcd", Flags(ANY, YES, CHAR)));
// Unexpected padding characters
EXPECT_FALSE(DecodeTest("YW=JjZA==", 7, "a", Flags(STRICT, YES, CHAR)));
EXPECT_FALSE(DecodeTest("YW=JjZA==", 7, "a", Flags(WHITE, YES, CHAR)));
EXPECT_TRUE (DecodeTest("YW=JjZA==", 0, "abcd", Flags(ANY, YES, CHAR)));
}
TEST(Base64, DecodePadOptions) {
// Padding
EXPECT_TRUE (DecodeTest("YWJjZA==", 0, "abcd", Flags(STRICT, YES, CHAR)));
EXPECT_TRUE (DecodeTest("YWJjZA==", 0, "abcd", Flags(STRICT, ANY, CHAR)));
EXPECT_TRUE (DecodeTest("YWJjZA==", 2, "abcd", Flags(STRICT, NO, CHAR)));
// Incomplete padding
EXPECT_FALSE(DecodeTest("YWJjZA=", 1, "abcd", Flags(STRICT, YES, CHAR)));
EXPECT_TRUE (DecodeTest("YWJjZA=", 1, "abcd", Flags(STRICT, ANY, CHAR)));
EXPECT_TRUE (DecodeTest("YWJjZA=", 1, "abcd", Flags(STRICT, NO, CHAR)));
// No padding
EXPECT_FALSE(DecodeTest("YWJjZA", 0, "abcd", Flags(STRICT, YES, CHAR)));
EXPECT_TRUE (DecodeTest("YWJjZA", 0, "abcd", Flags(STRICT, ANY, CHAR)));
EXPECT_TRUE (DecodeTest("YWJjZA", 0, "abcd", Flags(STRICT, NO, CHAR)));
}
TEST(Base64, DecodeTerminateOptions) {
// Complete quantum
EXPECT_TRUE (DecodeTest("YWJj", 0, "abc", Flags(STRICT, NO, BUFFER)));
EXPECT_TRUE (DecodeTest("YWJj", 0, "abc", Flags(STRICT, NO, CHAR)));
EXPECT_TRUE (DecodeTest("YWJj", 0, "abc", Flags(STRICT, NO, ANY)));
// Complete quantum with trailing data
EXPECT_FALSE(DecodeTest("YWJj*", 1, "abc", Flags(STRICT, NO, BUFFER)));
EXPECT_TRUE (DecodeTest("YWJj*", 1, "abc", Flags(STRICT, NO, CHAR)));
EXPECT_TRUE (DecodeTest("YWJj*", 1, "abc", Flags(STRICT, NO, ANY)));
// Incomplete quantum
EXPECT_FALSE(DecodeTest("YWJ", 0, "ab", Flags(STRICT, NO, BUFFER)));
EXPECT_FALSE(DecodeTest("YWJ", 0, "ab", Flags(STRICT, NO, CHAR)));
EXPECT_TRUE (DecodeTest("YWJ", 0, "ab", Flags(STRICT, NO, ANY)));
}
TEST(Base64, GetNextBase64Char) {
// The table looks like this:
// "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
char next_char;
EXPECT_TRUE(Base64::GetNextBase64Char('A', &next_char));
EXPECT_EQ('B', next_char);
EXPECT_TRUE(Base64::GetNextBase64Char('Z', &next_char));
EXPECT_EQ('a', next_char);
EXPECT_TRUE(Base64::GetNextBase64Char('/', &next_char));
EXPECT_EQ('A', next_char);
EXPECT_FALSE(Base64::GetNextBase64Char('&', &next_char));
EXPECT_FALSE(Base64::GetNextBase64Char('Z', nullptr));
}

View File

@ -0,0 +1,70 @@
/*
* 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_RTC_BASE_BASICTYPES_H_
#define WEBRTC_RTC_BASE_BASICTYPES_H_
#include <stddef.h> // for NULL, size_t
#include <stdint.h> // for uintptr_t and (u)int_t types.
// Detect compiler is for x86 or x64.
#if defined(__x86_64__) || defined(_M_X64) || \
defined(__i386__) || defined(_M_IX86)
#define CPU_X86 1
#endif
// Detect compiler is for arm.
#if defined(__arm__) || defined(_M_ARM)
#define CPU_ARM 1
#endif
#if defined(CPU_X86) && defined(CPU_ARM)
#error CPU_X86 and CPU_ARM both defined.
#endif
#if !defined(RTC_ARCH_CPU_BIG_ENDIAN) && !defined(RTC_ARCH_CPU_LITTLE_ENDIAN)
// x86, arm or GCC provided __BYTE_ORDER__ macros
#if defined(CPU_X86) || defined(CPU_ARM) || \
(defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
#define RTC_ARCH_CPU_LITTLE_ENDIAN
#elif defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
#define RTC_ARCH_CPU_BIG_ENDIAN
#else
#error RTC_ARCH_CPU_BIG_ENDIAN or RTC_ARCH_CPU_LITTLE_ENDIAN should be defined.
#endif
#endif
#if defined(RTC_ARCH_CPU_BIG_ENDIAN) && defined(RTC_ARCH_CPU_LITTLE_ENDIAN)
#error RTC_ARCH_CPU_BIG_ENDIAN and RTC_ARCH_CPU_LITTLE_ENDIAN both defined.
#endif
#if defined(WEBRTC_WIN)
typedef int socklen_t;
#endif
// The following only works for C++
#ifdef __cplusplus
#ifndef ALIGNP
#define ALIGNP(p, t) \
(reinterpret_cast<uint8_t*>(((reinterpret_cast<uintptr_t>(p) + \
((t) - 1)) & ~((t) - 1))))
#endif
#define RTC_IS_ALIGNED(p, a) (!((uintptr_t)(p) & ((a) - 1)))
// Use these to declare and define a static local variable that gets leaked so
// that its destructors are not called at exit.
#define RTC_DEFINE_STATIC_LOCAL(type, name, arguments) \
static type& name = *new type arguments
#endif // __cplusplus
#endif // WEBRTC_RTC_BASE_BASICTYPES_H_

View File

@ -0,0 +1,48 @@
/*
* Copyright 2012 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.
*/
#include "webrtc/base/basictypes.h"
#include "webrtc/base/gunit.h"
namespace rtc {
TEST(BasicTypesTest, Endian) {
uint16_t v16 = 0x1234u;
uint8_t first_byte = *reinterpret_cast<uint8_t*>(&v16);
#if defined(RTC_ARCH_CPU_LITTLE_ENDIAN)
EXPECT_EQ(0x34u, first_byte);
#elif defined(RTC_ARCH_CPU_BIG_ENDIAN)
EXPECT_EQ(0x12u, first_byte);
#endif
}
TEST(BasicTypesTest, SizeOfConstants) {
EXPECT_EQ(8u, sizeof(INT64_C(0)));
EXPECT_EQ(8u, sizeof(UINT64_C(0)));
EXPECT_EQ(8u, sizeof(INT64_C(0x1234567887654321)));
EXPECT_EQ(8u, sizeof(UINT64_C(0x8765432112345678)));
}
// Test CPU_ macros
#if !defined(CPU_ARM) && defined(__arm__)
#error expected CPU_ARM to be defined.
#endif
#if !defined(CPU_X86) && (defined(WEBRTC_WIN) || defined(WEBRTC_MAC) && !defined(WEBRTC_IOS))
#error expected CPU_X86 to be defined.
#endif
#if !defined(RTC_ARCH_CPU_LITTLE_ENDIAN) && \
(defined(WEBRTC_WIN) || defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) || defined(CPU_X86))
#error expected RTC_ARCH_CPU_LITTLE_ENDIAN to be defined.
#endif
// TODO(fbarchard): Test all macros in basictypes.h
} // namespace rtc

284
webrtc/rtc_base/bind.h Normal file
View File

@ -0,0 +1,284 @@
/*
* Copyright 2012 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.
*/
// Bind() is an overloaded function that converts method calls into function
// objects (aka functors). The method object is captured as a scoped_refptr<> if
// possible, and as a raw pointer otherwise. Any arguments to the method are
// captured by value. The return value of Bind is a stateful, nullary function
// object. Care should be taken about the lifetime of objects captured by
// Bind(); the returned functor knows nothing about the lifetime of a non
// ref-counted method object or any arguments passed by pointer, and calling the
// functor with a destroyed object will surely do bad things.
//
// To prevent the method object from being captured as a scoped_refptr<>, you
// can use Unretained. But this should only be done when absolutely necessary,
// and when the caller knows the extra reference isn't needed.
//
// Example usage:
// struct Foo {
// int Test1() { return 42; }
// int Test2() const { return 52; }
// int Test3(int x) { return x*x; }
// float Test4(int x, float y) { return x + y; }
// };
//
// int main() {
// Foo foo;
// cout << rtc::Bind(&Foo::Test1, &foo)() << endl;
// cout << rtc::Bind(&Foo::Test2, &foo)() << endl;
// cout << rtc::Bind(&Foo::Test3, &foo, 3)() << endl;
// cout << rtc::Bind(&Foo::Test4, &foo, 7, 8.5f)() << endl;
// }
//
// Example usage of ref counted objects:
// struct Bar {
// int AddRef();
// int Release();
//
// void Test() {}
// void BindThis() {
// // The functor passed to AsyncInvoke() will keep this object alive.
// invoker.AsyncInvoke(RTC_FROM_HERE,rtc::Bind(&Bar::Test, this));
// }
// };
//
// int main() {
// rtc::scoped_refptr<Bar> bar = new rtc::RefCountedObject<Bar>();
// auto functor = rtc::Bind(&Bar::Test, bar);
// bar = nullptr;
// // The functor stores an internal scoped_refptr<Bar>, so this is safe.
// functor();
// }
//
#ifndef WEBRTC_RTC_BASE_BIND_H_
#define WEBRTC_RTC_BASE_BIND_H_
#include <tuple>
#include <type_traits>
#include "webrtc/base/scoped_ref_ptr.h"
#include "webrtc/base/template_util.h"
#define NONAME
namespace rtc {
namespace detail {
// This is needed because the template parameters in Bind can't be resolved
// if they're used both as parameters of the function pointer type and as
// parameters to Bind itself: the function pointer parameters are exact
// matches to the function prototype, but the parameters to bind have
// references stripped. This trick allows the compiler to dictate the Bind
// parameter types rather than deduce them.
template <class T> struct identity { typedef T type; };
// IsRefCounted<T>::value will be true for types that can be used in
// rtc::scoped_refptr<T>, i.e. types that implements nullary functions AddRef()
// and Release(), regardless of their return types. AddRef() and Release() can
// be defined in T or any superclass of T.
template <typename T>
class IsRefCounted {
// This is a complex implementation detail done with SFINAE.
// Define types such that sizeof(Yes) != sizeof(No).
struct Yes { char dummy[1]; };
struct No { char dummy[2]; };
// Define two overloaded template functions with return types of different
// size. This way, we can use sizeof() on the return type to determine which
// function the compiler would have chosen. One function will be preferred
// over the other if it is possible to create it without compiler errors,
// otherwise the compiler will simply remove it, and default to the less
// preferred function.
template <typename R>
static Yes test(R* r, decltype(r->AddRef(), r->Release(), 42));
template <typename C> static No test(...);
public:
// Trick the compiler to tell if it's possible to call AddRef() and Release().
static const bool value = sizeof(test<T>((T*)nullptr, 42)) == sizeof(Yes);
};
// TernaryTypeOperator is a helper class to select a type based on a static bool
// value.
template <bool condition, typename IfTrueT, typename IfFalseT>
struct TernaryTypeOperator {};
template <typename IfTrueT, typename IfFalseT>
struct TernaryTypeOperator<true, IfTrueT, IfFalseT> {
typedef IfTrueT type;
};
template <typename IfTrueT, typename IfFalseT>
struct TernaryTypeOperator<false, IfTrueT, IfFalseT> {
typedef IfFalseT type;
};
// PointerType<T>::type will be scoped_refptr<T> for ref counted types, and T*
// otherwise.
template <class T>
struct PointerType {
typedef typename TernaryTypeOperator<IsRefCounted<T>::value,
scoped_refptr<T>,
T*>::type type;
};
template <typename T>
class UnretainedWrapper {
public:
explicit UnretainedWrapper(T* o) : ptr_(o) {}
T* get() const { return ptr_; }
private:
T* ptr_;
};
} // namespace detail
template <typename T>
static inline detail::UnretainedWrapper<T> Unretained(T* o) {
return detail::UnretainedWrapper<T>(o);
}
template <class ObjectT, class MethodT, class R, typename... Args>
class MethodFunctor {
public:
MethodFunctor(MethodT method, ObjectT* object, Args... args)
: method_(method), object_(object), args_(args...) {}
R operator()() const {
return CallMethod(typename sequence_generator<sizeof...(Args)>::type());
}
private:
// Use sequence_generator (see template_util.h) to expand a MethodFunctor
// with 2 arguments to (std::get<0>(args_), std::get<1>(args_)), for
// instance.
template <int... S>
R CallMethod(sequence<S...>) const {
return (object_->*method_)(std::get<S>(args_)...);
}
MethodT method_;
typename detail::PointerType<ObjectT>::type object_;
typename std::tuple<typename std::remove_reference<Args>::type...> args_;
};
template <class ObjectT, class MethodT, class R, typename... Args>
class UnretainedMethodFunctor {
public:
UnretainedMethodFunctor(MethodT method,
detail::UnretainedWrapper<ObjectT> object,
Args... args)
: method_(method), object_(object.get()), args_(args...) {}
R operator()() const {
return CallMethod(typename sequence_generator<sizeof...(Args)>::type());
}
private:
// Use sequence_generator (see template_util.h) to expand an
// UnretainedMethodFunctor with 2 arguments to (std::get<0>(args_),
// std::get<1>(args_)), for instance.
template <int... S>
R CallMethod(sequence<S...>) const {
return (object_->*method_)(std::get<S>(args_)...);
}
MethodT method_;
ObjectT* object_;
typename std::tuple<typename std::remove_reference<Args>::type...> args_;
};
template <class FunctorT, class R, typename... Args>
class Functor {
public:
Functor(const FunctorT& functor, Args... args)
: functor_(functor), args_(args...) {}
R operator()() const {
return CallFunction(typename sequence_generator<sizeof...(Args)>::type());
}
private:
// Use sequence_generator (see template_util.h) to expand a Functor
// with 2 arguments to (std::get<0>(args_), std::get<1>(args_)), for
// instance.
template <int... S>
R CallFunction(sequence<S...>) const {
return functor_(std::get<S>(args_)...);
}
FunctorT functor_;
typename std::tuple<typename std::remove_reference<Args>::type...> args_;
};
#define FP_T(x) R (ObjectT::*x)(Args...)
template <class ObjectT, class R, typename... Args>
MethodFunctor<ObjectT, FP_T(NONAME), R, Args...> Bind(
FP_T(method),
ObjectT* object,
typename detail::identity<Args>::type... args) {
return MethodFunctor<ObjectT, FP_T(NONAME), R, Args...>(method, object,
args...);
}
template <class ObjectT, class R, typename... Args>
MethodFunctor<ObjectT, FP_T(NONAME), R, Args...> Bind(
FP_T(method),
const scoped_refptr<ObjectT>& object,
typename detail::identity<Args>::type... args) {
return MethodFunctor<ObjectT, FP_T(NONAME), R, Args...>(method, object.get(),
args...);
}
template <class ObjectT, class R, typename... Args>
UnretainedMethodFunctor<ObjectT, FP_T(NONAME), R, Args...> Bind(
FP_T(method),
detail::UnretainedWrapper<ObjectT> object,
typename detail::identity<Args>::type... args) {
return UnretainedMethodFunctor<ObjectT, FP_T(NONAME), R, Args...>(
method, object, args...);
}
#undef FP_T
#define FP_T(x) R (ObjectT::*x)(Args...) const
template <class ObjectT, class R, typename... Args>
MethodFunctor<const ObjectT, FP_T(NONAME), R, Args...> Bind(
FP_T(method),
const ObjectT* object,
typename detail::identity<Args>::type... args) {
return MethodFunctor<const ObjectT, FP_T(NONAME), R, Args...>(method, object,
args...);
}
template <class ObjectT, class R, typename... Args>
UnretainedMethodFunctor<const ObjectT, FP_T(NONAME), R, Args...> Bind(
FP_T(method),
detail::UnretainedWrapper<const ObjectT> object,
typename detail::identity<Args>::type... args) {
return UnretainedMethodFunctor<const ObjectT, FP_T(NONAME), R, Args...>(
method, object, args...);
}
#undef FP_T
#define FP_T(x) R (*x)(Args...)
template <class R, typename... Args>
Functor<FP_T(NONAME), R, Args...> Bind(
FP_T(function),
typename detail::identity<Args>::type... args) {
return Functor<FP_T(NONAME), R, Args...>(function, args...);
}
#undef FP_T
} // namespace rtc
#undef NONAME
#endif // WEBRTC_RTC_BASE_BIND_H_

View File

@ -0,0 +1,222 @@
/*
* 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.
*/
#include <type_traits>
#include "webrtc/base/bind.h"
#include "webrtc/base/gunit.h"
#include "webrtc/base/refcount.h"
namespace rtc {
namespace {
struct LifeTimeCheck;
struct MethodBindTester {
void NullaryVoid() { ++call_count; }
int NullaryInt() { ++call_count; return 1; }
int NullaryConst() const { ++call_count; return 2; }
void UnaryVoid(int dummy) { ++call_count; }
template <class T> T Identity(T value) { ++call_count; return value; }
int UnaryByPointer(int* value) const {
++call_count;
return ++(*value);
}
int UnaryByRef(const int& value) const {
++call_count;
return ++const_cast<int&>(value);
}
int Multiply(int a, int b) const { ++call_count; return a * b; }
void RefArgument(const scoped_refptr<LifeTimeCheck>& object) {
EXPECT_TRUE(object.get() != nullptr);
}
mutable int call_count;
};
struct A { int dummy; };
struct B: public RefCountInterface { int dummy; };
struct C: public A, B {};
struct D {
int AddRef();
};
struct E: public D {
int Release();
};
struct F {
void AddRef();
void Release();
};
struct LifeTimeCheck {
LifeTimeCheck() : ref_count_(0) {}
void AddRef() { ++ref_count_; }
void Release() { --ref_count_; }
void NullaryVoid() {}
int ref_count_;
};
int Return42() { return 42; }
int Negate(int a) { return -a; }
int Multiply(int a, int b) { return a * b; }
} // namespace
// Try to catch any problem with scoped_refptr type deduction in rtc::Bind at
// compile time.
#define EXPECT_IS_CAPTURED_AS_PTR(T) \
static_assert(is_same<detail::PointerType<T>::type, T*>::value, \
"PointerType")
#define EXPECT_IS_CAPTURED_AS_SCOPED_REFPTR(T) \
static_assert( \
is_same<detail::PointerType<T>::type, scoped_refptr<T>>::value, \
"PointerType")
EXPECT_IS_CAPTURED_AS_PTR(void);
EXPECT_IS_CAPTURED_AS_PTR(int);
EXPECT_IS_CAPTURED_AS_PTR(double);
EXPECT_IS_CAPTURED_AS_PTR(A);
EXPECT_IS_CAPTURED_AS_PTR(D);
EXPECT_IS_CAPTURED_AS_PTR(RefCountInterface*);
EXPECT_IS_CAPTURED_AS_PTR(
decltype(Unretained<RefCountedObject<RefCountInterface>>));
EXPECT_IS_CAPTURED_AS_SCOPED_REFPTR(RefCountInterface);
EXPECT_IS_CAPTURED_AS_SCOPED_REFPTR(B);
EXPECT_IS_CAPTURED_AS_SCOPED_REFPTR(C);
EXPECT_IS_CAPTURED_AS_SCOPED_REFPTR(E);
EXPECT_IS_CAPTURED_AS_SCOPED_REFPTR(F);
EXPECT_IS_CAPTURED_AS_SCOPED_REFPTR(RefCountedObject<RefCountInterface>);
EXPECT_IS_CAPTURED_AS_SCOPED_REFPTR(RefCountedObject<B>);
EXPECT_IS_CAPTURED_AS_SCOPED_REFPTR(RefCountedObject<C>);
EXPECT_IS_CAPTURED_AS_SCOPED_REFPTR(const RefCountedObject<RefCountInterface>);
TEST(BindTest, BindToMethod) {
MethodBindTester object = {0};
EXPECT_EQ(0, object.call_count);
Bind(&MethodBindTester::NullaryVoid, &object)();
EXPECT_EQ(1, object.call_count);
EXPECT_EQ(1, Bind(&MethodBindTester::NullaryInt, &object)());
EXPECT_EQ(2, object.call_count);
EXPECT_EQ(2, Bind(&MethodBindTester::NullaryConst,
static_cast<const MethodBindTester*>(&object))());
EXPECT_EQ(3, object.call_count);
Bind(&MethodBindTester::UnaryVoid, &object, 5)();
EXPECT_EQ(4, object.call_count);
EXPECT_EQ(100, Bind(&MethodBindTester::Identity<int>, &object, 100)());
EXPECT_EQ(5, object.call_count);
const std::string string_value("test string");
EXPECT_EQ(string_value, Bind(&MethodBindTester::Identity<std::string>,
&object, string_value)());
EXPECT_EQ(6, object.call_count);
int value = 11;
// Bind binds by value, even if the method signature is by reference, so
// "reference" binds require pointers.
EXPECT_EQ(12, Bind(&MethodBindTester::UnaryByPointer, &object, &value)());
EXPECT_EQ(12, value);
EXPECT_EQ(7, object.call_count);
// It's possible to bind to a function that takes a const reference, though
// the capture will be a copy. See UnaryByRef hackery above where it removes
// the const to make sure the underlying storage is, in fact, a copy.
EXPECT_EQ(13, Bind(&MethodBindTester::UnaryByRef, &object, value)());
// But the original value is unmodified.
EXPECT_EQ(12, value);
EXPECT_EQ(8, object.call_count);
EXPECT_EQ(56, Bind(&MethodBindTester::Multiply, &object, 7, 8)());
EXPECT_EQ(9, object.call_count);
}
TEST(BindTest, BindToFunction) {
EXPECT_EQ(42, Bind(&Return42)());
EXPECT_EQ(3, Bind(&Negate, -3)());
EXPECT_EQ(56, Bind(&Multiply, 8, 7)());
}
// Test Bind where method object implements RefCountInterface and is passed as a
// pointer.
TEST(BindTest, CapturePointerAsScopedRefPtr) {
LifeTimeCheck object;
EXPECT_EQ(object.ref_count_, 0);
scoped_refptr<LifeTimeCheck> scoped_object(&object);
EXPECT_EQ(object.ref_count_, 1);
{
auto functor = Bind(&LifeTimeCheck::NullaryVoid, &object);
EXPECT_EQ(object.ref_count_, 2);
scoped_object = nullptr;
EXPECT_EQ(object.ref_count_, 1);
}
EXPECT_EQ(object.ref_count_, 0);
}
// Test Bind where method object implements RefCountInterface and is passed as a
// scoped_refptr<>.
TEST(BindTest, CaptureScopedRefPtrAsScopedRefPtr) {
LifeTimeCheck object;
EXPECT_EQ(object.ref_count_, 0);
scoped_refptr<LifeTimeCheck> scoped_object(&object);
EXPECT_EQ(object.ref_count_, 1);
{
auto functor = Bind(&LifeTimeCheck::NullaryVoid, scoped_object);
EXPECT_EQ(object.ref_count_, 2);
scoped_object = nullptr;
EXPECT_EQ(object.ref_count_, 1);
}
EXPECT_EQ(object.ref_count_, 0);
}
// Test Bind where method object is captured as scoped_refptr<> and the functor
// dies while there are references left.
TEST(BindTest, FunctorReleasesObjectOnDestruction) {
LifeTimeCheck object;
EXPECT_EQ(object.ref_count_, 0);
scoped_refptr<LifeTimeCheck> scoped_object(&object);
EXPECT_EQ(object.ref_count_, 1);
Bind(&LifeTimeCheck::NullaryVoid, &object)();
EXPECT_EQ(object.ref_count_, 1);
scoped_object = nullptr;
EXPECT_EQ(object.ref_count_, 0);
}
// Test Bind with scoped_refptr<> argument.
TEST(BindTest, ScopedRefPointerArgument) {
LifeTimeCheck object;
EXPECT_EQ(object.ref_count_, 0);
scoped_refptr<LifeTimeCheck> scoped_object(&object);
EXPECT_EQ(object.ref_count_, 1);
{
MethodBindTester bind_tester;
auto functor =
Bind(&MethodBindTester::RefArgument, &bind_tester, scoped_object);
EXPECT_EQ(object.ref_count_, 2);
}
EXPECT_EQ(object.ref_count_, 1);
scoped_object = nullptr;
EXPECT_EQ(object.ref_count_, 0);
}
namespace {
const int* Ref(const int& a) { return &a; }
} // anonymous namespace
// Test Bind with non-scoped_refptr<> reference argument, which should be
// modified to a non-reference capture.
TEST(BindTest, RefArgument) {
const int x = 42;
EXPECT_EQ(&x, Ref(x));
// Bind() should make a copy of |x|, i.e. the pointers should be different.
auto functor = Bind(&Ref, x);
EXPECT_NE(&x, functor());
}
} // namespace rtc

View File

@ -0,0 +1,310 @@
/*
* 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.
*/
#include "webrtc/base/bitbuffer.h"
#include <algorithm>
#include <limits>
#include "webrtc/base/checks.h"
namespace {
// Returns the lowest (right-most) |bit_count| bits in |byte|.
uint8_t LowestBits(uint8_t byte, size_t bit_count) {
RTC_DCHECK_LE(bit_count, 8);
return byte & ((1 << bit_count) - 1);
}
// Returns the highest (left-most) |bit_count| bits in |byte|, shifted to the
// lowest bits (to the right).
uint8_t HighestBits(uint8_t byte, size_t bit_count) {
RTC_DCHECK_LE(bit_count, 8);
uint8_t shift = 8 - static_cast<uint8_t>(bit_count);
uint8_t mask = 0xFF << shift;
return (byte & mask) >> shift;
}
// Returns the highest byte of |val| in a uint8_t.
uint8_t HighestByte(uint64_t val) {
return static_cast<uint8_t>(val >> 56);
}
// Returns the result of writing partial data from |source|, of
// |source_bit_count| size in the highest bits, to |target| at
// |target_bit_offset| from the highest bit.
uint8_t WritePartialByte(uint8_t source,
size_t source_bit_count,
uint8_t target,
size_t target_bit_offset) {
RTC_DCHECK(target_bit_offset < 8);
RTC_DCHECK(source_bit_count < 9);
RTC_DCHECK(source_bit_count <= (8 - target_bit_offset));
// Generate a mask for just the bits we're going to overwrite, so:
uint8_t mask =
// The number of bits we want, in the most significant bits...
static_cast<uint8_t>(0xFF << (8 - source_bit_count))
// ...shifted over to the target offset from the most signficant bit.
>> target_bit_offset;
// We want the target, with the bits we'll overwrite masked off, or'ed with
// the bits from the source we want.
return (target & ~mask) | (source >> target_bit_offset);
}
// Counts the number of bits used in the binary representation of val.
size_t CountBits(uint64_t val) {
size_t bit_count = 0;
while (val != 0) {
bit_count++;
val >>= 1;
}
return bit_count;
}
} // namespace
namespace rtc {
BitBuffer::BitBuffer(const uint8_t* bytes, size_t byte_count)
: bytes_(bytes), byte_count_(byte_count), byte_offset_(), bit_offset_() {
RTC_DCHECK(static_cast<uint64_t>(byte_count_) <=
std::numeric_limits<uint32_t>::max());
}
uint64_t BitBuffer::RemainingBitCount() const {
return (static_cast<uint64_t>(byte_count_) - byte_offset_) * 8 - bit_offset_;
}
bool BitBuffer::ReadUInt8(uint8_t* val) {
uint32_t bit_val;
if (!ReadBits(&bit_val, sizeof(uint8_t) * 8)) {
return false;
}
RTC_DCHECK(bit_val <= std::numeric_limits<uint8_t>::max());
*val = static_cast<uint8_t>(bit_val);
return true;
}
bool BitBuffer::ReadUInt16(uint16_t* val) {
uint32_t bit_val;
if (!ReadBits(&bit_val, sizeof(uint16_t) * 8)) {
return false;
}
RTC_DCHECK(bit_val <= std::numeric_limits<uint16_t>::max());
*val = static_cast<uint16_t>(bit_val);
return true;
}
bool BitBuffer::ReadUInt32(uint32_t* val) {
return ReadBits(val, sizeof(uint32_t) * 8);
}
bool BitBuffer::PeekBits(uint32_t* val, size_t bit_count) {
if (!val || bit_count > RemainingBitCount() || bit_count > 32) {
return false;
}
const uint8_t* bytes = bytes_ + byte_offset_;
size_t remaining_bits_in_current_byte = 8 - bit_offset_;
uint32_t bits = LowestBits(*bytes++, remaining_bits_in_current_byte);
// If we're reading fewer bits than what's left in the current byte, just
// return the portion of this byte that we need.
if (bit_count < remaining_bits_in_current_byte) {
*val = HighestBits(bits, bit_offset_ + bit_count);
return true;
}
// Otherwise, subtract what we've read from the bit count and read as many
// full bytes as we can into bits.
bit_count -= remaining_bits_in_current_byte;
while (bit_count >= 8) {
bits = (bits << 8) | *bytes++;
bit_count -= 8;
}
// Whatever we have left is smaller than a byte, so grab just the bits we need
// and shift them into the lowest bits.
if (bit_count > 0) {
bits <<= bit_count;
bits |= HighestBits(*bytes, bit_count);
}
*val = bits;
return true;
}
bool BitBuffer::ReadBits(uint32_t* val, size_t bit_count) {
return PeekBits(val, bit_count) && ConsumeBits(bit_count);
}
bool BitBuffer::ConsumeBytes(size_t byte_count) {
return ConsumeBits(byte_count * 8);
}
bool BitBuffer::ConsumeBits(size_t bit_count) {
if (bit_count > RemainingBitCount()) {
return false;
}
byte_offset_ += (bit_offset_ + bit_count) / 8;
bit_offset_ = (bit_offset_ + bit_count) % 8;
return true;
}
bool BitBuffer::ReadExponentialGolomb(uint32_t* val) {
if (!val) {
return false;
}
// Store off the current byte/bit offset, in case we want to restore them due
// to a failed parse.
size_t original_byte_offset = byte_offset_;
size_t original_bit_offset = bit_offset_;
// Count the number of leading 0 bits by peeking/consuming them one at a time.
size_t zero_bit_count = 0;
uint32_t peeked_bit;
while (PeekBits(&peeked_bit, 1) && peeked_bit == 0) {
zero_bit_count++;
ConsumeBits(1);
}
// We should either be at the end of the stream, or the next bit should be 1.
RTC_DCHECK(!PeekBits(&peeked_bit, 1) || peeked_bit == 1);
// The bit count of the value is the number of zeros + 1. Make sure that many
// bits fits in a uint32_t and that we have enough bits left for it, and then
// read the value.
size_t value_bit_count = zero_bit_count + 1;
if (value_bit_count > 32 || !ReadBits(val, value_bit_count)) {
RTC_CHECK(Seek(original_byte_offset, original_bit_offset));
return false;
}
*val -= 1;
return true;
}
bool BitBuffer::ReadSignedExponentialGolomb(int32_t* val) {
uint32_t unsigned_val;
if (!ReadExponentialGolomb(&unsigned_val)) {
return false;
}
if ((unsigned_val & 1) == 0) {
*val = -static_cast<int32_t>(unsigned_val / 2);
} else {
*val = (unsigned_val + 1) / 2;
}
return true;
}
void BitBuffer::GetCurrentOffset(
size_t* out_byte_offset, size_t* out_bit_offset) {
RTC_CHECK(out_byte_offset != nullptr);
RTC_CHECK(out_bit_offset != nullptr);
*out_byte_offset = byte_offset_;
*out_bit_offset = bit_offset_;
}
bool BitBuffer::Seek(size_t byte_offset, size_t bit_offset) {
if (byte_offset > byte_count_ || bit_offset > 7 ||
(byte_offset == byte_count_ && bit_offset > 0)) {
return false;
}
byte_offset_ = byte_offset;
bit_offset_ = bit_offset;
return true;
}
BitBufferWriter::BitBufferWriter(uint8_t* bytes, size_t byte_count)
: BitBuffer(bytes, byte_count), writable_bytes_(bytes) {
}
bool BitBufferWriter::WriteUInt8(uint8_t val) {
return WriteBits(val, sizeof(uint8_t) * 8);
}
bool BitBufferWriter::WriteUInt16(uint16_t val) {
return WriteBits(val, sizeof(uint16_t) * 8);
}
bool BitBufferWriter::WriteUInt32(uint32_t val) {
return WriteBits(val, sizeof(uint32_t) * 8);
}
bool BitBufferWriter::WriteBits(uint64_t val, size_t bit_count) {
if (bit_count > RemainingBitCount()) {
return false;
}
size_t total_bits = bit_count;
// For simplicity, push the bits we want to read from val to the highest bits.
val <<= (sizeof(uint64_t) * 8 - bit_count);
uint8_t* bytes = writable_bytes_ + byte_offset_;
// The first byte is relatively special; the bit offset to write to may put us
// in the middle of the byte, and the total bit count to write may require we
// save the bits at the end of the byte.
size_t remaining_bits_in_current_byte = 8 - bit_offset_;
size_t bits_in_first_byte =
std::min(bit_count, remaining_bits_in_current_byte);
*bytes = WritePartialByte(
HighestByte(val), bits_in_first_byte, *bytes, bit_offset_);
if (bit_count <= remaining_bits_in_current_byte) {
// Nothing left to write, so quit early.
return ConsumeBits(total_bits);
}
// Subtract what we've written from the bit count, shift it off the value, and
// write the remaining full bytes.
val <<= bits_in_first_byte;
bytes++;
bit_count -= bits_in_first_byte;
while (bit_count >= 8) {
*bytes++ = HighestByte(val);
val <<= 8;
bit_count -= 8;
}
// Last byte may also be partial, so write the remaining bits from the top of
// val.
if (bit_count > 0) {
*bytes = WritePartialByte(HighestByte(val), bit_count, *bytes, 0);
}
// All done! Consume the bits we've written.
return ConsumeBits(total_bits);
}
bool BitBufferWriter::WriteExponentialGolomb(uint32_t val) {
// We don't support reading UINT32_MAX, because it doesn't fit in a uint32_t
// when encoded, so don't support writing it either.
if (val == std::numeric_limits<uint32_t>::max()) {
return false;
}
uint64_t val_to_encode = static_cast<uint64_t>(val) + 1;
// We need to write CountBits(val+1) 0s and then val+1. Since val (as a
// uint64_t) has leading zeros, we can just write the total golomb encoded
// size worth of bits, knowing the value will appear last.
return WriteBits(val_to_encode, CountBits(val_to_encode) * 2 - 1);
}
bool BitBufferWriter::WriteSignedExponentialGolomb(int32_t val) {
if (val == 0) {
return WriteExponentialGolomb(0);
} else if (val > 0) {
uint32_t signed_val = val;
return WriteExponentialGolomb((signed_val * 2) - 1);
} else {
if (val == std::numeric_limits<int32_t>::min())
return false; // Not supported, would cause overflow.
uint32_t signed_val = -val;
return WriteExponentialGolomb(signed_val * 2);
}
}
} // namespace rtc

126
webrtc/rtc_base/bitbuffer.h Normal file
View File

@ -0,0 +1,126 @@
/*
* 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_RTC_BASE_BITBUFFER_H_
#define WEBRTC_RTC_BASE_BITBUFFER_H_
#include <stdint.h> // For integer types.
#include <stddef.h> // For size_t.
#include "webrtc/base/constructormagic.h"
namespace rtc {
// A class, similar to ByteBuffer, that can parse bit-sized data out of a set of
// bytes. Has a similar API to ByteBuffer, plus methods for reading bit-sized
// and exponential golomb encoded data. For a writable version, use
// BitBufferWriter. Unlike ByteBuffer, this class doesn't make a copy of the
// source bytes, so it can be used on read-only data.
// Sizes/counts specify bits/bytes, for clarity.
// Byte order is assumed big-endian/network.
class BitBuffer {
public:
BitBuffer(const uint8_t* bytes, size_t byte_count);
// Gets the current offset, in bytes/bits, from the start of the buffer. The
// bit offset is the offset into the current byte, in the range [0,7].
void GetCurrentOffset(size_t* out_byte_offset, size_t* out_bit_offset);
// The remaining bits in the byte buffer.
uint64_t RemainingBitCount() const;
// Reads byte-sized values from the buffer. Returns false if there isn't
// enough data left for the specified type.
bool ReadUInt8(uint8_t* val);
bool ReadUInt16(uint16_t* val);
bool ReadUInt32(uint32_t* val);
// Reads bit-sized values from the buffer. Returns false if there isn't enough
// data left for the specified bit count..
bool ReadBits(uint32_t* val, size_t bit_count);
// Peeks bit-sized values from the buffer. Returns false if there isn't enough
// data left for the specified number of bits. Doesn't move the current
// offset.
bool PeekBits(uint32_t* val, size_t bit_count);
// Reads the exponential golomb encoded value at the current offset.
// Exponential golomb values are encoded as:
// 1) x = source val + 1
// 2) In binary, write [countbits(x) - 1] 0s, then x
// To decode, we count the number of leading 0 bits, read that many + 1 bits,
// and increment the result by 1.
// Returns false if there isn't enough data left for the specified type, or if
// the value wouldn't fit in a uint32_t.
bool ReadExponentialGolomb(uint32_t* val);
// Reads signed exponential golomb values at the current offset. Signed
// exponential golomb values are just the unsigned values mapped to the
// sequence 0, 1, -1, 2, -2, etc. in order.
bool ReadSignedExponentialGolomb(int32_t* val);
// Moves current position |byte_count| bytes forward. Returns false if
// there aren't enough bytes left in the buffer.
bool ConsumeBytes(size_t byte_count);
// Moves current position |bit_count| bits forward. Returns false if
// there aren't enough bits left in the buffer.
bool ConsumeBits(size_t bit_count);
// Sets the current offset to the provied byte/bit offsets. The bit
// offset is from the given byte, in the range [0,7].
bool Seek(size_t byte_offset, size_t bit_offset);
protected:
const uint8_t* const bytes_;
// The total size of |bytes_|.
size_t byte_count_;
// The current offset, in bytes, from the start of |bytes_|.
size_t byte_offset_;
// The current offset, in bits, into the current byte.
size_t bit_offset_;
RTC_DISALLOW_COPY_AND_ASSIGN(BitBuffer);
};
// A BitBuffer API for write operations. Supports symmetric write APIs to the
// reading APIs of BitBuffer. Note that the read/write offset is shared with the
// BitBuffer API, so both reading and writing will consume bytes/bits.
class BitBufferWriter : public BitBuffer {
public:
// Constructs a bit buffer for the writable buffer of |bytes|.
BitBufferWriter(uint8_t* bytes, size_t byte_count);
// Writes byte-sized values from the buffer. Returns false if there isn't
// enough data left for the specified type.
bool WriteUInt8(uint8_t val);
bool WriteUInt16(uint16_t val);
bool WriteUInt32(uint32_t val);
// Writes bit-sized values to the buffer. Returns false if there isn't enough
// room left for the specified number of bits.
bool WriteBits(uint64_t val, size_t bit_count);
// Writes the exponential golomb encoded version of the supplied value.
// Returns false if there isn't enough room left for the value.
bool WriteExponentialGolomb(uint32_t val);
// Writes the signed exponential golomb version of the supplied value.
// Signed exponential golomb values are just the unsigned values mapped to the
// sequence 0, 1, -1, 2, -2, etc. in order.
bool WriteSignedExponentialGolomb(int32_t val);
private:
// The buffer, as a writable array.
uint8_t* const writable_bytes_;
RTC_DISALLOW_COPY_AND_ASSIGN(BitBufferWriter);
};
} // namespace rtc
#endif // WEBRTC_RTC_BASE_BITBUFFER_H_

View File

@ -0,0 +1,329 @@
/*
* 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.
*/
#include "webrtc/base/arraysize.h"
#include "webrtc/base/bitbuffer.h"
#include "webrtc/base/bytebuffer.h"
#include "webrtc/base/gunit.h"
namespace rtc {
TEST(BitBufferTest, ConsumeBits) {
const uint8_t bytes[64] = {0};
BitBuffer buffer(bytes, 32);
uint64_t total_bits = 32 * 8;
EXPECT_EQ(total_bits, buffer.RemainingBitCount());
EXPECT_TRUE(buffer.ConsumeBits(3));
total_bits -= 3;
EXPECT_EQ(total_bits, buffer.RemainingBitCount());
EXPECT_TRUE(buffer.ConsumeBits(3));
total_bits -= 3;
EXPECT_EQ(total_bits, buffer.RemainingBitCount());
EXPECT_TRUE(buffer.ConsumeBits(15));
total_bits -= 15;
EXPECT_EQ(total_bits, buffer.RemainingBitCount());
EXPECT_TRUE(buffer.ConsumeBits(37));
total_bits -= 37;
EXPECT_EQ(total_bits, buffer.RemainingBitCount());
EXPECT_FALSE(buffer.ConsumeBits(32 * 8));
EXPECT_EQ(total_bits, buffer.RemainingBitCount());
}
TEST(BitBufferTest, ReadBytesAligned) {
const uint8_t bytes[] = {0x0A, 0xBC, 0xDE, 0xF1, 0x23, 0x45, 0x67, 0x89};
uint8_t val8;
uint16_t val16;
uint32_t val32;
BitBuffer buffer(bytes, 8);
EXPECT_TRUE(buffer.ReadUInt8(&val8));
EXPECT_EQ(0x0Au, val8);
EXPECT_TRUE(buffer.ReadUInt8(&val8));
EXPECT_EQ(0xBCu, val8);
EXPECT_TRUE(buffer.ReadUInt16(&val16));
EXPECT_EQ(0xDEF1u, val16);
EXPECT_TRUE(buffer.ReadUInt32(&val32));
EXPECT_EQ(0x23456789u, val32);
}
TEST(BitBufferTest, ReadBytesOffset4) {
const uint8_t bytes[] = {0x0A, 0xBC, 0xDE, 0xF1, 0x23,
0x45, 0x67, 0x89, 0x0A};
uint8_t val8;
uint16_t val16;
uint32_t val32;
BitBuffer buffer(bytes, 9);
EXPECT_TRUE(buffer.ConsumeBits(4));
EXPECT_TRUE(buffer.ReadUInt8(&val8));
EXPECT_EQ(0xABu, val8);
EXPECT_TRUE(buffer.ReadUInt8(&val8));
EXPECT_EQ(0xCDu, val8);
EXPECT_TRUE(buffer.ReadUInt16(&val16));
EXPECT_EQ(0xEF12u, val16);
EXPECT_TRUE(buffer.ReadUInt32(&val32));
EXPECT_EQ(0x34567890u, val32);
}
TEST(BitBufferTest, ReadBytesOffset3) {
// The pattern we'll check against is counting down from 0b1111. It looks
// weird here because it's all offset by 3.
// Byte pattern is:
// 56701234
// 0b00011111,
// 0b11011011,
// 0b10010111,
// 0b01010011,
// 0b00001110,
// 0b11001010,
// 0b10000110,
// 0b01000010
// xxxxx <-- last 5 bits unused.
// The bytes. It almost looks like counting down by two at a time, except the
// jump at 5->3->0, since that's when the high bit is turned off.
const uint8_t bytes[] = {0x1F, 0xDB, 0x97, 0x53, 0x0E, 0xCA, 0x86, 0x42};
uint8_t val8;
uint16_t val16;
uint32_t val32;
BitBuffer buffer(bytes, 8);
EXPECT_TRUE(buffer.ConsumeBits(3));
EXPECT_TRUE(buffer.ReadUInt8(&val8));
EXPECT_EQ(0xFEu, val8);
EXPECT_TRUE(buffer.ReadUInt16(&val16));
EXPECT_EQ(0xDCBAu, val16);
EXPECT_TRUE(buffer.ReadUInt32(&val32));
EXPECT_EQ(0x98765432u, val32);
// 5 bits left unread. Not enough to read a uint8_t.
EXPECT_EQ(5u, buffer.RemainingBitCount());
EXPECT_FALSE(buffer.ReadUInt8(&val8));
}
TEST(BitBufferTest, ReadBits) {
// Bit values are:
// 0b01001101,
// 0b00110010
const uint8_t bytes[] = {0x4D, 0x32};
uint32_t val;
BitBuffer buffer(bytes, 2);
EXPECT_TRUE(buffer.ReadBits(&val, 3));
// 0b010
EXPECT_EQ(0x2u, val);
EXPECT_TRUE(buffer.ReadBits(&val, 2));
// 0b01
EXPECT_EQ(0x1u, val);
EXPECT_TRUE(buffer.ReadBits(&val, 7));
// 0b1010011
EXPECT_EQ(0x53u, val);
EXPECT_TRUE(buffer.ReadBits(&val, 2));
// 0b00
EXPECT_EQ(0x0u, val);
EXPECT_TRUE(buffer.ReadBits(&val, 1));
// 0b1
EXPECT_EQ(0x1u, val);
EXPECT_TRUE(buffer.ReadBits(&val, 1));
// 0b0
EXPECT_EQ(0x0u, val);
EXPECT_FALSE(buffer.ReadBits(&val, 1));
}
TEST(BitBufferTest, SetOffsetValues) {
uint8_t bytes[4] = {0};
BitBufferWriter buffer(bytes, 4);
size_t byte_offset, bit_offset;
// Bit offsets are [0,7].
EXPECT_TRUE(buffer.Seek(0, 0));
EXPECT_TRUE(buffer.Seek(0, 7));
buffer.GetCurrentOffset(&byte_offset, &bit_offset);
EXPECT_EQ(0u, byte_offset);
EXPECT_EQ(7u, bit_offset);
EXPECT_FALSE(buffer.Seek(0, 8));
buffer.GetCurrentOffset(&byte_offset, &bit_offset);
EXPECT_EQ(0u, byte_offset);
EXPECT_EQ(7u, bit_offset);
// Byte offsets are [0,length]. At byte offset length, the bit offset must be
// 0.
EXPECT_TRUE(buffer.Seek(0, 0));
EXPECT_TRUE(buffer.Seek(2, 4));
buffer.GetCurrentOffset(&byte_offset, &bit_offset);
EXPECT_EQ(2u, byte_offset);
EXPECT_EQ(4u, bit_offset);
EXPECT_TRUE(buffer.Seek(4, 0));
EXPECT_FALSE(buffer.Seek(5, 0));
buffer.GetCurrentOffset(&byte_offset, &bit_offset);
EXPECT_EQ(4u, byte_offset);
EXPECT_EQ(0u, bit_offset);
EXPECT_FALSE(buffer.Seek(4, 1));
// Disable death test on Android because it relies on fork() and doesn't play
// nicely.
#if GTEST_HAS_DEATH_TEST
#if !defined(WEBRTC_ANDROID)
// Passing a null out parameter is death.
EXPECT_DEATH(buffer.GetCurrentOffset(&byte_offset, nullptr), "");
#endif
#endif
}
uint64_t GolombEncoded(uint32_t val) {
val++;
uint32_t bit_counter = val;
uint64_t bit_count = 0;
while (bit_counter > 0) {
bit_count++;
bit_counter >>= 1;
}
return static_cast<uint64_t>(val) << (64 - (bit_count * 2 - 1));
}
TEST(BitBufferTest, GolombUint32Values) {
ByteBufferWriter byteBuffer;
byteBuffer.Resize(16);
BitBuffer buffer(reinterpret_cast<const uint8_t*>(byteBuffer.Data()),
byteBuffer.Capacity());
// Test over the uint32_t range with a large enough step that the test doesn't
// take forever. Around 20,000 iterations should do.
const int kStep = std::numeric_limits<uint32_t>::max() / 20000;
for (uint32_t i = 0; i < std::numeric_limits<uint32_t>::max() - kStep;
i += kStep) {
uint64_t encoded_val = GolombEncoded(i);
byteBuffer.Clear();
byteBuffer.WriteUInt64(encoded_val);
uint32_t decoded_val;
EXPECT_TRUE(buffer.Seek(0, 0));
EXPECT_TRUE(buffer.ReadExponentialGolomb(&decoded_val));
EXPECT_EQ(i, decoded_val);
}
}
TEST(BitBufferTest, SignedGolombValues) {
uint8_t golomb_bits[] = {
0x80, // 1
0x40, // 010
0x60, // 011
0x20, // 00100
0x38, // 00111
};
int32_t expected[] = {0, 1, -1, 2, -3};
for (size_t i = 0; i < sizeof(golomb_bits); ++i) {
BitBuffer buffer(&golomb_bits[i], 1);
int32_t decoded_val;
ASSERT_TRUE(buffer.ReadSignedExponentialGolomb(&decoded_val));
EXPECT_EQ(expected[i], decoded_val)
<< "Mismatch in expected/decoded value for golomb_bits[" << i
<< "]: " << static_cast<int>(golomb_bits[i]);
}
}
TEST(BitBufferTest, NoGolombOverread) {
const uint8_t bytes[] = {0x00, 0xFF, 0xFF};
// Make sure the bit buffer correctly enforces byte length on golomb reads.
// If it didn't, the above buffer would be valid at 3 bytes.
BitBuffer buffer(bytes, 1);
uint32_t decoded_val;
EXPECT_FALSE(buffer.ReadExponentialGolomb(&decoded_val));
BitBuffer longer_buffer(bytes, 2);
EXPECT_FALSE(longer_buffer.ReadExponentialGolomb(&decoded_val));
BitBuffer longest_buffer(bytes, 3);
EXPECT_TRUE(longest_buffer.ReadExponentialGolomb(&decoded_val));
// Golomb should have read 9 bits, so 0x01FF, and since it is golomb, the
// result is 0x01FF - 1 = 0x01FE.
EXPECT_EQ(0x01FEu, decoded_val);
}
TEST(BitBufferWriterTest, SymmetricReadWrite) {
uint8_t bytes[16] = {0};
BitBufferWriter buffer(bytes, 4);
// Write some bit data at various sizes.
EXPECT_TRUE(buffer.WriteBits(0x2u, 3));
EXPECT_TRUE(buffer.WriteBits(0x1u, 2));
EXPECT_TRUE(buffer.WriteBits(0x53u, 7));
EXPECT_TRUE(buffer.WriteBits(0x0u, 2));
EXPECT_TRUE(buffer.WriteBits(0x1u, 1));
EXPECT_TRUE(buffer.WriteBits(0x1ABCDu, 17));
// That should be all that fits in the buffer.
EXPECT_FALSE(buffer.WriteBits(1, 1));
EXPECT_TRUE(buffer.Seek(0, 0));
uint32_t val;
EXPECT_TRUE(buffer.ReadBits(&val, 3));
EXPECT_EQ(0x2u, val);
EXPECT_TRUE(buffer.ReadBits(&val, 2));
EXPECT_EQ(0x1u, val);
EXPECT_TRUE(buffer.ReadBits(&val, 7));
EXPECT_EQ(0x53u, val);
EXPECT_TRUE(buffer.ReadBits(&val, 2));
EXPECT_EQ(0x0u, val);
EXPECT_TRUE(buffer.ReadBits(&val, 1));
EXPECT_EQ(0x1u, val);
EXPECT_TRUE(buffer.ReadBits(&val, 17));
EXPECT_EQ(0x1ABCDu, val);
// And there should be nothing left.
EXPECT_FALSE(buffer.ReadBits(&val, 1));
}
TEST(BitBufferWriterTest, SymmetricBytesMisaligned) {
uint8_t bytes[16] = {0};
BitBufferWriter buffer(bytes, 16);
// Offset 3, to get things misaligned.
EXPECT_TRUE(buffer.ConsumeBits(3));
EXPECT_TRUE(buffer.WriteUInt8(0x12u));
EXPECT_TRUE(buffer.WriteUInt16(0x3456u));
EXPECT_TRUE(buffer.WriteUInt32(0x789ABCDEu));
buffer.Seek(0, 3);
uint8_t val8;
uint16_t val16;
uint32_t val32;
EXPECT_TRUE(buffer.ReadUInt8(&val8));
EXPECT_EQ(0x12u, val8);
EXPECT_TRUE(buffer.ReadUInt16(&val16));
EXPECT_EQ(0x3456u, val16);
EXPECT_TRUE(buffer.ReadUInt32(&val32));
EXPECT_EQ(0x789ABCDEu, val32);
}
TEST(BitBufferWriterTest, SymmetricGolomb) {
char test_string[] = "my precious";
uint8_t bytes[64] = {0};
BitBufferWriter buffer(bytes, 64);
for (size_t i = 0; i < arraysize(test_string); ++i) {
EXPECT_TRUE(buffer.WriteExponentialGolomb(test_string[i]));
}
buffer.Seek(0, 0);
for (size_t i = 0; i < arraysize(test_string); ++i) {
uint32_t val;
EXPECT_TRUE(buffer.ReadExponentialGolomb(&val));
EXPECT_LE(val, std::numeric_limits<uint8_t>::max());
EXPECT_EQ(test_string[i], static_cast<char>(val));
}
}
TEST(BitBufferWriterTest, WriteClearsBits) {
uint8_t bytes[] = {0xFF, 0xFF};
BitBufferWriter buffer(bytes, 2);
EXPECT_TRUE(buffer.ConsumeBits(3));
EXPECT_TRUE(buffer.WriteBits(0, 1));
EXPECT_EQ(0xEFu, bytes[0]);
EXPECT_TRUE(buffer.WriteBits(0, 3));
EXPECT_EQ(0xE1u, bytes[0]);
EXPECT_TRUE(buffer.WriteBits(0, 2));
EXPECT_EQ(0xE0u, bytes[0]);
EXPECT_EQ(0x7F, bytes[1]);
}
} // namespace rtc

383
webrtc/rtc_base/buffer.h Normal file
View File

@ -0,0 +1,383 @@
/*
* 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_RTC_BASE_BUFFER_H_
#define WEBRTC_RTC_BASE_BUFFER_H_
#include <algorithm>
#include <cstring>
#include <memory>
#include <type_traits>
#include <utility>
#include "webrtc/base/array_view.h"
#include "webrtc/base/checks.h"
#include "webrtc/base/type_traits.h"
namespace rtc {
namespace internal {
// (Internal; please don't use outside this file.) Determines if elements of
// type U are compatible with a BufferT<T>. For most types, we just ignore
// top-level const and forbid top-level volatile and require T and U to be
// otherwise equal, but all byte-sized integers (notably char, int8_t, and
// uint8_t) are compatible with each other. (Note: We aim to get rid of this
// behavior, and treat all types the same.)
template <typename T, typename U>
struct BufferCompat {
static constexpr bool value =
!std::is_volatile<U>::value &&
((std::is_integral<T>::value && sizeof(T) == 1)
? (std::is_integral<U>::value && sizeof(U) == 1)
: (std::is_same<T, typename std::remove_const<U>::type>::value));
};
} // namespace internal
// Basic buffer class, can be grown and shrunk dynamically.
// Unlike std::string/vector, does not initialize data when increasing size.
template <typename T>
class BufferT {
// We want T's destructor and default constructor to be trivial, i.e. perform
// no action, so that we don't have to touch the memory we allocate and
// deallocate. And we want T to be trivially copyable, so that we can copy T
// instances with std::memcpy. This is precisely the definition of a trivial
// type.
static_assert(std::is_trivial<T>::value, "T must be a trivial type.");
// This class relies heavily on being able to mutate its data.
static_assert(!std::is_const<T>::value, "T may not be const");
public:
using value_type = T;
// An empty BufferT.
BufferT() : size_(0), capacity_(0), data_(nullptr) {
RTC_DCHECK(IsConsistent());
}
// Disable copy construction and copy assignment, since copying a buffer is
// expensive enough that we want to force the user to be explicit about it.
BufferT(const BufferT&) = delete;
BufferT& operator=(const BufferT&) = delete;
BufferT(BufferT&& buf)
: size_(buf.size()),
capacity_(buf.capacity()),
data_(std::move(buf.data_)) {
RTC_DCHECK(IsConsistent());
buf.OnMovedFrom();
}
// Construct a buffer with the specified number of uninitialized elements.
explicit BufferT(size_t size) : BufferT(size, size) {}
BufferT(size_t size, size_t capacity)
: size_(size),
capacity_(std::max(size, capacity)),
data_(new T[capacity_]) {
RTC_DCHECK(IsConsistent());
}
// Construct a buffer and copy the specified number of elements into it.
template <typename U,
typename std::enable_if<
internal::BufferCompat<T, U>::value>::type* = nullptr>
BufferT(const U* data, size_t size) : BufferT(data, size, size) {}
template <typename U,
typename std::enable_if<
internal::BufferCompat<T, U>::value>::type* = nullptr>
BufferT(U* data, size_t size, size_t capacity) : BufferT(size, capacity) {
static_assert(sizeof(T) == sizeof(U), "");
std::memcpy(data_.get(), data, size * sizeof(U));
}
// Construct a buffer from the contents of an array.
template <typename U,
size_t N,
typename std::enable_if<
internal::BufferCompat<T, U>::value>::type* = nullptr>
BufferT(U (&array)[N]) : BufferT(array, N) {}
// Get a pointer to the data. Just .data() will give you a (const) T*, but if
// T is a byte-sized integer, you may also use .data<U>() for any other
// byte-sized integer U.
template <typename U = T,
typename std::enable_if<
internal::BufferCompat<T, U>::value>::type* = nullptr>
const U* data() const {
RTC_DCHECK(IsConsistent());
return reinterpret_cast<U*>(data_.get());
}
template <typename U = T,
typename std::enable_if<
internal::BufferCompat<T, U>::value>::type* = nullptr>
U* data() {
RTC_DCHECK(IsConsistent());
return reinterpret_cast<U*>(data_.get());
}
bool empty() const {
RTC_DCHECK(IsConsistent());
return size_ == 0;
}
size_t size() const {
RTC_DCHECK(IsConsistent());
return size_;
}
size_t capacity() const {
RTC_DCHECK(IsConsistent());
return capacity_;
}
BufferT& operator=(BufferT&& buf) {
RTC_DCHECK(IsConsistent());
RTC_DCHECK(buf.IsConsistent());
size_ = buf.size_;
capacity_ = buf.capacity_;
data_ = std::move(buf.data_);
buf.OnMovedFrom();
return *this;
}
bool operator==(const BufferT& buf) const {
RTC_DCHECK(IsConsistent());
if (size_ != buf.size_) {
return false;
}
if (std::is_integral<T>::value) {
// Optimization.
return std::memcmp(data_.get(), buf.data_.get(), size_ * sizeof(T)) == 0;
}
for (size_t i = 0; i < size_; ++i) {
if (data_[i] != buf.data_[i]) {
return false;
}
}
return true;
}
bool operator!=(const BufferT& buf) const { return !(*this == buf); }
T& operator[](size_t index) {
RTC_DCHECK_LT(index, size_);
return data()[index];
}
T operator[](size_t index) const {
RTC_DCHECK_LT(index, size_);
return data()[index];
}
T* begin() { return data(); }
T* end() { return data() + size(); }
const T* begin() const { return data(); }
const T* end() const { return data() + size(); }
const T* cbegin() const { return data(); }
const T* cend() const { return data() + size(); }
// The SetData functions replace the contents of the buffer. They accept the
// same input types as the constructors.
template <typename U,
typename std::enable_if<
internal::BufferCompat<T, U>::value>::type* = nullptr>
void SetData(const U* data, size_t size) {
RTC_DCHECK(IsConsistent());
size_ = 0;
AppendData(data, size);
}
template <typename U,
size_t N,
typename std::enable_if<
internal::BufferCompat<T, U>::value>::type* = nullptr>
void SetData(const U (&array)[N]) {
SetData(array, N);
}
template <typename W,
typename std::enable_if<
HasDataAndSize<const W, const T>::value>::type* = nullptr>
void SetData(const W& w) {
SetData(w.data(), w.size());
}
// Replace the data in the buffer with at most |max_elements| of data, using
// the function |setter|, which should have the following signature:
// size_t setter(ArrayView<U> view)
// |setter| is given an appropriately typed ArrayView of the area in which to
// write the data (i.e. starting at the beginning of the buffer) and should
// return the number of elements actually written. This number must be <=
// |max_elements|.
template <typename U = T,
typename F,
typename std::enable_if<
internal::BufferCompat<T, U>::value>::type* = nullptr>
size_t SetData(size_t max_elements, F&& setter) {
RTC_DCHECK(IsConsistent());
size_ = 0;
return AppendData<U>(max_elements, std::forward<F>(setter));
}
// The AppendData functions add data to the end of the buffer. They accept
// the same input types as the constructors.
template <typename U,
typename std::enable_if<
internal::BufferCompat<T, U>::value>::type* = nullptr>
void AppendData(const U* data, size_t size) {
RTC_DCHECK(IsConsistent());
const size_t new_size = size_ + size;
EnsureCapacityWithHeadroom(new_size, true);
static_assert(sizeof(T) == sizeof(U), "");
std::memcpy(data_.get() + size_, data, size * sizeof(U));
size_ = new_size;
RTC_DCHECK(IsConsistent());
}
template <typename U,
size_t N,
typename std::enable_if<
internal::BufferCompat<T, U>::value>::type* = nullptr>
void AppendData(const U (&array)[N]) {
AppendData(array, N);
}
template <typename W,
typename std::enable_if<
HasDataAndSize<const W, const T>::value>::type* = nullptr>
void AppendData(const W& w) {
AppendData(w.data(), w.size());
}
template <typename U,
typename std::enable_if<
internal::BufferCompat<T, U>::value>::type* = nullptr>
void AppendData(const U& item) {
AppendData(&item, 1);
}
// Append at most |max_elements| to the end of the buffer, using the function
// |setter|, which should have the following signature:
// size_t setter(ArrayView<U> view)
// |setter| is given an appropriately typed ArrayView of the area in which to
// write the data (i.e. starting at the former end of the buffer) and should
// return the number of elements actually written. This number must be <=
// |max_elements|.
template <typename U = T,
typename F,
typename std::enable_if<
internal::BufferCompat<T, U>::value>::type* = nullptr>
size_t AppendData(size_t max_elements, F&& setter) {
RTC_DCHECK(IsConsistent());
const size_t old_size = size_;
SetSize(old_size + max_elements);
U* base_ptr = data<U>() + old_size;
size_t written_elements = setter(rtc::ArrayView<U>(base_ptr, max_elements));
RTC_CHECK_LE(written_elements, max_elements);
size_ = old_size + written_elements;
RTC_DCHECK(IsConsistent());
return written_elements;
}
// 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) {
EnsureCapacityWithHeadroom(size, true);
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) {
// Don't allocate extra headroom, since the user is asking for a specific
// capacity.
EnsureCapacityWithHeadroom(capacity, false);
}
// Resets the buffer to zero size without altering capacity. Works even if the
// buffer has been moved from.
void Clear() {
size_ = 0;
RTC_DCHECK(IsConsistent());
}
// Swaps two buffers. Also works for buffers that have been moved from.
friend void swap(BufferT& a, BufferT& b) {
using std::swap;
swap(a.size_, b.size_);
swap(a.capacity_, b.capacity_);
swap(a.data_, b.data_);
}
private:
void EnsureCapacityWithHeadroom(size_t capacity, bool extra_headroom) {
RTC_DCHECK(IsConsistent());
if (capacity <= capacity_)
return;
// If the caller asks for extra headroom, ensure that the new capacity is
// >= 1.5 times the old capacity. Any constant > 1 is sufficient to prevent
// quadratic behavior; as to why we pick 1.5 in particular, see
// https://github.com/facebook/folly/blob/master/folly/docs/FBVector.md and
// http://www.gahcep.com/cpp-internals-stl-vector-part-1/.
const size_t new_capacity =
extra_headroom ? std::max(capacity, capacity_ + capacity_ / 2)
: capacity;
std::unique_ptr<T[]> new_data(new T[new_capacity]);
std::memcpy(new_data.get(), data_.get(), size_ * sizeof(T));
data_ = std::move(new_data);
capacity_ = new_capacity;
RTC_DCHECK(IsConsistent());
}
// 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() {
#if RTC_DCHECK_IS_ON
// 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_;
std::unique_ptr<T[]> data_;
};
// By far the most common sort of buffer.
using Buffer = BufferT<uint8_t>;
} // namespace rtc
#endif // WEBRTC_RTC_BASE_BUFFER_H_

View File

@ -0,0 +1,437 @@
/*
* 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.
*/
#include "webrtc/base/buffer.h"
#include "webrtc/base/array_view.h"
#include "webrtc/base/gunit.h"
#include <type_traits>
#include <utility>
namespace rtc {
namespace {
// clang-format off
const uint8_t kTestData[] = {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,
0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf};
// clang-format on
void TestBuf(const Buffer& b1, size_t size, size_t capacity) {
EXPECT_EQ(b1.size(), size);
EXPECT_EQ(b1.capacity(), capacity);
}
} // namespace
TEST(BufferTest, TestConstructEmpty) {
TestBuf(Buffer(), 0, 0);
TestBuf(Buffer(Buffer()), 0, 0);
TestBuf(Buffer(0), 0, 0);
// We can't use a literal 0 for the first argument, because C++ will allow
// that to be considered a null pointer, which makes the call ambiguous.
TestBuf(Buffer(0 + 0, 10), 0, 10);
TestBuf(Buffer(kTestData, 0), 0, 0);
TestBuf(Buffer(kTestData, 0, 20), 0, 20);
}
TEST(BufferTest, TestConstructData) {
Buffer buf(kTestData, 7);
EXPECT_EQ(buf.size(), 7u);
EXPECT_EQ(buf.capacity(), 7u);
EXPECT_FALSE(buf.empty());
EXPECT_EQ(0, memcmp(buf.data(), kTestData, 7));
}
TEST(BufferTest, TestConstructDataWithCapacity) {
Buffer buf(kTestData, 7, 14);
EXPECT_EQ(buf.size(), 7u);
EXPECT_EQ(buf.capacity(), 14u);
EXPECT_FALSE(buf.empty());
EXPECT_EQ(0, memcmp(buf.data(), kTestData, 7));
}
TEST(BufferTest, TestConstructArray) {
Buffer buf(kTestData);
EXPECT_EQ(buf.size(), 16u);
EXPECT_EQ(buf.capacity(), 16u);
EXPECT_FALSE(buf.empty());
EXPECT_EQ(0, memcmp(buf.data(), kTestData, 16));
}
TEST(BufferTest, TestSetData) {
Buffer buf(kTestData + 4, 7);
buf.SetData(kTestData, 9);
EXPECT_EQ(buf.size(), 9u);
EXPECT_EQ(buf.capacity(), 7u * 3 / 2);
EXPECT_FALSE(buf.empty());
EXPECT_EQ(0, memcmp(buf.data(), kTestData, 9));
Buffer buf2;
buf2.SetData(buf);
EXPECT_EQ(buf.size(), 9u);
EXPECT_EQ(buf.capacity(), 7u * 3 / 2);
EXPECT_EQ(0, memcmp(buf.data(), kTestData, 9));
}
TEST(BufferTest, TestAppendData) {
Buffer buf(kTestData + 4, 3);
buf.AppendData(kTestData + 10, 2);
const int8_t exp[] = {0x4, 0x5, 0x6, 0xa, 0xb};
EXPECT_EQ(buf, Buffer(exp));
Buffer buf2;
buf2.AppendData(buf);
buf2.AppendData(rtc::ArrayView<uint8_t>(buf));
const int8_t exp2[] = {0x4, 0x5, 0x6, 0xa, 0xb, 0x4, 0x5, 0x6, 0xa, 0xb};
EXPECT_EQ(buf2, Buffer(exp2));
}
TEST(BufferTest, TestSetAndAppendWithUnknownArg) {
struct TestDataContainer {
size_t size() const { return 3; }
const uint8_t* data() const { return kTestData; }
};
Buffer buf;
buf.SetData(TestDataContainer());
EXPECT_EQ(3u, buf.size());
EXPECT_EQ(Buffer(kTestData, 3), buf);
buf.AppendData(TestDataContainer());
EXPECT_EQ(6u, buf.size());
EXPECT_EQ(0, memcmp(buf.data(), kTestData, 3));
EXPECT_EQ(0, memcmp(buf.data() + 3, kTestData, 3));
}
TEST(BufferTest, TestSetSizeSmaller) {
Buffer buf;
buf.SetData(kTestData, 15);
buf.SetSize(10);
EXPECT_EQ(buf.size(), 10u);
EXPECT_EQ(buf.capacity(), 15u); // Hasn't shrunk.
EXPECT_FALSE(buf.empty());
EXPECT_EQ(buf, Buffer(kTestData, 10));
}
TEST(BufferTest, TestSetSizeLarger) {
Buffer buf;
buf.SetData(kTestData, 15);
EXPECT_EQ(buf.size(), 15u);
EXPECT_EQ(buf.capacity(), 15u);
EXPECT_FALSE(buf.empty());
buf.SetSize(20);
EXPECT_EQ(buf.size(), 20u);
EXPECT_EQ(buf.capacity(), 15u * 3 / 2); // Has grown.
EXPECT_FALSE(buf.empty());
EXPECT_EQ(0, memcmp(buf.data(), kTestData, 15));
}
TEST(BufferTest, TestEnsureCapacitySmaller) {
Buffer buf(kTestData);
const char* data = buf.data<char>();
buf.EnsureCapacity(4);
EXPECT_EQ(buf.capacity(), 16u); // Hasn't shrunk.
EXPECT_EQ(buf.data<char>(), data); // No reallocation.
EXPECT_FALSE(buf.empty());
EXPECT_EQ(buf, Buffer(kTestData));
}
TEST(BufferTest, TestEnsureCapacityLarger) {
Buffer buf(kTestData, 5);
buf.EnsureCapacity(10);
const int8_t* data = buf.data<int8_t>();
EXPECT_EQ(buf.capacity(), 10u);
buf.AppendData(kTestData + 5, 5);
EXPECT_EQ(buf.data<int8_t>(), data); // No reallocation.
EXPECT_FALSE(buf.empty());
EXPECT_EQ(buf, Buffer(kTestData, 10));
}
TEST(BufferTest, TestMoveConstruct) {
Buffer buf1(kTestData, 3, 40);
const uint8_t* data = buf1.data();
Buffer buf2(std::move(buf1));
EXPECT_EQ(buf2.size(), 3u);
EXPECT_EQ(buf2.capacity(), 40u);
EXPECT_EQ(buf2.data(), data);
EXPECT_FALSE(buf2.empty());
buf1.Clear();
EXPECT_EQ(buf1.size(), 0u);
EXPECT_EQ(buf1.capacity(), 0u);
EXPECT_EQ(buf1.data(), nullptr);
EXPECT_TRUE(buf1.empty());
}
TEST(BufferTest, TestMoveAssign) {
Buffer buf1(kTestData, 3, 40);
const uint8_t* data = buf1.data();
Buffer buf2(kTestData);
buf2 = std::move(buf1);
EXPECT_EQ(buf2.size(), 3u);
EXPECT_EQ(buf2.capacity(), 40u);
EXPECT_EQ(buf2.data(), data);
EXPECT_FALSE(buf2.empty());
buf1.Clear();
EXPECT_EQ(buf1.size(), 0u);
EXPECT_EQ(buf1.capacity(), 0u);
EXPECT_EQ(buf1.data(), nullptr);
EXPECT_TRUE(buf1.empty());
}
TEST(BufferTest, TestSwap) {
Buffer buf1(kTestData, 3);
Buffer buf2(kTestData, 6, 40);
uint8_t* data1 = buf1.data();
uint8_t* data2 = buf2.data();
using std::swap;
swap(buf1, buf2);
EXPECT_EQ(buf1.size(), 6u);
EXPECT_EQ(buf1.capacity(), 40u);
EXPECT_EQ(buf1.data(), data2);
EXPECT_FALSE(buf1.empty());
EXPECT_EQ(buf2.size(), 3u);
EXPECT_EQ(buf2.capacity(), 3u);
EXPECT_EQ(buf2.data(), data1);
EXPECT_FALSE(buf2.empty());
}
TEST(BufferTest, TestClear) {
Buffer buf;
buf.SetData(kTestData, 15);
EXPECT_EQ(buf.size(), 15u);
EXPECT_EQ(buf.capacity(), 15u);
EXPECT_FALSE(buf.empty());
const char *data = buf.data<char>();
buf.Clear();
EXPECT_EQ(buf.size(), 0u);
EXPECT_EQ(buf.capacity(), 15u); // Hasn't shrunk.
EXPECT_EQ(buf.data<char>(), data); // No reallocation.
EXPECT_TRUE(buf.empty());
}
TEST(BufferTest, TestLambdaSetAppend) {
auto setter = [] (rtc::ArrayView<uint8_t> av) {
for (int i = 0; i != 15; ++i)
av[i] = kTestData[i];
return 15;
};
Buffer buf1;
buf1.SetData(kTestData, 15);
buf1.AppendData(kTestData, 15);
Buffer buf2;
EXPECT_EQ(buf2.SetData(15, setter), 15u);
EXPECT_EQ(buf2.AppendData(15, setter), 15u);
EXPECT_EQ(buf1, buf2);
EXPECT_EQ(buf1.capacity(), buf2.capacity());
EXPECT_FALSE(buf1.empty());
EXPECT_FALSE(buf2.empty());
}
TEST(BufferTest, TestLambdaSetAppendSigned) {
auto setter = [] (rtc::ArrayView<int8_t> av) {
for (int i = 0; i != 15; ++i)
av[i] = kTestData[i];
return 15;
};
Buffer buf1;
buf1.SetData(kTestData, 15);
buf1.AppendData(kTestData, 15);
Buffer buf2;
EXPECT_EQ(buf2.SetData<int8_t>(15, setter), 15u);
EXPECT_EQ(buf2.AppendData<int8_t>(15, setter), 15u);
EXPECT_EQ(buf1, buf2);
EXPECT_EQ(buf1.capacity(), buf2.capacity());
EXPECT_FALSE(buf1.empty());
EXPECT_FALSE(buf2.empty());
}
TEST(BufferTest, TestLambdaAppendEmpty) {
auto setter = [] (rtc::ArrayView<uint8_t> av) {
for (int i = 0; i != 15; ++i)
av[i] = kTestData[i];
return 15;
};
Buffer buf1;
buf1.SetData(kTestData, 15);
Buffer buf2;
EXPECT_EQ(buf2.AppendData(15, setter), 15u);
EXPECT_EQ(buf1, buf2);
EXPECT_EQ(buf1.capacity(), buf2.capacity());
EXPECT_FALSE(buf1.empty());
EXPECT_FALSE(buf2.empty());
}
TEST(BufferTest, TestLambdaAppendPartial) {
auto setter = [] (rtc::ArrayView<uint8_t> av) {
for (int i = 0; i != 7; ++i)
av[i] = kTestData[i];
return 7;
};
Buffer buf;
EXPECT_EQ(buf.AppendData(15, setter), 7u);
EXPECT_EQ(buf.size(), 7u); // Size is exactly what we wrote.
EXPECT_GE(buf.capacity(), 7u); // Capacity is valid.
EXPECT_NE(buf.data<char>(), nullptr); // Data is actually stored.
EXPECT_FALSE(buf.empty());
}
TEST(BufferTest, TestMutableLambdaSetAppend) {
uint8_t magic_number = 17;
auto setter = [magic_number] (rtc::ArrayView<uint8_t> av) mutable {
for (int i = 0; i != 15; ++i) {
av[i] = magic_number;
++magic_number;
}
return 15;
};
EXPECT_EQ(magic_number, 17);
Buffer buf;
EXPECT_EQ(buf.SetData(15, setter), 15u);
EXPECT_EQ(buf.AppendData(15, setter), 15u);
EXPECT_EQ(buf.size(), 30u); // Size is exactly what we wrote.
EXPECT_GE(buf.capacity(), 30u); // Capacity is valid.
EXPECT_NE(buf.data<char>(), nullptr); // Data is actually stored.
EXPECT_FALSE(buf.empty());
for (uint8_t i = 0; i != buf.size(); ++i) {
EXPECT_EQ(buf.data()[i], magic_number + i);
}
}
TEST(BufferTest, TestBracketRead) {
Buffer buf(kTestData, 7);
EXPECT_EQ(buf.size(), 7u);
EXPECT_EQ(buf.capacity(), 7u);
EXPECT_NE(buf.data(), nullptr);
EXPECT_FALSE(buf.empty());
for (size_t i = 0; i != 7u; ++i) {
EXPECT_EQ(buf[i], kTestData[i]);
}
}
TEST(BufferTest, TestBracketReadConst) {
Buffer buf(kTestData, 7);
EXPECT_EQ(buf.size(), 7u);
EXPECT_EQ(buf.capacity(), 7u);
EXPECT_NE(buf.data(), nullptr);
EXPECT_FALSE(buf.empty());
const Buffer& cbuf = buf;
for (size_t i = 0; i != 7u; ++i) {
EXPECT_EQ(cbuf[i], kTestData[i]);
}
}
TEST(BufferTest, TestBracketWrite) {
Buffer buf(7);
EXPECT_EQ(buf.size(), 7u);
EXPECT_EQ(buf.capacity(), 7u);
EXPECT_NE(buf.data(), nullptr);
EXPECT_FALSE(buf.empty());
for (size_t i = 0; i != 7u; ++i) {
buf[i] = kTestData[i];
}
for (size_t i = 0; i != 7u; ++i) {
EXPECT_EQ(buf[i], kTestData[i]);
}
}
TEST(BufferTest, TestBeginEnd) {
const Buffer cbuf(kTestData);
Buffer buf(kTestData);
auto b1 = cbuf.begin();
for (auto& x : buf) {
EXPECT_EQ(*b1, x);
++b1;
++x;
}
EXPECT_EQ(cbuf.end(), b1);
auto b2 = buf.begin();
for (auto& y : cbuf) {
EXPECT_EQ(*b2, y + 1);
++b2;
}
EXPECT_EQ(buf.end(), b2);
}
TEST(BufferTest, TestInt16) {
static constexpr int16_t test_data[] = {14, 15, 16, 17, 18};
BufferT<int16_t> buf(test_data);
EXPECT_EQ(buf.size(), 5u);
EXPECT_EQ(buf.capacity(), 5u);
EXPECT_NE(buf.data(), nullptr);
EXPECT_FALSE(buf.empty());
for (size_t i = 0; i != buf.size(); ++i) {
EXPECT_EQ(test_data[i], buf[i]);
}
BufferT<int16_t> buf2(test_data);
EXPECT_EQ(buf, buf2);
buf2[0] = 9;
EXPECT_NE(buf, buf2);
}
TEST(BufferTest, TestFloat) {
static constexpr float test_data[] = {14, 15, 16, 17, 18};
BufferT<float> buf;
EXPECT_EQ(buf.size(), 0u);
EXPECT_EQ(buf.capacity(), 0u);
EXPECT_EQ(buf.data(), nullptr);
EXPECT_TRUE(buf.empty());
buf.SetData(test_data);
EXPECT_EQ(buf.size(), 5u);
EXPECT_EQ(buf.capacity(), 5u);
EXPECT_NE(buf.data(), nullptr);
EXPECT_FALSE(buf.empty());
float* p1 = buf.data();
while (buf.data() == p1) {
buf.AppendData(test_data);
}
EXPECT_EQ(buf.size(), buf.capacity());
EXPECT_GT(buf.size(), 5u);
EXPECT_EQ(buf.size() % 5, 0u);
EXPECT_NE(buf.data(), nullptr);
for (size_t i = 0; i != buf.size(); ++i) {
EXPECT_EQ(test_data[i % 5], buf[i]);
}
}
TEST(BufferTest, TestStruct) {
struct BloodStone {
bool blood;
const char* stone;
};
BufferT<BloodStone> buf(4);
EXPECT_EQ(buf.size(), 4u);
EXPECT_EQ(buf.capacity(), 4u);
EXPECT_NE(buf.data(), nullptr);
EXPECT_FALSE(buf.empty());
BufferT<BloodStone*> buf2(4);
for (size_t i = 0; i < buf2.size(); ++i) {
buf2[i] = &buf[i];
}
static const char kObsidian[] = "obsidian";
buf2[2]->stone = kObsidian;
EXPECT_EQ(kObsidian, buf[2].stone);
}
} // namespace rtc

View File

@ -0,0 +1,94 @@
/*
* 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.
*/
#include "webrtc/base/bufferqueue.h"
#include <algorithm>
namespace rtc {
BufferQueue::BufferQueue(size_t capacity, size_t default_size)
: capacity_(capacity), default_size_(default_size) {
}
BufferQueue::~BufferQueue() {
CritScope cs(&crit_);
for (Buffer* buffer : queue_) {
delete buffer;
}
for (Buffer* buffer : free_list_) {
delete buffer;
}
}
size_t BufferQueue::size() const {
CritScope cs(&crit_);
return queue_.size();
}
void BufferQueue::Clear() {
CritScope cs(&crit_);
while (!queue_.empty()) {
free_list_.push_back(queue_.front());
queue_.pop_front();
}
}
bool BufferQueue::ReadFront(void* buffer, size_t bytes, size_t* bytes_read) {
CritScope cs(&crit_);
if (queue_.empty()) {
return false;
}
bool was_writable = queue_.size() < capacity_;
Buffer* packet = queue_.front();
queue_.pop_front();
bytes = std::min(bytes, packet->size());
memcpy(buffer, packet->data(), bytes);
if (bytes_read) {
*bytes_read = bytes;
}
free_list_.push_back(packet);
if (!was_writable) {
NotifyWritableForTest();
}
return true;
}
bool BufferQueue::WriteBack(const void* buffer, size_t bytes,
size_t* bytes_written) {
CritScope cs(&crit_);
if (queue_.size() == capacity_) {
return false;
}
bool was_readable = !queue_.empty();
Buffer* packet;
if (!free_list_.empty()) {
packet = free_list_.back();
free_list_.pop_back();
} else {
packet = new Buffer(bytes, default_size_);
}
packet->SetData(static_cast<const uint8_t*>(buffer), bytes);
if (bytes_written) {
*bytes_written = bytes;
}
queue_.push_back(packet);
if (!was_readable) {
NotifyReadableForTest();
}
return true;
}
} // namespace rtc

View File

@ -0,0 +1,61 @@
/*
* 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_RTC_BASE_BUFFERQUEUE_H_
#define WEBRTC_RTC_BASE_BUFFERQUEUE_H_
#include <deque>
#include <vector>
#include "webrtc/base/buffer.h"
#include "webrtc/base/constructormagic.h"
#include "webrtc/base/criticalsection.h"
namespace rtc {
class BufferQueue {
public:
// Creates a buffer queue with a given capacity and default buffer size.
BufferQueue(size_t capacity, size_t default_size);
virtual ~BufferQueue();
// Return number of queued buffers.
size_t size() const;
// Clear the BufferQueue by moving all Buffers from |queue_| to |free_list_|.
void Clear();
// ReadFront will only read one buffer at a time and will truncate buffers
// that don't fit in the passed memory.
// Returns true unless no data could be returned.
bool ReadFront(void* data, size_t bytes, size_t* bytes_read);
// WriteBack always writes either the complete memory or nothing.
// Returns true unless no data could be written.
bool WriteBack(const void* data, size_t bytes, size_t* bytes_written);
protected:
// These methods are called when the state of the queue changes.
virtual void NotifyReadableForTest() {}
virtual void NotifyWritableForTest() {}
private:
size_t capacity_;
size_t default_size_;
CriticalSection crit_;
std::deque<Buffer*> queue_ GUARDED_BY(crit_);
std::vector<Buffer*> free_list_ GUARDED_BY(crit_);
RTC_DISALLOW_COPY_AND_ASSIGN(BufferQueue);
};
} // namespace rtc
#endif // WEBRTC_RTC_BASE_BUFFERQUEUE_H_

View File

@ -0,0 +1,86 @@
/*
* 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.
*/
#include "webrtc/base/bufferqueue.h"
#include "webrtc/base/gunit.h"
namespace rtc {
TEST(BufferQueueTest, TestAll) {
const size_t kSize = 16;
const char in[kSize * 2 + 1] = "0123456789ABCDEFGHIJKLMNOPQRSTUV";
char out[kSize * 2];
size_t bytes;
BufferQueue queue1(1, kSize);
BufferQueue queue2(2, kSize);
// The queue is initially empty.
EXPECT_EQ(0u, queue1.size());
EXPECT_FALSE(queue1.ReadFront(out, kSize, &bytes));
// A write should succeed.
EXPECT_TRUE(queue1.WriteBack(in, kSize, &bytes));
EXPECT_EQ(kSize, bytes);
EXPECT_EQ(1u, queue1.size());
// The queue is full now (only one buffer allowed).
EXPECT_FALSE(queue1.WriteBack(in, kSize, &bytes));
EXPECT_EQ(1u, queue1.size());
// Reading previously written buffer.
EXPECT_TRUE(queue1.ReadFront(out, kSize, &bytes));
EXPECT_EQ(kSize, bytes);
EXPECT_EQ(0, memcmp(in, out, kSize));
// The queue is empty again now.
EXPECT_FALSE(queue1.ReadFront(out, kSize, &bytes));
EXPECT_EQ(0u, queue1.size());
// Reading only returns available data.
EXPECT_TRUE(queue1.WriteBack(in, kSize, &bytes));
EXPECT_EQ(kSize, bytes);
EXPECT_EQ(1u, queue1.size());
EXPECT_TRUE(queue1.ReadFront(out, kSize * 2, &bytes));
EXPECT_EQ(kSize, bytes);
EXPECT_EQ(0, memcmp(in, out, kSize));
EXPECT_EQ(0u, queue1.size());
// Reading maintains buffer boundaries.
EXPECT_TRUE(queue2.WriteBack(in, kSize / 2, &bytes));
EXPECT_EQ(1u, queue2.size());
EXPECT_TRUE(queue2.WriteBack(in + kSize / 2, kSize / 2, &bytes));
EXPECT_EQ(2u, queue2.size());
EXPECT_TRUE(queue2.ReadFront(out, kSize, &bytes));
EXPECT_EQ(kSize / 2, bytes);
EXPECT_EQ(0, memcmp(in, out, kSize / 2));
EXPECT_EQ(1u, queue2.size());
EXPECT_TRUE(queue2.ReadFront(out, kSize, &bytes));
EXPECT_EQ(kSize / 2, bytes);
EXPECT_EQ(0, memcmp(in + kSize / 2, out, kSize / 2));
EXPECT_EQ(0u, queue2.size());
// Reading truncates buffers.
EXPECT_TRUE(queue2.WriteBack(in, kSize / 2, &bytes));
EXPECT_EQ(1u, queue2.size());
EXPECT_TRUE(queue2.WriteBack(in + kSize / 2, kSize / 2, &bytes));
EXPECT_EQ(2u, queue2.size());
// Read first packet partially in too-small buffer.
EXPECT_TRUE(queue2.ReadFront(out, kSize / 4, &bytes));
EXPECT_EQ(kSize / 4, bytes);
EXPECT_EQ(0, memcmp(in, out, kSize / 4));
EXPECT_EQ(1u, queue2.size());
// Remainder of first packet is truncated, reading starts with next packet.
EXPECT_TRUE(queue2.ReadFront(out, kSize, &bytes));
EXPECT_EQ(kSize / 2, bytes);
EXPECT_EQ(0, memcmp(in + kSize / 2, out, kSize / 2));
EXPECT_EQ(0u, queue2.size());
}
} // namespace rtc

View File

@ -0,0 +1,283 @@
/*
* 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.
*/
#include "webrtc/base/bytebuffer.h"
#include <string.h>
#include <algorithm>
#include "webrtc/base/basictypes.h"
#include "webrtc/base/byteorder.h"
namespace rtc {
static const int DEFAULT_SIZE = 4096;
ByteBufferWriter::ByteBufferWriter()
: ByteBuffer(ORDER_NETWORK) {
Construct(nullptr, DEFAULT_SIZE);
}
ByteBufferWriter::ByteBufferWriter(ByteOrder byte_order)
: ByteBuffer(byte_order) {
Construct(nullptr, DEFAULT_SIZE);
}
ByteBufferWriter::ByteBufferWriter(const char* bytes, size_t len)
: ByteBuffer(ORDER_NETWORK) {
Construct(bytes, len);
}
ByteBufferWriter::ByteBufferWriter(const char* bytes, size_t len,
ByteOrder byte_order)
: ByteBuffer(byte_order) {
Construct(bytes, len);
}
void ByteBufferWriter::Construct(const char* bytes, size_t len) {
size_ = len;
bytes_ = new char[size_];
if (bytes) {
end_ = len;
memcpy(bytes_, bytes, end_);
} else {
end_ = 0;
}
}
ByteBufferWriter::~ByteBufferWriter() {
delete[] bytes_;
}
void ByteBufferWriter::WriteUInt8(uint8_t val) {
WriteBytes(reinterpret_cast<const char*>(&val), 1);
}
void ByteBufferWriter::WriteUInt16(uint16_t val) {
uint16_t v = (Order() == ORDER_NETWORK) ? HostToNetwork16(val) : val;
WriteBytes(reinterpret_cast<const char*>(&v), 2);
}
void ByteBufferWriter::WriteUInt24(uint32_t val) {
uint32_t v = (Order() == ORDER_NETWORK) ? HostToNetwork32(val) : val;
char* start = reinterpret_cast<char*>(&v);
if (Order() == ORDER_NETWORK || IsHostBigEndian()) {
++start;
}
WriteBytes(start, 3);
}
void ByteBufferWriter::WriteUInt32(uint32_t val) {
uint32_t v = (Order() == ORDER_NETWORK) ? HostToNetwork32(val) : val;
WriteBytes(reinterpret_cast<const char*>(&v), 4);
}
void ByteBufferWriter::WriteUInt64(uint64_t val) {
uint64_t v = (Order() == ORDER_NETWORK) ? HostToNetwork64(val) : val;
WriteBytes(reinterpret_cast<const char*>(&v), 8);
}
// Serializes an unsigned varint in the format described by
// https://developers.google.com/protocol-buffers/docs/encoding#varints
// with the caveat that integers are 64-bit, not 128-bit.
void ByteBufferWriter::WriteUVarint(uint64_t val) {
while (val >= 0x80) {
// Write 7 bits at a time, then set the msb to a continuation byte (msb=1).
char byte = static_cast<char>(val) | 0x80;
WriteBytes(&byte, 1);
val >>= 7;
}
char last_byte = static_cast<char>(val);
WriteBytes(&last_byte, 1);
}
void ByteBufferWriter::WriteString(const std::string& val) {
WriteBytes(val.c_str(), val.size());
}
void ByteBufferWriter::WriteBytes(const char* val, size_t len) {
memcpy(ReserveWriteBuffer(len), val, len);
}
char* ByteBufferWriter::ReserveWriteBuffer(size_t len) {
if (Length() + len > Capacity())
Resize(Length() + len);
char* start = bytes_ + end_;
end_ += len;
return start;
}
void ByteBufferWriter::Resize(size_t size) {
size_t len = std::min(end_, size);
if (size > size_) {
// Reallocate a larger buffer.
size_ = std::max(size, 3 * size_ / 2);
char* new_bytes = new char[size_];
memcpy(new_bytes, bytes_, len);
delete [] bytes_;
bytes_ = new_bytes;
}
end_ = len;
}
void ByteBufferWriter::Clear() {
memset(bytes_, 0, size_);
end_ = 0;
}
ByteBufferReader::ByteBufferReader(const char* bytes, size_t len)
: ByteBuffer(ORDER_NETWORK) {
Construct(bytes, len);
}
ByteBufferReader::ByteBufferReader(const char* bytes, size_t len,
ByteOrder byte_order)
: ByteBuffer(byte_order) {
Construct(bytes, len);
}
ByteBufferReader::ByteBufferReader(const char* bytes)
: ByteBuffer(ORDER_NETWORK) {
Construct(bytes, strlen(bytes));
}
ByteBufferReader::ByteBufferReader(const Buffer& buf)
: ByteBuffer(ORDER_NETWORK) {
Construct(buf.data<char>(), buf.size());
}
ByteBufferReader::ByteBufferReader(const ByteBufferWriter& buf)
: ByteBuffer(buf.Order()) {
Construct(buf.Data(), buf.Length());
}
void ByteBufferReader::Construct(const char* bytes, size_t len) {
bytes_ = bytes;
size_ = len;
start_ = 0;
end_ = len;
}
bool ByteBufferReader::ReadUInt8(uint8_t* val) {
if (!val) return false;
return ReadBytes(reinterpret_cast<char*>(val), 1);
}
bool ByteBufferReader::ReadUInt16(uint16_t* val) {
if (!val) return false;
uint16_t v;
if (!ReadBytes(reinterpret_cast<char*>(&v), 2)) {
return false;
} else {
*val = (Order() == ORDER_NETWORK) ? NetworkToHost16(v) : v;
return true;
}
}
bool ByteBufferReader::ReadUInt24(uint32_t* val) {
if (!val) return false;
uint32_t v = 0;
char* read_into = reinterpret_cast<char*>(&v);
if (Order() == ORDER_NETWORK || IsHostBigEndian()) {
++read_into;
}
if (!ReadBytes(read_into, 3)) {
return false;
} else {
*val = (Order() == ORDER_NETWORK) ? NetworkToHost32(v) : v;
return true;
}
}
bool ByteBufferReader::ReadUInt32(uint32_t* val) {
if (!val) return false;
uint32_t v;
if (!ReadBytes(reinterpret_cast<char*>(&v), 4)) {
return false;
} else {
*val = (Order() == ORDER_NETWORK) ? NetworkToHost32(v) : v;
return true;
}
}
bool ByteBufferReader::ReadUInt64(uint64_t* val) {
if (!val) return false;
uint64_t v;
if (!ReadBytes(reinterpret_cast<char*>(&v), 8)) {
return false;
} else {
*val = (Order() == ORDER_NETWORK) ? NetworkToHost64(v) : v;
return true;
}
}
bool ByteBufferReader::ReadUVarint(uint64_t* val) {
if (!val) {
return false;
}
// Integers are deserialized 7 bits at a time, with each byte having a
// continuation byte (msb=1) if there are more bytes to be read.
uint64_t v = 0;
for (int i = 0; i < 64; i += 7) {
char byte;
if (!ReadBytes(&byte, 1)) {
return false;
}
// Read the first 7 bits of the byte, then offset by bits read so far.
v |= (static_cast<uint64_t>(byte) & 0x7F) << i;
// True if the msb is not a continuation byte.
if (static_cast<uint64_t>(byte) < 0x80) {
*val = v;
return true;
}
}
return false;
}
bool ByteBufferReader::ReadString(std::string* val, size_t len) {
if (!val) return false;
if (len > Length()) {
return false;
} else {
val->append(bytes_ + start_, len);
start_ += len;
return true;
}
}
bool ByteBufferReader::ReadBytes(char* val, size_t len) {
if (len > Length()) {
return false;
} else {
memcpy(val, bytes_ + start_, len);
start_ += len;
return true;
}
}
bool ByteBufferReader::Consume(size_t size) {
if (size > Length())
return false;
start_ += size;
return true;
}
} // namespace rtc

View File

@ -0,0 +1,139 @@
/*
* 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_RTC_BASE_BYTEBUFFER_H_
#define WEBRTC_RTC_BASE_BYTEBUFFER_H_
#include <string>
#include "webrtc/base/basictypes.h"
#include "webrtc/base/buffer.h"
#include "webrtc/base/constructormagic.h"
namespace rtc {
class ByteBuffer {
public:
enum ByteOrder {
ORDER_NETWORK = 0, // Default, use network byte order (big endian).
ORDER_HOST, // Use the native order of the host.
};
explicit ByteBuffer(ByteOrder byte_order) : byte_order_(byte_order) {}
ByteOrder Order() const { return byte_order_; }
private:
ByteOrder byte_order_;
RTC_DISALLOW_COPY_AND_ASSIGN(ByteBuffer);
};
class ByteBufferWriter : public ByteBuffer {
public:
// |byte_order| defines order of bytes in the buffer.
ByteBufferWriter();
explicit ByteBufferWriter(ByteOrder byte_order);
ByteBufferWriter(const char* bytes, size_t len);
ByteBufferWriter(const char* bytes, size_t len, ByteOrder byte_order);
~ByteBufferWriter();
const char* Data() const { return bytes_; }
size_t Length() const { return end_; }
size_t Capacity() const { return size_; }
// Write value to the buffer. Resizes the buffer when it is
// neccessary.
void WriteUInt8(uint8_t val);
void WriteUInt16(uint16_t val);
void WriteUInt24(uint32_t val);
void WriteUInt32(uint32_t val);
void WriteUInt64(uint64_t val);
void WriteUVarint(uint64_t val);
void WriteString(const std::string& val);
void WriteBytes(const char* val, size_t len);
// Reserves the given number of bytes and returns a char* that can be written
// into. Useful for functions that require a char* buffer and not a
// ByteBufferWriter.
char* ReserveWriteBuffer(size_t len);
// Resize the buffer to the specified |size|.
void Resize(size_t size);
// Clears the contents of the buffer. After this, Length() will be 0.
void Clear();
private:
void Construct(const char* bytes, size_t size);
char* bytes_;
size_t size_;
size_t end_;
// There are sensible ways to define these, but they aren't needed in our code
// base.
RTC_DISALLOW_COPY_AND_ASSIGN(ByteBufferWriter);
};
// The ByteBufferReader references the passed data, i.e. the pointer must be
// valid during the lifetime of the reader.
class ByteBufferReader : public ByteBuffer {
public:
ByteBufferReader(const char* bytes, size_t len);
ByteBufferReader(const char* bytes, size_t len, ByteOrder byte_order);
// Initializes buffer from a zero-terminated string.
explicit ByteBufferReader(const char* bytes);
explicit ByteBufferReader(const Buffer& buf);
explicit ByteBufferReader(const ByteBufferWriter& buf);
// Returns start of unprocessed data.
const char* Data() const { return bytes_ + start_; }
// Returns number of unprocessed bytes.
size_t Length() const { return end_ - start_; }
// Read a next value from the buffer. Return false if there isn't
// enough data left for the specified type.
bool ReadUInt8(uint8_t* val);
bool ReadUInt16(uint16_t* val);
bool ReadUInt24(uint32_t* val);
bool ReadUInt32(uint32_t* val);
bool ReadUInt64(uint64_t* val);
bool ReadUVarint(uint64_t* val);
bool ReadBytes(char* val, size_t len);
// Appends next |len| bytes from the buffer to |val|. Returns false
// if there is less than |len| bytes left.
bool ReadString(std::string* val, size_t len);
// Moves current position |size| bytes forward. Returns false if
// there is less than |size| bytes left in the buffer. Consume doesn't
// permanently remove data, so remembered read positions are still valid
// after this call.
bool Consume(size_t size);
private:
void Construct(const char* bytes, size_t size);
const char* bytes_;
size_t size_;
size_t start_;
size_t end_;
RTC_DISALLOW_COPY_AND_ASSIGN(ByteBufferReader);
};
} // namespace rtc
#endif // WEBRTC_RTC_BASE_BYTEBUFFER_H_

View File

@ -0,0 +1,258 @@
/*
* 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.
*/
#include "webrtc/base/arraysize.h"
#include "webrtc/base/bytebuffer.h"
#include "webrtc/base/byteorder.h"
#include "webrtc/base/gunit.h"
namespace rtc {
TEST(ByteBufferTest, TestByteOrder) {
uint16_t n16 = 1;
uint32_t n32 = 1;
uint64_t n64 = 1;
EXPECT_EQ(n16, NetworkToHost16(HostToNetwork16(n16)));
EXPECT_EQ(n32, NetworkToHost32(HostToNetwork32(n32)));
EXPECT_EQ(n64, NetworkToHost64(HostToNetwork64(n64)));
if (IsHostBigEndian()) {
// The host is the network (big) endian.
EXPECT_EQ(n16, HostToNetwork16(n16));
EXPECT_EQ(n32, HostToNetwork32(n32));
EXPECT_EQ(n64, HostToNetwork64(n64));
// GetBE converts big endian to little endian here.
EXPECT_EQ(n16 >> 8, GetBE16(&n16));
EXPECT_EQ(n32 >> 24, GetBE32(&n32));
EXPECT_EQ(n64 >> 56, GetBE64(&n64));
} else {
// The host is little endian.
EXPECT_NE(n16, HostToNetwork16(n16));
EXPECT_NE(n32, HostToNetwork32(n32));
EXPECT_NE(n64, HostToNetwork64(n64));
// GetBE converts little endian to big endian here.
EXPECT_EQ(GetBE16(&n16), HostToNetwork16(n16));
EXPECT_EQ(GetBE32(&n32), HostToNetwork32(n32));
EXPECT_EQ(GetBE64(&n64), HostToNetwork64(n64));
// GetBE converts little endian to big endian here.
EXPECT_EQ(n16 << 8, GetBE16(&n16));
EXPECT_EQ(n32 << 24, GetBE32(&n32));
EXPECT_EQ(n64 << 56, GetBE64(&n64));
}
}
TEST(ByteBufferTest, TestBufferLength) {
ByteBufferWriter buffer;
size_t size = 0;
EXPECT_EQ(size, buffer.Length());
buffer.WriteUInt8(1);
++size;
EXPECT_EQ(size, buffer.Length());
buffer.WriteUInt16(1);
size += 2;
EXPECT_EQ(size, buffer.Length());
buffer.WriteUInt24(1);
size += 3;
EXPECT_EQ(size, buffer.Length());
buffer.WriteUInt32(1);
size += 4;
EXPECT_EQ(size, buffer.Length());
buffer.WriteUInt64(1);
size += 8;
EXPECT_EQ(size, buffer.Length());
}
TEST(ByteBufferTest, TestReadWriteBuffer) {
ByteBufferWriter::ByteOrder orders[2] = { ByteBufferWriter::ORDER_HOST,
ByteBufferWriter::ORDER_NETWORK };
for (size_t i = 0; i < arraysize(orders); i++) {
ByteBufferWriter buffer(orders[i]);
EXPECT_EQ(orders[i], buffer.Order());
ByteBufferReader read_buf(nullptr, 0, orders[i]);
EXPECT_EQ(orders[i], read_buf.Order());
uint8_t ru8;
EXPECT_FALSE(read_buf.ReadUInt8(&ru8));
// Write and read uint8_t.
uint8_t wu8 = 1;
buffer.WriteUInt8(wu8);
ByteBufferReader read_buf1(buffer.Data(), buffer.Length(), orders[i]);
EXPECT_TRUE(read_buf1.ReadUInt8(&ru8));
EXPECT_EQ(wu8, ru8);
EXPECT_EQ(0U, read_buf1.Length());
buffer.Clear();
// Write and read uint16_t.
uint16_t wu16 = (1 << 8) + 1;
buffer.WriteUInt16(wu16);
ByteBufferReader read_buf2(buffer.Data(), buffer.Length(), orders[i]);
uint16_t ru16;
EXPECT_TRUE(read_buf2.ReadUInt16(&ru16));
EXPECT_EQ(wu16, ru16);
EXPECT_EQ(0U, read_buf2.Length());
buffer.Clear();
// Write and read uint24.
uint32_t wu24 = (3 << 16) + (2 << 8) + 1;
buffer.WriteUInt24(wu24);
ByteBufferReader read_buf3(buffer.Data(), buffer.Length(), orders[i]);
uint32_t ru24;
EXPECT_TRUE(read_buf3.ReadUInt24(&ru24));
EXPECT_EQ(wu24, ru24);
EXPECT_EQ(0U, read_buf3.Length());
buffer.Clear();
// Write and read uint32_t.
uint32_t wu32 = (4 << 24) + (3 << 16) + (2 << 8) + 1;
buffer.WriteUInt32(wu32);
ByteBufferReader read_buf4(buffer.Data(), buffer.Length(), orders[i]);
uint32_t ru32;
EXPECT_TRUE(read_buf4.ReadUInt32(&ru32));
EXPECT_EQ(wu32, ru32);
EXPECT_EQ(0U, read_buf3.Length());
buffer.Clear();
// Write and read uint64_t.
uint32_t another32 = (8 << 24) + (7 << 16) + (6 << 8) + 5;
uint64_t wu64 = (static_cast<uint64_t>(another32) << 32) + wu32;
buffer.WriteUInt64(wu64);
ByteBufferReader read_buf5(buffer.Data(), buffer.Length(), orders[i]);
uint64_t ru64;
EXPECT_TRUE(read_buf5.ReadUInt64(&ru64));
EXPECT_EQ(wu64, ru64);
EXPECT_EQ(0U, read_buf5.Length());
buffer.Clear();
// Write and read string.
std::string write_string("hello");
buffer.WriteString(write_string);
ByteBufferReader read_buf6(buffer.Data(), buffer.Length(), orders[i]);
std::string read_string;
EXPECT_TRUE(read_buf6.ReadString(&read_string, write_string.size()));
EXPECT_EQ(write_string, read_string);
EXPECT_EQ(0U, read_buf6.Length());
buffer.Clear();
// Write and read bytes
char write_bytes[] = "foo";
buffer.WriteBytes(write_bytes, 3);
ByteBufferReader read_buf7(buffer.Data(), buffer.Length(), orders[i]);
char read_bytes[3];
EXPECT_TRUE(read_buf7.ReadBytes(read_bytes, 3));
for (int i = 0; i < 3; ++i) {
EXPECT_EQ(write_bytes[i], read_bytes[i]);
}
EXPECT_EQ(0U, read_buf7.Length());
buffer.Clear();
// Write and read reserved buffer space
char* write_dst = buffer.ReserveWriteBuffer(3);
memcpy(write_dst, write_bytes, 3);
ByteBufferReader read_buf8(buffer.Data(), buffer.Length(), orders[i]);
memset(read_bytes, 0, 3);
EXPECT_TRUE(read_buf8.ReadBytes(read_bytes, 3));
for (int i = 0; i < 3; ++i) {
EXPECT_EQ(write_bytes[i], read_bytes[i]);
}
EXPECT_EQ(0U, read_buf8.Length());
buffer.Clear();
// Write and read in order.
buffer.WriteUInt8(wu8);
buffer.WriteUInt16(wu16);
buffer.WriteUInt24(wu24);
buffer.WriteUInt32(wu32);
buffer.WriteUInt64(wu64);
ByteBufferReader read_buf9(buffer.Data(), buffer.Length(), orders[i]);
EXPECT_TRUE(read_buf9.ReadUInt8(&ru8));
EXPECT_EQ(wu8, ru8);
EXPECT_TRUE(read_buf9.ReadUInt16(&ru16));
EXPECT_EQ(wu16, ru16);
EXPECT_TRUE(read_buf9.ReadUInt24(&ru24));
EXPECT_EQ(wu24, ru24);
EXPECT_TRUE(read_buf9.ReadUInt32(&ru32));
EXPECT_EQ(wu32, ru32);
EXPECT_TRUE(read_buf9.ReadUInt64(&ru64));
EXPECT_EQ(wu64, ru64);
EXPECT_EQ(0U, read_buf9.Length());
buffer.Clear();
}
}
TEST(ByteBufferTest, TestReadWriteUVarint) {
ByteBufferWriter::ByteOrder orders[2] = {ByteBufferWriter::ORDER_HOST,
ByteBufferWriter::ORDER_NETWORK};
for (ByteBufferWriter::ByteOrder& order : orders) {
ByteBufferWriter write_buffer(order);
size_t size = 0;
EXPECT_EQ(size, write_buffer.Length());
write_buffer.WriteUVarint(1u);
++size;
EXPECT_EQ(size, write_buffer.Length());
write_buffer.WriteUVarint(2u);
++size;
EXPECT_EQ(size, write_buffer.Length());
write_buffer.WriteUVarint(27u);
++size;
EXPECT_EQ(size, write_buffer.Length());
write_buffer.WriteUVarint(149u);
size += 2;
EXPECT_EQ(size, write_buffer.Length());
write_buffer.WriteUVarint(68719476736u);
size += 6;
EXPECT_EQ(size, write_buffer.Length());
ByteBufferReader read_buffer(write_buffer.Data(), write_buffer.Length(),
order);
EXPECT_EQ(size, read_buffer.Length());
uint64_t val1, val2, val3, val4, val5;
ASSERT_TRUE(read_buffer.ReadUVarint(&val1));
EXPECT_EQ(1u, val1);
--size;
EXPECT_EQ(size, read_buffer.Length());
ASSERT_TRUE(read_buffer.ReadUVarint(&val2));
EXPECT_EQ(2u, val2);
--size;
EXPECT_EQ(size, read_buffer.Length());
ASSERT_TRUE(read_buffer.ReadUVarint(&val3));
EXPECT_EQ(27u, val3);
--size;
EXPECT_EQ(size, read_buffer.Length());
ASSERT_TRUE(read_buffer.ReadUVarint(&val4));
EXPECT_EQ(149u, val4);
size -= 2;
EXPECT_EQ(size, read_buffer.Length());
ASSERT_TRUE(read_buffer.ReadUVarint(&val5));
EXPECT_EQ(68719476736u, val5);
size -= 6;
EXPECT_EQ(size, read_buffer.Length());
}
}
} // namespace rtc

178
webrtc/rtc_base/byteorder.h Normal file
View File

@ -0,0 +1,178 @@
/*
* 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_RTC_BASE_BYTEORDER_H_
#define WEBRTC_RTC_BASE_BYTEORDER_H_
#if defined(WEBRTC_POSIX) && !defined(__native_client__)
#include <arpa/inet.h>
#endif
#include "webrtc/base/basictypes.h"
#if defined(WEBRTC_MAC)
#include <libkern/OSByteOrder.h>
#define htobe16(v) OSSwapHostToBigInt16(v)
#define htobe32(v) OSSwapHostToBigInt32(v)
#define htobe64(v) OSSwapHostToBigInt64(v)
#define be16toh(v) OSSwapBigToHostInt16(v)
#define be32toh(v) OSSwapBigToHostInt32(v)
#define be64toh(v) OSSwapBigToHostInt64(v)
#define htole16(v) OSSwapHostToLittleInt16(v)
#define htole32(v) OSSwapHostToLittleInt32(v)
#define htole64(v) OSSwapHostToLittleInt64(v)
#define le16toh(v) OSSwapLittleToHostInt16(v)
#define le32toh(v) OSSwapLittleToHostInt32(v)
#define le64toh(v) OSSwapLittleToHostInt64(v)
#elif defined(WEBRTC_WIN) || defined(__native_client__)
#if defined(WEBRTC_WIN)
#include <stdlib.h>
#include <winsock2.h>
#else
#include <netinet/in.h>
#endif
#define htobe16(v) htons(v)
#define htobe32(v) htonl(v)
#define be16toh(v) ntohs(v)
#define be32toh(v) ntohl(v)
#if defined(WEBRTC_WIN)
#define htobe64(v) htonll(v)
#define be64toh(v) ntohll(v)
#endif
#if defined(RTC_ARCH_CPU_LITTLE_ENDIAN)
#define htole16(v) (v)
#define htole32(v) (v)
#define htole64(v) (v)
#define le16toh(v) (v)
#define le32toh(v) (v)
#define le64toh(v) (v)
#if defined(__native_client__)
#define htobe64(v) __builtin_bswap64(v)
#define be64toh(v) __builtin_bswap64(v)
#endif
#elif defined(RTC_ARCH_CPU_BIG_ENDIAN)
#define htole16(v) __builtin_bswap16(v)
#define htole32(v) __builtin_bswap32(v)
#define htole64(v) __builtin_bswap64(v)
#define le16toh(v) __builtin_bswap16(v)
#define le32toh(v) __builtin_bswap32(v)
#define le64toh(v) __builtin_bswap64(v)
#if defined(__native_client__)
#define htobe64(v) (v)
#define be64toh(v) (v)
#endif
#else
#error RTC_ARCH_CPU_BIG_ENDIAN or RTC_ARCH_CPU_LITTLE_ENDIAN must be defined.
#endif // defined(RTC_ARCH_CPU_LITTLE_ENDIAN)
#elif defined(WEBRTC_POSIX)
#include <endian.h>
#endif
namespace rtc {
// Reading and writing of little and big-endian numbers from memory
inline void Set8(void* memory, size_t offset, uint8_t v) {
static_cast<uint8_t*>(memory)[offset] = v;
}
inline uint8_t Get8(const void* memory, size_t offset) {
return static_cast<const uint8_t*>(memory)[offset];
}
inline void SetBE16(void* memory, uint16_t v) {
*static_cast<uint16_t*>(memory) = htobe16(v);
}
inline void SetBE32(void* memory, uint32_t v) {
*static_cast<uint32_t*>(memory) = htobe32(v);
}
inline void SetBE64(void* memory, uint64_t v) {
*static_cast<uint64_t*>(memory) = htobe64(v);
}
inline uint16_t GetBE16(const void* memory) {
return be16toh(*static_cast<const uint16_t*>(memory));
}
inline uint32_t GetBE32(const void* memory) {
return be32toh(*static_cast<const uint32_t*>(memory));
}
inline uint64_t GetBE64(const void* memory) {
return be64toh(*static_cast<const uint64_t*>(memory));
}
inline void SetLE16(void* memory, uint16_t v) {
*static_cast<uint16_t*>(memory) = htole16(v);
}
inline void SetLE32(void* memory, uint32_t v) {
*static_cast<uint32_t*>(memory) = htole32(v);
}
inline void SetLE64(void* memory, uint64_t v) {
*static_cast<uint64_t*>(memory) = htole64(v);
}
inline uint16_t GetLE16(const void* memory) {
return le16toh(*static_cast<const uint16_t*>(memory));
}
inline uint32_t GetLE32(const void* memory) {
return le32toh(*static_cast<const uint32_t*>(memory));
}
inline uint64_t GetLE64(const void* memory) {
return le64toh(*static_cast<const uint64_t*>(memory));
}
// Check if the current host is big endian.
inline bool IsHostBigEndian() {
#if defined(RTC_ARCH_CPU_BIG_ENDIAN)
return true;
#else
return false;
#endif
}
inline uint16_t HostToNetwork16(uint16_t n) {
return htobe16(n);
}
inline uint32_t HostToNetwork32(uint32_t n) {
return htobe32(n);
}
inline uint64_t HostToNetwork64(uint64_t n) {
return htobe64(n);
}
inline uint16_t NetworkToHost16(uint16_t n) {
return be16toh(n);
}
inline uint32_t NetworkToHost32(uint32_t n) {
return be32toh(n);
}
inline uint64_t NetworkToHost64(uint64_t n) {
return be64toh(n);
}
} // namespace rtc
#endif // WEBRTC_RTC_BASE_BYTEORDER_H_

View File

@ -0,0 +1,83 @@
/*
* Copyright 2012 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.
*/
#include <stdint.h>
#include "webrtc/base/byteorder.h"
#include "webrtc/base/gunit.h"
namespace rtc {
// Test memory set functions put values into memory in expected order.
TEST(ByteOrderTest, TestSet) {
uint8_t buf[8] = {0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u};
Set8(buf, 0, 0xfb);
Set8(buf, 1, 0x12);
EXPECT_EQ(0xfb, buf[0]);
EXPECT_EQ(0x12, buf[1]);
SetBE16(buf, 0x1234);
EXPECT_EQ(0x12, buf[0]);
EXPECT_EQ(0x34, buf[1]);
SetLE16(buf, 0x1234);
EXPECT_EQ(0x34, buf[0]);
EXPECT_EQ(0x12, buf[1]);
SetBE32(buf, 0x12345678);
EXPECT_EQ(0x12, buf[0]);
EXPECT_EQ(0x34, buf[1]);
EXPECT_EQ(0x56, buf[2]);
EXPECT_EQ(0x78, buf[3]);
SetLE32(buf, 0x12345678);
EXPECT_EQ(0x78, buf[0]);
EXPECT_EQ(0x56, buf[1]);
EXPECT_EQ(0x34, buf[2]);
EXPECT_EQ(0x12, buf[3]);
SetBE64(buf, UINT64_C(0x0123456789abcdef));
EXPECT_EQ(0x01, buf[0]);
EXPECT_EQ(0x23, buf[1]);
EXPECT_EQ(0x45, buf[2]);
EXPECT_EQ(0x67, buf[3]);
EXPECT_EQ(0x89, buf[4]);
EXPECT_EQ(0xab, buf[5]);
EXPECT_EQ(0xcd, buf[6]);
EXPECT_EQ(0xef, buf[7]);
SetLE64(buf, UINT64_C(0x0123456789abcdef));
EXPECT_EQ(0xef, buf[0]);
EXPECT_EQ(0xcd, buf[1]);
EXPECT_EQ(0xab, buf[2]);
EXPECT_EQ(0x89, buf[3]);
EXPECT_EQ(0x67, buf[4]);
EXPECT_EQ(0x45, buf[5]);
EXPECT_EQ(0x23, buf[6]);
EXPECT_EQ(0x01, buf[7]);
}
// Test memory get functions get values from memory in expected order.
TEST(ByteOrderTest, TestGet) {
uint8_t buf[8];
buf[0] = 0x01u;
buf[1] = 0x23u;
buf[2] = 0x45u;
buf[3] = 0x67u;
buf[4] = 0x89u;
buf[5] = 0xabu;
buf[6] = 0xcdu;
buf[7] = 0xefu;
EXPECT_EQ(0x01u, Get8(buf, 0));
EXPECT_EQ(0x23u, Get8(buf, 1));
EXPECT_EQ(0x0123u, GetBE16(buf));
EXPECT_EQ(0x2301u, GetLE16(buf));
EXPECT_EQ(0x01234567u, GetBE32(buf));
EXPECT_EQ(0x67452301u, GetLE32(buf));
EXPECT_EQ(UINT64_C(0x0123456789abcdef), GetBE64(buf));
EXPECT_EQ(UINT64_C(0xefcdab8967452301), GetLE64(buf));
}
} // namespace rtc

260
webrtc/rtc_base/callback.h Normal file
View File

@ -0,0 +1,260 @@
// This file was GENERATED by command:
// pump.py callback.h.pump
// DO NOT EDIT BY HAND!!!
/*
* Copyright 2012 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.
*/
// To generate callback.h from callback.h.pump, execute:
// /home/build/google3/third_party/gtest/scripts/pump.py callback.h.pump
// Callbacks are callable object containers. They can hold a function pointer
// or a function object and behave like a value type. Internally, data is
// reference-counted, making copies and pass-by-value inexpensive.
//
// Callbacks are typed using template arguments. The format is:
// CallbackN<ReturnType, ParamType1, ..., ParamTypeN>
// where N is the number of arguments supplied to the callable object.
// Callbacks are invoked using operator(), just like a function or a function
// object. Default-constructed callbacks are "empty," and executing an empty
// callback does nothing. A callback can be made empty by assigning it from
// a default-constructed callback.
//
// Callbacks are similar in purpose to std::function (which isn't available on
// all platforms we support) and a lightweight alternative to sigslots. Since
// they effectively hide the type of the object they call, they're useful in
// breaking dependencies between objects that need to interact with one another.
// Notably, they can hold the results of Bind(), std::bind*, etc, without
// needing
// to know the resulting object type of those calls.
//
// Sigslots, on the other hand, provide a fuller feature set, such as multiple
// subscriptions to a signal, optional thread-safety, and lifetime tracking of
// slots. When these features are needed, choose sigslots.
//
// Example:
// int sqr(int x) { return x * x; }
// struct AddK {
// int k;
// int operator()(int x) const { return x + k; }
// } add_k = {5};
//
// Callback1<int, int> my_callback;
// cout << my_callback.empty() << endl; // true
//
// my_callback = Callback1<int, int>(&sqr);
// cout << my_callback.empty() << endl; // false
// cout << my_callback(3) << endl; // 9
//
// my_callback = Callback1<int, int>(add_k);
// cout << my_callback(10) << endl; // 15
//
// my_callback = Callback1<int, int>();
// cout << my_callback.empty() << endl; // true
#ifndef WEBRTC_RTC_BASE_CALLBACK_H_
#define WEBRTC_RTC_BASE_CALLBACK_H_
#include "webrtc/base/refcount.h"
#include "webrtc/base/scoped_ref_ptr.h"
namespace rtc {
template <class R>
class Callback0 {
public:
// Default copy operations are appropriate for this class.
Callback0() {}
template <class T> Callback0(const T& functor)
: helper_(new RefCountedObject< HelperImpl<T> >(functor)) {}
R operator()() {
if (empty())
return R();
return helper_->Run();
}
bool empty() const { return !helper_; }
private:
struct Helper : RefCountInterface {
virtual ~Helper() {}
virtual R Run() = 0;
};
template <class T> struct HelperImpl : Helper {
explicit HelperImpl(const T& functor) : functor_(functor) {}
virtual R Run() {
return functor_();
}
T functor_;
};
scoped_refptr<Helper> helper_;
};
template <class R,
class P1>
class Callback1 {
public:
// Default copy operations are appropriate for this class.
Callback1() {}
template <class T> Callback1(const T& functor)
: helper_(new RefCountedObject< HelperImpl<T> >(functor)) {}
R operator()(P1 p1) {
if (empty())
return R();
return helper_->Run(p1);
}
bool empty() const { return !helper_; }
private:
struct Helper : RefCountInterface {
virtual ~Helper() {}
virtual R Run(P1 p1) = 0;
};
template <class T> struct HelperImpl : Helper {
explicit HelperImpl(const T& functor) : functor_(functor) {}
virtual R Run(P1 p1) {
return functor_(p1);
}
T functor_;
};
scoped_refptr<Helper> helper_;
};
template <class R,
class P1,
class P2>
class Callback2 {
public:
// Default copy operations are appropriate for this class.
Callback2() {}
template <class T> Callback2(const T& functor)
: helper_(new RefCountedObject< HelperImpl<T> >(functor)) {}
R operator()(P1 p1, P2 p2) {
if (empty())
return R();
return helper_->Run(p1, p2);
}
bool empty() const { return !helper_; }
private:
struct Helper : RefCountInterface {
virtual ~Helper() {}
virtual R Run(P1 p1, P2 p2) = 0;
};
template <class T> struct HelperImpl : Helper {
explicit HelperImpl(const T& functor) : functor_(functor) {}
virtual R Run(P1 p1, P2 p2) {
return functor_(p1, p2);
}
T functor_;
};
scoped_refptr<Helper> helper_;
};
template <class R,
class P1,
class P2,
class P3>
class Callback3 {
public:
// Default copy operations are appropriate for this class.
Callback3() {}
template <class T> Callback3(const T& functor)
: helper_(new RefCountedObject< HelperImpl<T> >(functor)) {}
R operator()(P1 p1, P2 p2, P3 p3) {
if (empty())
return R();
return helper_->Run(p1, p2, p3);
}
bool empty() const { return !helper_; }
private:
struct Helper : RefCountInterface {
virtual ~Helper() {}
virtual R Run(P1 p1, P2 p2, P3 p3) = 0;
};
template <class T> struct HelperImpl : Helper {
explicit HelperImpl(const T& functor) : functor_(functor) {}
virtual R Run(P1 p1, P2 p2, P3 p3) {
return functor_(p1, p2, p3);
}
T functor_;
};
scoped_refptr<Helper> helper_;
};
template <class R,
class P1,
class P2,
class P3,
class P4>
class Callback4 {
public:
// Default copy operations are appropriate for this class.
Callback4() {}
template <class T> Callback4(const T& functor)
: helper_(new RefCountedObject< HelperImpl<T> >(functor)) {}
R operator()(P1 p1, P2 p2, P3 p3, P4 p4) {
if (empty())
return R();
return helper_->Run(p1, p2, p3, p4);
}
bool empty() const { return !helper_; }
private:
struct Helper : RefCountInterface {
virtual ~Helper() {}
virtual R Run(P1 p1, P2 p2, P3 p3, P4 p4) = 0;
};
template <class T> struct HelperImpl : Helper {
explicit HelperImpl(const T& functor) : functor_(functor) {}
virtual R Run(P1 p1, P2 p2, P3 p3, P4 p4) {
return functor_(p1, p2, p3, p4);
}
T functor_;
};
scoped_refptr<Helper> helper_;
};
template <class R,
class P1,
class P2,
class P3,
class P4,
class P5>
class Callback5 {
public:
// Default copy operations are appropriate for this class.
Callback5() {}
template <class T> Callback5(const T& functor)
: helper_(new RefCountedObject< HelperImpl<T> >(functor)) {}
R operator()(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) {
if (empty())
return R();
return helper_->Run(p1, p2, p3, p4, p5);
}
bool empty() const { return !helper_; }
private:
struct Helper : RefCountInterface {
virtual ~Helper() {}
virtual R Run(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) = 0;
};
template <class T> struct HelperImpl : Helper {
explicit HelperImpl(const T& functor) : functor_(functor) {}
virtual R Run(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) {
return functor_(p1, p2, p3, p4, p5);
}
T functor_;
};
scoped_refptr<Helper> helper_;
};
} // namespace rtc
#endif // WEBRTC_RTC_BASE_CALLBACK_H_

View File

@ -0,0 +1,103 @@
/*
* Copyright 2012 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.
*/
// To generate callback.h from callback.h.pump, execute:
// /home/build/google3/third_party/gtest/scripts/pump.py callback.h.pump
// Callbacks are callable object containers. They can hold a function pointer
// or a function object and behave like a value type. Internally, data is
// reference-counted, making copies and pass-by-value inexpensive.
//
// Callbacks are typed using template arguments. The format is:
// CallbackN<ReturnType, ParamType1, ..., ParamTypeN>
// where N is the number of arguments supplied to the callable object.
// Callbacks are invoked using operator(), just like a function or a function
// object. Default-constructed callbacks are "empty," and executing an empty
// callback does nothing. A callback can be made empty by assigning it from
// a default-constructed callback.
//
// Callbacks are similar in purpose to std::function (which isn't available on
// all platforms we support) and a lightweight alternative to sigslots. Since
// they effectively hide the type of the object they call, they're useful in
// breaking dependencies between objects that need to interact with one another.
// Notably, they can hold the results of Bind(), std::bind*, etc, without needing
// to know the resulting object type of those calls.
//
// Sigslots, on the other hand, provide a fuller feature set, such as multiple
// subscriptions to a signal, optional thread-safety, and lifetime tracking of
// slots. When these features are needed, choose sigslots.
//
// Example:
// int sqr(int x) { return x * x; }
// struct AddK {
// int k;
// int operator()(int x) const { return x + k; }
// } add_k = {5};
//
// Callback1<int, int> my_callback;
// cout << my_callback.empty() << endl; // true
//
// my_callback = Callback1<int, int>(&sqr);
// cout << my_callback.empty() << endl; // false
// cout << my_callback(3) << endl; // 9
//
// my_callback = Callback1<int, int>(add_k);
// cout << my_callback(10) << endl; // 15
//
// my_callback = Callback1<int, int>();
// cout << my_callback.empty() << endl; // true
#ifndef WEBRTC_RTC_BASE_CALLBACK_H_
#define WEBRTC_RTC_BASE_CALLBACK_H_
#include "webrtc/base/refcount.h"
#include "webrtc/base/scoped_ref_ptr.h"
namespace rtc {
$var n = 5
$range i 0..n
$for i [[
$range j 1..i
template <class R$for j [[,
class P$j]]>
class Callback$i {
public:
// Default copy operations are appropriate for this class.
Callback$i() {}
template <class T> Callback$i(const T& functor)
: helper_(new RefCountedObject< HelperImpl<T> >(functor)) {}
R operator()($for j , [[P$j p$j]]) {
if (empty())
return R();
return helper_->Run($for j , [[p$j]]);
}
bool empty() const { return !helper_; }
private:
struct Helper : RefCountInterface {
virtual ~Helper() {}
virtual R Run($for j , [[P$j p$j]]) = 0;
};
template <class T> struct HelperImpl : Helper {
explicit HelperImpl(const T& functor) : functor_(functor) {}
virtual R Run($for j , [[P$j p$j]]) {
return functor_($for j , [[p$j]]);
}
T functor_;
};
scoped_refptr<Helper> helper_;
};
]]
} // namespace rtc
#endif // WEBRTC_RTC_BASE_CALLBACK_H_

View File

@ -0,0 +1,140 @@
/*
* 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.
*/
#include "webrtc/base/bind.h"
#include "webrtc/base/callback.h"
#include "webrtc/base/gunit.h"
#include "webrtc/base/keep_ref_until_done.h"
#include "webrtc/base/refcount.h"
namespace rtc {
namespace {
void f() {}
int g() { return 42; }
int h(int x) { return x * x; }
void i(int& x) { x *= x; } // NOLINT: Testing refs
struct BindTester {
int a() { return 24; }
int b(int x) const { return x * x; }
};
class RefCountedBindTester : public RefCountInterface {
public:
RefCountedBindTester() : count_(0) {}
int AddRef() const override {
return ++count_;
}
int Release() const override {
return --count_;
}
int RefCount() const { return count_; }
private:
mutable int count_;
};
} // namespace
TEST(CallbackTest, VoidReturn) {
Callback0<void> cb;
EXPECT_TRUE(cb.empty());
cb(); // Executing an empty callback should not crash.
cb = Callback0<void>(&f);
EXPECT_FALSE(cb.empty());
cb();
}
TEST(CallbackTest, IntReturn) {
Callback0<int> cb;
EXPECT_TRUE(cb.empty());
cb = Callback0<int>(&g);
EXPECT_FALSE(cb.empty());
EXPECT_EQ(42, cb());
EXPECT_EQ(42, cb());
}
TEST(CallbackTest, OneParam) {
Callback1<int, int> cb1(&h);
EXPECT_FALSE(cb1.empty());
EXPECT_EQ(9, cb1(-3));
EXPECT_EQ(100, cb1(10));
// Try clearing a callback.
cb1 = Callback1<int, int>();
EXPECT_TRUE(cb1.empty());
// Try a callback with a ref parameter.
Callback1<void, int&> cb2(&i);
int x = 3;
cb2(x);
EXPECT_EQ(9, x);
cb2(x);
EXPECT_EQ(81, x);
}
TEST(CallbackTest, WithBind) {
BindTester t;
Callback0<int> cb1 = Bind(&BindTester::a, &t);
EXPECT_EQ(24, cb1());
EXPECT_EQ(24, cb1());
cb1 = Bind(&BindTester::b, &t, 10);
EXPECT_EQ(100, cb1());
EXPECT_EQ(100, cb1());
cb1 = Bind(&BindTester::b, &t, 5);
EXPECT_EQ(25, cb1());
EXPECT_EQ(25, cb1());
}
TEST(KeepRefUntilDoneTest, simple) {
RefCountedBindTester t;
EXPECT_EQ(0, t.RefCount());
{
Callback0<void> cb = KeepRefUntilDone(&t);
EXPECT_EQ(1, t.RefCount());
cb();
EXPECT_EQ(1, t.RefCount());
cb();
EXPECT_EQ(1, t.RefCount());
}
EXPECT_EQ(0, t.RefCount());
}
TEST(KeepRefUntilDoneTest, copy) {
RefCountedBindTester t;
EXPECT_EQ(0, t.RefCount());
Callback0<void> cb2;
{
Callback0<void> cb = KeepRefUntilDone(&t);
EXPECT_EQ(1, t.RefCount());
cb2 = cb;
}
EXPECT_EQ(1, t.RefCount());
cb2 = Callback0<void>();
EXPECT_EQ(0, t.RefCount());
}
TEST(KeepRefUntilDoneTest, scopedref) {
RefCountedBindTester t;
EXPECT_EQ(0, t.RefCount());
{
scoped_refptr<RefCountedBindTester> t_scoped_ref(&t);
Callback0<void> cb = KeepRefUntilDone(t_scoped_ref);
t_scoped_ref = nullptr;
EXPECT_EQ(1, t.RefCount());
cb();
EXPECT_EQ(1, t.RefCount());
}
EXPECT_EQ(0, t.RefCount());
}
} // namespace rtc

141
webrtc/rtc_base/checks.cc Normal file
View File

@ -0,0 +1,141 @@
/*
* Copyright 2006 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.
*/
// Most of this was borrowed (with minor modifications) from V8's and Chromium's
// src/base/logging.cc.
// Use the C++ version to provide __GLIBCXX__.
#include <cstdarg>
#include <cstdio>
#include <cstdlib>
#if defined(__GLIBCXX__) && !defined(__UCLIBC__)
#include <cxxabi.h>
#include <execinfo.h>
#endif
#if defined(WEBRTC_ANDROID)
#define RTC_LOG_TAG "rtc"
#include <android/log.h> // NOLINT
#endif
#if defined(WEBRTC_WIN)
#include <windows.h>
#endif
#include "webrtc/base/checks.h"
#include "webrtc/base/logging.h"
#if defined(_MSC_VER)
// Warning C4722: destructor never returns, potential memory leak.
// FatalMessage's dtor very intentionally aborts.
#pragma warning(disable:4722)
#endif
namespace rtc {
void VPrintError(const char* format, va_list args) {
#if defined(WEBRTC_ANDROID)
__android_log_vprint(ANDROID_LOG_ERROR, RTC_LOG_TAG, format, args);
#else
vfprintf(stderr, format, args);
#endif
}
void PrintError(const char* format, ...) {
va_list args;
va_start(args, format);
VPrintError(format, args);
va_end(args);
}
// TODO(ajm): This works on Mac (although the parsing fails) but I don't seem
// to get usable symbols on Linux. This is copied from V8. Chromium has a more
// advanced stace trace system; also more difficult to copy.
void DumpBacktrace() {
#if defined(__GLIBCXX__) && !defined(__UCLIBC__)
void* trace[100];
int size = backtrace(trace, sizeof(trace) / sizeof(*trace));
char** symbols = backtrace_symbols(trace, size);
PrintError("\n==== C stack trace ===============================\n\n");
if (size == 0) {
PrintError("(empty)\n");
} else if (symbols == nullptr) {
PrintError("(no symbols)\n");
} else {
for (int i = 1; i < size; ++i) {
char mangled[201];
if (sscanf(symbols[i], "%*[^(]%*[(]%200[^)+]", mangled) == 1) { // NOLINT
PrintError("%2d: ", i);
int status;
size_t length;
char* demangled =
abi::__cxa_demangle(mangled, nullptr, &length, &status);
PrintError("%s\n", demangled != nullptr ? demangled : mangled);
free(demangled);
} else {
// If parsing failed, at least print the unparsed symbol.
PrintError("%s\n", symbols[i]);
}
}
}
free(symbols);
#endif
}
FatalMessage::FatalMessage(const char* file, int line) {
Init(file, line);
}
FatalMessage::FatalMessage(const char* file, int line, std::string* result) {
Init(file, line);
stream_ << "Check failed: " << *result << std::endl << "# ";
delete result;
}
NO_RETURN FatalMessage::~FatalMessage() {
fflush(stdout);
fflush(stderr);
stream_ << std::endl << "#" << std::endl;
PrintError(stream_.str().c_str());
DumpBacktrace();
fflush(stderr);
abort();
}
void FatalMessage::Init(const char* file, int line) {
stream_ << std::endl << std::endl
<< "#" << std::endl
<< "# Fatal error in " << file << ", line " << line << std::endl
<< "# last system error: " << LAST_SYSTEM_ERROR << std::endl
<< "# ";
}
// MSVC doesn't like complex extern templates and DLLs.
#if !defined(COMPILER_MSVC)
// Explicit instantiations for commonly used comparisons.
template std::string* MakeCheckOpString<int, int>(
const int&, const int&, const char* names);
template std::string* MakeCheckOpString<unsigned long, unsigned long>(
const unsigned long&, const unsigned long&, const char* names);
template std::string* MakeCheckOpString<unsigned long, unsigned int>(
const unsigned long&, const unsigned int&, const char* names);
template std::string* MakeCheckOpString<unsigned int, unsigned long>(
const unsigned int&, const unsigned long&, const char* names);
template std::string* MakeCheckOpString<std::string, std::string>(
const std::string&, const std::string&, const char* name);
#endif
} // namespace rtc
// Function to call from the C version of the RTC_CHECK and RTC_DCHECK macros.
NO_RETURN void rtc_FatalMessage(const char* file, int line, const char* msg) {
rtc::FatalMessage(file, line).stream() << msg;
}

289
webrtc/rtc_base/checks.h Normal file
View File

@ -0,0 +1,289 @@
/*
* Copyright 2006 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_RTC_BASE_CHECKS_H_
#define WEBRTC_RTC_BASE_CHECKS_H_
#include "webrtc/typedefs.h"
// If you for some reson need to know if DCHECKs are on, test the value of
// RTC_DCHECK_IS_ON. (Test its value, not if it's defined; it'll always be
// defined, to either a true or a false value.)
#if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)
#define RTC_DCHECK_IS_ON 1
#else
#define RTC_DCHECK_IS_ON 0
#endif
#ifdef __cplusplus
extern "C" {
#endif
NO_RETURN void rtc_FatalMessage(const char* file, int line, const char* msg);
#ifdef __cplusplus
} // extern "C"
#endif
#ifdef __cplusplus
// C++ version.
#include <sstream>
#include <string>
#include "webrtc/base/safe_compare.h"
// The macros here print a message to stderr and abort under various
// conditions. All will accept additional stream messages. For example:
// RTC_DCHECK_EQ(foo, bar) << "I'm printed when foo != bar.";
//
// - RTC_CHECK(x) is an assertion that x is always true, and that if it isn't,
// it's better to terminate the process than to continue. During development,
// the reason that it's better to terminate might simply be that the error
// handling code isn't in place yet; in production, the reason might be that
// the author of the code truly believes that x will always be true, but that
// she recognizes that if she is wrong, abrupt and unpleasant process
// termination is still better than carrying on with the assumption violated.
//
// RTC_CHECK always evaluates its argument, so it's OK for x to have side
// effects.
//
// - RTC_DCHECK(x) is the same as RTC_CHECK(x)---an assertion that x is always
// true---except that x will only be evaluated in debug builds; in production
// builds, x is simply assumed to be true. This is useful if evaluating x is
// expensive and the expected cost of failing to detect the violated
// assumption is acceptable. You should not handle cases where a production
// build fails to spot a violated condition, even those that would result in
// crashes. If the code needs to cope with the error, make it cope, but don't
// call RTC_DCHECK; if the condition really can't occur, but you'd sleep
// better at night knowing that the process will suicide instead of carrying
// on in case you were wrong, use RTC_CHECK instead of RTC_DCHECK.
//
// RTC_DCHECK only evaluates its argument in debug builds, so if x has visible
// side effects, you need to write e.g.
// bool w = x; RTC_DCHECK(w);
//
// - RTC_CHECK_EQ, _NE, _GT, ..., and RTC_DCHECK_EQ, _NE, _GT, ... are
// specialized variants of RTC_CHECK and RTC_DCHECK that print prettier
// messages if the condition doesn't hold. Prefer them to raw RTC_CHECK and
// RTC_DCHECK.
//
// - FATAL() aborts unconditionally.
//
// TODO(ajm): Ideally, checks.h would be combined with logging.h, but
// consolidation with system_wrappers/logging.h should happen first.
namespace rtc {
// Helper macro which avoids evaluating the arguments to a stream if
// the condition doesn't hold.
#define RTC_LAZY_STREAM(stream, condition) \
!(condition) ? static_cast<void>(0) : rtc::FatalMessageVoidify() & (stream)
// The actual stream used isn't important. We reference |ignored| in the code
// but don't evaluate it; this is to avoid "unused variable" warnings (we do so
// in a particularly convoluted way with an extra ?: because that appears to be
// the simplest construct that keeps Visual Studio from complaining about
// condition being unused).
#define RTC_EAT_STREAM_PARAMETERS(ignored) \
(true ? true : ((void)(ignored), true)) \
? static_cast<void>(0) \
: rtc::FatalMessageVoidify() & rtc::FatalMessage("", 0).stream()
// Call RTC_EAT_STREAM_PARAMETERS with an argument that fails to compile if
// values of the same types as |a| and |b| can't be compared with the given
// operation, and that would evaluate |a| and |b| if evaluated.
#define RTC_EAT_STREAM_PARAMETERS_OP(op, a, b) \
RTC_EAT_STREAM_PARAMETERS(((void)rtc::Safe##op(a, b)))
// RTC_CHECK dies with a fatal error if condition is not true. It is *not*
// controlled by NDEBUG or anything else, so the check will be executed
// regardless of compilation mode.
//
// We make sure RTC_CHECK et al. always evaluates their arguments, as
// doing RTC_CHECK(FunctionWithSideEffect()) is a common idiom.
#define RTC_CHECK(condition) \
RTC_LAZY_STREAM(rtc::FatalMessage(__FILE__, __LINE__).stream(), \
!(condition)) \
<< "Check failed: " #condition << std::endl << "# "
// Helper macro for binary operators.
// Don't use this macro directly in your code, use RTC_CHECK_EQ et al below.
//
// TODO(akalin): Rewrite this so that constructs like if (...)
// RTC_CHECK_EQ(...) else { ... } work properly.
#define RTC_CHECK_OP(name, op, val1, val2) \
if (std::string* _result = \
rtc::Check##name##Impl((val1), (val2), #val1 " " #op " " #val2)) \
rtc::FatalMessage(__FILE__, __LINE__, _result).stream()
// Build the error message string. This is separate from the "Impl"
// function template because it is not performance critical and so can
// be out of line, while the "Impl" code should be inline. Caller
// takes ownership of the returned string.
template<class t1, class t2>
std::string* MakeCheckOpString(const t1& v1, const t2& v2, const char* names) {
std::ostringstream ss;
ss << names << " (" << v1 << " vs. " << v2 << ")";
std::string* msg = new std::string(ss.str());
return msg;
}
// MSVC doesn't like complex extern templates and DLLs.
#if !defined(COMPILER_MSVC)
// Commonly used instantiations of MakeCheckOpString<>. Explicitly instantiated
// in logging.cc.
extern template std::string* MakeCheckOpString<int, int>(
const int&, const int&, const char* names);
extern template
std::string* MakeCheckOpString<unsigned long, unsigned long>(
const unsigned long&, const unsigned long&, const char* names);
extern template
std::string* MakeCheckOpString<unsigned long, unsigned int>(
const unsigned long&, const unsigned int&, const char* names);
extern template
std::string* MakeCheckOpString<unsigned int, unsigned long>(
const unsigned int&, const unsigned long&, const char* names);
extern template
std::string* MakeCheckOpString<std::string, std::string>(
const std::string&, const std::string&, const char* name);
#endif
// Helper functions for RTC_CHECK_OP macro.
// The (int, int) specialization works around the issue that the compiler
// will not instantiate the template version of the function on values of
// unnamed enum type - see comment below.
#define DEFINE_RTC_CHECK_OP_IMPL(name) \
template <class t1, class t2> \
inline std::string* Check##name##Impl(const t1& v1, const t2& v2, \
const char* names) { \
if (rtc::Safe##name(v1, v2)) \
return nullptr; \
else \
return rtc::MakeCheckOpString(v1, v2, names); \
} \
inline std::string* Check##name##Impl(int v1, int v2, const char* names) { \
if (rtc::Safe##name(v1, v2)) \
return nullptr; \
else \
return rtc::MakeCheckOpString(v1, v2, names); \
}
DEFINE_RTC_CHECK_OP_IMPL(Eq)
DEFINE_RTC_CHECK_OP_IMPL(Ne)
DEFINE_RTC_CHECK_OP_IMPL(Le)
DEFINE_RTC_CHECK_OP_IMPL(Lt)
DEFINE_RTC_CHECK_OP_IMPL(Ge)
DEFINE_RTC_CHECK_OP_IMPL(Gt)
#undef DEFINE_RTC_CHECK_OP_IMPL
#define RTC_CHECK_EQ(val1, val2) RTC_CHECK_OP(Eq, ==, val1, val2)
#define RTC_CHECK_NE(val1, val2) RTC_CHECK_OP(Ne, !=, val1, val2)
#define RTC_CHECK_LE(val1, val2) RTC_CHECK_OP(Le, <=, val1, val2)
#define RTC_CHECK_LT(val1, val2) RTC_CHECK_OP(Lt, <, val1, val2)
#define RTC_CHECK_GE(val1, val2) RTC_CHECK_OP(Ge, >=, val1, val2)
#define RTC_CHECK_GT(val1, val2) RTC_CHECK_OP(Gt, >, val1, val2)
// The RTC_DCHECK macro is equivalent to RTC_CHECK except that it only generates
// code in debug builds. It does reference the condition parameter in all cases,
// though, so callers won't risk getting warnings about unused variables.
#if RTC_DCHECK_IS_ON
#define RTC_DCHECK(condition) RTC_CHECK(condition)
#define RTC_DCHECK_EQ(v1, v2) RTC_CHECK_EQ(v1, v2)
#define RTC_DCHECK_NE(v1, v2) RTC_CHECK_NE(v1, v2)
#define RTC_DCHECK_LE(v1, v2) RTC_CHECK_LE(v1, v2)
#define RTC_DCHECK_LT(v1, v2) RTC_CHECK_LT(v1, v2)
#define RTC_DCHECK_GE(v1, v2) RTC_CHECK_GE(v1, v2)
#define RTC_DCHECK_GT(v1, v2) RTC_CHECK_GT(v1, v2)
#else
#define RTC_DCHECK(condition) RTC_EAT_STREAM_PARAMETERS(condition)
#define RTC_DCHECK_EQ(v1, v2) RTC_EAT_STREAM_PARAMETERS_OP(Eq, v1, v2)
#define RTC_DCHECK_NE(v1, v2) RTC_EAT_STREAM_PARAMETERS_OP(Ne, v1, v2)
#define RTC_DCHECK_LE(v1, v2) RTC_EAT_STREAM_PARAMETERS_OP(Le, v1, v2)
#define RTC_DCHECK_LT(v1, v2) RTC_EAT_STREAM_PARAMETERS_OP(Lt, v1, v2)
#define RTC_DCHECK_GE(v1, v2) RTC_EAT_STREAM_PARAMETERS_OP(Ge, v1, v2)
#define RTC_DCHECK_GT(v1, v2) RTC_EAT_STREAM_PARAMETERS_OP(Gt, v1, v2)
#endif
// This is identical to LogMessageVoidify but in name.
class FatalMessageVoidify {
public:
FatalMessageVoidify() { }
// This has to be an operator with a precedence lower than << but
// higher than ?:
void operator&(std::ostream&) { }
};
#define RTC_UNREACHABLE_CODE_HIT false
#define RTC_NOTREACHED() RTC_DCHECK(RTC_UNREACHABLE_CODE_HIT)
#define FATAL() rtc::FatalMessage(__FILE__, __LINE__).stream()
// TODO(ajm): Consider adding RTC_NOTIMPLEMENTED macro when
// base/logging.h and system_wrappers/logging.h are consolidated such that we
// can match the Chromium behavior.
// Like a stripped-down LogMessage from logging.h, except that it aborts.
class FatalMessage {
public:
FatalMessage(const char* file, int line);
// Used for RTC_CHECK_EQ(), etc. Takes ownership of the given string.
FatalMessage(const char* file, int line, std::string* result);
NO_RETURN ~FatalMessage();
std::ostream& stream() { return stream_; }
private:
void Init(const char* file, int line);
std::ostringstream stream_;
};
// Performs the integer division a/b and returns the result. CHECKs that the
// remainder is zero.
template <typename T>
inline T CheckedDivExact(T a, T b) {
RTC_CHECK_EQ(a % b, 0) << a << " is not evenly divisible by " << b;
return a / b;
}
} // namespace rtc
#else // __cplusplus not defined
// C version. Lacks many features compared to the C++ version, but usage
// guidelines are the same.
#define RTC_CHECK(condition) \
do { \
if (!(condition)) { \
rtc_FatalMessage(__FILE__, __LINE__, "CHECK failed: " #condition); \
} \
} while (0)
#define RTC_CHECK_EQ(a, b) RTC_CHECK((a) == (b))
#define RTC_CHECK_NE(a, b) RTC_CHECK((a) != (b))
#define RTC_CHECK_LE(a, b) RTC_CHECK((a) <= (b))
#define RTC_CHECK_LT(a, b) RTC_CHECK((a) < (b))
#define RTC_CHECK_GE(a, b) RTC_CHECK((a) >= (b))
#define RTC_CHECK_GT(a, b) RTC_CHECK((a) > (b))
#define RTC_DCHECK(condition) \
do { \
if (RTC_DCHECK_IS_ON && !(condition)) { \
rtc_FatalMessage(__FILE__, __LINE__, "DCHECK failed: " #condition); \
} \
} while (0)
#define RTC_DCHECK_EQ(a, b) RTC_DCHECK((a) == (b))
#define RTC_DCHECK_NE(a, b) RTC_DCHECK((a) != (b))
#define RTC_DCHECK_LE(a, b) RTC_DCHECK((a) <= (b))
#define RTC_DCHECK_LT(a, b) RTC_DCHECK((a) < (b))
#define RTC_DCHECK_GE(a, b) RTC_DCHECK((a) >= (b))
#define RTC_DCHECK_GT(a, b) RTC_DCHECK((a) > (b))
#endif // __cplusplus
#endif // WEBRTC_RTC_BASE_CHECKS_H_

View File

@ -0,0 +1,21 @@
/*
* Copyright (c) 2012 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_RTC_BASE_COMPILE_ASSERT_C_H_
#define WEBRTC_RTC_BASE_COMPILE_ASSERT_C_H_
// Use this macro to verify at compile time that certain restrictions are met.
// The argument is the boolean expression to evaluate.
// Example:
// RTC_COMPILE_ASSERT(sizeof(foo) < 128);
// Note: In C++, use static_assert instead!
#define RTC_COMPILE_ASSERT(expression) switch (0) {case 0: case expression:;}
#endif // WEBRTC_RTC_BASE_COMPILE_ASSERT_C_H_

View File

@ -0,0 +1,34 @@
/*
* 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_RTC_BASE_CONSTRUCTORMAGIC_H_
#define WEBRTC_RTC_BASE_CONSTRUCTORMAGIC_H_
// Put this in the declarations for a class to be unassignable.
#define RTC_DISALLOW_ASSIGN(TypeName) \
void operator=(const TypeName&) = delete
// A macro to disallow the copy constructor and operator= functions. This should
// be used in the declarations for a class.
#define RTC_DISALLOW_COPY_AND_ASSIGN(TypeName) \
TypeName(const TypeName&) = delete; \
RTC_DISALLOW_ASSIGN(TypeName)
// A macro to disallow all the implicit constructors, namely the default
// constructor, copy constructor and operator= functions.
//
// This should be used in the declarations for a class that wants to prevent
// anyone from instantiating it. This is especially useful for classes
// containing only static methods.
#define RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
TypeName() = delete; \
RTC_DISALLOW_COPY_AND_ASSIGN(TypeName)
#endif // WEBRTC_RTC_BASE_CONSTRUCTORMAGIC_H_

View File

@ -0,0 +1,112 @@
/*
* Copyright 2016 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.
*/
#include "webrtc/base/copyonwritebuffer.h"
namespace rtc {
CopyOnWriteBuffer::CopyOnWriteBuffer() {
RTC_DCHECK(IsConsistent());
}
CopyOnWriteBuffer::CopyOnWriteBuffer(const CopyOnWriteBuffer& buf)
: buffer_(buf.buffer_) {
}
CopyOnWriteBuffer::CopyOnWriteBuffer(CopyOnWriteBuffer&& buf)
: buffer_(std::move(buf.buffer_)) {
}
CopyOnWriteBuffer::CopyOnWriteBuffer(size_t size)
: buffer_(size > 0 ? new RefCountedObject<Buffer>(size) : nullptr) {
RTC_DCHECK(IsConsistent());
}
CopyOnWriteBuffer::CopyOnWriteBuffer(size_t size, size_t capacity)
: buffer_(size > 0 || capacity > 0
? new RefCountedObject<Buffer>(size, capacity)
: nullptr) {
RTC_DCHECK(IsConsistent());
}
CopyOnWriteBuffer::~CopyOnWriteBuffer() = default;
bool CopyOnWriteBuffer::operator==(const CopyOnWriteBuffer& buf) const {
// Must either use the same buffer internally or have the same contents.
RTC_DCHECK(IsConsistent());
RTC_DCHECK(buf.IsConsistent());
return buffer_.get() == buf.buffer_.get() ||
(buffer_.get() && buf.buffer_.get() &&
*buffer_.get() == *buf.buffer_.get());
}
void CopyOnWriteBuffer::SetSize(size_t size) {
RTC_DCHECK(IsConsistent());
if (!buffer_) {
if (size > 0) {
buffer_ = new RefCountedObject<Buffer>(size);
}
RTC_DCHECK(IsConsistent());
return;
}
// Clone data if referenced.
if (!buffer_->HasOneRef()) {
buffer_ = new RefCountedObject<Buffer>(
buffer_->data(),
std::min(buffer_->size(), size),
std::max(buffer_->capacity(), size));
}
buffer_->SetSize(size);
RTC_DCHECK(IsConsistent());
}
void CopyOnWriteBuffer::EnsureCapacity(size_t capacity) {
RTC_DCHECK(IsConsistent());
if (!buffer_) {
if (capacity > 0) {
buffer_ = new RefCountedObject<Buffer>(0, capacity);
}
RTC_DCHECK(IsConsistent());
return;
} else if (capacity <= buffer_->capacity()) {
return;
}
CloneDataIfReferenced(std::max(buffer_->capacity(), capacity));
buffer_->EnsureCapacity(capacity);
RTC_DCHECK(IsConsistent());
}
void CopyOnWriteBuffer::Clear() {
if (!buffer_)
return;
if (buffer_->HasOneRef()) {
buffer_->Clear();
} else {
buffer_ = new RefCountedObject<Buffer>(0, buffer_->capacity());
}
RTC_DCHECK(IsConsistent());
}
void CopyOnWriteBuffer::CloneDataIfReferenced(size_t new_capacity) {
if (buffer_->HasOneRef()) {
return;
}
buffer_ = new RefCountedObject<Buffer>(buffer_->data(), buffer_->size(),
new_capacity);
RTC_DCHECK(IsConsistent());
}
} // namespace rtc

View File

@ -0,0 +1,241 @@
/*
* Copyright 2016 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_RTC_BASE_COPYONWRITEBUFFER_H_
#define WEBRTC_RTC_BASE_COPYONWRITEBUFFER_H_
#include <algorithm>
#include <utility>
#include "webrtc/base/buffer.h"
#include "webrtc/base/checks.h"
#include "webrtc/base/refcount.h"
#include "webrtc/base/scoped_ref_ptr.h"
namespace rtc {
class CopyOnWriteBuffer {
public:
// An empty buffer.
CopyOnWriteBuffer();
// Copy size and contents of an existing buffer.
CopyOnWriteBuffer(const CopyOnWriteBuffer& buf);
// Move contents from an existing buffer.
CopyOnWriteBuffer(CopyOnWriteBuffer&& buf);
// Construct a buffer with the specified number of uninitialized bytes.
explicit CopyOnWriteBuffer(size_t size);
CopyOnWriteBuffer(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 std::enable_if<
internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
CopyOnWriteBuffer(const T* data, size_t size)
: CopyOnWriteBuffer(data, size, size) {}
template <typename T,
typename std::enable_if<
internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
CopyOnWriteBuffer(const T* data, size_t size, size_t capacity)
: CopyOnWriteBuffer(size, capacity) {
if (buffer_) {
std::memcpy(buffer_->data(), data, size);
}
}
// Construct a buffer from the contents of an array.
template <typename T,
size_t N,
typename std::enable_if<
internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
CopyOnWriteBuffer(const T (&array)[N]) // NOLINT: runtime/explicit
: CopyOnWriteBuffer(array, N) {}
~CopyOnWriteBuffer();
// 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 std::enable_if<
internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
const T* data() const {
return cdata<T>();
}
// Get writable pointer to the data. This will create a copy of the underlying
// data if it is shared with other buffers.
template <typename T = uint8_t,
typename std::enable_if<
internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
T* data() {
RTC_DCHECK(IsConsistent());
if (!buffer_) {
return nullptr;
}
CloneDataIfReferenced(buffer_->capacity());
return buffer_->data<T>();
}
// Get const pointer to the data. This will not create a copy of the
// underlying data if it is shared with other buffers.
template <typename T = uint8_t,
typename std::enable_if<
internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
const T* cdata() const {
RTC_DCHECK(IsConsistent());
if (!buffer_) {
return nullptr;
}
return buffer_->data<T>();
}
size_t size() const {
RTC_DCHECK(IsConsistent());
return buffer_ ? buffer_->size() : 0;
}
size_t capacity() const {
RTC_DCHECK(IsConsistent());
return buffer_ ? buffer_->capacity() : 0;
}
CopyOnWriteBuffer& operator=(const CopyOnWriteBuffer& buf) {
RTC_DCHECK(IsConsistent());
RTC_DCHECK(buf.IsConsistent());
if (&buf != this) {
buffer_ = buf.buffer_;
}
return *this;
}
CopyOnWriteBuffer& operator=(CopyOnWriteBuffer&& buf) {
RTC_DCHECK(IsConsistent());
RTC_DCHECK(buf.IsConsistent());
buffer_ = std::move(buf.buffer_);
return *this;
}
bool operator==(const CopyOnWriteBuffer& buf) const;
bool operator!=(const CopyOnWriteBuffer& buf) const {
return !(*this == buf);
}
uint8_t& operator[](size_t index) {
RTC_DCHECK_LT(index, size());
return data()[index];
}
uint8_t operator[](size_t index) const {
RTC_DCHECK_LT(index, size());
return cdata()[index];
}
// Replace the contents of the buffer. Accepts the same types as the
// constructors.
template <typename T,
typename std::enable_if<
internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
void SetData(const T* data, size_t size) {
RTC_DCHECK(IsConsistent());
if (!buffer_) {
buffer_ = size > 0 ? new RefCountedObject<Buffer>(data, size) : nullptr;
} else if (!buffer_->HasOneRef()) {
buffer_ = new RefCountedObject<Buffer>(data, size, buffer_->capacity());
} else {
buffer_->SetData(data, size);
}
RTC_DCHECK(IsConsistent());
}
template <typename T,
size_t N,
typename std::enable_if<
internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
void SetData(const T (&array)[N]) {
SetData(array, N);
}
void SetData(const CopyOnWriteBuffer& buf) {
RTC_DCHECK(IsConsistent());
RTC_DCHECK(buf.IsConsistent());
if (&buf != this) {
buffer_ = buf.buffer_;
}
}
// Append data to the buffer. Accepts the same types as the constructors.
template <typename T,
typename std::enable_if<
internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
void AppendData(const T* data, size_t size) {
RTC_DCHECK(IsConsistent());
if (!buffer_) {
buffer_ = new RefCountedObject<Buffer>(data, size);
RTC_DCHECK(IsConsistent());
return;
}
CloneDataIfReferenced(std::max(buffer_->capacity(),
buffer_->size() + size));
buffer_->AppendData(data, size);
RTC_DCHECK(IsConsistent());
}
template <typename T,
size_t N,
typename std::enable_if<
internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
void AppendData(const T (&array)[N]) {
AppendData(array, N);
}
void AppendData(const CopyOnWriteBuffer& 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);
// 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);
// Resets the buffer to zero size without altering capacity. Works even if the
// buffer has been moved from.
void Clear();
// Swaps two buffers.
friend void swap(CopyOnWriteBuffer& a, CopyOnWriteBuffer& b) {
std::swap(a.buffer_, b.buffer_);
}
private:
// Create a copy of the underlying data if it is referenced from other Buffer
// objects.
void CloneDataIfReferenced(size_t new_capacity);
// Pre- and postcondition of all methods.
bool IsConsistent() const {
return (!buffer_ || buffer_->capacity() > 0);
}
// buffer_ is either null, or points to an rtc::Buffer with capacity > 0.
scoped_refptr<RefCountedObject<Buffer>> buffer_;
};
} // namespace rtc
#endif // WEBRTC_RTC_BASE_COPYONWRITEBUFFER_H_

View File

@ -0,0 +1,319 @@
/*
* Copyright 2016 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.
*/
#include "webrtc/base/copyonwritebuffer.h"
#include "webrtc/base/gunit.h"
namespace rtc {
namespace {
// clang-format off
const uint8_t kTestData[] = {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,
0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf};
// clang-format on
} // namespace
void EnsureBuffersShareData(const CopyOnWriteBuffer& buf1,
const CopyOnWriteBuffer& buf2) {
// Data is shared between buffers.
EXPECT_EQ(buf1.size(), buf2.size());
EXPECT_EQ(buf1.capacity(), buf2.capacity());
const uint8_t* data1 = buf1.data();
const uint8_t* data2 = buf2.data();
EXPECT_EQ(data1, data2);
EXPECT_EQ(buf1, buf2);
}
void EnsureBuffersDontShareData(const CopyOnWriteBuffer& buf1,
const CopyOnWriteBuffer& buf2) {
// Data is not shared between buffers.
const uint8_t* data1 = buf1.cdata();
const uint8_t* data2 = buf2.cdata();
EXPECT_NE(data1, data2);
}
TEST(CopyOnWriteBufferTest, TestCreateEmptyData) {
CopyOnWriteBuffer buf(static_cast<const uint8_t*>(nullptr), 0);
EXPECT_EQ(buf.size(), 0u);
EXPECT_EQ(buf.capacity(), 0u);
EXPECT_EQ(buf.data(), nullptr);
}
TEST(CopyOnWriteBufferTest, TestMoveConstruct) {
CopyOnWriteBuffer buf1(kTestData, 3, 10);
size_t buf1_size = buf1.size();
size_t buf1_capacity = buf1.capacity();
const uint8_t* buf1_data = buf1.cdata();
CopyOnWriteBuffer buf2(std::move(buf1));
EXPECT_EQ(buf1.size(), 0u);
EXPECT_EQ(buf1.capacity(), 0u);
EXPECT_EQ(buf1.data(), nullptr);
EXPECT_EQ(buf2.size(), buf1_size);
EXPECT_EQ(buf2.capacity(), buf1_capacity);
EXPECT_EQ(buf2.data(), buf1_data);
}
TEST(CopyOnWriteBufferTest, TestMoveAssign) {
CopyOnWriteBuffer buf1(kTestData, 3, 10);
size_t buf1_size = buf1.size();
size_t buf1_capacity = buf1.capacity();
const uint8_t* buf1_data = buf1.cdata();
CopyOnWriteBuffer buf2;
buf2 = std::move(buf1);
EXPECT_EQ(buf1.size(), 0u);
EXPECT_EQ(buf1.capacity(), 0u);
EXPECT_EQ(buf1.data(), nullptr);
EXPECT_EQ(buf2.size(), buf1_size);
EXPECT_EQ(buf2.capacity(), buf1_capacity);
EXPECT_EQ(buf2.data(), buf1_data);
}
TEST(CopyOnWriteBufferTest, TestSwap) {
CopyOnWriteBuffer buf1(kTestData, 3, 10);
size_t buf1_size = buf1.size();
size_t buf1_capacity = buf1.capacity();
const uint8_t* buf1_data = buf1.cdata();
CopyOnWriteBuffer buf2(kTestData, 6, 20);
size_t buf2_size = buf2.size();
size_t buf2_capacity = buf2.capacity();
const uint8_t* buf2_data = buf2.cdata();
std::swap(buf1, buf2);
EXPECT_EQ(buf1.size(), buf2_size);
EXPECT_EQ(buf1.capacity(), buf2_capacity);
EXPECT_EQ(buf1.data(), buf2_data);
EXPECT_EQ(buf2.size(), buf1_size);
EXPECT_EQ(buf2.capacity(), buf1_capacity);
EXPECT_EQ(buf2.data(), buf1_data);
}
TEST(CopyOnWriteBufferTest, TestAppendData) {
CopyOnWriteBuffer buf1(kTestData, 3, 10);
CopyOnWriteBuffer buf2(buf1);
EnsureBuffersShareData(buf1, buf2);
// AppendData copies the underlying buffer.
buf2.AppendData("foo");
EXPECT_EQ(buf2.size(), buf1.size() + 4); // "foo" + trailing 0x00
EXPECT_EQ(buf2.capacity(), buf1.capacity());
EXPECT_NE(buf2.data(), buf1.data());
EXPECT_EQ(buf1, CopyOnWriteBuffer(kTestData, 3));
const int8_t exp[] = {0x0, 0x1, 0x2, 'f', 'o', 'o', 0x0};
EXPECT_EQ(buf2, CopyOnWriteBuffer(exp));
}
TEST(CopyOnWriteBufferTest, SetEmptyData) {
CopyOnWriteBuffer buf(10);
buf.SetData<uint8_t>(nullptr, 0);
EXPECT_EQ(0u, buf.size());
}
TEST(CopyOnWriteBufferTest, SetDataNoMoreThanCapacityDoesntCauseReallocation) {
CopyOnWriteBuffer buf1(3, 10);
const uint8_t* const original_allocation = buf1.cdata();
buf1.SetData(kTestData, 10);
EXPECT_EQ(original_allocation, buf1.cdata());
EXPECT_EQ(buf1, CopyOnWriteBuffer(kTestData, 10));
}
TEST(CopyOnWriteBufferTest, SetDataMakeReferenceCopy) {
CopyOnWriteBuffer buf1(kTestData, 3, 10);
CopyOnWriteBuffer buf2;
buf2.SetData(buf1);
EnsureBuffersShareData(buf1, buf2);
}
TEST(CopyOnWriteBufferTest, SetDataOnSharedKeepsOriginal) {
const uint8_t data[] = "foo";
CopyOnWriteBuffer buf1(kTestData, 3, 10);
const uint8_t* const original_allocation = buf1.cdata();
CopyOnWriteBuffer buf2(buf1);
buf2.SetData(data);
EnsureBuffersDontShareData(buf1, buf2);
EXPECT_EQ(original_allocation, buf1.cdata());
EXPECT_EQ(buf1, CopyOnWriteBuffer(kTestData, 3));
EXPECT_EQ(buf2, CopyOnWriteBuffer(data));
}
TEST(CopyOnWriteBufferTest, SetDataOnSharedKeepsCapacity) {
CopyOnWriteBuffer buf1(kTestData, 3, 10);
CopyOnWriteBuffer buf2(buf1);
EnsureBuffersShareData(buf1, buf2);
buf2.SetData(kTestData, 2);
EnsureBuffersDontShareData(buf1, buf2);
EXPECT_EQ(2u, buf2.size());
EXPECT_EQ(10u, buf2.capacity());
}
TEST(CopyOnWriteBufferTest, TestEnsureCapacity) {
CopyOnWriteBuffer buf1(kTestData, 3, 10);
CopyOnWriteBuffer buf2(buf1);
// Smaller than existing capacity -> no change and still same contents.
buf2.EnsureCapacity(8);
EnsureBuffersShareData(buf1, buf2);
EXPECT_EQ(buf1.size(), 3u);
EXPECT_EQ(buf1.capacity(), 10u);
EXPECT_EQ(buf2.size(), 3u);
EXPECT_EQ(buf2.capacity(), 10u);
// Lager than existing capacity -> data is cloned.
buf2.EnsureCapacity(16);
EnsureBuffersDontShareData(buf1, buf2);
EXPECT_EQ(buf1.size(), 3u);
EXPECT_EQ(buf1.capacity(), 10u);
EXPECT_EQ(buf2.size(), 3u);
EXPECT_EQ(buf2.capacity(), 16u);
// The size and contents are still the same.
EXPECT_EQ(buf1, buf2);
}
TEST(CopyOnWriteBufferTest, SetSizeDoesntChangeOriginal) {
CopyOnWriteBuffer buf1(kTestData, 3, 10);
const uint8_t* const original_allocation = buf1.cdata();
CopyOnWriteBuffer buf2(buf1);
buf2.SetSize(16);
EnsureBuffersDontShareData(buf1, buf2);
EXPECT_EQ(original_allocation, buf1.cdata());
EXPECT_EQ(3u, buf1.size());
EXPECT_EQ(10u, buf1.capacity());
}
TEST(CopyOnWriteBufferTest, SetSizeCloneContent) {
CopyOnWriteBuffer buf1(kTestData, 3, 10);
CopyOnWriteBuffer buf2(buf1);
buf2.SetSize(16);
EXPECT_EQ(buf2.size(), 16u);
EXPECT_EQ(0, memcmp(buf2.data(), kTestData, 3));
}
TEST(CopyOnWriteBufferTest, SetSizeMayIncreaseCapacity) {
CopyOnWriteBuffer buf(kTestData, 3, 10);
buf.SetSize(16);
EXPECT_EQ(16u, buf.size());
EXPECT_EQ(16u, buf.capacity());
}
TEST(CopyOnWriteBufferTest, SetSizeDoesntDecreaseCapacity) {
CopyOnWriteBuffer buf1(kTestData, 5, 10);
CopyOnWriteBuffer buf2(buf1);
buf2.SetSize(2);
EXPECT_EQ(2u, buf2.size());
EXPECT_EQ(10u, buf2.capacity());
}
TEST(CopyOnWriteBufferTest, ClearDoesntChangeOriginal) {
CopyOnWriteBuffer buf1(kTestData, 3, 10);
const uint8_t* const original_allocation = buf1.cdata();
CopyOnWriteBuffer buf2(buf1);
buf2.Clear();
EnsureBuffersDontShareData(buf1, buf2);
EXPECT_EQ(3u, buf1.size());
EXPECT_EQ(10u, buf1.capacity());
EXPECT_EQ(original_allocation, buf1.cdata());
EXPECT_EQ(0u, buf2.size());
}
TEST(CopyOnWriteBufferTest, ClearDoesntChangeCapacity) {
CopyOnWriteBuffer buf1(kTestData, 3, 10);
CopyOnWriteBuffer buf2(buf1);
buf2.Clear();
EXPECT_EQ(0u, buf2.size());
EXPECT_EQ(10u, buf2.capacity());
}
TEST(CopyOnWriteBufferTest, TestConstDataAccessor) {
CopyOnWriteBuffer buf1(kTestData, 3, 10);
CopyOnWriteBuffer buf2(buf1);
// .cdata() doesn't clone data.
const uint8_t* cdata1 = buf1.cdata();
const uint8_t* cdata2 = buf2.cdata();
EXPECT_EQ(cdata1, cdata2);
// Non-const .data() clones data if shared.
const uint8_t* data1 = buf1.data();
const uint8_t* data2 = buf2.data();
EXPECT_NE(data1, data2);
// buf1 was cloned above.
EXPECT_NE(data1, cdata1);
// Therefore buf2 was no longer sharing data and was not cloned.
EXPECT_EQ(data2, cdata1);
}
TEST(CopyOnWriteBufferTest, TestBacketRead) {
CopyOnWriteBuffer buf1(kTestData, 3, 10);
CopyOnWriteBuffer buf2(buf1);
EnsureBuffersShareData(buf1, buf2);
// Non-const reads clone the data if shared.
for (size_t i = 0; i != 3u; ++i) {
EXPECT_EQ(buf1[i], kTestData[i]);
}
EnsureBuffersDontShareData(buf1, buf2);
}
TEST(CopyOnWriteBufferTest, TestBacketReadConst) {
CopyOnWriteBuffer buf1(kTestData, 3, 10);
CopyOnWriteBuffer buf2(buf1);
EnsureBuffersShareData(buf1, buf2);
const CopyOnWriteBuffer& cbuf1 = buf1;
for (size_t i = 0; i != 3u; ++i) {
EXPECT_EQ(cbuf1[i], kTestData[i]);
}
EnsureBuffersShareData(buf1, buf2);
}
TEST(CopyOnWriteBufferTest, TestBacketWrite) {
CopyOnWriteBuffer buf1(kTestData, 3, 10);
CopyOnWriteBuffer buf2(buf1);
EnsureBuffersShareData(buf1, buf2);
for (size_t i = 0; i != 3u; ++i) {
buf1[i] = kTestData[i] + 1;
}
EXPECT_EQ(buf1.size(), 3u);
EXPECT_EQ(buf1.capacity(), 10u);
EXPECT_EQ(buf2.size(), 3u);
EXPECT_EQ(buf2.capacity(), 10u);
EXPECT_EQ(0, memcmp(buf2.cdata(), kTestData, 3));
}
} // namespace rtc

114
webrtc/rtc_base/cpu_time.cc Normal file
View File

@ -0,0 +1,114 @@
/*
* Copyright (c) 2017 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.
*/
#include "webrtc/base/cpu_time.h"
#include "webrtc/base/logging.h"
#include "webrtc/base/timeutils.h"
#if defined(WEBRTC_LINUX)
#include <time.h>
#elif defined(WEBRTC_MAC)
#include <sys/resource.h>
#include <sys/types.h>
#include <sys/times.h>
#include <mach/thread_info.h>
#include <mach/thread_act.h>
#include <mach/mach_init.h>
#include <unistd.h>
#elif defined(WEBRTC_WIN)
#include <windows.h>
#endif
#if defined(WEBRTC_WIN)
namespace {
// FILETIME resolution is 100 nanosecs.
const int64_t kNanosecsPerFiletime = 100;
} // namespace
#endif
namespace rtc {
int64_t GetProcessCpuTimeNanos() {
#if defined(WEBRTC_LINUX)
struct timespec ts;
if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts) == 0) {
return ts.tv_sec * kNumNanosecsPerSec + ts.tv_nsec;
} else {
LOG_ERR(LS_ERROR) << "clock_gettime() failed.";
}
#elif defined(WEBRTC_MAC)
struct rusage rusage;
if (getrusage(RUSAGE_SELF, &rusage) == 0) {
return rusage.ru_utime.tv_sec * kNumNanosecsPerSec +
rusage.ru_utime.tv_usec * kNumNanosecsPerMicrosec;
} else {
LOG_ERR(LS_ERROR) << "getrusage() failed.";
}
#elif defined(WEBRTC_WIN)
FILETIME createTime;
FILETIME exitTime;
FILETIME kernelTime;
FILETIME userTime;
if (GetProcessTimes(GetCurrentProcess(), &createTime, &exitTime, &kernelTime,
&userTime) != 0) {
return ((static_cast<uint64_t>(userTime.dwHighDateTime) << 32) +
userTime.dwLowDateTime) *
kNanosecsPerFiletime;
} else {
LOG_ERR(LS_ERROR) << "GetProcessTimes() failed.";
}
#else
// Not implemented yet.
static_assert(
false, "GetProcessCpuTimeNanos() platform support not yet implemented.");
#endif
return -1;
}
int64_t GetThreadCpuTimeNanos() {
#if defined(WEBRTC_LINUX)
struct timespec ts;
if (clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts) == 0) {
return ts.tv_sec * kNumNanosecsPerSec + ts.tv_nsec;
} else {
LOG_ERR(LS_ERROR) << "clock_gettime() failed.";
}
#elif defined(WEBRTC_MAC)
thread_basic_info_data_t info;
mach_msg_type_number_t count = THREAD_BASIC_INFO_COUNT;
if (thread_info(mach_thread_self(), THREAD_BASIC_INFO, (thread_info_t)&info,
&count) == KERN_SUCCESS) {
return info.user_time.seconds * kNumNanosecsPerSec +
info.user_time.microseconds * kNumNanosecsPerMicrosec;
} else {
LOG_ERR(LS_ERROR) << "thread_info() failed.";
}
#elif defined(WEBRTC_WIN)
FILETIME createTime;
FILETIME exitTime;
FILETIME kernelTime;
FILETIME userTime;
if (GetThreadTimes(GetCurrentThread(), &createTime, &exitTime, &kernelTime,
&userTime) != 0) {
return ((static_cast<uint64_t>(userTime.dwHighDateTime) << 32) +
userTime.dwLowDateTime) *
kNanosecsPerFiletime;
} else {
LOG_ERR(LS_ERROR) << "GetThreadTimes() failed.";
}
#else
// Not implemented yet.
static_assert(
false, "GetProcessCpuTimeNanos() platform support not yet implemented.");
#endif
return -1;
}
} // namespace rtc

View File

@ -0,0 +1,28 @@
/*
* Copyright (c) 2017 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_RTC_BASE_CPU_TIME_H_
#define WEBRTC_RTC_BASE_CPU_TIME_H_
#include <stdint.h>
namespace rtc {
// Returns total CPU time of a current process in nanoseconds.
// Time base is unknown, therefore use only to calculate deltas.
int64_t GetProcessCpuTimeNanos();
// Returns total CPU time of a current thread in nanoseconds.
// Time base is unknown, therefore use only to calculate deltas.
int64_t GetThreadCpuTimeNanos();
} // namespace rtc
#endif // WEBRTC_RTC_BASE_CPU_TIME_H_

View File

@ -0,0 +1,106 @@
/*
* Copyright (c) 2017 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.
*/
#include <memory>
#include <algorithm>
#include "webrtc/base/cpu_time.h"
#include "webrtc/base/platform_thread.h"
#include "webrtc/base/timeutils.h"
#include "webrtc/test/gtest.h"
#include "webrtc/system_wrappers/include/cpu_info.h"
#include "webrtc/system_wrappers/include/sleep.h"
// Only run these tests on non-instrumented builds, because timing on
// instrumented builds is unreliable, causing the test to be flaky.
#if defined(THREAD_SANITIZER) || defined(MEMORY_SANITIZER) || \
defined(ADDRESS_SANITIZER)
#define MAYBE_TEST(test_name) DISABLED_##test_name
#else
#define MAYBE_TEST(test_name) test_name
#endif
namespace {
const int kAllowedErrorMillisecs = 30;
const int kProcessingTimeMillisecs = 300;
const int kWorkingThreads = 2;
// Consumes approximately kProcessingTimeMillisecs of CPU time in single thread.
bool WorkingFunction(void* counter_pointer) {
int64_t* counter = reinterpret_cast<int64_t*>(counter_pointer);
*counter = 0;
int64_t stop_cpu_time =
rtc::GetThreadCpuTimeNanos() +
kProcessingTimeMillisecs * rtc::kNumNanosecsPerMillisec;
while (rtc::GetThreadCpuTimeNanos() < stop_cpu_time) {
(*counter)++;
}
return false;
}
} // namespace
namespace rtc {
// A minimal test which can be run on instrumented builds, so that they're at
// least exercising the code to check for memory leaks/etc.
TEST(CpuTimeTest, BasicTest) {
int64_t process_start_time_nanos = GetProcessCpuTimeNanos();
int64_t thread_start_time_nanos = GetThreadCpuTimeNanos();
int64_t process_duration_nanos =
GetProcessCpuTimeNanos() - process_start_time_nanos;
int64_t thread_duration_nanos =
GetThreadCpuTimeNanos() - thread_start_time_nanos;
EXPECT_GE(process_duration_nanos, 0);
EXPECT_GE(thread_duration_nanos, 0);
}
TEST(CpuTimeTest, MAYBE_TEST(TwoThreads)) {
int64_t process_start_time_nanos = GetProcessCpuTimeNanos();
int64_t thread_start_time_nanos = GetThreadCpuTimeNanos();
int64_t counter1;
int64_t counter2;
PlatformThread thread1(WorkingFunction, reinterpret_cast<void*>(&counter1),
"Thread1");
PlatformThread thread2(WorkingFunction, reinterpret_cast<void*>(&counter2),
"Thread2");
thread1.Start();
thread2.Start();
thread1.Stop();
thread2.Stop();
EXPECT_GE(counter1, 0);
EXPECT_GE(counter2, 0);
int64_t process_duration_nanos =
GetProcessCpuTimeNanos() - process_start_time_nanos;
int64_t thread_duration_nanos =
GetThreadCpuTimeNanos() - thread_start_time_nanos;
// This thread did almost nothing.
// Therefore GetThreadCpuTime is not a wall clock.
EXPECT_LE(thread_duration_nanos,
kAllowedErrorMillisecs * kNumNanosecsPerMillisec);
// Total process time is at least twice working threads' CPU time.
// Therefore process and thread times are correctly related.
EXPECT_GE(
process_duration_nanos,
kWorkingThreads * (kProcessingTimeMillisecs - kAllowedErrorMillisecs)
* kNumNanosecsPerMillisec);
}
TEST(CpuTimeTest, MAYBE_TEST(Sleeping)) {
int64_t process_start_time_nanos = GetProcessCpuTimeNanos();
webrtc::SleepMs(kProcessingTimeMillisecs);
int64_t process_duration_nanos =
GetProcessCpuTimeNanos() - process_start_time_nanos;
// Sleeping should not introduce any additional CPU time.
// Therefore GetProcessCpuTime is not a wall clock.
EXPECT_LE(process_duration_nanos,
kWorkingThreads * kAllowedErrorMillisecs * kNumNanosecsPerMillisec);
}
} // namespace rtc

52
webrtc/rtc_base/crc32.cc Normal file
View File

@ -0,0 +1,52 @@
/*
* Copyright 2012 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.
*/
#include "webrtc/base/crc32.h"
#include "webrtc/base/arraysize.h"
namespace rtc {
// This implementation is based on the sample implementation in RFC 1952.
// CRC32 polynomial, in reversed form.
// See RFC 1952, or http://en.wikipedia.org/wiki/Cyclic_redundancy_check
static const uint32_t kCrc32Polynomial = 0xEDB88320;
static uint32_t kCrc32Table[256] = {0};
static void EnsureCrc32TableInited() {
if (kCrc32Table[arraysize(kCrc32Table) - 1])
return; // already inited
for (uint32_t i = 0; i < arraysize(kCrc32Table); ++i) {
uint32_t c = i;
for (size_t j = 0; j < 8; ++j) {
if (c & 1) {
c = kCrc32Polynomial ^ (c >> 1);
} else {
c >>= 1;
}
}
kCrc32Table[i] = c;
}
}
uint32_t UpdateCrc32(uint32_t start, const void* buf, size_t len) {
EnsureCrc32TableInited();
uint32_t c = start ^ 0xFFFFFFFF;
const uint8_t* u = static_cast<const uint8_t*>(buf);
for (size_t i = 0; i < len; ++i) {
c = kCrc32Table[(c ^ u[i]) & 0xFF] ^ (c >> 8);
}
return c ^ 0xFFFFFFFF;
}
} // namespace rtc

34
webrtc/rtc_base/crc32.h Normal file
View File

@ -0,0 +1,34 @@
/*
* Copyright 2012 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_RTC_BASE_CRC32_H_
#define WEBRTC_RTC_BASE_CRC32_H_
#include <string>
#include "webrtc/base/basictypes.h"
namespace rtc {
// Updates a CRC32 checksum with |len| bytes from |buf|. |initial| holds the
// checksum result from the previous update; for the first call, it should be 0.
uint32_t UpdateCrc32(uint32_t initial, const void* buf, size_t len);
// Computes a CRC32 checksum using |len| bytes from |buf|.
inline uint32_t ComputeCrc32(const void* buf, size_t len) {
return UpdateCrc32(0, buf, len);
}
inline uint32_t ComputeCrc32(const std::string& str) {
return ComputeCrc32(str.c_str(), str.size());
}
} // namespace rtc
#endif // WEBRTC_RTC_BASE_CRC32_H_

View File

@ -0,0 +1,35 @@
/*
* Copyright 2012 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.
*/
#include "webrtc/base/crc32.h"
#include "webrtc/base/gunit.h"
#include <string>
namespace rtc {
TEST(Crc32Test, TestBasic) {
EXPECT_EQ(0U, ComputeCrc32(""));
EXPECT_EQ(0x352441C2U, ComputeCrc32("abc"));
EXPECT_EQ(0x171A3F5FU,
ComputeCrc32("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"));
}
TEST(Crc32Test, TestMultipleUpdates) {
std::string input =
"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq";
uint32_t c = 0;
for (size_t i = 0; i < input.size(); ++i) {
c = UpdateCrc32(c, &input[i], 1);
}
EXPECT_EQ(0x171A3F5FU, c);
}
} // namespace rtc

View File

@ -0,0 +1,252 @@
/*
* 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.
*/
#include "webrtc/base/criticalsection.h"
#include "webrtc/base/checks.h"
#include "webrtc/base/platform_thread.h"
// TODO(tommi): Split this file up to per-platform implementation files.
namespace rtc {
CriticalSection::CriticalSection() {
#if defined(WEBRTC_WIN)
InitializeCriticalSection(&crit_);
#elif defined(WEBRTC_POSIX)
# if defined(WEBRTC_MAC) && !USE_NATIVE_MUTEX_ON_MAC
lock_queue_ = 0;
owning_thread_ = 0;
recursion_ = 0;
semaphore_ = dispatch_semaphore_create(0);
# else
pthread_mutexattr_t mutex_attribute;
pthread_mutexattr_init(&mutex_attribute);
pthread_mutexattr_settype(&mutex_attribute, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&mutex_, &mutex_attribute);
pthread_mutexattr_destroy(&mutex_attribute);
# endif
CS_DEBUG_CODE(thread_ = 0);
CS_DEBUG_CODE(recursion_count_ = 0);
RTC_UNUSED(thread_);
RTC_UNUSED(recursion_count_);
#else
# error Unsupported platform.
#endif
}
CriticalSection::~CriticalSection() {
#if defined(WEBRTC_WIN)
DeleteCriticalSection(&crit_);
#elif defined(WEBRTC_POSIX)
# if defined(WEBRTC_MAC) && !USE_NATIVE_MUTEX_ON_MAC
dispatch_release(semaphore_);
# else
pthread_mutex_destroy(&mutex_);
# endif
#else
# error Unsupported platform.
#endif
}
void CriticalSection::Enter() const EXCLUSIVE_LOCK_FUNCTION() {
#if defined(WEBRTC_WIN)
EnterCriticalSection(&crit_);
#elif defined(WEBRTC_POSIX)
# if defined(WEBRTC_MAC) && !USE_NATIVE_MUTEX_ON_MAC
int spin = 3000;
PlatformThreadRef self = CurrentThreadRef();
bool have_lock = false;
do {
// Instead of calling TryEnter() in this loop, we do two interlocked
// operations, first a read-only one in order to avoid affecting the lock
// cache-line while spinning, in case another thread is using the lock.
if (!IsThreadRefEqual(owning_thread_, self)) {
if (AtomicOps::AcquireLoad(&lock_queue_) == 0) {
if (AtomicOps::CompareAndSwap(&lock_queue_, 0, 1) == 0) {
have_lock = true;
break;
}
}
} else {
AtomicOps::Increment(&lock_queue_);
have_lock = true;
break;
}
sched_yield();
} while (--spin);
if (!have_lock && AtomicOps::Increment(&lock_queue_) > 1) {
// Owning thread cannot be the current thread since TryEnter() would
// have succeeded.
RTC_DCHECK(!IsThreadRefEqual(owning_thread_, self));
// Wait for the lock to become available.
dispatch_semaphore_wait(semaphore_, DISPATCH_TIME_FOREVER);
RTC_DCHECK(owning_thread_ == 0);
RTC_DCHECK(!recursion_);
}
owning_thread_ = self;
++recursion_;
# else
pthread_mutex_lock(&mutex_);
# endif
# if CS_DEBUG_CHECKS
if (!recursion_count_) {
RTC_DCHECK(!thread_);
thread_ = CurrentThreadRef();
} else {
RTC_DCHECK(CurrentThreadIsOwner());
}
++recursion_count_;
# endif
#else
# error Unsupported platform.
#endif
}
bool CriticalSection::TryEnter() const EXCLUSIVE_TRYLOCK_FUNCTION(true) {
#if defined(WEBRTC_WIN)
return TryEnterCriticalSection(&crit_) != FALSE;
#elif defined(WEBRTC_POSIX)
# if defined(WEBRTC_MAC) && !USE_NATIVE_MUTEX_ON_MAC
if (!IsThreadRefEqual(owning_thread_, CurrentThreadRef())) {
if (AtomicOps::CompareAndSwap(&lock_queue_, 0, 1) != 0)
return false;
owning_thread_ = CurrentThreadRef();
RTC_DCHECK(!recursion_);
} else {
AtomicOps::Increment(&lock_queue_);
}
++recursion_;
# else
if (pthread_mutex_trylock(&mutex_) != 0)
return false;
# endif
# if CS_DEBUG_CHECKS
if (!recursion_count_) {
RTC_DCHECK(!thread_);
thread_ = CurrentThreadRef();
} else {
RTC_DCHECK(CurrentThreadIsOwner());
}
++recursion_count_;
# endif
return true;
#else
# error Unsupported platform.
#endif
}
void CriticalSection::Leave() const UNLOCK_FUNCTION() {
RTC_DCHECK(CurrentThreadIsOwner());
#if defined(WEBRTC_WIN)
LeaveCriticalSection(&crit_);
#elif defined(WEBRTC_POSIX)
# if CS_DEBUG_CHECKS
--recursion_count_;
RTC_DCHECK(recursion_count_ >= 0);
if (!recursion_count_)
thread_ = 0;
# endif
# if defined(WEBRTC_MAC) && !USE_NATIVE_MUTEX_ON_MAC
RTC_DCHECK(IsThreadRefEqual(owning_thread_, CurrentThreadRef()));
RTC_DCHECK_GE(recursion_, 0);
--recursion_;
if (!recursion_)
owning_thread_ = 0;
if (AtomicOps::Decrement(&lock_queue_) > 0 && !recursion_)
dispatch_semaphore_signal(semaphore_);
# else
pthread_mutex_unlock(&mutex_);
# endif
#else
# error Unsupported platform.
#endif
}
bool CriticalSection::CurrentThreadIsOwner() const {
#if defined(WEBRTC_WIN)
// OwningThread has type HANDLE but actually contains the Thread ID:
// http://stackoverflow.com/questions/12675301/why-is-the-owningthread-member-of-critical-section-of-type-handle-when-it-is-de
// Converting through size_t avoids the VS 2015 warning C4312: conversion from
// 'type1' to 'type2' of greater size
return crit_.OwningThread ==
reinterpret_cast<HANDLE>(static_cast<size_t>(GetCurrentThreadId()));
#elif defined(WEBRTC_POSIX)
# if CS_DEBUG_CHECKS
return IsThreadRefEqual(thread_, CurrentThreadRef());
# else
return true;
# endif // CS_DEBUG_CHECKS
#else
# error Unsupported platform.
#endif
}
CritScope::CritScope(const CriticalSection* cs) : cs_(cs) { cs_->Enter(); }
CritScope::~CritScope() { cs_->Leave(); }
TryCritScope::TryCritScope(const CriticalSection* cs)
: cs_(cs), locked_(cs->TryEnter()) {
CS_DEBUG_CODE(lock_was_called_ = false);
RTC_UNUSED(lock_was_called_);
}
TryCritScope::~TryCritScope() {
CS_DEBUG_CODE(RTC_DCHECK(lock_was_called_));
if (locked_)
cs_->Leave();
}
bool TryCritScope::locked() const {
CS_DEBUG_CODE(lock_was_called_ = true);
return locked_;
}
void GlobalLockPod::Lock() {
#if !defined(WEBRTC_WIN) && (!defined(WEBRTC_MAC) || USE_NATIVE_MUTEX_ON_MAC)
const struct timespec ts_null = {0};
#endif
while (AtomicOps::CompareAndSwap(&lock_acquired, 0, 1)) {
#if defined(WEBRTC_WIN)
::Sleep(0);
#elif defined(WEBRTC_MAC) && !USE_NATIVE_MUTEX_ON_MAC
sched_yield();
#else
nanosleep(&ts_null, nullptr);
#endif
}
}
void GlobalLockPod::Unlock() {
int old_value = AtomicOps::CompareAndSwap(&lock_acquired, 1, 0);
RTC_DCHECK_EQ(1, old_value) << "Unlock called without calling Lock first";
}
GlobalLock::GlobalLock() {
lock_acquired = 0;
}
GlobalLockScope::GlobalLockScope(GlobalLockPod* lock)
: lock_(lock) {
lock_->Lock();
}
GlobalLockScope::~GlobalLockScope() {
lock_->Unlock();
}
} // namespace rtc

View File

@ -0,0 +1,156 @@
/*
* 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_RTC_BASE_CRITICALSECTION_H_
#define WEBRTC_RTC_BASE_CRITICALSECTION_H_
#include "webrtc/base/atomicops.h"
#include "webrtc/base/checks.h"
#include "webrtc/base/constructormagic.h"
#include "webrtc/base/platform_thread_types.h"
#include "webrtc/base/thread_annotations.h"
#include "webrtc/typedefs.h"
#if defined(WEBRTC_WIN)
// Include winsock2.h before including <windows.h> to maintain consistency with
// win32.h. We can't include win32.h directly here since it pulls in
// headers such as basictypes.h which causes problems in Chromium where webrtc
// exists as two separate projects, webrtc and libjingle.
#include <winsock2.h>
#include <windows.h>
#include <sal.h> // must come after windows headers.
#endif // defined(WEBRTC_WIN)
#if defined(WEBRTC_POSIX)
#include <pthread.h>
#endif
// See notes in the 'Performance' unit test for the effects of this flag.
#define USE_NATIVE_MUTEX_ON_MAC 0
#if defined(WEBRTC_MAC) && !USE_NATIVE_MUTEX_ON_MAC
#include <dispatch/dispatch.h>
#endif
#define CS_DEBUG_CHECKS RTC_DCHECK_IS_ON
#if CS_DEBUG_CHECKS
#define CS_DEBUG_CODE(x) x
#else // !CS_DEBUG_CHECKS
#define CS_DEBUG_CODE(x)
#endif // !CS_DEBUG_CHECKS
namespace rtc {
// Locking methods (Enter, TryEnter, Leave)are const to permit protecting
// members inside a const context without requiring mutable CriticalSections
// everywhere.
class LOCKABLE CriticalSection {
public:
CriticalSection();
~CriticalSection();
void Enter() const EXCLUSIVE_LOCK_FUNCTION();
bool TryEnter() const EXCLUSIVE_TRYLOCK_FUNCTION(true);
void Leave() const UNLOCK_FUNCTION();
private:
// Use only for RTC_DCHECKing.
bool CurrentThreadIsOwner() const;
#if defined(WEBRTC_WIN)
mutable CRITICAL_SECTION crit_;
#elif defined(WEBRTC_POSIX)
# if defined(WEBRTC_MAC) && !USE_NATIVE_MUTEX_ON_MAC
// Number of times the lock has been locked + number of threads waiting.
// TODO(tommi): We could use this number and subtract the recursion count
// to find places where we have multiple threads contending on the same lock.
mutable volatile int lock_queue_;
// |recursion_| represents the recursion count + 1 for the thread that owns
// the lock. Only modified by the thread that owns the lock.
mutable int recursion_;
// Used to signal a single waiting thread when the lock becomes available.
mutable dispatch_semaphore_t semaphore_;
// The thread that currently holds the lock. Required to handle recursion.
mutable PlatformThreadRef owning_thread_;
# else
mutable pthread_mutex_t mutex_;
# endif
mutable PlatformThreadRef thread_; // Only used by RTC_DCHECKs.
mutable int recursion_count_; // Only used by RTC_DCHECKs.
#else // !defined(WEBRTC_WIN) && !defined(WEBRTC_POSIX)
# error Unsupported platform.
#endif
};
// CritScope, for serializing execution through a scope.
class SCOPED_LOCKABLE CritScope {
public:
explicit CritScope(const CriticalSection* cs) EXCLUSIVE_LOCK_FUNCTION(cs);
~CritScope() UNLOCK_FUNCTION();
private:
const CriticalSection* const cs_;
RTC_DISALLOW_COPY_AND_ASSIGN(CritScope);
};
// Tries to lock a critical section on construction via
// CriticalSection::TryEnter, and unlocks on destruction if the
// lock was taken. Never blocks.
//
// IMPORTANT: Unlike CritScope, the lock may not be owned by this thread in
// subsequent code. Users *must* check locked() to determine if the
// lock was taken. If you're not calling locked(), you're doing it wrong!
class TryCritScope {
public:
explicit TryCritScope(const CriticalSection* cs);
~TryCritScope();
#if defined(WEBRTC_WIN)
_Check_return_ bool locked() const;
#elif defined(WEBRTC_POSIX)
bool locked() const __attribute__ ((__warn_unused_result__));
#else // !defined(WEBRTC_WIN) && !defined(WEBRTC_POSIX)
# error Unsupported platform.
#endif
private:
const CriticalSection* const cs_;
const bool locked_;
mutable bool lock_was_called_; // Only used by RTC_DCHECKs.
RTC_DISALLOW_COPY_AND_ASSIGN(TryCritScope);
};
// A POD lock used to protect global variables. Do NOT use for other purposes.
// No custom constructor or private data member should be added.
class LOCKABLE GlobalLockPod {
public:
void Lock() EXCLUSIVE_LOCK_FUNCTION();
void Unlock() UNLOCK_FUNCTION();
volatile int lock_acquired;
};
class GlobalLock : public GlobalLockPod {
public:
GlobalLock();
};
// GlobalLockScope, for serializing execution through a scope.
class SCOPED_LOCKABLE GlobalLockScope {
public:
explicit GlobalLockScope(GlobalLockPod* lock) EXCLUSIVE_LOCK_FUNCTION(lock);
~GlobalLockScope() UNLOCK_FUNCTION();
private:
GlobalLockPod* const lock_;
RTC_DISALLOW_COPY_AND_ASSIGN(GlobalLockScope);
};
} // namespace rtc
#endif // WEBRTC_RTC_BASE_CRITICALSECTION_H_

View File

@ -0,0 +1,413 @@
/*
* Copyright 2014 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.
*/
#include <memory>
#include <set>
#include <vector>
#include "webrtc/base/arraysize.h"
#include "webrtc/base/checks.h"
#include "webrtc/base/criticalsection.h"
#include "webrtc/base/event.h"
#include "webrtc/base/gunit.h"
#include "webrtc/base/platform_thread.h"
#include "webrtc/base/thread.h"
namespace rtc {
namespace {
const int kLongTime = 10000; // 10 seconds
const int kNumThreads = 16;
const int kOperationsToRun = 1000;
class UniqueValueVerifier {
public:
void Verify(const std::vector<int>& values) {
for (size_t i = 0; i < values.size(); ++i) {
std::pair<std::set<int>::iterator, bool> result =
all_values_.insert(values[i]);
// Each value should only be taken by one thread, so if this value
// has already been added, something went wrong.
EXPECT_TRUE(result.second)
<< " Thread=" << Thread::Current() << " value=" << values[i];
}
}
void Finalize() {}
private:
std::set<int> all_values_;
};
class CompareAndSwapVerifier {
public:
CompareAndSwapVerifier() : zero_count_(0) {}
void Verify(const std::vector<int>& values) {
for (auto v : values) {
if (v == 0) {
EXPECT_EQ(0, zero_count_) << "Thread=" << Thread::Current();
++zero_count_;
} else {
EXPECT_EQ(1, v) << " Thread=" << Thread::Current();
}
}
}
void Finalize() {
EXPECT_EQ(1, zero_count_);
}
private:
int zero_count_;
};
class RunnerBase : public MessageHandler {
public:
explicit RunnerBase(int value)
: threads_active_(0),
start_event_(true, false),
done_event_(true, false),
shared_value_(value) {}
bool Run() {
// Signal all threads to start.
start_event_.Set();
// Wait for all threads to finish.
return done_event_.Wait(kLongTime);
}
void SetExpectedThreadCount(int count) {
threads_active_ = count;
}
int shared_value() const { return shared_value_; }
protected:
// Derived classes must override OnMessage, and call BeforeStart and AfterEnd
// at the beginning and the end of OnMessage respectively.
void BeforeStart() {
ASSERT_TRUE(start_event_.Wait(kLongTime));
}
// Returns true if all threads have finished.
bool AfterEnd() {
if (AtomicOps::Decrement(&threads_active_) == 0) {
done_event_.Set();
return true;
}
return false;
}
int threads_active_;
Event start_event_;
Event done_event_;
int shared_value_;
};
class LOCKABLE CriticalSectionLock {
public:
void Lock() EXCLUSIVE_LOCK_FUNCTION() {
cs_.Enter();
}
void Unlock() UNLOCK_FUNCTION() {
cs_.Leave();
}
private:
CriticalSection cs_;
};
template <class Lock>
class LockRunner : public RunnerBase {
public:
LockRunner() : RunnerBase(0) {}
void OnMessage(Message* msg) override {
BeforeStart();
lock_.Lock();
EXPECT_EQ(0, shared_value_);
int old = shared_value_;
// Use a loop to increase the chance of race.
for (int i = 0; i < kOperationsToRun; ++i) {
++shared_value_;
}
EXPECT_EQ(old + kOperationsToRun, shared_value_);
shared_value_ = 0;
lock_.Unlock();
AfterEnd();
}
private:
Lock lock_;
};
template <class Op, class Verifier>
class AtomicOpRunner : public RunnerBase {
public:
explicit AtomicOpRunner(int initial_value) : RunnerBase(initial_value) {}
void OnMessage(Message* msg) override {
BeforeStart();
std::vector<int> values;
values.reserve(kOperationsToRun);
// Generate a bunch of values by updating shared_value_ atomically.
for (int i = 0; i < kOperationsToRun; ++i) {
values.push_back(Op::AtomicOp(&shared_value_));
}
{ // Add them all to the set.
CritScope cs(&all_values_crit_);
verifier_.Verify(values);
}
if (AfterEnd()) {
verifier_.Finalize();
}
}
private:
CriticalSection all_values_crit_;
Verifier verifier_;
};
struct IncrementOp {
static int AtomicOp(int* i) { return AtomicOps::Increment(i); }
};
struct DecrementOp {
static int AtomicOp(int* i) { return AtomicOps::Decrement(i); }
};
struct CompareAndSwapOp {
static int AtomicOp(int* i) { return AtomicOps::CompareAndSwap(i, 0, 1); }
};
void StartThreads(std::vector<std::unique_ptr<Thread>>* threads,
MessageHandler* handler) {
for (int i = 0; i < kNumThreads; ++i) {
std::unique_ptr<Thread> thread(new Thread());
thread->Start();
thread->Post(RTC_FROM_HERE, handler);
threads->push_back(std::move(thread));
}
}
} // namespace
TEST(AtomicOpsTest, Simple) {
int value = 0;
EXPECT_EQ(1, AtomicOps::Increment(&value));
EXPECT_EQ(1, value);
EXPECT_EQ(2, AtomicOps::Increment(&value));
EXPECT_EQ(2, value);
EXPECT_EQ(1, AtomicOps::Decrement(&value));
EXPECT_EQ(1, value);
EXPECT_EQ(0, AtomicOps::Decrement(&value));
EXPECT_EQ(0, value);
}
TEST(AtomicOpsTest, SimplePtr) {
class Foo {};
Foo* volatile foo = nullptr;
std::unique_ptr<Foo> a(new Foo());
std::unique_ptr<Foo> b(new Foo());
// Reading the initial value should work as expected.
EXPECT_TRUE(rtc::AtomicOps::AcquireLoadPtr(&foo) == nullptr);
// Setting using compare and swap should work.
EXPECT_TRUE(rtc::AtomicOps::CompareAndSwapPtr(
&foo, static_cast<Foo*>(nullptr), a.get()) == nullptr);
EXPECT_TRUE(rtc::AtomicOps::AcquireLoadPtr(&foo) == a.get());
// Setting another value but with the wrong previous pointer should fail
// (remain a).
EXPECT_TRUE(rtc::AtomicOps::CompareAndSwapPtr(
&foo, static_cast<Foo*>(nullptr), b.get()) == a.get());
EXPECT_TRUE(rtc::AtomicOps::AcquireLoadPtr(&foo) == a.get());
// Replacing a with b should work.
EXPECT_TRUE(rtc::AtomicOps::CompareAndSwapPtr(&foo, a.get(), b.get()) ==
a.get());
EXPECT_TRUE(rtc::AtomicOps::AcquireLoadPtr(&foo) == b.get());
}
TEST(AtomicOpsTest, Increment) {
// Create and start lots of threads.
AtomicOpRunner<IncrementOp, UniqueValueVerifier> runner(0);
std::vector<std::unique_ptr<Thread>> threads;
StartThreads(&threads, &runner);
runner.SetExpectedThreadCount(kNumThreads);
// Release the hounds!
EXPECT_TRUE(runner.Run());
EXPECT_EQ(kOperationsToRun * kNumThreads, runner.shared_value());
}
TEST(AtomicOpsTest, Decrement) {
// Create and start lots of threads.
AtomicOpRunner<DecrementOp, UniqueValueVerifier> runner(
kOperationsToRun * kNumThreads);
std::vector<std::unique_ptr<Thread>> threads;
StartThreads(&threads, &runner);
runner.SetExpectedThreadCount(kNumThreads);
// Release the hounds!
EXPECT_TRUE(runner.Run());
EXPECT_EQ(0, runner.shared_value());
}
TEST(AtomicOpsTest, CompareAndSwap) {
// Create and start lots of threads.
AtomicOpRunner<CompareAndSwapOp, CompareAndSwapVerifier> runner(0);
std::vector<std::unique_ptr<Thread>> threads;
StartThreads(&threads, &runner);
runner.SetExpectedThreadCount(kNumThreads);
// Release the hounds!
EXPECT_TRUE(runner.Run());
EXPECT_EQ(1, runner.shared_value());
}
TEST(GlobalLockTest, Basic) {
// Create and start lots of threads.
LockRunner<GlobalLock> runner;
std::vector<std::unique_ptr<Thread>> threads;
StartThreads(&threads, &runner);
runner.SetExpectedThreadCount(kNumThreads);
// Release the hounds!
EXPECT_TRUE(runner.Run());
EXPECT_EQ(0, runner.shared_value());
}
TEST(CriticalSectionTest, Basic) {
// Create and start lots of threads.
LockRunner<CriticalSectionLock> runner;
std::vector<std::unique_ptr<Thread>> threads;
StartThreads(&threads, &runner);
runner.SetExpectedThreadCount(kNumThreads);
// Release the hounds!
EXPECT_TRUE(runner.Run());
EXPECT_EQ(0, runner.shared_value());
}
class PerfTestData {
public:
PerfTestData(int expected_count, Event* event)
: cache_line_barrier_1_(), cache_line_barrier_2_(),
expected_count_(expected_count), event_(event) {
cache_line_barrier_1_[0]++; // Avoid 'is not used'.
cache_line_barrier_2_[0]++; // Avoid 'is not used'.
}
~PerfTestData() {}
void AddToCounter(int add) {
rtc::CritScope cs(&lock_);
my_counter_ += add;
if (my_counter_ == expected_count_)
event_->Set();
}
int64_t total() const {
// Assume that only one thread is running now.
return my_counter_;
}
private:
uint8_t cache_line_barrier_1_[64];
CriticalSection lock_;
uint8_t cache_line_barrier_2_[64];
int64_t my_counter_ = 0;
const int expected_count_;
Event* const event_;
};
class PerfTestThread {
public:
PerfTestThread() : thread_(&ThreadFunc, this, "CsPerf") {}
void Start(PerfTestData* data, int repeats, int id) {
RTC_DCHECK(!thread_.IsRunning());
RTC_DCHECK(!data_);
data_ = data;
repeats_ = repeats;
my_id_ = id;
thread_.Start();
}
void Stop() {
RTC_DCHECK(thread_.IsRunning());
RTC_DCHECK(data_);
thread_.Stop();
repeats_ = 0;
data_ = nullptr;
my_id_ = 0;
}
private:
static bool ThreadFunc(void* param) {
PerfTestThread* me = static_cast<PerfTestThread*>(param);
for (int i = 0; i < me->repeats_; ++i)
me->data_->AddToCounter(me->my_id_);
return false;
}
PlatformThread thread_;
PerfTestData* data_ = nullptr;
int repeats_ = 0;
int my_id_ = 0;
};
// Comparison of output of this test as tested on a MacBook Pro Retina, 15-inch,
// Mid 2014, 2,8 GHz Intel Core i7, 16 GB 1600 MHz DDR3,
// running OS X El Capitan, 10.11.2.
//
// Native mutex implementation:
// Approximate CPU usage:
// System: ~16%
// User mode: ~1.3%
// Idle: ~82%
// Unit test output:
// [ OK ] CriticalSectionTest.Performance (234545 ms)
//
// Special partially spin lock based implementation:
// Approximate CPU usage:
// System: ~75%
// User mode: ~16%
// Idle: ~8%
// Unit test output:
// [ OK ] CriticalSectionTest.Performance (2107 ms)
//
// The test is disabled by default to avoid unecessarily loading the bots.
TEST(CriticalSectionTest, DISABLED_Performance) {
PerfTestThread threads[8];
Event event(false, false);
static const int kThreadRepeats = 10000000;
static const int kExpectedCount = kThreadRepeats * arraysize(threads);
PerfTestData test_data(kExpectedCount, &event);
for (auto& t : threads)
t.Start(&test_data, kThreadRepeats, 1);
event.Wait(Event::kForever);
for (auto& t : threads)
t.Stop();
}
} // namespace rtc

View File

@ -0,0 +1,75 @@
/*
* 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.
*/
#include "webrtc/base/cryptstring.h"
namespace rtc {
size_t EmptyCryptStringImpl::GetLength() const {
return 0;
}
void EmptyCryptStringImpl::CopyTo(char* dest, bool nullterminate) const {
if (nullterminate) {
*dest = '\0';
}
}
std::string EmptyCryptStringImpl::UrlEncode() const {
return "";
}
CryptStringImpl* EmptyCryptStringImpl::Copy() const {
return new EmptyCryptStringImpl();
}
void EmptyCryptStringImpl::CopyRawTo(std::vector<unsigned char>* dest) const {
dest->clear();
}
CryptString::CryptString() : impl_(new EmptyCryptStringImpl()) {
}
CryptString::CryptString(const CryptString& other)
: impl_(other.impl_->Copy()) {
}
CryptString::CryptString(const CryptStringImpl& impl) : impl_(impl.Copy()) {
}
CryptString::~CryptString() = default;
size_t InsecureCryptStringImpl::GetLength() const {
return password_.size();
}
void InsecureCryptStringImpl::CopyTo(char* dest, bool nullterminate) const {
memcpy(dest, password_.data(), password_.size());
if (nullterminate)
dest[password_.size()] = 0;
}
std::string InsecureCryptStringImpl::UrlEncode() const {
return password_;
}
CryptStringImpl* InsecureCryptStringImpl::Copy() const {
InsecureCryptStringImpl* copy = new InsecureCryptStringImpl;
copy->password() = password_;
return copy;
}
void InsecureCryptStringImpl::CopyRawTo(
std::vector<unsigned char>* dest) const {
dest->resize(password_.size());
memcpy(&dest->front(), password_.data(), password_.size());
}
}; // namespace rtc

View File

@ -0,0 +1,167 @@
/*
* 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_RTC_BASE_CRYPTSTRING_H_
#define WEBRTC_RTC_BASE_CRYPTSTRING_H_
#include <string.h>
#include <memory>
#include <string>
#include <vector>
namespace rtc {
class CryptStringImpl {
public:
virtual ~CryptStringImpl() {}
virtual size_t GetLength() const = 0;
virtual void CopyTo(char * dest, bool nullterminate) const = 0;
virtual std::string UrlEncode() const = 0;
virtual CryptStringImpl * Copy() const = 0;
virtual void CopyRawTo(std::vector<unsigned char> * dest) const = 0;
};
class EmptyCryptStringImpl : public CryptStringImpl {
public:
~EmptyCryptStringImpl() override {}
size_t GetLength() const override;
void CopyTo(char* dest, bool nullterminate) const override;
std::string UrlEncode() const override;
CryptStringImpl* Copy() const override;
void CopyRawTo(std::vector<unsigned char>* dest) const override;
};
class CryptString {
public:
CryptString();
size_t GetLength() const { return impl_->GetLength(); }
void CopyTo(char * dest, bool nullterminate) const { impl_->CopyTo(dest, nullterminate); }
CryptString(const CryptString& other);
explicit CryptString(const CryptStringImpl& impl);
~CryptString();
CryptString & operator=(const CryptString & other) {
if (this != &other) {
impl_.reset(other.impl_->Copy());
}
return *this;
}
void Clear() { impl_.reset(new EmptyCryptStringImpl()); }
std::string UrlEncode() const { return impl_->UrlEncode(); }
void CopyRawTo(std::vector<unsigned char> * dest) const {
return impl_->CopyRawTo(dest);
}
private:
std::unique_ptr<const CryptStringImpl> impl_;
};
// Used for constructing strings where a password is involved and we
// need to ensure that we zero memory afterwards
class FormatCryptString {
public:
FormatCryptString() {
storage_ = new char[32];
capacity_ = 32;
length_ = 0;
storage_[0] = 0;
}
void Append(const std::string & text) {
Append(text.data(), text.length());
}
void Append(const char * data, size_t length) {
EnsureStorage(length_ + length + 1);
memcpy(storage_ + length_, data, length);
length_ += length;
storage_[length_] = '\0';
}
void Append(const CryptString * password) {
size_t len = password->GetLength();
EnsureStorage(length_ + len + 1);
password->CopyTo(storage_ + length_, true);
length_ += len;
}
size_t GetLength() {
return length_;
}
const char * GetData() {
return storage_;
}
// Ensures storage of at least n bytes
void EnsureStorage(size_t n) {
if (capacity_ >= n) {
return;
}
size_t old_capacity = capacity_;
char * old_storage = storage_;
for (;;) {
capacity_ *= 2;
if (capacity_ >= n)
break;
}
storage_ = new char[capacity_];
if (old_capacity) {
memcpy(storage_, old_storage, length_);
// zero memory in a way that an optimizer won't optimize it out
old_storage[0] = 0;
for (size_t i = 1; i < old_capacity; i++) {
old_storage[i] = old_storage[i - 1];
}
delete[] old_storage;
}
}
~FormatCryptString() {
if (capacity_) {
storage_[0] = 0;
for (size_t i = 1; i < capacity_; i++) {
storage_[i] = storage_[i - 1];
}
}
delete[] storage_;
}
private:
char * storage_;
size_t capacity_;
size_t length_;
};
class InsecureCryptStringImpl : public CryptStringImpl {
public:
std::string& password() { return password_; }
const std::string& password() const { return password_; }
~InsecureCryptStringImpl() override = default;
size_t GetLength() const override;
void CopyTo(char* dest, bool nullterminate) const override;
std::string UrlEncode() const override;
CryptStringImpl* Copy() const override;
void CopyRawTo(std::vector<unsigned char>* dest) const override;
private:
std::string password_;
};
}
#endif // WEBRTC_RTC_BASE_CRYPTSTRING_H_

View File

@ -0,0 +1,45 @@
/*
* Copyright (c) 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_RTC_BASE_DEPRECATION_H_
#define WEBRTC_RTC_BASE_DEPRECATION_H_
// Annotate the declarations of deprecated functions with this to cause a
// compiler warning when they're used. Like so:
//
// RTC_DEPRECATED std::pony PonyPlz(const std::pony_spec& ps);
//
// NOTE 1: The annotation goes on the declaration in the .h file, not the
// definition in the .cc file!
//
// NOTE 2: In order to keep unit testing the deprecated function without
// getting warnings, do something like this:
//
// std::pony DEPRECATED_PonyPlz(const std::pony_spec& ps);
// RTC_DEPRECATED inline std::pony PonyPlz(const std::pony_spec& ps) {
// return DEPRECATED_PonyPlz(ps);
// }
//
// In other words, rename the existing function, and provide an inline wrapper
// using the original name that calls it. That way, callers who are willing to
// call it using the DEPRECATED_-prefixed name don't get the warning.
//
// TODO(kwiberg): Remove this when we can use [[deprecated]] from C++14.
#if defined(_MSC_VER)
// Note: Deprecation warnings seem to fail to trigger on Windows
// (https://bugs.chromium.org/p/webrtc/issues/detail?id=5368).
#define RTC_DEPRECATED __declspec(deprecated)
#elif defined(__GNUC__)
#define RTC_DEPRECATED __attribute__ ((__deprecated__))
#else
#define RTC_DEPRECATED
#endif
#endif // WEBRTC_RTC_BASE_DEPRECATION_H_

45
webrtc/rtc_base/dscp.h Normal file
View File

@ -0,0 +1,45 @@
/*
* Copyright 2013 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_RTC_BASE_DSCP_H_
#define WEBRTC_RTC_BASE_DSCP_H_
namespace rtc {
// Differentiated Services Code Point.
// See http://tools.ietf.org/html/rfc2474 for details.
enum DiffServCodePoint {
DSCP_NO_CHANGE = -1,
DSCP_DEFAULT = 0, // Same as DSCP_CS0
DSCP_CS0 = 0, // The default
DSCP_CS1 = 8, // Bulk/background traffic
DSCP_AF11 = 10,
DSCP_AF12 = 12,
DSCP_AF13 = 14,
DSCP_CS2 = 16,
DSCP_AF21 = 18,
DSCP_AF22 = 20,
DSCP_AF23 = 22,
DSCP_CS3 = 24,
DSCP_AF31 = 26,
DSCP_AF32 = 28,
DSCP_AF33 = 30,
DSCP_CS4 = 32,
DSCP_AF41 = 34, // Video
DSCP_AF42 = 36, // Video
DSCP_AF43 = 38, // Video
DSCP_CS5 = 40, // Video
DSCP_EF = 46, // Voice
DSCP_CS6 = 48, // Voice
DSCP_CS7 = 56, // Control messages
};
} // namespace rtc
#endif // WEBRTC_RTC_BASE_DSCP_H_

136
webrtc/rtc_base/event.cc Normal file
View File

@ -0,0 +1,136 @@
/*
* 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.
*/
#include "webrtc/base/event.h"
#if defined(WEBRTC_WIN)
#include <windows.h>
#elif defined(WEBRTC_POSIX)
#include <pthread.h>
#include <sys/time.h>
#include <time.h>
#else
#error "Must define either WEBRTC_WIN or WEBRTC_POSIX."
#endif
#include "webrtc/base/checks.h"
namespace rtc {
#if defined(WEBRTC_WIN)
Event::Event(bool manual_reset, bool initially_signaled) {
event_handle_ = ::CreateEvent(nullptr, // Security attributes.
manual_reset, initially_signaled,
nullptr); // Name.
RTC_CHECK(event_handle_);
}
Event::~Event() {
CloseHandle(event_handle_);
}
void Event::Set() {
SetEvent(event_handle_);
}
void Event::Reset() {
ResetEvent(event_handle_);
}
bool Event::Wait(int milliseconds) {
DWORD ms = (milliseconds == kForever) ? INFINITE : milliseconds;
return (WaitForSingleObject(event_handle_, ms) == WAIT_OBJECT_0);
}
#elif defined(WEBRTC_POSIX)
Event::Event(bool manual_reset, bool initially_signaled)
: is_manual_reset_(manual_reset),
event_status_(initially_signaled) {
RTC_CHECK(pthread_mutex_init(&event_mutex_, nullptr) == 0);
RTC_CHECK(pthread_cond_init(&event_cond_, nullptr) == 0);
}
Event::~Event() {
pthread_mutex_destroy(&event_mutex_);
pthread_cond_destroy(&event_cond_);
}
void Event::Set() {
pthread_mutex_lock(&event_mutex_);
event_status_ = true;
pthread_cond_broadcast(&event_cond_);
pthread_mutex_unlock(&event_mutex_);
}
void Event::Reset() {
pthread_mutex_lock(&event_mutex_);
event_status_ = false;
pthread_mutex_unlock(&event_mutex_);
}
bool Event::Wait(int milliseconds) {
int error = 0;
struct timespec ts;
if (milliseconds != kForever) {
// Converting from seconds and microseconds (1e-6) plus
// milliseconds (1e-3) to seconds and nanoseconds (1e-9).
#ifdef HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE
// Use relative time version, which tends to be more efficient for
// pthread implementations where provided (like on Android).
ts.tv_sec = milliseconds / 1000;
ts.tv_nsec = (milliseconds % 1000) * 1000000;
#else
struct timeval tv;
gettimeofday(&tv, nullptr);
ts.tv_sec = tv.tv_sec + (milliseconds / 1000);
ts.tv_nsec = tv.tv_usec * 1000 + (milliseconds % 1000) * 1000000;
// Handle overflow.
if (ts.tv_nsec >= 1000000000) {
ts.tv_sec++;
ts.tv_nsec -= 1000000000;
}
#endif
}
pthread_mutex_lock(&event_mutex_);
if (milliseconds != kForever) {
while (!event_status_ && error == 0) {
#ifdef HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE
error = pthread_cond_timedwait_relative_np(
&event_cond_, &event_mutex_, &ts);
#else
error = pthread_cond_timedwait(&event_cond_, &event_mutex_, &ts);
#endif
}
} else {
while (!event_status_ && error == 0)
error = pthread_cond_wait(&event_cond_, &event_mutex_);
}
// NOTE(liulk): Exactly one thread will auto-reset this event. All
// the other threads will think it's unsignaled. This seems to be
// consistent with auto-reset events in WEBRTC_WIN
if (error == 0 && !is_manual_reset_)
event_status_ = false;
pthread_mutex_unlock(&event_mutex_);
return (error == 0);
}
#endif
} // namespace rtc

54
webrtc/rtc_base/event.h Normal file
View File

@ -0,0 +1,54 @@
/*
* 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_RTC_BASE_EVENT_H_
#define WEBRTC_RTC_BASE_EVENT_H_
#include "webrtc/base/constructormagic.h"
#if defined(WEBRTC_WIN)
#include "webrtc/base/win32.h" // NOLINT: consider this a system header.
#elif defined(WEBRTC_POSIX)
#include <pthread.h>
#else
#error "Must define either WEBRTC_WIN or WEBRTC_POSIX."
#endif
namespace rtc {
class Event {
public:
static const int kForever = -1;
Event(bool manual_reset, bool initially_signaled);
~Event();
void Set();
void Reset();
// Wait for the event to become signaled, for the specified number of
// |milliseconds|. To wait indefinetly, pass kForever.
bool Wait(int milliseconds);
private:
#if defined(WEBRTC_WIN)
HANDLE event_handle_;
#elif defined(WEBRTC_POSIX)
pthread_mutex_t event_mutex_;
pthread_cond_t event_cond_;
const bool is_manual_reset_;
bool event_status_;
#endif
RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(Event);
};
} // namespace rtc
#endif // WEBRTC_RTC_BASE_EVENT_H_

View File

@ -0,0 +1,407 @@
/*
* Copyright (c) 2012 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.
*/
#include "webrtc/base/event_tracer.h"
#include <inttypes.h>
#include <string>
#include <vector>
#include "webrtc/base/checks.h"
#include "webrtc/base/criticalsection.h"
#include "webrtc/base/event.h"
#include "webrtc/base/logging.h"
#include "webrtc/base/platform_thread.h"
#include "webrtc/base/stringutils.h"
#include "webrtc/base/timeutils.h"
#include "webrtc/base/trace_event.h"
// This is a guesstimate that should be enough in most cases.
static const size_t kEventLoggerArgsStrBufferInitialSize = 256;
static const size_t kTraceArgBufferLength = 32;
namespace webrtc {
namespace {
GetCategoryEnabledPtr g_get_category_enabled_ptr = nullptr;
AddTraceEventPtr g_add_trace_event_ptr = nullptr;
} // namespace
void SetupEventTracer(GetCategoryEnabledPtr get_category_enabled_ptr,
AddTraceEventPtr add_trace_event_ptr) {
g_get_category_enabled_ptr = get_category_enabled_ptr;
g_add_trace_event_ptr = add_trace_event_ptr;
}
const unsigned char* EventTracer::GetCategoryEnabled(const char* name) {
if (g_get_category_enabled_ptr)
return g_get_category_enabled_ptr(name);
// A string with null terminator means category is disabled.
return reinterpret_cast<const unsigned char*>("\0");
}
// Arguments to this function (phase, etc.) are as defined in
// webrtc/base/trace_event.h.
void EventTracer::AddTraceEvent(char phase,
const unsigned char* category_enabled,
const char* name,
unsigned long long id,
int num_args,
const char** arg_names,
const unsigned char* arg_types,
const unsigned long long* arg_values,
unsigned char flags) {
if (g_add_trace_event_ptr) {
g_add_trace_event_ptr(phase,
category_enabled,
name,
id,
num_args,
arg_names,
arg_types,
arg_values,
flags);
}
}
} // namespace webrtc
namespace rtc {
namespace tracing {
namespace {
static void EventTracingThreadFunc(void* params);
// Atomic-int fast path for avoiding logging when disabled.
static volatile int g_event_logging_active = 0;
// TODO(pbos): Log metadata for all threads, etc.
class EventLogger final {
public:
EventLogger()
: logging_thread_(EventTracingThreadFunc,
this,
"EventTracingThread",
kLowPriority),
shutdown_event_(false, false) {}
~EventLogger() { RTC_DCHECK(thread_checker_.CalledOnValidThread()); }
void AddTraceEvent(const char* name,
const unsigned char* category_enabled,
char phase,
int num_args,
const char** arg_names,
const unsigned char* arg_types,
const unsigned long long* arg_values,
uint64_t timestamp,
int pid,
rtc::PlatformThreadId thread_id) {
std::vector<TraceArg> args(num_args);
for (int i = 0; i < num_args; ++i) {
TraceArg& arg = args[i];
arg.name = arg_names[i];
arg.type = arg_types[i];
arg.value.as_uint = arg_values[i];
// Value is a pointer to a temporary string, so we have to make a copy.
if (arg.type == TRACE_VALUE_TYPE_COPY_STRING) {
// Space for the string and for the terminating null character.
size_t str_length = strlen(arg.value.as_string) + 1;
char* str_copy = new char[str_length];
memcpy(str_copy, arg.value.as_string, str_length);
arg.value.as_string = str_copy;
}
}
rtc::CritScope lock(&crit_);
trace_events_.push_back(
{name, category_enabled, phase, args, timestamp, 1, thread_id});
}
// The TraceEvent format is documented here:
// https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/preview
void Log() {
RTC_DCHECK(output_file_);
static const int kLoggingIntervalMs = 100;
fprintf(output_file_, "{ \"traceEvents\": [\n");
bool has_logged_event = false;
while (true) {
bool shutting_down = shutdown_event_.Wait(kLoggingIntervalMs);
std::vector<TraceEvent> events;
{
rtc::CritScope lock(&crit_);
trace_events_.swap(events);
}
std::string args_str;
args_str.reserve(kEventLoggerArgsStrBufferInitialSize);
for (TraceEvent& e : events) {
args_str.clear();
if (!e.args.empty()) {
args_str += ", \"args\": {";
bool is_first_argument = true;
for (TraceArg& arg : e.args) {
if (!is_first_argument)
args_str += ",";
is_first_argument = false;
args_str += " \"";
args_str += arg.name;
args_str += "\": ";
args_str += TraceArgValueAsString(arg);
// Delete our copy of the string.
if (arg.type == TRACE_VALUE_TYPE_COPY_STRING) {
delete[] arg.value.as_string;
arg.value.as_string = nullptr;
}
}
args_str += " }";
}
fprintf(output_file_,
"%s{ \"name\": \"%s\""
", \"cat\": \"%s\""
", \"ph\": \"%c\""
", \"ts\": %" PRIu64
", \"pid\": %d"
#if defined(WEBRTC_WIN)
", \"tid\": %lu"
#else
", \"tid\": %d"
#endif // defined(WEBRTC_WIN)
"%s"
"}\n",
has_logged_event ? "," : " ", e.name, e.category_enabled,
e.phase, e.timestamp, e.pid, e.tid, args_str.c_str());
has_logged_event = true;
}
if (shutting_down)
break;
}
fprintf(output_file_, "]}\n");
if (output_file_owned_)
fclose(output_file_);
output_file_ = nullptr;
}
void Start(FILE* file, bool owned) {
RTC_DCHECK(thread_checker_.CalledOnValidThread());
RTC_DCHECK(file);
RTC_DCHECK(!output_file_);
output_file_ = file;
output_file_owned_ = owned;
{
rtc::CritScope lock(&crit_);
// Since the atomic fast-path for adding events to the queue can be
// bypassed while the logging thread is shutting down there may be some
// stale events in the queue, hence the vector needs to be cleared to not
// log events from a previous logging session (which may be days old).
trace_events_.clear();
}
// Enable event logging (fast-path). This should be disabled since starting
// shouldn't be done twice.
RTC_CHECK_EQ(0,
rtc::AtomicOps::CompareAndSwap(&g_event_logging_active, 0, 1));
// Finally start, everything should be set up now.
logging_thread_.Start();
TRACE_EVENT_INSTANT0("webrtc", "EventLogger::Start");
}
void Stop() {
RTC_DCHECK(thread_checker_.CalledOnValidThread());
TRACE_EVENT_INSTANT0("webrtc", "EventLogger::Stop");
// Try to stop. Abort if we're not currently logging.
if (rtc::AtomicOps::CompareAndSwap(&g_event_logging_active, 1, 0) == 0)
return;
// Wake up logging thread to finish writing.
shutdown_event_.Set();
// Join the logging thread.
logging_thread_.Stop();
}
private:
struct TraceArg {
const char* name;
unsigned char type;
// Copied from webrtc/base/trace_event.h TraceValueUnion.
union TraceArgValue {
bool as_bool;
unsigned long long as_uint;
long long as_int;
double as_double;
const void* as_pointer;
const char* as_string;
} value;
// Assert that the size of the union is equal to the size of the as_uint
// field since we are assigning to arbitrary types using it.
static_assert(sizeof(TraceArgValue) == sizeof(unsigned long long),
"Size of TraceArg value union is not equal to the size of "
"the uint field of that union.");
};
struct TraceEvent {
const char* name;
const unsigned char* category_enabled;
char phase;
std::vector<TraceArg> args;
uint64_t timestamp;
int pid;
rtc::PlatformThreadId tid;
};
static std::string TraceArgValueAsString(TraceArg arg) {
std::string output;
if (arg.type == TRACE_VALUE_TYPE_STRING ||
arg.type == TRACE_VALUE_TYPE_COPY_STRING) {
// Space for every character to be an espaced character + two for
// quatation marks.
output.reserve(strlen(arg.value.as_string) * 2 + 2);
output += '\"';
const char* c = arg.value.as_string;
do {
if (*c == '"' || *c == '\\') {
output += '\\';
output += *c;
} else {
output += *c;
}
} while (*++c);
output += '\"';
} else {
output.resize(kTraceArgBufferLength);
size_t print_length = 0;
switch (arg.type) {
case TRACE_VALUE_TYPE_BOOL:
if (arg.value.as_bool) {
strcpy(&output[0], "true");
print_length = 4;
} else {
strcpy(&output[0], "false");
print_length = 5;
}
break;
case TRACE_VALUE_TYPE_UINT:
print_length = sprintfn(&output[0], kTraceArgBufferLength, "%llu",
arg.value.as_uint);
break;
case TRACE_VALUE_TYPE_INT:
print_length = sprintfn(&output[0], kTraceArgBufferLength, "%lld",
arg.value.as_int);
break;
case TRACE_VALUE_TYPE_DOUBLE:
print_length = sprintfn(&output[0], kTraceArgBufferLength, "%f",
arg.value.as_double);
break;
case TRACE_VALUE_TYPE_POINTER:
print_length = sprintfn(&output[0], kTraceArgBufferLength, "\"%p\"",
arg.value.as_pointer);
break;
}
size_t output_length = print_length < kTraceArgBufferLength
? print_length
: kTraceArgBufferLength - 1;
// This will hopefully be very close to nop. On most implementations, it
// just writes null byte and sets the length field of the string.
output.resize(output_length);
}
return output;
}
rtc::CriticalSection crit_;
std::vector<TraceEvent> trace_events_ GUARDED_BY(crit_);
rtc::PlatformThread logging_thread_;
rtc::Event shutdown_event_;
rtc::ThreadChecker thread_checker_;
FILE* output_file_ = nullptr;
bool output_file_owned_ = false;
};
static void EventTracingThreadFunc(void* params) {
static_cast<EventLogger*>(params)->Log();
}
static EventLogger* volatile g_event_logger = nullptr;
static const char* const kDisabledTracePrefix = TRACE_DISABLED_BY_DEFAULT("");
const unsigned char* InternalGetCategoryEnabled(const char* name) {
const char* prefix_ptr = &kDisabledTracePrefix[0];
const char* name_ptr = name;
// Check whether name contains the default-disabled prefix.
while (*prefix_ptr == *name_ptr && *prefix_ptr != '\0') {
++prefix_ptr;
++name_ptr;
}
return reinterpret_cast<const unsigned char*>(*prefix_ptr == '\0' ? ""
: name);
}
void InternalAddTraceEvent(char phase,
const unsigned char* category_enabled,
const char* name,
unsigned long long id,
int num_args,
const char** arg_names,
const unsigned char* arg_types,
const unsigned long long* arg_values,
unsigned char flags) {
// Fast path for when event tracing is inactive.
if (rtc::AtomicOps::AcquireLoad(&g_event_logging_active) == 0)
return;
g_event_logger->AddTraceEvent(name, category_enabled, phase, num_args,
arg_names, arg_types, arg_values,
rtc::TimeMicros(), 1, rtc::CurrentThreadId());
}
} // namespace
void SetupInternalTracer() {
RTC_CHECK(rtc::AtomicOps::CompareAndSwapPtr(
&g_event_logger, static_cast<EventLogger*>(nullptr),
new EventLogger()) == nullptr);
webrtc::SetupEventTracer(InternalGetCategoryEnabled, InternalAddTraceEvent);
}
void StartInternalCaptureToFile(FILE* file) {
g_event_logger->Start(file, false);
}
bool StartInternalCapture(const char* filename) {
FILE* file = fopen(filename, "w");
if (!file) {
LOG(LS_ERROR) << "Failed to open trace file '" << filename
<< "' for writing.";
return false;
}
g_event_logger->Start(file, true);
return true;
}
void StopInternalCapture() {
g_event_logger->Stop();
}
void ShutdownInternalTracer() {
StopInternalCapture();
EventLogger* old_logger = rtc::AtomicOps::AcquireLoadPtr(&g_event_logger);
RTC_DCHECK(old_logger);
RTC_CHECK(rtc::AtomicOps::CompareAndSwapPtr(
&g_event_logger, old_logger,
static_cast<EventLogger*>(nullptr)) == old_logger);
delete old_logger;
webrtc::SetupEventTracer(nullptr, nullptr);
}
} // namespace tracing
} // namespace rtc

View File

@ -0,0 +1,85 @@
/*
* Copyright (c) 2012 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.
*/
// This file defines the interface for event tracing in WebRTC.
//
// Event log handlers are set through SetupEventTracer(). User of this API will
// provide two function pointers to handle event tracing calls.
//
// * GetCategoryEnabledPtr
// Event tracing system calls this function to determine if a particular
// event category is enabled.
//
// * AddTraceEventPtr
// Adds a tracing event. It is the user's responsibility to log the data
// provided.
//
// Parameters for the above two functions are described in trace_event.h.
#ifndef WEBRTC_RTC_BASE_EVENT_TRACER_H_
#define WEBRTC_RTC_BASE_EVENT_TRACER_H_
#include <stdio.h>
namespace webrtc {
typedef const unsigned char* (*GetCategoryEnabledPtr)(const char* name);
typedef void (*AddTraceEventPtr)(char phase,
const unsigned char* category_enabled,
const char* name,
unsigned long long id,
int num_args,
const char** arg_names,
const unsigned char* arg_types,
const unsigned long long* arg_values,
unsigned char flags);
// User of WebRTC can call this method to setup event tracing.
//
// This method must be called before any WebRTC methods. Functions
// provided should be thread-safe.
void SetupEventTracer(
GetCategoryEnabledPtr get_category_enabled_ptr,
AddTraceEventPtr add_trace_event_ptr);
// This class defines interface for the event tracing system to call
// internally. Do not call these methods directly.
class EventTracer {
public:
static const unsigned char* GetCategoryEnabled(
const char* name);
static void AddTraceEvent(
char phase,
const unsigned char* category_enabled,
const char* name,
unsigned long long id,
int num_args,
const char** arg_names,
const unsigned char* arg_types,
const unsigned long long* arg_values,
unsigned char flags);
};
} // namespace webrtc
namespace rtc {
namespace tracing {
// Set up internal event tracer.
void SetupInternalTracer();
bool StartInternalCapture(const char* filename);
void StartInternalCaptureToFile(FILE* file);
void StopInternalCapture();
// Make sure we run this, this will tear down the internal tracing.
void ShutdownInternalTracer();
} // namespace tracing
} // namespace rtc
#endif // WEBRTC_RTC_BASE_EVENT_TRACER_H_

View File

@ -0,0 +1,82 @@
/*
* Copyright (c) 2012 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.
*/
#include "webrtc/base/event_tracer.h"
#include "webrtc/base/trace_event.h"
#include "webrtc/system_wrappers/include/static_instance.h"
#include "webrtc/test/gtest.h"
namespace {
class TestStatistics {
public:
TestStatistics() : events_logged_(0) {
}
void Reset() {
events_logged_ = 0;
}
void Increment() {
++events_logged_;
}
int Count() const { return events_logged_; }
static TestStatistics* Get() {
static TestStatistics* test_stats = nullptr;
if (!test_stats)
test_stats = new TestStatistics();
return test_stats;
}
private:
int events_logged_;
};
static const unsigned char* GetCategoryEnabledHandler(const char* name) {
return reinterpret_cast<const unsigned char*>("test");
}
static void AddTraceEventHandler(char phase,
const unsigned char* category_enabled,
const char* name,
unsigned long long id,
int num_args,
const char** arg_names,
const unsigned char* arg_types,
const unsigned long long* arg_values,
unsigned char flags) {
TestStatistics::Get()->Increment();
}
} // namespace
namespace webrtc {
TEST(EventTracerTest, EventTracerDisabled) {
{
TRACE_EVENT0("test", "EventTracerDisabled");
}
EXPECT_FALSE(TestStatistics::Get()->Count());
TestStatistics::Get()->Reset();
}
TEST(EventTracerTest, ScopedTraceEvent) {
SetupEventTracer(&GetCategoryEnabledHandler, &AddTraceEventHandler);
{
TRACE_EVENT0("test", "ScopedTraceEvent");
}
EXPECT_EQ(2, TestStatistics::Get()->Count());
TestStatistics::Get()->Reset();
}
} // namespace webrtc

View File

@ -0,0 +1,42 @@
/*
* 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.
*/
#include "webrtc/base/event.h"
#include "webrtc/base/gunit.h"
namespace rtc {
TEST(EventTest, InitiallySignaled) {
Event event(false, true);
ASSERT_TRUE(event.Wait(0));
}
TEST(EventTest, ManualReset) {
Event event(true, false);
ASSERT_FALSE(event.Wait(0));
event.Set();
ASSERT_TRUE(event.Wait(0));
ASSERT_TRUE(event.Wait(0));
event.Reset();
ASSERT_FALSE(event.Wait(0));
}
TEST(EventTest, AutoReset) {
Event event(false, false);
ASSERT_FALSE(event.Wait(0));
event.Set();
ASSERT_TRUE(event.Wait(0));
ASSERT_FALSE(event.Wait(0));
}
} // namespace rtc

View File

@ -0,0 +1,50 @@
/*
* Copyright 2016 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.
*/
#include "webrtc/base/fakeclock.h"
#include "webrtc/base/checks.h"
#include "webrtc/base/messagequeue.h"
namespace rtc {
int64_t FakeClock::TimeNanos() const {
CritScope cs(&lock_);
return time_;
}
void FakeClock::SetTimeNanos(int64_t nanos) {
{
CritScope cs(&lock_);
RTC_DCHECK(nanos >= time_);
time_ = nanos;
}
// If message queues are waiting in a socket select() with a timeout provided
// by the OS, they should wake up and dispatch all messages that are ready.
MessageQueueManager::ProcessAllMessageQueues();
}
void FakeClock::AdvanceTime(TimeDelta delta) {
{
CritScope cs(&lock_);
time_ += delta.ToNanoseconds();
}
MessageQueueManager::ProcessAllMessageQueues();
}
ScopedFakeClock::ScopedFakeClock() {
prev_clock_ = SetClockForTesting(this);
}
ScopedFakeClock::~ScopedFakeClock() {
SetClockForTesting(prev_clock_);
}
} // namespace rtc

View File

@ -0,0 +1,71 @@
/*
* Copyright 2016 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_RTC_BASE_FAKECLOCK_H_
#define WEBRTC_RTC_BASE_FAKECLOCK_H_
#include "webrtc/base/criticalsection.h"
#include "webrtc/base/timedelta.h"
#include "webrtc/base/timeutils.h"
namespace rtc {
// Fake clock for use with unit tests, which does not tick on its own.
// Starts at time 0.
//
// TODO(deadbeef): Unify with webrtc::SimulatedClock.
class FakeClock : public ClockInterface {
public:
~FakeClock() override {}
// ClockInterface implementation.
int64_t TimeNanos() const override;
// Methods that can be used by the test to control the time.
// Should only be used to set a time in the future.
void SetTimeNanos(int64_t nanos);
void SetTimeMicros(int64_t micros) {
SetTimeNanos(kNumNanosecsPerMicrosec * micros);
}
void AdvanceTime(TimeDelta delta);
void AdvanceTimeMicros(int64_t micros) {
AdvanceTime(rtc::TimeDelta::FromMicroseconds(micros));
}
private:
CriticalSection lock_;
int64_t time_ GUARDED_BY(lock_) = 0;
};
// Helper class that sets itself as the global clock in its constructor and
// unsets it in its destructor.
class ScopedFakeClock : public FakeClock {
public:
ScopedFakeClock();
~ScopedFakeClock() override;
private:
ClockInterface* prev_clock_;
};
// Helper class to "undo" the fake clock temporarily.
class ScopedRealClock {
public:
ScopedRealClock();
~ScopedRealClock();
private:
ClockInterface* prev_clock_;
};
} // namespace rtc
#endif // WEBRTC_RTC_BASE_FAKECLOCK_H_

View File

@ -0,0 +1,129 @@
/*
* Copyright 2009 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_RTC_BASE_FAKENETWORK_H_
#define WEBRTC_RTC_BASE_FAKENETWORK_H_
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "webrtc/base/network.h"
#include "webrtc/base/messagehandler.h"
#include "webrtc/base/socketaddress.h"
#include "webrtc/base/stringencode.h"
#include "webrtc/base/thread.h"
namespace rtc {
const int kFakeIPv4NetworkPrefixLength = 24;
const int kFakeIPv6NetworkPrefixLength = 64;
// Fake network manager that allows us to manually specify the IPs to use.
class FakeNetworkManager : public NetworkManagerBase,
public MessageHandler {
public:
FakeNetworkManager() {}
typedef std::vector<std::pair<SocketAddress, AdapterType>> IfaceList;
void AddInterface(const SocketAddress& iface) {
// Ensure a unique name for the interface if its name is not given.
AddInterface(iface, "test" + rtc::ToString(next_index_++));
}
void AddInterface(const SocketAddress& iface, const std::string& if_name) {
AddInterface(iface, if_name, ADAPTER_TYPE_UNKNOWN);
}
void AddInterface(const SocketAddress& iface,
const std::string& if_name,
AdapterType type) {
SocketAddress address(if_name, 0);
address.SetResolvedIP(iface.ipaddr());
ifaces_.push_back(std::make_pair(address, type));
DoUpdateNetworks();
}
void RemoveInterface(const SocketAddress& iface) {
for (IfaceList::iterator it = ifaces_.begin();
it != ifaces_.end(); ++it) {
if (it->first.EqualIPs(iface)) {
ifaces_.erase(it);
break;
}
}
DoUpdateNetworks();
}
virtual void StartUpdating() {
++start_count_;
if (start_count_ == 1) {
sent_first_update_ = false;
rtc::Thread::Current()->Post(RTC_FROM_HERE, this);
} else {
if (sent_first_update_) {
SignalNetworksChanged();
}
}
}
virtual void StopUpdating() { --start_count_; }
// MessageHandler interface.
virtual void OnMessage(Message* msg) {
DoUpdateNetworks();
}
using NetworkManagerBase::set_enumeration_permission;
using NetworkManagerBase::set_default_local_addresses;
private:
void DoUpdateNetworks() {
if (start_count_ == 0)
return;
std::vector<Network*> networks;
for (IfaceList::iterator it = ifaces_.begin();
it != ifaces_.end(); ++it) {
int prefix_length = 0;
if (it->first.ipaddr().family() == AF_INET) {
prefix_length = kFakeIPv4NetworkPrefixLength;
} else if (it->first.ipaddr().family() == AF_INET6) {
prefix_length = kFakeIPv6NetworkPrefixLength;
}
IPAddress prefix = TruncateIP(it->first.ipaddr(), prefix_length);
std::unique_ptr<Network> net(new Network(it->first.hostname(),
it->first.hostname(), prefix,
prefix_length, it->second));
net->set_default_local_address_provider(this);
net->AddIP(it->first.ipaddr());
networks.push_back(net.release());
}
bool changed;
MergeNetworkList(networks, &changed);
if (changed || !sent_first_update_) {
SignalNetworksChanged();
sent_first_update_ = true;
}
}
IfaceList ifaces_;
int next_index_ = 0;
int start_count_ = 0;
bool sent_first_update_ = false;
IPAddress default_local_ipv4_address_;
IPAddress default_local_ipv6_address_;
};
} // namespace rtc
#endif // WEBRTC_RTC_BASE_FAKENETWORK_H_

View File

@ -0,0 +1,120 @@
/*
* Copyright 2012 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_RTC_BASE_FAKESSLIDENTITY_H_
#define WEBRTC_RTC_BASE_FAKESSLIDENTITY_H_
#include <algorithm>
#include <memory>
#include <vector>
#include "webrtc/base/checks.h"
#include "webrtc/base/messagedigest.h"
#include "webrtc/base/sslidentity.h"
namespace rtc {
class FakeSSLCertificate : public rtc::SSLCertificate {
public:
// SHA-1 is the default digest algorithm because it is available in all build
// configurations used for unit testing.
explicit FakeSSLCertificate(const std::string& data)
: data_(data), digest_algorithm_(DIGEST_SHA_1), expiration_time_(-1) {}
explicit FakeSSLCertificate(const std::vector<std::string>& certs)
: data_(certs.front()),
digest_algorithm_(DIGEST_SHA_1),
expiration_time_(-1) {
std::vector<std::string>::const_iterator it;
// Skip certs[0].
for (it = certs.begin() + 1; it != certs.end(); ++it) {
certs_.push_back(FakeSSLCertificate(*it));
}
}
FakeSSLCertificate* GetReference() const override {
return new FakeSSLCertificate(*this);
}
std::string ToPEMString() const override {
return data_;
}
void ToDER(Buffer* der_buffer) const override {
std::string der_string;
RTC_CHECK(SSLIdentity::PemToDer(kPemTypeCertificate, data_, &der_string));
der_buffer->SetData(der_string.c_str(), der_string.size());
}
int64_t CertificateExpirationTime() const override {
return expiration_time_;
}
void SetCertificateExpirationTime(int64_t expiration_time) {
expiration_time_ = expiration_time;
}
void set_digest_algorithm(const std::string& algorithm) {
digest_algorithm_ = algorithm;
}
bool GetSignatureDigestAlgorithm(std::string* algorithm) const override {
*algorithm = digest_algorithm_;
return true;
}
bool ComputeDigest(const std::string& algorithm,
unsigned char* digest,
size_t size,
size_t* length) const override {
*length = rtc::ComputeDigest(algorithm, data_.c_str(), data_.size(),
digest, size);
return (*length != 0);
}
std::unique_ptr<SSLCertChain> GetChain() const override {
if (certs_.empty())
return nullptr;
std::vector<SSLCertificate*> new_certs(certs_.size());
std::transform(certs_.begin(), certs_.end(), new_certs.begin(), DupCert);
std::unique_ptr<SSLCertChain> chain(new SSLCertChain(new_certs));
std::for_each(new_certs.begin(), new_certs.end(), DeleteCert);
return chain;
}
private:
static FakeSSLCertificate* DupCert(FakeSSLCertificate cert) {
return cert.GetReference();
}
static void DeleteCert(SSLCertificate* cert) { delete cert; }
std::string data_;
std::vector<FakeSSLCertificate> certs_;
std::string digest_algorithm_;
// Expiration time in seconds relative to epoch, 1970-01-01T00:00:00Z (UTC).
int64_t expiration_time_;
};
class FakeSSLIdentity : public rtc::SSLIdentity {
public:
explicit FakeSSLIdentity(const std::string& data) : cert_(data) {}
explicit FakeSSLIdentity(const FakeSSLCertificate& cert) : cert_(cert) {}
virtual FakeSSLIdentity* GetReference() const {
return new FakeSSLIdentity(*this);
}
virtual const FakeSSLCertificate& certificate() const { return cert_; }
virtual std::string PrivateKeyToPEMString() const {
RTC_NOTREACHED(); // Not implemented.
return "";
}
virtual std::string PublicKeyToPEMString() const {
RTC_NOTREACHED(); // Not implemented.
return "";
}
virtual bool operator==(const SSLIdentity& other) const {
RTC_NOTREACHED(); // Not implemented.
return false;
}
private:
FakeSSLCertificate cert_;
};
} // namespace rtc
#endif // WEBRTC_RTC_BASE_FAKESSLIDENTITY_H_

94
webrtc/rtc_base/file.cc Normal file
View File

@ -0,0 +1,94 @@
/*
* Copyright (c) 2016 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.
*/
#include "webrtc/base/file.h"
#include <utility>
namespace rtc {
namespace {
std::string NormalizePathname(Pathname&& path) {
path.Normalize();
return path.pathname();
}
} // namespace
File::File(PlatformFile file) : file_(file) {}
File::File() : file_(kInvalidPlatformFileValue) {}
File::~File() {
Close();
}
// static
File File::Open(const std::string& path) {
return File(OpenPlatformFile(path));
}
// static
File File::Open(Pathname&& path) {
return Open(NormalizePathname(std::move(path)));
}
// static
File File::Open(const Pathname& path) {
return Open(Pathname(path));
}
// static
File File::Create(const std::string& path) {
return File(CreatePlatformFile(path));
}
// static
File File::Create(Pathname&& path) {
return Create(NormalizePathname(std::move(path)));
}
// static
File File::Create(const Pathname& path) {
return Create(Pathname(path));
}
// static
bool File::Remove(const std::string& path) {
return RemoveFile(path);
}
// static
bool File::Remove(Pathname&& path) {
return Remove(NormalizePathname(std::move(path)));
}
// static
bool File::Remove(const Pathname& path) {
return Remove(Pathname(path));
}
File::File(File&& other) : file_(other.file_) {
other.file_ = kInvalidPlatformFileValue;
}
File& File::operator=(File&& other) {
Close();
file_ = other.file_;
other.file_ = kInvalidPlatformFileValue;
return *this;
}
bool File::IsOpen() {
return file_ != kInvalidPlatformFileValue;
}
} // namespace rtc

82
webrtc/rtc_base/file.h Normal file
View File

@ -0,0 +1,82 @@
/*
* Copyright (c) 2016 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_RTC_BASE_FILE_H_
#define WEBRTC_RTC_BASE_FILE_H_
#include <stdint.h>
#include <string>
#include "webrtc/base/constructormagic.h"
#include "webrtc/base/pathutils.h"
#include "webrtc/base/platform_file.h"
namespace rtc {
// This class wraps the platform specific APIs for simple file interactions.
//
// The various read and write methods are best effort, i.e. if an underlying
// call does not manage to read/write all the data more calls will be performed,
// until an error is detected or all data is read/written.
class File {
public:
// Wraps the given PlatformFile. This class is then responsible for closing
// the file, which will be done in the destructor if Close is never called.
explicit File(PlatformFile);
// The default constructor produces a closed file.
File();
~File();
File(File&& other);
File& operator=(File&& other);
// Open and Create give files with both reading and writing enabled.
static File Open(const std::string& path);
static File Open(Pathname&& path);
static File Open(const Pathname& path);
// If the file already exists it will be overwritten.
static File Create(const std::string& path);
static File Create(Pathname&& path);
static File Create(const Pathname& path);
// Remove a file in the file system.
static bool Remove(const std::string& path);
static bool Remove(Pathname&& path);
static bool Remove(const Pathname& path);
size_t Write(const uint8_t* data, size_t length);
size_t Read(uint8_t* buffer, size_t length);
// The current position in the file after a call to these methods is platform
// dependent (MSVC gives position offset+length, most other
// compilers/platforms do not alter the position), i.e. do not depend on it,
// do a Seek before any subsequent Read/Write.
size_t WriteAt(const uint8_t* data, size_t length, size_t offset);
size_t ReadAt(uint8_t* buffer, size_t length, size_t offset);
// Attempt to position the file at the given offset from the start.
// Returns true if successful, false otherwise.
bool Seek(size_t offset);
// Attempt to close the file. Returns true if successful, false otherwise,
// most notably when the file is already closed.
bool Close();
bool IsOpen();
private:
PlatformFile file_;
RTC_DISALLOW_COPY_AND_ASSIGN(File);
};
} // namespace rtc
#endif // WEBRTC_RTC_BASE_FILE_H_

View File

@ -0,0 +1,96 @@
/*
* Copyright (c) 2016 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.
*/
#include "webrtc/base/file.h"
#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <limits>
#include "webrtc/base/checks.h"
namespace rtc {
size_t File::Write(const uint8_t* data, size_t length) {
size_t total_written = 0;
do {
ssize_t written;
do {
written = ::write(file_, data + total_written, length - total_written);
} while (written == -1 && errno == EINTR);
if (written == -1)
break;
total_written += written;
} while (total_written < length);
return total_written;
}
size_t File::Read(uint8_t* buffer, size_t length) {
size_t total_read = 0;
do {
ssize_t read;
do {
read = ::read(file_, buffer + total_read, length - total_read);
} while (read == -1 && errno == EINTR);
if (read == -1)
break;
total_read += read;
} while (total_read < length);
return total_read;
}
size_t File::WriteAt(const uint8_t* data, size_t length, size_t offset) {
size_t total_written = 0;
do {
ssize_t written;
do {
written = ::pwrite(file_, data + total_written, length - total_written,
offset + total_written);
} while (written == -1 && errno == EINTR);
if (written == -1)
break;
total_written += written;
} while (total_written < length);
return total_written;
}
size_t File::ReadAt(uint8_t* buffer, size_t length, size_t offset) {
size_t total_read = 0;
do {
ssize_t read;
do {
read = ::pread(file_, buffer + total_read, length - total_read,
offset + total_read);
} while (read == -1 && errno == EINTR);
if (read == -1)
break;
total_read += read;
} while (total_read < length);
return total_read;
}
bool File::Seek(size_t offset) {
RTC_DCHECK_LE(offset, std::numeric_limits<off_t>::max());
return lseek(file_, static_cast<off_t>(offset), SEEK_SET) != -1;
}
bool File::Close() {
if (file_ == rtc::kInvalidPlatformFileValue)
return false;
bool ret = close(file_) == 0;
file_ = rtc::kInvalidPlatformFileValue;
return ret;
}
} // namespace rtc

View File

@ -0,0 +1,201 @@
/*
* Copyright 2016 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.
*/
#include <limits>
#include <memory>
#include <string>
#include "webrtc/base/gunit.h"
#include "webrtc/base/file.h"
#include "webrtc/test/testsupport/fileutils.h"
#if defined(WEBRTC_WIN)
#include "webrtc/base/win32.h"
#else // if defined(WEBRTC_WIN)
#include <errno.h>
#endif
namespace rtc {
int LastError() {
#if defined(WEBRTC_WIN)
return ::GetLastError();
#else
return errno;
#endif
}
bool VerifyBuffer(uint8_t* buffer, size_t length, uint8_t start_value) {
for (size_t i = 0; i < length; ++i) {
uint8_t val = start_value++;
EXPECT_EQ(val, buffer[i]);
if (buffer[i] != val)
return false;
}
// Prevent the same buffer from being verified multiple times simply
// because some operation that should have written to it failed
memset(buffer, 0, length);
return true;
}
class FileTest : public ::testing::Test {
protected:
std::string path_;
void SetUp() override {
path_ = webrtc::test::TempFilename(webrtc::test::OutputPath(), "test_file");
ASSERT_FALSE(path_.empty());
}
void TearDown() override { RemoveFile(path_); }
};
TEST_F(FileTest, DefaultConstructor) {
File file;
uint8_t buffer[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
EXPECT_FALSE(file.IsOpen());
EXPECT_EQ(0u, file.Write(buffer, 10));
EXPECT_FALSE(file.Seek(0));
EXPECT_EQ(0u, file.Read(buffer, 10));
EXPECT_EQ(0u, file.WriteAt(buffer, 10, 0));
EXPECT_EQ(0u, file.ReadAt(buffer, 10, 0));
EXPECT_FALSE(file.Close());
}
TEST_F(FileTest, DoubleClose) {
File file = File::Open(path_);
ASSERT_TRUE(file.IsOpen()) << "Error: " << LastError();
EXPECT_TRUE(file.Close());
EXPECT_FALSE(file.Close());
}
TEST_F(FileTest, SimpleReadWrite) {
File file = File::Open(path_);
ASSERT_TRUE(file.IsOpen()) << "Error: " << LastError();
uint8_t data[100] = {0};
uint8_t out[100] = {0};
for (int i = 0; i < 100; ++i) {
data[i] = i;
}
EXPECT_EQ(10u, file.Write(data, 10));
EXPECT_TRUE(file.Seek(0));
EXPECT_EQ(10u, file.Read(out, 10));
EXPECT_TRUE(VerifyBuffer(out, 10, 0));
EXPECT_TRUE(file.Seek(0));
EXPECT_EQ(100u, file.Write(data, 100));
EXPECT_TRUE(file.Seek(0));
EXPECT_EQ(100u, file.Read(out, 100));
EXPECT_TRUE(VerifyBuffer(out, 100, 0));
EXPECT_TRUE(file.Seek(1));
EXPECT_EQ(50u, file.Write(data, 50));
EXPECT_EQ(50u, file.Write(data + 50, 50));
EXPECT_TRUE(file.Seek(1));
EXPECT_EQ(100u, file.Read(out, 100));
EXPECT_TRUE(VerifyBuffer(out, 100, 0));
}
TEST_F(FileTest, ReadWriteClose) {
File file = File::Open(path_);
ASSERT_TRUE(file.IsOpen()) << "Error: " << LastError();
uint8_t data[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
uint8_t out[10] = {0};
EXPECT_EQ(10u, file.Write(data, 10));
EXPECT_TRUE(file.Close());
File file2 = File::Open(path_);
ASSERT_TRUE(file2.IsOpen()) << "Error: " << LastError();
EXPECT_EQ(10u, file2.Read(out, 10));
EXPECT_TRUE(VerifyBuffer(out, 10, 0));
}
TEST_F(FileTest, RandomAccessRead) {
File file = File::Open(path_);
ASSERT_TRUE(file.IsOpen()) << "Error: " << LastError();
uint8_t data[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
uint8_t out[10] = {0};
EXPECT_EQ(10u, file.Write(data, 10));
EXPECT_EQ(4u, file.ReadAt(out, 4, 0));
EXPECT_TRUE(VerifyBuffer(out, 4, 0));
EXPECT_EQ(4u, file.ReadAt(out, 4, 4));
EXPECT_TRUE(VerifyBuffer(out, 4, 4));
EXPECT_EQ(5u, file.ReadAt(out, 5, 5));
EXPECT_TRUE(VerifyBuffer(out, 5, 5));
}
TEST_F(FileTest, RandomAccessReadWrite) {
File file = File::Open(path_);
ASSERT_TRUE(file.IsOpen()) << "Error: " << LastError();
uint8_t data[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
uint8_t out[10] = {0};
EXPECT_EQ(10u, file.Write(data, 10));
EXPECT_TRUE(file.Seek(4));
EXPECT_EQ(4u, file.WriteAt(data, 4, 4));
EXPECT_EQ(4u, file.ReadAt(out, 4, 4));
EXPECT_TRUE(VerifyBuffer(out, 4, 0));
EXPECT_EQ(2u, file.WriteAt(data, 2, 8));
EXPECT_EQ(2u, file.ReadAt(out, 2, 8));
EXPECT_TRUE(VerifyBuffer(out, 2, 0));
}
TEST_F(FileTest, OpenFromPathname) {
{
File file = File::Open(Pathname(path_));
ASSERT_TRUE(file.IsOpen()) << "Error: " << LastError();
}
{
Pathname path(path_);
File file = File::Open(path);
ASSERT_TRUE(file.IsOpen()) << "Error: " << LastError();
}
}
TEST_F(FileTest, CreateFromPathname) {
{
File file = File::Create(Pathname(path_));
ASSERT_TRUE(file.IsOpen()) << "Error: " << LastError();
}
{
Pathname path(path_);
File file = File::Create(path);
ASSERT_TRUE(file.IsOpen()) << "Error: " << LastError();
}
}
TEST_F(FileTest, ShouldBeAbleToRemoveFile) {
{
File file = File::Open(Pathname(path_));
ASSERT_TRUE(file.IsOpen()) << "Error: " << LastError();
}
ASSERT_TRUE(File::Remove(Pathname(path_))) << "Error: " << LastError();
}
} // namespace rtc

113
webrtc/rtc_base/file_win.cc Normal file
View File

@ -0,0 +1,113 @@
/*
* Copyright (c) 2016 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.
*/
#include "webrtc/base/file.h"
#include <io.h>
#include "webrtc/base/win32.h"
#include <limits> // NOLINT: win32.h should be considered a system header
#include "webrtc/base/checks.h"
namespace rtc {
size_t File::Write(const uint8_t* data, size_t length) {
RTC_DCHECK_LT(length, std::numeric_limits<DWORD>::max());
size_t total_written = 0;
do {
DWORD written;
if (!::WriteFile(file_, data + total_written,
static_cast<DWORD>(length - total_written), &written,
nullptr)) {
break;
}
total_written += written;
} while (total_written < length);
return total_written;
}
size_t File::Read(uint8_t* buffer, size_t length) {
RTC_DCHECK_LT(length, std::numeric_limits<DWORD>::max());
size_t total_read = 0;
do {
DWORD read;
if (!::ReadFile(file_, buffer + total_read,
static_cast<DWORD>(length - total_read), &read, nullptr)) {
break;
}
total_read += read;
} while (total_read < length);
return total_read;
}
size_t File::WriteAt(const uint8_t* data, size_t length, size_t offset) {
RTC_DCHECK_LT(length, std::numeric_limits<DWORD>::max());
size_t total_written = 0;
do {
DWORD written;
LARGE_INTEGER offset_li;
offset_li.QuadPart = offset + total_written;
OVERLAPPED overlapped = {0};
overlapped.Offset = offset_li.LowPart;
overlapped.OffsetHigh = offset_li.HighPart;
if (!::WriteFile(file_, data + total_written,
static_cast<DWORD>(length - total_written), &written,
&overlapped)) {
break;
}
total_written += written;
} while (total_written < length);
return total_written;
}
size_t File::ReadAt(uint8_t* buffer, size_t length, size_t offset) {
RTC_DCHECK_LT(length, std::numeric_limits<DWORD>::max());
size_t total_read = 0;
do {
DWORD read;
LARGE_INTEGER offset_li;
offset_li.QuadPart = offset + total_read;
OVERLAPPED overlapped = {0};
overlapped.Offset = offset_li.LowPart;
overlapped.OffsetHigh = offset_li.HighPart;
if (!::ReadFile(file_, buffer + total_read,
static_cast<DWORD>(length - total_read), &read,
&overlapped)) {
break;
}
total_read += read;
} while (total_read < length);
return total_read;
}
bool File::Seek(size_t offset) {
LARGE_INTEGER distance;
distance.QuadPart = offset;
return SetFilePointerEx(file_, distance, nullptr, FILE_BEGIN) != 0;
}
bool File::Close() {
if (file_ == kInvalidPlatformFileValue)
return false;
bool ret = CloseHandle(file_) != 0;
file_ = kInvalidPlatformFileValue;
return ret;
}
} // namespace rtc

View File

@ -0,0 +1,400 @@
/*
* 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.
*/
#include "webrtc/base/filerotatingstream.h"
#include <algorithm>
#include <iostream>
#include <string>
#include "webrtc/base/checks.h"
#include "webrtc/base/fileutils.h"
#include "webrtc/base/pathutils.h"
// Note: We use std::cerr for logging in the write paths of this stream to avoid
// infinite loops when logging.
namespace rtc {
FileRotatingStream::FileRotatingStream(const std::string& dir_path,
const std::string& file_prefix)
: FileRotatingStream(dir_path, file_prefix, 0, 0, kRead) {
}
FileRotatingStream::FileRotatingStream(const std::string& dir_path,
const std::string& file_prefix,
size_t max_file_size,
size_t num_files)
: FileRotatingStream(dir_path,
file_prefix,
max_file_size,
num_files,
kWrite) {
RTC_DCHECK_GT(max_file_size, 0);
RTC_DCHECK_GT(num_files, 1);
}
FileRotatingStream::FileRotatingStream(const std::string& dir_path,
const std::string& file_prefix,
size_t max_file_size,
size_t num_files,
Mode mode)
: dir_path_(dir_path),
file_prefix_(file_prefix),
mode_(mode),
file_stream_(nullptr),
max_file_size_(max_file_size),
current_file_index_(0),
rotation_index_(0),
current_bytes_written_(0),
disable_buffering_(false) {
RTC_DCHECK(Filesystem::IsFolder(dir_path));
switch (mode) {
case kWrite: {
file_names_.clear();
for (size_t i = 0; i < num_files; ++i) {
file_names_.push_back(GetFilePath(i, num_files));
}
rotation_index_ = num_files - 1;
break;
}
case kRead: {
file_names_ = GetFilesWithPrefix();
std::sort(file_names_.begin(), file_names_.end());
if (file_names_.size() > 0) {
// |file_names_| is sorted newest first, so read from the end.
current_file_index_ = file_names_.size() - 1;
}
break;
}
}
}
FileRotatingStream::~FileRotatingStream() {
}
StreamState FileRotatingStream::GetState() const {
if (mode_ == kRead && current_file_index_ < file_names_.size()) {
return SS_OPEN;
}
if (!file_stream_) {
return SS_CLOSED;
}
return file_stream_->GetState();
}
StreamResult FileRotatingStream::Read(void* buffer,
size_t buffer_len,
size_t* read,
int* error) {
RTC_DCHECK(buffer);
if (mode_ != kRead) {
return SR_EOS;
}
if (current_file_index_ >= file_names_.size()) {
return SR_EOS;
}
// We will have no file stream initially, and when we are finished with the
// previous file.
if (!file_stream_) {
if (!OpenCurrentFile()) {
return SR_ERROR;
}
}
int local_error = 0;
if (!error) {
error = &local_error;
}
StreamResult result = file_stream_->Read(buffer, buffer_len, read, error);
if (result == SR_EOS || result == SR_ERROR) {
if (result == SR_ERROR) {
LOG(LS_ERROR) << "Failed to read from: "
<< file_names_[current_file_index_] << "Error: " << error;
}
// Reached the end of the file, read next file. If there is an error return
// the error status but allow for a next read by reading next file.
CloseCurrentFile();
if (current_file_index_ == 0) {
// Just finished reading the last file, signal EOS by setting index.
current_file_index_ = file_names_.size();
} else {
--current_file_index_;
}
if (read) {
*read = 0;
}
return result == SR_EOS ? SR_SUCCESS : result;
} else if (result == SR_SUCCESS) {
// Succeeded, continue reading from this file.
return SR_SUCCESS;
} else {
RTC_NOTREACHED();
}
return result;
}
StreamResult FileRotatingStream::Write(const void* data,
size_t data_len,
size_t* written,
int* error) {
if (mode_ != kWrite) {
return SR_EOS;
}
if (!file_stream_) {
std::cerr << "Open() must be called before Write." << std::endl;
return SR_ERROR;
}
// Write as much as will fit in to the current file.
RTC_DCHECK_LT(current_bytes_written_, max_file_size_);
size_t remaining_bytes = max_file_size_ - current_bytes_written_;
size_t write_length = std::min(data_len, remaining_bytes);
size_t local_written = 0;
if (!written) {
written = &local_written;
}
StreamResult result = file_stream_->Write(data, write_length, written, error);
current_bytes_written_ += *written;
// If we're done with this file, rotate it out.
if (current_bytes_written_ >= max_file_size_) {
RTC_DCHECK_EQ(current_bytes_written_, max_file_size_);
RotateFiles();
}
return result;
}
bool FileRotatingStream::Flush() {
if (!file_stream_) {
return false;
}
return file_stream_->Flush();
}
bool FileRotatingStream::GetSize(size_t* size) const {
if (mode_ != kRead) {
// Not possible to get accurate size on disk when writing because of
// potential buffering.
return false;
}
RTC_DCHECK(size);
*size = 0;
size_t total_size = 0;
for (auto file_name : file_names_) {
Pathname pathname(file_name);
size_t file_size = 0;
if (Filesystem::GetFileSize(file_name, &file_size)) {
total_size += file_size;
}
}
*size = total_size;
return true;
}
void FileRotatingStream::Close() {
CloseCurrentFile();
}
bool FileRotatingStream::Open() {
switch (mode_) {
case kRead:
// Defer opening to when we first read since we want to return read error
// if we fail to open next file.
return true;
case kWrite: {
// Delete existing files when opening for write.
std::vector<std::string> matching_files = GetFilesWithPrefix();
for (auto matching_file : matching_files) {
if (!Filesystem::DeleteFile(matching_file)) {
std::cerr << "Failed to delete: " << matching_file << std::endl;
}
}
return OpenCurrentFile();
}
}
return false;
}
bool FileRotatingStream::DisableBuffering() {
disable_buffering_ = true;
if (!file_stream_) {
std::cerr << "Open() must be called before DisableBuffering()."
<< std::endl;
return false;
}
return file_stream_->DisableBuffering();
}
std::string FileRotatingStream::GetFilePath(size_t index) const {
RTC_DCHECK_LT(index, file_names_.size());
return file_names_[index];
}
bool FileRotatingStream::OpenCurrentFile() {
CloseCurrentFile();
// Opens the appropriate file in the appropriate mode.
RTC_DCHECK_LT(current_file_index_, file_names_.size());
std::string file_path = file_names_[current_file_index_];
file_stream_.reset(new FileStream());
const char* mode = nullptr;
switch (mode_) {
case kWrite:
mode = "w+";
// We should always we writing to the zero-th file.
RTC_DCHECK_EQ(current_file_index_, 0);
break;
case kRead:
mode = "r";
break;
}
int error = 0;
if (!file_stream_->Open(file_path, mode, &error)) {
std::cerr << "Failed to open: " << file_path << "Error: " << error
<< std::endl;
file_stream_.reset();
return false;
}
if (disable_buffering_) {
file_stream_->DisableBuffering();
}
return true;
}
void FileRotatingStream::CloseCurrentFile() {
if (!file_stream_) {
return;
}
current_bytes_written_ = 0;
file_stream_.reset();
}
void FileRotatingStream::RotateFiles() {
RTC_DCHECK_EQ(mode_, kWrite);
CloseCurrentFile();
// Rotates the files by deleting the file at |rotation_index_|, which is the
// oldest file and then renaming the newer files to have an incremented index.
// See header file comments for example.
RTC_DCHECK_LT(rotation_index_, file_names_.size());
std::string file_to_delete = file_names_[rotation_index_];
if (Filesystem::IsFile(file_to_delete)) {
if (!Filesystem::DeleteFile(file_to_delete)) {
std::cerr << "Failed to delete: " << file_to_delete << std::endl;
}
}
for (auto i = rotation_index_; i > 0; --i) {
std::string rotated_name = file_names_[i];
std::string unrotated_name = file_names_[i - 1];
if (Filesystem::IsFile(unrotated_name)) {
if (!Filesystem::MoveFile(unrotated_name, rotated_name)) {
std::cerr << "Failed to move: " << unrotated_name << " to "
<< rotated_name << std::endl;
}
}
}
// Create a new file for 0th index.
OpenCurrentFile();
OnRotation();
}
std::vector<std::string> FileRotatingStream::GetFilesWithPrefix() const {
std::vector<std::string> files;
// Iterate over the files in the directory.
DirectoryIterator it;
Pathname dir_path;
dir_path.SetFolder(dir_path_);
if (!it.Iterate(dir_path)) {
return files;
}
do {
std::string current_name = it.Name();
if (current_name.size() && !it.IsDirectory() &&
current_name.compare(0, file_prefix_.size(), file_prefix_) == 0) {
Pathname path(dir_path_, current_name);
files.push_back(path.pathname());
}
} while (it.Next());
return files;
}
std::string FileRotatingStream::GetFilePath(size_t index,
size_t num_files) const {
RTC_DCHECK_LT(index, num_files);
std::ostringstream file_name;
// The format will be "_%<num_digits>zu". We want to zero pad the index so
// that it will sort nicely.
size_t max_digits = ((num_files - 1) / 10) + 1;
size_t num_digits = (index / 10) + 1;
RTC_DCHECK_LE(num_digits, max_digits);
size_t padding = max_digits - num_digits;
file_name << file_prefix_ << "_";
for (size_t i = 0; i < padding; ++i) {
file_name << "0";
}
file_name << index;
Pathname file_path(dir_path_, file_name.str());
return file_path.pathname();
}
CallSessionFileRotatingStream::CallSessionFileRotatingStream(
const std::string& dir_path)
: FileRotatingStream(dir_path, kLogPrefix),
max_total_log_size_(0),
num_rotations_(0) {
}
CallSessionFileRotatingStream::CallSessionFileRotatingStream(
const std::string& dir_path,
size_t max_total_log_size)
: FileRotatingStream(dir_path,
kLogPrefix,
max_total_log_size / 2,
GetNumRotatingLogFiles(max_total_log_size) + 1),
max_total_log_size_(max_total_log_size),
num_rotations_(0) {
RTC_DCHECK_GE(max_total_log_size, 4);
}
const char* CallSessionFileRotatingStream::kLogPrefix = "webrtc_log";
const size_t CallSessionFileRotatingStream::kRotatingLogFileDefaultSize =
1024 * 1024;
void CallSessionFileRotatingStream::OnRotation() {
++num_rotations_;
if (num_rotations_ == 1) {
// On the first rotation adjust the max file size so subsequent files after
// the first are smaller.
SetMaxFileSize(GetRotatingLogSize(max_total_log_size_));
} else if (num_rotations_ == (GetNumFiles() - 1)) {
// On the next rotation the very first file is going to be deleted. Change
// the rotation index so this doesn't happen.
SetRotationIndex(GetRotationIndex() - 1);
}
}
size_t CallSessionFileRotatingStream::GetRotatingLogSize(
size_t max_total_log_size) {
size_t num_rotating_log_files = GetNumRotatingLogFiles(max_total_log_size);
size_t rotating_log_size = num_rotating_log_files > 2
? kRotatingLogFileDefaultSize
: max_total_log_size / 4;
return rotating_log_size;
}
size_t CallSessionFileRotatingStream::GetNumRotatingLogFiles(
size_t max_total_log_size) {
// At minimum have two rotating files. Otherwise split the available log size
// evenly across 1MB files.
return std::max((size_t)2,
(max_total_log_size / 2) / kRotatingLogFileDefaultSize);
}
} // namespace rtc

View File

@ -0,0 +1,173 @@
/*
* 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_RTC_BASE_FILEROTATINGSTREAM_H_
#define WEBRTC_RTC_BASE_FILEROTATINGSTREAM_H_
#include <memory>
#include <string>
#include <vector>
#include "webrtc/base/constructormagic.h"
#include "webrtc/base/stream.h"
namespace rtc {
// FileRotatingStream writes to a file in the directory specified in the
// constructor. It rotates the files once the current file is full. The
// individual file size and the number of files used is configurable in the
// constructor. Open() must be called before using this stream.
class FileRotatingStream : public StreamInterface {
public:
// Use this constructor for reading a directory previously written to with
// this stream.
FileRotatingStream(const std::string& dir_path,
const std::string& file_prefix);
// Use this constructor for writing to a directory. Files in the directory
// matching the prefix will be deleted on open.
FileRotatingStream(const std::string& dir_path,
const std::string& file_prefix,
size_t max_file_size,
size_t num_files);
~FileRotatingStream() override;
// StreamInterface methods.
StreamState GetState() const override;
StreamResult Read(void* buffer,
size_t buffer_len,
size_t* read,
int* error) override;
StreamResult Write(const void* data,
size_t data_len,
size_t* written,
int* error) override;
bool Flush() override;
// Returns the total file size currently used on disk.
bool GetSize(size_t* size) const override;
void Close() override;
// Opens the appropriate file(s). Call this before using the stream.
bool Open();
// Disabling buffering causes writes to block until disk is updated. This is
// enabled by default for performance.
bool DisableBuffering();
// Returns the path used for the i-th newest file, where the 0th file is the
// newest file. The file may or may not exist, this is just used for
// formatting. Index must be less than GetNumFiles().
std::string GetFilePath(size_t index) const;
// Returns the number of files that will used by this stream.
size_t GetNumFiles() const { return file_names_.size(); }
protected:
size_t GetMaxFileSize() const { return max_file_size_; }
void SetMaxFileSize(size_t size) { max_file_size_ = size; }
size_t GetRotationIndex() const { return rotation_index_; }
void SetRotationIndex(size_t index) { rotation_index_ = index; }
virtual void OnRotation() {}
private:
enum Mode { kRead, kWrite };
FileRotatingStream(const std::string& dir_path,
const std::string& file_prefix,
size_t max_file_size,
size_t num_files,
Mode mode);
bool OpenCurrentFile();
void CloseCurrentFile();
// Rotates the files by creating a new current file, renaming the
// existing files, and deleting the oldest one. e.g.
// file_0 -> file_1
// file_1 -> file_2
// file_2 -> delete
// create new file_0
void RotateFiles();
// Returns a list of file names in the directory beginning with the prefix.
std::vector<std::string> GetFilesWithPrefix() const;
// Private version of GetFilePath.
std::string GetFilePath(size_t index, size_t num_files) const;
const std::string dir_path_;
const std::string file_prefix_;
const Mode mode_;
// FileStream is used to write to the current file.
std::unique_ptr<FileStream> file_stream_;
// Convenience storage for file names so we don't generate them over and over.
std::vector<std::string> file_names_;
size_t max_file_size_;
size_t current_file_index_;
// The rotation index indicates the index of the file that will be
// deleted first on rotation. Indices lower than this index will be rotated.
size_t rotation_index_;
// Number of bytes written to current file. We need this because with
// buffering the file size read from disk might not be accurate.
size_t current_bytes_written_;
bool disable_buffering_;
RTC_DISALLOW_COPY_AND_ASSIGN(FileRotatingStream);
};
// CallSessionFileRotatingStream is meant to be used in situations where we will
// have limited disk space. Its purpose is to read and write logs up to a
// maximum size. Once the maximum size is exceeded, logs from the middle are
// deleted whereas logs from the beginning and end are preserved. The reason for
// this is because we anticipate that in WebRTC the beginning and end of the
// logs are most useful for call diagnostics.
//
// This implementation simply writes to a single file until
// |max_total_log_size| / 2 bytes are written to it, and subsequently writes to
// a set of rotating files. We do this by inheriting FileRotatingStream and
// setting the appropriate internal variables so that we don't delete the last
// (earliest) file on rotate, and that that file's size is bigger.
//
// Open() must be called before using this stream.
class CallSessionFileRotatingStream : public FileRotatingStream {
public:
// Use this constructor for reading a directory previously written to with
// this stream.
explicit CallSessionFileRotatingStream(const std::string& dir_path);
// Use this constructor for writing to a directory. Files in the directory
// matching what's used by the stream will be deleted. |max_total_log_size|
// must be at least 4.
CallSessionFileRotatingStream(const std::string& dir_path,
size_t max_total_log_size);
~CallSessionFileRotatingStream() override {}
protected:
void OnRotation() override;
private:
static size_t GetRotatingLogSize(size_t max_total_log_size);
static size_t GetNumRotatingLogFiles(size_t max_total_log_size);
static const char* kLogPrefix;
static const size_t kRotatingLogFileDefaultSize;
const size_t max_total_log_size_;
size_t num_rotations_;
RTC_DISALLOW_COPY_AND_ASSIGN(CallSessionFileRotatingStream);
};
} // namespace rtc
#endif // WEBRTC_RTC_BASE_FILEROTATINGSTREAM_H_

View File

@ -0,0 +1,345 @@
/*
* 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.
*/
#include <memory>
#include "webrtc/base/arraysize.h"
#include "webrtc/base/checks.h"
#include "webrtc/base/filerotatingstream.h"
#include "webrtc/base/fileutils.h"
#include "webrtc/base/gunit.h"
#include "webrtc/base/pathutils.h"
#include "webrtc/test/testsupport/fileutils.h"
namespace rtc {
namespace {
void CleanupLogDirectory(const FileRotatingStream& stream) {
for (size_t i = 0; i < stream.GetNumFiles(); ++i) {
// Ignore return value, not all files are expected to exist.
webrtc::test::RemoveFile(stream.GetFilePath(i));
}
}
} // namespace
#if defined (WEBRTC_ANDROID)
// Fails on Android: https://bugs.chromium.org/p/webrtc/issues/detail?id=4364.
#define MAYBE_FileRotatingStreamTest DISABLED_FileRotatingStreamTest
#else
#define MAYBE_FileRotatingStreamTest FileRotatingStreamTest
#endif
class MAYBE_FileRotatingStreamTest : public ::testing::Test {
protected:
static const char* kFilePrefix;
static const size_t kMaxFileSize;
void Init(const std::string& dir_name,
const std::string& file_prefix,
size_t max_file_size,
size_t num_log_files) {
dir_path_ = webrtc::test::OutputPath();
// Append per-test output path in order to run within gtest parallel.
dir_path_.append(dir_name);
dir_path_.push_back(Pathname::DefaultFolderDelimiter());
ASSERT_TRUE(webrtc::test::CreateDir(dir_path_));
stream_.reset(new FileRotatingStream(dir_path_, file_prefix, max_file_size,
num_log_files));
}
void TearDown() override {
// On windows, open files can't be removed.
stream_->Close();
CleanupLogDirectory(*stream_);
EXPECT_TRUE(webrtc::test::RemoveDir(dir_path_));
stream_.reset();
}
// Writes the data to the stream and flushes it.
void WriteAndFlush(const void* data, const size_t data_len) {
EXPECT_EQ(SR_SUCCESS, stream_->WriteAll(data, data_len, nullptr, nullptr));
EXPECT_TRUE(stream_->Flush());
}
// Checks that the stream reads in the expected contents and then returns an
// end of stream result.
void VerifyStreamRead(const char* expected_contents,
const size_t expected_length,
const std::string& dir_path,
const char* file_prefix) {
std::unique_ptr<FileRotatingStream> stream;
stream.reset(new FileRotatingStream(dir_path, file_prefix));
ASSERT_TRUE(stream->Open());
size_t read = 0;
size_t stream_size = 0;
EXPECT_TRUE(stream->GetSize(&stream_size));
std::unique_ptr<uint8_t[]> buffer(new uint8_t[expected_length]);
EXPECT_EQ(SR_SUCCESS,
stream->ReadAll(buffer.get(), expected_length, &read, nullptr));
EXPECT_EQ(0, memcmp(expected_contents, buffer.get(), expected_length));
EXPECT_EQ(SR_EOS, stream->ReadAll(buffer.get(), 1, nullptr, nullptr));
EXPECT_EQ(stream_size, read);
}
void VerifyFileContents(const char* expected_contents,
const size_t expected_length,
const std::string& file_path) {
std::unique_ptr<uint8_t[]> buffer(new uint8_t[expected_length]);
FileStream stream;
ASSERT_TRUE(stream.Open(file_path, "r", nullptr));
EXPECT_EQ(rtc::SR_SUCCESS,
stream.ReadAll(buffer.get(), expected_length, nullptr, nullptr));
EXPECT_EQ(0, memcmp(expected_contents, buffer.get(), expected_length));
size_t file_size = 0;
EXPECT_TRUE(stream.GetSize(&file_size));
EXPECT_EQ(file_size, expected_length);
}
std::unique_ptr<FileRotatingStream> stream_;
std::string dir_path_;
};
const char* MAYBE_FileRotatingStreamTest::kFilePrefix =
"FileRotatingStreamTest";
const size_t MAYBE_FileRotatingStreamTest::kMaxFileSize = 2;
// Tests that stream state is correct before and after Open / Close.
TEST_F(MAYBE_FileRotatingStreamTest, State) {
Init("FileRotatingStreamTestState", kFilePrefix, kMaxFileSize, 3);
EXPECT_EQ(SS_CLOSED, stream_->GetState());
ASSERT_TRUE(stream_->Open());
EXPECT_EQ(SS_OPEN, stream_->GetState());
stream_->Close();
EXPECT_EQ(SS_CLOSED, stream_->GetState());
}
// Tests that nothing is written to file when data of length zero is written.
TEST_F(MAYBE_FileRotatingStreamTest, EmptyWrite) {
Init("FileRotatingStreamTestEmptyWrite", kFilePrefix, kMaxFileSize, 3);
ASSERT_TRUE(stream_->Open());
WriteAndFlush("a", 0);
std::string logfile_path = stream_->GetFilePath(0);
FileStream stream;
ASSERT_TRUE(stream.Open(logfile_path, "r", nullptr));
size_t file_size = 0;
EXPECT_TRUE(stream.GetSize(&file_size));
EXPECT_EQ(0u, file_size);
}
// Tests that a write operation followed by a read returns the expected data
// and writes to the expected files.
TEST_F(MAYBE_FileRotatingStreamTest, WriteAndRead) {
Init("FileRotatingStreamTestWriteAndRead", kFilePrefix, kMaxFileSize, 3);
ASSERT_TRUE(stream_->Open());
// The test is set up to create three log files of length 2. Write and check
// contents.
std::string messages[3] = {"aa", "bb", "cc"};
for (size_t i = 0; i < arraysize(messages); ++i) {
const std::string& message = messages[i];
WriteAndFlush(message.c_str(), message.size());
// Since the max log size is 2, we will be causing rotation. Read from the
// next file.
VerifyFileContents(message.c_str(), message.size(),
stream_->GetFilePath(1));
}
// Check that exactly three files exist.
for (size_t i = 0; i < arraysize(messages); ++i) {
EXPECT_TRUE(Filesystem::IsFile(stream_->GetFilePath(i)));
}
std::string message("d");
WriteAndFlush(message.c_str(), message.size());
for (size_t i = 0; i < arraysize(messages); ++i) {
EXPECT_TRUE(Filesystem::IsFile(stream_->GetFilePath(i)));
}
// TODO(tkchin): Maybe check all the files in the dir.
// Reopen for read.
std::string expected_contents("bbccd");
VerifyStreamRead(expected_contents.c_str(), expected_contents.size(),
dir_path_, kFilePrefix);
}
// Tests that writing data greater than the total capacity of the files
// overwrites the files correctly and is read correctly after.
TEST_F(MAYBE_FileRotatingStreamTest, WriteOverflowAndRead) {
Init("FileRotatingStreamTestWriteOverflowAndRead", kFilePrefix, kMaxFileSize,
3);
ASSERT_TRUE(stream_->Open());
// This should cause overflow across all three files, such that the first file
// we wrote to also gets overwritten.
std::string message("foobarbaz");
WriteAndFlush(message.c_str(), message.size());
std::string expected_file_contents("z");
VerifyFileContents(expected_file_contents.c_str(),
expected_file_contents.size(), stream_->GetFilePath(0));
std::string expected_stream_contents("arbaz");
VerifyStreamRead(expected_stream_contents.c_str(),
expected_stream_contents.size(), dir_path_, kFilePrefix);
}
// Tests that the returned file paths have the right folder and prefix.
TEST_F(MAYBE_FileRotatingStreamTest, GetFilePath) {
Init("FileRotatingStreamTestGetFilePath", kFilePrefix, kMaxFileSize, 20);
for (auto i = 0; i < 20; ++i) {
Pathname path(stream_->GetFilePath(i));
EXPECT_EQ(0, path.folder().compare(dir_path_));
EXPECT_EQ(0, path.filename().compare(0, strlen(kFilePrefix), kFilePrefix));
}
}
#if defined (WEBRTC_ANDROID)
// Fails on Android: https://bugs.chromium.org/p/webrtc/issues/detail?id=4364.
#define MAYBE_CallSessionFileRotatingStreamTest \
DISABLED_CallSessionFileRotatingStreamTest
#else
#define MAYBE_CallSessionFileRotatingStreamTest \
CallSessionFileRotatingStreamTest
#endif
class MAYBE_CallSessionFileRotatingStreamTest : public ::testing::Test {
protected:
void Init(const std::string& dir_name, size_t max_total_log_size) {
dir_path_ = webrtc::test::OutputPath();
// Append per-test output path in order to run within gtest parallel.
dir_path_.append(dir_name);
dir_path_.push_back(Pathname::DefaultFolderDelimiter());
ASSERT_TRUE(webrtc::test::CreateDir(dir_path_));
stream_.reset(
new CallSessionFileRotatingStream(dir_path_, max_total_log_size));
}
virtual void TearDown() {
// On windows, open files can't be removed.
stream_->Close();
CleanupLogDirectory(*stream_);
EXPECT_TRUE(webrtc::test::RemoveDir(dir_path_));
stream_.reset();
}
// Writes the data to the stream and flushes it.
void WriteAndFlush(const void* data, const size_t data_len) {
EXPECT_EQ(SR_SUCCESS, stream_->WriteAll(data, data_len, nullptr, nullptr));
EXPECT_TRUE(stream_->Flush());
}
// Checks that the stream reads in the expected contents and then returns an
// end of stream result.
void VerifyStreamRead(const char* expected_contents,
const size_t expected_length,
const std::string& dir_path) {
std::unique_ptr<CallSessionFileRotatingStream> stream(
new CallSessionFileRotatingStream(dir_path));
ASSERT_TRUE(stream->Open());
size_t read = 0;
size_t stream_size = 0;
EXPECT_TRUE(stream->GetSize(&stream_size));
std::unique_ptr<uint8_t[]> buffer(new uint8_t[expected_length]);
EXPECT_EQ(SR_SUCCESS,
stream->ReadAll(buffer.get(), expected_length, &read, nullptr));
EXPECT_EQ(0, memcmp(expected_contents, buffer.get(), expected_length));
EXPECT_EQ(SR_EOS, stream->ReadAll(buffer.get(), 1, nullptr, nullptr));
EXPECT_EQ(stream_size, read);
}
std::unique_ptr<CallSessionFileRotatingStream> stream_;
std::string dir_path_;
};
// Tests that writing and reading to a stream with the smallest possible
// capacity works.
TEST_F(MAYBE_CallSessionFileRotatingStreamTest, WriteAndReadSmallest) {
Init("CallSessionFileRotatingStreamTestWriteAndReadSmallest", 4);
ASSERT_TRUE(stream_->Open());
std::string message("abcde");
WriteAndFlush(message.c_str(), message.size());
std::string expected_contents("abe");
VerifyStreamRead(expected_contents.c_str(), expected_contents.size(),
dir_path_);
}
// Tests that writing and reading to a stream with capacity lesser than 4MB
// behaves correctly.
TEST_F(MAYBE_CallSessionFileRotatingStreamTest, WriteAndReadSmall) {
Init("CallSessionFileRotatingStreamTestWriteAndReadSmall", 8);
ASSERT_TRUE(stream_->Open());
std::string message("123456789");
WriteAndFlush(message.c_str(), message.size());
std::string expected_contents("1234789");
VerifyStreamRead(expected_contents.c_str(), expected_contents.size(),
dir_path_);
}
// Tests that writing and reading to a stream with capacity greater than 4MB
// behaves correctly.
TEST_F(MAYBE_CallSessionFileRotatingStreamTest, WriteAndReadLarge) {
Init("CallSessionFileRotatingStreamTestWriteAndReadLarge", 6 * 1024 * 1024);
ASSERT_TRUE(stream_->Open());
const size_t buffer_size = 1024 * 1024;
std::unique_ptr<uint8_t[]> buffer(new uint8_t[buffer_size]);
for (int i = 0; i < 8; i++) {
memset(buffer.get(), i, buffer_size);
EXPECT_EQ(SR_SUCCESS,
stream_->WriteAll(buffer.get(), buffer_size, nullptr, nullptr));
}
stream_.reset(new CallSessionFileRotatingStream(dir_path_));
ASSERT_TRUE(stream_->Open());
std::unique_ptr<uint8_t[]> expected_buffer(new uint8_t[buffer_size]);
int expected_vals[] = {0, 1, 2, 6, 7};
for (size_t i = 0; i < arraysize(expected_vals); ++i) {
memset(expected_buffer.get(), expected_vals[i], buffer_size);
EXPECT_EQ(SR_SUCCESS,
stream_->ReadAll(buffer.get(), buffer_size, nullptr, nullptr));
EXPECT_EQ(0, memcmp(buffer.get(), expected_buffer.get(), buffer_size));
}
EXPECT_EQ(SR_EOS, stream_->ReadAll(buffer.get(), 1, nullptr, nullptr));
}
// Tests that writing and reading to a stream where only the first file is
// written to behaves correctly.
TEST_F(MAYBE_CallSessionFileRotatingStreamTest, WriteAndReadFirstHalf) {
Init("CallSessionFileRotatingStreamTestWriteAndReadFirstHalf",
6 * 1024 * 1024);
ASSERT_TRUE(stream_->Open());
const size_t buffer_size = 1024 * 1024;
std::unique_ptr<uint8_t[]> buffer(new uint8_t[buffer_size]);
for (int i = 0; i < 2; i++) {
memset(buffer.get(), i, buffer_size);
EXPECT_EQ(SR_SUCCESS,
stream_->WriteAll(buffer.get(), buffer_size, nullptr, nullptr));
}
stream_.reset(new CallSessionFileRotatingStream(dir_path_));
ASSERT_TRUE(stream_->Open());
std::unique_ptr<uint8_t[]> expected_buffer(new uint8_t[buffer_size]);
int expected_vals[] = {0, 1};
for (size_t i = 0; i < arraysize(expected_vals); ++i) {
memset(expected_buffer.get(), expected_vals[i], buffer_size);
EXPECT_EQ(SR_SUCCESS,
stream_->ReadAll(buffer.get(), buffer_size, nullptr, nullptr));
EXPECT_EQ(0, memcmp(buffer.get(), expected_buffer.get(), buffer_size));
}
EXPECT_EQ(SR_EOS, stream_->ReadAll(buffer.get(), 1, nullptr, nullptr));
}
} // namespace rtc

View File

@ -0,0 +1,133 @@
/*
* 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.
*/
#include "webrtc/base/fileutils.h"
#include "webrtc/base/arraysize.h"
#include "webrtc/base/checks.h"
#include "webrtc/base/pathutils.h"
#include "webrtc/base/stringutils.h"
#if defined(WEBRTC_WIN)
#include "webrtc/base/win32filesystem.h"
#else
#include "webrtc/base/unixfilesystem.h"
#endif
#if !defined(WEBRTC_WIN)
#define MAX_PATH 260
#endif
namespace rtc {
//////////////////////////
// Directory Iterator //
//////////////////////////
// A DirectoryIterator is created with a given directory. It originally points
// to the first file in the directory, and can be advanecd with Next(). This
// allows you to get information about each file.
// Constructor
DirectoryIterator::DirectoryIterator()
#ifdef WEBRTC_WIN
: handle_(INVALID_HANDLE_VALUE) {
#else
: dir_(nullptr),
dirent_(nullptr){
#endif
}
// Destructor
DirectoryIterator::~DirectoryIterator() {
#if defined(WEBRTC_WIN)
if (handle_ != INVALID_HANDLE_VALUE)
::FindClose(handle_);
#else
if (dir_)
closedir(dir_);
#endif
}
// Starts traversing a directory.
// dir is the directory to traverse
// returns true if the directory exists and is valid
bool DirectoryIterator::Iterate(const Pathname &dir) {
directory_ = dir.pathname();
#if defined(WEBRTC_WIN)
if (handle_ != INVALID_HANDLE_VALUE)
::FindClose(handle_);
std::string d = dir.pathname() + '*';
handle_ = ::FindFirstFile(ToUtf16(d).c_str(), &data_);
if (handle_ == INVALID_HANDLE_VALUE)
return false;
#else
if (dir_ != nullptr)
closedir(dir_);
dir_ = ::opendir(directory_.c_str());
if (dir_ == nullptr)
return false;
dirent_ = readdir(dir_);
if (dirent_ == nullptr)
return false;
if (::stat(std::string(directory_ + Name()).c_str(), &stat_) != 0)
return false;
#endif
return true;
}
// Advances to the next file
// returns true if there were more files in the directory.
bool DirectoryIterator::Next() {
#if defined(WEBRTC_WIN)
return ::FindNextFile(handle_, &data_) == TRUE;
#else
dirent_ = ::readdir(dir_);
if (dirent_ == nullptr)
return false;
return ::stat(std::string(directory_ + Name()).c_str(), &stat_) == 0;
#endif
}
// returns true if the file currently pointed to is a directory
bool DirectoryIterator::IsDirectory() const {
#if defined(WEBRTC_WIN)
return (data_.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != FALSE;
#else
return S_ISDIR(stat_.st_mode);
#endif
}
// returns the name of the file currently pointed to
std::string DirectoryIterator::Name() const {
#if defined(WEBRTC_WIN)
return ToUtf8(data_.cFileName);
#else
RTC_DCHECK(dirent_);
return dirent_->d_name;
#endif
}
FilesystemInterface* Filesystem::default_filesystem_ = nullptr;
FilesystemInterface *Filesystem::EnsureDefaultFilesystem() {
if (!default_filesystem_) {
#if defined(WEBRTC_WIN)
default_filesystem_ = new Win32Filesystem();
#else
default_filesystem_ = new UnixFilesystem();
#endif
}
return default_filesystem_;
}
} // namespace rtc

182
webrtc/rtc_base/fileutils.h Normal file
View File

@ -0,0 +1,182 @@
/*
* 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_RTC_BASE_FILEUTILS_H_
#define WEBRTC_RTC_BASE_FILEUTILS_H_
#include <string>
#if !defined(WEBRTC_WIN)
#include <dirent.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#endif
#include "webrtc/base/checks.h"
#include "webrtc/base/constructormagic.h"
#include "webrtc/base/platform_file.h"
namespace rtc {
class FileStream;
class Pathname;
//////////////////////////
// Directory Iterator //
//////////////////////////
// A DirectoryIterator is created with a given directory. It originally points
// to the first file in the directory, and can be advanecd with Next(). This
// allows you to get information about each file.
class DirectoryIterator {
friend class Filesystem;
public:
// Constructor
DirectoryIterator();
// Destructor
virtual ~DirectoryIterator();
// Starts traversing a directory
// dir is the directory to traverse
// returns true if the directory exists and is valid
// The iterator will point to the first entry in the directory
virtual bool Iterate(const Pathname &path);
// Advances to the next file
// returns true if there were more files in the directory.
virtual bool Next();
// returns true if the file currently pointed to is a directory
virtual bool IsDirectory() const;
// returns the name of the file currently pointed to
virtual std::string Name() const;
private:
std::string directory_;
#if defined(WEBRTC_WIN)
WIN32_FIND_DATA data_;
HANDLE handle_;
#else
DIR *dir_;
struct dirent *dirent_;
struct stat stat_;
#endif
};
class FilesystemInterface {
public:
virtual ~FilesystemInterface() {}
// This will attempt to delete the path located at filename.
// It DCHECKs and returns false if the path points to a folder or a
// non-existent file.
virtual bool DeleteFile(const Pathname &filename) = 0;
// Creates a directory. This will call itself recursively to create /foo/bar
// even if /foo does not exist. Returns true if the function succeeds.
virtual bool CreateFolder(const Pathname &pathname) = 0;
// This moves a file from old_path to new_path, where "old_path" is a
// plain file. This DCHECKs and returns false if old_path points to a
// directory, and returns true if the function succeeds.
virtual bool MoveFile(const Pathname &old_path, const Pathname &new_path) = 0;
// Returns true if pathname refers to a directory
virtual bool IsFolder(const Pathname& pathname) = 0;
// Returns true if pathname refers to a file
virtual bool IsFile(const Pathname& pathname) = 0;
// Returns true if pathname refers to no filesystem object, every parent
// directory either exists, or is also absent.
virtual bool IsAbsent(const Pathname& pathname) = 0;
// A folder appropriate for storing temporary files (Contents are
// automatically deleted when the program exits)
virtual bool GetTemporaryFolder(Pathname &path, bool create,
const std::string *append) = 0;
virtual std::string TempFilename(const Pathname &dir,
const std::string &prefix) = 0;
// Determines the size of the file indicated by path.
virtual bool GetFileSize(const Pathname& path, size_t* size) = 0;
};
class Filesystem {
public:
static FilesystemInterface *default_filesystem() {
RTC_DCHECK(default_filesystem_);
return default_filesystem_;
}
static void set_default_filesystem(FilesystemInterface *filesystem) {
default_filesystem_ = filesystem;
}
static FilesystemInterface *swap_default_filesystem(
FilesystemInterface *filesystem) {
FilesystemInterface *cur = default_filesystem_;
default_filesystem_ = filesystem;
return cur;
}
static bool CreateFolder(const Pathname &pathname) {
return EnsureDefaultFilesystem()->CreateFolder(pathname);
}
static bool DeleteFile(const Pathname &filename) {
return EnsureDefaultFilesystem()->DeleteFile(filename);
}
static bool MoveFile(const Pathname &old_path, const Pathname &new_path) {
return EnsureDefaultFilesystem()->MoveFile(old_path, new_path);
}
static bool IsFolder(const Pathname& pathname) {
return EnsureDefaultFilesystem()->IsFolder(pathname);
}
static bool IsFile(const Pathname &pathname) {
return EnsureDefaultFilesystem()->IsFile(pathname);
}
static bool IsAbsent(const Pathname &pathname) {
return EnsureDefaultFilesystem()->IsAbsent(pathname);
}
static bool GetTemporaryFolder(Pathname &path, bool create,
const std::string *append) {
return EnsureDefaultFilesystem()->GetTemporaryFolder(path, create, append);
}
static std::string TempFilename(const Pathname &dir,
const std::string &prefix) {
return EnsureDefaultFilesystem()->TempFilename(dir, prefix);
}
static bool GetFileSize(const Pathname& path, size_t* size) {
return EnsureDefaultFilesystem()->GetFileSize(path, size);
}
private:
static FilesystemInterface* default_filesystem_;
static FilesystemInterface *EnsureDefaultFilesystem();
RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(Filesystem);
};
} // namespace rtc
#endif // WEBRTC_RTC_BASE_FILEUTILS_H_

View File

@ -0,0 +1,33 @@
/*
* 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.
*/
#include <memory>
#include "webrtc/base/fileutils.h"
#include "webrtc/base/gunit.h"
#include "webrtc/base/pathutils.h"
#include "webrtc/base/stream.h"
namespace rtc {
#if defined (WEBRTC_ANDROID)
// Fails on Android: https://bugs.chromium.org/p/webrtc/issues/detail?id=4364.
#define MAYBE_FilesystemTest DISABLED_FilesystemTest
#else
#define MAYBE_FilesystemTest FilesystemTest
#endif
// Make sure we can get a temp folder for the later tests.
TEST(MAYBE_FilesystemTest, GetTemporaryFolder) {
Pathname path;
EXPECT_TRUE(Filesystem::GetTemporaryFolder(path, true, nullptr));
}
} // namespace rtc

View File

@ -0,0 +1,271 @@
/*
* 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.
*/
#include "webrtc/base/firewallsocketserver.h"
#include <algorithm>
#include "webrtc/base/asyncsocket.h"
#include "webrtc/base/checks.h"
#include "webrtc/base/logging.h"
namespace rtc {
class FirewallSocket : public AsyncSocketAdapter {
public:
FirewallSocket(FirewallSocketServer* server, AsyncSocket* socket, int type)
: AsyncSocketAdapter(socket), server_(server), type_(type) {
}
int Bind(const SocketAddress& addr) override {
if (!server_->IsBindableIp(addr.ipaddr())) {
SetError(EINVAL);
return SOCKET_ERROR;
}
return AsyncSocketAdapter::Bind(addr);
}
int Connect(const SocketAddress& addr) override {
if (type_ == SOCK_STREAM) {
if (!server_->Check(FP_TCP, GetLocalAddress(), addr)) {
LOG(LS_VERBOSE) << "FirewallSocket outbound TCP connection from "
<< GetLocalAddress().ToSensitiveString() << " to "
<< addr.ToSensitiveString() << " denied";
// TODO: Handle this asynchronously.
SetError(EHOSTUNREACH);
return SOCKET_ERROR;
}
}
return AsyncSocketAdapter::Connect(addr);
}
int Send(const void* pv, size_t cb) override {
return SendTo(pv, cb, GetRemoteAddress());
}
int SendTo(const void* pv, size_t cb, const SocketAddress& addr) override {
RTC_DCHECK(type_ == SOCK_DGRAM || type_ == SOCK_STREAM);
FirewallProtocol protocol = (type_ == SOCK_DGRAM) ? FP_UDP : FP_TCP;
if (!server_->Check(protocol, GetLocalAddress(), addr)) {
LOG(LS_VERBOSE) << "FirewallSocket outbound packet with type " << type_
<< " from " << GetLocalAddress().ToSensitiveString()
<< " to " << addr.ToSensitiveString() << " dropped";
return static_cast<int>(cb);
}
return AsyncSocketAdapter::SendTo(pv, cb, addr);
}
int Recv(void* pv, size_t cb, int64_t* timestamp) override {
SocketAddress addr;
return RecvFrom(pv, cb, &addr, timestamp);
}
int RecvFrom(void* pv,
size_t cb,
SocketAddress* paddr,
int64_t* timestamp) override {
if (type_ == SOCK_DGRAM) {
while (true) {
int res = AsyncSocketAdapter::RecvFrom(pv, cb, paddr, timestamp);
if (res <= 0)
return res;
if (server_->Check(FP_UDP, *paddr, GetLocalAddress()))
return res;
LOG(LS_VERBOSE) << "FirewallSocket inbound UDP packet from "
<< paddr->ToSensitiveString() << " to "
<< GetLocalAddress().ToSensitiveString() << " dropped";
}
}
return AsyncSocketAdapter::RecvFrom(pv, cb, paddr, timestamp);
}
int Listen(int backlog) override {
if (!server_->tcp_listen_enabled()) {
LOG(LS_VERBOSE) << "FirewallSocket listen attempt denied";
return -1;
}
return AsyncSocketAdapter::Listen(backlog);
}
AsyncSocket* Accept(SocketAddress* paddr) override {
SocketAddress addr;
while (AsyncSocket* sock = AsyncSocketAdapter::Accept(&addr)) {
if (server_->Check(FP_TCP, addr, GetLocalAddress())) {
if (paddr)
*paddr = addr;
return sock;
}
sock->Close();
delete sock;
LOG(LS_VERBOSE) << "FirewallSocket inbound TCP connection from "
<< addr.ToSensitiveString() << " to "
<< GetLocalAddress().ToSensitiveString() << " denied";
}
return 0;
}
private:
FirewallSocketServer* server_;
int type_;
};
FirewallSocketServer::FirewallSocketServer(SocketServer* server,
FirewallManager* manager,
bool should_delete_server)
: server_(server), manager_(manager),
should_delete_server_(should_delete_server),
udp_sockets_enabled_(true), tcp_sockets_enabled_(true),
tcp_listen_enabled_(true) {
if (manager_)
manager_->AddServer(this);
}
FirewallSocketServer::~FirewallSocketServer() {
if (manager_)
manager_->RemoveServer(this);
if (server_ && should_delete_server_) {
delete server_;
server_ = nullptr;
}
}
void FirewallSocketServer::AddRule(bool allow, FirewallProtocol p,
FirewallDirection d,
const SocketAddress& addr) {
SocketAddress any;
if (d == FD_IN || d == FD_ANY) {
AddRule(allow, p, any, addr);
}
if (d == FD_OUT || d == FD_ANY) {
AddRule(allow, p, addr, any);
}
}
void FirewallSocketServer::AddRule(bool allow, FirewallProtocol p,
const SocketAddress& src,
const SocketAddress& dst) {
Rule r;
r.allow = allow;
r.p = p;
r.src = src;
r.dst = dst;
CritScope scope(&crit_);
rules_.push_back(r);
}
void FirewallSocketServer::ClearRules() {
CritScope scope(&crit_);
rules_.clear();
}
bool FirewallSocketServer::Check(FirewallProtocol p,
const SocketAddress& src,
const SocketAddress& dst) {
CritScope scope(&crit_);
for (size_t i = 0; i < rules_.size(); ++i) {
const Rule& r = rules_[i];
if ((r.p != p) && (r.p != FP_ANY))
continue;
if ((r.src.ipaddr() != src.ipaddr()) && !r.src.IsNil())
continue;
if ((r.src.port() != src.port()) && (r.src.port() != 0))
continue;
if ((r.dst.ipaddr() != dst.ipaddr()) && !r.dst.IsNil())
continue;
if ((r.dst.port() != dst.port()) && (r.dst.port() != 0))
continue;
return r.allow;
}
return true;
}
void FirewallSocketServer::SetUnbindableIps(
const std::vector<rtc::IPAddress>& unbindable_ips) {
unbindable_ips_ = unbindable_ips;
}
bool FirewallSocketServer::IsBindableIp(const rtc::IPAddress& ip) {
return std::find(unbindable_ips_.begin(), unbindable_ips_.end(), ip) ==
unbindable_ips_.end();
}
Socket* FirewallSocketServer::CreateSocket(int type) {
return CreateSocket(AF_INET, type);
}
Socket* FirewallSocketServer::CreateSocket(int family, int type) {
return WrapSocket(server_->CreateAsyncSocket(family, type), type);
}
AsyncSocket* FirewallSocketServer::CreateAsyncSocket(int type) {
return CreateAsyncSocket(AF_INET, type);
}
AsyncSocket* FirewallSocketServer::CreateAsyncSocket(int family, int type) {
return WrapSocket(server_->CreateAsyncSocket(family, type), type);
}
void FirewallSocketServer::SetMessageQueue(MessageQueue* queue) {
server_->SetMessageQueue(queue);
}
bool FirewallSocketServer::Wait(int cms, bool process_io) {
return server_->Wait(cms, process_io);
}
void FirewallSocketServer::WakeUp() {
return server_->WakeUp();
}
AsyncSocket* FirewallSocketServer::WrapSocket(AsyncSocket* sock, int type) {
if (!sock ||
(type == SOCK_STREAM && !tcp_sockets_enabled_) ||
(type == SOCK_DGRAM && !udp_sockets_enabled_)) {
LOG(LS_VERBOSE) << "FirewallSocketServer socket creation denied";
delete sock;
return nullptr;
}
return new FirewallSocket(this, sock, type);
}
FirewallManager::FirewallManager() {
}
FirewallManager::~FirewallManager() {
RTC_DCHECK(servers_.empty());
}
void FirewallManager::AddServer(FirewallSocketServer* server) {
CritScope scope(&crit_);
servers_.push_back(server);
}
void FirewallManager::RemoveServer(FirewallSocketServer* server) {
CritScope scope(&crit_);
servers_.erase(std::remove(servers_.begin(), servers_.end(), server),
servers_.end());
}
void FirewallManager::AddRule(bool allow, FirewallProtocol p,
FirewallDirection d, const SocketAddress& addr) {
CritScope scope(&crit_);
for (std::vector<FirewallSocketServer*>::const_iterator it =
servers_.begin(); it != servers_.end(); ++it) {
(*it)->AddRule(allow, p, d, addr);
}
}
void FirewallManager::ClearRules() {
CritScope scope(&crit_);
for (std::vector<FirewallSocketServer*>::const_iterator it =
servers_.begin(); it != servers_.end(); ++it) {
(*it)->ClearRules();
}
}
} // namespace rtc

View File

@ -0,0 +1,125 @@
/*
* 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_RTC_BASE_FIREWALLSOCKETSERVER_H_
#define WEBRTC_RTC_BASE_FIREWALLSOCKETSERVER_H_
#include <vector>
#include "webrtc/base/socketserver.h"
#include "webrtc/base/criticalsection.h"
namespace rtc {
class FirewallManager;
// This SocketServer shim simulates a rule-based firewall server.
enum FirewallProtocol { FP_UDP, FP_TCP, FP_ANY };
enum FirewallDirection { FD_IN, FD_OUT, FD_ANY };
class FirewallSocketServer : public SocketServer {
public:
FirewallSocketServer(SocketServer* server,
FirewallManager* manager = nullptr,
bool should_delete_server = false);
~FirewallSocketServer() override;
SocketServer* socketserver() const { return server_; }
void set_socketserver(SocketServer* server) {
if (server_ && should_delete_server_) {
delete server_;
server_ = nullptr;
should_delete_server_ = false;
}
server_ = server;
}
// Settings to control whether CreateSocket or Socket::Listen succeed.
void set_udp_sockets_enabled(bool enabled) { udp_sockets_enabled_ = enabled; }
void set_tcp_sockets_enabled(bool enabled) { tcp_sockets_enabled_ = enabled; }
bool tcp_listen_enabled() const { return tcp_listen_enabled_; }
void set_tcp_listen_enabled(bool enabled) { tcp_listen_enabled_ = enabled; }
// Rules govern the behavior of Connect/Accept/Send/Recv attempts.
void AddRule(bool allow, FirewallProtocol p = FP_ANY,
FirewallDirection d = FD_ANY,
const SocketAddress& addr = SocketAddress());
void AddRule(bool allow, FirewallProtocol p,
const SocketAddress& src, const SocketAddress& dst);
void ClearRules();
bool Check(FirewallProtocol p,
const SocketAddress& src, const SocketAddress& dst);
// Set the IP addresses for which Bind will fail. By default this list is
// empty. This can be used to simulate a real OS that refuses to bind to
// addresses under various circumstances.
//
// No matter how many addresses are added (including INADDR_ANY), the server
// will still allow creating outgoing TCP connections, since they don't
// require explicitly binding a socket.
void SetUnbindableIps(const std::vector<rtc::IPAddress>& unbindable_ips);
bool IsBindableIp(const rtc::IPAddress& ip);
Socket* CreateSocket(int type) override;
Socket* CreateSocket(int family, int type) override;
AsyncSocket* CreateAsyncSocket(int type) override;
AsyncSocket* CreateAsyncSocket(int family, int type) override;
void SetMessageQueue(MessageQueue* queue) override;
bool Wait(int cms, bool process_io) override;
void WakeUp() override;
Socket * WrapSocket(Socket * sock, int type);
AsyncSocket * WrapSocket(AsyncSocket * sock, int type);
private:
SocketServer * server_;
FirewallManager * manager_;
CriticalSection crit_;
struct Rule {
bool allow;
FirewallProtocol p;
FirewallDirection d;
SocketAddress src;
SocketAddress dst;
};
std::vector<Rule> rules_;
std::vector<rtc::IPAddress> unbindable_ips_;
bool should_delete_server_;
bool udp_sockets_enabled_;
bool tcp_sockets_enabled_;
bool tcp_listen_enabled_;
};
// FirewallManager allows you to manage firewalls in multiple threads together
class FirewallManager {
public:
FirewallManager();
~FirewallManager();
void AddServer(FirewallSocketServer * server);
void RemoveServer(FirewallSocketServer * server);
void AddRule(bool allow, FirewallProtocol p = FP_ANY,
FirewallDirection d = FD_ANY,
const SocketAddress& addr = SocketAddress());
void ClearRules();
private:
CriticalSection crit_;
std::vector<FirewallSocketServer *> servers_;
};
} // namespace rtc
#endif // WEBRTC_RTC_BASE_FIREWALLSOCKETSERVER_H_

299
webrtc/rtc_base/flags.cc Normal file
View File

@ -0,0 +1,299 @@
/*
* Copyright 2006 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.
*/
#include "webrtc/base/flags.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "webrtc/base/checks.h"
#if defined(WEBRTC_WIN)
#include "webrtc/base/win32.h"
#include <shellapi.h>
#endif
namespace rtc {
// -----------------------------------------------------------------------------
// Implementation of Flag
Flag::Flag(const char* file, const char* name, const char* comment,
Type type, void* variable, FlagValue default__)
: file_(file),
name_(name),
comment_(comment),
type_(type),
variable_(reinterpret_cast<FlagValue*>(variable)),
default_(default__) {
FlagList::Register(this);
}
void Flag::SetToDefault() {
// Note that we cannot simply do '*variable_ = default_;' since
// flag variables are not really of type FlagValue and thus may
// be smaller! The FlagValue union is simply 'overlayed' on top
// of a flag variable for convenient access. Since union members
// are guarantee to be aligned at the beginning, this works.
switch (type_) {
case Flag::BOOL:
variable_->b = default_.b;
return;
case Flag::INT:
variable_->i = default_.i;
return;
case Flag::FLOAT:
variable_->f = default_.f;
return;
case Flag::STRING:
variable_->s = default_.s;
return;
}
FATAL() << "unreachable code";
}
static const char* Type2String(Flag::Type type) {
switch (type) {
case Flag::BOOL: return "bool";
case Flag::INT: return "int";
case Flag::FLOAT: return "float";
case Flag::STRING: return "string";
}
FATAL() << "unreachable code";
}
static void PrintFlagValue(Flag::Type type, FlagValue* p) {
switch (type) {
case Flag::BOOL:
printf("%s", (p->b ? "true" : "false"));
return;
case Flag::INT:
printf("%d", p->i);
return;
case Flag::FLOAT:
printf("%f", p->f);
return;
case Flag::STRING:
printf("%s", p->s);
return;
}
FATAL() << "unreachable code";
}
void Flag::Print(bool print_current_value) {
printf(" --%s (%s) type: %s default: ", name_, comment_,
Type2String(type_));
PrintFlagValue(type_, &default_);
if (print_current_value) {
printf(" current value: ");
PrintFlagValue(type_, variable_);
}
printf("\n");
}
// -----------------------------------------------------------------------------
// Implementation of FlagList
Flag* FlagList::list_ = nullptr;
FlagList::FlagList() {
list_ = nullptr;
}
void FlagList::Print(const char* file, bool print_current_value) {
// Since flag registration is likely by file (= C++ file),
// we don't need to sort by file and still get grouped output.
const char* current = nullptr;
for (Flag* f = list_; f != nullptr; f = f->next()) {
if (file == nullptr || file == f->file()) {
if (current != f->file()) {
printf("Flags from %s:\n", f->file());
current = f->file();
}
f->Print(print_current_value);
}
}
}
Flag* FlagList::Lookup(const char* name) {
Flag* f = list_;
while (f != nullptr && strcmp(name, f->name()) != 0)
f = f->next();
return f;
}
void FlagList::SplitArgument(const char* arg,
char* buffer, int buffer_size,
const char** name, const char** value,
bool* is_bool) {
*name = nullptr;
*value = nullptr;
*is_bool = false;
if (*arg == '-') {
// find the begin of the flag name
arg++; // remove 1st '-'
if (*arg == '-')
arg++; // remove 2nd '-'
if (arg[0] == 'n' && arg[1] == 'o') {
arg += 2; // remove "no"
*is_bool = true;
}
*name = arg;
// find the end of the flag name
while (*arg != '\0' && *arg != '=')
arg++;
// get the value if any
if (*arg == '=') {
// make a copy so we can NUL-terminate flag name
int n = static_cast<int>(arg - *name);
RTC_CHECK_LT(n, buffer_size);
memcpy(buffer, *name, n * sizeof(char));
buffer[n] = '\0';
*name = buffer;
// get the value
*value = arg + 1;
}
}
}
int FlagList::SetFlagsFromCommandLine(int* argc, const char** argv,
bool remove_flags) {
// parse arguments
for (int i = 1; i < *argc; /* see below */) {
int j = i; // j > 0
const char* arg = argv[i++];
// split arg into flag components
char buffer[1024];
const char* name;
const char* value;
bool is_bool;
SplitArgument(arg, buffer, sizeof buffer, &name, &value, &is_bool);
if (name != nullptr) {
// lookup the flag
Flag* flag = Lookup(name);
if (flag == nullptr) {
fprintf(stderr, "Error: unrecognized flag %s\n", arg);
return j;
}
// if we still need a flag value, use the next argument if available
if (flag->type() != Flag::BOOL && value == nullptr) {
if (i < *argc) {
value = argv[i++];
} else {
fprintf(stderr, "Error: missing value for flag %s of type %s\n",
arg, Type2String(flag->type()));
return j;
}
}
// set the flag
char empty[] = { '\0' };
char* endp = empty;
switch (flag->type()) {
case Flag::BOOL:
*flag->bool_variable() = !is_bool;
break;
case Flag::INT:
*flag->int_variable() = strtol(value, &endp, 10);
break;
case Flag::FLOAT:
*flag->float_variable() = strtod(value, &endp);
break;
case Flag::STRING:
*flag->string_variable() = value;
break;
}
// handle errors
if ((flag->type() == Flag::BOOL && value != nullptr) ||
(flag->type() != Flag::BOOL && is_bool) || *endp != '\0') {
fprintf(stderr, "Error: illegal value for flag %s of type %s\n",
arg, Type2String(flag->type()));
return j;
}
// remove the flag & value from the command
if (remove_flags)
while (j < i)
argv[j++] = nullptr;
}
}
// shrink the argument list
if (remove_flags) {
int j = 1;
for (int i = 1; i < *argc; i++) {
if (argv[i] != nullptr)
argv[j++] = argv[i];
}
*argc = j;
}
// parsed all flags successfully
return 0;
}
void FlagList::Register(Flag* flag) {
RTC_DCHECK(flag);
RTC_DCHECK_GT(strlen(flag->name()), 0);
// NOTE: Don't call Lookup() within Register because it accesses the name_
// of other flags in list_, and if the flags are coming from two different
// compilation units, the initialization order between them is undefined, and
// this will trigger an asan initialization-order-fiasco error.
flag->next_ = list_;
list_ = flag;
}
#if defined(WEBRTC_WIN)
WindowsCommandLineArguments::WindowsCommandLineArguments() {
// start by getting the command line.
LPTSTR command_line = ::GetCommandLine();
// now, convert it to a list of wide char strings.
LPWSTR *wide_argv = ::CommandLineToArgvW(command_line, &argc_);
// now allocate an array big enough to hold that many string pointers.
argv_ = new char*[argc_];
// iterate over the returned wide strings;
for(int i = 0; i < argc_; ++i) {
std::string s = rtc::ToUtf8(wide_argv[i], wcslen(wide_argv[i]));
char *buffer = new char[s.length() + 1];
rtc::strcpyn(buffer, s.length() + 1, s.c_str());
// make sure the argv array has the right string at this point.
argv_[i] = buffer;
}
LocalFree(wide_argv);
}
WindowsCommandLineArguments::~WindowsCommandLineArguments() {
// need to free each string in the array, and then the array.
for(int i = 0; i < argc_; i++) {
delete[] argv_[i];
}
delete[] argv_;
}
#endif // WEBRTC_WIN
} // namespace rtc

268
webrtc/rtc_base/flags.h Normal file
View File

@ -0,0 +1,268 @@
/*
* Copyright 2006 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.
*/
// Originally comes from shared/commandlineflags/flags.h
// Flags are defined and declared using DEFINE_xxx and DECLARE_xxx macros,
// where xxx is the flag type. Flags are referred to via FLAG_yyy,
// where yyy is the flag name. For intialization and iteration of flags,
// see the FlagList class. For full programmatic access to any
// flag, see the Flag class.
//
// The implementation only relies and basic C++ functionality
// and needs no special library or STL support.
#ifndef WEBRTC_RTC_BASE_FLAGS_H_
#define WEBRTC_RTC_BASE_FLAGS_H_
#include "webrtc/base/checks.h"
#include "webrtc/base/constructormagic.h"
namespace rtc {
// Internal use only.
union FlagValue {
// Note: Because in C++ non-bool values are silently converted into
// bool values ('bool b = "false";' results in b == true!), we pass
// and int argument to New_BOOL as this appears to be safer - sigh.
// In particular, it prevents the (not uncommon!) bug where a bool
// flag is defined via: DEFINE_bool(flag, "false", "some comment");.
static FlagValue New_BOOL(int b) {
FlagValue v;
v.b = (b != 0);
return v;
}
static FlagValue New_INT(int i) {
FlagValue v;
v.i = i;
return v;
}
static FlagValue New_FLOAT(float f) {
FlagValue v;
v.f = f;
return v;
}
static FlagValue New_STRING(const char* s) {
FlagValue v;
v.s = s;
return v;
}
bool b;
int i;
double f;
const char* s;
};
// Each flag can be accessed programmatically via a Flag object.
class Flag {
public:
enum Type { BOOL, INT, FLOAT, STRING };
// Internal use only.
Flag(const char* file, const char* name, const char* comment,
Type type, void* variable, FlagValue default_);
// General flag information
const char* file() const { return file_; }
const char* name() const { return name_; }
const char* comment() const { return comment_; }
// Flag type
Type type() const { return type_; }
// Flag variables
bool* bool_variable() const {
RTC_DCHECK_EQ(BOOL, type_);
return &variable_->b;
}
int* int_variable() const {
RTC_DCHECK_EQ(INT, type_);
return &variable_->i;
}
double* float_variable() const {
RTC_DCHECK_EQ(FLOAT, type_);
return &variable_->f;
}
const char** string_variable() const {
RTC_DCHECK_EQ(STRING, type_);
return &variable_->s;
}
// Default values
bool bool_default() const {
RTC_DCHECK_EQ(BOOL, type_);
return default_.b;
}
int int_default() const {
RTC_DCHECK_EQ(INT, type_);
return default_.i;
}
double float_default() const {
RTC_DCHECK_EQ(FLOAT, type_);
return default_.f;
}
const char* string_default() const {
RTC_DCHECK_EQ(STRING, type_);
return default_.s;
}
// Resets a flag to its default value
void SetToDefault();
// Iteration support
Flag* next() const { return next_; }
// Prints flag information. The current flag value is only printed
// if print_current_value is set.
void Print(bool print_current_value);
private:
const char* file_;
const char* name_;
const char* comment_;
Type type_;
FlagValue* variable_;
FlagValue default_;
Flag* next_;
friend class FlagList; // accesses next_
};
// Internal use only.
#define DEFINE_FLAG(type, c_type, name, default, comment) \
/* define and initialize the flag */ \
c_type FLAG_##name = (default); \
/* register the flag */ \
static rtc::Flag Flag_##name(__FILE__, #name, (comment), \
rtc::Flag::type, &FLAG_##name, \
rtc::FlagValue::New_##type(default))
// Internal use only.
#define DECLARE_FLAG(c_type, name) \
/* declare the external flag */ \
extern c_type FLAG_##name
// Use the following macros to define a new flag:
#define DEFINE_bool(name, default, comment) \
DEFINE_FLAG(BOOL, bool, name, default, comment)
#define DEFINE_int(name, default, comment) \
DEFINE_FLAG(INT, int, name, default, comment)
#define DEFINE_float(name, default, comment) \
DEFINE_FLAG(FLOAT, double, name, default, comment)
#define DEFINE_string(name, default, comment) \
DEFINE_FLAG(STRING, const char*, name, default, comment)
// Use the following macros to declare a flag defined elsewhere:
#define DECLARE_bool(name) DECLARE_FLAG(bool, name)
#define DECLARE_int(name) DECLARE_FLAG(int, name)
#define DECLARE_float(name) DECLARE_FLAG(double, name)
#define DECLARE_string(name) DECLARE_FLAG(const char*, name)
// The global list of all flags.
class FlagList {
public:
FlagList();
// The null-terminated list of all flags. Traverse with Flag::next().
static Flag* list() { return list_; }
// If file != nullptr, prints information for all flags defined in file;
// otherwise prints information for all flags in all files. The current flag
// value is only printed if print_current_value is set.
static void Print(const char* file, bool print_current_value);
// Lookup a flag by name. Returns the matching flag or null.
static Flag* Lookup(const char* name);
// Helper function to parse flags: Takes an argument arg and splits it into
// a flag name and flag value (or null if they are missing). is_bool is set
// if the arg started with "-no" or "--no". The buffer may be used to NUL-
// terminate the name, it must be large enough to hold any possible name.
static void SplitArgument(const char* arg,
char* buffer, int buffer_size,
const char** name, const char** value,
bool* is_bool);
// Set the flag values by parsing the command line. If remove_flags
// is set, the flags and associated values are removed from (argc,
// argv). Returns 0 if no error occurred. Otherwise, returns the
// argv index > 0 for the argument where an error occurred. In that
// case, (argc, argv) will remain unchanged indepdendent of the
// remove_flags value, and no assumptions about flag settings should
// be made.
//
// The following syntax for flags is accepted (both '-' and '--' are ok):
//
// --flag (bool flags only)
// --noflag (bool flags only)
// --flag=value (non-bool flags only, no spaces around '=')
// --flag value (non-bool flags only)
static int SetFlagsFromCommandLine(int* argc,
const char** argv,
bool remove_flags);
static inline int SetFlagsFromCommandLine(int* argc,
char** argv,
bool remove_flags) {
return SetFlagsFromCommandLine(argc, const_cast<const char**>(argv),
remove_flags);
}
// Registers a new flag. Called during program initialization. Not
// thread-safe.
static void Register(Flag* flag);
private:
static Flag* list_;
};
#if defined(WEBRTC_WIN)
// A helper class to translate Windows command line arguments into UTF8,
// which then allows us to just pass them to the flags system.
// This encapsulates all the work of getting the command line and translating
// it to an array of 8-bit strings; all you have to do is create one of these,
// and then call argc() and argv().
class WindowsCommandLineArguments {
public:
WindowsCommandLineArguments();
~WindowsCommandLineArguments();
int argc() { return argc_; }
char **argv() { return argv_; }
private:
int argc_;
char **argv_;
private:
RTC_DISALLOW_COPY_AND_ASSIGN(WindowsCommandLineArguments);
};
#endif // WEBRTC_WIN
} // namespace rtc
#endif // SHARED_COMMANDLINEFLAGS_FLAGS_H_

View File

@ -0,0 +1,96 @@
/*
* Copyright (c) 2014 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_RTC_BASE_FORMAT_MACROS_H_
#define WEBRTC_RTC_BASE_FORMAT_MACROS_H_
// This file defines the format macros for some integer types and is derived
// from Chromium's base/format_macros.h.
// To print a 64-bit value in a portable way:
// int64_t value;
// printf("xyz:%" PRId64, value);
// The "d" in the macro corresponds to %d; you can also use PRIu64 etc.
//
// To print a size_t value in a portable way:
// size_t size;
// printf("xyz: %" PRIuS, size);
// The "u" in the macro corresponds to %u, and S is for "size".
#include "webrtc/typedefs.h"
#if defined(WEBRTC_POSIX)
#if (defined(_INTTYPES_H) || defined(_INTTYPES_H_)) && !defined(PRId64)
#error "inttypes.h has already been included before this header file, but "
#error "without __STDC_FORMAT_MACROS defined."
#endif
#if !defined(__STDC_FORMAT_MACROS)
#define __STDC_FORMAT_MACROS
#endif
#include <inttypes.h>
#if !defined(PRIuS)
#define PRIuS "zu"
#endif
// The size of NSInteger and NSUInteger varies between 32-bit and 64-bit
// architectures and Apple does not provides standard format macros and
// recommends casting. This has many drawbacks, so instead define macros
// for formatting those types.
#if defined(WEBRTC_MAC)
#if defined(WEBRTC_ARCH_64_BITS)
#if !defined(PRIdNS)
#define PRIdNS "ld"
#endif
#if !defined(PRIuNS)
#define PRIuNS "lu"
#endif
#if !defined(PRIxNS)
#define PRIxNS "lx"
#endif
#else // defined(WEBRTC_ARCH_64_BITS)
#if !defined(PRIdNS)
#define PRIdNS "d"
#endif
#if !defined(PRIuNS)
#define PRIuNS "u"
#endif
#if !defined(PRIxNS)
#define PRIxNS "x"
#endif
#endif
#endif // defined(WEBRTC_MAC)
#else // WEBRTC_WIN
#include <inttypes.h>
#if !defined(PRId64)
#define PRId64 "I64d"
#endif
#if !defined(PRIu64)
#define PRIu64 "I64u"
#endif
#if !defined(PRIx64)
#define PRIx64 "I64x"
#endif
#if !defined(PRIuS)
#define PRIuS "Iu"
#endif
#endif
#endif // WEBRTC_RTC_BASE_FORMAT_MACROS_H_

View File

@ -0,0 +1,130 @@
/*
* Copyright 2016 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_RTC_BASE_FUNCTION_VIEW_H_
#define WEBRTC_RTC_BASE_FUNCTION_VIEW_H_
#include <type_traits>
#include <utility>
#include "webrtc/base/checks.h"
// Just like std::function, FunctionView will wrap any callable and hide its
// actual type, exposing only its signature. But unlike std::function,
// FunctionView doesn't own its callable---it just points to it. Thus, it's a
// good choice mainly as a function argument when the callable argument will
// not be called again once the function has returned.
//
// Its constructors are implicit, so that callers won't have to convert lambdas
// and other callables to FunctionView<Blah(Blah, Blah)> explicitly. This is
// safe because FunctionView is only a reference to the real callable.
//
// Example use:
//
// void SomeFunction(rtc::FunctionView<int(int)> index_transform);
// ...
// SomeFunction([](int i) { return 2 * i + 1; });
//
// Note: FunctionView is tiny (essentially just two pointers) and trivially
// copyable, so it's probably cheaper to pass it by value than by const
// reference.
namespace rtc {
template <typename T>
class FunctionView; // Undefined.
template <typename RetT, typename... ArgT>
class FunctionView<RetT(ArgT...)> final {
public:
// Constructor for lambdas and other callables; it accepts every type of
// argument except those noted in its enable_if call.
template <
typename F,
typename std::enable_if<
// Not for function pointers; we have another constructor for that
// below.
!std::is_function<typename std::remove_pointer<
typename std::remove_reference<F>::type>::type>::value &&
// Not for nullptr; we have another constructor for that below.
!std::is_same<std::nullptr_t,
typename std::remove_cv<F>::type>::value &&
// Not for FunctionView objects; we have another constructor for that
// (the implicitly declared copy constructor).
!std::is_same<FunctionView,
typename std::remove_cv<typename std::remove_reference<
F>::type>::type>::value>::type* = nullptr>
FunctionView(F&& f)
: call_(CallVoidPtr<typename std::remove_reference<F>::type>) {
f_.void_ptr = &f;
}
// Constructor that accepts function pointers. If the argument is null, the
// result is an empty FunctionView.
template <
typename F,
typename std::enable_if<std::is_function<typename std::remove_pointer<
typename std::remove_reference<F>::type>::type>::value>::type* =
nullptr>
FunctionView(F&& f)
: call_(f ? CallFunPtr<typename std::remove_pointer<F>::type> : nullptr) {
f_.fun_ptr = reinterpret_cast<void (*)()>(f);
}
// Constructor that accepts nullptr. It creates an empty FunctionView.
template <typename F,
typename std::enable_if<std::is_same<
std::nullptr_t,
typename std::remove_cv<F>::type>::value>::type* = nullptr>
FunctionView(F&& f) : call_(nullptr) {}
// Default constructor. Creates an empty FunctionView.
FunctionView() : call_(nullptr) {}
RetT operator()(ArgT... args) const {
RTC_DCHECK(call_);
return call_(f_, std::forward<ArgT>(args)...);
}
// Returns true if we have a function, false if we don't (i.e., we're null).
explicit operator bool() const { return !!call_; }
private:
union VoidUnion {
void* void_ptr;
void (*fun_ptr)();
};
template <typename F>
static RetT CallVoidPtr(VoidUnion vu, ArgT... args) {
return (*static_cast<F*>(vu.void_ptr))(std::forward<ArgT>(args)...);
}
template <typename F>
static RetT CallFunPtr(VoidUnion vu, ArgT... args) {
return (reinterpret_cast<typename std::add_pointer<F>::type>(vu.fun_ptr))(
std::forward<ArgT>(args)...);
}
// A pointer to the callable thing, with type information erased. It's a
// union because we have to use separate types depending on if the callable
// thing is a function pointer or something else.
VoidUnion f_;
// Pointer to a dispatch function that knows the type of the callable thing
// that's stored in f_, and how to call it. A FunctionView object is empty
// (null) iff call_ is null.
RetT (*call_)(VoidUnion, ArgT...);
};
} // namespace rtc
#endif // WEBRTC_RTC_BASE_FUNCTION_VIEW_H_

View File

@ -0,0 +1,175 @@
/*
* Copyright 2016 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.
*/
#include <memory>
#include <utility>
#include "webrtc/base/function_view.h"
#include "webrtc/base/gunit.h"
namespace rtc {
namespace {
int CallWith33(rtc::FunctionView<int(int)> fv) {
return fv ? fv(33) : -1;
}
int Add33(int x) {
return x + 33;
}
} // namespace
// Test the main use case of FunctionView: implicitly converting a callable
// argument.
TEST(FunctionViewTest, ImplicitConversion) {
EXPECT_EQ(38, CallWith33([](int x) { return x + 5; }));
EXPECT_EQ(66, CallWith33(Add33));
EXPECT_EQ(-1, CallWith33(nullptr));
}
TEST(FunctionViewTest, IntIntLambdaWithoutState) {
auto f = [](int x) { return x + 1; };
EXPECT_EQ(18, f(17));
rtc::FunctionView<int(int)> fv(f);
EXPECT_TRUE(fv);
EXPECT_EQ(18, fv(17));
}
TEST(FunctionViewTest, IntVoidLambdaWithState) {
int x = 13;
auto f = [x]() mutable { return ++x; };
rtc::FunctionView<int()> fv(f);
EXPECT_TRUE(fv);
EXPECT_EQ(14, f());
EXPECT_EQ(15, fv());
EXPECT_EQ(16, f());
EXPECT_EQ(17, fv());
}
TEST(FunctionViewTest, IntIntFunction) {
rtc::FunctionView<int(int)> fv(Add33);
EXPECT_TRUE(fv);
EXPECT_EQ(50, fv(17));
}
TEST(FunctionViewTest, IntIntFunctionPointer) {
rtc::FunctionView<int(int)> fv(&Add33);
EXPECT_TRUE(fv);
EXPECT_EQ(50, fv(17));
}
TEST(FunctionViewTest, Null) {
// These two call constructors that statically construct null FunctionViews.
EXPECT_FALSE(rtc::FunctionView<int()>());
EXPECT_FALSE(rtc::FunctionView<int()>(nullptr));
// This calls the constructor for function pointers.
EXPECT_FALSE(rtc::FunctionView<int()>(reinterpret_cast<int(*)()>(0)));
}
// Ensure that FunctionView handles move-only arguments and return values.
TEST(FunctionViewTest, UniquePtrPassthrough) {
auto f = [](std::unique_ptr<int> x) { return x; };
rtc::FunctionView<std::unique_ptr<int>(std::unique_ptr<int>)> fv(f);
std::unique_ptr<int> x(new int);
int* x_addr = x.get();
auto y = fv(std::move(x));
EXPECT_EQ(x_addr, y.get());
}
TEST(FunctionViewTest, CopyConstructor) {
auto f17 = [] { return 17; };
rtc::FunctionView<int()> fv1(f17);
rtc::FunctionView<int()> fv2(fv1);
EXPECT_EQ(17, fv1());
EXPECT_EQ(17, fv2());
}
TEST(FunctionViewTest, MoveConstructorIsCopy) {
auto f17 = [] { return 17; };
rtc::FunctionView<int()> fv1(f17);
rtc::FunctionView<int()> fv2(std::move(fv1));
EXPECT_EQ(17, fv1());
EXPECT_EQ(17, fv2());
}
TEST(FunctionViewTest, CopyAssignment) {
auto f17 = [] { return 17; };
rtc::FunctionView<int()> fv1(f17);
auto f23 = [] { return 23; };
rtc::FunctionView<int()> fv2(f23);
EXPECT_EQ(17, fv1());
EXPECT_EQ(23, fv2());
fv2 = fv1;
EXPECT_EQ(17, fv1());
EXPECT_EQ(17, fv2());
}
TEST(FunctionViewTest, MoveAssignmentIsCopy) {
auto f17 = [] { return 17; };
rtc::FunctionView<int()> fv1(f17);
auto f23 = [] { return 23; };
rtc::FunctionView<int()> fv2(f23);
EXPECT_EQ(17, fv1());
EXPECT_EQ(23, fv2());
fv2 = std::move(fv1);
EXPECT_EQ(17, fv1());
EXPECT_EQ(17, fv2());
}
TEST(FunctionViewTest, Swap) {
auto f17 = [] { return 17; };
rtc::FunctionView<int()> fv1(f17);
auto f23 = [] { return 23; };
rtc::FunctionView<int()> fv2(f23);
EXPECT_EQ(17, fv1());
EXPECT_EQ(23, fv2());
using std::swap;
swap(fv1, fv2);
EXPECT_EQ(23, fv1());
EXPECT_EQ(17, fv2());
}
// Ensure that when you copy-construct a FunctionView, the new object points to
// the same function as the old one (as opposed to the new object pointing to
// the old one).
TEST(FunctionViewTest, CopyConstructorChaining) {
auto f17 = [] { return 17; };
rtc::FunctionView<int()> fv1(f17);
rtc::FunctionView<int()> fv2(fv1);
EXPECT_EQ(17, fv1());
EXPECT_EQ(17, fv2());
auto f23 = [] { return 23; };
fv1 = f23;
EXPECT_EQ(23, fv1());
EXPECT_EQ(17, fv2());
}
// Ensure that when you assign one FunctionView to another, we actually make a
// copy (as opposed to making the second FunctionView point to the first one).
TEST(FunctionViewTest, CopyAssignmentChaining) {
auto f17 = [] { return 17; };
rtc::FunctionView<int()> fv1(f17);
rtc::FunctionView<int()> fv2;
EXPECT_TRUE(fv1);
EXPECT_EQ(17, fv1());
EXPECT_FALSE(fv2);
fv2 = fv1;
EXPECT_EQ(17, fv1());
EXPECT_EQ(17, fv2());
auto f23 = [] { return 23; };
fv1 = f23;
EXPECT_EQ(23, fv1());
EXPECT_EQ(17, fv2());
}
} // namespace rtc

View File

@ -0,0 +1,38 @@
/*
* Copyright (c) 2012 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_RTC_BASE_GTEST_PROD_UTIL_H_
#define WEBRTC_RTC_BASE_GTEST_PROD_UTIL_H_
// Define our own version of FRIEND_TEST here rather than including
// gtest_prod.h to avoid depending on any part of GTest in production code.
#define FRIEND_TEST_WEBRTC(test_case_name, test_name)\
friend class test_case_name##_##test_name##_Test
// This file is a plain copy of Chromium's base/gtest_prod_util.h.
//
// This is a wrapper for gtest's FRIEND_TEST macro that friends
// test with all possible prefixes. This is very helpful when changing the test
// prefix, because the friend declarations don't need to be updated.
//
// Example usage:
//
// class MyClass {
// private:
// void MyMethod();
// FRIEND_TEST_ALL_PREFIXES(MyClassTest, MyMethod);
// };
#define FRIEND_TEST_ALL_PREFIXES(test_case_name, test_name) \
FRIEND_TEST_WEBRTC(test_case_name, test_name); \
FRIEND_TEST_WEBRTC(test_case_name, DISABLED_##test_name); \
FRIEND_TEST_WEBRTC(test_case_name, FLAKY_##test_name); \
FRIEND_TEST_WEBRTC(test_case_name, FAILS_##test_name)
#endif // WEBRTC_RTC_BASE_GTEST_PROD_UTIL_H_

150
webrtc/rtc_base/gunit.h Normal file
View File

@ -0,0 +1,150 @@
/*
* 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_RTC_BASE_GUNIT_H_
#define WEBRTC_RTC_BASE_GUNIT_H_
#include "webrtc/base/fakeclock.h"
#include "webrtc/base/logging.h"
#include "webrtc/base/thread.h"
#if defined(GTEST_RELATIVE_PATH)
#include "webrtc/test/gtest.h"
#else
#include "testing/base/public/gunit.h"
#endif
// Wait until "ex" is true, or "timeout" expires.
#define WAIT(ex, timeout) \
for (int64_t start = rtc::SystemTimeMillis(); \
!(ex) && rtc::SystemTimeMillis() < start + (timeout);) { \
rtc::Thread::Current()->ProcessMessages(0); \
rtc::Thread::Current()->SleepMs(1); \
}
// This returns the result of the test in res, so that we don't re-evaluate
// the expression in the XXXX_WAIT macros below, since that causes problems
// when the expression is only true the first time you check it.
#define WAIT_(ex, timeout, res) \
do { \
int64_t start = rtc::SystemTimeMillis(); \
res = (ex); \
while (!res && rtc::SystemTimeMillis() < start + (timeout)) { \
rtc::Thread::Current()->ProcessMessages(0); \
rtc::Thread::Current()->SleepMs(1); \
res = (ex); \
} \
} while (0)
// The typical EXPECT_XXXX and ASSERT_XXXXs, but done until true or a timeout.
#define EXPECT_TRUE_WAIT(ex, timeout) \
do { \
bool res; \
WAIT_(ex, timeout, res); \
if (!res) EXPECT_TRUE(ex); \
} while (0)
#define EXPECT_EQ_WAIT(v1, v2, timeout) \
do { \
bool res; \
WAIT_(v1 == v2, timeout, res); \
if (!res) EXPECT_EQ(v1, v2); \
} while (0)
#define ASSERT_TRUE_WAIT(ex, timeout) \
do { \
bool res; \
WAIT_(ex, timeout, res); \
if (!res) ASSERT_TRUE(ex); \
} while (0)
#define ASSERT_EQ_WAIT(v1, v2, timeout) \
do { \
bool res; \
WAIT_(v1 == v2, timeout, res); \
if (!res) ASSERT_EQ(v1, v2); \
} while (0)
// Version with a "soft" timeout and a margin. This logs if the timeout is
// exceeded, but it only fails if the expression still isn't true after the
// margin time passes.
#define EXPECT_TRUE_WAIT_MARGIN(ex, timeout, margin) \
do { \
bool res; \
WAIT_(ex, timeout, res); \
if (res) { \
break; \
} \
LOG(LS_WARNING) << "Expression " << #ex << " still not true after " \
<< (timeout) << "ms; waiting an additional " << margin \
<< "ms"; \
WAIT_(ex, margin, res); \
if (!res) { \
EXPECT_TRUE(ex); \
} \
} while (0)
// Wait until "ex" is true, or "timeout" expires, using fake clock where
// messages are processed every millisecond.
// TODO(pthatcher): Allow tests to control how many milliseconds to advance.
#define SIMULATED_WAIT(ex, timeout, clock) \
for (int64_t start = rtc::TimeMillis(); \
!(ex) && rtc::TimeMillis() < start + (timeout);) { \
(clock).AdvanceTime(rtc::TimeDelta::FromMilliseconds(1)); \
}
// This returns the result of the test in res, so that we don't re-evaluate
// the expression in the XXXX_WAIT macros below, since that causes problems
// when the expression is only true the first time you check it.
#define SIMULATED_WAIT_(ex, timeout, res, clock) \
do { \
int64_t start = rtc::TimeMillis(); \
res = (ex); \
while (!res && rtc::TimeMillis() < start + (timeout)) { \
(clock).AdvanceTime(rtc::TimeDelta::FromMilliseconds(1)); \
res = (ex); \
} \
} while (0)
// The typical EXPECT_XXXX, but done until true or a timeout with a fake clock.
#define EXPECT_TRUE_SIMULATED_WAIT(ex, timeout, clock) \
do { \
bool res; \
SIMULATED_WAIT_(ex, timeout, res, clock); \
if (!res) { \
EXPECT_TRUE(ex); \
} \
} while (0)
#define EXPECT_EQ_SIMULATED_WAIT(v1, v2, timeout, clock) \
do { \
bool res; \
SIMULATED_WAIT_(v1 == v2, timeout, res, clock); \
if (!res) { \
EXPECT_EQ(v1, v2); \
} \
} while (0)
#define ASSERT_TRUE_SIMULATED_WAIT(ex, timeout, clock) \
do { \
bool res; \
SIMULATED_WAIT_(ex, timeout, res, clock); \
if (!res) \
ASSERT_TRUE(ex); \
} while (0)
#define ASSERT_EQ_SIMULATED_WAIT(v1, v2, timeout, clock) \
do { \
bool res; \
SIMULATED_WAIT_(v1 == v2, timeout, res, clock); \
if (!res) \
ASSERT_EQ(v1, v2); \
} while (0)
#endif // WEBRTC_RTC_BASE_GUNIT_H_

View File

@ -0,0 +1,24 @@
/*
* Copyright 2012 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_RTC_BASE_GUNIT_PROD_H_
#define WEBRTC_RTC_BASE_GUNIT_PROD_H_
#if defined(WEBRTC_ANDROID)
// Android doesn't use gtest at all, so anything that relies on gtest should
// check this define first.
#define NO_GTEST
#elif defined (GTEST_RELATIVE_PATH)
#include "gtest/gtest_prod.h"
#else
#include "testing/base/gunit_prod.h"
#endif
#endif // WEBRTC_RTC_BASE_GUNIT_PROD_H_

218
webrtc/rtc_base/helpers.cc Normal file
View File

@ -0,0 +1,218 @@
/*
* 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.
*/
#include "webrtc/base/helpers.h"
#include <limits>
#include <memory>
#include <openssl/rand.h>
#include "webrtc/base/base64.h"
#include "webrtc/base/basictypes.h"
#include "webrtc/base/checks.h"
#include "webrtc/base/logging.h"
#include "webrtc/base/timeutils.h"
// Protect against max macro inclusion.
#undef max
namespace rtc {
// Base class for RNG implementations.
class RandomGenerator {
public:
virtual ~RandomGenerator() {}
virtual bool Init(const void* seed, size_t len) = 0;
virtual bool Generate(void* buf, size_t len) = 0;
};
// The OpenSSL RNG.
class SecureRandomGenerator : public RandomGenerator {
public:
SecureRandomGenerator() {}
~SecureRandomGenerator() override {}
bool Init(const void* seed, size_t len) override { return true; }
bool Generate(void* buf, size_t len) override {
return (RAND_bytes(reinterpret_cast<unsigned char*>(buf), len) > 0);
}
};
// A test random generator, for predictable output.
class TestRandomGenerator : public RandomGenerator {
public:
TestRandomGenerator() : seed_(7) {
}
~TestRandomGenerator() override {
}
bool Init(const void* seed, size_t len) override { return true; }
bool Generate(void* buf, size_t len) override {
for (size_t i = 0; i < len; ++i) {
static_cast<uint8_t*>(buf)[i] = static_cast<uint8_t>(GetRandom());
}
return true;
}
private:
int GetRandom() {
return ((seed_ = seed_ * 214013L + 2531011L) >> 16) & 0x7fff;
}
int seed_;
};
namespace {
// TODO: Use Base64::Base64Table instead.
static const char kBase64[64] = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
static const char kHex[16] = {'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
static const char kUuidDigit17[4] = {'8', '9', 'a', 'b'};
// This round about way of creating a global RNG is to safe-guard against
// indeterminant static initialization order.
std::unique_ptr<RandomGenerator>& GetGlobalRng() {
RTC_DEFINE_STATIC_LOCAL(std::unique_ptr<RandomGenerator>, global_rng,
(new SecureRandomGenerator()));
return global_rng;
}
RandomGenerator& Rng() {
return *GetGlobalRng();
}
} // namespace
void SetRandomTestMode(bool test) {
if (!test) {
GetGlobalRng().reset(new SecureRandomGenerator());
} else {
GetGlobalRng().reset(new TestRandomGenerator());
}
}
bool InitRandom(int seed) {
return InitRandom(reinterpret_cast<const char*>(&seed), sizeof(seed));
}
bool InitRandom(const char* seed, size_t len) {
if (!Rng().Init(seed, len)) {
LOG(LS_ERROR) << "Failed to init random generator!";
return false;
}
return true;
}
std::string CreateRandomString(size_t len) {
std::string str;
RTC_CHECK(CreateRandomString(len, &str));
return str;
}
static bool CreateRandomString(size_t len,
const char* table, int table_size,
std::string* str) {
str->clear();
// Avoid biased modulo division below.
if (256 % table_size) {
LOG(LS_ERROR) << "Table size must divide 256 evenly!";
return false;
}
std::unique_ptr<uint8_t[]> bytes(new uint8_t[len]);
if (!Rng().Generate(bytes.get(), len)) {
LOG(LS_ERROR) << "Failed to generate random string!";
return false;
}
str->reserve(len);
for (size_t i = 0; i < len; ++i) {
str->push_back(table[bytes[i] % table_size]);
}
return true;
}
bool CreateRandomString(size_t len, std::string* str) {
return CreateRandomString(len, kBase64, 64, str);
}
bool CreateRandomString(size_t len, const std::string& table,
std::string* str) {
return CreateRandomString(len, table.c_str(),
static_cast<int>(table.size()), str);
}
bool CreateRandomData(size_t length, std::string* data) {
data->resize(length);
// std::string is guaranteed to use contiguous memory in c++11 so we can
// safely write directly to it.
return Rng().Generate(&data->at(0), length);
}
// Version 4 UUID is of the form:
// xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx
// Where 'x' is a hex digit, and 'y' is 8, 9, a or b.
std::string CreateRandomUuid() {
std::string str;
std::unique_ptr<uint8_t[]> bytes(new uint8_t[31]);
RTC_CHECK(Rng().Generate(bytes.get(), 31));
str.reserve(36);
for (size_t i = 0; i < 8; ++i) {
str.push_back(kHex[bytes[i] % 16]);
}
str.push_back('-');
for (size_t i = 8; i < 12; ++i) {
str.push_back(kHex[bytes[i] % 16]);
}
str.push_back('-');
str.push_back('4');
for (size_t i = 12; i < 15; ++i) {
str.push_back(kHex[bytes[i] % 16]);
}
str.push_back('-');
str.push_back(kUuidDigit17[bytes[15] % 4]);
for (size_t i = 16; i < 19; ++i) {
str.push_back(kHex[bytes[i] % 16]);
}
str.push_back('-');
for (size_t i = 19; i < 31; ++i) {
str.push_back(kHex[bytes[i] % 16]);
}
return str;
}
uint32_t CreateRandomId() {
uint32_t id;
RTC_CHECK(Rng().Generate(&id, sizeof(id)));
return id;
}
uint64_t CreateRandomId64() {
return static_cast<uint64_t>(CreateRandomId()) << 32 | CreateRandomId();
}
uint32_t CreateRandomNonZeroId() {
uint32_t id;
do {
id = CreateRandomId();
} while (id == 0);
return id;
}
double CreateRandomDouble() {
return CreateRandomId() / (std::numeric_limits<uint32_t>::max() +
std::numeric_limits<double>::epsilon());
}
} // namespace rtc

64
webrtc/rtc_base/helpers.h Normal file
View File

@ -0,0 +1,64 @@
/*
* 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_RTC_BASE_HELPERS_H_
#define WEBRTC_RTC_BASE_HELPERS_H_
#include <string>
#include "webrtc/base/basictypes.h"
namespace rtc {
// For testing, we can return predictable data.
void SetRandomTestMode(bool test);
// Initializes the RNG, and seeds it with the specified entropy.
bool InitRandom(int seed);
bool InitRandom(const char* seed, size_t len);
// Generates a (cryptographically) random string of the given length.
// We generate base64 values so that they will be printable.
std::string CreateRandomString(size_t length);
// Generates a (cryptographically) random string of the given length.
// We generate base64 values so that they will be printable.
// Return false if the random number generator failed.
bool CreateRandomString(size_t length, std::string* str);
// Generates a (cryptographically) random string of the given length,
// with characters from the given table. Return false if the random
// number generator failed.
// For ease of implementation, the function requires that the table
// size evenly divide 256; otherwise, it returns false.
bool CreateRandomString(size_t length, const std::string& table,
std::string* str);
// Generates (cryptographically) random data of the given length.
// Return false if the random number generator failed.
bool CreateRandomData(size_t length, std::string* data);
// Generates a (cryptographically) random UUID version 4 string.
std::string CreateRandomUuid();
// Generates a random id.
uint32_t CreateRandomId();
// Generates a 64 bit random id.
uint64_t CreateRandomId64();
// Generates a random id > 0.
uint32_t CreateRandomNonZeroId();
// Generates a random double between 0.0 (inclusive) and 1.0 (exclusive).
double CreateRandomDouble();
} // namespace rtc
#endif // WEBRTC_RTC_BASE_HELPERS_H_

Some files were not shown because too many files have changed in this diff Show More