/* * Copyright © 2016 Mozilla Foundation * * This program is made available under an ISC-style license. See the * accompanying file LICENSE for details. */ #if !defined(CUBEB_UTILS) #define CUBEB_UTILS #include "cubeb/cubeb.h" #ifdef __cplusplus #include #include #include #include #include #if defined(_WIN32) #include "cubeb_utils_win.h" #else #include "cubeb_utils_unix.h" #endif /** Similar to memcpy, but accounts for the size of an element. */ template void PodCopy(T * destination, const T * source, size_t count) { static_assert(std::is_trivial::value, "Requires trivial type"); assert(destination && source); memcpy(destination, source, count * sizeof(T)); } /** Similar to memmove, but accounts for the size of an element. */ template void PodMove(T * destination, const T * source, size_t count) { static_assert(std::is_trivial::value, "Requires trivial type"); assert(destination && source); memmove(destination, source, count * sizeof(T)); } /** Similar to a memset to zero, but accounts for the size of an element. */ template void PodZero(T * destination, size_t count) { static_assert(std::is_trivial::value, "Requires trivial type"); assert(destination); memset(destination, 0, count * sizeof(T)); } namespace { template void Copy(T * destination, const T * source, size_t count, Trait) { for (size_t i = 0; i < count; i++) { destination[i] = source[i]; } } template void Copy(T * destination, const T * source, size_t count, std::true_type) { PodCopy(destination, source, count); } } // namespace /** * This allows copying a number of elements from a `source` pointer to a * `destination` pointer, using `memcpy` if it is safe to do so, or a loop that * calls the constructors and destructors otherwise. */ template void Copy(T * destination, const T * source, size_t count) { assert(destination && source); Copy(destination, source, count, typename std::is_trivial::type()); } namespace { template void ConstructDefault(T * destination, size_t count, Trait) { for (size_t i = 0; i < count; i++) { destination[i] = T(); } } template void ConstructDefault(T * destination, size_t count, std::true_type) { PodZero(destination, count); } } // namespace /** * This allows zeroing (using memset) or default-constructing a number of * elements calling the constructors and destructors if necessary. */ template void ConstructDefault(T * destination, size_t count) { assert(destination); ConstructDefault(destination, count, typename std::is_arithmetic::type()); } template class auto_array { public: explicit auto_array(uint32_t capacity = 0) : data_(capacity ? new T[capacity] : nullptr), capacity_(capacity), length_(0) { } ~auto_array() { delete[] data_; } /** Get a constant pointer to the underlying data. */ T * data() const { return data_; } T * end() const { return data_ + length_; } const T & at(size_t index) const { assert(index < length_ && "out of range"); return data_[index]; } T & at(size_t index) { assert(index < length_ && "out of range"); return data_[index]; } /** Get how much underlying storage this auto_array has. */ size_t capacity() const { return capacity_; } /** Get how much elements this auto_array contains. */ size_t length() const { return length_; } /** Keeps the storage, but removes all the elements from the array. */ void clear() { length_ = 0; } /** Change the storage of this auto array, copying the elements to the new * storage. * @returns true in case of success * @returns false if the new capacity is not big enough to accomodate for the * elements in the array. */ bool reserve(size_t new_capacity) { if (new_capacity < length_) { return false; } T * new_data = new T[new_capacity]; if (data_ && length_) { PodCopy(new_data, data_, length_); } capacity_ = new_capacity; delete[] data_; data_ = new_data; return true; } /** Append `length` elements to the end of the array, resizing the array if * needed. * @parameter elements the elements to append to the array. * @parameter length the number of elements to append to the array. */ void push(const T * elements, size_t length) { if (length_ + length > capacity_) { reserve(length_ + length); } if (data_) { PodCopy(data_ + length_, elements, length); } length_ += length; } /** Append `length` zero-ed elements to the end of the array, resizing the * array if needed. * @parameter length the number of elements to append to the array. */ void push_silence(size_t length) { if (length_ + length > capacity_) { reserve(length + length_); } if (data_) { PodZero(data_ + length_, length); } length_ += length; } /** Prepend `length` zero-ed elements to the front of the array, resizing and * shifting the array if needed. * @parameter length the number of elements to prepend to the array. */ void push_front_silence(size_t length) { if (length_ + length > capacity_) { reserve(length + length_); } if (data_) { PodMove(data_ + length, data_, length_); PodZero(data_, length); } length_ += length; } /** Return the number of free elements in the array. */ size_t available() const { return capacity_ - length_; } /** Copies `length` elements to `elements` if it is not null, and shift * the remaining elements of the `auto_array` to the beginning. * @parameter elements a buffer to copy the elements to, or nullptr. * @parameter length the number of elements to copy. * @returns true in case of success. * @returns false if the auto_array contains less than `length` elements. */ bool pop(T * elements, size_t length) { if (length > length_) { return false; } if (!data_) { return true; } if (elements) { PodCopy(elements, data_, length); } PodMove(data_, data_ + length, length_ - length); length_ -= length; return true; } void set_length(size_t length) { assert(length <= capacity_); length_ = length; } private: /** The underlying storage */ T * data_; /** The size, in number of elements, of the storage. */ size_t capacity_; /** The number of elements the array contains. */ size_t length_; }; struct auto_array_wrapper { virtual void push(void * elements, size_t length) = 0; virtual size_t length() = 0; virtual void push_silence(size_t length) = 0; virtual bool pop(size_t length) = 0; virtual void * data() = 0; virtual void * end() = 0; virtual void clear() = 0; virtual bool reserve(size_t capacity) = 0; virtual void set_length(size_t length) = 0; virtual ~auto_array_wrapper() {} }; template struct auto_array_wrapper_impl : public auto_array_wrapper { auto_array_wrapper_impl() {} explicit auto_array_wrapper_impl(uint32_t size) : ar(size) {} void push(void * elements, size_t length) override { ar.push(static_cast(elements), length); } size_t length() override { return ar.length(); } void push_silence(size_t length) override { ar.push_silence(length); } bool pop(size_t length) override { return ar.pop(nullptr, length); } void * data() override { return ar.data(); } void * end() override { return ar.end(); } void clear() override { ar.clear(); } bool reserve(size_t capacity) override { return ar.reserve(capacity); } void set_length(size_t length) override { ar.set_length(length); } ~auto_array_wrapper_impl() { ar.clear(); } private: auto_array ar; }; extern "C" { size_t cubeb_sample_size(cubeb_sample_format format); } using auto_lock = std::lock_guard; #endif // __cplusplus #endif /* CUBEB_UTILS */