diff options
Diffstat (limited to 'src/fs/io')
-rw-r--r-- | src/fs/io/AutoGunzipReader.cxx | 70 | ||||
-rw-r--r-- | src/fs/io/AutoGunzipReader.hxx | 52 | ||||
-rw-r--r-- | src/fs/io/BufferedOutputStream.cxx | 143 | ||||
-rw-r--r-- | src/fs/io/BufferedOutputStream.hxx | 71 | ||||
-rw-r--r-- | src/fs/io/BufferedReader.cxx | 82 | ||||
-rw-r--r-- | src/fs/io/BufferedReader.hxx | 75 | ||||
-rw-r--r-- | src/fs/io/FileOutputStream.cxx | 137 | ||||
-rw-r--r-- | src/fs/io/FileOutputStream.hxx | 69 | ||||
-rw-r--r-- | src/fs/io/FileReader.cxx | 98 | ||||
-rw-r--r-- | src/fs/io/FileReader.hxx | 68 | ||||
-rw-r--r-- | src/fs/io/GunzipReader.cxx | 101 | ||||
-rw-r--r-- | src/fs/io/GunzipReader.hxx | 68 | ||||
-rw-r--r-- | src/fs/io/GzipOutputStream.cxx | 105 | ||||
-rw-r--r-- | src/fs/io/GzipOutputStream.hxx | 70 | ||||
-rw-r--r-- | src/fs/io/OutputStream.hxx | 38 | ||||
-rw-r--r-- | src/fs/io/PeekReader.cxx | 60 | ||||
-rw-r--r-- | src/fs/io/PeekReader.hxx | 53 | ||||
-rw-r--r-- | src/fs/io/Reader.hxx | 52 | ||||
-rw-r--r-- | src/fs/io/StdioOutputStream.hxx | 46 | ||||
-rw-r--r-- | src/fs/io/TextFile.cxx | 71 | ||||
-rw-r--r-- | src/fs/io/TextFile.hxx | 73 |
21 files changed, 1602 insertions, 0 deletions
diff --git a/src/fs/io/AutoGunzipReader.cxx b/src/fs/io/AutoGunzipReader.cxx new file mode 100644 index 000000000..2552f7b99 --- /dev/null +++ b/src/fs/io/AutoGunzipReader.cxx @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2003-2014 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 "AutoGunzipReader.hxx" +#include "GunzipReader.hxx" +#include "util/Error.hxx" + +AutoGunzipReader::~AutoGunzipReader() +{ + delete gunzip; +} + +gcc_pure +static bool +IsGzip(const uint8_t data[4]) +{ + return data[0] == 0x1f && data[1] == 0x8b && data[2] == 0x08 && + (data[3] & 0xe0) == 0; +} + +inline bool +AutoGunzipReader::Detect(Error &error) +{ + const uint8_t *data = (const uint8_t *)peek.Peek(4, error); + if (data == nullptr) { + if (error.IsDefined()) + return false; + + next = &peek; + return true; + } + + if (IsGzip(data)) { + gunzip = new GunzipReader(peek, error); + if (!gunzip->IsDefined()) + return false; + + + next = gunzip; + } else + next = &peek; + + return true; +} + +size_t +AutoGunzipReader::Read(void *data, size_t size, Error &error) +{ + if (next == nullptr && !Detect(error)) + return false; + + return next->Read(data, size, error); +} diff --git a/src/fs/io/AutoGunzipReader.hxx b/src/fs/io/AutoGunzipReader.hxx new file mode 100644 index 000000000..9f031e0f5 --- /dev/null +++ b/src/fs/io/AutoGunzipReader.hxx @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2003-2014 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_AUTO_GUNZIP_READER_HXX +#define MPD_AUTO_GUNZIP_READER_HXX + +#include "check.h" +#include "PeekReader.hxx" +#include "Compiler.h" + +#include <stdint.h> + +class GunzipReader; + +/** + * A filter that detects gzip compression and optionally inserts a + * #GunzipReader. + */ +class AutoGunzipReader final : public Reader { + Reader *next; + PeekReader peek; + GunzipReader *gunzip; + +public: + AutoGunzipReader(Reader &_next) + :next(nullptr), peek(_next), gunzip(nullptr) {} + ~AutoGunzipReader(); + + /* virtual methods from class Reader */ + virtual size_t Read(void *data, size_t size, Error &error) override; + +private: + bool Detect(Error &error); +}; + +#endif diff --git a/src/fs/io/BufferedOutputStream.cxx b/src/fs/io/BufferedOutputStream.cxx new file mode 100644 index 000000000..088a3e279 --- /dev/null +++ b/src/fs/io/BufferedOutputStream.cxx @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2003-2014 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 "BufferedOutputStream.hxx" +#include "OutputStream.hxx" + +#include <stdarg.h> +#include <string.h> +#include <stdio.h> + +bool +BufferedOutputStream::AppendToBuffer(const void *data, size_t size) +{ + auto r = buffer.Write(); + if (r.size < size) + return false; + + memcpy(r.data, data, size); + buffer.Append(size); + return true; +} + +bool +BufferedOutputStream::Write(const void *data, size_t size) +{ + if (gcc_unlikely(last_error.IsDefined())) + return false; + + if (AppendToBuffer(data, size)) + return true; + + if (!Flush()) + return false; + + if (AppendToBuffer(data, size)) + return true; + + return os.Write(data, size, last_error); +} + +bool +BufferedOutputStream::Write(const char *p) +{ + return Write(p, strlen(p)); +} + +bool +BufferedOutputStream::Format(const char *fmt, ...) +{ + if (gcc_unlikely(last_error.IsDefined())) + return false; + + auto r = buffer.Write(); + if (r.IsEmpty()) { + if (!Flush()) + return false; + + r = buffer.Write(); + } + + /* format into the buffer */ + va_list ap; + va_start(ap, fmt); + size_t size = vsnprintf(r.data, r.size, fmt, ap); + va_end(ap); + + if (gcc_unlikely(size >= r.size)) { + /* buffer was not large enough; flush it and try + again */ + + if (!Flush()) + return false; + + r = buffer.Write(); + + if (gcc_unlikely(size >= r.size)) { + /* still not enough space: grow the buffer and + try again */ + r.size = size + 1; + r.data = buffer.Write(r.size); + } + + /* format into the new buffer */ + va_start(ap, fmt); + size = vsnprintf(r.data, r.size, fmt, ap); + va_end(ap); + + /* this time, it must fit */ + assert(size < r.size); + } + + buffer.Append(size); + return true; +} + +bool +BufferedOutputStream::Flush() +{ + if (!Check()) + return false; + + auto r = buffer.Read(); + if (r.IsEmpty()) + return true; + + bool success = os.Write(r.data, r.size, last_error); + if (gcc_likely(success)) + buffer.Consume(r.size); + return success; +} + +bool +BufferedOutputStream::Flush(Error &error) +{ + if (!Check(error)) + return false; + + auto r = buffer.Read(); + if (r.IsEmpty()) + return true; + + bool success = os.Write(r.data, r.size, error); + if (gcc_likely(success)) + buffer.Consume(r.size); + return success; +} diff --git a/src/fs/io/BufferedOutputStream.hxx b/src/fs/io/BufferedOutputStream.hxx new file mode 100644 index 000000000..f2de758a2 --- /dev/null +++ b/src/fs/io/BufferedOutputStream.hxx @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2003-2014 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_BUFFERED_OUTPUT_STREAM_HXX +#define MPD_BUFFERED_OUTPUT_STREAM_HXX + +#include "check.h" +#include "Compiler.h" +#include "util/DynamicFifoBuffer.hxx" +#include "util/Error.hxx" + +#include <stddef.h> + +class OutputStream; +class Error; + +class BufferedOutputStream { + OutputStream &os; + + DynamicFifoBuffer<char> buffer; + + Error last_error; + +public: + BufferedOutputStream(OutputStream &_os) + :os(_os), buffer(32768) {} + + bool Write(const void *data, size_t size); + bool Write(const char *p); + + gcc_printf(2,3) + bool Format(const char *fmt, ...); + + gcc_pure + bool Check() const { + return !last_error.IsDefined(); + } + + bool Check(Error &error) const { + if (last_error.IsDefined()) { + error.Set(last_error); + return false; + } else + return true; + } + + bool Flush(); + + bool Flush(Error &error); + +private: + bool AppendToBuffer(const void *data, size_t size); +}; + +#endif diff --git a/src/fs/io/BufferedReader.cxx b/src/fs/io/BufferedReader.cxx new file mode 100644 index 000000000..ba2f17dcf --- /dev/null +++ b/src/fs/io/BufferedReader.cxx @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2003-2014 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 "BufferedReader.hxx" +#include "Reader.hxx" +#include "util/TextFile.hxx" + +bool +BufferedReader::Fill(bool need_more) +{ + if (gcc_unlikely(last_error.IsDefined())) + return false; + + if (eof) + return !need_more; + + auto w = buffer.Write(); + if (w.IsEmpty()) { + if (buffer.GetCapacity() >= MAX_SIZE) + return !need_more; + + buffer.Grow(buffer.GetCapacity() * 2); + w = buffer.Write(); + assert(!w.IsEmpty()); + } + + size_t nbytes = reader.Read(w.data, w.size, last_error); + if (nbytes == 0) { + if (gcc_unlikely(last_error.IsDefined())) + return false; + + eof = true; + return !need_more; + } + + buffer.Append(nbytes); + return true; +} + +char * +BufferedReader::ReadLine() +{ + do { + char *line = ReadBufferedLine(buffer); + if (line != nullptr) + return line; + } while (Fill(true)); + + if (last_error.IsDefined() || !eof || buffer.IsEmpty()) + return nullptr; + + auto w = buffer.Write(); + if (w.IsEmpty()) { + buffer.Grow(buffer.GetCapacity() + 1); + w = buffer.Write(); + assert(!w.IsEmpty()); + } + + /* terminate the last line */ + w[0] = 0; + + char *line = buffer.Read().data; + buffer.Clear(); + return line; +} diff --git a/src/fs/io/BufferedReader.hxx b/src/fs/io/BufferedReader.hxx new file mode 100644 index 000000000..61cc8df83 --- /dev/null +++ b/src/fs/io/BufferedReader.hxx @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2003-2014 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_BUFFERED_READER_HXX +#define MPD_BUFFERED_READER_HXX + +#include "check.h" +#include "Compiler.h" +#include "util/DynamicFifoBuffer.hxx" +#include "util/Error.hxx" + +#include <stddef.h> + +class Reader; +class Error; + +class BufferedReader { + static constexpr size_t MAX_SIZE = 512 * 1024; + + Reader &reader; + + DynamicFifoBuffer<char> buffer; + + Error last_error; + + bool eof; + +public: + BufferedReader(Reader &_reader) + :reader(_reader), buffer(4096), eof(false) {} + + gcc_pure + bool Check() const { + return !last_error.IsDefined(); + } + + bool Check(Error &error) const { + if (last_error.IsDefined()) { + error.Set(last_error); + return false; + } else + return true; + } + + bool Fill(bool need_more); + + gcc_pure + WritableBuffer<void> Read() const { + return buffer.Read().ToVoid(); + } + + void Consume(size_t n) { + buffer.Consume(n); + } + + char *ReadLine(); +}; + +#endif diff --git a/src/fs/io/FileOutputStream.cxx b/src/fs/io/FileOutputStream.cxx new file mode 100644 index 000000000..0eff8b5f0 --- /dev/null +++ b/src/fs/io/FileOutputStream.cxx @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2003-2014 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 "FileOutputStream.hxx" +#include "fs/FileSystem.hxx" +#include "system/fd_util.h" +#include "util/Error.hxx" + +#ifdef WIN32 + +FileOutputStream::FileOutputStream(Path _path, Error &error) + :path(_path), + handle(CreateFile(path.c_str(), GENERIC_WRITE, 0, nullptr, + TRUNCATE_EXISTING, + FILE_ATTRIBUTE_NORMAL|FILE_FLAG_WRITE_THROUGH, + nullptr)) +{ + if (handle == INVALID_HANDLE_VALUE) + error.FormatLastError("Failed to create %s", path.c_str()); +} + +bool +FileOutputStream::Write(const void *data, size_t size, Error &error) +{ + assert(IsDefined()); + + DWORD nbytes; + if (!WriteFile(handle, data, size, &nbytes, nullptr)) { + error.FormatLastError("Failed to write to %s", path.c_str()); + return false; + } + + if (size_t(nbytes) != size) { + error.FormatLastError(ERROR_DISK_FULL, + "Failed to write to %s", path.c_str()); + return false; + } + + return true; +} + +bool +FileOutputStream::Commit(gcc_unused Error &error) +{ + assert(IsDefined()); + + CloseHandle(handle); + handle = INVALID_HANDLE_VALUE; + return true; +} + +void +FileOutputStream::Cancel() +{ + assert(IsDefined()); + + CloseHandle(handle); + handle = INVALID_HANDLE_VALUE; + RemoveFile(path); +} + +#else + +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> + +FileOutputStream::FileOutputStream(Path _path, Error &error) + :path(_path), + fd(open_cloexec(path.c_str(), + O_WRONLY|O_CREAT|O_TRUNC, + 0666)) +{ + if (fd < 0) + error.FormatErrno("Failed to create %s", path.c_str()); +} + +bool +FileOutputStream::Write(const void *data, size_t size, Error &error) +{ + assert(IsDefined()); + + ssize_t nbytes = write(fd, data, size); + if (nbytes < 0) { + error.FormatErrno("Failed to write to %s", path.c_str()); + return false; + } else if ((size_t)nbytes < size) { + error.FormatErrno(ENOSPC, + "Failed to write to %s", path.c_str()); + return false; + } + + return true; +} + +bool +FileOutputStream::Commit(Error &error) +{ + assert(IsDefined()); + + bool success = close(fd) == 0; + fd = -1; + if (!success) + error.FormatErrno("Failed to commit %s", path.c_str()); + + return success; +} + +void +FileOutputStream::Cancel() +{ + assert(IsDefined()); + + close(fd); + fd = -1; + + RemoveFile(path); +} + +#endif diff --git a/src/fs/io/FileOutputStream.hxx b/src/fs/io/FileOutputStream.hxx new file mode 100644 index 000000000..5b6309957 --- /dev/null +++ b/src/fs/io/FileOutputStream.hxx @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2003-2014 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_FILE_OUTPUT_STREAM_HXX +#define MPD_FILE_OUTPUT_STREAM_HXX + +#include "check.h" +#include "OutputStream.hxx" +#include "fs/AllocatedPath.hxx" +#include "Compiler.h" + +#include <assert.h> + +#ifdef WIN32 +#include <windows.h> +#endif + +class Path; + +class FileOutputStream final : public OutputStream { + AllocatedPath path; + +#ifdef WIN32 + HANDLE handle; +#else + int fd; +#endif + +public: + FileOutputStream(Path _path, Error &error); + + ~FileOutputStream() { + if (IsDefined()) + Cancel(); + } + + + bool IsDefined() const { +#ifdef WIN32 + return handle != INVALID_HANDLE_VALUE; +#else + return fd >= 0; +#endif + } + + bool Commit(Error &error); + void Cancel(); + + /* virtual methods from class OutputStream */ + bool Write(const void *data, size_t size, Error &error) override; +}; + +#endif diff --git a/src/fs/io/FileReader.cxx b/src/fs/io/FileReader.cxx new file mode 100644 index 000000000..d63cd8ab0 --- /dev/null +++ b/src/fs/io/FileReader.cxx @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2003-2014 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 "FileReader.hxx" +#include "system/fd_util.h" +#include "util/Error.hxx" + +#ifdef WIN32 + +FileReader::FileReader(Path _path, Error &error) + :path(_path), + handle(CreateFile(path.c_str(), GENERIC_READ, FILE_SHARE_READ, + nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, + nullptr)) +{ + if (handle == INVALID_HANDLE_VALUE) + error.FormatLastError("Failed to open %s", path.c_str()); +} + +size_t +FileReader::Read(void *data, size_t size, Error &error) +{ + assert(IsDefined()); + + DWORD nbytes; + if (!ReadFile(handle, data, size, &nbytes, nullptr)) { + error.FormatLastError("Failed to read from %s", path.c_str()); + nbytes = 0; + } + + return nbytes; +} + +void +FileReader::Close() +{ + assert(IsDefined()); + + CloseHandle(handle); +} + +#else + +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> + +FileReader::FileReader(Path _path, Error &error) + :path(_path), + fd(open_cloexec(path.c_str(), + O_RDONLY, + 0)) +{ + if (fd < 0) + error.FormatErrno("Failed to open %s", path.c_str()); +} + +size_t +FileReader::Read(void *data, size_t size, Error &error) +{ + assert(IsDefined()); + + ssize_t nbytes = read(fd, data, size); + if (nbytes < 0) { + error.FormatErrno("Failed to read from %s", path.c_str()); + nbytes = 0; + } + + return nbytes; +} + +void +FileReader::Close() +{ + assert(IsDefined()); + + close(fd); + fd = -1; +} + +#endif diff --git a/src/fs/io/FileReader.hxx b/src/fs/io/FileReader.hxx new file mode 100644 index 000000000..9f459aee2 --- /dev/null +++ b/src/fs/io/FileReader.hxx @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2003-2014 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_FILE_READER_HXX +#define MPD_FILE_READER_HXX + +#include "check.h" +#include "Reader.hxx" +#include "fs/AllocatedPath.hxx" +#include "Compiler.h" + +#include <assert.h> + +#ifdef WIN32 +#include <windows.h> +#endif + +class Path; + +class FileReader final : public Reader { + AllocatedPath path; + +#ifdef WIN32 + HANDLE handle; +#else + int fd; +#endif + +public: + FileReader(Path _path, Error &error); + + ~FileReader() { + if (IsDefined()) + Close(); + } + + + bool IsDefined() const { +#ifdef WIN32 + return handle != INVALID_HANDLE_VALUE; +#else + return fd >= 0; +#endif + } + + void Close(); + + /* virtual methods from class Reader */ + size_t Read(void *data, size_t size, Error &error) override; +}; + +#endif diff --git a/src/fs/io/GunzipReader.cxx b/src/fs/io/GunzipReader.cxx new file mode 100644 index 000000000..ad5e41784 --- /dev/null +++ b/src/fs/io/GunzipReader.cxx @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2003-2014 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 "GunzipReader.hxx" +#include "lib/zlib/Domain.hxx" +#include "util/Error.hxx" +#include "util/Domain.hxx" + +GunzipReader::GunzipReader(Reader &_next, Error &error) + :next(_next), eof(false) +{ + z.next_in = nullptr; + z.avail_in = 0; + z.zalloc = Z_NULL; + z.zfree = Z_NULL; + z.opaque = Z_NULL; + + int result = inflateInit2(&z, 16 + MAX_WBITS); + if (result != Z_OK) { + z.opaque = this; + error.Set(zlib_domain, result, zError(result)); + } +} + +GunzipReader::~GunzipReader() +{ + if (IsDefined()) + inflateEnd(&z); +} + +inline bool +GunzipReader::FillBuffer(Error &error) +{ + auto w = buffer.Write(); + assert(!w.IsEmpty()); + + size_t nbytes = next.Read(w.data, w.size, error); + if (nbytes == 0) + return false; + + buffer.Append(nbytes); + return true; +} + +size_t +GunzipReader::Read(void *data, size_t size, Error &error) +{ + if (eof) + return 0; + + z.next_out = (Bytef *)data; + z.avail_out = size; + + while (true) { + int flush = Z_NO_FLUSH; + + auto r = buffer.Read(); + if (r.IsEmpty()) { + if (FillBuffer(error)) + r = buffer.Read(); + else if (error.IsDefined()) + return 0; + else + flush = Z_FINISH; + } + + z.next_in = r.data; + z.avail_in = r.size; + + int result = inflate(&z, flush); + if (result == Z_STREAM_END) { + eof = true; + return size - z.avail_out; + } else if (result != Z_OK) { + error.Set(zlib_domain, result, zError(result)); + return 0; + } + + buffer.Consume(r.size - z.avail_in); + + if (z.avail_out < size) + return size - z.avail_out; + } +} diff --git a/src/fs/io/GunzipReader.hxx b/src/fs/io/GunzipReader.hxx new file mode 100644 index 000000000..06c44bad6 --- /dev/null +++ b/src/fs/io/GunzipReader.hxx @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2003-2014 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_GUNZIP_READER_HXX +#define MPD_GUNZIP_READER_HXX + +#include "check.h" +#include "Reader.hxx" +#include "util/StaticFifoBuffer.hxx" +#include "Compiler.h" + +#include <zlib.h> + +class Error; +class Domain; + +/** + * A filter that decompresses data using zlib. + */ +class GunzipReader final : public Reader { + Reader &next; + + bool eof; + + z_stream z; + + StaticFifoBuffer<Bytef, 4096> buffer; + +public: + /** + * Construct the filter. Call IsDefined() to check whether + * the constructor has succeeded. If not, #error will hold + * information about the failure. + */ + GunzipReader(Reader &_next, Error &error); + ~GunzipReader(); + + /** + * Check whether the constructor has succeeded. + */ + bool IsDefined() const { + return z.opaque == nullptr; + } + + /* virtual methods from class Reader */ + virtual size_t Read(void *data, size_t size, Error &error) override; + +private: + bool FillBuffer(Error &error); +}; + +#endif diff --git a/src/fs/io/GzipOutputStream.cxx b/src/fs/io/GzipOutputStream.cxx new file mode 100644 index 000000000..27ae6b2ad --- /dev/null +++ b/src/fs/io/GzipOutputStream.cxx @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2003-2014 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 "GzipOutputStream.hxx" +#include "lib/zlib/Domain.hxx" +#include "util/Error.hxx" +#include "util/Domain.hxx" + +GzipOutputStream::GzipOutputStream(OutputStream &_next, Error &error) + :next(_next) +{ + z.next_in = nullptr; + z.avail_in = 0; + z.zalloc = Z_NULL; + z.zfree = Z_NULL; + z.opaque = Z_NULL; + + constexpr int windowBits = 15; + constexpr int gzip_encoding = 16; + + int result = deflateInit2(&z, Z_DEFAULT_COMPRESSION, Z_DEFLATED, + windowBits | gzip_encoding, + 8, Z_DEFAULT_STRATEGY); + if (result != Z_OK) { + z.opaque = this; + error.Set(zlib_domain, result, zError(result)); + } +} + +GzipOutputStream::~GzipOutputStream() +{ + if (IsDefined()) + deflateEnd(&z); +} + +bool +GzipOutputStream::Flush(Error &error) +{ + /* no more input */ + z.next_in = nullptr; + z.avail_in = 0; + + while (true) { + Bytef output[4096]; + z.next_out = output; + z.avail_out = sizeof(output); + + int result = deflate(&z, Z_FINISH); + if (z.next_out > output && + !next.Write(output, z.next_out - output, error)) + return false; + + if (result == Z_STREAM_END) + return true; + else if (result != Z_OK) { + error.Set(zlib_domain, result, zError(result)); + return false; + } + } +} + +bool +GzipOutputStream::Write(const void *_data, size_t size, Error &error) +{ + /* zlib's API requires non-const input pointer */ + void *data = const_cast<void *>(_data); + + z.next_in = reinterpret_cast<Bytef *>(data); + z.avail_in = size; + + while (z.avail_in > 0) { + Bytef output[4096]; + z.next_out = output; + z.avail_out = sizeof(output); + + int result = deflate(&z, Z_NO_FLUSH); + if (result != Z_OK) { + error.Set(zlib_domain, result, zError(result)); + return false; + } + + if (z.next_out > output && + !next.Write(output, z.next_out - output, error)) + return false; + } + + return true; +} diff --git a/src/fs/io/GzipOutputStream.hxx b/src/fs/io/GzipOutputStream.hxx new file mode 100644 index 000000000..27ee2dd24 --- /dev/null +++ b/src/fs/io/GzipOutputStream.hxx @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2003-2014 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_GZIP_OUTPUT_STREAM_HXX +#define MPD_GZIP_OUTPUT_STREAM_HXX + +#include "check.h" +#include "OutputStream.hxx" +#include "Compiler.h" + +#include <assert.h> +#include <zlib.h> + +class Error; +class Domain; + +/** + * A filter that compresses data written to it using zlib, forwarding + * compressed data in the "gzip" format. + * + * Don't forget to call Flush() before destructing this object. + */ +class GzipOutputStream final : public OutputStream { + OutputStream &next; + + z_stream z; + +public: + /** + * Construct the filter. Call IsDefined() to check whether + * the constructor has succeeded. If not, #error will hold + * information about the failure. + */ + GzipOutputStream(OutputStream &_next, Error &error); + ~GzipOutputStream(); + + /** + * Check whether the constructor has succeeded. + */ + bool IsDefined() const { + return z.opaque == nullptr; + } + + /** + * Finish the file and write all data remaining in zlib's + * output buffer. + */ + bool Flush(Error &error); + + /* virtual methods from class OutputStream */ + bool Write(const void *data, size_t size, Error &error) override; +}; + +#endif diff --git a/src/fs/io/OutputStream.hxx b/src/fs/io/OutputStream.hxx new file mode 100644 index 000000000..71311c71f --- /dev/null +++ b/src/fs/io/OutputStream.hxx @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2003-2014 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_OUTPUT_STREAM_HXX +#define MPD_OUTPUT_STREAM_HXX + +#include "check.h" +#include "Compiler.h" + +#include <stddef.h> + +class Error; + +class OutputStream { +public: + OutputStream() = default; + OutputStream(const OutputStream &) = delete; + + virtual bool Write(const void *data, size_t size, Error &error) = 0; +}; + +#endif diff --git a/src/fs/io/PeekReader.cxx b/src/fs/io/PeekReader.cxx new file mode 100644 index 000000000..2e8042ab6 --- /dev/null +++ b/src/fs/io/PeekReader.cxx @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2003-2014 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 "PeekReader.hxx" + +#include <algorithm> + +#include <assert.h> +#include <string.h> + +const void * +PeekReader::Peek(size_t size, Error &error) +{ + assert(size > 0); + assert(size < sizeof(buffer)); + assert(buffer_size == 0); + assert(buffer_position == 0); + + do { + size_t nbytes = next.Read(buffer + buffer_size, + size - buffer_size, error); + if (nbytes == 0) + return nullptr; + + buffer_size += nbytes; + } while (buffer_size < size); + + return buffer; +} + +size_t +PeekReader::Read(void *data, size_t size, Error &error) +{ + size_t buffer_remaining = buffer_size - buffer_position; + if (buffer_remaining > 0) { + size_t nbytes = std::min(buffer_remaining, size); + memcpy(data, buffer + buffer_position, nbytes); + buffer_position += nbytes; + return nbytes; + } + + return next.Read(data, size, error); +} diff --git a/src/fs/io/PeekReader.hxx b/src/fs/io/PeekReader.hxx new file mode 100644 index 000000000..32180b0a8 --- /dev/null +++ b/src/fs/io/PeekReader.hxx @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2003-2014 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_PEEK_READER_HXX +#define MPD_PEEK_READER_HXX + +#include "check.h" +#include "Reader.hxx" +#include "Compiler.h" + +#include <stdint.h> + +class AutoGunzipReader; + +/** + * A filter that allows the caller to peek the first few bytes without + * consuming them. The first call must be Peek(), and the following + * Read() will deliver the same bytes again. + */ +class PeekReader final : public Reader { + Reader &next; + + size_t buffer_size, buffer_position; + + uint8_t buffer[64]; + +public: + PeekReader(Reader &_next) + :next(_next), buffer_size(0), buffer_position(0) {} + + const void *Peek(size_t size, Error &error); + + /* virtual methods from class Reader */ + virtual size_t Read(void *data, size_t size, Error &error) override; +}; + +#endif diff --git a/src/fs/io/Reader.hxx b/src/fs/io/Reader.hxx new file mode 100644 index 000000000..d41e92dd0 --- /dev/null +++ b/src/fs/io/Reader.hxx @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2003-2014 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_READER_HXX +#define MPD_READER_HXX + +#include "check.h" +#include "Compiler.h" + +#include <stddef.h> + +class Error; + +/** + * An interface that can read bytes from a stream until the stream + * ends. + * + * This interface is simpler and less cumbersome to use than + * #InputStream. + */ +class Reader { +public: + Reader() = default; + Reader(const Reader &) = delete; + + /** + * Read data from the stream. + * + * @return the number of bytes read into the given buffer or 0 + * on error/end-of-stream + */ + gcc_nonnull_all + virtual size_t Read(void *data, size_t size, Error &error) = 0; +}; + +#endif diff --git a/src/fs/io/StdioOutputStream.hxx b/src/fs/io/StdioOutputStream.hxx new file mode 100644 index 000000000..c1c0a00bd --- /dev/null +++ b/src/fs/io/StdioOutputStream.hxx @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2003-2014 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_STDIO_OUTPUT_STREAM_HXX +#define MPD_STDIO_OUTPUT_STREAM_HXX + +#include "check.h" +#include "OutputStream.hxx" +#include "fs/AllocatedPath.hxx" +#include "Compiler.h" + +#include <stdio.h> + +class StdioOutputStream final : public OutputStream { + FILE *const file; + +public: + StdioOutputStream(FILE *_file):file(_file) {} + + /* virtual methods from class OutputStream */ + bool Write(const void *data, size_t size, + gcc_unused Error &error) override { + fwrite(data, 1, size, file); + + /* this class is debug-only and ignores errors */ + return true; + } +}; + +#endif diff --git a/src/fs/io/TextFile.cxx b/src/fs/io/TextFile.cxx new file mode 100644 index 000000000..28d6dabcb --- /dev/null +++ b/src/fs/io/TextFile.cxx @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2003-2014 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 "TextFile.hxx" +#include "FileReader.hxx" +#include "AutoGunzipReader.hxx" +#include "BufferedReader.hxx" +#include "fs/Path.hxx" + +#include <assert.h> + +TextFile::TextFile(Path path_fs, Error &error) + :file_reader(new FileReader(path_fs, error)), +#ifdef HAVE_ZLIB + gunzip_reader(file_reader->IsDefined() + ? new AutoGunzipReader(*file_reader) + : nullptr), +#endif + buffered_reader(file_reader->IsDefined() + ? new BufferedReader(* +#ifdef HAVE_ZLIB + gunzip_reader +#else + file_reader +#endif + ) + : nullptr) +{ +} + +TextFile::~TextFile() +{ + delete buffered_reader; +#ifdef HAVE_ZLIB + delete gunzip_reader; +#endif + delete file_reader; +} + +char * +TextFile::ReadLine() +{ + assert(buffered_reader != nullptr); + + return buffered_reader->ReadLine(); +} + +bool +TextFile::Check(Error &error) const +{ + assert(buffered_reader != nullptr); + + return buffered_reader->Check(error); +} diff --git a/src/fs/io/TextFile.hxx b/src/fs/io/TextFile.hxx new file mode 100644 index 000000000..5577363e7 --- /dev/null +++ b/src/fs/io/TextFile.hxx @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2003-2014 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_TEXT_FILE_HXX +#define MPD_TEXT_FILE_HXX + +#include "check.h" +#include "Compiler.h" + +#include <stddef.h> + +class Path; +class Error; +class FileReader; +class AutoGunzipReader; +class BufferedReader; + +class TextFile { + FileReader *const file_reader; + +#ifdef HAVE_ZLIB + AutoGunzipReader *const gunzip_reader; +#endif + + BufferedReader *const buffered_reader; + +public: + TextFile(Path path_fs, Error &error); + + TextFile(const TextFile &other) = delete; + + ~TextFile(); + + bool HasFailed() const { + return gcc_unlikely(buffered_reader == nullptr); + } + + /** + * Reads a line from the input file, and strips trailing + * space. There is a reasonable maximum line length, only to + * prevent denial of service. + * + * Use Check() after nullptr has been returned to check + * whether an error occurred or end-of-file has been reached. + * + * @param file the source file, opened in text mode + * @return a pointer to the line, or nullptr on end-of-file or error + */ + char *ReadLine(); + + /** + * Check whether a ReadLine() call has thrown an error. + */ + bool Check(Error &error) const; +}; + +#endif |