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:
File diff suppressed because it is too large
Load Diff
15
webrtc/rtc_base/DEPS
Normal file
15
webrtc/rtc_base/DEPS
Normal 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
19
webrtc/rtc_base/OWNERS
Normal 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
|
||||
53
webrtc/rtc_base/applefilesystem.mm
Normal file
53
webrtc/rtc_base/applefilesystem.mm
Normal 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]);
|
||||
}
|
||||
253
webrtc/rtc_base/array_view.h
Normal file
253
webrtc/rtc_base/array_view.h
Normal 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_
|
||||
411
webrtc/rtc_base/array_view_unittest.cc
Normal file
411
webrtc/rtc_base/array_view_unittest.cc
Normal 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
|
||||
31
webrtc/rtc_base/arraysize.h
Normal file
31
webrtc/rtc_base/arraysize.h
Normal 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_
|
||||
56
webrtc/rtc_base/asyncinvoker-inl.h
Normal file
56
webrtc/rtc_base/asyncinvoker-inl.h
Normal 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_
|
||||
119
webrtc/rtc_base/asyncinvoker.cc
Normal file
119
webrtc/rtc_base/asyncinvoker.cc
Normal 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
|
||||
221
webrtc/rtc_base/asyncinvoker.h
Normal file
221
webrtc/rtc_base/asyncinvoker.h
Normal 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_
|
||||
29
webrtc/rtc_base/asyncpacketsocket.cc
Normal file
29
webrtc/rtc_base/asyncpacketsocket.cc
Normal 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
|
||||
143
webrtc/rtc_base/asyncpacketsocket.h
Normal file
143
webrtc/rtc_base/asyncpacketsocket.h
Normal 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_
|
||||
20
webrtc/rtc_base/asyncresolverinterface.cc
Normal file
20
webrtc/rtc_base/asyncresolverinterface.cc
Normal 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
|
||||
47
webrtc/rtc_base/asyncresolverinterface.h
Normal file
47
webrtc/rtc_base/asyncresolverinterface.h
Normal 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
|
||||
127
webrtc/rtc_base/asyncsocket.cc
Normal file
127
webrtc/rtc_base/asyncsocket.cc
Normal 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
|
||||
83
webrtc/rtc_base/asyncsocket.h
Normal file
83
webrtc/rtc_base/asyncsocket.h
Normal 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_
|
||||
331
webrtc/rtc_base/asynctcpsocket.cc
Normal file
331
webrtc/rtc_base/asynctcpsocket.cc
Normal 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
|
||||
108
webrtc/rtc_base/asynctcpsocket.h
Normal file
108
webrtc/rtc_base/asynctcpsocket.h
Normal 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_
|
||||
50
webrtc/rtc_base/asynctcpsocket_unittest.cc
Normal file
50
webrtc/rtc_base/asynctcpsocket_unittest.cc
Normal 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
|
||||
130
webrtc/rtc_base/asyncudpsocket.cc
Normal file
130
webrtc/rtc_base/asyncudpsocket.cc
Normal 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, ×tamp);
|
||||
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
|
||||
67
webrtc/rtc_base/asyncudpsocket.h
Normal file
67
webrtc/rtc_base/asyncudpsocket.h
Normal 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_
|
||||
53
webrtc/rtc_base/asyncudpsocket_unittest.cc
Normal file
53
webrtc/rtc_base/asyncudpsocket_unittest.cc
Normal 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
|
||||
87
webrtc/rtc_base/atomicops.h
Normal file
87
webrtc/rtc_base/atomicops.h
Normal 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_
|
||||
12
webrtc/rtc_base/atomicops_unittest.cc
Normal file
12
webrtc/rtc_base/atomicops_unittest.cc
Normal 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
278
webrtc/rtc_base/base64.cc
Normal 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
123
webrtc/rtc_base/base64.h
Normal 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_
|
||||
999
webrtc/rtc_base/base64_unittest.cc
Normal file
999
webrtc/rtc_base/base64_unittest.cc
Normal 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));
|
||||
}
|
||||
70
webrtc/rtc_base/basictypes.h
Normal file
70
webrtc/rtc_base/basictypes.h
Normal 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_
|
||||
48
webrtc/rtc_base/basictypes_unittest.cc
Normal file
48
webrtc/rtc_base/basictypes_unittest.cc
Normal 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
284
webrtc/rtc_base/bind.h
Normal 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_
|
||||
222
webrtc/rtc_base/bind_unittest.cc
Normal file
222
webrtc/rtc_base/bind_unittest.cc
Normal 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
|
||||
310
webrtc/rtc_base/bitbuffer.cc
Normal file
310
webrtc/rtc_base/bitbuffer.cc
Normal 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
126
webrtc/rtc_base/bitbuffer.h
Normal 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_
|
||||
329
webrtc/rtc_base/bitbuffer_unittest.cc
Normal file
329
webrtc/rtc_base/bitbuffer_unittest.cc
Normal 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
383
webrtc/rtc_base/buffer.h
Normal 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_
|
||||
437
webrtc/rtc_base/buffer_unittest.cc
Normal file
437
webrtc/rtc_base/buffer_unittest.cc
Normal 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
|
||||
94
webrtc/rtc_base/bufferqueue.cc
Normal file
94
webrtc/rtc_base/bufferqueue.cc
Normal 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
|
||||
61
webrtc/rtc_base/bufferqueue.h
Normal file
61
webrtc/rtc_base/bufferqueue.h
Normal 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_
|
||||
86
webrtc/rtc_base/bufferqueue_unittest.cc
Normal file
86
webrtc/rtc_base/bufferqueue_unittest.cc
Normal 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
|
||||
283
webrtc/rtc_base/bytebuffer.cc
Normal file
283
webrtc/rtc_base/bytebuffer.cc
Normal 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
|
||||
139
webrtc/rtc_base/bytebuffer.h
Normal file
139
webrtc/rtc_base/bytebuffer.h
Normal 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_
|
||||
258
webrtc/rtc_base/bytebuffer_unittest.cc
Normal file
258
webrtc/rtc_base/bytebuffer_unittest.cc
Normal 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
178
webrtc/rtc_base/byteorder.h
Normal 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_
|
||||
83
webrtc/rtc_base/byteorder_unittest.cc
Normal file
83
webrtc/rtc_base/byteorder_unittest.cc
Normal 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
260
webrtc/rtc_base/callback.h
Normal 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_
|
||||
103
webrtc/rtc_base/callback.h.pump
Normal file
103
webrtc/rtc_base/callback.h.pump
Normal 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_
|
||||
140
webrtc/rtc_base/callback_unittest.cc
Normal file
140
webrtc/rtc_base/callback_unittest.cc
Normal 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
141
webrtc/rtc_base/checks.cc
Normal 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
289
webrtc/rtc_base/checks.h
Normal 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_
|
||||
21
webrtc/rtc_base/compile_assert_c.h
Normal file
21
webrtc/rtc_base/compile_assert_c.h
Normal 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_
|
||||
34
webrtc/rtc_base/constructormagic.h
Normal file
34
webrtc/rtc_base/constructormagic.h
Normal 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_
|
||||
112
webrtc/rtc_base/copyonwritebuffer.cc
Normal file
112
webrtc/rtc_base/copyonwritebuffer.cc
Normal 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
|
||||
241
webrtc/rtc_base/copyonwritebuffer.h
Normal file
241
webrtc/rtc_base/copyonwritebuffer.h
Normal 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_
|
||||
319
webrtc/rtc_base/copyonwritebuffer_unittest.cc
Normal file
319
webrtc/rtc_base/copyonwritebuffer_unittest.cc
Normal 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
114
webrtc/rtc_base/cpu_time.cc
Normal 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
|
||||
28
webrtc/rtc_base/cpu_time.h
Normal file
28
webrtc/rtc_base/cpu_time.h
Normal 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_
|
||||
106
webrtc/rtc_base/cpu_time_unittest.cc
Normal file
106
webrtc/rtc_base/cpu_time_unittest.cc
Normal 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
52
webrtc/rtc_base/crc32.cc
Normal 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
34
webrtc/rtc_base/crc32.h
Normal 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_
|
||||
35
webrtc/rtc_base/crc32_unittest.cc
Normal file
35
webrtc/rtc_base/crc32_unittest.cc
Normal 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
|
||||
252
webrtc/rtc_base/criticalsection.cc
Normal file
252
webrtc/rtc_base/criticalsection.cc
Normal 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
|
||||
156
webrtc/rtc_base/criticalsection.h
Normal file
156
webrtc/rtc_base/criticalsection.h
Normal 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_
|
||||
413
webrtc/rtc_base/criticalsection_unittest.cc
Normal file
413
webrtc/rtc_base/criticalsection_unittest.cc
Normal 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
|
||||
75
webrtc/rtc_base/cryptstring.cc
Normal file
75
webrtc/rtc_base/cryptstring.cc
Normal 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
|
||||
167
webrtc/rtc_base/cryptstring.h
Normal file
167
webrtc/rtc_base/cryptstring.h
Normal 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_
|
||||
45
webrtc/rtc_base/deprecation.h
Normal file
45
webrtc/rtc_base/deprecation.h
Normal 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
45
webrtc/rtc_base/dscp.h
Normal 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
136
webrtc/rtc_base/event.cc
Normal 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
54
webrtc/rtc_base/event.h
Normal 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_
|
||||
407
webrtc/rtc_base/event_tracer.cc
Normal file
407
webrtc/rtc_base/event_tracer.cc
Normal 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
|
||||
85
webrtc/rtc_base/event_tracer.h
Normal file
85
webrtc/rtc_base/event_tracer.h
Normal 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_
|
||||
82
webrtc/rtc_base/event_tracer_unittest.cc
Normal file
82
webrtc/rtc_base/event_tracer_unittest.cc
Normal 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
|
||||
42
webrtc/rtc_base/event_unittest.cc
Normal file
42
webrtc/rtc_base/event_unittest.cc
Normal 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
|
||||
50
webrtc/rtc_base/fakeclock.cc
Normal file
50
webrtc/rtc_base/fakeclock.cc
Normal 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
|
||||
71
webrtc/rtc_base/fakeclock.h
Normal file
71
webrtc/rtc_base/fakeclock.h
Normal 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_
|
||||
129
webrtc/rtc_base/fakenetwork.h
Normal file
129
webrtc/rtc_base/fakenetwork.h
Normal 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_
|
||||
120
webrtc/rtc_base/fakesslidentity.h
Normal file
120
webrtc/rtc_base/fakesslidentity.h
Normal 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
94
webrtc/rtc_base/file.cc
Normal 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
82
webrtc/rtc_base/file.h
Normal 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_
|
||||
96
webrtc/rtc_base/file_posix.cc
Normal file
96
webrtc/rtc_base/file_posix.cc
Normal 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
|
||||
201
webrtc/rtc_base/file_unittest.cc
Normal file
201
webrtc/rtc_base/file_unittest.cc
Normal 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
113
webrtc/rtc_base/file_win.cc
Normal 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
|
||||
400
webrtc/rtc_base/filerotatingstream.cc
Normal file
400
webrtc/rtc_base/filerotatingstream.cc
Normal 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
|
||||
173
webrtc/rtc_base/filerotatingstream.h
Normal file
173
webrtc/rtc_base/filerotatingstream.h
Normal 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_
|
||||
345
webrtc/rtc_base/filerotatingstream_unittest.cc
Normal file
345
webrtc/rtc_base/filerotatingstream_unittest.cc
Normal 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
|
||||
133
webrtc/rtc_base/fileutils.cc
Normal file
133
webrtc/rtc_base/fileutils.cc
Normal 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
182
webrtc/rtc_base/fileutils.h
Normal 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_
|
||||
33
webrtc/rtc_base/fileutils_unittest.cc
Normal file
33
webrtc/rtc_base/fileutils_unittest.cc
Normal 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
|
||||
271
webrtc/rtc_base/firewallsocketserver.cc
Normal file
271
webrtc/rtc_base/firewallsocketserver.cc
Normal 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
|
||||
125
webrtc/rtc_base/firewallsocketserver.h
Normal file
125
webrtc/rtc_base/firewallsocketserver.h
Normal 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
299
webrtc/rtc_base/flags.cc
Normal 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
268
webrtc/rtc_base/flags.h
Normal 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_
|
||||
96
webrtc/rtc_base/format_macros.h
Normal file
96
webrtc/rtc_base/format_macros.h
Normal 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_
|
||||
130
webrtc/rtc_base/function_view.h
Normal file
130
webrtc/rtc_base/function_view.h
Normal 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_
|
||||
175
webrtc/rtc_base/function_view_unittest.cc
Normal file
175
webrtc/rtc_base/function_view_unittest.cc
Normal 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
|
||||
38
webrtc/rtc_base/gtest_prod_util.h
Normal file
38
webrtc/rtc_base/gtest_prod_util.h
Normal 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
150
webrtc/rtc_base/gunit.h
Normal 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_
|
||||
24
webrtc/rtc_base/gunit_prod.h
Normal file
24
webrtc/rtc_base/gunit_prod.h
Normal 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
218
webrtc/rtc_base/helpers.cc
Normal 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
64
webrtc/rtc_base/helpers.h
Normal 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
Reference in New Issue
Block a user