diff options
Diffstat (limited to '')
27 files changed, 2205 insertions, 188 deletions
diff --git a/src/util/Domain.hxx b/src/util/Domain.hxx new file mode 100644 index 000000000..bbdbf8371 --- /dev/null +++ b/src/util/Domain.hxx @@ -0,0 +1,46 @@ +/* + * 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_DOMAIN_HXX +#define MPD_DOMAIN_HXX + +class Domain { + const char *const name; + +public: + constexpr explicit Domain(const char *_name) + :name(_name) {} + + Domain(const Domain &) = delete; + Domain &operator=(const Domain &) = delete; + + constexpr const char *GetName() const { + return name; + } + + bool operator==(const Domain &other) const { + return this == &other; + } + + bool operator!=(const Domain &other) const { + return !(*this == other); + } +}; + +#endif diff --git a/src/util/Error.cxx b/src/util/Error.cxx new file mode 100644 index 000000000..1e6bd9cff --- /dev/null +++ b/src/util/Error.cxx @@ -0,0 +1,114 @@ +/* + * 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. + */ + +#include "config.h" +#include "Error.hxx" +#include "Domain.hxx" + +#include <glib.h> + +#include <errno.h> +#include <stdarg.h> +#include <stdio.h> + +const Domain errno_domain("errno"); + +Error::~Error() {} + +void +Error::Set(const Domain &_domain, int _code, const char *_message) +{ + domain = &_domain; + code = _code; + message.assign(_message); +} + +void +Error::Format2(const Domain &_domain, int _code, const char *fmt, ...) +{ + char buffer[1024]; + va_list ap; + va_start(ap, fmt); + vsnprintf(buffer, sizeof(buffer), fmt, ap); + va_end(ap); + + Set(_domain, _code, buffer); +} + +void +Error::FormatPrefix(const char *fmt, ...) +{ + char buffer[1024]; + va_list ap; + va_start(ap, fmt); + vsnprintf(buffer, sizeof(buffer), fmt, ap); + va_end(ap); + + AddPrefix(buffer); +} + +void +Error::SetErrno(int e) +{ + Set(errno_domain, e, g_strerror(e)); +} + +void +Error::SetErrno() +{ + SetErrno(errno); +} + +void +Error::SetErrno(int e, const char *prefix) +{ + Format(errno_domain, e, "%s: %s", prefix, g_strerror(e)); +} + +void +Error::SetErrno(const char *prefix) +{ + SetErrno(errno, prefix); +} + +void +Error::FormatErrno(int e, const char *fmt, ...) +{ + char buffer[1024]; + va_list ap; + va_start(ap, fmt); + vsnprintf(buffer, sizeof(buffer), fmt, ap); + va_end(ap); + + SetErrno(e, buffer); +} + +void +Error::FormatErrno(const char *fmt, ...) +{ + const int e = errno; + + char buffer[1024]; + va_list ap; + va_start(ap, fmt); + vsnprintf(buffer, sizeof(buffer), fmt, ap); + va_end(ap); + + SetErrno(e, buffer); +} diff --git a/src/util/Error.hxx b/src/util/Error.hxx new file mode 100644 index 000000000..1fcf46061 --- /dev/null +++ b/src/util/Error.hxx @@ -0,0 +1,162 @@ +/* + * 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_ERROR_HXX +#define MPD_ERROR_HXX + +#include "check.h" +#include "gcc.h" + +#include <string> + +#include <assert.h> + +class Domain; + +extern const Domain errno_domain; + +#ifdef WIN32 +/* fuck WIN32! */ +#include <windows.h> +#define IgnoreError MPDIgnoreError +#undef GetMessage +#endif + +/** + * This class contains information about a runtime error. + */ +class Error { + const Domain *domain; + int code; + std::string message; + +public: + Error():domain(nullptr), code(0) {} + + Error(const Domain &_domain, int _code, const char *_message) + :domain(&_domain), code(_code), message(_message) {} + + Error(const Domain &_domain, const char *_message) + :domain(&_domain), code(0), message(_message) {} + + Error(Error &&other) + :domain(other.domain), code(other.code), + message(std::move(other.message)) {} + + ~Error(); + + Error(const Error &) = delete; + Error &operator=(const Error &) = delete; + + Error &operator=(Error &&other) { + domain = other.domain; + code = other.code; + std::swap(message, other.message); + return *this; + } + + bool IsDefined() const { + return domain != nullptr; + } + + void Clear() { + domain = nullptr; + } + + const Domain &GetDomain() const { + assert(IsDefined()); + + return *domain; + } + + bool IsDomain(const Domain &other) const { + return domain == &other; + } + + int GetCode() const { + assert(IsDefined()); + + return code; + } + + const char *GetMessage() const { + assert(IsDefined()); + + return message.c_str(); + } + + void Set(const Error &other) { + assert(!IsDefined()); + assert(other.IsDefined()); + + domain = other.domain; + code = other.code; + message = other.message; + } + + void Set(const Domain &_domain, int _code, const char *_message); + + void Set(const Domain &_domain, const char *_message) { + Set(_domain, 0, _message); + } + +private: + void Format2(const Domain &_domain, int _code, const char *fmt, ...); + +public: + template<typename... Args> + void Format(const Domain &_domain, int _code, + const char *fmt, Args&&... args) { + Format2(_domain, _code, fmt, std::forward<Args>(args)...); + } + + template<typename... Args> + void Format(const Domain &_domain, const char *fmt, Args&&... args) { + Format2(_domain, 0, fmt, std::forward<Args>(args)...); + } + + void AddPrefix(const char *prefix) { + message.insert(0, prefix); + } + + void FormatPrefix(const char *fmt, ...); + + void SetErrno(int e); + void SetErrno(); + void SetErrno(int e, const char *prefix); + void SetErrno(const char *prefix); + void FormatErrno(const char *prefix, ...); + void FormatErrno(int e, const char *prefix, ...); +}; + +/** + * Pass a temporary instance of this class to ignore errors. + */ +class IgnoreError final { + Error error; + +public: + operator Error &() { + assert(!error.IsDefined()); + + return error; + } +}; + +#endif diff --git a/src/util/HugeAllocator.cxx b/src/util/HugeAllocator.cxx new file mode 100644 index 000000000..d1c55c965 --- /dev/null +++ b/src/util/HugeAllocator.cxx @@ -0,0 +1,87 @@ +/* + * 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. + */ + +#include "HugeAllocator.hxx" + +#ifdef __linux__ +#include <sys/mman.h> +#include <unistd.h> +#else +#include <stdlib.h> +#endif + +#ifdef __linux__ + +/** + * Round up the parameter, make it page-aligned. + */ +gcc_const +static size_t +AlignToPageSize(size_t size) +{ + static const long page_size = sysconf(_SC_PAGESIZE); + if (page_size > 0) + return size; + + size_t ps(page_size); + return (size + ps - 1) / ps * ps; +} + +void * +HugeAllocate(size_t size) +{ + size = AlignToPageSize(size); + + constexpr int flags = MAP_ANONYMOUS|MAP_PRIVATE|MAP_NORESERVE; + void *p = mmap(nullptr, size, + PROT_READ|PROT_WRITE, flags, + -1, 0); + if (p == (void *)-1) + return nullptr; + +#ifdef MADV_HUGEPAGE + /* allow the Linux kernel to use "Huge Pages", which reduces page + table overhead for this big chunk of data */ + madvise(p, size, MADV_HUGEPAGE); +#endif + +#ifdef MADV_DONTFORK + /* just in case MPD needs to fork, don't copy this allocation + to the child process, to reduce overhead */ + madvise(p, size, MADV_DONTFORK); +#endif + + return p; +} + +void +HugeFree(void *p, size_t size) +{ + munmap(p, AlignToPageSize(size)); +} + +void +HugeDiscard(void *p, size_t size) +{ +#ifdef MADV_DONTNEED + madvise(p, AlignToPageSize(size), MADV_DONTNEED); +#endif +} + +#endif diff --git a/src/util/HugeAllocator.hxx b/src/util/HugeAllocator.hxx new file mode 100644 index 000000000..01c92cd43 --- /dev/null +++ b/src/util/HugeAllocator.hxx @@ -0,0 +1,82 @@ +/* + * 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_HUGE_ALLOCATOR_HXX +#define MPD_HUGE_ALLOCATOR_HXX + +#include "gcc.h" + +#include <stddef.h> + +#ifdef __linux__ + +/** + * Allocate a huge amount of memory. This will be done in a way that + * allows giving the memory back to the kernel as soon as we don't + * need it anymore. On the downside, this call is expensive. + */ +gcc_malloc +void * +HugeAllocate(size_t size); + +/** + * @param p an allocation returned by HugeAllocate() + * @param size the allocation's size as passed to HugeAllocate() + */ +void +HugeFree(void *p, size_t size); + +/** + * Discard any data stored in the allocation and give the memory back + * to the kernel. After returning, the allocation still exists and + * can be reused at any time, but its contents are undefined. + * + * @param p an allocation returned by HugeAllocate() + * @param size the allocation's size as passed to HugeAllocate() + */ +void +HugeDiscard(void *p, size_t size); + +#else + +/* not Linux: fall back to standard C calls */ + +#include <stdlib.h> + +gcc_malloc +static inline void * +HugeAllocate(size_t size) +{ + return malloc(size); +} + +static inline void +HugeFree(void *p, size_t) +{ + free(p); +} + +static inline void +HugeDiscard(void *, size_t) +{ +} + +#endif + +#endif diff --git a/src/util/LazyRandomEngine.cxx b/src/util/LazyRandomEngine.cxx new file mode 100644 index 000000000..0f90ebb2e --- /dev/null +++ b/src/util/LazyRandomEngine.cxx @@ -0,0 +1,31 @@ +/* + * 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. + */ + +#include "config.h" +#include "LazyRandomEngine.hxx" + +void +LazyRandomEngine::AutoCreate() +{ + if (engine != nullptr) + return; + + std::random_device rd; + engine = new std::mt19937(rd()); +} diff --git a/src/util/LazyRandomEngine.hxx b/src/util/LazyRandomEngine.hxx new file mode 100644 index 000000000..8afe1d1c0 --- /dev/null +++ b/src/util/LazyRandomEngine.hxx @@ -0,0 +1,67 @@ +/* + * 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_LAZY_RANDOM_ENGINE_HXX +#define MPD_LAZY_RANDOM_ENGINE_HXX + +#include "check.h" + +#include <random> + +#include <assert.h> + +/** + * A random engine that will be created and seeded on demand. + */ +class LazyRandomEngine { + std::mt19937 *engine; + +public: + typedef std::mt19937::result_type result_type; + + LazyRandomEngine():engine(nullptr) {} + ~LazyRandomEngine() { + delete engine; + } + + LazyRandomEngine(const LazyRandomEngine &other) = delete; + LazyRandomEngine &operator=(const LazyRandomEngine &other) = delete; + + /** + * Create and seed the real engine. Call this before any + * other method. + */ + void AutoCreate(); + + result_type min() const { + return engine->min(); + } + + result_type max() const { + return engine->max(); + } + + result_type operator()() { + assert(engine != nullptr); + + return engine->operator()(); + } +}; + +#endif diff --git a/src/util/Manual.hxx b/src/util/Manual.hxx new file mode 100644 index 000000000..802695224 --- /dev/null +++ b/src/util/Manual.hxx @@ -0,0 +1,121 @@ +/* + * 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 MPD_MANUAL_HXX +#define MPD_MANUAL_HXX + +#include "gcc.h" + +#include <new> +#include <utility> + +#if !defined(__clang__) && __GNUC__ && !GCC_CHECK_VERSION(4,8) +#include <type_traits> +#endif + +#include <assert.h> + +#if defined(__clang__) || GCC_CHECK_VERSION(4,7) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wstrict-aliasing" +#endif + +/** + * Container for an object that gets constructed and destructed + * manually. The object is constructed in-place, and therefore + * without allocation overhead. It can be constructed and destructed + * repeatedly. + */ +template<class T> +class Manual { +#if !defined(__clang__) && __GNUC__ && !GCC_CHECK_VERSION(4,8) + /* no alignas() on gcc < 4.8: apply worst-case fallback */ + __attribute__((aligned(8))) +#else + alignas(T) +#endif + char data[sizeof(T)]; + +#ifndef NDEBUG + bool initialized; +#endif + +public: +#ifndef NDEBUG + Manual():initialized(false) {} + ~Manual() { + assert(!initialized); + } +#endif + + template<typename... Args> + void Construct(Args&&... args) { + assert(!initialized); + + void *p = data; + new(p) T(std::forward<Args>(args)...); + +#ifndef NDEBUG + initialized = true; +#endif + } + + void Destruct() { + assert(initialized); + + T *t = (T *)data; + t->T::~T(); + +#ifndef NDEBUG + initialized = false; +#endif + } + + operator T &() { + return *(T *)data; + } + + operator const T &() const { + return *(const T *)data; + } + + T *operator->() { + return (T *)data; + } + + const T *operator->() const { + return (T *)data; + } +}; + +#if defined(__clang__) || GCC_VERSION >= 40700 +#pragma GCC diagnostic pop +#endif + +#endif diff --git a/src/util/PeakBuffer.cxx b/src/util/PeakBuffer.cxx new file mode 100644 index 000000000..a3659b8f4 --- /dev/null +++ b/src/util/PeakBuffer.cxx @@ -0,0 +1,143 @@ +/* + * 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. + */ + +#include "PeakBuffer.hxx" +#include "HugeAllocator.hxx" +#include "fifo_buffer.h" + +#include <algorithm> + +#include <assert.h> +#include <stdint.h> +#include <string.h> + +PeakBuffer::~PeakBuffer() +{ + if (normal_buffer != nullptr) + fifo_buffer_free(normal_buffer); + + if (peak_buffer != nullptr) + HugeFree(peak_buffer, peak_size); +} + +bool +PeakBuffer::IsEmpty() const +{ + return (normal_buffer == nullptr || + fifo_buffer_is_empty(normal_buffer)) && + (peak_buffer == nullptr || + fifo_buffer_is_empty(peak_buffer)); +} + +const void * +PeakBuffer::Read(size_t *length_r) const +{ + if (normal_buffer != nullptr) { + const void *p = fifo_buffer_read(normal_buffer, length_r); + if (p != nullptr) + return p; + } + + if (peak_buffer != nullptr) { + const void *p = fifo_buffer_read(peak_buffer, length_r); + if (p != nullptr) + return p; + } + + return nullptr; +} + +void +PeakBuffer::Consume(size_t length) +{ + if (normal_buffer != nullptr && !fifo_buffer_is_empty(normal_buffer)) { + fifo_buffer_consume(normal_buffer, length); + return; + } + + if (peak_buffer != nullptr && !fifo_buffer_is_empty(peak_buffer)) { + fifo_buffer_consume(peak_buffer, length); + if (fifo_buffer_is_empty(peak_buffer)) { + HugeFree(peak_buffer, peak_size); + peak_buffer = nullptr; + } + + return; + } +} + +static size_t +AppendTo(fifo_buffer *buffer, const void *data, size_t length) +{ + assert(data != nullptr); + assert(length > 0); + + size_t total = 0; + + do { + size_t max_length; + void *p = fifo_buffer_write(buffer, &max_length); + if (p == nullptr) + break; + + const size_t nbytes = std::min(length, max_length); + memcpy(p, data, nbytes); + fifo_buffer_append(buffer, nbytes); + + data = (const uint8_t *)data + nbytes; + length -= nbytes; + total += nbytes; + } while (length > 0); + + return total; +} + +bool +PeakBuffer::Append(const void *data, size_t length) +{ + if (length == 0) + return true; + + if (peak_buffer != nullptr && !fifo_buffer_is_empty(peak_buffer)) { + size_t nbytes = AppendTo(peak_buffer, data, length); + return nbytes == length; + } + + if (normal_buffer == nullptr) + normal_buffer = fifo_buffer_new(normal_size); + + size_t nbytes = AppendTo(normal_buffer, data, length); + if (nbytes > 0) { + data = (const uint8_t *)data + nbytes; + length -= nbytes; + if (length == 0) + return true; + } + + if (peak_buffer == nullptr && peak_size > 0) { + peak_buffer = (fifo_buffer *)HugeAllocate(peak_size); + if (peak_buffer == nullptr) + return false; + + fifo_buffer_init(peak_buffer, peak_size); + } + + nbytes = AppendTo(peak_buffer, data, length); + return nbytes == length; +} diff --git a/src/util/PeakBuffer.hxx b/src/util/PeakBuffer.hxx new file mode 100644 index 000000000..0fbba8d77 --- /dev/null +++ b/src/util/PeakBuffer.hxx @@ -0,0 +1,66 @@ +/* + * 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_PEAK_BUFFER_HXX +#define MPD_PEAK_BUFFER_HXX + +#include "gcc.h" + +#include <stddef.h> + +struct fifo_buffer; + +/** + * A FIFO-like buffer that will allocate more memory on demand to + * allow large peaks. This second buffer will be given back to the + * kernel when it has been consumed. + */ +class PeakBuffer { + size_t normal_size, peak_size; + + fifo_buffer *normal_buffer, *peak_buffer; + +public: + PeakBuffer(size_t _normal_size, size_t _peak_size) + :normal_size(_normal_size), peak_size(_peak_size), + normal_buffer(nullptr), peak_buffer(nullptr) {} + + PeakBuffer(PeakBuffer &&other) + :normal_size(other.normal_size), peak_size(other.peak_size), + normal_buffer(other.normal_buffer), + peak_buffer(other.peak_buffer) { + other.normal_buffer = nullptr; + other.peak_buffer = nullptr; + } + + ~PeakBuffer(); + + PeakBuffer(const PeakBuffer &) = delete; + PeakBuffer &operator=(const PeakBuffer &) = delete; + + gcc_pure + bool IsEmpty() const; + + const void *Read(size_t *length_r) const; + void Consume(size_t length); + + bool Append(const void *data, size_t length); +}; + +#endif diff --git a/src/util/RefCount.hxx b/src/util/RefCount.hxx new file mode 100644 index 000000000..9a45a585b --- /dev/null +++ b/src/util/RefCount.hxx @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2003-2013 The Music Player Daemon Project + * http://www.musicpd.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. + */ + +/** \file + * + * A very simple reference counting library. + */ + +#ifndef MPD_REFCOUNT_HXX +#define MPD_REFCOUNT_HXX + +#include <atomic> + +class RefCount { + std::atomic_uint n; + +public: + constexpr RefCount():n(1) {} + + void Increment() { + ++n; + } + + /** + * @return true if the number of references has been dropped to 0 + */ + bool Decrement() { + return --n == 0; + } +}; + +#endif diff --git a/src/util/ReusableArray.hxx b/src/util/ReusableArray.hxx new file mode 100644 index 000000000..30b1a9cd9 --- /dev/null +++ b/src/util/ReusableArray.hxx @@ -0,0 +1,88 @@ +/* + * 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 REUSABLE_ARRAY_HXX +#define REUSABLE_ARRAY_HXX + +#include <stddef.h> + +#include "gcc.h" + +/** + * Manager for a temporary array which grows as needed. This attempts + * to reduce the number of consecutive heap allocations and + * deallocations. + * + * @param T the array element type + * @param M always allocate multiples of this number; must be a power of 2 + */ +template<typename T, size_t M=1> +class ReusableArray { + T *buffer; + size_t capacity; + +public: + ReusableArray():buffer(nullptr), capacity(0) {} + + ReusableArray(const ReusableArray &other) = delete; + ReusableArray &operator=(const ReusableArray &other) = delete; + + ~ReusableArray() { + delete[] buffer; + } + + /** + * Free resources allocated by this object. This invalidates + * the buffer returned by Get(). + */ + void Clear() { + delete[] buffer; + buffer = nullptr; + capacity = 0; + } + + /** + * Get the buffer, and guarantee a minimum size. This buffer + * becomes invalid with the next Get() call. + */ + gcc_malloc + T *Get(size_t size) { + if (gcc_unlikely(size > capacity)) { + /* too small: grow */ + delete[] buffer; + + capacity = ((size - 1) | (M - 1)) + 1; + buffer = new T[capacity]; + } + + return buffer; + } +}; + +#endif diff --git a/src/util/SliceBuffer.hxx b/src/util/SliceBuffer.hxx new file mode 100644 index 000000000..c61f164f4 --- /dev/null +++ b/src/util/SliceBuffer.hxx @@ -0,0 +1,161 @@ +/* + * 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 "HugeAllocator.hxx" +#include "gcc.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 that are initialized. This is used to + * avoid page faulting on the new allocation, so the kernel + * does not need to reserve physical memory pages. + */ + unsigned n_initialized; + + /** + * The number of slices currently allocated. + */ + unsigned n_allocated; + + Slice *const data; + + /** + * Pointer to the first free element in the chain. + */ + Slice *available; + + size_t CalcAllocationSize() const { + return n_max * sizeof(Slice); + } + +public: + SliceBuffer(unsigned _count) + :n_max(_count), n_initialized(0), n_allocated(0), + data((Slice *)HugeAllocate(CalcAllocationSize())), + available(nullptr) { + assert(n_max > 0); + } + + ~SliceBuffer() { + /* all slices must be freed explicitly, and this + assertion checks for leaks */ + assert(n_allocated == 0); + + HugeFree(data, CalcAllocationSize()); + } + + SliceBuffer(const SliceBuffer &other) = delete; + SliceBuffer &operator=(const SliceBuffer &other) = delete; + + /** + * @return true if buffer allocation (by the constructor) has failed + */ + bool IsOOM() { + return data == nullptr; + } + + 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_initialized <= n_max); + assert(n_allocated <= n_initialized); + + if (available == nullptr) { + if (n_initialized == n_max) { + /* out of (internal) memory, buffer is full */ + assert(n_allocated == n_max); + return nullptr; + } + + available = &data[n_initialized++]; + available->next = 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_initialized <= n_max); + assert(n_allocated > 0); + assert(n_allocated <= n_initialized); + + 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; + + /* give memory back to the kernel when the last slice + was freed */ + if (n_allocated == 0) { + HugeDiscard(data, CalcAllocationSize()); + n_initialized = 0; + available = nullptr; + } + } +}; + +#endif diff --git a/src/util/StringUtil.cxx b/src/util/StringUtil.cxx new file mode 100644 index 000000000..87d032735 --- /dev/null +++ b/src/util/StringUtil.cxx @@ -0,0 +1,46 @@ +/* + * 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. + */ + +#include "StringUtil.hxx" + +#include <glib.h> + +#include <assert.h> + +const char * +strchug_fast_c(const char *p) +{ + while (*p != 0 && g_ascii_isspace(*p)) + ++p; + + return p; +} + +bool +string_array_contains(const char *const* haystack, const char *needle) +{ + assert(haystack != nullptr); + assert(needle != nullptr); + + for (; *haystack != nullptr; ++haystack) + if (g_ascii_strcasecmp(*haystack, needle) == 0) + return true; + + return false; +} diff --git a/src/util/StringUtil.hxx b/src/util/StringUtil.hxx new file mode 100644 index 000000000..6eeca893d --- /dev/null +++ b/src/util/StringUtil.hxx @@ -0,0 +1,58 @@ +/* + * 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_STRING_UTIL_HXX +#define MPD_STRING_UTIL_HXX + +#include "gcc.h" + +/** + * Returns a pointer to the first non-whitespace character in the + * string, or to the end of the string. + * + * This is a faster version of g_strchug(), because it does not move + * data. + */ +gcc_pure +const char * +strchug_fast_c(const char *p); + +/** + * Same as strchug_fast_c(), but works with a writable pointer. + */ +gcc_pure +static inline char * +strchug_fast(char *p) +{ + return const_cast<char *>(strchug_fast_c(p)); +} + +/** + * Checks whether a string array contains the specified string. + * + * @param haystack a NULL terminated list of strings + * @param needle the string to search for; the comparison is + * case-insensitive for ASCII characters + * @return true if found + */ +gcc_pure +bool +string_array_contains(const char *const* haystack, const char *needle); + +#endif diff --git a/src/util/Tokenizer.cxx b/src/util/Tokenizer.cxx new file mode 100644 index 000000000..726da0dd6 --- /dev/null +++ b/src/util/Tokenizer.cxx @@ -0,0 +1,193 @@ +/* + * 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. + */ + +#include "config.h" +#include "Tokenizer.hxx" +#include "StringUtil.hxx" +#include "Error.hxx" +#include "Domain.hxx" + +#include <glib.h> + +#include <assert.h> +#include <string.h> + +static constexpr Domain tokenizer_domain("tokenizer"); + +static inline bool +valid_word_first_char(char ch) +{ + return g_ascii_isalpha(ch); +} + +static inline bool +valid_word_char(char ch) +{ + return g_ascii_isalnum(ch) || ch == '_'; +} + +char * +Tokenizer::NextWord(Error &error) +{ + char *const word = input; + + if (*input == 0) + return nullptr; + + /* check the first character */ + + if (!valid_word_first_char(*input)) { + error.Set(tokenizer_domain, "Letter expected"); + return nullptr; + } + + /* now iterate over the other characters until we find a + whitespace or end-of-string */ + + while (*++input != 0) { + if (g_ascii_isspace(*input)) { + /* a whitespace: the word ends here */ + *input = 0; + /* skip all following spaces, too */ + input = strchug_fast(input + 1); + break; + } + + if (!valid_word_char(*input)) { + error.Set(tokenizer_domain, "Invalid word character"); + return nullptr; + } + } + + /* end of string: the string is already null-terminated + here */ + + return word; +} + +static inline bool +valid_unquoted_char(char ch) +{ + return (unsigned char)ch > 0x20 && ch != '"' && ch != '\''; +} + +char * +Tokenizer::NextUnquoted(Error &error) +{ + char *const word = input; + + if (*input == 0) + return nullptr; + + /* check the first character */ + + if (!valid_unquoted_char(*input)) { + error.Set(tokenizer_domain, "Invalid unquoted character"); + return nullptr; + } + + /* now iterate over the other characters until we find a + whitespace or end-of-string */ + + while (*++input != 0) { + if (g_ascii_isspace(*input)) { + /* a whitespace: the word ends here */ + *input = 0; + /* skip all following spaces, too */ + input = strchug_fast(input + 1); + break; + } + + if (!valid_unquoted_char(*input)) { + error.Set(tokenizer_domain, + "Invalid unquoted character"); + return nullptr; + } + } + + /* end of string: the string is already null-terminated + here */ + + return word; +} + +char * +Tokenizer::NextString(Error &error) +{ + char *const word = input, *dest = input; + + if (*input == 0) + /* end of line */ + return nullptr; + + /* check for the opening " */ + + if (*input != '"') { + error.Set(tokenizer_domain, "'\"' expected"); + return nullptr; + } + + ++input; + + /* copy all characters */ + + while (*input != '"') { + if (*input == '\\') + /* the backslash escapes the following + character */ + ++input; + + if (*input == 0) { + /* return input-1 so the caller can see the + difference between "end of line" and + "error" */ + --input; + error.Set(tokenizer_domain, "Missing closing '\"'"); + return nullptr; + } + + /* copy one character */ + *dest++ = *input++; + } + + /* the following character must be a whitespace (or end of + line) */ + + ++input; + if (*input != 0 && !g_ascii_isspace(*input)) { + error.Set(tokenizer_domain, + "Space expected after closing '\"'"); + return nullptr; + } + + /* finish the string and return it */ + + *dest = 0; + input = strchug_fast(input); + return word; +} + +char * +Tokenizer::NextParam(Error &error) +{ + if (*input == '"') + return NextString(error); + else + return NextUnquoted(error); +} diff --git a/src/util/Tokenizer.hxx b/src/util/Tokenizer.hxx new file mode 100644 index 000000000..a689dc31d --- /dev/null +++ b/src/util/Tokenizer.hxx @@ -0,0 +1,99 @@ +/* + * 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_TOKENIZER_HXX +#define MPD_TOKENIZER_HXX + +class Error; + +class Tokenizer { + char *input; + +public: + /** + * @param _input the input string; the contents will be + * modified by this class + */ + constexpr Tokenizer(char *_input):input(_input) {} + + Tokenizer(const Tokenizer &) = delete; + Tokenizer &operator=(const Tokenizer &) = delete; + + char *Rest() { + return input; + } + + char CurrentChar() const { + return *input; + } + + bool IsEnd() const { + return CurrentChar() == 0; + } + + /** + * Reads the next word. + * + * @param error if this function returns nullptr and + * **input_p!=0, it provides an #Error object in + * this argument + * @return a pointer to the null-terminated word, or nullptr + * on error or end of line + */ + char *NextWord(Error &error); + + /** + * Reads the next unquoted word from the input string. + * + * @param error_r if this function returns nullptr and **input_p!=0, it + * provides an #Error object in this argument + * @return a pointer to the null-terminated word, or nullptr + * on error or end of line + */ + char *NextUnquoted(Error &error); + + /** + * Reads the next quoted string from the input string. A backslash + * escapes the following character. This function modifies the input + * string. + * + * @param input_p the input string; this function returns a pointer to + * the first non-whitespace character of the following token + * @param error_r if this function returns nullptr and **input_p!=0, it + * provides an #Error object in this argument + * @return a pointer to the null-terminated string, or nullptr on error + * or end of line + */ + char *NextString(Error &error); + + /** + * Reads the next unquoted word or quoted string from the + * input. This is a wrapper for NextUnquoted() and + * NextString(). + * + * @param error_r if this function returns nullptr and + * **input_p!=0, it provides an #Error object in + * this argument + * @return a pointer to the null-terminated string, or nullptr + * on error or end of line + */ + char *NextParam(Error &error); +}; + +#endif diff --git a/src/util/UriUtil.cxx b/src/util/UriUtil.cxx new file mode 100644 index 000000000..4b0cec11b --- /dev/null +++ b/src/util/UriUtil.cxx @@ -0,0 +1,113 @@ +/* + * 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. + */ + +#include "UriUtil.hxx" + +#include <glib.h> + +#include <assert.h> +#include <string.h> + +bool uri_has_scheme(const char *uri) +{ + return strstr(uri, "://") != nullptr; +} + +/* suffixes should be ascii only characters */ +const char * +uri_get_suffix(const char *uri) +{ + const char *suffix = strrchr(uri, '.'); + if (suffix == nullptr) + return nullptr; + + ++suffix; + + if (strpbrk(suffix, "/\\") != nullptr) + return nullptr; + + return suffix; +} + +static const char * +verify_uri_segment(const char *p) +{ + const char *q; + + unsigned dots = 0; + while (*p == '.') { + ++p; + ++dots; + } + + if (dots <= 2 && (*p == 0 || *p == '/')) + return nullptr; + + q = strchr(p + 1, '/'); + return q != nullptr ? q : ""; +} + +bool +uri_safe_local(const char *uri) +{ + while (true) { + uri = verify_uri_segment(uri); + if (uri == nullptr) + return false; + + if (*uri == 0) + return true; + + assert(*uri == '/'); + + ++uri; + } +} + +char * +uri_remove_auth(const char *uri) +{ + const char *auth, *slash, *at; + char *p; + + if (strncmp(uri, "http://", 7) == 0) + auth = uri + 7; + else if (strncmp(uri, "https://", 8) == 0) + auth = uri + 8; + else + /* unrecognized URI */ + return nullptr; + + slash = strchr(auth, '/'); + if (slash == nullptr) + slash = auth + strlen(auth); + + at = (const char *)memchr(auth, '@', slash - auth); + if (at == nullptr) + /* no auth info present, do nothing */ + return nullptr; + + /* duplicate the full URI and then delete the auth + information */ + p = g_strdup(uri); + memmove(p + (auth - uri), p + (at + 1 - uri), + strlen(at)); + + return p; +} diff --git a/src/util/UriUtil.hxx b/src/util/UriUtil.hxx new file mode 100644 index 000000000..1d288ca1d --- /dev/null +++ b/src/util/UriUtil.hxx @@ -0,0 +1,58 @@ +/* + * 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_URI_UTIL_HXX +#define MPD_URI_UTIL_HXX + +#include "gcc.h" + +/** + * Checks whether the specified URI has a scheme in the form + * "scheme://". + */ +gcc_pure +bool uri_has_scheme(const char *uri); + +gcc_pure +const char * +uri_get_suffix(const char *uri); + +/** + * Returns true if this is a safe "local" URI: + * + * - non-empty + * - does not begin or end with a slash + * - no double slashes + * - no path component begins with a dot + */ +gcc_pure +bool +uri_safe_local(const char *uri); + +/** + * Removes HTTP username and password from the URI. This may be + * useful for displaying an URI without disclosing secrets. Returns + * NULL if nothing needs to be removed, or if the URI is not + * recognized. + */ +gcc_malloc +char * +uri_remove_auth(const char *uri); + +#endif diff --git a/src/util/bit_reverse.h b/src/util/bit_reverse.h index e44693b1d..54cb789bb 100644 --- a/src/util/bit_reverse.h +++ b/src/util/bit_reverse.h @@ -20,12 +20,13 @@ #ifndef MPD_BIT_REVERSE_H #define MPD_BIT_REVERSE_H -#include <glib.h> +#include "gcc.h" + #include <stdint.h> extern const uint8_t bit_reverse_table[256]; -G_GNUC_CONST +gcc_const static inline uint8_t bit_reverse(uint8_t x) { diff --git a/src/util/fifo_buffer.c b/src/util/fifo_buffer.c new file mode 100644 index 000000000..162ddf946 --- /dev/null +++ b/src/util/fifo_buffer.c @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2003-2011 The Music Player Daemon Project + * http://www.musicpd.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. + */ + +#include "config.h" +#include "fifo_buffer.h" + +#include <glib.h> + +#include <assert.h> +#include <string.h> + +struct fifo_buffer { + size_t size, start, end; + unsigned char buffer[sizeof(size_t)]; +}; + +struct fifo_buffer * +fifo_buffer_new(size_t size) +{ + struct fifo_buffer *buffer; + + assert(size > 0); + + buffer = (struct fifo_buffer *)g_malloc(sizeof(*buffer) - + sizeof(buffer->buffer) + size); + + buffer->size = size; + buffer->start = 0; + buffer->end = 0; + + return buffer; +} + +void +fifo_buffer_init(struct fifo_buffer *buffer, size_t size) +{ + buffer->size = size - (sizeof(*buffer) - sizeof(buffer->buffer)); + buffer->start = 0; + buffer->end = 0; +} + +static void +fifo_buffer_move(struct fifo_buffer *buffer); + +struct fifo_buffer * +fifo_buffer_realloc(struct fifo_buffer *buffer, size_t new_size) +{ + if (buffer == NULL) + return new_size > 0 + ? fifo_buffer_new(new_size) + : NULL; + + /* existing data must fit in new size */ + assert(new_size >= buffer->end - buffer->start); + + if (new_size == 0) { + fifo_buffer_free(buffer); + return NULL; + } + + /* compress the buffer when we're shrinking and the tail of + the buffer would exceed the new size */ + if (buffer->end > new_size) + fifo_buffer_move(buffer); + + /* existing data must fit in new size: second check */ + assert(buffer->end <= new_size); + + buffer = g_realloc(buffer, sizeof(*buffer) - sizeof(buffer->buffer) + + new_size); + buffer->size = new_size; + return buffer; +} + +void +fifo_buffer_free(struct fifo_buffer *buffer) +{ + assert(buffer != NULL); + + g_free(buffer); +} + +size_t +fifo_buffer_capacity(const struct fifo_buffer *buffer) +{ + assert(buffer != NULL); + + return buffer->size; +} + +size_t +fifo_buffer_available(const struct fifo_buffer *buffer) +{ + assert(buffer != NULL); + + return buffer->end - buffer->start; +} + +void +fifo_buffer_clear(struct fifo_buffer *buffer) +{ + assert(buffer != NULL); + + buffer->start = 0; + buffer->end = 0; +} + +const void * +fifo_buffer_read(const struct fifo_buffer *buffer, size_t *length_r) +{ + assert(buffer != NULL); + assert(buffer->end >= buffer->start); + assert(length_r != NULL); + + if (buffer->start == buffer->end) + /* the buffer is empty */ + return NULL; + + *length_r = buffer->end - buffer->start; + return buffer->buffer + buffer->start; +} + +void +fifo_buffer_consume(struct fifo_buffer *buffer, size_t length) +{ + assert(buffer != NULL); + assert(buffer->end >= buffer->start); + assert(buffer->start + length <= buffer->end); + + buffer->start += length; +} + +/** + * Move data to the beginning of the buffer, to make room at the end. + */ +static void +fifo_buffer_move(struct fifo_buffer *buffer) +{ + if (buffer->start == 0) + return; + + if (buffer->end > buffer->start) + memmove(buffer->buffer, + buffer->buffer + buffer->start, + buffer->end - buffer->start); + + buffer->end -= buffer->start; + buffer->start = 0; +} + +void * +fifo_buffer_write(struct fifo_buffer *buffer, size_t *max_length_r) +{ + assert(buffer != NULL); + assert(buffer->end <= buffer->size); + assert(max_length_r != NULL); + + if (buffer->end == buffer->size) { + fifo_buffer_move(buffer); + if (buffer->end == buffer->size) + return NULL; + } else if (buffer->start > 0 && buffer->start == buffer->end) { + buffer->start = 0; + buffer->end = 0; + } + + *max_length_r = buffer->size - buffer->end; + return buffer->buffer + buffer->end; +} + +void +fifo_buffer_append(struct fifo_buffer *buffer, size_t length) +{ + assert(buffer != NULL); + assert(buffer->end >= buffer->start); + assert(buffer->end + length <= buffer->size); + + buffer->end += length; +} + +bool +fifo_buffer_is_empty(struct fifo_buffer *buffer) +{ + return buffer->start == buffer->end; +} + +bool +fifo_buffer_is_full(struct fifo_buffer *buffer) +{ + return buffer->start == 0 && buffer->end == buffer->size; +} diff --git a/src/util/fifo_buffer.h b/src/util/fifo_buffer.h new file mode 100644 index 000000000..ccea97d86 --- /dev/null +++ b/src/util/fifo_buffer.h @@ -0,0 +1,164 @@ +/* + * Copyright (C) 2003-2011 The Music Player Daemon Project + * http://www.musicpd.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. + */ + +/** \file + * + * This is a general purpose FIFO buffer library. You may append data + * at the end, while another instance reads data from the beginning. + * It is optimized for zero-copy usage: you get pointers to the real + * buffer, where you may operate on. + * + * This library is not thread safe. + */ + +#ifndef MPD_FIFO_BUFFER_H +#define MPD_FIFO_BUFFER_H + +#include <stdbool.h> +#include <stddef.h> + +struct fifo_buffer; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Creates a new #fifo_buffer object. Free this object with + * fifo_buffer_free(). + * + * @param size the size of the buffer in bytes + * @return the new #fifo_buffer object + */ +struct fifo_buffer * +fifo_buffer_new(size_t size); + +void +fifo_buffer_init(struct fifo_buffer *buffer, size_t size); + +/** + * Change the capacity of the #fifo_buffer, while preserving existing + * data. + * + * @param buffer the old buffer, may be NULL + * @param new_size the requested new size of the #fifo_buffer; must + * not be smaller than the data which is stored in the old buffer + * @return the new buffer, may be NULL if the requested new size is 0 + */ +struct fifo_buffer * +fifo_buffer_realloc(struct fifo_buffer *buffer, size_t new_size); + +/** + * Frees the resources consumed by this #fifo_buffer object. + */ +void +fifo_buffer_free(struct fifo_buffer *buffer); + +/** + * Return the capacity of the buffer, i.e. the size that was passed to + * fifo_buffer_new(). + */ +size_t +fifo_buffer_capacity(const struct fifo_buffer *buffer); + +/** + * Return the number of bytes currently stored in the buffer. + */ +size_t +fifo_buffer_available(const struct fifo_buffer *buffer); + +/** + * Clears all data currently in this #fifo_buffer object. This does + * not overwrite the actuall buffer; it just resets the internal + * pointers. + */ +void +fifo_buffer_clear(struct fifo_buffer *buffer); + +/** + * Reads from the beginning of the buffer. To remove consumed data + * from the buffer, call fifo_buffer_consume(). + * + * @param buffer the #fifo_buffer object + * @param length_r the maximum amount to read is returned here + * @return a pointer to the beginning of the buffer, or NULL if the + * buffer is empty + */ +const void * +fifo_buffer_read(const struct fifo_buffer *buffer, size_t *length_r); + +/** + * Marks data at the beginning of the buffer as "consumed". + * + * @param buffer the #fifo_buffer object + * @param length the number of bytes which were consumed + */ +void +fifo_buffer_consume(struct fifo_buffer *buffer, size_t length); + +/** + * Prepares writing to the buffer. This returns a buffer which you + * can write to. To commit the write operation, call + * fifo_buffer_append(). + * + * @param buffer the #fifo_buffer object + * @param max_length_r the maximum amount to write is returned here + * @return a pointer to the end of the buffer, or NULL if the buffer + * is already full + */ +void * +fifo_buffer_write(struct fifo_buffer *buffer, size_t *max_length_r); + +/** + * Commits the write operation initiated by fifo_buffer_write(). + * + * @param buffer the #fifo_buffer object + * @param length the number of bytes which were written + */ +void +fifo_buffer_append(struct fifo_buffer *buffer, size_t length); + +/** + * Checks if the buffer is empty. + */ +bool +fifo_buffer_is_empty(struct fifo_buffer *buffer); + +/** + * Checks if the buffer is full. + */ +bool +fifo_buffer_is_full(struct fifo_buffer *buffer); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/growing_fifo.c b/src/util/growing_fifo.c index 88431f60e..88431f60e 100644 --- a/src/growing_fifo.c +++ b/src/util/growing_fifo.c diff --git a/src/growing_fifo.h b/src/util/growing_fifo.h index 723c3b3ff..723c3b3ff 100644 --- a/src/growing_fifo.h +++ b/src/util/growing_fifo.h diff --git a/src/util/list.h b/src/util/list.h index fdab47675..73d99befa 100644 --- a/src/util/list.h +++ b/src/util/list.h @@ -25,8 +25,6 @@ #ifndef _LINUX_LIST_H #define _LINUX_LIST_H -#include <glib.h> - #ifdef __clang__ /* allow typeof() */ #pragma GCC diagnostic ignored "-Wlanguage-extension-token" @@ -40,15 +38,15 @@ * */ #define container_of(ptr, type, member) \ - (&G_STRUCT_MEMBER(type, ptr, -G_STRUCT_OFFSET(type, member))) + ((type *)((uint8_t *)ptr - offsetof(type, member))) /* * These are non-NULL pointers that will result in page faults * under normal circumstances, used to verify that nobody uses * non-initialized list entries. */ -#define LIST_POISON1 ((void *) 0x00100100) -#define LIST_POISON2 ((void *) 0x00200200) +#define LIST_POISON1 ((struct list_head *)(void *) 0x00100100) +#define LIST_POISON2 ((struct list_head *)(void *) 0x00200200) /* * Simple doubly linked list implementation. @@ -82,46 +80,47 @@ static inline void INIT_LIST_HEAD(struct list_head *list) * the prev/next entries already! */ #ifndef CONFIG_DEBUG_LIST -static inline void __list_add(struct list_head *new, +static inline void __list_add(struct list_head *new_item, struct list_head *prev, struct list_head *next) { - next->prev = new; - new->next = next; - new->prev = prev; - prev->next = new; + next->prev = new_item; + new_item->next = next; + new_item->prev = prev; + prev->next = new_item; } #else -extern void __list_add(struct list_head *new, - struct list_head *prev, - struct list_head *next); +extern void __list_add(struct list_head *new_item, + struct list_head *prev, + struct list_head *next); #endif /** * list_add - add a new entry - * @new: new entry to be added + * @new_item: new entry to be added * @head: list head to add it after * * Insert a new entry after the specified head. * This is good for implementing stacks. */ -static inline void list_add(struct list_head *new, struct list_head *head) +static inline void list_add(struct list_head *new_item, struct list_head *head) { - __list_add(new, head, head->next); + __list_add(new_item, head, head->next); } /** * list_add_tail - add a new entry - * @new: new entry to be added + * @new_item: new entry to be added * @head: list head to add it before * * Insert a new entry before the specified head. * This is useful for implementing queues. */ -static inline void list_add_tail(struct list_head *new, struct list_head *head) +static inline void +list_add_tail(struct list_head *new_item, struct list_head *head) { - __list_add(new, head->prev, head); + __list_add(new_item, head->prev, head); } /* @@ -163,23 +162,23 @@ extern void list_del(struct list_head *entry); /** * list_replace - replace old entry by new one * @old : the element to be replaced - * @new : the new element to insert + * @new_item : the new element to insert * * If @old was empty, it will be overwritten. */ static inline void list_replace(struct list_head *old, - struct list_head *new) + struct list_head *new_item) { - new->next = old->next; - new->next->prev = new; - new->prev = old->prev; - new->prev->next = new; + new_item->next = old->next; + new_item->next->prev = new_item; + new_item->prev = old->prev; + new_item->prev->next = new_item; } static inline void list_replace_init(struct list_head *old, - struct list_head *new) + struct list_head *new_item) { - list_replace(old, new); + list_replace(old, new_item); INIT_LIST_HEAD(old); } diff --git a/src/utils.c b/src/utils.c deleted file mode 100644 index a2de3212e..000000000 --- a/src/utils.c +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (C) 2003-2011 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. - */ - -#include "config.h" -#include "utils.h" -#include "glib_compat.h" -#include "conf.h" - -#include <glib.h> - -#include <assert.h> -#include <string.h> -#include <sys/types.h> -#include <fcntl.h> -#include <errno.h> - -#ifndef WIN32 -#include <pwd.h> -#endif - -#if HAVE_IPV6 && WIN32 -#include <winsock2.h> -#endif - -#if HAVE_IPV6 && ! WIN32 -#include <sys/socket.h> -#endif - -#ifdef WIN32 -#include <windows.h> -#endif - -G_GNUC_CONST -static inline GQuark -parse_path_quark(void) -{ - return g_quark_from_static_string("path"); -} - -char * -parsePath(const char *path, G_GNUC_UNUSED GError **error_r) -{ - assert(path != NULL); - assert(error_r == NULL || *error_r == NULL); - -#ifndef WIN32 - if (!g_path_is_absolute(path) && path[0] != '~') { - g_set_error(error_r, parse_path_quark(), 0, - "not an absolute path: %s", path); - return NULL; - } else if (path[0] == '~') { - const char *home; - - if (path[1] == '/' || path[1] == '\0') { - const char *user = config_get_string(CONF_USER, NULL); - if (user != NULL) { - struct passwd *passwd = getpwnam(user); - if (!passwd) { - g_set_error(error_r, parse_path_quark(), 0, - "no such user: %s", user); - return NULL; - } - - home = passwd->pw_dir; - } else { - home = g_get_home_dir(); - if (home == NULL) { - g_set_error_literal(error_r, parse_path_quark(), 0, - "problems getting home " - "for current user"); - return NULL; - } - } - - ++path; - } else { - ++path; - - const char *slash = strchr(path, '/'); - char *user = slash != NULL - ? g_strndup(path, slash - path) - : g_strdup(path); - - struct passwd *passwd = getpwnam(user); - if (!passwd) { - g_set_error(error_r, parse_path_quark(), 0, - "no such user: %s", user); - g_free(user); - return NULL; - } - - g_free(user); - - home = passwd->pw_dir; - path = slash; - } - - return g_strconcat(home, path, NULL); - } else { -#endif - return g_strdup(path); -#ifndef WIN32 - } -#endif -} diff --git a/src/utils.h b/src/utils.h deleted file mode 100644 index f8d6657f2..000000000 --- a/src/utils.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2003-2011 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_UTILS_H -#define MPD_UTILS_H - -#include <glib.h> -#include <stdbool.h> - -#ifndef assert_static -/* Compile time assertion developed by Ralf Holly */ -/* http://pera-software.com/articles/compile-time-assertions.pdf */ -#define assert_static(e) \ - do { \ - enum { assert_static__ = 1/(e) }; \ - } while (0) -#endif /* !assert_static */ - -char * -parsePath(const char *path, GError **error_r); - -#endif |