Merge tag 'v0.22.4'
release v0.22.4
This commit is contained in:
@@ -91,7 +91,14 @@ song_print_info(Response &r, const LightSong &song, bool base) noexcept
|
||||
if (song.audio_format.IsDefined())
|
||||
r.Format("Format: %s\n", ToString(song.audio_format).c_str());
|
||||
|
||||
tag_print(r, song.tag);
|
||||
tag_print_values(r, song.tag);
|
||||
|
||||
const auto duration = song.GetDuration();
|
||||
if (!duration.IsNegative())
|
||||
r.Format("Time: %i\n"
|
||||
"duration: %1.3f\n",
|
||||
duration.RoundS(),
|
||||
duration.ToDoubleS());
|
||||
}
|
||||
|
||||
void
|
||||
|
@@ -84,6 +84,12 @@ public:
|
||||
*/
|
||||
TagMask tag_mask = TagMask::All();
|
||||
|
||||
/**
|
||||
* The maximum number of bytes transmitted in a binary
|
||||
* response. Can be changed with the "binarylimit" command.
|
||||
*/
|
||||
size_t binary_limit = 8192;
|
||||
|
||||
private:
|
||||
static constexpr size_t MAX_SUBSCRIPTIONS = 16;
|
||||
|
||||
@@ -122,6 +128,7 @@ public:
|
||||
~Client() noexcept;
|
||||
|
||||
using FullyBufferedSocket::GetEventLoop;
|
||||
using FullyBufferedSocket::GetOutputMaxSize;
|
||||
|
||||
gcc_pure
|
||||
bool IsExpired() const noexcept {
|
||||
|
@@ -59,7 +59,7 @@ Response::Format(const char *fmt, ...) noexcept
|
||||
bool
|
||||
Response::WriteBinary(ConstBuffer<void> payload) noexcept
|
||||
{
|
||||
assert(payload.size <= MAX_BINARY_SIZE);
|
||||
assert(payload.size <= client.binary_limit);
|
||||
|
||||
return Format("binary: %zu\n", payload.size) &&
|
||||
Write(payload.data, payload.size) &&
|
||||
|
@@ -75,9 +75,9 @@ public:
|
||||
bool Write(const void *data, size_t length) noexcept;
|
||||
bool Write(const char *data) noexcept;
|
||||
bool FormatV(const char *fmt, std::va_list args) noexcept;
|
||||
bool Format(const char *fmt, ...) noexcept;
|
||||
|
||||
static constexpr size_t MAX_BINARY_SIZE = 8192;
|
||||
gcc_printf(2,3)
|
||||
bool Format(const char *fmt, ...) noexcept;
|
||||
|
||||
/**
|
||||
* Write a binary chunk; this writes the "binary" line, the
|
||||
|
@@ -87,6 +87,7 @@ static constexpr struct command commands[] = {
|
||||
{ "addid", PERMISSION_ADD, 1, 2, handle_addid },
|
||||
{ "addtagid", PERMISSION_ADD, 3, 3, handle_addtagid },
|
||||
{ "albumart", PERMISSION_READ, 2, 2, handle_album_art },
|
||||
{ "binarylimit", PERMISSION_NONE, 1, 1, handle_binary_limit },
|
||||
{ "channels", PERMISSION_READ, 0, 0, handle_channels },
|
||||
{ "clear", PERMISSION_CONTROL, 0, 0, handle_clear },
|
||||
{ "clearerror", PERMISSION_CONTROL, 0, 0, handle_clearerror },
|
||||
|
@@ -40,6 +40,21 @@ handle_ping([[maybe_unused]] Client &client, [[maybe_unused]] Request args,
|
||||
return CommandResult::OK;
|
||||
}
|
||||
|
||||
CommandResult
|
||||
handle_binary_limit(Client &client, Request args,
|
||||
[[maybe_unused]] Response &r)
|
||||
{
|
||||
size_t value = args.ParseUnsigned(0, client.GetOutputMaxSize() - 4096);
|
||||
if (value < 64) {
|
||||
r.Error(ACK_ERROR_ARG, "Value too small");
|
||||
return CommandResult::ERROR;
|
||||
}
|
||||
|
||||
client.binary_limit = value;
|
||||
|
||||
return CommandResult::OK;
|
||||
}
|
||||
|
||||
CommandResult
|
||||
handle_password(Client &client, Request args, Response &r)
|
||||
{
|
||||
|
@@ -32,6 +32,9 @@ handle_close(Client &client, Request request, Response &response);
|
||||
CommandResult
|
||||
handle_ping(Client &client, Request request, Response &response);
|
||||
|
||||
CommandResult
|
||||
handle_binary_limit(Client &client, Request request, Response &response);
|
||||
|
||||
CommandResult
|
||||
handle_password(Client &client, Request request, Response &response);
|
||||
|
||||
|
@@ -43,6 +43,7 @@
|
||||
#include "thread/Mutex.hxx"
|
||||
#include "Log.hxx"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cinttypes> /* for PRIu64 */
|
||||
|
||||
@@ -202,17 +203,26 @@ read_stream_art(Response &r, const char *uri, size_t offset)
|
||||
|
||||
const offset_type art_file_size = is->GetSize();
|
||||
|
||||
uint8_t buffer[Response::MAX_BINARY_SIZE];
|
||||
size_t read_size;
|
||||
if (offset > art_file_size) {
|
||||
r.Error(ACK_ERROR_ARG, "Offset too large");
|
||||
return CommandResult::ERROR;
|
||||
}
|
||||
|
||||
{
|
||||
std::size_t buffer_size =
|
||||
std::min<offset_type>(art_file_size - offset,
|
||||
r.GetClient().binary_limit);
|
||||
|
||||
std::unique_ptr<std::byte[]> buffer(new std::byte[buffer_size]);
|
||||
|
||||
std::size_t read_size = 0;
|
||||
if (buffer_size > 0) {
|
||||
std::unique_lock<Mutex> lock(mutex);
|
||||
is->Seek(lock, offset);
|
||||
read_size = is->Read(lock, &buffer, sizeof(buffer));
|
||||
read_size = is->Read(lock, buffer.get(), buffer_size);
|
||||
}
|
||||
|
||||
r.Format("size: %" PRIoffset "\n", art_file_size);
|
||||
r.WriteBinary({buffer, read_size});
|
||||
r.WriteBinary({buffer.get(), read_size});
|
||||
|
||||
return CommandResult::OK;
|
||||
}
|
||||
@@ -293,14 +303,16 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
response.Format("size: %" PRIoffset "\n", buffer.size);
|
||||
response.Format("size: %zu\n", buffer.size);
|
||||
|
||||
if (mime_type != nullptr)
|
||||
response.Format("type: %s\n", mime_type);
|
||||
|
||||
buffer.size -= offset;
|
||||
if (buffer.size > Response::MAX_BINARY_SIZE)
|
||||
buffer.size = Response::MAX_BINARY_SIZE;
|
||||
|
||||
const std::size_t binary_limit = response.GetClient().binary_limit;
|
||||
if (buffer.size > binary_limit)
|
||||
buffer.size = binary_limit;
|
||||
buffer.data = OffsetPointer(buffer.data, offset);
|
||||
|
||||
response.WriteBinary(buffer);
|
||||
|
@@ -54,12 +54,12 @@ public:
|
||||
return ParseCommandArgInt(data[idx], min_value, max_value);
|
||||
}
|
||||
|
||||
int ParseUnsigned(unsigned idx) const {
|
||||
unsigned ParseUnsigned(unsigned idx) const {
|
||||
assert(idx < size);
|
||||
return ParseCommandArgUnsigned(data[idx]);
|
||||
}
|
||||
|
||||
int ParseUnsigned(unsigned idx, unsigned max_value) const {
|
||||
unsigned ParseUnsigned(unsigned idx, unsigned max_value) const {
|
||||
assert(idx < size);
|
||||
return ParseCommandArgUnsigned(data[idx], max_value);
|
||||
}
|
||||
|
@@ -18,11 +18,11 @@
|
||||
*/
|
||||
|
||||
#include "Directory.hxx"
|
||||
#include "ExportedSong.hxx"
|
||||
#include "SongSort.hxx"
|
||||
#include "Song.hxx"
|
||||
#include "Mount.hxx"
|
||||
#include "db/LightDirectory.hxx"
|
||||
#include "song/LightSong.hxx"
|
||||
#include "db/Uri.hxx"
|
||||
#include "db/DatabaseLock.hxx"
|
||||
#include "db/Interface.hxx"
|
||||
@@ -234,7 +234,7 @@ Directory::Walk(bool recursive, const SongFilter *filter,
|
||||
|
||||
if (visit_song) {
|
||||
for (auto &song : songs){
|
||||
const LightSong song2 = song.Export();
|
||||
const auto song2 = song.Export();
|
||||
if (filter == nullptr || filter->Match(song2))
|
||||
visit_song(song2);
|
||||
}
|
||||
|
42
src/db/plugins/simple/ExportedSong.hxx
Normal file
42
src/db/plugins/simple/ExportedSong.hxx
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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_DB_SIMPLE_EXPORTED_SONG_HXX
|
||||
#define MPD_DB_SIMPLE_EXPORTED_SONG_HXX
|
||||
|
||||
#include "song/LightSong.hxx"
|
||||
#include "tag/Tag.hxx"
|
||||
|
||||
/**
|
||||
* The return type for Song::Export(). In addition to implementing
|
||||
* #LightSong, it hold allocations necessary to represent the #Song as
|
||||
* a #LightSong, e.g. a merged #Tag.
|
||||
*/
|
||||
class ExportedSong : public LightSong {
|
||||
Tag tag_buffer;
|
||||
|
||||
public:
|
||||
using LightSong::LightSong;
|
||||
|
||||
ExportedSong(const char *_uri, Tag &&_tag) noexcept
|
||||
:LightSong(_uri, tag_buffer),
|
||||
tag_buffer(std::move(_tag)) {}
|
||||
};
|
||||
|
||||
#endif
|
@@ -233,25 +233,25 @@ SimpleDatabase::GetSong(std::string_view uri) const
|
||||
"No such song");
|
||||
|
||||
const Song *song = r.directory->FindSong(r.rest);
|
||||
protect.unlock();
|
||||
if (song == nullptr)
|
||||
throw DatabaseError(DatabaseErrorCode::NOT_FOUND,
|
||||
"No such song");
|
||||
|
||||
light_song.Construct(song->Export());
|
||||
exported_song.Construct(song->Export());
|
||||
protect.unlock();
|
||||
|
||||
#ifndef NDEBUG
|
||||
++borrowed_song_count;
|
||||
#endif
|
||||
|
||||
return &light_song.Get();
|
||||
return &exported_song.Get();
|
||||
}
|
||||
|
||||
void
|
||||
SimpleDatabase::ReturnSong([[maybe_unused]] const LightSong *song) const noexcept
|
||||
{
|
||||
assert(song != nullptr);
|
||||
assert(song == prefixed_light_song || song == &light_song.Get());
|
||||
assert(song == prefixed_light_song || song == &exported_song.Get());
|
||||
|
||||
if (prefixed_light_song != nullptr) {
|
||||
delete prefixed_light_song;
|
||||
@@ -262,7 +262,7 @@ SimpleDatabase::ReturnSong([[maybe_unused]] const LightSong *song) const noexcep
|
||||
--borrowed_song_count;
|
||||
#endif
|
||||
|
||||
light_song.Destruct();
|
||||
exported_song.Destruct();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -316,7 +316,7 @@ SimpleDatabase::Visit(const DatabaseSelection &selection,
|
||||
if (visit_song) {
|
||||
Song *song = r.directory->FindSong(r.rest);
|
||||
if (song != nullptr) {
|
||||
const LightSong song2 = song->Export();
|
||||
const auto song2 = song->Export();
|
||||
if (selection.Match(song2))
|
||||
visit_song(song2);
|
||||
|
||||
|
@@ -20,10 +20,10 @@
|
||||
#ifndef MPD_SIMPLE_DATABASE_PLUGIN_HXX
|
||||
#define MPD_SIMPLE_DATABASE_PLUGIN_HXX
|
||||
|
||||
#include "ExportedSong.hxx"
|
||||
#include "db/Interface.hxx"
|
||||
#include "db/Ptr.hxx"
|
||||
#include "fs/AllocatedPath.hxx"
|
||||
#include "song/LightSong.hxx"
|
||||
#include "util/Manual.hxx"
|
||||
#include "util/Compiler.h"
|
||||
#include "config.h"
|
||||
@@ -63,7 +63,7 @@ class SimpleDatabase : public Database {
|
||||
/**
|
||||
* A buffer for GetSong().
|
||||
*/
|
||||
mutable Manual<LightSong> light_song;
|
||||
mutable Manual<ExportedSong> exported_song;
|
||||
|
||||
#ifndef NDEBUG
|
||||
mutable unsigned borrowed_song_count;
|
||||
|
@@ -18,11 +18,15 @@
|
||||
*/
|
||||
|
||||
#include "Song.hxx"
|
||||
#include "ExportedSong.hxx"
|
||||
#include "Directory.hxx"
|
||||
#include "tag/Tag.hxx"
|
||||
#include "tag/Builder.hxx"
|
||||
#include "song/DetachedSong.hxx"
|
||||
#include "song/LightSong.hxx"
|
||||
#include "fs/Traits.hxx"
|
||||
#include "time/ChronoUtil.hxx"
|
||||
#include "util/IterableSplitString.hxx"
|
||||
|
||||
Song::Song(DetachedSong &&other, Directory &_parent) noexcept
|
||||
:tag(std::move(other.WritableTag())),
|
||||
@@ -53,17 +57,87 @@ Song::GetURI() const noexcept
|
||||
}
|
||||
}
|
||||
|
||||
LightSong
|
||||
/**
|
||||
* Path name traversal of a #Directory.
|
||||
*/
|
||||
gcc_pure
|
||||
static const Directory *
|
||||
FindTargetDirectory(const Directory &base, StringView path) noexcept
|
||||
{
|
||||
const auto *directory = &base;
|
||||
for (const StringView name : IterableSplitString(path, '/')) {
|
||||
if (name.empty() || name.Equals("."))
|
||||
continue;
|
||||
|
||||
directory = name.Equals("..")
|
||||
? directory->parent
|
||||
: directory->FindChild(name);
|
||||
if (directory == nullptr)
|
||||
break;
|
||||
}
|
||||
|
||||
return directory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Path name traversal of a #Song.
|
||||
*/
|
||||
gcc_pure
|
||||
static const Song *
|
||||
FindTargetSong(const Directory &_directory, StringView target) noexcept
|
||||
{
|
||||
auto [path, last] = target.SplitLast('/');
|
||||
if (last == nullptr) {
|
||||
last = path;
|
||||
path = nullptr;
|
||||
}
|
||||
|
||||
if (last.empty())
|
||||
return nullptr;
|
||||
|
||||
const auto *directory = FindTargetDirectory(_directory, path);
|
||||
if (directory == nullptr)
|
||||
return nullptr;
|
||||
|
||||
return directory->FindSong(last);
|
||||
}
|
||||
|
||||
ExportedSong
|
||||
Song::Export() const noexcept
|
||||
{
|
||||
LightSong dest(filename.c_str(), tag);
|
||||
const auto *target_song = !target.empty()
|
||||
? FindTargetSong(parent, (std::string_view)target)
|
||||
: nullptr;
|
||||
|
||||
Tag merged_tag;
|
||||
if (target_song != nullptr) {
|
||||
/* if we found the target song (which may be the
|
||||
underlying song file of a CUE file), merge the tags
|
||||
from that song with this song's tags (from the CUE
|
||||
file) */
|
||||
TagBuilder builder(tag);
|
||||
builder.Complement(target_song->tag);
|
||||
merged_tag = builder.Commit();
|
||||
}
|
||||
|
||||
ExportedSong dest = merged_tag.IsDefined()
|
||||
? ExportedSong(filename.c_str(), std::move(merged_tag))
|
||||
: ExportedSong(filename.c_str(), tag);
|
||||
if (!parent.IsRoot())
|
||||
dest.directory = parent.GetPath();
|
||||
if (!target.empty())
|
||||
dest.real_uri = target.c_str();
|
||||
dest.mtime = mtime;
|
||||
dest.start_time = start_time;
|
||||
dest.end_time = end_time;
|
||||
dest.audio_format = audio_format;
|
||||
dest.mtime = IsNegative(mtime) && target_song != nullptr
|
||||
? target_song->mtime
|
||||
: mtime;
|
||||
dest.start_time = start_time.IsZero() && target_song != nullptr
|
||||
? target_song->start_time
|
||||
: start_time;
|
||||
dest.end_time = end_time.IsZero() && target_song != nullptr
|
||||
? target_song->end_time
|
||||
: end_time;
|
||||
dest.audio_format = audio_format.IsDefined() || target_song == nullptr
|
||||
? audio_format
|
||||
: target_song->audio_format;
|
||||
return dest;
|
||||
}
|
||||
|
@@ -32,8 +32,8 @@
|
||||
#include <string>
|
||||
|
||||
struct StringView;
|
||||
struct LightSong;
|
||||
struct Directory;
|
||||
class ExportedSong;
|
||||
class DetachedSong;
|
||||
class Storage;
|
||||
class ArchiveFile;
|
||||
@@ -153,7 +153,7 @@ struct Song {
|
||||
std::string GetURI() const noexcept;
|
||||
|
||||
gcc_pure
|
||||
LightSong Export() const noexcept;
|
||||
ExportedSong Export() const noexcept;
|
||||
};
|
||||
|
||||
typedef boost::intrusive::list<Song,
|
||||
|
@@ -48,6 +48,10 @@ public:
|
||||
BufferedSocket::Close();
|
||||
}
|
||||
|
||||
std::size_t GetOutputMaxSize() const noexcept {
|
||||
return output.max_size();
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* @return the number of bytes written to the socket, 0 if the
|
||||
|
@@ -369,8 +369,15 @@ input_curl_init(EventLoop &event_loop, const ConfigBlock &block)
|
||||
proxy_user = block.GetBlockValue("proxy_user");
|
||||
proxy_password = block.GetBlockValue("proxy_password");
|
||||
|
||||
verify_peer = block.GetBlockValue("verify_peer", true);
|
||||
verify_host = block.GetBlockValue("verify_host", true);
|
||||
#ifdef ANDROID
|
||||
// TODO: figure out how to use Android's CA certificates and re-enable verify
|
||||
constexpr bool default_verify = false;
|
||||
#else
|
||||
constexpr bool default_verify = true;
|
||||
#endif
|
||||
|
||||
verify_peer = block.GetBlockValue("verify_peer", default_verify);
|
||||
verify_host = block.GetBlockValue("verify_host", default_verify);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@@ -40,6 +40,13 @@
|
||||
|
||||
#include <array>
|
||||
|
||||
#if GCC_CHECK_VERSION(11,0)
|
||||
#pragma GCC diagnostic push
|
||||
/* bogus GCC 11 warning "ovector may be used uninitialized" in the
|
||||
ovector.size() call */
|
||||
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
|
||||
#endif
|
||||
|
||||
class RegexPointer {
|
||||
protected:
|
||||
pcre *re = nullptr;
|
||||
@@ -63,4 +70,8 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
#if GCC_CHECK_VERSION(11,0)
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@@ -33,6 +33,7 @@
|
||||
#include <SLES/OpenSLES.h>
|
||||
#include <SLES/OpenSLES_Android.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <iterator>
|
||||
#include <stdexcept>
|
||||
|
||||
|
@@ -468,19 +468,11 @@ CurlStorage::GetInfo(std::string_view uri_utf8, [[maybe_unused]] bool follow)
|
||||
|
||||
gcc_pure
|
||||
static std::string_view
|
||||
UriPathOrSlash(const char *uri, bool relative) noexcept
|
||||
UriPathOrSlash(const char *uri) noexcept
|
||||
{
|
||||
auto path = uri_get_path(uri);
|
||||
if (path.data() == nullptr)
|
||||
path = "/";
|
||||
else if (relative) {
|
||||
// search after first slash
|
||||
path = path.substr(1);
|
||||
auto slash = path.find('/');
|
||||
if (slash != std::string_view::npos)
|
||||
path = path.substr(slash);
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
@@ -489,15 +481,13 @@ UriPathOrSlash(const char *uri, bool relative) noexcept
|
||||
*/
|
||||
class HttpListDirectoryOperation final : public PropfindOperation {
|
||||
const std::string base_path;
|
||||
const std::string base_path_relative;
|
||||
|
||||
MemoryStorageDirectoryReader::List entries;
|
||||
|
||||
public:
|
||||
HttpListDirectoryOperation(CurlGlobal &curl, const char *uri)
|
||||
:PropfindOperation(curl, uri, 1),
|
||||
base_path(CurlUnescape(GetEasy(), UriPathOrSlash(uri, false))),
|
||||
base_path_relative(CurlUnescape(GetEasy(), UriPathOrSlash(uri, true))) {}
|
||||
base_path(CurlUnescape(GetEasy(), UriPathOrSlash(uri))) {}
|
||||
|
||||
std::unique_ptr<StorageDirectoryReader> Perform() {
|
||||
DeferStart();
|
||||
@@ -523,15 +513,9 @@ private:
|
||||
/* kludge: ignoring case in this comparison to avoid
|
||||
false negatives if the web server uses a different
|
||||
case */
|
||||
if (uri_has_scheme(path)) {
|
||||
path = StringAfterPrefixIgnoreCase(path, base_path.c_str());
|
||||
} else {
|
||||
path = StringAfterPrefixIgnoreCase(path, base_path_relative.c_str());
|
||||
}
|
||||
|
||||
if (path == nullptr || path.empty()) {
|
||||
path = StringAfterPrefixIgnoreCase(path, base_path.c_str());
|
||||
if (path == nullptr || path.empty())
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const char *slash = path.Find('/');
|
||||
if (slash == nullptr)
|
||||
|
@@ -235,7 +235,7 @@ public:
|
||||
w = Write();
|
||||
}
|
||||
|
||||
size_t n = std::min(r.size, w.size);
|
||||
const auto n = std::min(r.size, w.size);
|
||||
|
||||
std::move(r.data, r.data + n, w.data);
|
||||
Append(n);
|
||||
|
@@ -25,7 +25,7 @@
|
||||
|
||||
#include <string.h>
|
||||
|
||||
PeakBuffer::~PeakBuffer()
|
||||
PeakBuffer::~PeakBuffer() noexcept
|
||||
{
|
||||
delete normal_buffer;
|
||||
delete peak_buffer;
|
||||
@@ -57,7 +57,7 @@ PeakBuffer::Read() const noexcept
|
||||
}
|
||||
|
||||
void
|
||||
PeakBuffer::Consume(size_t length) noexcept
|
||||
PeakBuffer::Consume(std::size_t length) noexcept
|
||||
{
|
||||
if (normal_buffer != nullptr && !normal_buffer->empty()) {
|
||||
normal_buffer->Consume(length);
|
||||
@@ -75,25 +75,25 @@ PeakBuffer::Consume(size_t length) noexcept
|
||||
}
|
||||
}
|
||||
|
||||
static size_t
|
||||
AppendTo(DynamicFifoBuffer<uint8_t> &buffer,
|
||||
static std::size_t
|
||||
AppendTo(DynamicFifoBuffer<std::byte> &buffer,
|
||||
const void *data, size_t length) noexcept
|
||||
{
|
||||
assert(data != nullptr);
|
||||
assert(length > 0);
|
||||
|
||||
size_t total = 0;
|
||||
std::size_t total = 0;
|
||||
|
||||
do {
|
||||
const auto p = buffer.Write();
|
||||
if (p.empty())
|
||||
break;
|
||||
|
||||
const size_t nbytes = std::min(length, p.size);
|
||||
const std::size_t nbytes = std::min(length, p.size);
|
||||
memcpy(p.data, data, nbytes);
|
||||
buffer.Append(nbytes);
|
||||
|
||||
data = (const uint8_t *)data + nbytes;
|
||||
data = (const std::byte *)data + nbytes;
|
||||
length -= nbytes;
|
||||
total += nbytes;
|
||||
} while (length > 0);
|
||||
@@ -102,22 +102,22 @@ AppendTo(DynamicFifoBuffer<uint8_t> &buffer,
|
||||
}
|
||||
|
||||
bool
|
||||
PeakBuffer::Append(const void *data, size_t length)
|
||||
PeakBuffer::Append(const void *data, std::size_t length)
|
||||
{
|
||||
if (length == 0)
|
||||
return true;
|
||||
|
||||
if (peak_buffer != nullptr && !peak_buffer->empty()) {
|
||||
size_t nbytes = AppendTo(*peak_buffer, data, length);
|
||||
std::size_t nbytes = AppendTo(*peak_buffer, data, length);
|
||||
return nbytes == length;
|
||||
}
|
||||
|
||||
if (normal_buffer == nullptr)
|
||||
normal_buffer = new DynamicFifoBuffer<uint8_t>(normal_size);
|
||||
normal_buffer = new DynamicFifoBuffer<std::byte>(normal_size);
|
||||
|
||||
size_t nbytes = AppendTo(*normal_buffer, data, length);
|
||||
std::size_t nbytes = AppendTo(*normal_buffer, data, length);
|
||||
if (nbytes > 0) {
|
||||
data = (const uint8_t *)data + nbytes;
|
||||
data = (const std::byte *)data + nbytes;
|
||||
length -= nbytes;
|
||||
if (length == 0)
|
||||
return true;
|
||||
@@ -125,7 +125,7 @@ PeakBuffer::Append(const void *data, size_t length)
|
||||
|
||||
if (peak_buffer == nullptr) {
|
||||
if (peak_size > 0)
|
||||
peak_buffer = new DynamicFifoBuffer<uint8_t>(peak_size);
|
||||
peak_buffer = new DynamicFifoBuffer<std::byte>(peak_size);
|
||||
if (peak_buffer == nullptr)
|
||||
return false;
|
||||
}
|
||||
|
@@ -23,7 +23,6 @@
|
||||
#include "Compiler.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
template<typename T> struct WritableBuffer;
|
||||
template<typename T> class DynamicFifoBuffer;
|
||||
@@ -34,16 +33,16 @@ template<typename T> class DynamicFifoBuffer;
|
||||
* kernel when it has been consumed.
|
||||
*/
|
||||
class PeakBuffer {
|
||||
size_t normal_size, peak_size;
|
||||
std::size_t normal_size, peak_size;
|
||||
|
||||
DynamicFifoBuffer<uint8_t> *normal_buffer, *peak_buffer;
|
||||
DynamicFifoBuffer<std::byte> *normal_buffer, *peak_buffer;
|
||||
|
||||
public:
|
||||
PeakBuffer(size_t _normal_size, size_t _peak_size)
|
||||
PeakBuffer(std::size_t _normal_size, std::size_t _peak_size) noexcept
|
||||
:normal_size(_normal_size), peak_size(_peak_size),
|
||||
normal_buffer(nullptr), peak_buffer(nullptr) {}
|
||||
|
||||
PeakBuffer(PeakBuffer &&other)
|
||||
PeakBuffer(PeakBuffer &&other) noexcept
|
||||
:normal_size(other.normal_size), peak_size(other.peak_size),
|
||||
normal_buffer(other.normal_buffer),
|
||||
peak_buffer(other.peak_buffer) {
|
||||
@@ -51,20 +50,24 @@ public:
|
||||
other.peak_buffer = nullptr;
|
||||
}
|
||||
|
||||
~PeakBuffer();
|
||||
~PeakBuffer() noexcept;
|
||||
|
||||
PeakBuffer(const PeakBuffer &) = delete;
|
||||
PeakBuffer &operator=(const PeakBuffer &) = delete;
|
||||
|
||||
std::size_t max_size() const noexcept {
|
||||
return normal_size + peak_size;
|
||||
}
|
||||
|
||||
gcc_pure
|
||||
bool empty() const noexcept;
|
||||
|
||||
gcc_pure
|
||||
WritableBuffer<void> Read() const noexcept;
|
||||
|
||||
void Consume(size_t length) noexcept;
|
||||
void Consume(std::size_t length) noexcept;
|
||||
|
||||
bool Append(const void *data, size_t length);
|
||||
bool Append(const void *data, std::size_t length);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
Reference in New Issue
Block a user