563 lines
10 KiB
C++
563 lines
10 KiB
C++
/*
|
|
* moogpp/strutils.cpp
|
|
*
|
|
* Copyright (C) 2004-2015 by Yevgen Muntyan <emuntyan@users.sourceforge.net>
|
|
*
|
|
* This file is part of medit. medit is free software; you can
|
|
* redistribute it and/or modify it under the terms of the
|
|
* GNU Lesser General Public License as published by the
|
|
* Free Software Foundation; either version 2.1 of the License,
|
|
* or (at your option) any later version.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with medit. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "moogpp/strutils.h"
|
|
#include <string.h>
|
|
|
|
using namespace g;
|
|
|
|
static bool str_equal(const char* s1, const char* s2)
|
|
{
|
|
if (!s1 || !*s1)
|
|
return !s2 || !*s2;
|
|
else if (!s2)
|
|
return false;
|
|
else
|
|
return strcmp(s1, s2) == 0;
|
|
}
|
|
|
|
bool g::operator==(const gstr& s1, const char* s2)
|
|
{
|
|
return str_equal(s1, s2);
|
|
}
|
|
|
|
bool g::operator==(const char* s1, const gstr& s2)
|
|
{
|
|
return str_equal(s1, s2);
|
|
}
|
|
|
|
bool g::operator==(const gstr& s1, const gstr& s2)
|
|
{
|
|
return str_equal(s1, s2);
|
|
}
|
|
|
|
bool g::operator!=(const gstr& s1, const gstr& s2)
|
|
{
|
|
return !(s1 == s2);
|
|
}
|
|
|
|
bool g::operator!=(const gstr& s1, const char* s2)
|
|
{
|
|
return !(s1 == s2);
|
|
}
|
|
|
|
bool g::operator!=(const char* s1, const gstr& s2)
|
|
{
|
|
return !(s1 == s2);
|
|
}
|
|
|
|
|
|
class StringData
|
|
{
|
|
public:
|
|
StringData(const char* s)
|
|
: m_p(g_strdup(s))
|
|
, m_ref(1)
|
|
{
|
|
}
|
|
|
|
~StringData()
|
|
{
|
|
::g_free(m_p);
|
|
}
|
|
|
|
StringData(const StringData&) = delete;
|
|
StringData& operator=(const StringData&) = delete;
|
|
|
|
char* get() const
|
|
{
|
|
return m_p;
|
|
}
|
|
|
|
char* release()
|
|
{
|
|
char* ret = m_p;
|
|
m_p = nullptr;
|
|
return ret;
|
|
}
|
|
|
|
void ref()
|
|
{
|
|
g_atomic_int_inc(&m_ref);
|
|
}
|
|
|
|
void unref()
|
|
{
|
|
if (g_atomic_int_dec_and_test(&m_ref))
|
|
delete this;
|
|
}
|
|
|
|
int ref_count() const
|
|
{
|
|
return g_atomic_int_get(&m_ref);
|
|
}
|
|
|
|
private:
|
|
char* m_p;
|
|
int m_ref;
|
|
};
|
|
|
|
const gstr gstr::null;
|
|
|
|
gstr::gstr()
|
|
: m_p(nullptr)
|
|
, m_is_inline(true)
|
|
, m_is_const(true)
|
|
{
|
|
}
|
|
|
|
gstr::gstr(const char* s, mem_transfer mt)
|
|
: gstr()
|
|
{
|
|
if (s == nullptr)
|
|
return;
|
|
|
|
if (*s == 0)
|
|
{
|
|
if (mt == mem_transfer::take_ownership)
|
|
::g_free(const_cast<char*>(s));
|
|
|
|
mt = mem_transfer::borrow;
|
|
s = "";
|
|
}
|
|
|
|
switch (mt)
|
|
{
|
|
case mem_transfer::borrow:
|
|
m_is_const = true;
|
|
m_is_inline = true;
|
|
m_p = const_cast<char*>(s);
|
|
break;
|
|
|
|
case mem_transfer::make_copy:
|
|
m_is_const = false;
|
|
m_is_inline = true;
|
|
m_p = g_strdup(s);
|
|
break;
|
|
|
|
case mem_transfer::take_ownership:
|
|
m_is_const = false;
|
|
m_is_inline = true;
|
|
m_p = const_cast<char*>(s);
|
|
break;
|
|
}
|
|
}
|
|
|
|
gstr::gstr(const gstr& other)
|
|
: gstr()
|
|
{
|
|
if (other.m_p == nullptr)
|
|
return;
|
|
|
|
if (other.m_is_const)
|
|
{
|
|
g_assert(other.m_is_inline);
|
|
m_p = other.m_p;
|
|
m_is_const = true;
|
|
m_is_inline = true;
|
|
}
|
|
else
|
|
{
|
|
g_assert(other.m_p != nullptr);
|
|
g_assert(!other.m_is_const);
|
|
|
|
StringData* d;
|
|
|
|
if (other.m_is_inline)
|
|
{
|
|
d = new StringData(static_cast<const char*>(other));
|
|
}
|
|
else
|
|
{
|
|
d = reinterpret_cast<StringData*>(other.m_p);
|
|
d->ref();
|
|
}
|
|
|
|
m_p = d;
|
|
m_is_inline = false;
|
|
m_is_const = false;
|
|
}
|
|
}
|
|
|
|
gstr& gstr::operator=(const gstr& other)
|
|
{
|
|
if (this != &other)
|
|
{
|
|
gstr tmp(other);
|
|
*this = std::move(tmp);
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
gstr::gstr(gstr&& other)
|
|
: gstr()
|
|
{
|
|
*this = std::move(other);
|
|
}
|
|
|
|
gstr& gstr::operator=(gstr&& other)
|
|
{
|
|
std::swap(m_is_const, other.m_is_const);
|
|
std::swap(m_is_inline, other.m_is_inline);
|
|
std::swap(m_p, other.m_p);
|
|
return *this;
|
|
}
|
|
|
|
gstr::gstr(gstrp&& s)
|
|
: gstr(s.release(), mem_transfer::take_ownership)
|
|
{
|
|
}
|
|
|
|
gstr& gstr::operator= (gstrp&& s)
|
|
{
|
|
set_new (s.release ());
|
|
return *this;
|
|
}
|
|
|
|
gstr::~gstr()
|
|
{
|
|
clear();
|
|
}
|
|
|
|
bool gstr::is_null() const
|
|
{
|
|
bool ret = (m_p == nullptr);
|
|
g_assert(!ret || m_is_const == true);
|
|
g_assert(!ret || m_is_inline == true);
|
|
return ret;
|
|
}
|
|
|
|
gstr::operator const char*() const
|
|
{
|
|
if (m_is_inline)
|
|
return reinterpret_cast<char*>(m_p);
|
|
else
|
|
return reinterpret_cast<StringData*>(m_p)->get();
|
|
}
|
|
|
|
void gstr::assign(const char* s, mem_transfer mt)
|
|
{
|
|
gstr tmp(s, mt);
|
|
*this = std::move(tmp);
|
|
}
|
|
|
|
void gstr::clear()
|
|
{
|
|
if (m_p == nullptr)
|
|
return;
|
|
|
|
if (!m_is_const)
|
|
{
|
|
g_assert(m_p != nullptr);
|
|
|
|
if (m_is_inline)
|
|
::g_free(m_p);
|
|
else
|
|
reinterpret_cast<StringData*>(m_p)->unref();
|
|
|
|
m_is_const = true;
|
|
}
|
|
|
|
m_is_inline = true;
|
|
m_p = nullptr;
|
|
}
|
|
|
|
char* gstr::get_mutable()
|
|
{
|
|
if (!m_p)
|
|
{
|
|
return nullptr;
|
|
}
|
|
else if (m_is_const)
|
|
{
|
|
char* s = reinterpret_cast<char*>(m_p);
|
|
|
|
if (*s != 0)
|
|
{
|
|
set(s);
|
|
g_assert(!m_is_const);
|
|
g_assert(m_is_inline);
|
|
}
|
|
|
|
return reinterpret_cast<char*>(m_p);
|
|
}
|
|
else if (m_is_inline)
|
|
{
|
|
return reinterpret_cast<char*>(m_p);
|
|
}
|
|
else
|
|
{
|
|
StringData* d = reinterpret_cast<StringData*>(m_p);
|
|
g_assert(d->get() && *d->get());
|
|
|
|
if (d->ref_count() == 1)
|
|
{
|
|
return d->get();
|
|
}
|
|
else
|
|
{
|
|
m_p = g_strdup(d->get());
|
|
m_is_inline = true;
|
|
return reinterpret_cast<char*>(m_p);
|
|
}
|
|
}
|
|
}
|
|
|
|
char* gstr::release_owned()
|
|
{
|
|
if (m_p == nullptr)
|
|
{
|
|
return nullptr;
|
|
}
|
|
else if (m_is_const)
|
|
{
|
|
return g_strdup(get());
|
|
}
|
|
else if (m_is_inline)
|
|
{
|
|
m_is_const = true;
|
|
char* p = reinterpret_cast<char*>(m_p);
|
|
m_p = nullptr;
|
|
return p;
|
|
}
|
|
|
|
StringData* d = reinterpret_cast<StringData*>(m_p);
|
|
g_assert(d->get() && *d->get());
|
|
|
|
char* p = d->ref_count() == 1 ? d->release() : g_strdup(d->get());
|
|
|
|
d->unref();
|
|
|
|
m_p = nullptr;
|
|
m_is_const = true;
|
|
m_is_inline = true;
|
|
return p;
|
|
}
|
|
|
|
gstr gstr::vprintf(const char* format, va_list args)
|
|
{
|
|
return gstr::wrap_new(g_strdup_vprintf(format, args));
|
|
}
|
|
|
|
|
|
size_t std::hash<g::gstr>::operator()(const g::gstr& s) const
|
|
{
|
|
return g_str_hash (s.is_null () ? "" : s.get ());
|
|
}
|
|
|
|
|
|
gstrvec g::convert(gstrv v)
|
|
{
|
|
char** pv = v.release();
|
|
|
|
gstrvec ret;
|
|
ret.reserve(g_strv_length(pv));
|
|
|
|
for (gsize i = 0, c = ret.size(); i < c; ++i)
|
|
ret.emplace_back(gstr::wrap_new(pv[i]));
|
|
|
|
::g_free(pv);
|
|
return ret;
|
|
}
|
|
|
|
|
|
gstrv gstrv::convert(gstrvec v)
|
|
{
|
|
const gsize c = v.size();
|
|
char **p = g_new (char*, c + 1);
|
|
for (gsize i = 0; i < c; ++i)
|
|
p[i] = v[i].release_owned();
|
|
p[c] = nullptr;
|
|
return p;
|
|
}
|
|
|
|
|
|
strbuilder::strbuilder(const char *init, gssize len)
|
|
: m_buf(nullptr)
|
|
, m_result(nullptr)
|
|
{
|
|
if (init != nullptr)
|
|
m_buf = g_string_new_len(init, len);
|
|
else if (len > 0)
|
|
m_buf = g_string_sized_new(len);
|
|
else
|
|
m_buf = g_string_new(nullptr);
|
|
}
|
|
|
|
strbuilder::strbuilder(gsize reserve)
|
|
: strbuilder(nullptr, reserve)
|
|
{
|
|
}
|
|
|
|
strbuilder::~strbuilder()
|
|
{
|
|
if (m_buf)
|
|
g_string_free (m_buf, true);
|
|
}
|
|
|
|
gstr strbuilder::release()
|
|
{
|
|
if (m_result)
|
|
{
|
|
return gstr::wrap_new(m_result.release());
|
|
}
|
|
else if (m_buf)
|
|
{
|
|
gstr s = gstr::wrap_new(g_string_free(m_buf, false));
|
|
m_buf = nullptr;
|
|
return s;
|
|
}
|
|
else
|
|
{
|
|
g_return_val_if_reached(gstr::null);
|
|
}
|
|
}
|
|
|
|
const char* strbuilder::get() const
|
|
{
|
|
if (m_buf)
|
|
{
|
|
m_result.set_new (g_string_free (m_buf, false));
|
|
m_buf = nullptr;
|
|
}
|
|
|
|
g_return_val_if_fail(m_result, nullptr);
|
|
return m_result;
|
|
}
|
|
|
|
void strbuilder::truncate(gsize len)
|
|
{
|
|
g_return_if_fail(m_buf);
|
|
g_string_truncate(m_buf, len);
|
|
}
|
|
|
|
void strbuilder::set_size(gsize len)
|
|
{
|
|
g_return_if_fail(m_buf);
|
|
g_string_set_size(m_buf, len);
|
|
}
|
|
|
|
void strbuilder::append(const char* val, gssize len)
|
|
{
|
|
g_return_if_fail(m_buf);
|
|
g_string_append_len(m_buf, val, len);
|
|
}
|
|
|
|
void strbuilder::append(const gstrp& val)
|
|
{
|
|
append(val.get());
|
|
}
|
|
|
|
void strbuilder::append(char c)
|
|
{
|
|
g_return_if_fail(m_buf);
|
|
g_string_append_c(m_buf, c);
|
|
}
|
|
|
|
void strbuilder::append(gunichar wc)
|
|
{
|
|
g_return_if_fail(m_buf);
|
|
g_string_append_unichar(m_buf, wc);
|
|
}
|
|
|
|
void strbuilder::prepend(const char* val, gssize len)
|
|
{
|
|
g_return_if_fail(m_buf);
|
|
g_string_prepend_len(m_buf, val, len);
|
|
}
|
|
|
|
void strbuilder::prepend(const gstrp& val)
|
|
{
|
|
prepend(val.get());
|
|
}
|
|
|
|
void strbuilder::prepend(char c)
|
|
{
|
|
g_return_if_fail(m_buf);
|
|
g_string_prepend_c(m_buf, c);
|
|
}
|
|
|
|
void strbuilder::prepend(gunichar wc)
|
|
{
|
|
g_return_if_fail(m_buf);
|
|
g_string_prepend_unichar(m_buf, wc);
|
|
}
|
|
|
|
void strbuilder::insert(gssize pos, const char* val, gssize len)
|
|
{
|
|
g_return_if_fail(m_buf);
|
|
g_string_insert_len(m_buf, pos, val, len);
|
|
}
|
|
|
|
void strbuilder::insert(gssize pos, const gstrp& val)
|
|
{
|
|
insert(pos, val.get());
|
|
}
|
|
|
|
void strbuilder::insert(gssize pos, char c)
|
|
{
|
|
g_return_if_fail(m_buf);
|
|
g_string_insert_c(m_buf, pos, c);
|
|
}
|
|
|
|
void strbuilder::insert(gssize pos, gunichar wc)
|
|
{
|
|
g_return_if_fail(m_buf);
|
|
g_string_insert_c(m_buf, pos, wc);
|
|
}
|
|
|
|
void strbuilder::overwrite(gsize pos, const char* val, gssize len)
|
|
{
|
|
g_return_if_fail(m_buf);
|
|
g_string_overwrite_len(m_buf, pos, val, len);
|
|
}
|
|
|
|
void strbuilder::erase(gssize pos, gssize len)
|
|
{
|
|
g_return_if_fail(m_buf);
|
|
g_string_erase(m_buf, pos, len);
|
|
}
|
|
|
|
void strbuilder::ascii_down()
|
|
{
|
|
g_return_if_fail(m_buf);
|
|
g_string_ascii_down(m_buf);
|
|
}
|
|
|
|
void strbuilder::ascii_up()
|
|
{
|
|
g_return_if_fail(m_buf);
|
|
g_string_ascii_up(m_buf);
|
|
}
|
|
|
|
void strbuilder::vprintf(const char* format, va_list args)
|
|
{
|
|
g_return_if_fail(m_buf);
|
|
g_string_vprintf(m_buf, format, args);
|
|
}
|
|
|
|
void strbuilder::append_vprintf(const char* format, va_list args)
|
|
{
|
|
g_return_if_fail(m_buf);
|
|
g_string_append_vprintf(m_buf, format, args);
|
|
}
|
|
|
|
void strbuilder::append_uri_escaped(const char* unescaped, const char* reserved_chars_allowed, bool allow_utf8)
|
|
{
|
|
g_return_if_fail(m_buf);
|
|
g_string_append_uri_escaped(m_buf, unescaped, reserved_chars_allowed, allow_utf8);
|
|
}
|