fs/io: move to io/

This commit is contained in:
Max Kellermann
2021-12-03 14:02:07 +01:00
parent 8681a3d74c
commit a5fa43b526
57 changed files with 68 additions and 71 deletions
+1 -1
View File
@@ -41,7 +41,7 @@
#ifdef USE_XDG
#include "util/StringStrip.hxx"
#include "util/StringCompare.hxx"
#include "io/TextFile.hxx"
#include "fs/io/TextFile.hxx"
#include <string.h>
#include <utility>
#endif
-59
View File
@@ -1,59 +0,0 @@
/*
* Copyright 2003-2021 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 "AutoGunzipReader.hxx"
#include "GunzipReader.hxx"
AutoGunzipReader::AutoGunzipReader(Reader &_next) noexcept
:peek(_next) {}
AutoGunzipReader::~AutoGunzipReader() noexcept = default;
[[gnu::pure]]
static bool
IsGzip(const uint8_t data[4]) noexcept
{
return data[0] == 0x1f && data[1] == 0x8b && data[2] == 0x08 &&
(data[3] & 0xe0) == 0;
}
inline void
AutoGunzipReader::Detect()
{
const auto *data = (const uint8_t *)peek.Peek(4);
if (data == nullptr) {
next = &peek;
return;
}
if (IsGzip(data))
next = (gunzip = std::make_unique<GunzipReader>(peek)).get();
else
next = &peek;
}
size_t
AutoGunzipReader::Read(void *data, size_t size)
{
if (next == nullptr)
Detect();
assert(next != nullptr);
return next->Read(data, size);
}
-49
View File
@@ -1,49 +0,0 @@
/*
* Copyright 2003-2021 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 "PeekReader.hxx"
#include <memory>
class GunzipReader;
/**
* A filter that detects gzip compression and optionally inserts a
* #GunzipReader.
*/
class AutoGunzipReader final : public Reader {
Reader *next = nullptr;
PeekReader peek;
std::unique_ptr<GunzipReader> gunzip;
public:
explicit AutoGunzipReader(Reader &_next) noexcept;
~AutoGunzipReader() noexcept;
/* virtual methods from class Reader */
size_t Read(void *data, size_t size) override;
private:
void Detect();
};
#endif
-177
View File
@@ -1,177 +0,0 @@
/*
* Copyright (C) 2014-2018 Max Kellermann <max.kellermann@gmail.com>
*
* 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 "BufferedOutputStream.hxx"
#include "OutputStream.hxx"
#include <cstdarg>
#include <string.h>
#include <stdio.h>
#ifdef _UNICODE
#include "system/Error.hxx"
#include <stringapiset.h>
#endif
bool
BufferedOutputStream::AppendToBuffer(const void *data, std::size_t size) noexcept
{
auto r = buffer.Write();
if (r.size < size)
return false;
memcpy(r.data, data, size);
buffer.Append(size);
return true;
}
void
BufferedOutputStream::Write(const void *data, std::size_t size)
{
/* try to append to the current buffer */
if (AppendToBuffer(data, size))
return;
/* not enough room in the buffer - flush it */
Flush();
/* see if there's now enough room */
if (AppendToBuffer(data, size))
return;
/* too large for the buffer: direct write */
os.Write(data, size);
}
void
BufferedOutputStream::Write(const char *p)
{
Write(p, strlen(p));
}
void
BufferedOutputStream::Format(const char *fmt, ...)
{
auto r = buffer.Write();
if (r.empty()) {
Flush();
r = buffer.Write();
}
/* format into the buffer */
std::va_list ap;
va_start(ap, fmt);
std::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 */
Flush();
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);
}
#ifdef _UNICODE
void
BufferedOutputStream::Write(const wchar_t *p)
{
WriteWideToUTF8(p, wcslen(p));
}
void
BufferedOutputStream::WriteWideToUTF8(const wchar_t *src,
std::size_t src_length)
{
if (src_length == 0)
return;
auto r = buffer.Write();
if (r.empty()) {
Flush();
r = buffer.Write();
}
int length = WideCharToMultiByte(CP_UTF8, 0, src, src_length,
r.data, r.size, nullptr, nullptr);
if (length <= 0) {
const auto error = GetLastError();
if (error != ERROR_INSUFFICIENT_BUFFER)
throw MakeLastError(error, "UTF-8 conversion failed");
/* how much buffer do we need? */
length = WideCharToMultiByte(CP_UTF8, 0, src, src_length,
nullptr, 0, nullptr, nullptr);
if (length <= 0)
throw MakeLastError(error, "UTF-8 conversion failed");
/* grow the buffer and try again */
length = WideCharToMultiByte(CP_UTF8, 0, src, src_length,
buffer.Write(length), length,
nullptr, nullptr);
if (length <= 0)
throw MakeLastError(error, "UTF-8 conversion failed");
}
buffer.Append(length);
}
#endif
void
BufferedOutputStream::Flush()
{
auto r = buffer.Read();
if (r.empty())
return;
os.Write(r.data, r.size);
buffer.Consume(r.size);
}
-142
View File
@@ -1,142 +0,0 @@
/*
* Copyright 2014-2021 Max Kellermann <max.kellermann@gmail.com>
*
* 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 BUFFERED_OUTPUT_STREAM_HXX
#define BUFFERED_OUTPUT_STREAM_HXX
#include "util/Compiler.h"
#include "util/DynamicFifoBuffer.hxx"
#include <cstddef>
#ifdef _UNICODE
#include <wchar.h>
#endif
class OutputStream;
/**
* An #OutputStream wrapper that buffers its output to reduce the
* number of OutputStream::Write() calls.
*
* All wchar_t based strings are converted to UTF-8.
*
* To make sure everything is written to the underlying #OutputStream,
* call Flush() before destructing this object.
*/
class BufferedOutputStream {
OutputStream &os;
DynamicFifoBuffer<char> buffer;
public:
explicit BufferedOutputStream(OutputStream &_os,
size_t buffer_size=32768) noexcept
:os(_os), buffer(buffer_size) {}
/**
* Write the contents of a buffer.
*/
void Write(const void *data, std::size_t size);
/**
* Write the given object. Note that this is only safe with
* POD types. Types with padding can expose sensitive data.
*/
template<typename T>
void WriteT(const T &value) {
Write(&value, sizeof(value));
}
/**
* Write one narrow character.
*/
void Write(const char &ch) {
WriteT(ch);
}
/**
* Write a null-terminated string.
*/
void Write(const char *p);
/**
* Write a printf-style formatted string.
*/
gcc_printf(2,3)
void Format(const char *fmt, ...);
#ifdef _UNICODE
/**
* Write one narrow character.
*/
void Write(const wchar_t &ch) {
WriteWideToUTF8(&ch, 1);
}
/**
* Write a null-terminated wide string.
*/
void Write(const wchar_t *p);
#endif
/**
* Write buffer contents to the #OutputStream.
*/
void Flush();
/**
* Discard buffer contents.
*/
void Discard() noexcept {
buffer.Clear();
}
private:
bool AppendToBuffer(const void *data, std::size_t size) noexcept;
#ifdef _UNICODE
void WriteWideToUTF8(const wchar_t *p, std::size_t length);
#endif
};
/**
* Helper function which constructs a #BufferedOutputStream, calls the
* given function and flushes the #BufferedOutputStream.
*/
template<typename F>
void
WithBufferedOutputStream(OutputStream &os, F &&f)
{
BufferedOutputStream bos(os);
f(bos);
bos.Flush();
}
#endif
-133
View File
@@ -1,133 +0,0 @@
/*
* Copyright 2014-2019 Max Kellermann <max.kellermann@gmail.com>
*
* 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 "BufferedReader.hxx"
#include "Reader.hxx"
#include "util/TextFile.hxx"
#include <cstdint>
#include <stdexcept>
#include <string.h>
bool
BufferedReader::Fill(bool need_more)
{
if (eof)
return !need_more;
auto w = buffer.Write();
if (w.empty()) {
if (buffer.GetCapacity() >= MAX_SIZE)
return !need_more;
buffer.Grow(buffer.GetCapacity() * 2);
w = buffer.Write();
assert(!w.empty());
}
size_t nbytes = reader.Read(w.data, w.size);
if (nbytes == 0) {
eof = true;
return !need_more;
}
buffer.Append(nbytes);
return true;
}
void *
BufferedReader::ReadFull(size_t size)
{
while (true) {
auto r = Read();
if (r.size >= size)
return r.data;
if (!Fill(true))
throw std::runtime_error("Premature end of file");
}
}
size_t
BufferedReader::ReadFromBuffer(WritableBuffer<void> dest) noexcept
{
auto src = Read();
size_t nbytes = std::min(src.size, dest.size);
memcpy(dest.data, src.data, nbytes);
Consume(nbytes);
return nbytes;
}
void
BufferedReader::ReadFull(WritableBuffer<void> _dest)
{
auto dest = WritableBuffer<uint8_t>::FromVoid(_dest);
assert(dest.size == _dest.size);
while (true) {
size_t nbytes = ReadFromBuffer(dest.ToVoid());
dest.skip_front(nbytes);
if (dest.size == 0)
break;
if (!Fill(true))
throw std::runtime_error("Premature end of file");
}
}
char *
BufferedReader::ReadLine()
{
do {
char *line = ReadBufferedLine(buffer);
if (line != nullptr) {
++line_number;
return line;
}
} while (Fill(true));
if (!eof || buffer.empty())
return nullptr;
auto w = buffer.Write();
if (w.empty()) {
buffer.Grow(buffer.GetCapacity() + 1);
w = buffer.Write();
assert(!w.empty());
}
/* terminate the last line */
w[0] = 0;
char *line = buffer.Read().data;
buffer.Clear();
++line_number;
return line;
}
-102
View File
@@ -1,102 +0,0 @@
/*
* Copyright 2014-2019 Max Kellermann <max.kellermann@gmail.com>
*
* 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 BUFFERED_READER_HXX
#define BUFFERED_READER_HXX
#include "util/DynamicFifoBuffer.hxx"
#include <cstddef>
class Reader;
class BufferedReader {
static constexpr size_t MAX_SIZE = 512 * 1024;
Reader &reader;
DynamicFifoBuffer<char> buffer;
bool eof = false;
unsigned line_number = 0;
public:
explicit BufferedReader(Reader &_reader) noexcept
:reader(_reader), buffer(16384) {}
/**
* Reset the internal state. Should be called after rewinding
* the underlying #Reader.
*/
void Reset() noexcept {
buffer.Clear();
eof = false;
line_number = 0;
}
bool Fill(bool need_more);
[[gnu::pure]]
WritableBuffer<void> Read() const noexcept {
return buffer.Read().ToVoid();
}
/**
* Read a buffer of exactly the given size (without consuming
* it). Throws std::runtime_error if not enough data is
* available.
*/
void *ReadFull(size_t size);
void Consume(size_t n) noexcept {
buffer.Consume(n);
}
/**
* Read (and consume) data from the input buffer into the
* given buffer. Does not attempt to refill the buffer.
*/
size_t ReadFromBuffer(WritableBuffer<void> dest) noexcept;
/**
* Read data into the given buffer and consume it from our
* buffer. Throw an exception if the request cannot be
* forfilled.
*/
void ReadFull(WritableBuffer<void> dest);
char *ReadLine();
unsigned GetLineNumber() const noexcept {
return line_number;
}
};
#endif
-304
View File
@@ -1,304 +0,0 @@
/*
* Copyright (C) 2014-2018 Max Kellermann <max.kellermann@gmail.com>
*
* 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 "FileOutputStream.hxx"
#include "system/Error.hxx"
#include "util/StringFormat.hxx"
#ifdef __linux__
#include <fcntl.h>
#endif
#ifdef __linux__
FileOutputStream::FileOutputStream(FileDescriptor _directory_fd,
Path _path, Mode _mode)
:path(_path),
directory_fd(_directory_fd),
mode(_mode)
{
Open();
}
#endif
FileOutputStream::FileOutputStream(Path _path, Mode _mode)
:path(_path),
#ifdef __linux__
directory_fd(AT_FDCWD),
#endif
mode(_mode)
{
Open();
}
inline void
FileOutputStream::Open()
{
switch (mode) {
case Mode::CREATE:
OpenCreate(false);
break;
case Mode::CREATE_VISIBLE:
OpenCreate(true);
break;
case Mode::APPEND_EXISTING:
OpenAppend(false);
break;
case Mode::APPEND_OR_CREATE:
OpenAppend(true);
break;
}
}
#ifdef _WIN32
inline void
FileOutputStream::OpenCreate([[maybe_unused]] bool visible)
{
handle = CreateFile(path.c_str(), GENERIC_WRITE, 0, nullptr,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL|FILE_FLAG_WRITE_THROUGH,
nullptr);
if (!IsDefined())
throw FormatLastError("Failed to create %s",
path.ToUTF8().c_str());
}
inline void
FileOutputStream::OpenAppend(bool create)
{
handle = CreateFile(path.c_str(), GENERIC_WRITE, 0, nullptr,
create ? OPEN_ALWAYS : OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL|FILE_FLAG_WRITE_THROUGH,
nullptr);
if (!IsDefined())
throw FormatLastError("Failed to append to %s",
path.ToUTF8().c_str());
if (!SeekEOF()) {
auto code = GetLastError();
Close();
throw FormatLastError(code, "Failed seek end-of-file of %s",
path.ToUTF8().c_str());
}
}
uint64_t
FileOutputStream::Tell() const noexcept
{
LONG high = 0;
DWORD low = SetFilePointer(handle, 0, &high, FILE_CURRENT);
if (low == 0xffffffff)
return 0;
return uint64_t(high) << 32 | uint64_t(low);
}
void
FileOutputStream::Write(const void *data, size_t size)
{
assert(IsDefined());
DWORD nbytes;
if (!WriteFile(handle, data, size, &nbytes, nullptr))
throw FormatLastError("Failed to write to %s",
GetPath().c_str());
if (size_t(nbytes) != size)
throw FormatLastError(ERROR_DISK_FULL, "Failed to write to %s",
GetPath().c_str());
}
void
FileOutputStream::Commit()
{
assert(IsDefined());
Close();
}
void
FileOutputStream::Cancel() noexcept
{
assert(IsDefined());
Close();
DeleteFile(GetPath().c_str());
}
#else
#include <cerrno>
#include <fcntl.h>
#include <unistd.h>
#ifdef HAVE_O_TMPFILE
#ifndef O_TMPFILE
/* supported since Linux 3.11 */
#define __O_TMPFILE 020000000
#define O_TMPFILE (__O_TMPFILE | O_DIRECTORY)
#include <stdio.h>
#endif
/**
* Open a file using Linux's O_TMPFILE for writing the given file.
*/
static bool
OpenTempFile(FileDescriptor directory_fd,
FileDescriptor &fd, Path path) noexcept
{
if (directory_fd != FileDescriptor(AT_FDCWD))
return fd.Open(directory_fd, ".", O_TMPFILE|O_WRONLY, 0666);
const auto directory = path.GetDirectoryName();
if (directory.IsNull())
return false;
return fd.Open(directory.c_str(), O_TMPFILE|O_WRONLY, 0666);
}
#endif /* HAVE_O_TMPFILE */
inline void
FileOutputStream::OpenCreate(bool visible)
{
#ifdef HAVE_O_TMPFILE
/* try Linux's O_TMPFILE first */
is_tmpfile = !visible && OpenTempFile(directory_fd, fd, GetPath());
if (!is_tmpfile) {
#endif
/* fall back to plain POSIX */
if (!fd.Open(
#ifdef __linux__
directory_fd,
#endif
GetPath().c_str(),
O_WRONLY|O_CREAT|O_TRUNC,
0666))
throw FormatErrno("Failed to create %s",
GetPath().c_str());
#ifdef HAVE_O_TMPFILE
}
#else
(void)visible;
#endif
}
inline void
FileOutputStream::OpenAppend(bool create)
{
int flags = O_WRONLY|O_APPEND;
if (create)
flags |= O_CREAT;
if (!fd.Open(
#ifdef __linux__
directory_fd,
#endif
path.c_str(), flags))
throw FormatErrno("Failed to append to %s",
path.c_str());
}
uint64_t
FileOutputStream::Tell() const noexcept
{
return fd.Tell();
}
void
FileOutputStream::Write(const void *data, size_t size)
{
assert(IsDefined());
ssize_t nbytes = fd.Write(data, size);
if (nbytes < 0)
throw FormatErrno("Failed to write to %s", GetPath().c_str());
else if ((size_t)nbytes < size)
throw FormatErrno(ENOSPC, "Failed to write to %s",
GetPath().c_str());
}
void
FileOutputStream::Commit()
{
assert(IsDefined());
#ifdef HAVE_O_TMPFILE
if (is_tmpfile) {
unlinkat(directory_fd.Get(), GetPath().c_str(), 0);
/* hard-link the temporary file to the final path */
if (linkat(AT_FDCWD,
StringFormat<64>("/proc/self/fd/%d", fd.Get()),
directory_fd.Get(), path.c_str(),
AT_SYMLINK_FOLLOW) < 0)
throw FormatErrno("Failed to commit %s",
path.c_str());
}
#endif
if (!Close()) {
throw FormatErrno("Failed to commit %s", path.c_str());
}
}
void
FileOutputStream::Cancel() noexcept
{
assert(IsDefined());
Close();
switch (mode) {
case Mode::CREATE:
#ifdef HAVE_O_TMPFILE
if (!is_tmpfile)
#endif
#ifdef __linux__
unlinkat(directory_fd.Get(), GetPath().c_str(), 0);
#else
unlink(GetPath().c_str());
#endif
break;
case Mode::CREATE_VISIBLE:
case Mode::APPEND_EXISTING:
case Mode::APPEND_OR_CREATE:
/* can't roll this back */
break;
}
}
#endif
-177
View File
@@ -1,177 +0,0 @@
/*
* Copyright (C) 2014-2018 Max Kellermann <max.kellermann@gmail.com>
*
* 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 FILE_OUTPUT_STREAM_HXX
#define FILE_OUTPUT_STREAM_HXX
#include "OutputStream.hxx"
#include "fs/AllocatedPath.hxx"
#ifndef _WIN32
#include "io/FileDescriptor.hxx"
#endif
#include <cassert>
#include <cstdint>
#ifdef _WIN32
#include <fileapi.h>
#include <windef.h> // for HWND (needed by winbase.h)
#include <handleapi.h> // for INVALID_HANDLE_VALUE
#include <winbase.h> // for FILE_END
#endif
#if defined(__linux__) && !defined(ANDROID)
/* we don't use O_TMPFILE on Android because Android's braindead
SELinux policy disallows hardlinks
(https://android.googlesource.com/platform/external/sepolicy/+/85ce2c7),
even hardlinks from /proc/self/fd/N, which however is required to
use O_TMPFILE */
#define HAVE_O_TMPFILE
#endif
class Path;
class FileOutputStream final : public OutputStream {
const AllocatedPath path;
#ifdef __linux__
const FileDescriptor directory_fd;
#endif
#ifdef _WIN32
HANDLE handle = INVALID_HANDLE_VALUE;
#else
FileDescriptor fd = FileDescriptor::Undefined();
#endif
#ifdef HAVE_O_TMPFILE
/**
* Was O_TMPFILE used? If yes, then linkat() must be used to
* create a link to this file.
*/
bool is_tmpfile = false;
#endif
public:
enum class Mode : uint8_t {
/**
* Create a new file, or replace an existing file.
* File contents may not be visible until Commit() has
* been called.
*/
CREATE,
/**
* Like #CREATE, but no attempt is made to hide file
* contents during the transaction (e.g. via O_TMPFILE
* or a hidden temporary file).
*/
CREATE_VISIBLE,
/**
* Append to a file that already exists. If it does
* not, an exception is thrown.
*/
APPEND_EXISTING,
/**
* Like #APPEND_EXISTING, but create the file if it
* does not exist.
*/
APPEND_OR_CREATE,
};
private:
Mode mode;
public:
explicit FileOutputStream(Path _path, Mode _mode=Mode::CREATE);
#ifdef __linux__
FileOutputStream(FileDescriptor _directory_fd, Path _path,
Mode _mode=Mode::CREATE);
#endif
~FileOutputStream() noexcept {
if (IsDefined())
Cancel();
}
FileOutputStream(const FileOutputStream &) = delete;
FileOutputStream &operator=(const FileOutputStream &) = delete;
public:
Path GetPath() const noexcept {
return path;
}
[[gnu::pure]]
uint64_t Tell() const noexcept;
/* virtual methods from class OutputStream */
void Write(const void *data, size_t size) override;
void Commit();
void Cancel() noexcept;
private:
void OpenCreate(bool visible);
void OpenAppend(bool create);
void Open();
bool Close() noexcept {
assert(IsDefined());
#ifdef _WIN32
CloseHandle(handle);
handle = INVALID_HANDLE_VALUE;
return true;
#else
return fd.Close();
#endif
}
#ifdef _WIN32
bool SeekEOF() noexcept {
return SetFilePointer(handle, 0, nullptr,
FILE_END) != 0xffffffff;
}
#endif
bool IsDefined() const noexcept {
#ifdef _WIN32
return handle != INVALID_HANDLE_VALUE;
#else
return fd.IsDefined();
#endif
}
};
#endif
-161
View File
@@ -1,161 +0,0 @@
/*
* Copyright 2014-2019 Max Kellermann <max.kellermann@gmail.com>
*
* 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 "FileReader.hxx"
#include "fs/FileInfo.hxx"
#include "system/Error.hxx"
#include "io/Open.hxx"
#include <cassert>
#ifdef _WIN32
FileReader::FileReader(Path _path)
:path(_path),
handle(CreateFile(path.c_str(), GENERIC_READ, FILE_SHARE_READ,
nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
nullptr))
{
if (handle == INVALID_HANDLE_VALUE)
throw FormatLastError("Failed to open %s", path.ToUTF8().c_str());
}
FileInfo
FileReader::GetFileInfo() const
{
assert(IsDefined());
return FileInfo(path);
}
size_t
FileReader::Read(void *data, size_t size)
{
assert(IsDefined());
DWORD nbytes;
if (!ReadFile(handle, data, size, &nbytes, nullptr))
throw FormatLastError("Failed to read from %s",
path.ToUTF8().c_str());
return nbytes;
}
void
FileReader::Seek(off_t offset)
{
assert(IsDefined());
auto result = SetFilePointer(handle, offset, nullptr, FILE_BEGIN);
if (result == INVALID_SET_FILE_POINTER)
throw MakeLastError("Failed to seek");
}
void
FileReader::Skip(off_t offset)
{
assert(IsDefined());
auto result = SetFilePointer(handle, offset, nullptr, FILE_CURRENT);
if (result == INVALID_SET_FILE_POINTER)
throw MakeLastError("Failed to seek");
}
void
FileReader::Close() noexcept
{
assert(IsDefined());
CloseHandle(handle);
}
#else
FileReader::FileReader(Path _path)
:path(_path), fd(OpenReadOnly(path.c_str()))
{
}
FileInfo
FileReader::GetFileInfo() const
{
assert(IsDefined());
FileInfo info;
const bool success = fstat(fd.Get(), &info.st) == 0;
if (!success)
throw FormatErrno("Failed to access %s",
path.ToUTF8().c_str());
return info;
}
size_t
FileReader::Read(void *data, size_t size)
{
assert(IsDefined());
ssize_t nbytes = fd.Read(data, size);
if (nbytes < 0)
throw FormatErrno("Failed to read from %s", path.ToUTF8().c_str());
return nbytes;
}
void
FileReader::Seek(off_t offset)
{
assert(IsDefined());
auto result = fd.Seek(offset);
const bool success = result >= 0;
if (!success)
throw MakeErrno("Failed to seek");
}
void
FileReader::Skip(off_t offset)
{
assert(IsDefined());
auto result = fd.Skip(offset);
const bool success = result >= 0;
if (!success)
throw MakeErrno("Failed to seek");
}
void
FileReader::Close() noexcept
{
assert(IsDefined());
fd.Close();
}
#endif
-133
View File
@@ -1,133 +0,0 @@
/*
* Copyright 2014-2019 Max Kellermann <max.kellermann@gmail.com>
*
* 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 FILE_READER_HXX
#define FILE_READER_HXX
#include "Reader.hxx"
#include "fs/AllocatedPath.hxx"
#ifdef _WIN32
#include <fileapi.h>
#include <handleapi.h> // for INVALID_HANDLE_VALUE
#include <windef.h> // for HWND (needed by winbase.h)
#include <winbase.h> // for FILE_CURRENT
#else
#include "io/UniqueFileDescriptor.hxx"
#endif
class Path;
class FileInfo;
class FileReader final : public Reader {
AllocatedPath path;
#ifdef _WIN32
HANDLE handle;
#else
UniqueFileDescriptor fd;
#endif
public:
explicit FileReader(Path _path);
#ifdef _WIN32
FileReader(FileReader &&other) noexcept
:path(std::move(other.path)),
handle(std::exchange(other.handle, INVALID_HANDLE_VALUE)) {}
~FileReader() noexcept {
if (IsDefined())
Close();
}
#else
FileReader(FileReader &&other) noexcept
:path(std::move(other.path)),
fd(std::move(other.fd)) {}
#endif
protected:
bool IsDefined() const noexcept {
#ifdef _WIN32
return handle != INVALID_HANDLE_VALUE;
#else
return fd.IsDefined();
#endif
}
public:
#ifndef _WIN32
FileDescriptor GetFD() const noexcept {
return fd;
}
#endif
void Close() noexcept;
FileInfo GetFileInfo() const;
[[gnu::pure]]
uint64_t GetSize() const noexcept {
#ifdef _WIN32
LARGE_INTEGER size;
return GetFileSizeEx(handle, &size)
? size.QuadPart
: 0;
#else
return fd.GetSize();
#endif
}
[[gnu::pure]]
uint64_t GetPosition() const noexcept {
#ifdef _WIN32
LARGE_INTEGER zero;
zero.QuadPart = 0;
LARGE_INTEGER position;
return SetFilePointerEx(handle, zero, &position, FILE_CURRENT)
? position.QuadPart
: 0;
#else
return fd.Tell();
#endif
}
void Rewind() {
Seek(0);
}
void Seek(off_t offset);
void Skip(off_t offset);
/* virtual methods from class Reader */
size_t Read(void *data, size_t size) override;
};
#endif
-96
View File
@@ -1,96 +0,0 @@
/*
* Copyright 2014-2019 Max Kellermann <max.kellermann@gmail.com>
*
* 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 "GunzipReader.hxx"
#include "lib/zlib/Error.hxx"
GunzipReader::GunzipReader(Reader &_next)
:next(_next)
{
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)
throw ZlibError(result);
}
inline bool
GunzipReader::FillBuffer()
{
auto w = buffer.Write();
assert(!w.empty());
size_t nbytes = next.Read(w.data, w.size);
if (nbytes == 0)
return false;
buffer.Append(nbytes);
return true;
}
size_t
GunzipReader::Read(void *data, size_t size)
{
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.empty()) {
if (FillBuffer())
r = buffer.Read();
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)
throw ZlibError(result);
buffer.Consume(r.size - z.avail_in);
if (z.avail_out < size)
return size - z.avail_out;
}
}
-69
View File
@@ -1,69 +0,0 @@
/*
* Copyright 2014-2019 Max Kellermann <max.kellermann@gmail.com>
*
* 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 GUNZIP_READER_HXX
#define GUNZIP_READER_HXX
#include "Reader.hxx"
#include "util/StaticFifoBuffer.hxx"
#include <zlib.h>
/**
* A filter that decompresses data using zlib.
*/
class GunzipReader final : public Reader {
Reader &next;
bool eof = false;
z_stream z;
StaticFifoBuffer<Bytef, 65536> buffer;
public:
/**
* Construct the filter.
*
* Throws on error.
*/
explicit GunzipReader(Reader &_next);
~GunzipReader() noexcept {
inflateEnd(&z);
}
/* virtual methods from class Reader */
size_t Read(void *data, size_t size) override;
private:
bool FillBuffer();
};
#endif
-101
View File
@@ -1,101 +0,0 @@
/*
* Copyright (C) 2014-2018 Max Kellermann <max.kellermann@gmail.com>
*
* 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 "GzipOutputStream.hxx"
#include "lib/zlib/Error.hxx"
GzipOutputStream::GzipOutputStream(OutputStream &_next)
: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)
throw ZlibError(result);
}
GzipOutputStream::~GzipOutputStream()
{
deflateEnd(&z);
}
void
GzipOutputStream::Flush()
{
/* no more input */
z.next_in = nullptr;
z.avail_in = 0;
while (true) {
Bytef output[16384];
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);
if (result == Z_STREAM_END)
break;
else if (result != Z_OK)
throw ZlibError(result);
}
}
void
GzipOutputStream::Write(const void *_data, size_t size)
{
/* 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[16384];
z.next_out = output;
z.avail_out = sizeof(output);
int result = deflate(&z, Z_NO_FLUSH);
if (result != Z_OK)
throw ZlibError(result);
if (z.next_out > output)
next.Write(output, z.next_out - output);
}
}
-65
View File
@@ -1,65 +0,0 @@
/*
* Copyright (C) 2014-2018 Max Kellermann <max.kellermann@gmail.com>
*
* 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 GZIP_OUTPUT_STREAM_HXX
#define GZIP_OUTPUT_STREAM_HXX
#include "OutputStream.hxx"
#include <zlib.h>
/**
* 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.
*/
explicit GzipOutputStream(OutputStream &_next);
~GzipOutputStream();
/**
* Finish the file and write all data remaining in zlib's
* output buffer.
*/
void Flush();
/* virtual methods from class OutputStream */
void Write(const void *data, size_t size) override;
};
#endif
-33
View File
@@ -1,33 +0,0 @@
/*
* Copyright 2003-2021 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.
*/
#pragma once
class LineReader
{
public:
/**
* Reads a line from the input file, and strips trailing
* space. There is a reasonable maximum line length, only to
* prevent denial of service.
*
* @return a pointer to the line, or nullptr on end-of-file
*/
virtual char *ReadLine() = 0;
};
-46
View File
@@ -1,46 +0,0 @@
/*
* Copyright 2014-2019 Max Kellermann <max.kellermann@gmail.com>
*
* 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 OUTPUT_STREAM_HXX
#define OUTPUT_STREAM_HXX
#include <cstddef>
class OutputStream {
public:
OutputStream() = default;
OutputStream(const OutputStream &) = delete;
/**
* Throws std::exception on error.
*/
virtual void Write(const void *data, size_t size) = 0;
};
#endif
-59
View File
@@ -1,59 +0,0 @@
/*
* Copyright 2003-2021 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 "PeekReader.hxx"
#include <algorithm>
#include <cassert>
#include <string.h>
const void *
PeekReader::Peek(size_t size)
{
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);
if (nbytes == 0)
return nullptr;
buffer_size += nbytes;
} while (buffer_size < size);
return buffer;
}
size_t
PeekReader::Read(void *data, size_t size)
{
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);
}
-49
View File
@@ -1,49 +0,0 @@
/*
* Copyright 2003-2021 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 "Reader.hxx"
#include <cstdint>
/**
* 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 = 0, buffer_position = 0;
uint8_t buffer[64];
public:
explicit PeekReader(Reader &_next)
:next(_next) {}
const void *Peek(size_t size);
/* virtual methods from class Reader */
size_t Read(void *data, size_t size) override;
};
#endif
-57
View File
@@ -1,57 +0,0 @@
/*
* Copyright 2014-2019 Max Kellermann <max.kellermann@gmail.com>
*
* 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 READER_HXX
#define READER_HXX
#include <cstddef>
/**
* 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 end-of-stream
*/
[[gnu::nonnull]]
virtual size_t Read(void *data, size_t size) = 0;
};
#endif
-51
View File
@@ -1,51 +0,0 @@
/*
* Copyright 2014-2021 Max Kellermann <max.kellermann@gmail.com>
*
* 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 STDIO_OUTPUT_STREAM_HXX
#define STDIO_OUTPUT_STREAM_HXX
#include "OutputStream.hxx"
#include <stdio.h>
class StdioOutputStream final : public OutputStream {
FILE *const file;
public:
explicit StdioOutputStream(FILE *_file) noexcept:file(_file) {}
/* virtual methods from class OutputStream */
void Write(const void *data, size_t size) override {
fwrite(data, 1, size, file);
/* this class is debug-only and ignores errors */
}
};
#endif
+3 -3
View File
@@ -18,9 +18,9 @@
*/
#include "TextFile.hxx"
#include "FileReader.hxx"
#include "AutoGunzipReader.hxx"
#include "BufferedReader.hxx"
#include "io/FileReader.hxx"
#include "io/BufferedReader.hxx"
#include "lib/zlib/AutoGunzipReader.hxx"
#include "fs/Path.hxx"
#include <cassert>
+1 -1
View File
@@ -20,7 +20,7 @@
#ifndef MPD_TEXT_FILE_HXX
#define MPD_TEXT_FILE_HXX
#include "LineReader.hxx"
#include "io/LineReader.hxx"
#include "config.h"
#include <memory>
-13
View File
@@ -14,12 +14,7 @@ fs_sources = [
'CheckFile.cxx',
'LookupFile.cxx',
'DirectoryReader.cxx',
'io/PeekReader.cxx',
'io/FileReader.cxx',
'io/BufferedReader.cxx',
'io/TextFile.cxx',
'io/FileOutputStream.cxx',
'io/BufferedOutputStream.cxx',
]
if is_windows
@@ -28,14 +23,6 @@ else
shlwapi_dep = dependency('', required: false)
endif
if zlib_dep.found()
fs_sources += [
'io/GunzipReader.cxx',
'io/AutoGunzipReader.cxx',
'io/GzipOutputStream.cxx',
]
endif
fs = static_library(
'fs',
fs_sources,