From 97f670658fb0ab21d469eb69510faa8403c99d65 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Wed, 24 Jan 2018 12:52:43 +0100 Subject: [PATCH] util/StringFormat: new utility library --- Makefile.am | 1 + src/AudioFormat.cxx | 19 ++--- .../plugins/upnp/ContentDirectoryService.cxx | 17 ++--- src/decoder/plugins/GmeDecoderPlugin.cxx | 26 +++---- src/decoder/plugins/SidplayDecoderPlugin.cxx | 14 ++-- src/fs/io/FileOutputStream.cxx | 8 +-- src/input/plugins/CurlInputPlugin.cxx | 21 +++--- src/lib/ffmpeg/LogCallback.cxx | 8 ++- src/output/Init.cxx | 8 +-- src/output/plugins/ShoutOutputPlugin.cxx | 11 ++- src/protocol/Ack.hxx | 10 +-- src/storage/plugins/CurlStorage.cxx | 5 +- src/tag/Handler.cxx | 9 +-- src/thread/Name.hxx | 6 +- src/util/StringFormat.hxx | 69 +++++++++++++++++++ 15 files changed, 138 insertions(+), 94 deletions(-) create mode 100644 src/util/StringFormat.hxx diff --git a/Makefile.am b/Makefile.am index 3801c9300..59d3f2ebf 100644 --- a/Makefile.am +++ b/Makefile.am @@ -461,6 +461,7 @@ libutil_a_SOURCES = \ src/util/NumberParser.hxx \ src/util/MimeType.cxx src/util/MimeType.hxx \ src/util/StringBuffer.hxx \ + src/util/StringFormat.hxx \ src/util/StringPointer.hxx \ src/util/StringView.cxx src/util/StringView.hxx \ src/util/WStringView.hxx \ diff --git a/src/AudioFormat.cxx b/src/AudioFormat.cxx index f317d8abe..13d3709f9 100644 --- a/src/AudioFormat.cxx +++ b/src/AudioFormat.cxx @@ -19,9 +19,9 @@ #include "AudioFormat.hxx" #include "util/StringBuffer.hxx" +#include "util/StringFormat.hxx" #include -#include void AudioFormat::ApplyMask(AudioFormat mask) noexcept @@ -44,21 +44,16 @@ AudioFormat::ApplyMask(AudioFormat mask) noexcept StringBuffer<24> ToString(const AudioFormat af) noexcept { - StringBuffer<24> buffer; - if (af.format == SampleFormat::DSD && af.sample_rate > 0 && af.sample_rate % 44100 == 0) { /* use shortcuts such as "dsd64" which implies the sample rate */ - snprintf(buffer.data(), buffer.capacity(), "dsd%u:%u", - af.sample_rate * 8 / 44100, - af.channels); - return buffer; + return StringFormat<24>("dsd%u:%u", + af.sample_rate * 8 / 44100, + af.channels); } - snprintf(buffer.data(), buffer.capacity(), "%u:%s:%u", - af.sample_rate, sample_format_to_string(af.format), - af.channels); - - return buffer; + return StringFormat<24>("%u:%s:%u", + af.sample_rate, sample_format_to_string(af.format), + af.channels); } diff --git a/src/db/plugins/upnp/ContentDirectoryService.cxx b/src/db/plugins/upnp/ContentDirectoryService.cxx index 386a1fcaa..d7aeca82d 100644 --- a/src/db/plugins/upnp/ContentDirectoryService.cxx +++ b/src/db/plugins/upnp/ContentDirectoryService.cxx @@ -27,6 +27,7 @@ #include "util/UriUtil.hxx" #include "util/RuntimeError.hxx" #include "util/ScopeExit.hxx" +#include "util/StringFormat.hxx" #include @@ -47,10 +48,6 @@ ContentDirectoryService::readDirSlice(UpnpClient_Handle hdl, unsigned &didreadp, unsigned &totalp) const { - // Create request - char ofbuf[100], cntbuf[100]; - sprintf(ofbuf, "%u", offset); - sprintf(cntbuf, "%u", count); // Some devices require an empty SortCriteria, else bad params IXML_Document *request = MakeActionHelper("Browse", m_serviceType.c_str(), @@ -58,8 +55,10 @@ ContentDirectoryService::readDirSlice(UpnpClient_Handle hdl, "BrowseFlag", "BrowseDirectChildren", "Filter", "*", "SortCriteria", "", - "StartingIndex", ofbuf, - "RequestedCount", cntbuf); + "StartingIndex", + StringFormat<32>("%u", offset).c_str(), + "RequestedCount", + StringFormat<32>("%u", count).c_str()); if (request == nullptr) throw std::runtime_error("UpnpMakeAction() failed"); @@ -112,15 +111,13 @@ ContentDirectoryService::search(UpnpClient_Handle hdl, unsigned offset = 0, total = -1, count; do { - char ofbuf[100]; - sprintf(ofbuf, "%d", offset); - UniqueIxmlDocument request(MakeActionHelper("Search", m_serviceType.c_str(), "ContainerID", objectId, "SearchCriteria", ss, "Filter", "*", "SortCriteria", "", - "StartingIndex", ofbuf, + "StartingIndex", + StringFormat<32>("%u", offset).c_str(), "RequestedCount", "0")); // Setting a value here gets twonky into fits if (!request) throw std::runtime_error("UpnpMakeAction() failed"); diff --git a/src/decoder/plugins/GmeDecoderPlugin.cxx b/src/decoder/plugins/GmeDecoderPlugin.cxx index a4a894199..4708afb3a 100644 --- a/src/decoder/plugins/GmeDecoderPlugin.cxx +++ b/src/decoder/plugins/GmeDecoderPlugin.cxx @@ -29,7 +29,7 @@ #include "fs/AllocatedPath.hxx" #include "fs/FileSystem.hxx" #include "util/ScopeExit.hxx" -#include "util/FormatString.hxx" +#include "util/StringFormat.hxx" #include "util/UriUtil.hxx" #include "util/Domain.hxx" #include "Log.hxx" @@ -39,7 +39,6 @@ #include #include #include -#include #define SUBTUNE_PREFIX "tune_" @@ -222,20 +221,17 @@ ScanGmeInfo(const gme_info_t &info, unsigned song_num, int track_count, tag_handler_invoke_duration(handler, handler_ctx, SongTime::FromMS(info.play_length)); - if (track_count > 1) { - char track[16]; - sprintf(track, "%u", song_num + 1); - tag_handler_invoke_tag(handler, handler_ctx, TAG_TRACK, track); - } + if (track_count > 1) + tag_handler_invoke_tag(handler, handler_ctx, TAG_TRACK, + StringFormat<16>("%u", song_num + 1)); if (info.song != nullptr) { if (track_count > 1) { /* start numbering subtunes from 1 */ - char tag_title[1024]; - snprintf(tag_title, sizeof(tag_title), - "%s (%u/%d)", - info.song, song_num + 1, - track_count); + const auto tag_title = + StringFormat<1024>("%s (%u/%d)", + info.song, song_num + 1, + track_count); tag_handler_invoke_tag(handler, handler_ctx, TAG_TITLE, tag_title); } else @@ -323,9 +319,9 @@ gme_container_scan(Path path_fs) ScanMusicEmu(emu, i, add_tag_handler, &tag_builder); - char track_name[64]; - snprintf(track_name, sizeof(track_name), - SUBTUNE_PREFIX "%03u.%s", i+1, subtune_suffix); + const auto track_name = + StringFormat<64>(SUBTUNE_PREFIX "%03u.%s", i+1, + subtune_suffix); tail = list.emplace_after(tail, track_name, tag_builder.Commit()); } diff --git a/src/decoder/plugins/SidplayDecoderPlugin.cxx b/src/decoder/plugins/SidplayDecoderPlugin.cxx index 21f977e7e..f93de4bfb 100644 --- a/src/decoder/plugins/SidplayDecoderPlugin.cxx +++ b/src/decoder/plugins/SidplayDecoderPlugin.cxx @@ -26,7 +26,7 @@ #include "fs/Path.hxx" #include "fs/AllocatedPath.hxx" #include "util/Macros.hxx" -#include "util/FormatString.hxx" +#include "util/StringFormat.hxx" #include "util/Domain.hxx" #include "system/ByteOrder.hxx" #include "Log.hxx" @@ -413,10 +413,9 @@ ScanSidTuneInfo(const SidTuneInfo &info, unsigned track, unsigned n_tracks, title = ""; if (n_tracks > 1) { - char tag_title[1024]; - snprintf(tag_title, sizeof(tag_title), - "%s (%u/%u)", - title, track, n_tracks); + const auto tag_title = + StringFormat<1024>("%s (%u/%u)", + title, track, n_tracks); tag_handler_invoke_tag(handler, handler_ctx, TAG_TITLE, tag_title); } else @@ -435,9 +434,8 @@ ScanSidTuneInfo(const SidTuneInfo &info, unsigned track, unsigned n_tracks, date); /* track */ - char track_buffer[16]; - sprintf(track_buffer, "%d", track); - tag_handler_invoke_tag(handler, handler_ctx, TAG_TRACK, track_buffer); + tag_handler_invoke_tag(handler, handler_ctx, TAG_TRACK, + StringFormat<16>("%u", track)); } static bool diff --git a/src/fs/io/FileOutputStream.cxx b/src/fs/io/FileOutputStream.cxx index ddf7208a7..e9ce40387 100644 --- a/src/fs/io/FileOutputStream.cxx +++ b/src/fs/io/FileOutputStream.cxx @@ -20,6 +20,7 @@ #include "config.h" #include "FileOutputStream.hxx" #include "system/Error.hxx" +#include "util/StringFormat.hxx" FileOutputStream::FileOutputStream(Path _path, Mode _mode) :path(_path), mode(_mode) @@ -212,10 +213,9 @@ FileOutputStream::Commit() unlink(GetPath().c_str()); /* hard-link the temporary file to the final path */ - char fd_path[64]; - snprintf(fd_path, sizeof(fd_path), "/proc/self/fd/%d", - fd.Get()); - if (linkat(AT_FDCWD, fd_path, AT_FDCWD, path.c_str(), + if (linkat(AT_FDCWD, + StringFormat<64>("/proc/self/fd/%d", fd.Get()), + AT_FDCWD, path.c_str(), AT_SYMLINK_FOLLOW) < 0) throw FormatErrno("Failed to commit %s", path.c_str()); diff --git a/src/input/plugins/CurlInputPlugin.cxx b/src/input/plugins/CurlInputPlugin.cxx index aa4b0d6d5..69876a463 100644 --- a/src/input/plugins/CurlInputPlugin.cxx +++ b/src/input/plugins/CurlInputPlugin.cxx @@ -38,6 +38,7 @@ #include "thread/Cond.hxx" #include "util/ASCII.hxx" #include "util/StringUtil.hxx" +#include "util/StringFormat.hxx" #include "util/NumberParser.hxx" #include "util/RuntimeError.hxx" #include "util/Domain.hxx" @@ -384,13 +385,10 @@ CurlInputStream::InitEasy() if (proxy_port > 0) request->SetOption(CURLOPT_PROXYPORT, (long)proxy_port); - if (proxy_user != nullptr && proxy_password != nullptr) { - char proxy_auth_str[1024]; - snprintf(proxy_auth_str, sizeof(proxy_auth_str), - "%s:%s", - proxy_user, proxy_password); - request->SetOption(CURLOPT_PROXYUSERPWD, proxy_auth_str); - } + if (proxy_user != nullptr && proxy_password != nullptr) + request->SetOption(CURLOPT_PROXYUSERPWD, + StringFormat<1024>("%s:%s", proxy_user, + proxy_password).c_str()); request->SetOption(CURLOPT_SSL_VERIFYPEER, verify_peer ? 1l : 0l); request->SetOption(CURLOPT_SSL_VERIFYHOST, verify_host ? 2l : 0l); @@ -423,11 +421,10 @@ CurlInputStream::SeekInternal(offset_type new_offset) /* send the "Range" header */ - if (offset > 0) { - char range[32]; - sprintf(range, "%" PRIoffset "-", offset); - request->SetOption(CURLOPT_RANGE, range); - } + if (offset > 0) + request->SetOption(CURLOPT_RANGE, + StringFormat<40>("%" PRIoffset "-", + offset).c_str()); StartRequest(); } diff --git a/src/lib/ffmpeg/LogCallback.cxx b/src/lib/ffmpeg/LogCallback.cxx index c682da63d..5a0fa4a6f 100644 --- a/src/lib/ffmpeg/LogCallback.cxx +++ b/src/lib/ffmpeg/LogCallback.cxx @@ -25,6 +25,7 @@ #include "Domain.hxx" #include "LogV.hxx" #include "util/Domain.hxx" +#include "util/StringFormat.hxx" extern "C" { #include @@ -57,9 +58,10 @@ FfmpegLogCallback(gcc_unused void *ptr, int level, const char *fmt, va_list vl) cls = *(const AVClass *const*)ptr; if (cls != nullptr) { - char domain[64]; - snprintf(domain, sizeof(domain), "%s/%s", - ffmpeg_domain.GetName(), cls->item_name(ptr)); + const auto domain = + StringFormat<64>("%s/%s", + ffmpeg_domain.GetName(), + cls->item_name(ptr)); const Domain d(domain); LogFormatV(d, FfmpegImportLogLevel(level), fmt, vl); } diff --git a/src/output/Init.cxx b/src/output/Init.cxx index 0fe13db3a..3847e54b1 100644 --- a/src/output/Init.cxx +++ b/src/output/Init.cxx @@ -39,6 +39,7 @@ #include "config/ConfigGlobal.hxx" #include "config/Block.hxx" #include "util/RuntimeError.hxx" +#include "util/StringFormat.hxx" #include "Log.hxx" #include @@ -165,12 +166,7 @@ FilteredAudioOutput::Configure(const ConfigBlock &block) config_audio_format.Clear(); } - { - char buffer[64]; - snprintf(buffer, sizeof(buffer), "\"%s\" (%s)", - name, plugin_name); - log_name = buffer; - } + log_name = StringFormat<256>("\"%s\" (%s)", name, plugin_name); /* set up the filter chain */ diff --git a/src/output/plugins/ShoutOutputPlugin.cxx b/src/output/plugins/ShoutOutputPlugin.cxx index 9332fdaa9..0e78c24f6 100644 --- a/src/output/plugins/ShoutOutputPlugin.cxx +++ b/src/output/plugins/ShoutOutputPlugin.cxx @@ -26,6 +26,7 @@ #include "util/Domain.hxx" #include "util/ScopeExit.hxx" #include "util/StringAPI.hxx" +#include "util/StringFormat.hxx" #include "Log.hxx" #include @@ -87,13 +88,11 @@ require_block_string(const ConfigBlock &block, const char *name) static void ShoutSetAudioInfo(shout_t *shout_conn, const AudioFormat &audio_format) { - char temp[11]; + shout_set_audio_info(shout_conn, SHOUT_AI_CHANNELS, + StringFormat<11>("%u", audio_format.channels)); - snprintf(temp, sizeof(temp), "%u", audio_format.channels); - shout_set_audio_info(shout_conn, SHOUT_AI_CHANNELS, temp); - - snprintf(temp, sizeof(temp), "%u", audio_format.sample_rate); - shout_set_audio_info(shout_conn, SHOUT_AI_SAMPLERATE, temp); + shout_set_audio_info(shout_conn, SHOUT_AI_SAMPLERATE, + StringFormat<11>("%u", audio_format.sample_rate)); } ShoutOutput::ShoutOutput(const ConfigBlock &block) diff --git a/src/protocol/Ack.hxx b/src/protocol/Ack.hxx index 195f4bcb0..497f85b18 100644 --- a/src/protocol/Ack.hxx +++ b/src/protocol/Ack.hxx @@ -20,9 +20,9 @@ #ifndef MPD_ACK_H #define MPD_ACK_H -#include +#include "util/StringFormat.hxx" -#include +#include class Domain; @@ -60,9 +60,9 @@ template static inline ProtocolError FormatProtocolError(enum ack code, const char *fmt, Args&&... args) noexcept { - char buffer[256]; - snprintf(buffer, sizeof(buffer), fmt, std::forward(args)...); - return ProtocolError(code, buffer); + return ProtocolError(code, + StringFormat<256>(fmt, + std::forward(args)...)); } #endif diff --git a/src/storage/plugins/CurlStorage.cxx b/src/storage/plugins/CurlStorage.cxx index bc120f00a..70942b05c 100644 --- a/src/storage/plugins/CurlStorage.cxx +++ b/src/storage/plugins/CurlStorage.cxx @@ -37,6 +37,7 @@ #include "util/ChronoUtil.hxx" #include "util/RuntimeError.hxx" #include "util/StringCompare.hxx" +#include "util/StringFormat.hxx" #include "util/TimeParser.hxx" #include "util/UriUtil.hxx" @@ -259,9 +260,7 @@ public: { request.SetOption(CURLOPT_CUSTOMREQUEST, "PROPFIND"); - char buffer[40]; - sprintf(buffer, "depth: %u", depth); - request_headers.Append(buffer); + request_headers.Append(StringFormat<40>("depth: %u", depth)); request.SetOption(CURLOPT_HTTPHEADER, request_headers.Get()); diff --git a/src/tag/Handler.cxx b/src/tag/Handler.cxx index dda760c43..7c4210f7d 100644 --- a/src/tag/Handler.cxx +++ b/src/tag/Handler.cxx @@ -21,8 +21,8 @@ #include "Handler.hxx" #include "Builder.hxx" #include "util/ASCII.hxx" +#include "util/StringFormat.hxx" -#include #include static void @@ -42,11 +42,8 @@ add_tag_tag(TagType type, const char *value, void *ctx) /* filter out this extra data and leading zeroes */ char *end; unsigned n = strtoul(value, &end, 10); - if (value != end) { - char s[21]; - if (snprintf(s, 21, "%u", n) > 0) - tag.AddItem(type, s); - } + if (value != end) + tag.AddItem(type, StringFormat<21>("%u", n)); } else tag.AddItem(type, value); } diff --git a/src/thread/Name.hxx b/src/thread/Name.hxx index 70dbd59c2..268985d0b 100644 --- a/src/thread/Name.hxx +++ b/src/thread/Name.hxx @@ -31,7 +31,7 @@ #endif #ifdef HAVE_THREAD_NAME -# include +#include "util/StringFormat.hxx" #endif static inline void @@ -59,9 +59,7 @@ static inline void FormatThreadName(const char *fmt, gcc_unused Args&&... args) noexcept { #ifdef HAVE_THREAD_NAME - char buffer[16]; - snprintf(buffer, sizeof(buffer), fmt, args...); - SetThreadName(buffer); + SetThreadName(StringFormat<16>(fmt, args...)); #else (void)fmt; #endif diff --git a/src/util/StringFormat.hxx b/src/util/StringFormat.hxx new file mode 100644 index 000000000..4fee2fc37 --- /dev/null +++ b/src/util/StringFormat.hxx @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2010-2015 Max Kellermann + * + * 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 STRING_FORMAT_HXX +#define STRING_FORMAT_HXX + +#include "StringBuffer.hxx" + +#include + +template +static inline void +StringFormat(char *buffer, size_t size, + const char *fmt, Args&&... args) noexcept +{ + snprintf(buffer, size, fmt, args...); +} + +template +static inline void +StringFormat(StringBuffer &buffer, + const char *fmt, Args&&... args) noexcept +{ + StringFormat(buffer.data(), buffer.capacity(), fmt, args...); +} + +template +static inline StringBuffer +StringFormat(const char *fmt, Args&&... args) noexcept +{ + StringBuffer result; + StringFormat(result, fmt, args...); + return result; +} + +template +static inline void +StringFormatUnsafe(char *buffer, const char *fmt, Args&&... args) noexcept +{ + sprintf(buffer, fmt, args...); +} + +#endif