0
0
Fork 0
mirror of https://github.com/matrix-construct/construct synced 2024-11-02 11:58:53 +01:00
construct/include/ircd/lexical.h

538 lines
14 KiB
C
Raw Normal View History

/*
* charybdis: an advanced ircd.
* inline/stringops.h: inlined string operations used in a few places
*
* Copyright (C) 2005-2016 Charybdis Development Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA
*/
2016-08-13 05:05:54 +02:00
#pragma once
#define HAVE_IRCD_STRING_H
2016-08-13 05:05:54 +02:00
namespace ircd {
//
// Lexical conversions
//
IRCD_EXCEPTION(ircd::error, bad_lex_cast)
template<class T> bool try_lex_cast(const string_view &);
template<> bool try_lex_cast<long double>(const string_view &);
template<> bool try_lex_cast<double>(const string_view &);
template<> bool try_lex_cast<ulong>(const string_view &);
template<> bool try_lex_cast<long>(const string_view &);
template<> bool try_lex_cast<uint>(const string_view &);
template<> bool try_lex_cast<int>(const string_view &);
template<> bool try_lex_cast<ushort>(const string_view &);
template<> bool try_lex_cast<short>(const string_view &);
template<> bool try_lex_cast<uint8_t>(const string_view &);
template<> bool try_lex_cast<int8_t>(const string_view &);
template<> bool try_lex_cast<bool>(const string_view &);
template<class T> T lex_cast(const string_view &);
template<> long double lex_cast(const string_view &);
template<> double lex_cast(const string_view &);
template<> ulong lex_cast(const string_view &);
template<> long lex_cast(const string_view &);
template<> uint lex_cast(const string_view &);
template<> int lex_cast(const string_view &);
template<> ushort lex_cast(const string_view &);
template<> short lex_cast(const string_view &);
template<> uint8_t lex_cast(const string_view &);
template<> int8_t lex_cast(const string_view &);
template<> bool lex_cast(const string_view &);
// User supplied destination buffer
template<class T> string_view lex_cast(T, char *const &buf, const size_t &max);
template<> string_view lex_cast(long double, char *const &buf, const size_t &max);
template<> string_view lex_cast(double, char *const &buf, const size_t &max);
template<> string_view lex_cast(ulong, char *const &buf, const size_t &max);
template<> string_view lex_cast(long, char *const &buf, const size_t &max);
template<> string_view lex_cast(uint, char *const &buf, const size_t &max);
template<> string_view lex_cast(int, char *const &buf, const size_t &max);
template<> string_view lex_cast(ushort, char *const &buf, const size_t &max);
template<> string_view lex_cast(short, char *const &buf, const size_t &max);
template<> string_view lex_cast(uint8_t, char *const &buf, const size_t &max);
template<> string_view lex_cast(int8_t, char *const &buf, const size_t &max);
template<> string_view lex_cast(bool, char *const &buf, const size_t &max);
// Circular static thread_local buffer
const size_t LEX_CAST_BUFS {8};
template<class T> string_view lex_cast(const T &t);
2016-11-29 16:23:38 +01:00
//
// String tokenization.
2016-11-29 16:23:38 +01:00
//
// Use the closure for best performance. Note that string_view's
// are not required to be null terminated. Construct an std::string from the view to allocate
// and copy the token with null termination.
using token_view = std::function<void (const string_view &)>;
void tokens(const string_view &str, const char *const &sep, const token_view &);
size_t tokens(const string_view &str, const char *const &sep, const size_t &limit, const token_view &);
// Copies tokens into your buffer and null terminates strtok() style. Returns BYTES of buf consumed.
size_t tokens(const string_view &str, const char *const &sep, char *const &buf, const size_t &max, const token_view &);
// Receive token view into iterator range
template<class it> it tokens(const string_view &str, const char *const &sep, const it &b, const it &e);
// Receive token view into array
template<size_t N> size_t tokens(const string_view &str, const char *const &sep, string_view (&buf)[N]);
template<size_t N> size_t tokens(const string_view &str, const char *const &sep, std::array<string_view, N> &);
// Receive token view into new container (custom allocator)
template<template<class, class>
class C = std::vector,
class T = string_view,
class A>
C<T, A> tokens(A allocator, const string_view &str, const char *const &sep);
// Receive token view into new container
template<template<class, class>
class C = std::vector,
class T = string_view,
class A = std::allocator<T>>
C<T, A> tokens(const string_view &str, const char *const &sep);
// Receive token view into new associative container (custom allocator)
template<template<class, class, class>
class C,
class T = string_view,
class Comp = std::less<T>,
class A>
2017-03-11 04:31:20 +01:00
C<T, Comp, A> tokens(A allocator, const string_view &str, const char *const &sep);
2016-11-29 16:23:38 +01:00
// Receive token view into new associative container
template<template<class, class, class>
class C,
class T = string_view,
class Comp = std::less<T>,
class A = std::allocator<T>>
2017-03-11 04:31:20 +01:00
C<T, Comp, A> tokens(const string_view &str, const char *const &sep);
2016-11-29 16:23:38 +01:00
// Convenience to get individual tokens
size_t tokens_count(const string_view &str, const char *const &sep);
string_view token(const string_view &str, const char *const &sep, const size_t &at);
string_view token_last(const string_view &str, const char *const &sep);
string_view token_first(const string_view &str, const char *const &sep);
2017-03-18 04:25:14 +01:00
string_view tokens_after(const string_view &str, const char *const &sep, const size_t &at);
2016-11-29 16:23:38 +01:00
class params
{
string_view in;
const char *sep;
std::initializer_list<const char *> names;
const char *name(const size_t &i) const;
public:
IRCD_EXCEPTION(ircd::error, error)
IRCD_EXCEPTION(error, missing)
IRCD_EXCEPTION(error, invalid)
string_view operator[](const size_t &i) const; // returns empty
template<class T> T at(const size_t &i, const T &def) const; // throws invalid
template<class T> T at(const size_t &i) const; // throws missing or invalid
string_view at(const size_t &i) const; // throws missing
params(const string_view &in,
const char *const &sep,
const std::initializer_list<const char *> &names = {});
};
2016-11-29 16:23:38 +01:00
//
// Misc utils
//
// Simple case insensitive comparison convenience utils
bool iequals(const string_view &a, const string_view &b);
bool iless(const string_view &a, const string_view &b);
// Vintage
size_t strlcpy(char *const &dest, const char *const &src, const size_t &bufmax);
size_t strlcat(char *const &dest, const char *const &src, const size_t &bufmax);
// Legacy
char *strip_colour(char *string);
char *strip_unprintable(char *string);
char *reconstruct_parv(int parc, const char **parv);
2016-11-29 16:23:38 +01:00
string_view chomp(const string_view &str, const string_view &c = " "s);
std::pair<string_view, string_view> split(const string_view &str, const string_view &delim = " "s);
std::pair<string_view, string_view> rsplit(const string_view &str, const string_view &delim = " "s);
string_view between(const string_view &str, const string_view &a = "("s, const string_view &b = ")"s);
bool endswith(const string_view &str, const string_view &val);
bool endswith(const string_view &str, const char &val);
template<class It> bool endswith_any(const string_view &str, const It &begin, const It &end);
bool startswith(const string_view &str, const string_view &val);
bool startswith(const string_view &str, const char &val);
string_view unquote(string_view str);
2016-11-29 16:23:38 +01:00
} // namespace ircd
inline ircd::string_view
ircd::unquote(string_view str)
{
if(startswith(str, '"'))
str = { str.data() + 1, str.data() + str.size() };
if(endswith(str, '"'))
str = { str.data(), str.data() + str.size() - 1 };
return str;
}
inline bool
2016-11-29 16:23:38 +01:00
ircd::startswith(const string_view &str,
const char &val)
{
return !str.empty() && str[0] == val;
}
inline bool
2016-11-29 16:23:38 +01:00
ircd::startswith(const string_view &str,
const string_view &val)
{
const auto pos(str.find(val, 0));
return pos == 0;
}
template<class It>
bool
2016-11-29 16:23:38 +01:00
ircd::endswith_any(const string_view &str,
const It &begin,
const It &end)
{
return std::any_of(begin, end, [&str](const auto &val)
{
return endswith(str, val);
});
}
inline bool
2016-11-29 16:23:38 +01:00
ircd::endswith(const string_view &str,
const char &val)
{
return !str.empty() && str[str.size()-1] == val;
}
inline bool
2016-11-29 16:23:38 +01:00
ircd::endswith(const string_view &str,
const string_view &val)
{
const auto vlen(std::min(str.size(), val.size()));
const auto pos(str.find(val, vlen));
return pos == str.size() - vlen;
}
2016-11-29 16:23:38 +01:00
inline ircd::string_view
ircd::between(const string_view &str,
const string_view &a,
const string_view &b)
{
return split(split(str, a).second, b).first;
}
2016-11-29 16:23:38 +01:00
inline std::pair<ircd::string_view, ircd::string_view>
ircd::rsplit(const string_view &str,
const string_view &delim)
2016-10-26 05:16:28 +02:00
{
const auto pos(str.find_last_of(delim));
if(pos == string_view::npos) return
{
string_view{},
str
};
else return
{
str.substr(0, pos),
str.substr(pos + delim.size())
};
2016-10-26 05:16:28 +02:00
}
2016-11-29 16:23:38 +01:00
inline std::pair<ircd::string_view, ircd::string_view>
ircd::split(const string_view &str,
const string_view &delim)
{
const auto pos(str.find(delim));
if(pos == string_view::npos) return
{
str,
string_view{}
};
else return
{
str.substr(0, pos),
str.substr(pos + delim.size())
};
}
2016-11-29 16:23:38 +01:00
inline ircd::string_view
ircd::chomp(const string_view &str,
const string_view &c)
{
const auto pos(str.find_last_not_of(c));
return pos != string_view::npos? string_view{str.substr(0, pos + 1)} : str;
2016-11-29 16:23:38 +01:00
}
inline
ircd::params::params(const string_view &in,
const char *const &sep,
const std::initializer_list<const char *> &names)
:in{in}
,sep{sep}
,names{names}
{
}
template<class T>
T
ircd::params::at(const size_t &i,
const T &def)
const try
{
return tokens_count(in, sep) > i? at<T>(i) : def;
}
catch(const bad_lex_cast &e)
{
throw invalid("parameter #%zu <%s>", i, name(i));
}
template<class T>
T
ircd::params::at(const size_t &i)
const try
{
return lex_cast<T>(at(i));
}
catch(const bad_lex_cast &e)
{
throw invalid("parameter #%zu <%s>", i, name(i));
}
inline ircd::string_view
ircd::params::at(const size_t &i)
const try
{
return token(in, sep, i);
}
catch(const std::out_of_range &e)
{
throw missing("required parameter #%zu <%s>", i, name(i));
}
inline ircd::string_view
ircd::params::operator[](const size_t &i)
const
{
return tokens_count(in, sep) > i? token(in, sep, i) : string_view{};
}
inline const char *
ircd::params::name(const size_t &i)
const
{
return names.size() > i? *std::next(begin(names), i) : "<unnamed>";
}
2016-11-29 16:23:38 +01:00
template<size_t N>
size_t
ircd::tokens(const string_view &str,
const char *const &sep,
string_view (&buf)[N])
{
const auto e(tokens(str, sep, begin(buf), end(buf)));
return std::distance(begin(buf), e);
}
template<size_t N>
size_t
ircd::tokens(const string_view &str,
const char *const &sep,
std::array<string_view, N> &buf)
{
const auto e(tokens(str, sep, begin(buf), end(buf)));
return std::distance(begin(buf), e);
}
template<class it>
it
ircd::tokens(const string_view &str,
const char *const &sep,
const it &b,
const it &e)
{
it pos(b);
tokens(str, sep, std::distance(b, e), [&pos]
(const string_view &token)
{
*pos = token;
++pos;
});
return pos;
}
template<template<class, class, class>
class C,
class T,
class Comp,
class A>
C<T, Comp, A>
ircd::tokens(const string_view &str,
const char *const &sep)
{
A allocator;
return tokens<C, T, Comp, A>(allocator, str, sep);
}
template<template<class, class, class>
class C,
class T,
class Comp,
class A>
C<T, Comp, A>
ircd::tokens(A allocator,
const string_view &str,
const char *const &sep)
{
C<T, Comp, A> ret(allocator);
tokens(str, sep, [&ret]
(const string_view &token)
{
ret.emplace(ret.end(), token);
});
return ret;
}
template<template<class, class>
class C,
class T,
class A>
C<T, A>
ircd::tokens(const string_view &str,
const char *const &sep)
{
A allocator;
return tokens<C, T, A>(allocator, str, sep);
}
2016-11-29 16:23:38 +01:00
template<template<class, class>
class C,
class T,
class A>
C<T, A>
ircd::tokens(A allocator,
const string_view &str,
const char *const &sep)
{
C<T, A> ret(allocator);
tokens(str, sep, [&ret]
(const string_view &token)
{
ret.emplace(ret.end(), token);
});
return ret;
}
inline size_t
#ifndef HAVE_STRLCPY
ircd::strlcpy(char *const &dest,
const char *const &src,
const size_t &max)
{
if(!max)
return 0;
const size_t ret(strnlen(src, max));
const size_t len(ret >= max? max - 1 : ret);
memcpy(dest, src, len);
dest[len] = '\0';
return ret;
}
#else
ircd::strlcpy(char *const &dest,
const char *const &src,
const size_t &max)
{
return ::strlcpy(dest, src, max);
}
#endif
inline size_t
#ifndef HAVE_STRLCAT
ircd::strlcat(char *const &dest,
const char *const &src,
const size_t &max)
{
if(!max)
return 0;
const ssize_t dsize(strnlen(dest, max));
const ssize_t ssize(strnlen(src, max));
2016-11-29 16:23:38 +01:00
const ssize_t ret(dsize + ssize);
const ssize_t remain(max - dsize);
const ssize_t cpsz(ssize >= remain? remain - 1 : ssize);
char *const ptr(dest + dsize);
memcpy(ptr, src, cpsz);
ptr[cpsz] = '\0';
return ret;
}
#else
ircd::strlcat(char *const &dest,
const char *const &src,
const size_t &max)
{
return ::strlcat(dest, src, max);
}
#endif
inline bool
ircd::iless(const string_view &a,
const string_view &b)
{
return std::lexicographical_compare(begin(a), end(a), begin(b), end(b), []
(const char &a, const char &b)
{
return tolower(a) < tolower(b);
});
}
inline bool
ircd::iequals(const string_view &a,
const string_view &b)
{
return std::equal(begin(a), end(a), begin(b), end(b), []
(const char &a, const char &b)
{
return tolower(a) == tolower(b);
});
}
template<class T>
ircd::string_view
ircd::lex_cast(const T &t)
{
return lex_cast<T>(t, nullptr, 0);
}