aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMax Kellermann <max@duempel.org>2013-10-15 10:28:52 +0200
committerMax Kellermann <max@duempel.org>2013-10-15 10:28:52 +0200
commit84d20d9e433028125bfe36557ad54a28b97914b2 (patch)
tree9c0b9469ac91e14b90eebd08b5a04ba3a9f3b4a3
parent0c13703da3641951bf061cac7c5cef034eda16f9 (diff)
downloadmpd-84d20d9e433028125bfe36557ad54a28b97914b2.tar.gz
mpd-84d20d9e433028125bfe36557ad54a28b97914b2.tar.xz
mpd-84d20d9e433028125bfe36557ad54a28b97914b2.zip
util/FifoBuffer: C++ version of the fifo_buffer library
-rw-r--r--Makefile.am2
-rw-r--r--src/InotifySource.cxx30
-rw-r--r--src/InotifySource.hxx6
-rw-r--r--src/TextInputStream.cxx46
-rw-r--r--src/TextInputStream.hxx14
-rw-r--r--src/event/BufferedSocket.cxx49
-rw-r--r--src/event/BufferedSocket.hxx16
-rw-r--r--src/util/FifoBuffer.hxx132
-rw-r--r--src/util/WritableBuffer.hxx87
-rw-r--r--test/run_convert.cxx33
10 files changed, 294 insertions, 121 deletions
diff --git a/Makefile.am b/Makefile.am
index 064c181c2..cd83b14a9 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -256,6 +256,8 @@ libutil_a_SOURCES = \
src/util/Manual.hxx \
src/util/RefCount.hxx \
src/util/fifo_buffer.c src/util/fifo_buffer.h \
+ src/util/FifoBuffer.hxx \
+ src/util/WritableBuffer.hxx \
src/util/growing_fifo.c src/util/growing_fifo.h \
src/util/LazyRandomEngine.cxx src/util/LazyRandomEngine.hxx \
src/util/SliceBuffer.hxx \
diff --git a/src/InotifySource.cxx b/src/InotifySource.cxx
index 587625d3d..bf292af60 100644
--- a/src/InotifySource.cxx
+++ b/src/InotifySource.cxx
@@ -20,7 +20,6 @@
#include "config.h"
#include "InotifySource.hxx"
#include "InotifyDomain.hxx"
-#include "util/fifo_buffer.h"
#include "util/Error.hxx"
#include "system/fd_util.h"
#include "system/FatalError.hxx"
@@ -34,30 +33,27 @@
bool
InotifySource::OnSocketReady(gcc_unused unsigned flags)
{
- void *dest;
- size_t length;
- ssize_t nbytes;
-
- dest = fifo_buffer_write(buffer, &length);
- if (dest == NULL)
+ const auto dest = buffer.Write();
+ if (dest.IsEmpty())
FatalError("buffer full");
- nbytes = read(Get(), dest, length);
+ ssize_t nbytes = read(Get(), dest.data, dest.size);
if (nbytes < 0)
FatalSystemError("Failed to read from inotify");
if (nbytes == 0)
FatalError("end of file from inotify");
- fifo_buffer_append(buffer, nbytes);
+ buffer.Append(nbytes);
while (true) {
const char *name;
+ auto range = buffer.Read();
const struct inotify_event *event =
(const struct inotify_event *)
- fifo_buffer_read(buffer, &length);
- if (event == NULL || length < sizeof(*event) ||
- length < sizeof(*event) + event->len)
+ range.data;
+ if (range.size < sizeof(*event) ||
+ range.size < sizeof(*event) + event->len)
break;
if (event->len > 0 && event->name[event->len - 1] == 0)
@@ -66,7 +62,7 @@ InotifySource::OnSocketReady(gcc_unused unsigned flags)
name = NULL;
callback(event->wd, event->mask, name, callback_ctx);
- fifo_buffer_consume(buffer, sizeof(*event) + event->len);
+ buffer.Consume(sizeof(*event) + event->len);
}
return true;
@@ -77,8 +73,7 @@ InotifySource::InotifySource(EventLoop &_loop,
mpd_inotify_callback_t _callback, void *_ctx,
int _fd)
:SocketMonitor(_fd, _loop),
- callback(_callback), callback_ctx(_ctx),
- buffer(fifo_buffer_new(4096))
+ callback(_callback), callback_ctx(_ctx)
{
ScheduleRead();
@@ -98,11 +93,6 @@ InotifySource::Create(EventLoop &loop,
return new InotifySource(loop, callback, callback_ctx, fd);
}
-InotifySource::~InotifySource()
-{
- fifo_buffer_free(buffer);
-}
-
int
InotifySource::Add(const char *path_fs, unsigned mask, Error &error)
{
diff --git a/src/InotifySource.hxx b/src/InotifySource.hxx
index 1fe840c12..f6ddea966 100644
--- a/src/InotifySource.hxx
+++ b/src/InotifySource.hxx
@@ -21,6 +21,7 @@
#define MPD_INOTIFY_SOURCE_HXX
#include "event/SocketMonitor.hxx"
+#include "util/FifoBuffer.hxx"
#include "Compiler.h"
class Error;
@@ -32,7 +33,7 @@ class InotifySource final : private SocketMonitor {
mpd_inotify_callback_t callback;
void *callback_ctx;
- struct fifo_buffer *buffer;
+ FifoBuffer<uint8_t, 4096> buffer;
InotifySource(EventLoop &_loop,
mpd_inotify_callback_t callback, void *ctx, int fd);
@@ -49,9 +50,6 @@ public:
void *ctx,
Error &error);
- ~InotifySource();
-
-
/**
* Adds a path to the notify list.
*
diff --git a/src/TextInputStream.cxx b/src/TextInputStream.cxx
index d58a2fbee..a69e1d513 100644
--- a/src/TextInputStream.cxx
+++ b/src/TextInputStream.cxx
@@ -29,35 +29,23 @@
#include <assert.h>
#include <string.h>
-TextInputStream::TextInputStream(struct input_stream *_is)
- : is(_is),
- buffer(fifo_buffer_new(4096))
-{
-}
-
-TextInputStream::~TextInputStream()
-{
- fifo_buffer_free(buffer);
-}
-
bool TextInputStream::ReadLine(std::string &line)
{
- void *dest;
const char *src, *p;
- size_t length, nbytes;
do {
- dest = fifo_buffer_write(buffer, &length);
- if (dest != nullptr && length >= 2) {
+ size_t nbytes;
+ auto dest = buffer.Write();
+ if (dest.size >= 2) {
/* reserve one byte for the null terminator if
the last line is not terminated by a
newline character */
- --length;
+ --dest.size;
Error error;
- nbytes = is->LockRead(dest, length, error);
+ nbytes = is->LockRead(dest.data, dest.size, error);
if (nbytes > 0)
- fifo_buffer_append(buffer, nbytes);
+ buffer.Append(nbytes);
else if (error.IsDefined()) {
LogError(error);
return false;
@@ -65,28 +53,28 @@ bool TextInputStream::ReadLine(std::string &line)
} else
nbytes = 0;
- auto src_p = fifo_buffer_read(buffer, &length);
- src = reinterpret_cast<const char *>(src_p);
-
- if (src == nullptr)
+ auto src_p = buffer.Read();
+ if (src_p.IsEmpty())
return false;
- p = reinterpret_cast<const char*>(memchr(src, '\n', length));
+ src = src_p.data;
+
+ p = reinterpret_cast<const char*>(memchr(src, '\n', src_p.size));
if (p == nullptr && nbytes == 0) {
/* end of file (or line too long): terminate
the current line */
- dest = fifo_buffer_write(buffer, &nbytes);
- assert(dest != nullptr);
- *(char *)dest = '\n';
- fifo_buffer_append(buffer, 1);
+ dest = buffer.Write();
+ assert(!dest.IsEmpty());
+ dest.data[0] = '\n';
+ buffer.Append(1);
}
} while (p == nullptr);
- length = p - src + 1;
+ size_t length = p - src + 1;
while (p > src && g_ascii_isspace(p[-1]))
--p;
line = std::string(src, p - src);
- fifo_buffer_consume(buffer, length);
+ buffer.Consume(length);
return true;
}
diff --git a/src/TextInputStream.hxx b/src/TextInputStream.hxx
index 2608184e2..f3d87c90a 100644
--- a/src/TextInputStream.hxx
+++ b/src/TextInputStream.hxx
@@ -20,6 +20,8 @@
#ifndef MPD_TEXT_INPUT_STREAM_HXX
#define MPD_TEXT_INPUT_STREAM_HXX
+#include "util/FifoBuffer.hxx"
+
#include <string>
struct input_stream;
@@ -27,7 +29,8 @@ struct fifo_buffer;
class TextInputStream {
struct input_stream *is;
- struct fifo_buffer *buffer;
+ FifoBuffer<char, 4096> buffer;
+
public:
/**
* Wraps an existing #input_stream object into a #TextInputStream,
@@ -35,13 +38,8 @@ public:
*
* @param _is an open #input_stream object
*/
- explicit TextInputStream(struct input_stream *_is);
-
- /**
- * Frees the #TextInputStream object. Does not close or free the
- * underlying #input_stream.
- */
- ~TextInputStream();
+ explicit TextInputStream(struct input_stream *_is)
+ :is(_is) {}
TextInputStream(const TextInputStream &) = delete;
TextInputStream& operator=(const TextInputStream &) = delete;
diff --git a/src/event/BufferedSocket.cxx b/src/event/BufferedSocket.cxx
index f333a5987..92e350e85 100644
--- a/src/event/BufferedSocket.cxx
+++ b/src/event/BufferedSocket.cxx
@@ -20,20 +20,9 @@
#include "config.h"
#include "BufferedSocket.hxx"
#include "system/SocketError.hxx"
-#include "util/fifo_buffer.h"
#include "util/Error.hxx"
#include "util/Domain.hxx"
-#include <assert.h>
-#include <stdint.h>
-#include <string.h>
-
-BufferedSocket::~BufferedSocket()
-{
- if (input != nullptr)
- fifo_buffer_free(input);
-}
-
BufferedSocket::ssize_t
BufferedSocket::DirectRead(void *data, size_t length)
{
@@ -62,16 +51,12 @@ BufferedSocket::ReadToBuffer()
{
assert(IsDefined());
- if (input == nullptr)
- input = fifo_buffer_new(8192);
-
- size_t length;
- void *buffer = fifo_buffer_write(input, &length);
- assert(buffer != nullptr);
+ const auto buffer = input.Write();
+ assert(!buffer.IsEmpty());
- const auto nbytes = DirectRead(buffer, length);
+ const auto nbytes = DirectRead(buffer.data, buffer.size);
if (nbytes > 0)
- fifo_buffer_append(input, nbytes);
+ input.Append(nbytes);
return nbytes >= 0;
}
@@ -81,23 +66,17 @@ BufferedSocket::ResumeInput()
{
assert(IsDefined());
- if (input == nullptr) {
- ScheduleRead();
- return true;
- }
-
while (true) {
- size_t length;
- const void *data = fifo_buffer_read(input, &length);
- if (data == nullptr) {
+ const auto buffer = input.Read();
+ if (buffer.IsEmpty()) {
ScheduleRead();
return true;
}
- const auto result = OnSocketInput(data, length);
+ const auto result = OnSocketInput(buffer.data, buffer.size);
switch (result) {
case InputResult::MORE:
- if (fifo_buffer_is_full(input)) {
+ if (input.IsFull()) {
// TODO
static constexpr Domain buffered_socket_domain("buffered_socket");
Error error;
@@ -123,14 +102,6 @@ BufferedSocket::ResumeInput()
}
}
-void
-BufferedSocket::ConsumeInput(size_t nbytes)
-{
- assert(IsDefined());
-
- fifo_buffer_consume(input, nbytes);
-}
-
bool
BufferedSocket::OnSocketReady(unsigned flags)
{
@@ -142,12 +113,12 @@ BufferedSocket::OnSocketReady(unsigned flags)
}
if (flags & READ) {
- assert(input == nullptr || !fifo_buffer_is_full(input));
+ assert(!input.IsFull());
if (!ReadToBuffer() || !ResumeInput())
return false;
- if (input == nullptr || !fifo_buffer_is_full(input))
+ if (input.IsFull())
ScheduleRead();
}
diff --git a/src/event/BufferedSocket.hxx b/src/event/BufferedSocket.hxx
index 578000961..31d6c3c57 100644
--- a/src/event/BufferedSocket.hxx
+++ b/src/event/BufferedSocket.hxx
@@ -22,8 +22,12 @@
#include "check.h"
#include "SocketMonitor.hxx"
+#include "util/FifoBuffer.hxx"
#include "Compiler.h"
+#include <assert.h>
+#include <stdint.h>
+
struct fifo_buffer;
class Error;
@@ -31,16 +35,14 @@ class Error;
* A #SocketMonitor specialization that adds an input buffer.
*/
class BufferedSocket : protected SocketMonitor {
- fifo_buffer *input;
+ FifoBuffer<uint8_t, 8192> input;
public:
BufferedSocket(int _fd, EventLoop &_loop)
- :SocketMonitor(_fd, _loop), input(nullptr) {
+ :SocketMonitor(_fd, _loop) {
ScheduleRead();
}
- ~BufferedSocket();
-
using SocketMonitor::IsDefined;
using SocketMonitor::Close;
using SocketMonitor::Write;
@@ -67,7 +69,11 @@ protected:
* does not invalidate the pointer passed to OnSocketInput()
* yet.
*/
- void ConsumeInput(size_t nbytes);
+ void ConsumeInput(size_t nbytes) {
+ assert(IsDefined());
+
+ input.Consume(nbytes);
+ }
enum class InputResult {
/**
diff --git a/src/util/FifoBuffer.hxx b/src/util/FifoBuffer.hxx
new file mode 100644
index 000000000..75d2d2ef2
--- /dev/null
+++ b/src/util/FifoBuffer.hxx
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2003-2010 Max Kellermann <max@duempel.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef FIFO_BUFFER_HPP
+#define FIFO_BUFFER_HPP
+
+#include "WritableBuffer.hxx"
+
+#include <utility>
+#include <algorithm>
+
+#include <assert.h>
+#include <stddef.h>
+
+/**
+ * A first-in-first-out buffer: you can append data at the end, and
+ * read data from the beginning. This class automatically shifts the
+ * buffer as needed. It is not thread safe.
+ */
+template<class T, size_t size>
+class FifoBuffer {
+public:
+ typedef size_t size_type;
+
+public:
+ typedef WritableBuffer<T> Range;
+
+protected:
+ size_type head, tail;
+ T data[size];
+
+public:
+ constexpr
+ FifoBuffer():head(0), tail(0) {}
+
+protected:
+ void Shift() {
+ if (head == 0)
+ return;
+
+ assert(head <= size);
+ assert(tail <= size);
+ assert(tail >= head);
+
+ std::move(data + head, data + tail, data);
+
+ tail -= head;
+ head = 0;
+ }
+
+public:
+ void Clear() {
+ head = tail = 0;
+ }
+
+ bool IsEmpty() const {
+ return head == tail;
+ }
+
+ bool IsFull() const {
+ return head == 0 && tail == size;
+ }
+
+ /**
+ * Prepares writing. Returns a buffer range which may be written.
+ * When you are finished, call append().
+ */
+ Range Write() {
+ Shift();
+ return Range(data + tail, size - tail);
+ }
+
+ /**
+ * Expands the tail of the buffer, after data has been written to
+ * the buffer returned by write().
+ */
+ void Append(size_type n) {
+ assert(tail <= size);
+ assert(n <= size);
+ assert(tail + n <= size);
+
+ tail += n;
+ }
+
+ /**
+ * Return a buffer range which may be read. The buffer pointer is
+ * writable, to allow modifications while parsing.
+ */
+ Range Read() {
+ return Range(data + head, tail - head);
+ }
+
+ /**
+ * Marks a chunk as consumed.
+ */
+ void Consume(size_type n) {
+ assert(tail <= size);
+ assert(head <= tail);
+ assert(n <= tail);
+ assert(head + n <= tail);
+
+ head += n;
+ }
+};
+
+#endif
diff --git a/src/util/WritableBuffer.hxx b/src/util/WritableBuffer.hxx
new file mode 100644
index 000000000..4e529cfad
--- /dev/null
+++ b/src/util/WritableBuffer.hxx
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2013 Max Kellermann <max@duempel.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef WRITABLE_BUFFER_HPP
+#define WRITABLE_BUFFER_HPP
+
+#include "Compiler.h"
+
+#include <stddef.h>
+
+/**
+ * A reference to a memory area that is writable.
+ *
+ * @see ConstBuffer
+ */
+template<typename T>
+struct WritableBuffer {
+ typedef size_t size_type;
+ typedef T *pointer_type;
+ typedef const T *const_pointer_type;
+ typedef pointer_type iterator;
+ typedef const_pointer_type const_iterator;
+
+ pointer_type data;
+ size_type size;
+
+ WritableBuffer() = default;
+
+ constexpr WritableBuffer(pointer_type _data, size_type _size)
+ :data(_data), size(_size) {}
+
+ constexpr static WritableBuffer Null() {
+ return { nullptr, 0 };
+ }
+
+ constexpr bool IsNull() const {
+ return data == nullptr;
+ }
+
+ constexpr bool IsEmpty() const {
+ return size == 0;
+ }
+
+ constexpr iterator begin() const {
+ return data;
+ }
+
+ constexpr iterator end() const {
+ return data + size;
+ }
+
+ constexpr const_iterator cbegin() const {
+ return data;
+ }
+
+ constexpr const_iterator cend() const {
+ return data + size;
+ }
+};
+
+#endif
diff --git a/test/run_convert.cxx b/test/run_convert.cxx
index 939e279d0..ca55bcc0b 100644
--- a/test/run_convert.cxx
+++ b/test/run_convert.cxx
@@ -28,7 +28,7 @@
#include "AudioFormat.hxx"
#include "pcm/PcmConvert.hxx"
#include "ConfigGlobal.hxx"
-#include "util/fifo_buffer.h"
+#include "util/FifoBuffer.hxx"
#include "util/Error.hxx"
#include "stdbin.h"
@@ -59,8 +59,6 @@ int main(int argc, char **argv)
{
AudioFormat in_audio_format, out_audio_format;
const void *output;
- ssize_t nbytes;
- size_t length;
if (argc != 3) {
g_printerr("Usage: run_convert IN_FORMAT OUT_FORMAT <IN >OUT\n");
@@ -92,28 +90,31 @@ int main(int argc, char **argv)
PcmConvert state;
- struct fifo_buffer *buffer = fifo_buffer_new(4096);
+ FifoBuffer<uint8_t, 4096> buffer;
while (true) {
- void *p = fifo_buffer_write(buffer, &length);
- assert(p != NULL);
+ {
+ const auto dest = buffer.Write();
+ assert(!dest.IsEmpty());
- nbytes = read(0, p, length);
- if (nbytes <= 0)
- break;
+ ssize_t nbytes = read(0, dest.data, dest.size);
+ if (nbytes <= 0)
+ break;
- fifo_buffer_append(buffer, nbytes);
+ buffer.Append(nbytes);
+ }
- const void *src = fifo_buffer_read(buffer, &length);
- assert(src != NULL);
+ auto src = buffer.Read();
+ assert(!src.IsEmpty());
- length -= length % in_frame_size;
- if (length == 0)
+ src.size -= src.size % in_frame_size;
+ if (src.IsEmpty())
continue;
- fifo_buffer_consume(buffer, length);
+ buffer.Consume(src.size);
- output = state.Convert(in_audio_format, src, length,
+ size_t length;
+ output = state.Convert(in_audio_format, src.data, src.size,
out_audio_format, &length, error);
if (output == NULL) {
g_printerr("Failed to convert: %s\n", error.GetMessage());