Add an auto pointer and an auto array class

The auto pointer frees in the dtor.

The auto array has a number of feature that are useful to work with real-time
streaming audio: push back and pop front (because we're dealing with temporal
data), insert silence, auto resize, no compaction when it's resized down, bound
check, direct internal access to work nicely with other APIs.
This commit is contained in:
Paul Adenot 2016-02-02 18:01:18 +01:00
Родитель c438f775a6
Коммит 1f13325871
4 изменённых файлов: 287 добавлений и 2 удалений

2
.gitignore поставляемый
Просмотреть файл

@ -47,6 +47,8 @@ test/test_tone
test/test_tone.exe
test/test_devices
test/test_devices.exe
test/test_utils
test/test_utils.exe
include/cubeb/cubeb-stdint.h
test-suite.log
test/test_sanity.log

Просмотреть файл

@ -1,7 +1,7 @@
AUTOMAKE_OPTIONS = foreign 1.10 dist-bzip2 subdir-objects
ACLOCAL_AMFLAGS = -I m4
AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include -I. -I$(top_srcdir)/src
AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include -I. -I$(top_srcdir)/src -std=c++11
AM_CFLAGS = -ansi -std=gnu99 -Wall -Wextra -Wno-long-long -O2 -g -Wno-unused-parameter
SUBDIRS = docs
@ -80,6 +80,7 @@ check_PROGRAMS = test/test_sanity \
test/test_audio \
test/test_latency \
test/test_devices \
test/test_utils \
$(NULL)
test_test_sanity_SOURCES = test/test_sanity.cpp
@ -97,7 +98,10 @@ test_test_latency_LDADD = -lm src/libcubeb.la $(platform_lib)
test_test_devices_SOURCES = test/test_devices.cpp
test_test_devices_LDADD = -lm src/libcubeb.la $(platform_lib)
TESTS = test/test_sanity
test_test_resampler_SOURCES = test/test_utils.cpp
TESTS = $(check_PROGRAMS)
DISTCLEANFILES = include/cubeb/cubeb-stdint.h

199
src/cubeb_utils.h Normal file
Просмотреть файл

@ -0,0 +1,199 @@
/*
* 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 <stdint.h>
#include <string.h>
#include <assert.h>
/** Similar to memcpy, but accounts for the size of an element. */
template<typename T>
void PodCopy(T * destination, const T * source, size_t count)
{
memcpy(destination, source, count * sizeof(T));
}
/** Similar to memmove, but accounts for the size of an element. */
template<typename T>
void PodMove(T * destination, const T * source, size_t count)
{
memmove(destination, source, count * sizeof(T));
}
/** Similar to a memset to zero, but accounts for the size of an element. */
template<typename T>
void PodZero(T * destination, size_t count)
{
memset(destination, 0, count * sizeof(T));
}
template<typename T>
class auto_array
{
public:
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_;
}
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 resize(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_) {
resize(length_ + length);
}
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(size_t length)
{
if (length_ + length > capacity_) {
resize(length + length_);
}
PodZero(data_ + length_, 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 (elements) {
PodCopy(elements, data_, length);
}
PodMove(data_, data_ + length, length_ - length);
length_ -= length;
return true;
}
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_;
};
template<typename T>
class auto_ptr
{
public:
auto_ptr(T * ptr)
: ptr(ptr)
{}
~auto_ptr()
{
delete ptr;
}
T * get() const
{
return ptr;
}
T* operator->() const {
assert(ptr && "null pointer dereference.");
return ptr;
}
operator bool() const
{
return !!ptr;
}
private:
T * ptr;
};
#endif /* CUBEB_UTILS */

80
test/test_utils.cpp Normal file
Просмотреть файл

@ -0,0 +1,80 @@
#include <cassert>
#include "cubeb_utils.h"
int test_auto_array()
{
auto_array<uint32_t> array;
auto_array<uint32_t> array2(10);
uint32_t a[10];
assert(array2.length() == 0);
assert(array2.capacity() == 10);
for (uint32_t i = 0; i < 10; i++) {
a[i] = i;
}
assert(array.capacity() == 0);
assert(array.length() == 0);
array.push(a, 10);
assert(!array.resize(9));
for (uint32_t i = 0; i < 10; i++) {
assert(array.data()[i] == i);
}
assert(array.capacity() == 10);
assert(array.length() == 10);
uint32_t b[10];
array.pop(b, 5);
assert(array.capacity() == 10);
assert(array.length() == 5);
for (uint32_t i = 0; i < 5; i++) {
assert(b[i] == i);
assert(array.data()[i] == 5 + i);
}
uint32_t* bb = b + 5;
array.pop(bb, 5);
assert(array.capacity() == 10);
assert(array.length() == 0);
for (uint32_t i = 0; i < 5; i++) {
assert(bb[i] == 5 + i);
}
assert(!array.pop(nullptr, 1));
array.push(a, 10);
array.push(a, 10);
for (uint32_t j = 0; j < 2; j++) {
for (uint32_t i = 0; i < 10; i++) {
assert(array.data()[10 * j + i] == i);
}
}
assert(array.length() == 20);
assert(array.capacity() == 20);
array.pop(nullptr, 5);
for (uint32_t i = 0; i < 5; i++) {
assert(array.data()[i] == 5 + i);
}
assert(array.length() == 15);
assert(array.capacity() == 20);
return 0;
}
int main()
{
test_auto_array();
return 0;
}