release v0.22.4

-----BEGIN PGP SIGNATURE-----
 
 iQJEBAABCgAuFiEEA5IzWngIOJSkMBxDI26KWMbbRRIFAmAJqgAQHG1heEBtdXNp
 Y3BkLm9yZwAKCRAjbopYxttFEg2QEACJLeN2mk2RU7Iqxbh/ekwm6aTM8D6bx8RH
 Xys4l1YAFQ0cg7sKZwMqefedGJG2j8CORbihYIF6Z8EvFsAiI6I3LjElfXrmnAc0
 Y9SnWHIR5vxlSQgyqPlJ0jl213uzoHHpel8PpEJbTtYONT+8f3fQEuLpO4/uWOIT
 S6mlX16vI0/Ydp//8UIazUUvjar1pPvBnSEZ0JZsZl8RTYlS/4SOfqpHnhhWnWpO
 9RXlLP9Zo68rJzNhUPwRj7NkyVEkg74xpjHOWoyeTMNQ6tKMQn8b4jb/LcBfj6hk
 I7mof5oX0aS+GyYaehKi9c9Az7wUcBxnnaN02qlAaSutcuox7ce70fKMtiAXRN0o
 T9mFSJm1JKqHZb1dFvxqSqFjVr7eO9XCxHqaEqTbXCT+CL/6AJQZi5SVcX4gCY1P
 NSM3Jnydjr73WFLmEfjCkWLTdtiJhY/2Q/J6+vcILMb3W2y5FaSHZTOFbxVG4nMM
 spoQ27b7PoB9MbxLR3QJkYLa0WE3FrTORYgsH8Po7ZcCU9+JvqDSBnGXxx+Yv+JJ
 dKMI5bEAvPziodSHHQXgD4lhx744JuiLAJNtlSYJvev1s2Irf2TtMHdmGERHQZwH
 5cr9sQLgyHCLvTBDGt1dVZq/Z0T/PCkweIa5cT6ZBAim1hs7g20g8ksyFK2ZPUbB
 vEGBNcuMIg==
 =CEEr
 -----END PGP SIGNATURE-----

Merge tag 'v0.22.4'

release v0.22.4
This commit is contained in:
Max Kellermann 2021-01-21 17:42:26 +01:00
commit 8279cafd6d
33 changed files with 353 additions and 101 deletions

8
NEWS
View File

@ -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

View File

@ -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,

View File

@ -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

View File

@ -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"

View File

@ -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

View File

@ -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

View File

@ -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,

View File

@ -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',
)

55
python/build/openssl.py Normal file
View File

@ -0,0 +1,55 @@
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)

View File

@ -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)

View File

@ -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

View File

@ -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 {

View File

@ -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) &&

View File

@ -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

View File

@ -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 },

View File

@ -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)
{

View File

@ -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);

View File

@ -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);

View File

@ -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);
}

View File

@ -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);
}

View 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

View File

@ -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);

View File

@ -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;

View File

@ -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;
}

View File

@ -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,

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -33,6 +33,7 @@
#include <SLES/OpenSLES.h>
#include <SLES/OpenSLES_Android.h>
#include <cassert>
#include <iterator>
#include <stdexcept>

View File

@ -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()) {
if (path == nullptr || path.empty())
return nullptr;
}
const char *slash = path.Find('/');
if (slash == nullptr)

View File

@ -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);

View File

@ -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;
}

View File

@ -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