0
0
Fork 0
mirror of https://github.com/matrix-construct/construct synced 2024-11-25 16:22:35 +01:00

ircd: Split up lexical.h; comments; cleanup.

This commit is contained in:
Jason Volk 2017-10-01 21:14:34 -07:00
parent 01d7ec1560
commit 9c712486a1
5 changed files with 710 additions and 519 deletions

265
include/ircd/lex_cast.h Normal file
View file

@ -0,0 +1,265 @@
/*
* 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
*/
#pragma once
#define HAVE_IRCD_LEX_CAST_H
//
// Lexical conversions
//
namespace ircd
{
IRCD_EXCEPTION_HIDENAME(ircd::error, bad_lex_cast)
template<class T> bool try_lex_cast(const string_view &);
template<class T> T lex_cast(std::string &);
template<class T> T lex_cast(const std::string &);
template<class T> T lex_cast(const std::string_view &);
template<class T> T lex_cast(const string_view &);
// User supplied destination buffer
template<class T> string_view lex_cast(T, char *const &buf, const size_t &max);
// Circular static thread_local buffer
const size_t LEX_CAST_BUFS {256}; // plenty
template<class T> string_view lex_cast(const T &t);
// Binary <-> Hex conversion suite
string_view u2a(const mutable_buffer &out, const const_raw_buffer &in);
const_raw_buffer a2u(const mutable_raw_buffer &out, const const_buffer &in);
// Binary <-> Base64 conversion suite
string_view b64encode(const mutable_buffer &out, const const_raw_buffer &in);
}
namespace ircd
{
template<> bool try_lex_cast<std::string>(const string_view &); // stub always true
template<> bool try_lex_cast<std::string_view>(const string_view &); // stub always true
template<> bool try_lex_cast<string_view>(const string_view &); // stub always true
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<> std::string &lex_cast(std::string &); // trivial
template<> std::string lex_cast(const std::string &); // trivial
template<> std::string_view lex_cast(const std::string_view &); // trivial
template<> std::string lex_cast(const string_view &); // trivial
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 &);
template<> string_view lex_cast(const std::string &, char *const &buf, const size_t &max);
template<> string_view lex_cast(const std::string_view &, char *const &buf, const size_t &max);
template<> string_view lex_cast(const string_view &, 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);
}
/// Convert a native number to a string. The returned value is a view of the
/// string in a static ring buffer. There are LEX_CAST_BUFS number of buffers
/// so you should not hold on to the returned view for very long.
template<class T>
ircd::string_view
ircd::lex_cast(const T &t)
{
return lex_cast<T>(t, nullptr, 0);
}
/// Conversion to an std::string creates a copy when the input is a
/// string_view. Note this is not considered an "unnecessary lexical cast"
/// even though nothing is being converted, so there will be no warning.
template<>
inline std::string
ircd::lex_cast<std::string>(const string_view &s)
{
return std::string{s};
}
/// Template basis for a string_view input
template<class T>
T
ircd::lex_cast(const string_view &s)
{
return s;
}
/// Specialization of a string_view to string_view conversion which is just
/// a trivial copy of the view.
template<>
inline std::string_view
ircd::lex_cast<std::string_view>(const std::string_view &s)
{
return s;
}
/// Specialization of a string to string conversion which generates a warning
/// because the conversion has to copy the string while no numerical conversion
/// has taken place. The developer should remove the offending lex_cast.
template<>
__attribute__((warning("unnecessary lexical cast")))
inline std::string
ircd::lex_cast<std::string>(const std::string &s)
{
return s;
}
/// Template basis for a const std::string input
template<class T>
T
ircd::lex_cast(const std::string &s)
{
return lex_cast<T>(string_view{s});
}
/// Template basis for an lvalue string. If we can get this binding rather
/// than the const std::string alternative some trivial conversions are
/// easier to make in the specializations.
template<class T>
T
ircd::lex_cast(std::string &s)
{
return lex_cast<T>(string_view{s});
}
/// Specialization of a string to string conversion without a warning because
/// we can trivially pass through a reference from input to output.
template<>
inline std::string &
ircd::lex_cast(std::string &s)
{
return s;
}
/// Specialization of a string to string conversion to user's buffer;
/// marked as unnecessary because no numerical conversion takes place yet
/// data is still copied. (note: warning may be removed; may be intentional)
template<>
__attribute__((warning("unnecessary lexical cast")))
inline ircd::string_view
ircd::lex_cast(const string_view &s,
char *const &buf,
const size_t &max)
{
s.copy(buf, max);
return { buf, max };
}
/// Specialization of a string to string conversion to user's buffer;
/// marked as unnecessary because no numerical conversion takes place yet
/// data is still copied. (note: warning may be removed; may be intentional)
template<>
__attribute__((warning("unnecessary lexical cast")))
inline ircd::string_view
ircd::lex_cast(const std::string_view &s,
char *const &buf,
const size_t &max)
{
s.copy(buf, max);
return { buf, max };
}
/// Specialization of a string to string conversion to user's buffer;
/// marked as unnecessary because no numerical conversion takes place yet
/// data is still copied. (note: warning may be removed; may be intentional)
template<>
__attribute__((warning("unnecessary lexical cast")))
inline ircd::string_view
ircd::lex_cast(const std::string &s,
char *const &buf,
const size_t &max)
{
s.copy(buf, max);
return { buf, max };
}
/// Template basis; if no specialization is matched there is no fallback here
template<class T>
__attribute__((error("unsupported lexical cast")))
ircd::string_view
ircd::lex_cast(T t,
char *const &buf,
const size_t &max)
{
assert(0);
return {};
}
/// Template basis; if no specialization is matched there is no fallback here
template<class T>
__attribute__((error("unsupported lexical cast")))
bool
ircd::try_lex_cast(const string_view &s)
{
assert(0);
return false;
}
/// Trivial conversion; always returns true
template<>
inline bool
ircd::try_lex_cast<ircd::string_view>(const string_view &)
{
return true;
}
/// Trivial conversion; always returns true
template<>
inline bool
ircd::try_lex_cast<std::string_view>(const string_view &)
{
return true;
}
/// Trivial conversion; always returns true
template<>
inline bool
ircd::try_lex_cast<std::string>(const string_view &s)
{
return true;
}

View file

@ -192,7 +192,9 @@ namespace ircd
#include "localee.h" #include "localee.h"
#include "life_guard.h" #include "life_guard.h"
#include "color.h" #include "color.h"
#include "lexical.h" #include "lex_cast.h"
#include "stringops.h"
#include "tokens.h"
#include "params.h" #include "params.h"
#include "iov.h" #include "iov.h"
#include "parse.h" #include "parse.h"

View file

@ -2,7 +2,7 @@
* charybdis: an advanced ircd. * charybdis: an advanced ircd.
* inline/stringops.h: inlined string operations used in a few places * inline/stringops.h: inlined string operations used in a few places
* *
* Copyright (C) 2005-2016 Charybdis Development Team * Copyright (C) 2005-2017 Charybdis Development Team
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@ -21,154 +21,13 @@
*/ */
#pragma once #pragma once
#define HAVE_IRCD_LEXICAL_H #define HAVE_IRCD_STRING_H
// //
// Lexical conversions // Misc string utilities
// //
namespace ircd namespace ircd
{ {
IRCD_EXCEPTION_HIDENAME(ircd::error, bad_lex_cast)
template<class T> bool try_lex_cast(const string_view &);
template<> bool try_lex_cast<std::string>(const string_view &); // stub always true
template<> bool try_lex_cast<std::string_view>(const string_view &); // stub always true
template<> bool try_lex_cast<string_view>(const string_view &); // stub always true
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(std::string &);
template<class T> T lex_cast(const std::string &);
template<class T> T lex_cast(const std::string_view &);
template<class T> T lex_cast(const string_view &);
template<> std::string &lex_cast(std::string &); // trivial
template<> std::string lex_cast(const std::string &); // trivial
template<> std::string_view lex_cast(const std::string_view &); // trivial
template<> std::string lex_cast(const string_view &); // trivial
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(const std::string &, char *const &buf, const size_t &max);
template<> string_view lex_cast(const std::string_view &, char *const &buf, const size_t &max);
template<> string_view lex_cast(const string_view &, 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 {256}; // plenty
template<class T> string_view lex_cast(const T &t);
//
// Binary / Hex / Base64 conversion suite
//
string_view u2a(const mutable_buffer &out, const const_raw_buffer &in);
const_raw_buffer a2u(const mutable_raw_buffer &out, const const_buffer &in);
string_view b64encode(const mutable_buffer &out, const const_raw_buffer &in);
//
// String tokenization.
//
// 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 &sep, const token_view &);
void tokens(const string_view &str, const char *const &sep, const token_view &);
size_t tokens(const string_view &str, const char &sep, const size_t &limit, 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 &sep, char *const &buf, const size_t &max, const token_view &);
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, class sep> it tokens(const string_view &str, const sep &, const it &b, const it &e);
// Receive token view into array
template<size_t N, class sep> size_t tokens(const string_view &str, const sep &, string_view (&buf)[N]);
template<size_t N, class sep> size_t tokens(const string_view &str, 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,
class sep>
C<T, A> tokens(A&& allocator, const string_view &str, 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>,
class sep>
C<T, A> tokens(const string_view &str, 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,
class sep>
C<T, Comp, A> tokens(A&& allocator, const string_view &str, const sep &);
// 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>,
class sep>
C<T, Comp, A> tokens(const string_view &str, const sep &);
// Convenience to get individual tokens
size_t tokens_count(const string_view &str, const char &sep);
size_t tokens_count(const string_view &str, const char *const &sep);
string_view token(const string_view &str, const char &sep, const size_t &at);
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 &sep);
string_view token_last(const string_view &str, const char *const &sep);
string_view token_first(const string_view &str, const char &sep);
string_view token_first(const string_view &str, const char *const &sep);
string_view tokens_after(const string_view &str, const char &sep, const size_t &at);
string_view tokens_after(const string_view &str, const char *const &sep, const size_t &at);
//
// Misc utils
//
// Simple case insensitive comparison convenience utils // Simple case insensitive comparison convenience utils
struct iless; struct iless;
struct igreater; struct igreater;
@ -180,39 +39,66 @@ namespace ircd
size_t strlcpy(char *const &dest, const string_view &src, const size_t &bufmax); size_t strlcpy(char *const &dest, const string_view &src, const size_t &bufmax);
size_t strlcat(char *const &dest, const string_view &src, const size_t &bufmax); size_t strlcat(char *const &dest, const string_view &src, const size_t &bufmax);
// return view without trailing c
string_view rstrip(const string_view &str, const char &c = ' ');
string_view rstrip(const string_view &str, const string_view &c);
// return view without leading c
string_view lstrip(const string_view &str, const char &c = ' ');
string_view lstrip(const string_view &str, const string_view &c);
// return view without leading and trailing c
string_view strip(const string_view &str, const char &c = ' ');
string_view strip(const string_view &str, const string_view &c);
// split view on first match of delim; delim not included; no delim .second empty
std::pair<string_view, string_view> split(const string_view &str, const char &delim = ' ');
std::pair<string_view, string_view> split(const string_view &str, const string_view &delim);
// split view on last match of delim; delim not included; no delim .second empty
std::pair<string_view, string_view> rsplit(const string_view &str, const char &delim = ' ');
std::pair<string_view, string_view> rsplit(const string_view &str, const string_view &delim);
// view between first match of delim a and first match of delim b after it
string_view between(const string_view &str, const string_view &a, const string_view &b);
string_view between(const string_view &str, const char &a = '(', const char &b = ')');
// test string endswith delim; or any of the delims in iterable
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);
// test string startswith delim; or any of the delims in iterable
bool startswith(const string_view &str, const string_view &val);
bool startswith(const string_view &str, const char &val);
template<class It> bool startswith_any(const string_view &str, const It &begin, const It &end);
// test string is surrounded by val (ex. surrounded by quote characters)
bool surrounds(const string_view &str, const string_view &val);
bool surrounds(const string_view &str, const char &val);
// pop trailing char from view
char chop(string_view &str);
// remove trailing from view and return num chars removed
size_t chomp(string_view &str, const char &c = '\n');
size_t chomp(string_view &str, const string_view &c);
template<class T, class delim> size_t chomp(iterators<T>, const delim &d);
// Convenience to strip quotes
string_view unquote(const string_view &str);
std::string unquote(std::string &&);
// Legacy // Legacy
char *strip_colour(char *string); char *strip_colour(char *string);
char *strip_unprintable(char *string); char *strip_unprintable(char *string);
char *reconstruct_parv(int parc, const char **parv); char *reconstruct_parv(int parc, const char **parv);
char chop(string_view &str);
size_t chomp(string_view &str, const char &c = '\n');
size_t chomp(string_view &str, const string_view &c);
template<class T, class delim> size_t chomp(iterators<T>, const delim &d);
string_view rstrip(const string_view &str, const char &c = ' ');
string_view rstrip(const string_view &str, const string_view &c);
string_view lstrip(const string_view &str, const char &c = ' ');
string_view lstrip(const string_view &str, const string_view &c);
string_view strip(const string_view &str, const char &c = ' ');
string_view strip(const string_view &str, const string_view &c);
std::pair<string_view, string_view> split(const string_view &str, const char &delim = ' ');
std::pair<string_view, string_view> split(const string_view &str, const string_view &delim);
std::pair<string_view, string_view> rsplit(const string_view &str, const char &delim = ' ');
std::pair<string_view, string_view> rsplit(const string_view &str, const string_view &delim);
string_view between(const string_view &str, const string_view &a, const string_view &b);
string_view between(const string_view &str, const char &a = '(', const char &b = ')');
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);
template<class It> bool startswith_any(const string_view &str, const It &begin, const It &end);
bool surrounds(const string_view &str, const string_view &val);
bool surrounds(const string_view &str, const char &val);
string_view unquote(string_view str);
std::string unquote(std::string &&);
} }
/// Remove quotes on an std::string. Only operates on an rvalue reference so
/// that a copy of the string is not created when no quotes are found, and
/// movements can take place if they are. This overload is not needed often;
/// use string_view.
inline std::string inline std::string
ircd::unquote(std::string &&str) ircd::unquote(std::string &&str)
{ {
@ -225,18 +111,71 @@ ircd::unquote(std::string &&str)
return std::move(str); return std::move(str);
} }
/// Common convenience to remove quotes around the view of the string
inline ircd::string_view inline ircd::string_view
ircd::unquote(string_view str) ircd::unquote(const string_view &str)
{ {
if(startswith(str, '"')) return strip(str, '"');
str = { str.data() + 1, str.data() + str.size() };
if(endswith(str, '"'))
str = { str.data(), str.data() + str.size() - 1 };
return str;
} }
/// Chomps delim from all of the string views in the iterable (iterators<T> are
/// the T::iterator pair {begin(t), end(t)} of an iterable T) and returns the
/// total number of characters removed from all operations.
template<class T,
class delim>
size_t
ircd::chomp(iterators<T> its,
const delim &d)
{
return std::accumulate(begin(its), end(its), size_t(0), [&d]
(auto ret, const auto &s)
{
return ret += chomp(s, d);
});
}
/// Removes all characters from the end of the view starting with the last
/// instance of c. Different from rstrip() in that this will remove more than
/// just the delim from the end; it removes both the delim and everything after
/// it from wherever the last delim may be. Removes nothing if no delim is.
inline size_t
ircd::chomp(string_view &str,
const char &c)
{
const auto pos(str.find_last_of(c));
if(pos == string_view::npos)
return 0;
assert(str.size() - pos == 1);
str = str.substr(0, pos);
return 1;
}
/// Removes all characters from the end of the view starting with the last
/// instance of c. This matches the entire delim string c to chomp it and
/// everything after it.
inline size_t
ircd::chomp(string_view &str,
const string_view &c)
{
const auto pos(str.find_last_of(c));
if(pos == string_view::npos)
return 0;
assert(str.size() - pos == c.size());
str = str.substr(0, pos);
return c.size();
}
/// Removes any last character from the view, modifying the view, and returning
/// that character.
inline char
ircd::chop(string_view &str)
{
return !str.empty()? str.pop_back() : '\0';
}
/// Test if a string starts and ends with character
inline bool inline bool
ircd::surrounds(const string_view &str, ircd::surrounds(const string_view &str,
const char &val) const char &val)
@ -244,6 +183,7 @@ ircd::surrounds(const string_view &str,
return str.size() >= 2 && str.front() == val && str.back() == val; return str.size() >= 2 && str.front() == val && str.back() == val;
} }
/// Test if a string starts and ends with a string
inline bool inline bool
ircd::surrounds(const string_view &str, ircd::surrounds(const string_view &str,
const string_view &val) const string_view &val)
@ -251,6 +191,7 @@ ircd::surrounds(const string_view &str,
return startswith(str, val) && endswith(str, val); return startswith(str, val) && endswith(str, val);
} }
/// Test if a string starts with any of the values in the iterable
template<class It> template<class It>
bool bool
ircd::startswith_any(const string_view &str, ircd::startswith_any(const string_view &str,
@ -263,6 +204,7 @@ ircd::startswith_any(const string_view &str,
}); });
} }
/// Test if a string starts with a character
inline bool inline bool
ircd::startswith(const string_view &str, ircd::startswith(const string_view &str,
const char &val) const char &val)
@ -270,6 +212,7 @@ ircd::startswith(const string_view &str,
return !str.empty() && str[0] == val; return !str.empty() && str[0] == val;
} }
/// Test if a string starts with a string
inline bool inline bool
ircd::startswith(const string_view &str, ircd::startswith(const string_view &str,
const string_view &val) const string_view &val)
@ -278,6 +221,7 @@ ircd::startswith(const string_view &str,
return pos == 0; return pos == 0;
} }
/// Test if a string ends with any of the values in iterable
template<class It> template<class It>
bool bool
ircd::endswith_any(const string_view &str, ircd::endswith_any(const string_view &str,
@ -290,6 +234,7 @@ ircd::endswith_any(const string_view &str,
}); });
} }
/// Test if a string ends with character
inline bool inline bool
ircd::endswith(const string_view &str, ircd::endswith(const string_view &str,
const char &val) const char &val)
@ -297,6 +242,7 @@ ircd::endswith(const string_view &str,
return !str.empty() && str[str.size()-1] == val; return !str.empty() && str[str.size()-1] == val;
} }
/// Test if a string ends with a string
inline bool inline bool
ircd::endswith(const string_view &str, ircd::endswith(const string_view &str,
const string_view &val) const string_view &val)
@ -306,6 +252,8 @@ ircd::endswith(const string_view &str,
return pos == str.size() - vlen; return pos == str.size() - vlen;
} }
/// View a string between the first match of a and the first match of b
/// after a.
inline ircd::string_view inline ircd::string_view
ircd::between(const string_view &str, ircd::between(const string_view &str,
const string_view &a, const string_view &a,
@ -314,6 +262,8 @@ ircd::between(const string_view &str,
return split(split(str, a).second, b).first; return split(split(str, a).second, b).first;
} }
/// View a string between the first match of a and the first match of b
/// after a.
inline ircd::string_view inline ircd::string_view
ircd::between(const string_view &str, ircd::between(const string_view &str,
const char &a, const char &a,
@ -322,6 +272,8 @@ ircd::between(const string_view &str,
return split(split(str, a).second, b).first; return split(split(str, a).second, b).first;
} }
/// Split a string on the last match of delim. Delim not included; no match
/// will return original str in pair.first, pair.second empty.
inline std::pair<ircd::string_view, ircd::string_view> inline std::pair<ircd::string_view, ircd::string_view>
ircd::rsplit(const string_view &str, ircd::rsplit(const string_view &str,
const string_view &delim) const string_view &delim)
@ -339,6 +291,8 @@ ircd::rsplit(const string_view &str,
}; };
} }
/// Split a string on the last match of delim. Delim not included; no match
/// will return original str in pair.first, pair.second empty.
inline std::pair<ircd::string_view, ircd::string_view> inline std::pair<ircd::string_view, ircd::string_view>
ircd::rsplit(const string_view &str, ircd::rsplit(const string_view &str,
const char &delim) const char &delim)
@ -356,6 +310,8 @@ ircd::rsplit(const string_view &str,
}; };
} }
/// Split a string on the first match of delim. Delim not included; no match
/// will return original str in pair.first, pair.second empty.
inline std::pair<ircd::string_view, ircd::string_view> inline std::pair<ircd::string_view, ircd::string_view>
ircd::split(const string_view &str, ircd::split(const string_view &str,
const string_view &delim) const string_view &delim)
@ -373,6 +329,8 @@ ircd::split(const string_view &str,
}; };
} }
/// Split a string on the first match of delim. Delim not included; no match
/// will return original str in pair.first, pair.second empty.
inline std::pair<ircd::string_view, ircd::string_view> inline std::pair<ircd::string_view, ircd::string_view>
ircd::split(const string_view &str, ircd::split(const string_view &str,
const char &delim) const char &delim)
@ -390,6 +348,7 @@ ircd::split(const string_view &str,
}; };
} }
/// Remove leading and trailing instances of c from the returned view
inline ircd::string_view inline ircd::string_view
ircd::strip(const string_view &str, ircd::strip(const string_view &str,
const string_view &c) const string_view &c)
@ -397,6 +356,7 @@ ircd::strip(const string_view &str,
return lstrip(rstrip(str, c), c); return lstrip(rstrip(str, c), c);
} }
/// Remove leading and trailing instances of c from the returned view
inline ircd::string_view inline ircd::string_view
ircd::strip(const string_view &str, ircd::strip(const string_view &str,
const char &c) const char &c)
@ -404,6 +364,7 @@ ircd::strip(const string_view &str,
return lstrip(rstrip(str, c), c); return lstrip(rstrip(str, c), c);
} }
/// Remove trailing instances of c from the returned view
inline ircd::string_view inline ircd::string_view
ircd::rstrip(const string_view &str, ircd::rstrip(const string_view &str,
const string_view &c) const string_view &c)
@ -412,6 +373,7 @@ ircd::rstrip(const string_view &str,
return pos != string_view::npos? string_view{str.substr(0, pos + 1)} : str; return pos != string_view::npos? string_view{str.substr(0, pos + 1)} : str;
} }
/// Remove trailing instances of c from the returned view
inline ircd::string_view inline ircd::string_view
ircd::rstrip(const string_view &str, ircd::rstrip(const string_view &str,
const char &c) const char &c)
@ -420,6 +382,7 @@ ircd::rstrip(const string_view &str,
return pos != string_view::npos? string_view{str.substr(0, pos + 1)} : str; return pos != string_view::npos? string_view{str.substr(0, pos + 1)} : str;
} }
/// Remove leading instances of c from the returned view
inline ircd::string_view inline ircd::string_view
ircd::lstrip(const string_view &str, ircd::lstrip(const string_view &str,
const char &c) const char &c)
@ -428,6 +391,7 @@ ircd::lstrip(const string_view &str,
return pos != string_view::npos? string_view{str.substr(pos)} : string_view{}; return pos != string_view::npos? string_view{str.substr(pos)} : string_view{};
} }
/// Remove leading instances of c from the returned view
inline ircd::string_view inline ircd::string_view
ircd::lstrip(const string_view &str, ircd::lstrip(const string_view &str,
const string_view &c) const string_view &c)
@ -436,160 +400,7 @@ ircd::lstrip(const string_view &str,
return pos != string_view::npos? string_view{str.substr(pos)} : string_view{}; return pos != string_view::npos? string_view{str.substr(pos)} : string_view{};
} }
template<class T, /// Copy a string to dst will guaranteed null terminated output
class delim>
size_t
ircd::chomp(iterators<T> its,
const delim &d)
{
return std::accumulate(begin(its), end(its), size_t(0), [&d]
(auto ret, const auto &s)
{
return ret += chomp(s, d);
});
}
inline size_t
ircd::chomp(string_view &str,
const char &c)
{
const auto pos(str.find_last_of(c));
if(pos == string_view::npos)
return 0;
assert(str.size() - pos == 1);
str = str.substr(0, pos);
return 1;
}
inline size_t
ircd::chomp(string_view &str,
const string_view &c)
{
const auto pos(str.find_last_of(c));
if(pos == string_view::npos)
return 0;
assert(str.size() - pos == c.size());
str = str.substr(0, pos);
return c.size();
}
inline char
ircd::chop(string_view &str)
{
return !str.empty()? str.pop_back() : '\0';
}
template<size_t N,
class delim>
size_t
ircd::tokens(const string_view &str,
const delim &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,
class delim>
size_t
ircd::tokens(const string_view &str,
const delim &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,
class delim>
it
ircd::tokens(const string_view &str,
const delim &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,
class delim>
C<T, Comp, A>
ircd::tokens(const string_view &str,
const delim &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,
class delim>
C<T, Comp, A>
ircd::tokens(A&& allocator,
const string_view &str,
const delim &sep)
{
C<T, Comp, A> ret(std::forward<A>(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,
class delim>
C<T, A>
ircd::tokens(const string_view &str,
const delim &sep)
{
A allocator;
return tokens<C, T, A>(allocator, str, sep);
}
template<template<class, class>
class C,
class T,
class A,
class delim>
C<T, A>
ircd::tokens(A&& allocator,
const string_view &str,
const delim &sep)
{
C<T, A> ret(std::forward<A>(allocator));
tokens(str, sep, [&ret]
(const string_view &token)
{
ret.emplace(ret.end(), token);
});
return ret;
}
inline size_t inline size_t
ircd::strlcpy(char *const &dst, ircd::strlcpy(char *const &dst,
const string_view &src, const string_view &src,
@ -626,6 +437,8 @@ ircd::strlcpy(char *const &dst,
} }
#endif #endif
/// Append a string to dst will guaranteed null terminated output; Expects
/// dst to have null termination before calling this function.
inline size_t inline size_t
ircd::strlcat(char *const &dst, ircd::strlcat(char *const &dst,
const string_view &src, const string_view &src,
@ -655,6 +468,8 @@ ircd::strlcat(char *const &dst,
} }
#endif #endif
/// Case insensitive string comparison deciding which string compares 'less'
/// than the other.
struct ircd::iless struct ircd::iless
{ {
using is_transparent = std::true_type; using is_transparent = std::true_type;
@ -713,6 +528,7 @@ const
}); });
} }
/// Case insensitive string comparison deciding if two strings are equal
struct ircd::iequals struct ircd::iequals
{ {
using is_transparent = std::true_type; using is_transparent = std::true_type;
@ -771,6 +587,8 @@ const
}); });
} }
/// Case insensitive string comparison deciding which string compares 'greater'
/// than the other.
struct ircd::igreater struct ircd::igreater
{ {
using is_transparent = std::true_type; using is_transparent = std::true_type;
@ -828,131 +646,3 @@ const
return tolower(a) > tolower(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);
}
template<>
inline std::string
ircd::lex_cast<std::string>(const string_view &s)
{
return std::string{s};
}
template<class T>
T
ircd::lex_cast(const string_view &s)
{
return s;
}
template<>
inline std::string_view
ircd::lex_cast<std::string_view>(const std::string_view &s)
{
return s;
}
template<>
__attribute__((warning("unnecessary lexical cast")))
inline std::string
ircd::lex_cast<std::string>(const std::string &s)
{
return s;
}
template<class T>
T
ircd::lex_cast(const std::string &s)
{
return lex_cast<T>(string_view{s});
}
template<>
inline std::string &
ircd::lex_cast(std::string &s)
{
return s;
}
template<class T>
T
ircd::lex_cast(std::string &s)
{
return lex_cast<T>(string_view{s});
}
template<>
inline ircd::string_view
ircd::lex_cast(const string_view &s,
char *const &buf,
const size_t &max)
{
s.copy(buf, max);
return { buf, max };
}
template<>
inline ircd::string_view
ircd::lex_cast(const std::string_view &s,
char *const &buf,
const size_t &max)
{
s.copy(buf, max);
return { buf, max };
}
template<>
inline ircd::string_view
ircd::lex_cast(const std::string &s,
char *const &buf,
const size_t &max)
{
s.copy(buf, max);
return { buf, max };
}
template<class T>
__attribute__((error("unsupported lexical cast")))
ircd::string_view
ircd::lex_cast(T t,
char *const &buf,
const size_t &max)
{
assert(0);
return {};
}
template<>
inline bool
ircd::try_lex_cast<ircd::string_view>(const string_view &)
{
return true;
}
template<>
inline bool
ircd::try_lex_cast<std::string_view>(const string_view &)
{
return true;
}
template<>
inline bool
ircd::try_lex_cast<std::string>(const string_view &s)
{
return true;
}
template<class T>
__attribute__((error("unsupported lexical cast")))
bool
ircd::try_lex_cast(const string_view &s)
{
assert(0);
return false;
}

205
include/ircd/tokens.h Normal file
View file

@ -0,0 +1,205 @@
/*
* 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
*/
#pragma once
#define HAVE_IRCD_TOKENS_H
//
// String tokenization utils
//
namespace ircd
{
// 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 &sep, const token_view &);
void tokens(const string_view &str, const char *const &sep, const token_view &);
size_t tokens(const string_view &str, const char &sep, const size_t &limit, 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 &sep, char *const &buf, const size_t &max, const token_view &);
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, class sep> it tokens(const string_view &str, const sep &, const it &b, const it &e);
// Receive token view into array
template<size_t N, class sep> size_t tokens(const string_view &str, const sep &, string_view (&buf)[N]);
template<size_t N, class sep> size_t tokens(const string_view &str, 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,
class sep>
C<T, A> tokens(A&& allocator, const string_view &str, 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>,
class sep>
C<T, A> tokens(const string_view &str, 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,
class sep>
C<T, Comp, A> tokens(A&& allocator, const string_view &str, const sep &);
// 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>,
class sep>
C<T, Comp, A> tokens(const string_view &str, const sep &);
// Convenience to get individual tokens
size_t tokens_count(const string_view &str, const char &sep);
size_t tokens_count(const string_view &str, const char *const &sep);
string_view token(const string_view &str, const char &sep, const size_t &at);
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 &sep);
string_view token_last(const string_view &str, const char *const &sep);
string_view token_first(const string_view &str, const char &sep);
string_view token_first(const string_view &str, const char *const &sep);
string_view tokens_after(const string_view &str, const char &sep, const size_t &at);
string_view tokens_after(const string_view &str, const char *const &sep, const size_t &at);
}
template<size_t N,
class delim>
size_t
ircd::tokens(const string_view &str,
const delim &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,
class delim>
size_t
ircd::tokens(const string_view &str,
const delim &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,
class delim>
it
ircd::tokens(const string_view &str,
const delim &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,
class delim>
C<T, Comp, A>
ircd::tokens(const string_view &str,
const delim &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,
class delim>
C<T, Comp, A>
ircd::tokens(A&& allocator,
const string_view &str,
const delim &sep)
{
C<T, Comp, A> ret(std::forward<A>(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,
class delim>
C<T, A>
ircd::tokens(const string_view &str,
const delim &sep)
{
A allocator;
return tokens<C, T, A>(allocator, str, sep);
}
template<template<class, class>
class C,
class T,
class A,
class delim>
C<T, A>
ircd::tokens(A&& allocator,
const string_view &str,
const delim &sep)
{
C<T, A> ret(std::forward<A>(allocator));
tokens(str, sep, [&ret]
(const string_view &token)
{
ret.emplace(ret.end(), token);
});
return ret;
}

View file

@ -35,6 +35,11 @@
#include <boost/archive/iterators/transform_width.hpp> #include <boost/archive/iterators/transform_width.hpp>
#include <boost/archive/iterators/ostream_iterator.hpp> #include <boost/archive/iterators/ostream_iterator.hpp>
///////////////////////////////////////////////////////////////////////////////
//
// ircd/tokens.h
//
ircd::string_view ircd::string_view
ircd::tokens_after(const string_view &str, ircd::tokens_after(const string_view &str,
const char &sep, const char &sep,
@ -243,73 +248,46 @@ ircd::tokens(const string_view &str,
std::for_each(begin(view), end(view), closure); std::for_each(begin(view), end(view), closure);
} }
ircd::string_view ///////////////////////////////////////////////////////////////////////////////
ircd::b64encode(const mutable_buffer &out, //
const const_raw_buffer &in) // ircd/lex_cast.h
//
namespace ircd
{ {
/// The static lex_cast ring buffers are each LEX_CAST_BUFSIZE bytes;
/// Consider increasing if some lex_cast<T>(str) has more characters.
const size_t LEX_CAST_BUFSIZE {64};
using transform = boost::archive::iterators::transform_width<unsigned char *, 6, 8>; /// This is a static "ring buffer" to simplify a majority of lex_cast uses.
using b64fb = boost::archive::iterators::base64_from_binary<transform>; /// If the lex_cast has binary input and string output, and no user buffer
using ostream_iterator = boost::archive::iterators::ostream_iterator<char>; /// is supplied, the next buffer here will be used instead. The returned
/// string_view of data from this buffer is only valid for several more
/// calls to lex_cast before it is overwritten.
thread_local char lex_cast_buf[LEX_CAST_BUFS][LEX_CAST_BUFSIZE];
thread_local uint lex_cast_cur;
std::stringstream ss; template<size_t N, class T> static string_view _lex_cast(const T &i, char *buf, size_t max);
std::copy(b64fb(data(in)), b64fb(data(in) + size(in)), ostream_iterator(ss)); template<class T> static T _lex_cast(const string_view &s);
const auto outlen(ss.str().copy(data(out), size(out)));
return { data(out), outlen };
} }
ircd::const_raw_buffer /// Internal template providing conversions from a number to a string;
ircd::a2u(const mutable_raw_buffer &out, /// potentially using the ring buffer if no user buffer is supplied.
const const_buffer &in)
{
const size_t len{size(in) / 2};
for(size_t i(0); i < len; ++i)
{
const char gl[3]
{
in[i * 2],
in[i * 2 + 1],
'\0'
};
out[i] = strtol(gl, nullptr, 16);
}
return { data(out), len };
}
ircd::string_view
ircd::u2a(const mutable_buffer &out,
const const_raw_buffer &in)
{
char *p(data(out));
for(size_t i(0); i < size(in); ++i)
p += snprintf(p, size(out) - (p - data(out)), "%02x", in[i]);
return { data(out), size_t(p - data(out)) };
}
namespace ircd {
const size_t LEX_CAST_BUFSIZE {64};
thread_local char lex_cast_buf[LEX_CAST_BUFS][LEX_CAST_BUFSIZE];
template<size_t N, template<size_t N,
class T> class T>
static string_view ircd::string_view
_lex_cast(const T &i, ircd::_lex_cast(const T &i,
char *buf, char *buf,
size_t max) size_t max)
try try
{ {
using array = std::array<char, N>; using array = std::array<char, N>;
if(!buf) if(!buf)
{ {
static thread_local uint cur; buf = lex_cast_buf[lex_cast_cur++];
buf = lex_cast_buf[cur++];
max = LEX_CAST_BUFSIZE; max = LEX_CAST_BUFSIZE;
cur %= LEX_CAST_BUFS; lex_cast_cur %= LEX_CAST_BUFS;
} }
assert(max >= N); assert(max >= N);
@ -322,9 +300,11 @@ catch(const boost::bad_lexical_cast &e)
throw ircd::bad_lex_cast("%s", e.what()); throw ircd::bad_lex_cast("%s", e.what());
} }
/// Internal template providing conversions from a string to a number;
/// the native object is returned directly; no ring buffer is consumed.
template<class T> template<class T>
static T T
_lex_cast(const string_view &s) ircd::_lex_cast(const string_view &s)
try try
{ {
return boost::lexical_cast<T>(s); return boost::lexical_cast<T>(s);
@ -334,8 +314,6 @@ catch(const boost::bad_lexical_cast &e)
throw ircd::bad_lex_cast("%s", e.what()); throw ircd::bad_lex_cast("%s", e.what());
} }
} // namespace ircd
template<> ircd::string_view template<> ircd::string_view
ircd::lex_cast(bool i, ircd::lex_cast(bool i,
char *const &buf, char *const &buf,
@ -578,6 +556,57 @@ ircd::try_lex_cast<long double>(const string_view &s)
return boost::conversion::try_lexical_convert(s, i); return boost::conversion::try_lexical_convert(s, i);
} }
///////////////////////////////////////////////////////////////////////////////
//
// ircd/stringops.h
//
ircd::string_view
ircd::b64encode(const mutable_buffer &out,
const const_raw_buffer &in)
{
using transform = boost::archive::iterators::transform_width<unsigned char *, 6, 8>;
using b64fb = boost::archive::iterators::base64_from_binary<transform>;
using ostream_iterator = boost::archive::iterators::ostream_iterator<char>;
std::stringstream ss;
std::copy(b64fb(data(in)), b64fb(data(in) + size(in)), ostream_iterator(ss));
const auto outlen(ss.str().copy(data(out), size(out)));
return { data(out), outlen };
}
ircd::const_raw_buffer
ircd::a2u(const mutable_raw_buffer &out,
const const_buffer &in)
{
const size_t len{size(in) / 2};
for(size_t i(0); i < len; ++i)
{
const char gl[3]
{
in[i * 2],
in[i * 2 + 1],
'\0'
};
out[i] = strtol(gl, nullptr, 16);
}
return { data(out), len };
}
ircd::string_view
ircd::u2a(const mutable_buffer &out,
const const_raw_buffer &in)
{
char *p(data(out));
for(size_t i(0); i < size(in); ++i)
p += snprintf(p, size(out) - (p - data(out)), "%02x", in[i]);
return { data(out), size_t(p - data(out)) };
}
/* /*
* strip_colour - remove colour codes from a string * strip_colour - remove colour codes from a string
* -asuffield (?) * -asuffield (?)