diff options
-rw-r--r-- | Makefile.am | 1 | ||||
-rw-r--r-- | src/MusicBuffer.cxx | 72 | ||||
-rw-r--r-- | src/util/SliceBuffer.hxx | 134 |
3 files changed, 147 insertions, 60 deletions
diff --git a/Makefile.am b/Makefile.am index df402b038..8130d6fc4 100644 --- a/Makefile.am +++ b/Makefile.am @@ -347,6 +347,7 @@ endif # Generic utility library libutil_a_SOURCES = \ + src/util/SliceBuffer.hxx \ src/util/list.h \ src/util/list_sort.c src/util/list_sort.h \ src/util/byte_reverse.c src/util/byte_reverse.h \ diff --git a/src/MusicBuffer.cxx b/src/MusicBuffer.cxx index e90344aed..ec6e52054 100644 --- a/src/MusicBuffer.cxx +++ b/src/MusicBuffer.cxx @@ -20,53 +20,22 @@ #include "config.h" #include "MusicBuffer.hxx" #include "MusicChunk.hxx" +#include "util/SliceBuffer.hxx" #include <glib.h> #include <assert.h> -struct music_buffer { - struct music_chunk *chunks; - unsigned num_chunks; - - struct music_chunk *available; - +struct music_buffer : public SliceBuffer<music_chunk> { /** a mutex which protects #available */ GMutex *mutex; -#ifndef NDEBUG - unsigned num_allocated; -#endif - - music_buffer(unsigned _num_chunks) - :chunks(g_new(struct music_chunk, _num_chunks)), - num_chunks(_num_chunks), - available(chunks), - mutex(g_mutex_new()) -#ifndef NDEBUG - , num_allocated(0) -#endif - { - assert(num_chunks > 0); - - struct music_chunk *chunk; - chunk = available = chunks; - - for (unsigned i = 1; i < num_chunks; ++i) { - chunk->next = &chunks[i]; - chunk = chunk->next; - } - - chunk->next = nullptr; - } + music_buffer(unsigned num_chunks) + :SliceBuffer(num_chunks), + mutex(g_mutex_new()) {} ~music_buffer() { - assert(chunks != nullptr); - assert(num_chunks > 0); - assert(num_allocated == 0); - g_mutex_free(mutex); - g_free(chunks); } }; @@ -85,26 +54,14 @@ music_buffer_free(struct music_buffer *buffer) unsigned music_buffer_size(const struct music_buffer *buffer) { - return buffer->num_chunks; + return buffer->GetCapacity(); } struct music_chunk * music_buffer_allocate(struct music_buffer *buffer) { - struct music_chunk *chunk; - g_mutex_lock(buffer->mutex); - - chunk = buffer->available; - if (chunk != NULL) { - buffer->available = chunk->next; - music_chunk_init(chunk); - -#ifndef NDEBUG - ++buffer->num_allocated; -#endif - } - + struct music_chunk *chunk = buffer->Allocate(); g_mutex_unlock(buffer->mutex); return chunk; } @@ -115,19 +72,14 @@ music_buffer_return(struct music_buffer *buffer, struct music_chunk *chunk) assert(buffer != NULL); assert(chunk != NULL); - if (chunk->other != NULL) - music_buffer_return(buffer, chunk->other); - g_mutex_lock(buffer->mutex); - music_chunk_free(chunk); - - chunk->next = buffer->available; - buffer->available = chunk; + if (chunk->other != nullptr) { + assert(chunk->other->other == nullptr); + buffer->Free(chunk->other); + } -#ifndef NDEBUG - --buffer->num_allocated; -#endif + buffer->Free(chunk); g_mutex_unlock(buffer->mutex); } diff --git a/src/util/SliceBuffer.hxx b/src/util/SliceBuffer.hxx new file mode 100644 index 000000000..9ddad6b3d --- /dev/null +++ b/src/util/SliceBuffer.hxx @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2003-2013 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_SLICE_BUFFER_HXX +#define MPD_SLICE_BUFFER_HXX + +#include "gcc.h" + +#include <glib.h> + +#include <utility> +#include <new> + +#include <assert.h> +#include <stddef.h> + +/** + * This class pre-allocates a certain number of objects, and allows + * callers to allocate and free these objects ("slices"). + */ +template<typename T> +class SliceBuffer { + union Slice { + Slice *next; + + T value; + }; + + /** + * The maximum number of slices in this container. + */ + const unsigned n_max; + + /** + * The number of slices currently allocated. + */ + unsigned n_allocated; + + Slice *const data; + + /** + * Pointer to the first free element in the chain. + */ + Slice *available; + +public: + SliceBuffer(unsigned _count) + :n_max(_count), n_allocated(0), + data(g_new(Slice, n_max)), available(data) { + assert(n_max > 0); + + Slice *const last = data + n_max - 1; + for (Slice *slice = data; slice != last; ++slice) + slice->next = slice + 1; + + last->next = nullptr; + } + + ~SliceBuffer() { + /* all slices must be freed explicitly, and this + assertion checks for leaks */ + assert(n_allocated == 0); + + g_free(data); + } + + SliceBuffer(const SliceBuffer &other) = delete; + SliceBuffer &operator=(const SliceBuffer &other) = delete; + + unsigned GetCapacity() const { + return n_max; + } + + bool IsEmpty() const { + return n_allocated == 0; + } + + bool IsFull() const { + return n_allocated == n_max; + } + + template<typename... Args> + T *Allocate(Args&&... args) { + assert(n_allocated <= n_max); + + if (available == nullptr) { + /* out of (internal) memory, buffer is full */ + assert(n_allocated == n_max); + return nullptr; + } + + /* allocate a slice */ + T *value = &available->value; + available = available->next; + ++n_allocated; + + /* construct the object */ + return ::new((void *)value) T(std::forward<Args>(args)...); + } + + void Free(T *value) { + assert(n_allocated > 0); + assert(n_allocated <= n_max); + + Slice *slice = reinterpret_cast<Slice *>(value); + assert(slice >= data && slice < data + n_max); + + /* destruct the object */ + value->~T(); + + /* insert the slice in the "available" linked list */ + slice->next = available; + available = slice; + --n_allocated; + } +}; + +#endif |