Commit 356c829b authored by Max Kellermann's avatar Max Kellermann

util/StringView: new utility class

parent ffbb5c48
...@@ -401,6 +401,7 @@ libutil_a_SOURCES = \ ...@@ -401,6 +401,7 @@ libutil_a_SOURCES = \
src/util/CharUtil.hxx \ src/util/CharUtil.hxx \
src/util/NumberParser.hxx \ src/util/NumberParser.hxx \
src/util/StringPointer.hxx \ src/util/StringPointer.hxx \
src/util/StringView.hxx \
src/util/AllocatedString.cxx src/util/AllocatedString.hxx \ src/util/AllocatedString.cxx src/util/AllocatedString.hxx \
src/util/StringUtil.cxx src/util/StringUtil.hxx \ src/util/StringUtil.cxx src/util/StringUtil.hxx \
src/util/WStringUtil.cxx src/util/WStringUtil.hxx \ src/util/WStringUtil.cxx src/util/WStringUtil.hxx \
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include "tag/Tag.hxx" #include "tag/Tag.hxx"
#include "tag/TagBuilder.hxx" #include "tag/TagBuilder.hxx"
#include "util/Domain.hxx" #include "util/Domain.hxx"
#include "util/StringView.hxx"
#include "Log.hxx" #include "Log.hxx"
#include <assert.h> #include <assert.h>
...@@ -76,7 +77,7 @@ icy_add_item(TagBuilder &tag, TagType type, const char *value) ...@@ -76,7 +77,7 @@ icy_add_item(TagBuilder &tag, TagType type, const char *value)
} }
if (length > 0) if (length > 0)
tag.AddItem(type, value, length); tag.AddItem(type, {value, length});
} }
static void static void
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#include "tag/TagBuilder.hxx" #include "tag/TagBuilder.hxx"
#include "tag/TagTable.hxx" #include "tag/TagTable.hxx"
#include "util/NumberParser.hxx" #include "util/NumberParser.hxx"
#include "util/StringView.hxx"
#include <algorithm> #include <algorithm>
#include <string> #include <string>
...@@ -36,23 +37,13 @@ UPnPDirContent::~UPnPDirContent() ...@@ -36,23 +37,13 @@ UPnPDirContent::~UPnPDirContent()
/* this destructor exists here just so it won't get inlined */ /* this destructor exists here just so it won't get inlined */
} }
gcc_pure gcc_nonnull_all
static bool
CompareStringLiteral(const char *literal, const char *value, size_t length)
{
return length == strlen(literal) &&
memcmp(literal, value, length) == 0;
}
gcc_pure gcc_pure
static UPnPDirObject::ItemClass static UPnPDirObject::ItemClass
ParseItemClass(const char *name, size_t length) ParseItemClass(StringView name)
{ {
if (CompareStringLiteral("object.item.audioItem.musicTrack", if (name.EqualsLiteral("object.item.audioItem.musicTrack"))
name, length))
return UPnPDirObject::ItemClass::MUSIC; return UPnPDirObject::ItemClass::MUSIC;
else if (CompareStringLiteral("object.item.playlistItem", else if (name.EqualsLiteral("object.item.playlistItem"))
name, length))
return UPnPDirObject::ItemClass::PLAYLIST; return UPnPDirObject::ItemClass::PLAYLIST;
else else
return UPnPDirObject::ItemClass::UNKNOWN; return UPnPDirObject::ItemClass::UNKNOWN;
...@@ -239,7 +230,7 @@ protected: ...@@ -239,7 +230,7 @@ protected:
break; break;
case CLASS: case CLASS:
object.item_class = ParseItemClass(s, len); object.item_class = ParseItemClass(StringView(s, len));
break; break;
} }
} }
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#include "fs/Path.hxx" #include "fs/Path.hxx"
#include "util/Error.hxx" #include "util/Error.hxx"
#include "util/Domain.hxx" #include "util/Domain.hxx"
#include "util/StringView.hxx"
#include "Log.hxx" #include "Log.hxx"
#include <mpg123.h> #include <mpg123.h>
...@@ -111,7 +112,7 @@ AddTagItem(TagBuilder &tag, TagType type, const mpg123_string &s) ...@@ -111,7 +112,7 @@ AddTagItem(TagBuilder &tag, TagType type, const mpg123_string &s)
assert(s.size >= s.fill); assert(s.size >= s.fill);
assert(s.fill > 0); assert(s.fill > 0);
tag.AddItem(type, s.p, s.fill - 1); tag.AddItem(type, {s.p, s.fill - 1});
} }
static void static void
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#include "tag/TagBuilder.hxx" #include "tag/TagBuilder.hxx"
#include "util/ASCII.hxx" #include "util/ASCII.hxx"
#include "util/Error.hxx" #include "util/Error.hxx"
#include "util/StringView.hxx"
#include "lib/expat/ExpatParser.hxx" #include "lib/expat/ExpatParser.hxx"
#include "Log.hxx" #include "Log.hxx"
...@@ -130,7 +131,8 @@ asx_char_data(void *user_data, const XML_Char *s, int len) ...@@ -130,7 +131,8 @@ asx_char_data(void *user_data, const XML_Char *s, int len)
case AsxParser::ENTRY: case AsxParser::ENTRY:
if (parser->tag_type != TAG_NUM_OF_ITEM_TYPES) if (parser->tag_type != TAG_NUM_OF_ITEM_TYPES)
parser->tag_builder.AddItem(parser->tag_type, s, len); parser->tag_builder.AddItem(parser->tag_type,
StringView(s, len));
break; break;
} }
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#include "tag/TagBuilder.hxx" #include "tag/TagBuilder.hxx"
#include "util/ASCII.hxx" #include "util/ASCII.hxx"
#include "util/Error.hxx" #include "util/Error.hxx"
#include "util/StringView.hxx"
#include "lib/expat/ExpatParser.hxx" #include "lib/expat/ExpatParser.hxx"
#include "Log.hxx" #include "Log.hxx"
...@@ -128,7 +129,8 @@ rss_char_data(void *user_data, const XML_Char *s, int len) ...@@ -128,7 +129,8 @@ rss_char_data(void *user_data, const XML_Char *s, int len)
case RssParser::ITEM: case RssParser::ITEM:
if (parser->tag_type != TAG_NUM_OF_ITEM_TYPES) if (parser->tag_type != TAG_NUM_OF_ITEM_TYPES)
parser->tag_builder.AddItem(parser->tag_type, s, len); parser->tag_builder.AddItem(parser->tag_type,
StringView(s, len));
break; break;
} }
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#include "input/InputStream.hxx" #include "input/InputStream.hxx"
#include "tag/TagBuilder.hxx" #include "tag/TagBuilder.hxx"
#include "util/Error.hxx" #include "util/Error.hxx"
#include "util/StringView.hxx"
#include "lib/expat/ExpatParser.hxx" #include "lib/expat/ExpatParser.hxx"
#include "Log.hxx" #include "Log.hxx"
...@@ -170,7 +171,8 @@ xspf_char_data(void *user_data, const XML_Char *s, int len) ...@@ -170,7 +171,8 @@ xspf_char_data(void *user_data, const XML_Char *s, int len)
case XspfParser::TRACK: case XspfParser::TRACK:
if (!parser->location.empty() && if (!parser->location.empty() &&
parser->tag_type != TAG_NUM_OF_ITEM_TYPES) parser->tag_type != TAG_NUM_OF_ITEM_TYPES)
parser->tag_builder.AddItem(parser->tag_type, s, len); parser->tag_builder.AddItem(parser->tag_type,
StringView(s, len));
break; break;
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include "ApeLoader.hxx" #include "ApeLoader.hxx"
#include "system/ByteOrder.hxx" #include "system/ByteOrder.hxx"
#include "fs/FileSystem.hxx" #include "fs/FileSystem.hxx"
#include "util/StringView.hxx"
#include <stdint.h> #include <stdint.h>
#include <assert.h> #include <assert.h>
...@@ -89,7 +90,7 @@ ape_scan_internal(FILE *fp, ApeTagCallback callback) ...@@ -89,7 +90,7 @@ ape_scan_internal(FILE *fp, ApeTagCallback callback)
if (remaining < size) if (remaining < size)
break; break;
if (!callback(flags, key, p, size)) if (!callback(flags, key, {p, size}))
break; break;
p += size; p += size;
......
...@@ -26,11 +26,11 @@ ...@@ -26,11 +26,11 @@
#include <stddef.h> #include <stddef.h>
struct StringView;
class Path; class Path;
typedef std::function<bool(unsigned long flags, const char *key, typedef std::function<bool(unsigned long flags, const char *key,
const char *value, StringView value)> ApeTagCallback;
size_t value_length)> ApeTagCallback;
/** /**
* Scans the APE tag values from a file. * Scans the APE tag values from a file.
......
...@@ -21,15 +21,16 @@ ...@@ -21,15 +21,16 @@
#include "ApeReplayGain.hxx" #include "ApeReplayGain.hxx"
#include "ApeLoader.hxx" #include "ApeLoader.hxx"
#include "ReplayGain.hxx" #include "ReplayGain.hxx"
#include "util/ASCII.hxx"
#include "fs/Path.hxx" #include "fs/Path.hxx"
#include "util/ASCII.hxx"
#include "util/StringView.hxx"
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
static bool static bool
replay_gain_ape_callback(unsigned long flags, const char *key, replay_gain_ape_callback(unsigned long flags, const char *key,
const char *_value, size_t value_length, StringView _value,
ReplayGainInfo &info) ReplayGainInfo &info)
{ {
/* we only care about utf-8 text tags */ /* we only care about utf-8 text tags */
...@@ -37,11 +38,11 @@ replay_gain_ape_callback(unsigned long flags, const char *key, ...@@ -37,11 +38,11 @@ replay_gain_ape_callback(unsigned long flags, const char *key,
return false; return false;
char value[16]; char value[16];
if (value_length >= sizeof(value)) if (_value.size >= sizeof(value))
return false; return false;
memcpy(value, _value, value_length); memcpy(value, _value.data, _value.size);
value[value_length] = 0; value[_value.size] = 0;
return ParseReplayGainTag(info, key, value); return ParseReplayGainTag(info, key, value);
} }
...@@ -53,10 +54,9 @@ replay_gain_ape_read(Path path_fs, ReplayGainInfo &info) ...@@ -53,10 +54,9 @@ replay_gain_ape_read(Path path_fs, ReplayGainInfo &info)
auto callback = [&info, &found] auto callback = [&info, &found]
(unsigned long flags, const char *key, (unsigned long flags, const char *key,
const char *value, StringView value) {
size_t value_length) {
found |= replay_gain_ape_callback(flags, key, found |= replay_gain_ape_callback(flags, key,
value, value_length, value,
info); info);
return true; return true;
}; };
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#include "TagTable.hxx" #include "TagTable.hxx"
#include "TagHandler.hxx" #include "TagHandler.hxx"
#include "fs/Path.hxx" #include "fs/Path.hxx"
#include "util/StringView.hxx"
#include <string> #include <string>
...@@ -75,17 +76,18 @@ ForEachValue(const char *value, const char *end, C &&callback) ...@@ -75,17 +76,18 @@ ForEachValue(const char *value, const char *end, C &&callback)
*/ */
static bool static bool
tag_ape_import_item(unsigned long flags, tag_ape_import_item(unsigned long flags,
const char *key, const char *value, size_t value_length, const char *key, StringView value,
const struct tag_handler *handler, void *handler_ctx) const struct tag_handler *handler, void *handler_ctx)
{ {
/* we only care about utf-8 text tags */ /* we only care about utf-8 text tags */
if ((flags & (0x3 << 1)) != 0) if ((flags & (0x3 << 1)) != 0)
return false; return false;
const char *const end = value + value_length; const auto begin = value.begin();
const auto end = value.end();
if (handler->pair != nullptr) if (handler->pair != nullptr)
ForEachValue(value, end, [handler, handler_ctx, ForEachValue(begin, end, [handler, handler_ctx,
key](const char *_value) { key](const char *_value) {
handler->pair(key, _value, handler_ctx); handler->pair(key, _value, handler_ctx);
}); });
...@@ -94,8 +96,8 @@ tag_ape_import_item(unsigned long flags, ...@@ -94,8 +96,8 @@ tag_ape_import_item(unsigned long flags,
if (type == TAG_NUM_OF_ITEM_TYPES) if (type == TAG_NUM_OF_ITEM_TYPES)
return false; return false;
ForEachValue(value, end, [handler, handler_ctx, ForEachValue(begin, end, [handler, handler_ctx,
type](const char *_value) { type](const char *_value) {
tag_handler_invoke_tag(handler, handler_ctx, tag_handler_invoke_tag(handler, handler_ctx,
type, _value); type, _value);
}); });
...@@ -111,10 +113,8 @@ tag_ape_scan2(Path path_fs, ...@@ -111,10 +113,8 @@ tag_ape_scan2(Path path_fs,
auto callback = [handler, handler_ctx, &recognized] auto callback = [handler, handler_ctx, &recognized]
(unsigned long flags, const char *key, (unsigned long flags, const char *key,
const char *value, StringView value) {
size_t value_length) {
recognized |= tag_ape_import_item(flags, key, value, recognized |= tag_ape_import_item(flags, key, value,
value_length,
handler, handler_ctx); handler, handler_ctx);
return true; return true;
}; };
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#include "TagString.hxx" #include "TagString.hxx"
#include "Tag.hxx" #include "Tag.hxx"
#include "util/WritableBuffer.hxx" #include "util/WritableBuffer.hxx"
#include "util/StringView.hxx"
#include <array> #include <array>
...@@ -189,22 +190,16 @@ TagBuilder::Complement(const Tag &other) ...@@ -189,22 +190,16 @@ TagBuilder::Complement(const Tag &other)
} }
inline void inline void
TagBuilder::AddItemInternal(TagType type, const char *value, size_t length) TagBuilder::AddItemInternal(TagType type, StringView value)
{ {
#if !CLANG_CHECK_VERSION(3,6) assert(!value.IsEmpty());
/* disabled on clang due to -Wtautological-pointer-compare */
assert(value != nullptr);
#endif
assert(length > 0);
auto f = FixTagString(value, length); auto f = FixTagString(value);
if (!f.IsNull()) { if (!f.IsNull())
value = f.data; value = { f.data, f.size };
length = f.size;
}
tag_pool_lock.lock(); tag_pool_lock.lock();
auto i = tag_pool_get_item(type, value, length); auto i = tag_pool_get_item(type, value);
tag_pool_lock.unlock(); tag_pool_lock.unlock();
free(f.data); free(f.data);
...@@ -213,17 +208,12 @@ TagBuilder::AddItemInternal(TagType type, const char *value, size_t length) ...@@ -213,17 +208,12 @@ TagBuilder::AddItemInternal(TagType type, const char *value, size_t length)
} }
void void
TagBuilder::AddItem(TagType type, const char *value, size_t length) TagBuilder::AddItem(TagType type, StringView value)
{ {
#if !CLANG_CHECK_VERSION(3,6) if (value.IsEmpty() || !IsTagEnabled(type))
/* disabled on clang due to -Wtautological-pointer-compare */
assert(value != nullptr);
#endif
if (length == 0 || !IsTagEnabled(type))
return; return;
AddItemInternal(type, value, length); AddItemInternal(type, value);
} }
void void
...@@ -234,14 +224,14 @@ TagBuilder::AddItem(TagType type, const char *value) ...@@ -234,14 +224,14 @@ TagBuilder::AddItem(TagType type, const char *value)
assert(value != nullptr); assert(value != nullptr);
#endif #endif
AddItem(type, value, strlen(value)); AddItem(type, StringView(value));
} }
void void
TagBuilder::AddEmptyItem(TagType type) TagBuilder::AddEmptyItem(TagType type)
{ {
tag_pool_lock.lock(); tag_pool_lock.lock();
auto i = tag_pool_get_item(type, "", 0); auto i = tag_pool_get_item(type, StringView::Empty());
tag_pool_lock.unlock(); tag_pool_lock.unlock();
items.push_back(i); items.push_back(i);
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#include <stddef.h> #include <stddef.h>
struct StringView;
struct TagItem; struct TagItem;
struct Tag; struct Tag;
...@@ -141,7 +142,7 @@ public: ...@@ -141,7 +142,7 @@ public:
* @param length the length of #value * @param length the length of #value
*/ */
gcc_nonnull_all gcc_nonnull_all
void AddItem(TagType type, const char *value, size_t length); void AddItem(TagType type, StringView value);
/** /**
* Appends a new tag item. * Appends a new tag item.
...@@ -171,7 +172,7 @@ public: ...@@ -171,7 +172,7 @@ public:
private: private:
gcc_nonnull_all gcc_nonnull_all
void AddItemInternal(TagType type, const char *value, size_t length); void AddItemInternal(TagType type, StringView value);
}; };
#endif #endif
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include "TagItem.hxx" #include "TagItem.hxx"
#include "util/Cast.hxx" #include "util/Cast.hxx"
#include "util/VarSize.hxx" #include "util/VarSize.hxx"
#include "util/StringView.hxx"
#include <assert.h> #include <assert.h>
#include <string.h> #include <string.h>
...@@ -37,39 +38,37 @@ struct TagPoolSlot { ...@@ -37,39 +38,37 @@ struct TagPoolSlot {
TagItem item; TagItem item;
TagPoolSlot(TagPoolSlot *_next, TagType type, TagPoolSlot(TagPoolSlot *_next, TagType type,
const char *value, size_t length) StringView value)
:next(_next), ref(1) { :next(_next), ref(1) {
item.type = type; item.type = type;
memcpy(item.value, value, length); memcpy(item.value, value.data, value.size);
item.value[length] = 0; item.value[value.size] = 0;
} }
static TagPoolSlot *Create(TagPoolSlot *_next, TagType type, static TagPoolSlot *Create(TagPoolSlot *_next, TagType type,
const char *value, size_t length); StringView value);
} gcc_packed; } gcc_packed;
TagPoolSlot * TagPoolSlot *
TagPoolSlot::Create(TagPoolSlot *_next, TagType type, TagPoolSlot::Create(TagPoolSlot *_next, TagType type,
const char *value, size_t length) StringView value)
{ {
TagPoolSlot *dummy; TagPoolSlot *dummy;
return NewVarSize<TagPoolSlot>(sizeof(dummy->item.value), return NewVarSize<TagPoolSlot>(sizeof(dummy->item.value),
length + 1, value.size + 1,
_next, type, _next, type,
value, length); value);
} }
static TagPoolSlot *slots[NUM_SLOTS]; static TagPoolSlot *slots[NUM_SLOTS];
static inline unsigned static inline unsigned
calc_hash_n(TagType type, const char *p, size_t length) calc_hash(TagType type, StringView p)
{ {
unsigned hash = 5381; unsigned hash = 5381;
assert(p != nullptr); for (auto ch : p)
hash = (hash << 5) + hash + ch;
while (length-- > 0)
hash = (hash << 5) + hash + *p++;
return hash ^ type; return hash ^ type;
} }
...@@ -97,9 +96,9 @@ tag_item_to_slot(TagItem *item) ...@@ -97,9 +96,9 @@ tag_item_to_slot(TagItem *item)
} }
static inline TagPoolSlot ** static inline TagPoolSlot **
tag_value_slot_p(TagType type, const char *value, size_t length) tag_value_slot_p(TagType type, StringView value)
{ {
return &slots[calc_hash_n(type, value, length) % NUM_SLOTS]; return &slots[calc_hash(type, value) % NUM_SLOTS];
} }
static inline TagPoolSlot ** static inline TagPoolSlot **
...@@ -109,13 +108,12 @@ tag_value_slot_p(TagType type, const char *value) ...@@ -109,13 +108,12 @@ tag_value_slot_p(TagType type, const char *value)
} }
TagItem * TagItem *
tag_pool_get_item(TagType type, const char *value, size_t length) tag_pool_get_item(TagType type, StringView value)
{ {
auto slot_p = tag_value_slot_p(type, value, length); auto slot_p = tag_value_slot_p(type, value);
for (auto slot = *slot_p; slot != nullptr; slot = slot->next) { for (auto slot = *slot_p; slot != nullptr; slot = slot->next) {
if (slot->item.type == type && if (slot->item.type == type &&
length == strlen(slot->item.value) && value.Equals(slot->item.value) &&
memcmp(value, slot->item.value, length) == 0 &&
slot->ref < 0xff) { slot->ref < 0xff) {
assert(slot->ref > 0); assert(slot->ref > 0);
++slot->ref; ++slot->ref;
...@@ -123,7 +121,7 @@ tag_pool_get_item(TagType type, const char *value, size_t length) ...@@ -123,7 +121,7 @@ tag_pool_get_item(TagType type, const char *value, size_t length)
} }
} }
auto slot = TagPoolSlot::Create(*slot_p, type, value, length); auto slot = TagPoolSlot::Create(*slot_p, type, value);
*slot_p = slot; *slot_p = slot;
return &slot->item; return &slot->item;
} }
...@@ -141,11 +139,9 @@ tag_pool_dup_item(TagItem *item) ...@@ -141,11 +139,9 @@ tag_pool_dup_item(TagItem *item)
} else { } else {
/* the reference counter overflows above 0xff; /* the reference counter overflows above 0xff;
duplicate the item, and start with 1 */ duplicate the item, and start with 1 */
size_t length = strlen(item->value); auto slot_p = tag_value_slot_p(item->type, item->value);
auto slot_p = tag_value_slot_p(item->type,
item->value, length);
slot = TagPoolSlot::Create(*slot_p, item->type, slot = TagPoolSlot::Create(*slot_p, item->type,
item->value, strlen(item->value)); item->value);
*slot_p = slot; *slot_p = slot;
return &slot->item; return &slot->item;
} }
......
...@@ -26,9 +26,10 @@ ...@@ -26,9 +26,10 @@
extern Mutex tag_pool_lock; extern Mutex tag_pool_lock;
struct TagItem; struct TagItem;
struct StringView;
TagItem * TagItem *
tag_pool_get_item(TagType type, const char *value, size_t length); tag_pool_get_item(TagType type, StringView value);
TagItem * TagItem *
tag_pool_dup_item(TagItem *item); tag_pool_dup_item(TagItem *item);
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include "TagString.hxx" #include "TagString.hxx"
#include "util/Alloc.hxx" #include "util/Alloc.hxx"
#include "util/WritableBuffer.hxx" #include "util/WritableBuffer.hxx"
#include "util/StringView.hxx"
#include "util/UTF8.hxx" #include "util/UTF8.hxx"
#include <assert.h> #include <assert.h>
...@@ -54,14 +55,14 @@ FindInvalidUTF8(const char *p, const char *const end) ...@@ -54,14 +55,14 @@ FindInvalidUTF8(const char *p, const char *const end)
* Replace invalid sequences with the question mark. * Replace invalid sequences with the question mark.
*/ */
static WritableBuffer<char> static WritableBuffer<char>
patch_utf8(const char *src, size_t length, const char *_invalid) patch_utf8(StringView src, const char *_invalid)
{ {
/* duplicate the string, and replace invalid bytes in that /* duplicate the string, and replace invalid bytes in that
buffer */ buffer */
char *dest = (char *)xmemdup(src, length); char *dest = (char *)xmemdup(src.data, src.size);
char *const end = dest + length; char *const end = dest + src.size;
char *invalid = dest + (_invalid - src); char *invalid = dest + (_invalid - src.data);
do { do {
*invalid = '?'; *invalid = '?';
...@@ -69,19 +70,19 @@ patch_utf8(const char *src, size_t length, const char *_invalid) ...@@ -69,19 +70,19 @@ patch_utf8(const char *src, size_t length, const char *_invalid)
invalid = const_cast<char *>(__invalid); invalid = const_cast<char *>(__invalid);
} while (invalid != nullptr); } while (invalid != nullptr);
return { dest, length }; return { dest, src.size };
} }
static WritableBuffer<char> static WritableBuffer<char>
fix_utf8(const char *str, size_t length) fix_utf8(StringView p)
{ {
/* check if the string is already valid UTF-8 */ /* check if the string is already valid UTF-8 */
const char *invalid = FindInvalidUTF8(str, str + length); const char *invalid = FindInvalidUTF8(p.begin(), p.end());
if (invalid == nullptr) if (invalid == nullptr)
return nullptr; return nullptr;
/* no, broken - patch invalid sequences */ /* no, broken - patch invalid sequences */
return patch_utf8(str, length, invalid); return patch_utf8(p, invalid);
} }
static bool static bool
...@@ -91,11 +92,11 @@ char_is_non_printable(unsigned char ch) ...@@ -91,11 +92,11 @@ char_is_non_printable(unsigned char ch)
} }
static const char * static const char *
find_non_printable(const char *p, size_t length) find_non_printable(StringView p)
{ {
for (size_t i = 0; i < length; ++i) for (const char &ch : p)
if (char_is_non_printable(p[i])) if (char_is_non_printable(ch))
return p + i; return &ch;
return nullptr; return nullptr;
} }
...@@ -105,31 +106,29 @@ find_non_printable(const char *p, size_t length) ...@@ -105,31 +106,29 @@ find_non_printable(const char *p, size_t length)
* Returns nullptr if nothing needs to be cleared. * Returns nullptr if nothing needs to be cleared.
*/ */
static WritableBuffer<char> static WritableBuffer<char>
clear_non_printable(const char *p, size_t length) clear_non_printable(StringView src)
{ {
const char *first = find_non_printable(p, length); const char *first = find_non_printable(src);
if (first == nullptr) if (first == nullptr)
return nullptr; return nullptr;
char *dest = (char *)xmemdup(p, length); char *dest = (char *)xmemdup(src.data, src.size);
for (size_t i = first - p; i < length; ++i) for (size_t i = first - src.data; i < src.size; ++i)
if (char_is_non_printable(dest[i])) if (char_is_non_printable(dest[i]))
dest[i] = ' '; dest[i] = ' ';
return { dest, length }; return { dest, src.size };
} }
WritableBuffer<char> WritableBuffer<char>
FixTagString(const char *p, size_t length) FixTagString(StringView p)
{ {
auto utf8 = fix_utf8(p, length); auto utf8 = fix_utf8(p);
if (!utf8.IsNull()) { if (!utf8.IsNull())
p = utf8.data; p = {utf8.data, utf8.size};
length = utf8.size;
}
WritableBuffer<char> cleared = clear_non_printable(p, length); WritableBuffer<char> cleared = clear_non_printable(p);
if (cleared.IsNull()) if (cleared.IsNull())
cleared = utf8; cleared = utf8;
else else
......
...@@ -25,10 +25,11 @@ ...@@ -25,10 +25,11 @@
#include <stddef.h> #include <stddef.h>
struct StringView;
template<typename T> struct WritableBuffer; template<typename T> struct WritableBuffer;
gcc_nonnull_all gcc_nonnull_all
WritableBuffer<char> WritableBuffer<char>
FixTagString(const char *p, size_t length); FixTagString(StringView p);
#endif #endif
/*
* Copyright (C) 2013-2015 Max Kellermann <max@duempel.org>
*
* 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_VIEW_HXX
#define STRING_VIEW_HXX
#include "ConstBuffer.hxx"
#include <string.h>
struct StringView : ConstBuffer<char> {
StringView() = default;
constexpr StringView(pointer_type _data, size_type _size)
:ConstBuffer<char>(_data, _size) {}
constexpr StringView(pointer_type _begin, pointer_type _end)
:ConstBuffer<char>(_begin, _end - _begin) {}
StringView(pointer_type _data)
:ConstBuffer<char>(_data,
_data != nullptr ? strlen(_data) : 0) {}
StringView(std::nullptr_t n)
:ConstBuffer<char>(n) {}
static constexpr StringView Empty() {
return StringView("", size_t(0));
}
void SetEmpty() {
data = "";
size = 0;
}
gcc_pure
pointer_type Find(char ch) const {
return (pointer_type)memchr(data, ch, size);
}
StringView &operator=(std::nullptr_t) {
data = nullptr;
size = 0;
return *this;
}
StringView &operator=(pointer_type _data) {
data = _data;
size = _data != nullptr ? strlen(_data) : 0;
return *this;
}
gcc_pure
bool StartsWith(StringView needle) const {
return size >= needle.size &&
memcmp(data, needle.data, needle.size) == 0;
}
gcc_pure
bool Equals(StringView other) const {
return size == other.size &&
memcmp(data, other.data, size) == 0;
}
template<size_t n>
bool EqualsLiteral(const char (&other)[n]) const {
return Equals({other, n - 1});
}
gcc_pure
bool EqualsIgnoreCase(StringView other) const {
return size == other.size &&
strncasecmp(data, other.data, size) == 0;
}
template<size_t n>
bool EqualsLiteralIgnoreCase(const char (&other)[n]) const {
return EqualsIgnoreCase({other, n - 1});
}
};
#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