util/StringView: new utility class
This commit is contained in:
@@ -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 @@
|
|||||||
#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)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (length > 0)
|
if (length > 0)
|
||||||
tag.AddItem(type, value, length);
|
tag.AddItem(type, {value, length});
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@@ -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()
|
|||||||
/* 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:
|
|||||||
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 @@
|
|||||||
#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)
|
|||||||
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 @@
|
|||||||
#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)
|
|||||||
|
|
||||||
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 @@
|
|||||||
#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)
|
|||||||
|
|
||||||
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 @@
|
|||||||
#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)
|
|||||||
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 @@
|
|||||||
#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)
|
|||||||
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 @@
|
|||||||
|
|
||||||
#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 @@
|
|||||||
#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,
|
|||||||
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)
|
|||||||
|
|
||||||
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 @@
|
|||||||
#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)
|
|||||||
*/
|
*/
|
||||||
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,
|
|||||||
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,
|
|||||||
|
|
||||||
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 @@
|
|||||||
#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)
|
|||||||
}
|
}
|
||||||
|
|
||||||
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)
|
|||||||
}
|
}
|
||||||
|
|
||||||
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)
|
|||||||
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 @@
|
|||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
|
struct StringView;
|
||||||
struct TagItem;
|
struct TagItem;
|
||||||
struct Tag;
|
struct Tag;
|
||||||
|
|
||||||
@@ -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:
|
|||||||
|
|
||||||
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 @@
|
|||||||
#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 {
|
|||||||
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)
|
|||||||
}
|
}
|
||||||
|
|
||||||
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)
|
|||||||
}
|
}
|
||||||
|
|
||||||
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)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
|||||||
} 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 @@
|
|||||||
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 @@
|
|||||||
#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)
|
|||||||
* 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)
|
|||||||
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)
|
|||||||
}
|
}
|
||||||
|
|
||||||
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)
|
|||||||
* 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 @@
|
|||||||
|
|
||||||
#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
|
||||||
|
108
src/util/StringView.hxx
Normal file
108
src/util/StringView.hxx
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
Reference in New Issue
Block a user