diff options
Diffstat (limited to 'third_party/libwebrtc/rtc_base/memory')
10 files changed, 1463 insertions, 0 deletions
diff --git a/third_party/libwebrtc/rtc_base/memory/BUILD.gn b/third_party/libwebrtc/rtc_base/memory/BUILD.gn new file mode 100644 index 0000000000..ee3baa40fb --- /dev/null +++ b/third_party/libwebrtc/rtc_base/memory/BUILD.gn @@ -0,0 +1,61 @@ +# Copyright (c) 2018 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. + +import("../../webrtc.gni") +if (is_android) { + import("//build/config/android/config.gni") + import("//build/config/android/rules.gni") +} + +rtc_library("aligned_malloc") { + sources = [ + "aligned_malloc.cc", + "aligned_malloc.h", + ] + deps = [ "..:checks" ] +} + +# Test only utility. +rtc_library("fifo_buffer") { + testonly = true + visibility = [ + ":unittests", + "..:rtc_base_tests_utils", + "..:rtc_base_unittests", + ] + sources = [ + "fifo_buffer.cc", + "fifo_buffer.h", + ] + deps = [ + "..:stream", + "..:threading", + "../../api/task_queue:pending_task_safety_flag", + "../synchronization:mutex", + ] +} + +rtc_library("unittests") { + testonly = true + sources = [ + "aligned_malloc_unittest.cc", + "always_valid_pointer_unittest.cc", + "fifo_buffer_unittest.cc", + ] + deps = [ + ":aligned_malloc", + ":always_valid_pointer", + ":fifo_buffer", + "../../test:test_support", + ] +} + +rtc_source_set("always_valid_pointer") { + sources = [ "always_valid_pointer.h" ] + deps = [ "..:checks" ] +} diff --git a/third_party/libwebrtc/rtc_base/memory/aligned_malloc.cc b/third_party/libwebrtc/rtc_base/memory/aligned_malloc.cc new file mode 100644 index 0000000000..7add079398 --- /dev/null +++ b/third_party/libwebrtc/rtc_base/memory/aligned_malloc.cc @@ -0,0 +1,98 @@ +/* + * Copyright (c) 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 "rtc_base/memory/aligned_malloc.h" + +#include <stdlib.h> // for free, malloc +#include <string.h> // for memcpy + +#include "rtc_base/checks.h" + +#ifdef _WIN32 +#include <windows.h> +#else +#include <stdint.h> +#endif + +// Reference on memory alignment: +// http://stackoverflow.com/questions/227897/solve-the-memory-alignment-in-c-interview-question-that-stumped-me +namespace webrtc { + +uintptr_t GetRightAlign(uintptr_t start_pos, size_t alignment) { + // The pointer should be aligned with `alignment` bytes. The - 1 guarantees + // that it is aligned towards the closest higher (right) address. + return (start_pos + alignment - 1) & ~(alignment - 1); +} + +// Alignment must be an integer power of two. +bool ValidAlignment(size_t alignment) { + if (!alignment) { + return false; + } + return (alignment & (alignment - 1)) == 0; +} + +void* GetRightAlign(const void* pointer, size_t alignment) { + if (!pointer) { + return NULL; + } + if (!ValidAlignment(alignment)) { + return NULL; + } + uintptr_t start_pos = reinterpret_cast<uintptr_t>(pointer); + return reinterpret_cast<void*>(GetRightAlign(start_pos, alignment)); +} + +void* AlignedMalloc(size_t size, size_t alignment) { + if (size == 0) { + return NULL; + } + if (!ValidAlignment(alignment)) { + return NULL; + } + + // The memory is aligned towards the lowest address that so only + // alignment - 1 bytes needs to be allocated. + // A pointer to the start of the memory must be stored so that it can be + // retreived for deletion, ergo the sizeof(uintptr_t). + void* memory_pointer = malloc(size + sizeof(uintptr_t) + alignment - 1); + RTC_CHECK(memory_pointer) << "Couldn't allocate memory in AlignedMalloc"; + + // Aligning after the sizeof(uintptr_t) bytes will leave room for the header + // in the same memory block. + uintptr_t align_start_pos = reinterpret_cast<uintptr_t>(memory_pointer); + align_start_pos += sizeof(uintptr_t); + uintptr_t aligned_pos = GetRightAlign(align_start_pos, alignment); + void* aligned_pointer = reinterpret_cast<void*>(aligned_pos); + + // Store the address to the beginning of the memory just before the aligned + // memory. + uintptr_t header_pos = aligned_pos - sizeof(uintptr_t); + void* header_pointer = reinterpret_cast<void*>(header_pos); + uintptr_t memory_start = reinterpret_cast<uintptr_t>(memory_pointer); + memcpy(header_pointer, &memory_start, sizeof(uintptr_t)); + + return aligned_pointer; +} + +void AlignedFree(void* mem_block) { + if (mem_block == NULL) { + return; + } + uintptr_t aligned_pos = reinterpret_cast<uintptr_t>(mem_block); + uintptr_t header_pos = aligned_pos - sizeof(uintptr_t); + + // Read out the address of the AlignedMemory struct from the header. + uintptr_t memory_start_pos = *reinterpret_cast<uintptr_t*>(header_pos); + void* memory_start = reinterpret_cast<void*>(memory_start_pos); + free(memory_start); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/rtc_base/memory/aligned_malloc.h b/third_party/libwebrtc/rtc_base/memory/aligned_malloc.h new file mode 100644 index 0000000000..1c7d303885 --- /dev/null +++ b/third_party/libwebrtc/rtc_base/memory/aligned_malloc.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 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 RTC_BASE_MEMORY_ALIGNED_MALLOC_H_ +#define RTC_BASE_MEMORY_ALIGNED_MALLOC_H_ + +// The functions declared here +// 1) Allocates block of aligned memory. +// 2) Re-calculates a pointer such that it is aligned to a higher or equal +// address. +// Note: alignment must be a power of two. The alignment is in bytes. + +#include <stddef.h> + +namespace webrtc { + +// Returns a pointer to the first boundry of `alignment` bytes following the +// address of `ptr`. +// Note that there is no guarantee that the memory in question is available. +// `ptr` has no requirements other than it can't be NULL. +void* GetRightAlign(const void* ptr, size_t alignment); + +// Allocates memory of `size` bytes aligned on an `alignment` boundry. +// The return value is a pointer to the memory. Note that the memory must +// be de-allocated using AlignedFree. +void* AlignedMalloc(size_t size, size_t alignment); +// De-allocates memory created using the AlignedMalloc() API. +void AlignedFree(void* mem_block); + +// Templated versions to facilitate usage of aligned malloc without casting +// to and from void*. +template <typename T> +T* GetRightAlign(const T* ptr, size_t alignment) { + return reinterpret_cast<T*>( + GetRightAlign(reinterpret_cast<const void*>(ptr), alignment)); +} +template <typename T> +T* AlignedMalloc(size_t size, size_t alignment) { + return reinterpret_cast<T*>(AlignedMalloc(size, alignment)); +} + +// Deleter for use with unique_ptr. E.g., use as +// std::unique_ptr<Foo, AlignedFreeDeleter> foo; +struct AlignedFreeDeleter { + inline void operator()(void* ptr) const { AlignedFree(ptr); } +}; + +} // namespace webrtc + +#endif // RTC_BASE_MEMORY_ALIGNED_MALLOC_H_ diff --git a/third_party/libwebrtc/rtc_base/memory/aligned_malloc_gn/moz.build b/third_party/libwebrtc/rtc_base/memory/aligned_malloc_gn/moz.build new file mode 100644 index 0000000000..f5720480dd --- /dev/null +++ b/third_party/libwebrtc/rtc_base/memory/aligned_malloc_gn/moz.build @@ -0,0 +1,225 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + + ### This moz.build was AUTOMATICALLY GENERATED from a GN config, ### + ### DO NOT edit it by hand. ### + +COMPILE_FLAGS["OS_INCLUDES"] = [] +AllowCompilerWarnings() + +DEFINES["ABSL_ALLOCATOR_NOTHROW"] = "1" +DEFINES["RTC_DAV1D_IN_INTERNAL_DECODER_FACTORY"] = True +DEFINES["RTC_ENABLE_VP9"] = True +DEFINES["WEBRTC_ENABLE_PROTOBUF"] = "0" +DEFINES["WEBRTC_LIBRARY_IMPL"] = True +DEFINES["WEBRTC_MOZILLA_BUILD"] = True +DEFINES["WEBRTC_NON_STATIC_TRACE_EVENT_HANDLERS"] = "0" +DEFINES["WEBRTC_STRICT_FIELD_TRIALS"] = "0" + +FINAL_LIBRARY = "webrtc" + + +LOCAL_INCLUDES += [ + "!/ipc/ipdl/_ipdlheaders", + "!/third_party/libwebrtc/gen", + "/ipc/chromium/src", + "/third_party/libwebrtc/", + "/third_party/libwebrtc/third_party/abseil-cpp/", + "/tools/profiler/public" +] + +UNIFIED_SOURCES += [ + "/third_party/libwebrtc/rtc_base/memory/aligned_malloc.cc" +] + +if not CONFIG["MOZ_DEBUG"]: + + DEFINES["DYNAMIC_ANNOTATIONS_ENABLED"] = "0" + DEFINES["NDEBUG"] = True + DEFINES["NVALGRIND"] = True + +if CONFIG["MOZ_DEBUG"] == "1": + + DEFINES["DYNAMIC_ANNOTATIONS_ENABLED"] = "1" + +if CONFIG["OS_TARGET"] == "Android": + + DEFINES["ANDROID"] = True + DEFINES["ANDROID_NDK_VERSION_ROLL"] = "r22_1" + DEFINES["HAVE_SYS_UIO_H"] = True + DEFINES["WEBRTC_ANDROID"] = True + DEFINES["WEBRTC_ANDROID_OPENSLES"] = True + DEFINES["WEBRTC_ENABLE_LIBEVENT"] = True + DEFINES["WEBRTC_LINUX"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["_GNU_SOURCE"] = True + DEFINES["__STDC_CONSTANT_MACROS"] = True + DEFINES["__STDC_FORMAT_MACROS"] = True + + OS_LIBS += [ + "log" + ] + +if CONFIG["OS_TARGET"] == "Darwin": + + DEFINES["WEBRTC_MAC"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["_LIBCPP_HAS_NO_ALIGNED_ALLOCATION"] = True + DEFINES["__ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES"] = "0" + DEFINES["__STDC_CONSTANT_MACROS"] = True + DEFINES["__STDC_FORMAT_MACROS"] = True + +if CONFIG["OS_TARGET"] == "Linux": + + DEFINES["USE_AURA"] = "1" + DEFINES["USE_GLIB"] = "1" + DEFINES["USE_NSS_CERTS"] = "1" + DEFINES["USE_OZONE"] = "1" + DEFINES["USE_UDEV"] = True + DEFINES["WEBRTC_ENABLE_LIBEVENT"] = True + DEFINES["WEBRTC_LINUX"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["_FILE_OFFSET_BITS"] = "64" + DEFINES["_LARGEFILE64_SOURCE"] = True + DEFINES["_LARGEFILE_SOURCE"] = True + DEFINES["__STDC_CONSTANT_MACROS"] = True + DEFINES["__STDC_FORMAT_MACROS"] = True + +if CONFIG["OS_TARGET"] == "OpenBSD": + + DEFINES["USE_GLIB"] = "1" + DEFINES["USE_OZONE"] = "1" + DEFINES["USE_X11"] = "1" + DEFINES["WEBRTC_BSD"] = True + DEFINES["WEBRTC_ENABLE_LIBEVENT"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["_FILE_OFFSET_BITS"] = "64" + DEFINES["_LARGEFILE64_SOURCE"] = True + DEFINES["_LARGEFILE_SOURCE"] = True + DEFINES["__STDC_CONSTANT_MACROS"] = True + DEFINES["__STDC_FORMAT_MACROS"] = True + +if CONFIG["OS_TARGET"] == "WINNT": + + DEFINES["CERT_CHAIN_PARA_HAS_EXTRA_FIELDS"] = True + DEFINES["NOMINMAX"] = True + DEFINES["NTDDI_VERSION"] = "0x0A000000" + DEFINES["PSAPI_VERSION"] = "2" + DEFINES["RTC_ENABLE_WIN_WGC"] = True + DEFINES["UNICODE"] = True + DEFINES["USE_AURA"] = "1" + DEFINES["WEBRTC_WIN"] = True + DEFINES["WIN32"] = True + DEFINES["WIN32_LEAN_AND_MEAN"] = True + DEFINES["WINAPI_FAMILY"] = "WINAPI_FAMILY_DESKTOP_APP" + DEFINES["WINVER"] = "0x0A00" + DEFINES["_ATL_NO_OPENGL"] = True + DEFINES["_CRT_RAND_S"] = True + DEFINES["_CRT_SECURE_NO_DEPRECATE"] = True + DEFINES["_ENABLE_EXTENDED_ALIGNED_STORAGE"] = True + DEFINES["_HAS_EXCEPTIONS"] = "0" + DEFINES["_HAS_NODISCARD"] = True + DEFINES["_SCL_SECURE_NO_DEPRECATE"] = True + DEFINES["_SECURE_ATL"] = True + DEFINES["_UNICODE"] = True + DEFINES["_WIN32_WINNT"] = "0x0A00" + DEFINES["_WINDOWS"] = True + DEFINES["__STD_C"] = True + +if CONFIG["TARGET_CPU"] == "aarch64": + + DEFINES["WEBRTC_ARCH_ARM64"] = True + DEFINES["WEBRTC_HAS_NEON"] = True + +if CONFIG["TARGET_CPU"] == "arm": + + CXXFLAGS += [ + "-mfpu=neon" + ] + + DEFINES["WEBRTC_ARCH_ARM"] = True + DEFINES["WEBRTC_ARCH_ARM_V7"] = True + DEFINES["WEBRTC_HAS_NEON"] = True + +if CONFIG["TARGET_CPU"] == "mips32": + + DEFINES["MIPS32_LE"] = True + DEFINES["MIPS_FPU_LE"] = True + DEFINES["_GNU_SOURCE"] = True + +if CONFIG["TARGET_CPU"] == "mips64": + + DEFINES["_GNU_SOURCE"] = True + +if CONFIG["TARGET_CPU"] == "x86": + + DEFINES["WEBRTC_ENABLE_AVX2"] = True + +if CONFIG["TARGET_CPU"] == "x86_64": + + DEFINES["WEBRTC_ENABLE_AVX2"] = True + +if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "Android": + + DEFINES["_DEBUG"] = True + +if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "Darwin": + + DEFINES["_DEBUG"] = True + +if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "Linux": + + DEFINES["_DEBUG"] = True + +if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "OpenBSD": + + DEFINES["_DEBUG"] = True + +if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "WINNT": + + DEFINES["_HAS_ITERATOR_DEBUGGING"] = "0" + +if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux": + + DEFINES["USE_X11"] = "1" + +if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm": + + OS_LIBS += [ + "android_support", + "unwind" + ] + +if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86": + + CXXFLAGS += [ + "-msse2" + ] + + OS_LIBS += [ + "android_support" + ] + +if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64": + + DEFINES["_GNU_SOURCE"] = True + +if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "arm": + + DEFINES["_GNU_SOURCE"] = True + +if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "x86": + + CXXFLAGS += [ + "-msse2" + ] + + DEFINES["_GNU_SOURCE"] = True + +if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "x86_64": + + DEFINES["_GNU_SOURCE"] = True + +Library("aligned_malloc_gn") diff --git a/third_party/libwebrtc/rtc_base/memory/aligned_malloc_unittest.cc b/third_party/libwebrtc/rtc_base/memory/aligned_malloc_unittest.cc new file mode 100644 index 0000000000..8e750035f0 --- /dev/null +++ b/third_party/libwebrtc/rtc_base/memory/aligned_malloc_unittest.cc @@ -0,0 +1,81 @@ +/* + * 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 "rtc_base/memory/aligned_malloc.h" + +#include <memory> + +#ifdef _WIN32 +#include <windows.h> +#else +#include <stdint.h> +#endif + +#include "test/gtest.h" + +namespace webrtc { + +// Returns true if `size` and `alignment` are valid combinations. +bool CorrectUsage(size_t size, size_t alignment) { + std::unique_ptr<char, AlignedFreeDeleter> scoped( + static_cast<char*>(AlignedMalloc(size, alignment))); + if (scoped.get() == NULL) { + return false; + } + const uintptr_t scoped_address = reinterpret_cast<uintptr_t>(scoped.get()); + return 0u == scoped_address % alignment; +} + +TEST(AlignedMalloc, GetRightAlign) { + const size_t size = 100; + const size_t alignment = 32; + const size_t left_misalignment = 1; + std::unique_ptr<char, AlignedFreeDeleter> scoped( + static_cast<char*>(AlignedMalloc(size, alignment))); + EXPECT_TRUE(scoped.get() != NULL); + const uintptr_t aligned_address = reinterpret_cast<uintptr_t>(scoped.get()); + const uintptr_t misaligned_address = aligned_address - left_misalignment; + const char* misaligned_ptr = + reinterpret_cast<const char*>(misaligned_address); + const char* realigned_ptr = GetRightAlign(misaligned_ptr, alignment); + EXPECT_EQ(scoped.get(), realigned_ptr); +} + +TEST(AlignedMalloc, IncorrectSize) { + const size_t incorrect_size = 0; + const size_t alignment = 64; + EXPECT_FALSE(CorrectUsage(incorrect_size, alignment)); +} + +TEST(AlignedMalloc, IncorrectAlignment) { + const size_t size = 100; + const size_t incorrect_alignment = 63; + EXPECT_FALSE(CorrectUsage(size, incorrect_alignment)); +} + +TEST(AlignedMalloc, AlignTo2Bytes) { + size_t size = 100; + size_t alignment = 2; + EXPECT_TRUE(CorrectUsage(size, alignment)); +} + +TEST(AlignedMalloc, AlignTo32Bytes) { + size_t size = 100; + size_t alignment = 32; + EXPECT_TRUE(CorrectUsage(size, alignment)); +} + +TEST(AlignedMalloc, AlignTo128Bytes) { + size_t size = 100; + size_t alignment = 128; + EXPECT_TRUE(CorrectUsage(size, alignment)); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/rtc_base/memory/always_valid_pointer.h b/third_party/libwebrtc/rtc_base/memory/always_valid_pointer.h new file mode 100644 index 0000000000..4e68104b55 --- /dev/null +++ b/third_party/libwebrtc/rtc_base/memory/always_valid_pointer.h @@ -0,0 +1,248 @@ +/* + * Copyright 2022 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 RTC_BASE_MEMORY_ALWAYS_VALID_POINTER_H_ +#define RTC_BASE_MEMORY_ALWAYS_VALID_POINTER_H_ + +#include <memory> +#include <utility> + +#include "rtc_base/checks.h" + +namespace webrtc { + +// This template allows the instantiation of a pointer to Interface in such a +// way that if it is passed a null pointer, an object of class Default will be +// created, which will be deallocated when the pointer is deleted. +template <typename Interface, typename Default = Interface> +class AlwaysValidPointer { + public: + explicit AlwaysValidPointer(Interface* pointer) + : owned_instance_(pointer ? nullptr : std::make_unique<Default>()), + pointer_(pointer ? pointer : owned_instance_.get()) { + RTC_DCHECK(pointer_); + } + + template <typename Arg, + typename std::enable_if<!(std::is_invocable<Arg>::value), + bool>::type = true> + AlwaysValidPointer(Interface* pointer, Arg arg) + : owned_instance_(pointer ? nullptr + : std::make_unique<Default>(std::move(arg))), + pointer_(pointer ? pointer : owned_instance_.get()) { + RTC_DCHECK(pointer_); + } + + // Multiple arguments + template <typename Arg1, typename... Args> + AlwaysValidPointer(Interface* pointer, Arg1 arg1, Args... args) + : owned_instance_(pointer + ? nullptr + : std::make_unique<Default>(std::move(arg1), + std::move(args...))), + pointer_(pointer ? pointer : owned_instance_.get()) { + RTC_DCHECK(pointer_); + } + + // Create a pointer by + // a) using |pointer|, without taking ownership + // b) calling |function| and taking ownership of the result + template <typename Func, + typename std::enable_if<std::is_invocable<Func>::value, + bool>::type = true> + AlwaysValidPointer(Interface* pointer, Func function) + : owned_instance_(pointer ? nullptr : function()), + pointer_(owned_instance_ ? owned_instance_.get() : pointer) { + RTC_DCHECK(pointer_); + } + + // Create a pointer by + // a) taking over ownership of |instance| + // b) or fallback to |pointer|, without taking ownership. + // c) or Default. + AlwaysValidPointer(std::unique_ptr<Interface>&& instance, Interface* pointer) + : owned_instance_( + instance + ? std::move(instance) + : (pointer == nullptr ? std::make_unique<Default>() : nullptr)), + pointer_(owned_instance_ ? owned_instance_.get() : pointer) { + RTC_DCHECK(pointer_); + } + + // Create a pointer by + // a) taking over ownership of |instance| + // b) or fallback to |pointer|, without taking ownership. + // c) or Default (with forwarded args). + template <typename... Args> + AlwaysValidPointer(std::unique_ptr<Interface>&& instance, + Interface* pointer, + Args... args) + : owned_instance_( + instance ? std::move(instance) + : (pointer == nullptr + ? std::make_unique<Default>(std::move(args...)) + : nullptr)), + pointer_(owned_instance_ ? owned_instance_.get() : pointer) { + RTC_DCHECK(pointer_); + } + + Interface* get() { return pointer_; } + Interface* operator->() { return pointer_; } + Interface& operator*() { return *pointer_; } + + Interface* get() const { return pointer_; } + Interface* operator->() const { return pointer_; } + Interface& operator*() const { return *pointer_; } + + private: + const std::unique_ptr<Interface> owned_instance_; + Interface* const pointer_; +}; + +// This class is similar to AlwaysValidPointer, but it does not create +// a default object and crashes if none of the input pointers are non-null. +template <typename Interface> +class AlwaysValidPointerNoDefault { + public: + explicit AlwaysValidPointerNoDefault(Interface* pointer) : pointer_(pointer) { + RTC_CHECK(pointer_); + } + + // Create a pointer by + // a) taking over ownership of |instance| + // b) or fallback to |pointer|, without taking ownership. + // At least one of the arguments must be non-null. + explicit AlwaysValidPointerNoDefault(std::unique_ptr<Interface> instance, + Interface* pointer = nullptr) + : owned_instance_(std::move(instance)), + pointer_(owned_instance_ ? owned_instance_.get() : pointer) { + RTC_CHECK(pointer_); + } + + Interface* get() { return pointer_; } + Interface* operator->() { return pointer_; } + Interface& operator*() { return *pointer_; } + + Interface* get() const { return pointer_; } + Interface* operator->() const { return pointer_; } + Interface& operator*() const { return *pointer_; } + + private: + const std::unique_ptr<Interface> owned_instance_; + Interface* const pointer_; +}; + +template <typename T, typename U, typename V, typename W> +bool operator==(const AlwaysValidPointer<T, U>& a, + const AlwaysValidPointer<V, W>& b) { + return a.get() == b.get(); +} + +template <typename T, typename U, typename V, typename W> +bool operator!=(const AlwaysValidPointer<T, U>& a, + const AlwaysValidPointer<V, W>& b) { + return !(a == b); +} + +template <typename T, typename U> +bool operator==(const AlwaysValidPointer<T, U>& a, std::nullptr_t) { + return a.get() == nullptr; +} + +template <typename T, typename U> +bool operator!=(const AlwaysValidPointer<T, U>& a, std::nullptr_t) { + return !(a == nullptr); +} + +template <typename T, typename U> +bool operator==(std::nullptr_t, const AlwaysValidPointer<T, U>& a) { + return a.get() == nullptr; +} + +template <typename T, typename U> +bool operator!=(std::nullptr_t, const AlwaysValidPointer<T, U>& a) { + return !(a == nullptr); +} + +template <typename T, typename U> +bool operator==(const AlwaysValidPointerNoDefault<T>& a, + const AlwaysValidPointerNoDefault<U>& b) { + return a.get() == b.get(); +} + +template <typename T, typename U> +bool operator!=(const AlwaysValidPointerNoDefault<T>& a, + const AlwaysValidPointerNoDefault<U>& b) { + return !(a == b); +} + +template <typename T> +bool operator==(const AlwaysValidPointerNoDefault<T>& a, std::nullptr_t) { + return a.get() == nullptr; +} + +template <typename T> +bool operator!=(const AlwaysValidPointerNoDefault<T>& a, std::nullptr_t) { + return !(a == nullptr); +} + +template <typename T> +bool operator==(std::nullptr_t, const AlwaysValidPointerNoDefault<T>& a) { + return a.get() == nullptr; +} + +template <typename T> +bool operator!=(std::nullptr_t, const AlwaysValidPointerNoDefault<T>& a) { + return !(a == nullptr); +} + +// Comparison with raw pointer. +template <typename T, typename U, typename V> +bool operator==(const AlwaysValidPointer<T, U>& a, const V* b) { + return a.get() == b; +} + +template <typename T, typename U, typename V> +bool operator!=(const AlwaysValidPointer<T, U>& a, const V* b) { + return !(a == b); +} + +template <typename T, typename U, typename V> +bool operator==(const T* a, const AlwaysValidPointer<U, V>& b) { + return a == b.get(); +} + +template <typename T, typename U, typename V> +bool operator!=(const T* a, const AlwaysValidPointer<U, V>& b) { + return !(a == b); +} + +template <typename T, typename U> +bool operator==(const AlwaysValidPointerNoDefault<T>& a, const U* b) { + return a.get() == b; +} + +template <typename T, typename U> +bool operator!=(const AlwaysValidPointerNoDefault<T>& a, const U* b) { + return !(a == b); +} + +template <typename T, typename U> +bool operator==(const T* a, const AlwaysValidPointerNoDefault<U>& b) { + return a == b.get(); +} + +template <typename T, typename U> +bool operator!=(const T* a, const AlwaysValidPointerNoDefault<U>& b) { + return !(a == b); +} + +} // namespace webrtc + +#endif // RTC_BASE_MEMORY_ALWAYS_VALID_POINTER_H_ diff --git a/third_party/libwebrtc/rtc_base/memory/always_valid_pointer_unittest.cc b/third_party/libwebrtc/rtc_base/memory/always_valid_pointer_unittest.cc new file mode 100644 index 0000000000..a228c102d0 --- /dev/null +++ b/third_party/libwebrtc/rtc_base/memory/always_valid_pointer_unittest.cc @@ -0,0 +1,129 @@ +/* + * 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 "rtc_base/memory/always_valid_pointer.h" + +#include <string> + +#include "test/gtest.h" + +namespace webrtc { + +TEST(AlwaysValidPointerTest, DefaultToEmptyValue) { + AlwaysValidPointer<std::string> ptr(nullptr); + EXPECT_EQ(*ptr, ""); +} +TEST(AlwaysValidPointerTest, DefaultWithForwardedArgument) { + AlwaysValidPointer<std::string> ptr(nullptr, "test"); + EXPECT_EQ(*ptr, "test"); +} +TEST(AlwaysValidPointerTest, DefaultToSubclass) { + struct A { + virtual ~A() {} + virtual int f() = 0; + }; + struct B : public A { + int b = 0; + explicit B(int val) : b(val) {} + virtual ~B() {} + int f() override { return b; } + }; + AlwaysValidPointer<A, B> ptr(nullptr, 3); + EXPECT_EQ(ptr->f(), 3); + EXPECT_EQ((*ptr).f(), 3); + EXPECT_EQ(ptr.get()->f(), 3); +} +TEST(AlwaysValidPointerTest, NonDefaultValue) { + std::string str("keso"); + AlwaysValidPointer<std::string> ptr(&str, "test"); + EXPECT_EQ(*ptr, "keso"); +} + +TEST(AlwaysValidPointerTest, TakeOverOwnershipOfInstance) { + std::string str("keso"); + std::unique_ptr<std::string> str2 = std::make_unique<std::string>("kent"); + AlwaysValidPointer<std::string> ptr(std::move(str2), &str); + EXPECT_EQ(*ptr, "kent"); + EXPECT_EQ(str2, nullptr); +} + +TEST(AlwaysValidPointerTest, TakeOverOwnershipFallbackOnPointer) { + std::string str("keso"); + std::unique_ptr<std::string> str2; + AlwaysValidPointer<std::string> ptr(std::move(str2), &str); + EXPECT_EQ(*ptr, "keso"); +} + +TEST(AlwaysValidPointerTest, TakeOverOwnershipFallbackOnDefault) { + std::unique_ptr<std::string> str; + std::string* str_ptr = nullptr; + AlwaysValidPointer<std::string> ptr(std::move(str), str_ptr); + EXPECT_EQ(*ptr, ""); +} + +TEST(AlwaysValidPointerTest, + TakeOverOwnershipFallbackOnDefaultWithForwardedArgument) { + std::unique_ptr<std::string> str2; + AlwaysValidPointer<std::string> ptr(std::move(str2), nullptr, "keso"); + EXPECT_EQ(*ptr, "keso"); +} + +TEST(AlwaysValidPointerTest, TakeOverOwnershipDoesNotForwardDefaultArguments) { + std::unique_ptr<std::string> str = std::make_unique<std::string>("kalle"); + std::unique_ptr<std::string> str2 = std::make_unique<std::string>("anka"); + AlwaysValidPointer<std::string> ptr(std::move(str), nullptr, *str2); + EXPECT_EQ(*ptr, "kalle"); + EXPECT_TRUE(!str); + EXPECT_EQ(*str2, "anka"); +} + +TEST(AlwaysValidPointerTest, DefaultToLambda) { + AlwaysValidPointer<std::string> ptr( + nullptr, []() { return std::make_unique<std::string>("onkel skrue"); }); + EXPECT_EQ(*ptr, "onkel skrue"); +} + +TEST(AlwaysValidPointerTest, NoDefaultObjectPassValidPointer) { + std::string str("foo"); + AlwaysValidPointerNoDefault<std::string> ptr(&str); + EXPECT_EQ(*ptr, "foo"); + EXPECT_EQ(ptr, &str); +} + +TEST(AlwaysValidPointerTest, NoDefaultObjectWithTakeOverOwnership) { + std::unique_ptr<std::string> str = std::make_unique<std::string>("yum"); + AlwaysValidPointerNoDefault<std::string> ptr(std::move(str)); + EXPECT_EQ(*ptr, "yum"); + std::unique_ptr<std::string> str2 = std::make_unique<std::string>("fun"); + AlwaysValidPointerNoDefault<std::string> ptr2(std::move(str), str2.get()); + EXPECT_EQ(*ptr2, "fun"); + EXPECT_EQ(ptr2, str2.get()); +} + +#if GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID) + +TEST(AlwaysValidPointerTest, NoDefaultObjectPassNullPointer) { + auto pass_null = []() { + AlwaysValidPointerNoDefault<std::string> ptr(nullptr); + }; + EXPECT_DEATH(pass_null(), ""); +} + +TEST(AlwaysValidPointerTest, NoDefaultObjectPassNullUniquePointer) { + auto pass_null = []() { + std::unique_ptr<std::string> str; + AlwaysValidPointerNoDefault<std::string> ptr(std::move(str)); + }; + EXPECT_DEATH(pass_null(), ""); +} + +#endif + +} // namespace webrtc diff --git a/third_party/libwebrtc/rtc_base/memory/fifo_buffer.cc b/third_party/libwebrtc/rtc_base/memory/fifo_buffer.cc new file mode 100644 index 0000000000..c159bc979f --- /dev/null +++ b/third_party/libwebrtc/rtc_base/memory/fifo_buffer.cc @@ -0,0 +1,197 @@ +/* + * Copyright 2019 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 "rtc_base/memory/fifo_buffer.h" + +#include <algorithm> + +#include "rtc_base/thread.h" + +namespace rtc { + +FifoBuffer::FifoBuffer(size_t size) + : state_(SS_OPEN), + buffer_(new char[size]), + buffer_length_(size), + data_length_(0), + read_position_(0), + owner_(Thread::Current()) { + // all events are done on the owner_ thread +} + +FifoBuffer::FifoBuffer(size_t size, Thread* owner) + : state_(SS_OPEN), + buffer_(new char[size]), + buffer_length_(size), + data_length_(0), + read_position_(0), + owner_(owner) { + // all events are done on the owner_ thread +} + +FifoBuffer::~FifoBuffer() {} + +bool FifoBuffer::GetBuffered(size_t* size) const { + webrtc::MutexLock lock(&mutex_); + *size = data_length_; + return true; +} + +StreamState FifoBuffer::GetState() const { + webrtc::MutexLock lock(&mutex_); + return state_; +} + +StreamResult FifoBuffer::Read(rtc::ArrayView<uint8_t> buffer, + size_t& bytes_read, + int& error) { + webrtc::MutexLock lock(&mutex_); + const bool was_writable = data_length_ < buffer_length_; + size_t copy = 0; + StreamResult result = ReadLocked(buffer.data(), buffer.size(), ©); + + if (result == SR_SUCCESS) { + // If read was successful then adjust the read position and number of + // bytes buffered. + read_position_ = (read_position_ + copy) % buffer_length_; + data_length_ -= copy; + bytes_read = copy; + + // if we were full before, and now we're not, post an event + if (!was_writable && copy > 0) { + PostEvent(SE_WRITE, 0); + } + } + return result; +} + +StreamResult FifoBuffer::Write(rtc::ArrayView<const uint8_t> buffer, + size_t& bytes_written, + int& error) { + webrtc::MutexLock lock(&mutex_); + + const bool was_readable = (data_length_ > 0); + size_t copy = 0; + StreamResult result = WriteLocked(buffer.data(), buffer.size(), ©); + + if (result == SR_SUCCESS) { + // If write was successful then adjust the number of readable bytes. + data_length_ += copy; + bytes_written = copy; + // if we didn't have any data to read before, and now we do, post an event + if (!was_readable && copy > 0) { + PostEvent(SE_READ, 0); + } + } + return result; +} + +void FifoBuffer::Close() { + webrtc::MutexLock lock(&mutex_); + state_ = SS_CLOSED; +} + +const void* FifoBuffer::GetReadData(size_t* size) { + webrtc::MutexLock lock(&mutex_); + *size = (read_position_ + data_length_ <= buffer_length_) + ? data_length_ + : buffer_length_ - read_position_; + return &buffer_[read_position_]; +} + +void FifoBuffer::ConsumeReadData(size_t size) { + webrtc::MutexLock lock(&mutex_); + RTC_DCHECK(size <= data_length_); + const bool was_writable = data_length_ < buffer_length_; + read_position_ = (read_position_ + size) % buffer_length_; + data_length_ -= size; + if (!was_writable && size > 0) { + PostEvent(SE_WRITE, 0); + } +} + +void* FifoBuffer::GetWriteBuffer(size_t* size) { + webrtc::MutexLock lock(&mutex_); + if (state_ == SS_CLOSED) { + return nullptr; + } + + // if empty, reset the write position to the beginning, so we can get + // the biggest possible block + if (data_length_ == 0) { + read_position_ = 0; + } + + const size_t write_position = + (read_position_ + data_length_) % buffer_length_; + *size = (write_position > read_position_ || data_length_ == 0) + ? buffer_length_ - write_position + : read_position_ - write_position; + return &buffer_[write_position]; +} + +void FifoBuffer::ConsumeWriteBuffer(size_t size) { + webrtc::MutexLock lock(&mutex_); + RTC_DCHECK(size <= buffer_length_ - data_length_); + const bool was_readable = (data_length_ > 0); + data_length_ += size; + if (!was_readable && size > 0) { + PostEvent(SE_READ, 0); + } +} + +StreamResult FifoBuffer::ReadLocked(void* buffer, + size_t bytes, + size_t* bytes_read) { + if (data_length_ == 0) { + return (state_ != SS_CLOSED) ? SR_BLOCK : SR_EOS; + } + + const size_t available = data_length_; + const size_t read_position = read_position_ % buffer_length_; + const size_t copy = std::min(bytes, available); + const size_t tail_copy = std::min(copy, buffer_length_ - read_position); + char* const p = static_cast<char*>(buffer); + memcpy(p, &buffer_[read_position], tail_copy); + memcpy(p + tail_copy, &buffer_[0], copy - tail_copy); + + if (bytes_read) { + *bytes_read = copy; + } + return SR_SUCCESS; +} + +StreamResult FifoBuffer::WriteLocked(const void* buffer, + size_t bytes, + size_t* bytes_written) { + if (state_ == SS_CLOSED) { + return SR_EOS; + } + + if (data_length_ >= buffer_length_) { + return SR_BLOCK; + } + + const size_t available = buffer_length_ - data_length_; + const size_t write_position = + (read_position_ + data_length_) % buffer_length_; + const size_t copy = std::min(bytes, available); + const size_t tail_copy = std::min(copy, buffer_length_ - write_position); + const char* const p = static_cast<const char*>(buffer); + memcpy(&buffer_[write_position], p, tail_copy); + memcpy(&buffer_[0], p + tail_copy, copy - tail_copy); + + if (bytes_written) { + *bytes_written = copy; + } + return SR_SUCCESS; +} + +} // namespace rtc diff --git a/third_party/libwebrtc/rtc_base/memory/fifo_buffer.h b/third_party/libwebrtc/rtc_base/memory/fifo_buffer.h new file mode 100644 index 0000000000..a225c688ac --- /dev/null +++ b/third_party/libwebrtc/rtc_base/memory/fifo_buffer.h @@ -0,0 +1,118 @@ +/* + * Copyright 2019 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 RTC_BASE_MEMORY_FIFO_BUFFER_H_ +#define RTC_BASE_MEMORY_FIFO_BUFFER_H_ + +#include <memory> + +#include "api/task_queue/pending_task_safety_flag.h" +#include "rtc_base/stream.h" +#include "rtc_base/synchronization/mutex.h" + +namespace rtc { + +// FifoBuffer allows for efficient, thread-safe buffering of data between +// writer and reader. +class FifoBuffer final : public StreamInterface { + public: + // Creates a FIFO buffer with the specified capacity. + explicit FifoBuffer(size_t length); + // Creates a FIFO buffer with the specified capacity and owner + FifoBuffer(size_t length, Thread* owner); + ~FifoBuffer() override; + + FifoBuffer(const FifoBuffer&) = delete; + FifoBuffer& operator=(const FifoBuffer&) = delete; + + // Gets the amount of data currently readable from the buffer. + bool GetBuffered(size_t* data_len) const; + + // StreamInterface methods + StreamState GetState() const override; + StreamResult Read(rtc::ArrayView<uint8_t> buffer, + size_t& bytes_read, + int& error) override; + StreamResult Write(rtc::ArrayView<const uint8_t> buffer, + size_t& bytes_written, + int& error) override; + void Close() override; + + // Seek to a byte offset from the beginning of the stream. Returns false if + // the stream does not support seeking, or cannot seek to the specified + // position. + bool SetPosition(size_t position); + + // Get the byte offset of the current position from the start of the stream. + // Returns false if the position is not known. + bool GetPosition(size_t* position) const; + + // Seek to the start of the stream. + bool Rewind() { return SetPosition(0); } + + // GetReadData returns a pointer to a buffer which is owned by the stream. + // The buffer contains data_len bytes. null is returned if no data is + // available, or if the method fails. If the caller processes the data, it + // must call ConsumeReadData with the number of processed bytes. GetReadData + // does not require a matching call to ConsumeReadData if the data is not + // processed. Read and ConsumeReadData invalidate the buffer returned by + // GetReadData. + const void* GetReadData(size_t* data_len); + void ConsumeReadData(size_t used); + // GetWriteBuffer returns a pointer to a buffer which is owned by the stream. + // The buffer has a capacity of buf_len bytes. null is returned if there is + // no buffer available, or if the method fails. The call may write data to + // the buffer, and then call ConsumeWriteBuffer with the number of bytes + // written. GetWriteBuffer does not require a matching call to + // ConsumeWriteData if no data is written. Write and + // ConsumeWriteData invalidate the buffer returned by GetWriteBuffer. + void* GetWriteBuffer(size_t* buf_len); + void ConsumeWriteBuffer(size_t used); + + private: + void PostEvent(int events, int err) { + owner_->PostTask(webrtc::SafeTask( + task_safety_.flag(), + [this, events, err]() { SignalEvent(this, events, err); })); + } + + // Helper method that implements Read. Caller must acquire a lock + // when calling this method. + StreamResult ReadLocked(void* buffer, size_t bytes, size_t* bytes_read) + RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); + + // Helper method that implements Write. Caller must acquire a lock + // when calling this method. + StreamResult WriteLocked(const void* buffer, + size_t bytes, + size_t* bytes_written) + RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); + + webrtc::ScopedTaskSafety task_safety_; + + // keeps the opened/closed state of the stream + StreamState state_ RTC_GUARDED_BY(mutex_); + // the allocated buffer + std::unique_ptr<char[]> buffer_ RTC_GUARDED_BY(mutex_); + // size of the allocated buffer + const size_t buffer_length_; + // amount of readable data in the buffer + size_t data_length_ RTC_GUARDED_BY(mutex_); + // offset to the readable data + size_t read_position_ RTC_GUARDED_BY(mutex_); + // stream callbacks are dispatched on this thread + Thread* const owner_; + // object lock + mutable webrtc::Mutex mutex_; +}; + +} // namespace rtc + +#endif // RTC_BASE_MEMORY_FIFO_BUFFER_H_ diff --git a/third_party/libwebrtc/rtc_base/memory/fifo_buffer_unittest.cc b/third_party/libwebrtc/rtc_base/memory/fifo_buffer_unittest.cc new file mode 100644 index 0000000000..27eb8d8b45 --- /dev/null +++ b/third_party/libwebrtc/rtc_base/memory/fifo_buffer_unittest.cc @@ -0,0 +1,249 @@ +/* + * 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 "rtc_base/memory/fifo_buffer.h" + +#include <string.h> + +#include "test/gtest.h" + +namespace rtc { + +TEST(FifoBufferTest, TestAll) { + rtc::AutoThread main_thread; + const size_t kSize = 16; + const uint8_t in[kSize * 2 + 1] = "0123456789ABCDEFGHIJKLMNOPQRSTUV"; + uint8_t out[kSize * 2]; + void* p; + const void* q; + size_t bytes; + FifoBuffer buf(kSize); + + // Test assumptions about base state + EXPECT_EQ(SS_OPEN, buf.GetState()); + int error; + EXPECT_EQ(SR_BLOCK, buf.Read(rtc::MakeArrayView(out, kSize), bytes, error)); + EXPECT_TRUE(nullptr != buf.GetWriteBuffer(&bytes)); + EXPECT_EQ(kSize, bytes); + buf.ConsumeWriteBuffer(0); + + // Try a full write + EXPECT_EQ(SR_SUCCESS, buf.Write(rtc::MakeArrayView(in, kSize), bytes, error)); + EXPECT_EQ(kSize, bytes); + + // Try a write that should block + EXPECT_EQ(SR_BLOCK, buf.Write(rtc::MakeArrayView(in, kSize), bytes, error)); + + // Try a full read + EXPECT_EQ(SR_SUCCESS, buf.Read(rtc::MakeArrayView(out, kSize), bytes, error)); + EXPECT_EQ(kSize, bytes); + EXPECT_EQ(0, memcmp(in, out, kSize)); + + // Try a read that should block + EXPECT_EQ(SR_BLOCK, buf.Read(rtc::MakeArrayView(out, kSize), bytes, error)); + + // Try a too-big write + EXPECT_EQ(SR_SUCCESS, + buf.Write(rtc::MakeArrayView(in, kSize * 2), bytes, error)); + EXPECT_EQ(bytes, kSize); + + // Try a too-big read + EXPECT_EQ(SR_SUCCESS, + buf.Read(rtc::MakeArrayView(out, kSize * 2), bytes, error)); + EXPECT_EQ(kSize, bytes); + EXPECT_EQ(0, memcmp(in, out, kSize)); + + // Try some small writes and reads + EXPECT_EQ(SR_SUCCESS, + buf.Write(rtc::MakeArrayView(in, kSize / 2), bytes, error)); + EXPECT_EQ(kSize / 2, bytes); + EXPECT_EQ(SR_SUCCESS, + buf.Read(rtc::MakeArrayView(out, kSize / 2), bytes, error)); + EXPECT_EQ(kSize / 2, bytes); + EXPECT_EQ(0, memcmp(in, out, kSize / 2)); + EXPECT_EQ(SR_SUCCESS, + buf.Write(rtc::MakeArrayView(in, kSize / 2), bytes, error)); + EXPECT_EQ(kSize / 2, bytes); + EXPECT_EQ(SR_SUCCESS, + buf.Write(rtc::MakeArrayView(in, kSize / 2), bytes, error)); + EXPECT_EQ(kSize / 2, bytes); + EXPECT_EQ(SR_SUCCESS, + buf.Read(rtc::MakeArrayView(out, kSize / 2), bytes, error)); + EXPECT_EQ(kSize / 2, bytes); + EXPECT_EQ(0, memcmp(in, out, kSize / 2)); + EXPECT_EQ(SR_SUCCESS, + buf.Read(rtc::MakeArrayView(out, kSize / 2), bytes, error)); + EXPECT_EQ(kSize / 2, bytes); + EXPECT_EQ(0, memcmp(in, out, kSize / 2)); + + // Try wraparound reads and writes in the following pattern + // WWWWWWWWWWWW.... 0123456789AB.... + // RRRRRRRRXXXX.... ........89AB.... + // WWWW....XXXXWWWW 4567....89AB0123 + // XXXX....RRRRXXXX 4567........0123 + // XXXXWWWWWWWWXXXX 4567012345670123 + // RRRRXXXXXXXXRRRR ....01234567.... + // ....RRRRRRRR.... ................ + EXPECT_EQ(SR_SUCCESS, + buf.Write(rtc::MakeArrayView(in, kSize * 3 / 4), bytes, error)); + EXPECT_EQ(kSize * 3 / 4, bytes); + EXPECT_EQ(SR_SUCCESS, + buf.Read(rtc::MakeArrayView(out, kSize / 2), bytes, error)); + EXPECT_EQ(kSize / 2, bytes); + EXPECT_EQ(0, memcmp(in, out, kSize / 2)); + EXPECT_EQ(SR_SUCCESS, + buf.Write(rtc::MakeArrayView(in, kSize / 2), bytes, error)); + EXPECT_EQ(kSize / 2, bytes); + EXPECT_EQ(SR_SUCCESS, + buf.Read(rtc::MakeArrayView(out, kSize / 4), bytes, error)); + EXPECT_EQ(kSize / 4, bytes); + EXPECT_EQ(0, memcmp(in + kSize / 2, out, kSize / 4)); + EXPECT_EQ(SR_SUCCESS, + buf.Write(rtc::MakeArrayView(in, kSize / 2), bytes, error)); + EXPECT_EQ(kSize / 2, bytes); + EXPECT_EQ(SR_SUCCESS, + buf.Read(rtc::MakeArrayView(out, kSize / 2), bytes, error)); + EXPECT_EQ(kSize / 2, bytes); + EXPECT_EQ(0, memcmp(in, out, kSize / 2)); + EXPECT_EQ(SR_SUCCESS, + buf.Read(rtc::MakeArrayView(out, kSize / 2), bytes, error)); + EXPECT_EQ(kSize / 2, bytes); + EXPECT_EQ(0, memcmp(in, out, kSize / 2)); + + // Use GetWriteBuffer to reset the read_position for the next tests + buf.GetWriteBuffer(&bytes); + buf.ConsumeWriteBuffer(0); + + // Try using GetReadData to do a full read + EXPECT_EQ(SR_SUCCESS, buf.Write(rtc::MakeArrayView(in, kSize), bytes, error)); + q = buf.GetReadData(&bytes); + EXPECT_TRUE(nullptr != q); + EXPECT_EQ(kSize, bytes); + EXPECT_EQ(0, memcmp(q, in, kSize)); + buf.ConsumeReadData(kSize); + EXPECT_EQ(SR_BLOCK, buf.Read(rtc::MakeArrayView(out, kSize), bytes, error)); + + // Try using GetReadData to do some small reads + EXPECT_EQ(SR_SUCCESS, buf.Write(rtc::MakeArrayView(in, kSize), bytes, error)); + q = buf.GetReadData(&bytes); + EXPECT_TRUE(nullptr != q); + EXPECT_EQ(kSize, bytes); + EXPECT_EQ(0, memcmp(q, in, kSize / 2)); + buf.ConsumeReadData(kSize / 2); + q = buf.GetReadData(&bytes); + EXPECT_TRUE(nullptr != q); + EXPECT_EQ(kSize / 2, bytes); + EXPECT_EQ(0, memcmp(q, in + kSize / 2, kSize / 2)); + buf.ConsumeReadData(kSize / 2); + EXPECT_EQ(SR_BLOCK, buf.Read(rtc::MakeArrayView(out, kSize), bytes, error)); + + // Try using GetReadData in a wraparound case + // WWWWWWWWWWWWWWWW 0123456789ABCDEF + // RRRRRRRRRRRRXXXX ............CDEF + // WWWWWWWW....XXXX 01234567....CDEF + // ............RRRR 01234567........ + // RRRRRRRR........ ................ + EXPECT_EQ(SR_SUCCESS, buf.Write(rtc::MakeArrayView(in, kSize), bytes, error)); + EXPECT_EQ(SR_SUCCESS, + buf.Read(rtc::MakeArrayView(out, kSize * 3 / 4), bytes, error)); + EXPECT_EQ(SR_SUCCESS, + buf.Write(rtc::MakeArrayView(in, kSize / 2), bytes, error)); + q = buf.GetReadData(&bytes); + EXPECT_TRUE(nullptr != q); + EXPECT_EQ(kSize / 4, bytes); + EXPECT_EQ(0, memcmp(q, in + kSize * 3 / 4, kSize / 4)); + buf.ConsumeReadData(kSize / 4); + q = buf.GetReadData(&bytes); + EXPECT_TRUE(nullptr != q); + EXPECT_EQ(kSize / 2, bytes); + EXPECT_EQ(0, memcmp(q, in, kSize / 2)); + buf.ConsumeReadData(kSize / 2); + + // Use GetWriteBuffer to reset the read_position for the next tests + buf.GetWriteBuffer(&bytes); + buf.ConsumeWriteBuffer(0); + + // Try using GetWriteBuffer to do a full write + p = buf.GetWriteBuffer(&bytes); + EXPECT_TRUE(nullptr != p); + EXPECT_EQ(kSize, bytes); + memcpy(p, in, kSize); + buf.ConsumeWriteBuffer(kSize); + EXPECT_EQ(SR_SUCCESS, buf.Read(rtc::MakeArrayView(out, kSize), bytes, error)); + EXPECT_EQ(kSize, bytes); + EXPECT_EQ(0, memcmp(in, out, kSize)); + + // Try using GetWriteBuffer to do some small writes + p = buf.GetWriteBuffer(&bytes); + EXPECT_TRUE(nullptr != p); + EXPECT_EQ(kSize, bytes); + memcpy(p, in, kSize / 2); + buf.ConsumeWriteBuffer(kSize / 2); + p = buf.GetWriteBuffer(&bytes); + EXPECT_TRUE(nullptr != p); + EXPECT_EQ(kSize / 2, bytes); + memcpy(p, in + kSize / 2, kSize / 2); + buf.ConsumeWriteBuffer(kSize / 2); + EXPECT_EQ(SR_SUCCESS, buf.Read(rtc::MakeArrayView(out, kSize), bytes, error)); + EXPECT_EQ(kSize, bytes); + EXPECT_EQ(0, memcmp(in, out, kSize)); + + // Try using GetWriteBuffer in a wraparound case + // WWWWWWWWWWWW.... 0123456789AB.... + // RRRRRRRRXXXX.... ........89AB.... + // ........XXXXWWWW ........89AB0123 + // WWWW....XXXXXXXX 4567....89AB0123 + // RRRR....RRRRRRRR ................ + EXPECT_EQ(SR_SUCCESS, + buf.Write(rtc::MakeArrayView(in, kSize * 3 / 4), bytes, error)); + EXPECT_EQ(SR_SUCCESS, + buf.Read(rtc::MakeArrayView(out, kSize / 2), bytes, error)); + p = buf.GetWriteBuffer(&bytes); + EXPECT_TRUE(nullptr != p); + EXPECT_EQ(kSize / 4, bytes); + memcpy(p, in, kSize / 4); + buf.ConsumeWriteBuffer(kSize / 4); + p = buf.GetWriteBuffer(&bytes); + EXPECT_TRUE(nullptr != p); + EXPECT_EQ(kSize / 2, bytes); + memcpy(p, in + kSize / 4, kSize / 4); + buf.ConsumeWriteBuffer(kSize / 4); + EXPECT_EQ(SR_SUCCESS, + buf.Read(rtc::MakeArrayView(out, kSize * 3 / 4), bytes, error)); + EXPECT_EQ(kSize * 3 / 4, bytes); + EXPECT_EQ(0, memcmp(in + kSize / 2, out, kSize / 4)); + EXPECT_EQ(0, memcmp(in, out + kSize / 4, kSize / 4)); + + // Check that the stream is now empty + EXPECT_EQ(SR_BLOCK, buf.Read(rtc::MakeArrayView(out, kSize), bytes, error)); + + // Write to the stream, close it, read the remaining bytes + EXPECT_EQ(SR_SUCCESS, + buf.Write(rtc::MakeArrayView(in, kSize / 2), bytes, error)); + buf.Close(); + EXPECT_EQ(SS_CLOSED, buf.GetState()); + EXPECT_EQ(SR_EOS, buf.Write(rtc::MakeArrayView(in, kSize / 2), bytes, error)); + EXPECT_EQ(SR_SUCCESS, + buf.Read(rtc::MakeArrayView(out, kSize / 2), bytes, error)); + EXPECT_EQ(0, memcmp(in, out, kSize / 2)); + EXPECT_EQ(SR_EOS, buf.Read(rtc::MakeArrayView(out, kSize / 2), bytes, error)); +} + +TEST(FifoBufferTest, FullBufferCheck) { + rtc::AutoThread main_thread; + FifoBuffer buff(10); + buff.ConsumeWriteBuffer(10); + + size_t free; + EXPECT_TRUE(buff.GetWriteBuffer(&free) != nullptr); + EXPECT_EQ(0U, free); +} + +} // namespace rtc |