From 356c829b767863512da1792c048f6ddbeb8457a3 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Wed, 30 Sep 2015 22:03:01 +0200 Subject: util/StringView: new utility class --- src/IcyMetaDataParser.cxx | 3 +- src/db/plugins/upnp/Directory.cxx | 19 ++--- src/decoder/plugins/Mpg123DecoderPlugin.cxx | 3 +- src/playlist/plugins/AsxPlaylistPlugin.cxx | 4 +- src/playlist/plugins/RssPlaylistPlugin.cxx | 4 +- src/playlist/plugins/XspfPlaylistPlugin.cxx | 4 +- src/tag/ApeLoader.cxx | 3 +- src/tag/ApeLoader.hxx | 4 +- src/tag/ApeReplayGain.cxx | 16 ++--- src/tag/ApeTag.cxx | 16 ++--- src/tag/TagBuilder.cxx | 34 ++++----- src/tag/TagBuilder.hxx | 5 +- src/tag/TagPool.cxx | 42 +++++------ src/tag/TagPool.hxx | 3 +- src/tag/TagString.cxx | 47 ++++++------ src/tag/TagString.hxx | 3 +- src/util/StringView.hxx | 108 ++++++++++++++++++++++++++++ 17 files changed, 207 insertions(+), 111 deletions(-) create mode 100644 src/util/StringView.hxx (limited to 'src') diff --git a/src/IcyMetaDataParser.cxx b/src/IcyMetaDataParser.cxx index 4fdb81aa1..9fb97d4d4 100644 --- a/src/IcyMetaDataParser.cxx +++ b/src/IcyMetaDataParser.cxx @@ -22,6 +22,7 @@ #include "tag/Tag.hxx" #include "tag/TagBuilder.hxx" #include "util/Domain.hxx" +#include "util/StringView.hxx" #include "Log.hxx" #include @@ -76,7 +77,7 @@ icy_add_item(TagBuilder &tag, TagType type, const char *value) } if (length > 0) - tag.AddItem(type, value, length); + tag.AddItem(type, {value, length}); } static void diff --git a/src/db/plugins/upnp/Directory.cxx b/src/db/plugins/upnp/Directory.cxx index 894a53c6a..e7f92432a 100644 --- a/src/db/plugins/upnp/Directory.cxx +++ b/src/db/plugins/upnp/Directory.cxx @@ -25,6 +25,7 @@ #include "tag/TagBuilder.hxx" #include "tag/TagTable.hxx" #include "util/NumberParser.hxx" +#include "util/StringView.hxx" #include #include @@ -36,23 +37,13 @@ UPnPDirContent::~UPnPDirContent() /* 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 static UPnPDirObject::ItemClass -ParseItemClass(const char *name, size_t length) +ParseItemClass(StringView name) { - if (CompareStringLiteral("object.item.audioItem.musicTrack", - name, length)) + if (name.EqualsLiteral("object.item.audioItem.musicTrack")) return UPnPDirObject::ItemClass::MUSIC; - else if (CompareStringLiteral("object.item.playlistItem", - name, length)) + else if (name.EqualsLiteral("object.item.playlistItem")) return UPnPDirObject::ItemClass::PLAYLIST; else return UPnPDirObject::ItemClass::UNKNOWN; @@ -239,7 +230,7 @@ protected: break; case CLASS: - object.item_class = ParseItemClass(s, len); + object.item_class = ParseItemClass(StringView(s, len)); break; } } diff --git a/src/decoder/plugins/Mpg123DecoderPlugin.cxx b/src/decoder/plugins/Mpg123DecoderPlugin.cxx index 760b8b9c2..43c0c868f 100644 --- a/src/decoder/plugins/Mpg123DecoderPlugin.cxx +++ b/src/decoder/plugins/Mpg123DecoderPlugin.cxx @@ -28,6 +28,7 @@ #include "fs/Path.hxx" #include "util/Error.hxx" #include "util/Domain.hxx" +#include "util/StringView.hxx" #include "Log.hxx" #include @@ -111,7 +112,7 @@ AddTagItem(TagBuilder &tag, TagType type, const mpg123_string &s) assert(s.size >= s.fill); assert(s.fill > 0); - tag.AddItem(type, s.p, s.fill - 1); + tag.AddItem(type, {s.p, s.fill - 1}); } static void diff --git a/src/playlist/plugins/AsxPlaylistPlugin.cxx b/src/playlist/plugins/AsxPlaylistPlugin.cxx index c6226346b..1a0334237 100644 --- a/src/playlist/plugins/AsxPlaylistPlugin.cxx +++ b/src/playlist/plugins/AsxPlaylistPlugin.cxx @@ -24,6 +24,7 @@ #include "tag/TagBuilder.hxx" #include "util/ASCII.hxx" #include "util/Error.hxx" +#include "util/StringView.hxx" #include "lib/expat/ExpatParser.hxx" #include "Log.hxx" @@ -130,7 +131,8 @@ asx_char_data(void *user_data, const XML_Char *s, int len) case AsxParser::ENTRY: 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; } diff --git a/src/playlist/plugins/RssPlaylistPlugin.cxx b/src/playlist/plugins/RssPlaylistPlugin.cxx index 27588cddf..a2d8e7a42 100644 --- a/src/playlist/plugins/RssPlaylistPlugin.cxx +++ b/src/playlist/plugins/RssPlaylistPlugin.cxx @@ -24,6 +24,7 @@ #include "tag/TagBuilder.hxx" #include "util/ASCII.hxx" #include "util/Error.hxx" +#include "util/StringView.hxx" #include "lib/expat/ExpatParser.hxx" #include "Log.hxx" @@ -128,7 +129,8 @@ rss_char_data(void *user_data, const XML_Char *s, int len) case RssParser::ITEM: 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; } diff --git a/src/playlist/plugins/XspfPlaylistPlugin.cxx b/src/playlist/plugins/XspfPlaylistPlugin.cxx index 6d09b6db2..0b7358e15 100644 --- a/src/playlist/plugins/XspfPlaylistPlugin.cxx +++ b/src/playlist/plugins/XspfPlaylistPlugin.cxx @@ -25,6 +25,7 @@ #include "input/InputStream.hxx" #include "tag/TagBuilder.hxx" #include "util/Error.hxx" +#include "util/StringView.hxx" #include "lib/expat/ExpatParser.hxx" #include "Log.hxx" @@ -170,7 +171,8 @@ xspf_char_data(void *user_data, const XML_Char *s, int len) case XspfParser::TRACK: if (!parser->location.empty() && 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; diff --git a/src/tag/ApeLoader.cxx b/src/tag/ApeLoader.cxx index 8c89f34c9..c42d088a8 100644 --- a/src/tag/ApeLoader.cxx +++ b/src/tag/ApeLoader.cxx @@ -21,6 +21,7 @@ #include "ApeLoader.hxx" #include "system/ByteOrder.hxx" #include "fs/FileSystem.hxx" +#include "util/StringView.hxx" #include #include @@ -89,7 +90,7 @@ ape_scan_internal(FILE *fp, ApeTagCallback callback) if (remaining < size) break; - if (!callback(flags, key, p, size)) + if (!callback(flags, key, {p, size})) break; p += size; diff --git a/src/tag/ApeLoader.hxx b/src/tag/ApeLoader.hxx index 1bdfe692d..4587ff063 100644 --- a/src/tag/ApeLoader.hxx +++ b/src/tag/ApeLoader.hxx @@ -26,11 +26,11 @@ #include +struct StringView; class Path; typedef std::function ApeTagCallback; + StringView value)> ApeTagCallback; /** * Scans the APE tag values from a file. diff --git a/src/tag/ApeReplayGain.cxx b/src/tag/ApeReplayGain.cxx index 139d44d1f..883885369 100644 --- a/src/tag/ApeReplayGain.cxx +++ b/src/tag/ApeReplayGain.cxx @@ -21,15 +21,16 @@ #include "ApeReplayGain.hxx" #include "ApeLoader.hxx" #include "ReplayGain.hxx" -#include "util/ASCII.hxx" #include "fs/Path.hxx" +#include "util/ASCII.hxx" +#include "util/StringView.hxx" #include #include static bool replay_gain_ape_callback(unsigned long flags, const char *key, - const char *_value, size_t value_length, + StringView _value, ReplayGainInfo &info) { /* we only care about utf-8 text tags */ @@ -37,11 +38,11 @@ replay_gain_ape_callback(unsigned long flags, const char *key, return false; char value[16]; - if (value_length >= sizeof(value)) + if (_value.size >= sizeof(value)) return false; - memcpy(value, _value, value_length); - value[value_length] = 0; + memcpy(value, _value.data, _value.size); + value[_value.size] = 0; return ParseReplayGainTag(info, key, value); } @@ -53,10 +54,9 @@ replay_gain_ape_read(Path path_fs, ReplayGainInfo &info) auto callback = [&info, &found] (unsigned long flags, const char *key, - const char *value, - size_t value_length) { + StringView value) { found |= replay_gain_ape_callback(flags, key, - value, value_length, + value, info); return true; }; diff --git a/src/tag/ApeTag.cxx b/src/tag/ApeTag.cxx index 49ae7a036..81318a771 100644 --- a/src/tag/ApeTag.cxx +++ b/src/tag/ApeTag.cxx @@ -24,6 +24,7 @@ #include "TagTable.hxx" #include "TagHandler.hxx" #include "fs/Path.hxx" +#include "util/StringView.hxx" #include @@ -75,17 +76,18 @@ ForEachValue(const char *value, const char *end, C &&callback) */ static bool 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) { /* we only care about utf-8 text tags */ if ((flags & (0x3 << 1)) != 0) return false; - const char *const end = value + value_length; + const auto begin = value.begin(); + const auto end = value.end(); if (handler->pair != nullptr) - ForEachValue(value, end, [handler, handler_ctx, + ForEachValue(begin, end, [handler, handler_ctx, key](const char *_value) { handler->pair(key, _value, handler_ctx); }); @@ -94,8 +96,8 @@ tag_ape_import_item(unsigned long flags, if (type == TAG_NUM_OF_ITEM_TYPES) return false; - ForEachValue(value, end, [handler, handler_ctx, - type](const char *_value) { + ForEachValue(begin, end, [handler, handler_ctx, + type](const char *_value) { tag_handler_invoke_tag(handler, handler_ctx, type, _value); }); @@ -111,10 +113,8 @@ tag_ape_scan2(Path path_fs, auto callback = [handler, handler_ctx, &recognized] (unsigned long flags, const char *key, - const char *value, - size_t value_length) { + StringView value) { recognized |= tag_ape_import_item(flags, key, value, - value_length, handler, handler_ctx); return true; }; diff --git a/src/tag/TagBuilder.cxx b/src/tag/TagBuilder.cxx index 4ce50cc4c..82d99006a 100644 --- a/src/tag/TagBuilder.cxx +++ b/src/tag/TagBuilder.cxx @@ -24,6 +24,7 @@ #include "TagString.hxx" #include "Tag.hxx" #include "util/WritableBuffer.hxx" +#include "util/StringView.hxx" #include @@ -189,22 +190,16 @@ TagBuilder::Complement(const Tag &other) } inline void -TagBuilder::AddItemInternal(TagType type, const char *value, size_t length) +TagBuilder::AddItemInternal(TagType type, StringView value) { -#if !CLANG_CHECK_VERSION(3,6) - /* disabled on clang due to -Wtautological-pointer-compare */ - assert(value != nullptr); -#endif - assert(length > 0); + assert(!value.IsEmpty()); - auto f = FixTagString(value, length); - if (!f.IsNull()) { - value = f.data; - length = f.size; - } + auto f = FixTagString(value); + if (!f.IsNull()) + value = { f.data, f.size }; 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(); free(f.data); @@ -213,17 +208,12 @@ TagBuilder::AddItemInternal(TagType type, const char *value, size_t length) } void -TagBuilder::AddItem(TagType type, const char *value, size_t length) +TagBuilder::AddItem(TagType type, StringView value) { -#if !CLANG_CHECK_VERSION(3,6) - /* disabled on clang due to -Wtautological-pointer-compare */ - assert(value != nullptr); -#endif - - if (length == 0 || !IsTagEnabled(type)) + if (value.IsEmpty() || !IsTagEnabled(type)) return; - AddItemInternal(type, value, length); + AddItemInternal(type, value); } void @@ -234,14 +224,14 @@ TagBuilder::AddItem(TagType type, const char *value) assert(value != nullptr); #endif - AddItem(type, value, strlen(value)); + AddItem(type, StringView(value)); } void TagBuilder::AddEmptyItem(TagType type) { 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(); items.push_back(i); diff --git a/src/tag/TagBuilder.hxx b/src/tag/TagBuilder.hxx index 738caa6cd..08c66d0b3 100644 --- a/src/tag/TagBuilder.hxx +++ b/src/tag/TagBuilder.hxx @@ -28,6 +28,7 @@ #include +struct StringView; struct TagItem; struct Tag; @@ -141,7 +142,7 @@ public: * @param length the length of #value */ gcc_nonnull_all - void AddItem(TagType type, const char *value, size_t length); + void AddItem(TagType type, StringView value); /** * Appends a new tag item. @@ -171,7 +172,7 @@ public: private: gcc_nonnull_all - void AddItemInternal(TagType type, const char *value, size_t length); + void AddItemInternal(TagType type, StringView value); }; #endif diff --git a/src/tag/TagPool.cxx b/src/tag/TagPool.cxx index 0280948fd..88b2ba333 100644 --- a/src/tag/TagPool.cxx +++ b/src/tag/TagPool.cxx @@ -22,6 +22,7 @@ #include "TagItem.hxx" #include "util/Cast.hxx" #include "util/VarSize.hxx" +#include "util/StringView.hxx" #include #include @@ -37,39 +38,37 @@ struct TagPoolSlot { TagItem item; TagPoolSlot(TagPoolSlot *_next, TagType type, - const char *value, size_t length) + StringView value) :next(_next), ref(1) { item.type = type; - memcpy(item.value, value, length); - item.value[length] = 0; + memcpy(item.value, value.data, value.size); + item.value[value.size] = 0; } static TagPoolSlot *Create(TagPoolSlot *_next, TagType type, - const char *value, size_t length); + StringView value); } gcc_packed; TagPoolSlot * TagPoolSlot::Create(TagPoolSlot *_next, TagType type, - const char *value, size_t length) + StringView value) { TagPoolSlot *dummy; return NewVarSize(sizeof(dummy->item.value), - length + 1, + value.size + 1, _next, type, - value, length); + value); } static TagPoolSlot *slots[NUM_SLOTS]; static inline unsigned -calc_hash_n(TagType type, const char *p, size_t length) +calc_hash(TagType type, StringView p) { unsigned hash = 5381; - assert(p != nullptr); - - while (length-- > 0) - hash = (hash << 5) + hash + *p++; + for (auto ch : p) + hash = (hash << 5) + hash + ch; return hash ^ type; } @@ -97,9 +96,9 @@ tag_item_to_slot(TagItem *item) } 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 ** @@ -109,13 +108,12 @@ tag_value_slot_p(TagType type, const char *value) } 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) { if (slot->item.type == type && - length == strlen(slot->item.value) && - memcmp(value, slot->item.value, length) == 0 && + value.Equals(slot->item.value) && slot->ref < 0xff) { assert(slot->ref > 0); ++slot->ref; @@ -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; return &slot->item; } @@ -141,11 +139,9 @@ tag_pool_dup_item(TagItem *item) } else { /* the reference counter overflows above 0xff; duplicate the item, and start with 1 */ - size_t length = strlen(item->value); - auto slot_p = tag_value_slot_p(item->type, - item->value, length); + auto slot_p = tag_value_slot_p(item->type, item->value); slot = TagPoolSlot::Create(*slot_p, item->type, - item->value, strlen(item->value)); + item->value); *slot_p = slot; return &slot->item; } diff --git a/src/tag/TagPool.hxx b/src/tag/TagPool.hxx index 7fba85b54..7b4b5dadf 100644 --- a/src/tag/TagPool.hxx +++ b/src/tag/TagPool.hxx @@ -26,9 +26,10 @@ extern Mutex tag_pool_lock; struct TagItem; +struct StringView; TagItem * -tag_pool_get_item(TagType type, const char *value, size_t length); +tag_pool_get_item(TagType type, StringView value); TagItem * tag_pool_dup_item(TagItem *item); diff --git a/src/tag/TagString.cxx b/src/tag/TagString.cxx index 85227de24..d30a07a27 100644 --- a/src/tag/TagString.cxx +++ b/src/tag/TagString.cxx @@ -21,6 +21,7 @@ #include "TagString.hxx" #include "util/Alloc.hxx" #include "util/WritableBuffer.hxx" +#include "util/StringView.hxx" #include "util/UTF8.hxx" #include @@ -54,14 +55,14 @@ FindInvalidUTF8(const char *p, const char *const end) * Replace invalid sequences with the question mark. */ static WritableBuffer -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 buffer */ - char *dest = (char *)xmemdup(src, length); - char *const end = dest + length; + char *dest = (char *)xmemdup(src.data, src.size); + char *const end = dest + src.size; - char *invalid = dest + (_invalid - src); + char *invalid = dest + (_invalid - src.data); do { *invalid = '?'; @@ -69,19 +70,19 @@ patch_utf8(const char *src, size_t length, const char *_invalid) invalid = const_cast(__invalid); } while (invalid != nullptr); - return { dest, length }; + return { dest, src.size }; } static WritableBuffer -fix_utf8(const char *str, size_t length) +fix_utf8(StringView p) { /* 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) return nullptr; /* no, broken - patch invalid sequences */ - return patch_utf8(str, length, invalid); + return patch_utf8(p, invalid); } static bool @@ -91,11 +92,11 @@ char_is_non_printable(unsigned char ch) } 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) - if (char_is_non_printable(p[i])) - return p + i; + for (const char &ch : p) + if (char_is_non_printable(ch)) + return &ch; return nullptr; } @@ -105,31 +106,29 @@ find_non_printable(const char *p, size_t length) * Returns nullptr if nothing needs to be cleared. */ static WritableBuffer -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) 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])) dest[i] = ' '; - return { dest, length }; + return { dest, src.size }; } WritableBuffer -FixTagString(const char *p, size_t length) +FixTagString(StringView p) { - auto utf8 = fix_utf8(p, length); - if (!utf8.IsNull()) { - p = utf8.data; - length = utf8.size; - } + auto utf8 = fix_utf8(p); + if (!utf8.IsNull()) + p = {utf8.data, utf8.size}; - WritableBuffer cleared = clear_non_printable(p, length); + WritableBuffer cleared = clear_non_printable(p); if (cleared.IsNull()) cleared = utf8; else diff --git a/src/tag/TagString.hxx b/src/tag/TagString.hxx index f4cba9a40..53fbc7abd 100644 --- a/src/tag/TagString.hxx +++ b/src/tag/TagString.hxx @@ -25,10 +25,11 @@ #include +struct StringView; template struct WritableBuffer; gcc_nonnull_all WritableBuffer -FixTagString(const char *p, size_t length); +FixTagString(StringView p); #endif diff --git a/src/util/StringView.hxx b/src/util/StringView.hxx new file mode 100644 index 000000000..fbae9a7cc --- /dev/null +++ b/src/util/StringView.hxx @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2013-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_VIEW_HXX +#define STRING_VIEW_HXX + +#include "ConstBuffer.hxx" + +#include + +struct StringView : ConstBuffer { + StringView() = default; + + constexpr StringView(pointer_type _data, size_type _size) + :ConstBuffer(_data, _size) {} + + constexpr StringView(pointer_type _begin, pointer_type _end) + :ConstBuffer(_begin, _end - _begin) {} + + StringView(pointer_type _data) + :ConstBuffer(_data, + _data != nullptr ? strlen(_data) : 0) {} + + StringView(std::nullptr_t n) + :ConstBuffer(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 + 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 + bool EqualsLiteralIgnoreCase(const char (&other)[n]) const { + return EqualsIgnoreCase({other, n - 1}); + } +}; + +#endif -- cgit v1.2.3