Commit 8279cafd authored by Max Kellermann's avatar Max Kellermann

Merge tag 'v0.22.4'

release v0.22.4
parents 014c2a82 a0d76c3b
......@@ -2,7 +2,11 @@ ver 0.23 (not yet released)
* protocol
- new command "getvol"
ver 0.22.4 (not yet released)
ver 0.22.4 (2021/01/21)
* protocol
- add command "binarylimit" to allow larger chunk sizes
- fix "readpicture" on 32 bit machines
- show duration and tags of songs in virtual playlist (CUE) folders
* storage
- curl: fix several WebDAV protocol bugs
* decoder
......@@ -11,6 +15,8 @@ ver 0.22.4 (not yet released)
- ffmpeg: detect the output sample format
* output
- moveoutput: fix always_on and tag lost on move
* Android
- enable https:// support (via OpenSSL)
ver 0.22.3 (2020/11/06)
* playlist
......
......@@ -103,7 +103,7 @@ class AndroidNdkToolchain:
llvm_bin = os.path.join(llvm_path, 'bin')
self.cc = os.path.join(llvm_bin, 'clang')
self.cxx = os.path.join(llvm_bin, 'clang++')
common_flags += ' -target ' + llvm_triple + ' -integrated-as -gcc-toolchain ' + toolchain_path
common_flags += ' -target ' + llvm_triple + ' -gcc-toolchain ' + toolchain_path
common_flags += ' -fvisibility=hidden -fdata-sections -ffunction-sections'
......@@ -172,6 +172,7 @@ thirdparty_libs = [
wildmidi,
gme,
ffmpeg,
openssl,
curl,
libexpat,
libnfs,
......
......@@ -130,7 +130,8 @@ audio_output
replaygain <off or album or track or auto>
If specified, mpd will adjust the volume of songs played using ReplayGain
tags (see http://www.replaygain.org/). Setting this to "album" will
tags (see https://wiki.hydrogenaud.io/index.php?title=Replaygain).
Setting this to "album" will
adjust volume using the album's ReplayGain tags, while setting it to "track"
will adjust it using the track ReplayGain tags. "auto" uses the track
ReplayGain tags if random play is activated otherwise the album ReplayGain
......
......@@ -372,7 +372,8 @@ input {
# the argument "off", "album", "track" or "auto". "auto" is a special mode that
# chooses between "track" and "album" depending on the current state of
# random playback. If random playback is enabled then "track" mode is used.
# See <http://www.replaygain.org> for more details about ReplayGain.
# See <https://wiki.hydrogenaud.io/index.php?title=Replaygain> for
# more details about ReplayGain.
# This setting is off by default.
#
#replaygain "album"
......
......@@ -69,11 +69,14 @@ that, the specified number of bytes of binary data follows, then a
newline, and finally the ``OK`` line.
If the object to be transmitted is large, the server may choose a
reasonable chunk size and transmit only a portion. Usually, the
response also contains a ``size`` line which specifies the total
(uncropped) size, and the command usually has a way to specify an
offset into the object; this way, the client can copy the whole file
without blocking the connection for too long.
reasonable chunk size and transmit only a portion. The maximum chunk
size can be changed by clients with the :ref:`binarylimit
<command_binarylimit>` command.
Usually, the response also contains a ``size`` line which specifies
the total (uncropped) size, and the command usually has a way to
specify an offset into the object; this way, the client can copy the
whole file without blocking the connection for too long.
Example::
......@@ -739,7 +742,7 @@ Whenever possible, ids should be used.
.. _command_playlistfind:
:command:`playlistfind {TAG} {NEEDLE}`
:command:`playlistfind {FILTER}`
Finds songs in the queue with strict
matching.
......@@ -760,7 +763,7 @@ Whenever possible, ids should be used.
.. _command_playlistsearch:
:command:`playlistsearch {TAG} {NEEDLE}`
:command:`playlistsearch {FILTER}`
Searches case-insensitively for partial matches in the
queue.
......@@ -1367,6 +1370,17 @@ Connection settings
:command:`ping`
Does nothing but return "OK".
.. _command_binarylimit:
:command:`binarylimit SIZE` [#since_0_22_4]_
Set the maximum :ref:`binary response <binary>` size for the
current connection to the specified number of bytes.
A bigger value means less overhead for transmitting large
entities, but it also means that the connection is blocked for a
longer time.
.. _command_tagtypes:
:command:`tagtypes`
......@@ -1595,3 +1609,4 @@ client-to-client messages are local to the current partition.
.. [#since_0_19] Since :program:`MPD` 0.19
.. [#since_0_20] Since :program:`MPD` 0.20
.. [#since_0_21] Since :program:`MPD` 0.21
.. [#since_0_22_4] Since :program:`MPD` 0.22.4
......@@ -167,7 +167,7 @@ Compiling for Android
You need:
* Android SDK
* Android NDK
* `Android NDK r22 <https://developer.android.com/ndk/downloads>`_
Just like with the native build, unpack the :program:`MPD` source
tarball and change into the directory. Then, instead of
......
......@@ -10,11 +10,6 @@ class FfmpegProject(Project):
self.configure_args = configure_args
self.cppflags = cppflags
def _filter_cflags(self, flags):
# FFmpeg expects the GNU as syntax
flags = flags.replace(' -integrated-as ', ' -no-integrated-as ')
return flags
def build(self, toolchain):
src = self.unpack(toolchain)
build = self.make_build_path(toolchain)
......@@ -36,8 +31,8 @@ class FfmpegProject(Project):
'--cc=' + toolchain.cc,
'--cxx=' + toolchain.cxx,
'--nm=' + toolchain.nm,
'--extra-cflags=' + self._filter_cflags(toolchain.cflags) + ' ' + toolchain.cppflags + ' ' + self.cppflags,
'--extra-cxxflags=' + self._filter_cflags(toolchain.cxxflags) + ' ' + toolchain.cppflags + ' ' + self.cppflags,
'--extra-cflags=' + toolchain.cflags + ' ' + toolchain.cppflags + ' ' + self.cppflags,
'--extra-cxxflags=' + toolchain.cxxflags + ' ' + toolchain.cppflags + ' ' + self.cppflags,
'--extra-ldflags=' + toolchain.ldflags,
'--extra-libs=' + toolchain.libs,
'--ar=' + toolchain.ar,
......
......@@ -7,6 +7,7 @@ from build.meson import MesonProject
from build.cmake import CmakeProject
from build.autotools import AutotoolsProject
from build.ffmpeg import FfmpegProject
from build.openssl import OpenSSLProject
from build.boost import BoostProject
libmpdclient = MesonProject(
......@@ -376,9 +377,15 @@ ffmpeg = FfmpegProject(
],
)
openssl = OpenSSLProject(
'https://www.openssl.org/source/openssl-3.0.0-alpha10.tar.gz',
'b1699acf2148db31f12edf5ebfdf12a92bfd3f0e60538d169710408a3cd3b138',
'include/openssl/ossl_typ.h',
)
curl = AutotoolsProject(
'http://curl.haxx.se/download/curl-7.73.0.tar.xz',
'7c4c7ca4ea88abe00fea4740dcf81075c031b1d0bb23aff2d5efde20a3c2408a',
'http://curl.haxx.se/download/curl-7.74.0.tar.xz',
'999d5f2c403cf6e25d58319fdd596611e455dd195208746bc6e6d197a77e878b',
'lib/libcurl.a',
[
'--disable-shared', '--enable-static',
......@@ -399,7 +406,7 @@ curl = AutotoolsProject(
'--disable-netrc',
'--disable-progress-meter',
'--disable-alt-svc',
'--without-ssl', '--without-gnutls', '--without-nss', '--without-libssh2',
'--without-gnutls', '--without-nss', '--without-libssh2',
],
patches='src/lib/curl/patches',
......@@ -434,7 +441,7 @@ libnfs = AutotoolsProject(
)
boost = BoostProject(
'https://dl.bintray.com/boostorg/release/1.74.0/source/boost_1_74_0.tar.bz2',
'83bfc1507731a0906e387fc28b7ef5417d591429e51e788417fe9ff025e116b1',
'https://dl.bintray.com/boostorg/release/1.75.0/source/boost_1_75_0.tar.bz2',
'953db31e016db7bb207f11432bef7df100516eeb746843fa0486a222e3fd49cb',
'include/boost/version.hpp',
)
import subprocess
from build.makeproject import MakeProject
class OpenSSLProject(MakeProject):
def __init__(self, url, md5, installed,
**kwargs):
MakeProject.__init__(self, url, md5, installed, install_target='install_dev', **kwargs)
def get_make_args(self, toolchain):
return MakeProject.get_make_args(self, toolchain) + [
'CC=' + toolchain.cc,
'CFLAGS=' + toolchain.cflags,
'CPPFLAGS=' + toolchain.cppflags,
'AR=' + toolchain.ar,
'RANLIB=' + toolchain.ranlib,
'build_libs',
]
def build(self, toolchain):
src = self.unpack(toolchain, out_of_tree=False)
# OpenSSL has a weird target architecture scheme with lots of
# hard-coded architectures; this table translates between our
# "toolchain_arch" (HOST_TRIPLET) and the OpenSSL target
openssl_archs = {
# not using "android-*" because those OpenSSL targets want
# to know where the SDK is, but our own build scripts
# prepared everything already to look like a regular Linux
# build
'arm-linux-androideabi': 'linux-generic32',
'aarch64-linux-android': 'linux-aarch64',
'i686-linux-android': 'linux-x86-clang',
'x86_64-linux-android': 'linux-x86_64-clang',
# Kobo
'arm-linux-gnueabihf': 'linux-generic32',
# Windows
'i686-w64-mingw32': 'mingw',
'x86_64-w64-mingw32': 'mingw64',
}
openssl_arch = openssl_archs[toolchain.arch]
subprocess.check_call(['./Configure',
'no-shared',
'no-module', 'no-engine', 'no-static-engine',
'no-async',
'no-tests',
'no-asm', # "asm" causes build failures on Windows
openssl_arch,
'--prefix=' + toolchain.install_prefix],
cwd=src, env=toolchain.env)
MakeProject.build(self, toolchain, src)
......@@ -20,7 +20,7 @@ class Project:
self.base = base
if name is None or version is None:
m = re.match(r'^([-\w]+)-(\d[\d.]*[a-z]?[\d.]*)$', self.base)
m = re.match(r'^([-\w]+)-(\d[\d.]*[a-z]?[\d.]*(?:-alpha\d+)?)$', self.base)
if name is None: name = m.group(1)
if version is None: version = m.group(2)
......
......@@ -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 },
......
......@@ -41,6 +41,21 @@ handle_ping([[maybe_unused]] Client &client, [[maybe_unused]] Request args,
}
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)
{
unsigned permission = 0;
......
......@@ -33,6 +33,9 @@ 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);
CommandResult
......
......@@ -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);
}
......
/*
* 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
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment