Skip to content

Instantly share code, notes, and snippets.

@Justasic
Last active September 10, 2018 07:38
Show Gist options
  • Save Justasic/e3094683aeab2325b955aabd58068d99 to your computer and use it in GitHub Desktop.
Save Justasic/e3094683aeab2325b955aabd58068d99 to your computer and use it in GitHub Desktop.
kstring -- The C++ string class with extra features std::string does not include
/*************************************************************************
* BSD 2-Clause License
*
* Copyright (c) 2017-2018, Justin Crawford
* Copyright (c) 2017, William Haugen
* All rights reserved.
*
* 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 COPYRIGHT HOLDER 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.
*/
// William Haugen
//
// The purpose of this class, kstring, is to overload the standard
// cstring library's operators to preform deep copies and have
// it act like it is part of the standard library.
// Allow for custom allocators - Justasic
#ifndef CUSTOM_REALLOC
# define CUSTOM_REALLOC realloc
#endif
#ifndef CUSTOM_FREE
# define CUSTOM_FREE free
#endif
// Allow different include locations in case the include
// is elsewhere in the system instead of these default spots.
#ifdef USE_LIBFMT
# ifdef CUSTOM_LIBFMT_INCLUDE
# include CUSTOM_LIBFMT_INCLUDE
# else
# include <fmt/fmt.h>
# endif
#endif
// CAUTION: Do not move this line above the libfmt line. spdlog uses a different location.
#ifdef USE_SPDLOG
# ifdef CUSTOM_SPDLOG_INCLUDE
# include CUSTOM_SPDLOG_INCLUDE
# else
# include <spdlog/fmt/fmt.h>
# endif
# define USE_LIBFMT 1
#endif
#include <cctype>
#include <cstring>
#include <iostream>
#include <map>
#include <strings.h>
#include <vector>
// TO DO:
// Length to spit out len
// Change char at a given index
class kstring
{
public:
// Define npos.
static const size_t npos = -1;
// Standard constructors
kstring() { this->ResizeString(1); }
kstring(const kstring &other) { memcpy(this->ResizeString(other.str->allocatedsz), other.str, other.str->allocatedsz); }
kstring(const char *cstr, size_t len) { memcpy(this->ResizeString(len)->str, cstr, len); }
kstring(const char *cstr) { size_t tmplen = strlen(cstr); memcpy(this->ResizeString(tmplen)->str, cstr, tmplen); }
kstring(const char ch) { this->ResizeString(2); this->str->str[0] = ch; this->str->str[1] = '\0'; }
kstring(const std::string &str) { memcpy(this->ResizeString(str.capacity())->str, str.c_str(), str.size()); }
// Strange constructors
template <typename... Args>
kstring(const std::string &str, const Args&&... args)
{
#ifdef USE_LIBFMT
*this = fmt::format(str, args...);
#endif
}
// Numerical conversions
explicit kstring(int i) { *this = std::to_string(i); }
explicit kstring(long int i) { *this = std::to_string(i); }
explicit kstring(long long int i) { *this = std::to_string(i); }
explicit kstring(unsigned int i) { *this = std::to_string(i); }
explicit kstring(unsigned long int i) { *this = std::to_string(i); }
explicit kstring(unsigned long long int i) { *this = std::to_string(i); }
explicit kstring(float i) { *this = std::to_string(i); }
explicit kstring(double i) { *this = std::to_string(i); }
explicit kstring(long double i) { *this = std::to_string(i); }
// Destructor
~kstring()
{
this->FreeString();
}
inline kstring &insert(size_t index, size_t count, char ch) {}
inline kstring &insert(size_t index, const char *s) {}
inline kstring &insert(size_t index, const char *s, size_t count) {}
inline kstring &insert(size_t index, const kstring &s) {}
inline kstring &insert(size_t index, const kstring &s, size_t index_str, size_t count = npos) {}
inline kstring &insert(size_t index, const std::string &s) {}
inline kstring &insert(size_t index, const std::string &s, size_t index_str, size_t count = npos) {}
inline kstring &erase(size_t index = 0, size_t count = kstring::npos) {}
inline void push_back(char ch) {}
inline void pop_back() { this->erase(this->size() - 1, 1); }
// TODO: all append() functions.
inline kstring &append(size_t count, char ch) {}
inline kstring &append(const kstring &str) {}
inline kstring &append(const kstring &str, size_t pos, size_t count = npos) {}
inline kstring &append(const char *s, size_t count) {}
inline kstring &append(const char *s) {}
inline kstring &append(const std::string &str) {}
inline kstring &append(const std::string &str, size_t pos, size_t count = npos) {}
// TODO: all compare() functions.
inline int compare(const kstring &str) const {}
inline int compare(size_t pos1, size_t count1, const kstring &str) const {}
inline int compare(size_t pos1, size_t count1, const kstring &str, size_t pos2, size_t count2 = npos) const {}
inline int compare(const std::string &str) const {}
inline int compare(size_t pos1, size_t count1, const std::string &str) const {}
inline int compare(size_t pos1, size_t count1, const std::string &str, size_t pos2, size_t count2 = npos) const {}
inline int compare(const char *s) const {}
inline int compare(size_t pos1, size_t count1, const char *s) const {}
inline int compare(size_t pos1, size_t count1, const char *s, size_t count2) const {}
inline bool starts_with(kstring x) const noexcept {}
inline bool starts_with(char x) const noexcept {}
inline bool starts_with(const char *x) const {}
inline bool ends_with(kstring x) const noexcept {}
inline bool ends_with(char x) const noexcept {}
inline bool ends_with(const char *x) const {}
// TODO: all replace() functions.
inline kstring &replace(size_t pos, size_t count, const kstring &str) {}
inline kstring &replace(size_t pos, size_t count, const kstring &str, size_t pos2, size_t count2 = npos) {}
inline kstring &replace(size_t pos, size_t count, const std::string &str) {}
inline kstring &replace(size_t pos, size_t count, const std::string &str, size_t pos2, size_t count2 = npos) {}
inline kstring &replace(size_t pos, size_t count, const char *str, size_t count2) {}
inline kstring &replace(size_t pos, size_t count, const char *cstr) {}
inline kstring &replace(size_t pos, size_t count, size_t count2, char ch) {}
kstring substr(size_t = 0, size_t = npos) const;
inline void resize(size_t count) {}
inline void resize(size_t count, char ch) {}
inline void swap(kstring &other) {}
size_t copy(char *dest, size_t count, size_t pos = 0) const;
// a timing-safe compaison of strings (used for passwords)
inline bool securecmp(const kstring &);
// Vector functions (useful for tokenization)
std::vector<kstring> expand(const kstring &delim) const;
kstring contract(const std::vector<kstring> &_vec, const kstring &delim);
// Make use of tinyformat here.
template<typename... Args>
kstring fmt(const Args &... args)
{
#ifdef USE_LIBFMT
return fmt::format(this->c_str(), args...);
#endif
}
// Search functions
size_t find(const kstring &, size_t = 0) const;
inline size_t find(const char *s, size_t pos, size_t count) const {}
inline size_t find(const char *s, size_t pos = 0) const {}
inline size_t find(char ch, size_t pos = 0) const {}
// Reverse find
inline size_t rfind(const kstring &, size_t = 0) const {}
inline size_t rfind(const char *s, size_t pos, size_t count) const {}
inline size_t rfind(const char *s, size_t pos = 0) const {}
inline size_t rfind(char ch, size_t pos = 0) const {}
inline size_t find_first_of(const kstring &str, size_t pos = 0) const {}
inline size_t find_first_of(const char *s, size_t pos, size_t count) const {}
inline size_t find_first_of(const char *s, size_t pos = 0) const {}
inline size_t find_first_of(char ch, size_t pos = 0) const {}
inline size_t find_first_not_of(const kstring &str, size_t pos = 0) const {}
inline size_t find_first_not_of(const char *s, size_t pos, size_t count) const {}
inline size_t find_first_not_of(const char *s, size_t pos = 0) const {}
inline size_t find_first_not_of(char ch, size_t pos = 0) const {}
inline size_t find_last_of(const kstring &str, size_t pos = 0) const {}
inline size_t find_last_of(const char *s, size_t pos, size_t count) const {}
inline size_t find_last_of(const char *s, size_t pos = 0) const {}
inline size_t find_last_of(char ch, size_t pos = 0) const {}
inline size_t find_last_not_of(const kstring &str, size_t pos = 0) const {}
inline size_t find_last_not_of(const char *s, size_t pos, size_t count) const {}
inline size_t find_last_not_of(const char *s, size_t pos = 0) const {}
inline size_t find_last_not_of(char ch, size_t pos = 0) const {}
// Assignment Operators
kstring &operator=(const kstring &other) { memcpy(this->ResizeString(other.str->allocatedsz), other.str, other.str->allocatedsz); }
kstring &operator=(const std::string &other) { memcpy(this->ResizeString(other.capacity())->str, other.c_str(), other.size()); }
kstring &operator=(const char *other) { size_t tmplen = strlen(other); memcpy(this->ResizeString(tmplen)->str, other, tmplen); }
kstring &operator=(const char ch) { this->ResizeString(2); this->str->str[0] = ch; this->str->str[1] = 0; }
inline kstring &operator+=(const kstring &str) {}
inline kstring &operator+=(const std::string &str) {}
inline kstring &operator+=(char ch) {}
inline kstring &operator+=(const char *s) {}
inline kstring &operator+=(std::initializer_list<char> ilist) {}
template<class T>
inline kstring &operator+=(const T &t)
{}
// Able to use std::ostream and std::istream natively
friend std::ostream &operator<<(std::ostream &, const kstring &);
friend std::istream &operator>>(std::istream &, kstring &);
// Ability to add onto strings, just uses std::strcat
friend kstring operator+(const kstring &, char *);
friend kstring operator+(char *, const kstring &);
friend kstring operator+(const kstring &, const char *);
friend kstring operator+(const char *, const kstring &);
friend kstring operator+(const kstring &, const kstring &);
// Using strcmp to determine all the relations.
friend bool operator<(const kstring &, char *);
friend bool operator<(char *, const kstring &);
friend bool operator<(const kstring &, const kstring &);
friend bool operator<=(const kstring &, char *);
friend bool operator<=(char *, const kstring &);
friend bool operator<=(const kstring &, const kstring &);
friend bool operator>(const kstring &, char *);
friend bool operator>(char *, const kstring &);
friend bool operator>(const kstring &, const kstring &);
friend bool operator>=(const kstring &, char *);
friend bool operator>=(char *, const kstring &);
friend bool operator>=(const kstring &, const kstring &);
friend bool operator!=(const kstring &, char *);
friend bool operator!=(char *, const kstring &);
friend bool operator!=(const kstring &, const kstring &);
friend bool operator==(const kstring &, char *);
friend bool operator==(char *, const kstring &);
friend bool operator==(const kstring &, const kstring &);
// READ ONLY for the subscript
char & operator[](int i);
const char &operator[](int i) const;
// Getters/Setters/Others
inline size_t size() const { return this->isnull() ? 0 : this->str->length; }
inline size_t length() const { this->size(); }
inline const char *c_str() const { return this->isnull() ? nullptr : this->str->str; }
inline bool isnull() const { return !this->str; }
inline bool empty() const { return this->isnull() ? true : !this->str->length; }
inline char * data() { this->isnull() ? return nullptr : return this->str->str; }
inline char & front() { return this->str->str[0]; }
inline const char & front() const { return this->front(); }
inline char & back() { return this->str->str[this->str->length - 1]; }
inline const char & back() const { return this->back(); }
inline void reserve(size_t sz) { this->ResizeString(sz); }
inline size_t capacity() const { return this->str->allocatedsz; }
inline void shrink_to_fit() { this->ResizeString(str->length); }
inline void clear()
{
if (this->str)
{
bzero(this->str->str, this->str->length);
this->str->length = 0;
}
}
// Casting operators
inline explicit operator int() { return this->isnull() ? 0 : static_cast<int>(strtol(this->str->str, nullptr, 10)); }
inline explicit operator long int() { return this->isnull() ? 0 : strtol(this->str->str, nullptr, 10); }
inline explicit operator long long int() { return this->isnull() ? 0 : strtoll(this->str->str, nullptr, 10); }
// C/C++ does not have a strtou function cuz apparently being consistent isn't top
// priority for ISOCPP group. So we have to do a truncating cast from unsigned long.
inline explicit operator unsigned int() { return this->isnull() ? 0 : static_cast<unsigned int>(strtoul(this->str->str, nullptr, 10)); }
inline explicit operator unsigned long int() { return this->isnull() ? 0 : strtoul(this->str->str, nullptr, 10); }
inline explicit operator unsigned long long int() { return this->isnull() ? 0 : strtoull(this->str->str, nullptr, 10); }
inline explicit operator float() { return this->isnull() ? 0.0 : strtof(this->str->str, nullptr); }
inline explicit operator double() { return this->isnull() ? 0.0 : strtod(this->str->str, nullptr); }
inline explicit operator long double() { return this->isnull() ? 0.0 : strtold(this->str->str, nullptr); }
private:
// This structure should never be exposed to the user!
typedef struct string_s
{
// We reference count to dynamically expand the buffer, this is
// also why we require an internal object.
size_t refs; // Used for reference counting
size_t allocatedsz; // Used to get allocated size
size_t length; // Length of the string
// Okay, this takes some explaining. I got this trick from
// https://stackoverflow.com/a/599441
// and basically what is going on is we allocate this struct
// for the size of the string plus the size of the struct.
// This will allow us to keep all the data related to the actual
// bytes of the string tied together so it isn't lost, even if
// we return somestringobj->str; as a pointer to the user.
// The user can use pointer arithmatic to get the length or size
// without having an out of bounds deference.
char str[1]; // String itself.
} _string_t;
// I use realloc here to change the size of the pointer
// if the pointer is null then it will act as a malloc call
// if the requested size is greater than the current size, it will allocate more
// space and keep the data
// if the requested space is smaller than the allocated space, it will shrink and truncate.
_string_t *ResizeString(size_t len)
{
void *ptr = CUSTOM_REALLOC(this->str, len);
if (!ptr)
throw std::bad_alloc();
this->str = reinterpret_cast<_string_t *>(ptr);
this->str->allocatedsz = len;
return this->str;
}
void FreeString()
{
CUSTOM_FREE(this->str);
this->str = nullptr;
}
_string_t *str;
};
// Useful :p
typedef std::vector<kstring> kvector;
// user-defined literal
inline kstring operator"" _k(const char *str, size_t len) { return kstring(str, len); }
// A case-insensitive map
struct insensitive
{
inline bool operator()(const kstring &a, const kstring &b) const { return !strcasecmp(a.c_str(), b.c_str()); }
};
template<typename T>
class kmap : public std::map<kstring, T, insensitive>
{};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment