libluna: Add a reference-counted immutable string type

This commit is contained in:
apio 2024-09-01 12:40:20 +02:00
parent 0abd9153ae
commit a11aa7a2d0
Signed by: asleepymoon
GPG Key ID: B8A7D06E42258954
5 changed files with 299 additions and 0 deletions

View File

@ -19,6 +19,7 @@ set(FREESTANDING_SOURCES
src/SHA.cpp
src/Stack.cpp
src/String.cpp
src/RefString.cpp
src/StringBuilder.cpp
src/StringView.cpp
src/Utf8.cpp

View File

@ -0,0 +1,120 @@
/**
* @file RefString.h
* @author apio (cloudapio.eu)
* @brief Reference-counted, trivially-copyable, immutable string type.
*
* @copyright Copyright (c) 2024, the Luna authors.
*
*/
#pragma once
#include <luna/Hash.h>
#include <luna/Result.h>
#include <luna/SharedPtr.h>
#include <luna/String.h>
#include <luna/StringView.h>
#include <stdarg.h>
class RefString
{
typedef const char* ConstIterator;
public:
/* Constructs an empty String. */
RefString();
RefString(RefString&&);
RefString(const RefString&);
RefString& operator=(const RefString&);
bool operator==(const RefString& other) const
{
return !compare(this, &other);
}
~RefString() = default;
/* Creates a copy of a specific segment of this String and returns it. */
Result<RefString> substring(usize begin, usize size) const;
/* Splits a String with a delimiter. */
Result<Vector<RefString>> split(StringView delim) const
{
return view().ref_split(delim);
}
/* Splits a String into two parts with a delimiter. */
Result<Vector<RefString>> split_once(char delim) const
{
return view().ref_split_once(delim);
}
/* Creates a single String consisting of a list of strings separated by a delimiter. */
static Result<RefString> join(const Vector<RefString>& vec, StringView delim);
/* Creates a single String consisting of a list of strings separated by a delimiter. */
static Result<RefString> join(const Vector<StringView>& vec, StringView delim);
static Result<RefString> format(StringView fmt, ...);
static Result<RefString> vformat(StringView fmt, va_list ap);
static Result<RefString> from_cstring(const char* str);
static Result<RefString> from_string_view(StringView str);
static Result<RefString> from_string(const String& str);
static int compare(const RefString* a, const RefString* b);
const char* chars() const;
usize length() const
{
return m_string_data ? m_string_data->length : 0;
}
bool is_empty() const
{
return length() == 0;
}
StringView view() const
{
return { chars(), length() };
}
const char& operator[](usize) const;
ConstIterator begin() const
{
return chars();
}
ConstIterator end() const
{
return begin() + length();
}
private:
struct RefStringData : public Shareable
{
char* string { nullptr };
usize length { 0 };
RefStringData(char* s, usize l) : Shareable(), string(s), length(l)
{
}
~RefStringData()
{
if (string) free_impl(string);
}
};
SharedPtr<RefStringData> m_string_data { nullptr };
static Result<RefString> adopt(char* c_str);
static Result<RefString> adopt(char* c_str, usize length);
};
template <> u64 hash(const RefString& value, u64 salt);

View File

@ -4,6 +4,7 @@
#include <luna/Vector.h>
class String;
class RefString;
class StringView
{
@ -43,6 +44,8 @@ class StringView
Result<Vector<String>> split(StringView delim) const;
Result<Vector<String>> split_once(char delim) const;
Result<Vector<RefString>> ref_split(StringView delim) const;
Result<Vector<RefString>> ref_split_once(char delim) const;
Result<Vector<StringView>> split_view(char delim) const;
bool contains(char v) const;

122
libluna/src/RefString.cpp Normal file
View File

@ -0,0 +1,122 @@
/**
* @file RefString.cpp
* @author apio (cloudapio.eu)
* @brief Reference-counted, trivially-copyable, immutable string type.
*
* @copyright Copyright (c) 2024, the Luna authors.
*
*/
#include <luna/Format.h>
#include <luna/RefString.h>
static const char empty_string[] = { 0 };
RefString::RefString()
{
}
RefString::RefString(RefString&& other) : m_string_data(move(other.m_string_data))
{
}
RefString::RefString(const RefString& other) : m_string_data(other.m_string_data)
{
}
const char* RefString::chars() const
{
if (m_string_data) return m_string_data->string;
return empty_string;
}
RefString& RefString::operator=(const RefString& other)
{
if (&other == this) return *this;
m_string_data = other.m_string_data;
return *this;
}
Result<RefString> RefString::substring(usize begin, usize size) const
{
if (begin + size >= size) return err(EINVAL);
char* const dup = strndup(chars() + begin, size);
if (!dup) return err(ENOMEM);
return adopt(dup, size);
}
const char& RefString::operator[](usize index) const
{
expect(index < length(), "index out of range");
return chars()[index];
}
Result<RefString> RefString::format(StringView fmt, ...)
{
va_list ap;
va_start(ap, fmt);
auto rc = vformat(fmt, ap);
va_end(ap);
return rc;
}
Result<RefString> RefString::vformat(StringView fmt, va_list ap)
{
Vector<char> buf;
TRY(cstyle_format(
fmt.chars(), [](char c, void* data) -> Result<void> { return ((Vector<char>*)data)->try_append(c); }, &buf,
ap));
TRY(buf.try_append(0));
return adopt(buf.release_data());
}
Result<RefString> RefString::from_cstring(const char* str)
{
return from_string_view(StringView { str });
}
Result<RefString> RefString::from_string(const String& str)
{
return from_string_view(str.view());
}
Result<RefString> RefString::from_string_view(StringView str)
{
char* const dup = strndup(str.chars(), str.length());
if (!dup) return err(ENOMEM);
return adopt(dup, str.length());
}
int RefString::compare(const RefString* a, const RefString* b)
{
return strcmp(a->chars(), b->chars());
}
Result<RefString> RefString::adopt(char* c_str)
{
return adopt(c_str, strlen(c_str));
}
Result<RefString> RefString::adopt(char* c_str, usize length)
{
auto guard = make_scope_guard([&] { free_impl(c_str); });
auto data = TRY(make_shared<RefStringData>(c_str, length));
guard.deactivate();
RefString string;
string.m_string_data = data;
return string;
}
template <> u64 hash(const RefString& value, u64 salt)
{
return hash_memory(value.chars(), value.length(), salt);
}

View File

@ -1,5 +1,6 @@
#include <luna/CString.h>
#include <luna/NumberParsing.h>
#include <luna/RefString.h>
#include <luna/ScopeGuard.h>
#include <luna/String.h>
#include <luna/StringView.h>
@ -86,6 +87,32 @@ Result<Vector<String>> StringView::split(StringView delim) const
return result;
}
Result<Vector<RefString>> StringView::ref_split(StringView delim) const
{
Vector<RefString> result;
RefString str;
char* copy = strndup(m_string, m_length);
auto guard = make_scope_guard([copy] { free_impl(copy); });
char* segment = strtok(copy, delim.chars());
if (!segment) return result;
str = TRY(RefString::from_cstring(segment));
TRY(result.try_append(move(str)));
while (true)
{
segment = strtok(nullptr, delim.chars());
if (!segment) return result;
str = TRY(RefString::from_cstring(segment));
TRY(result.try_append(move(str)));
}
return result;
}
Result<Vector<String>> StringView::split_once(char delim) const
{
Vector<String> result;
@ -112,6 +139,32 @@ Result<Vector<String>> StringView::split_once(char delim) const
return result;
}
Result<Vector<RefString>> StringView::ref_split_once(char delim) const
{
Vector<RefString> result;
char* copy = strndup(m_string, m_length);
auto guard = make_scope_guard([copy] { free_impl(copy); });
char* middle = strchr(copy, delim);
if (!middle)
{
auto str = TRY(RefString::from_cstring(copy));
TRY(result.try_append(move(str)));
return result;
}
*middle = '\0';
auto begin = TRY(RefString::from_cstring(copy));
auto end = TRY(RefString::from_cstring(middle + 1));
TRY(result.try_append(move(begin)));
TRY(result.try_append(move(end)));
return result;
}
Result<Vector<StringView>> StringView::split_view(char delim) const
{
Vector<StringView> result;