diff --git a/config/tawashi.ini b/config/tawashi.ini new file mode 100644 index 0000000..1b9f9f0 --- /dev/null +++ b/config/tawashi.ini @@ -0,0 +1,5 @@ +[tawashi] +redis_server = 127.0.0.1 +redis_port = 6379 +redis_mode = inet +base_uri = http://127.0.0.1:8080 diff --git a/src/pathname/kakoune/assert.hh b/src/pathname/kakoune/assert.hh new file mode 100644 index 0000000..87a1da3 --- /dev/null +++ b/src/pathname/kakoune/assert.hh @@ -0,0 +1,30 @@ +#ifndef assert_hh_INCLUDED +#define assert_hh_INCLUDED + +namespace Kakoune +{ + +class StringView; + +// return true if user asked to ignore the error +bool notify_fatal_error(StringView message); + +void on_assert_failed(const char* message); + +} + +#define STRINGIFY(X) #X +#define TOSTRING(X) STRINGIFY(X) + +#ifdef KAK_DEBUG + #define kak_assert(...) do { \ + if (not (__VA_ARGS__)) \ + on_assert_failed("assert failed \"" #__VA_ARGS__ \ + "\" at " __FILE__ ":" TOSTRING(__LINE__)); \ + } while (false) +#else + #define kak_assert(...) do {} while(false) +#endif + + +#endif // assert_hh_INCLUDED diff --git a/src/pathname/kakoune/ref_ptr.hh b/src/pathname/kakoune/ref_ptr.hh new file mode 100644 index 0000000..692aa63 --- /dev/null +++ b/src/pathname/kakoune/ref_ptr.hh @@ -0,0 +1,133 @@ +#ifndef ref_ptr_hh_INCLUDED +#define ref_ptr_hh_INCLUDED + +#include + +#if !defined(NDEBUG) && !defined(KAK_DEBUG) +# define KAK_DEBUG +#endif + +namespace Kakoune +{ + +struct WorstMatch { [[gnu::always_inline]] WorstMatch(...) {} }; + +[[gnu::always_inline]] +inline void ref_ptr_moved(WorstMatch, void*, void*) noexcept {} + +template +struct RefPtr +{ + RefPtr() = default; + explicit RefPtr(T* ptr) : m_ptr(ptr) { acquire(); } + ~RefPtr() { release(); } + RefPtr(const RefPtr& other) : m_ptr(other.m_ptr) { acquire(); } + RefPtr(RefPtr&& other) + noexcept(noexcept(std::declval().moved(nullptr))) + : m_ptr(other.m_ptr) { other.m_ptr = nullptr; moved(&other); } + + RefPtr& operator=(const RefPtr& other) + { + if (other.m_ptr != m_ptr) + { + release(); + m_ptr = other.m_ptr; + acquire(); + } + return *this; + } + + RefPtr& operator=(RefPtr&& other) + { + release(); + m_ptr = other.m_ptr; + other.m_ptr = nullptr; + moved(&other); + return *this; + } + + RefPtr& operator=(T* ptr) + { + if (ptr != m_ptr) + { + release(); + m_ptr = ptr; + acquire(); + } + return *this; + } + + [[gnu::always_inline]] + T* operator->() const { return m_ptr; } + [[gnu::always_inline]] + T& operator*() const { return *m_ptr; } + + [[gnu::always_inline]] + T* get() const { return m_ptr; } + + [[gnu::always_inline]] + explicit operator bool() const { return m_ptr; } + + void reset(T* ptr = nullptr) + { + if (ptr == m_ptr) + return; + release(); + m_ptr = ptr; + acquire(); + } + + friend bool operator==(const RefPtr& lhs, const RefPtr& rhs) { return lhs.m_ptr == rhs.m_ptr; } + friend bool operator!=(const RefPtr& lhs, const RefPtr& rhs) { return lhs.m_ptr != rhs.m_ptr; } + +private: + T* m_ptr = nullptr; + + [[gnu::always_inline]] + void acquire() + { + if (m_ptr) + inc_ref_count(static_cast(m_ptr), this); + } + + [[gnu::always_inline]] + void release() + { + if (m_ptr) + dec_ref_count(static_cast(m_ptr), this); + m_ptr = nullptr; + } + + [[gnu::always_inline]] + void moved(void* from) + noexcept(noexcept(ref_ptr_moved(static_cast(nullptr), nullptr, nullptr))) + { + if (m_ptr) + ref_ptr_moved(static_cast(m_ptr), from, this); + } +}; + +struct RefCountable +{ + int refcount = 0; + virtual ~RefCountable() = default; + + friend void inc_ref_count(RefCountable* r, void*) + { + ++r->refcount; + } + + friend void dec_ref_count(RefCountable* r, void*) + { + if (--r->refcount == 0) + delete r; + } +}; + +} + +#if defined(KAK_DEBUG) +# undef KAK_DEBUG +#endif + +#endif // ref_ptr_hh_INCLUDED diff --git a/src/pathname/kakoune/safe_ptr.hh b/src/pathname/kakoune/safe_ptr.hh new file mode 100644 index 0000000..3ba71b3 --- /dev/null +++ b/src/pathname/kakoune/safe_ptr.hh @@ -0,0 +1,106 @@ +#ifndef safe_ptr_hh_INCLUDED +#define safe_ptr_hh_INCLUDED + +// #define SAFE_PTR_TRACK_CALLSTACKS + +#include "ref_ptr.hh" + +#include +#include + +#ifdef SAFE_PTR_TRACK_CALLSTACKS +#include "vector.hh" +#include +#endif + +#include + +#define kak_assert assert + +#if !defined(NDEBUG) && !defined(KAK_DEBUG) +# define KAK_DEBUG +#endif + +namespace Kakoune +{ + +// *** SafePtr: objects that assert nobody references them when they die *** + +class SafeCountable +{ +public: +#ifdef KAK_DEBUG + SafeCountable() : m_count(0) {} + ~SafeCountable() + { + kak_assert(m_count == 0); + #ifdef SAFE_PTR_TRACK_CALLSTACKS + kak_assert(m_callstacks.empty()); + #endif + } + + friend void inc_ref_count(const SafeCountable* sc, void* /*ptr*/) + { + ++sc->m_count; + #ifdef SAFE_PTR_TRACK_CALLSTACKS + sc->m_callstacks.emplace_back(ptr); + #endif + } + + friend void dec_ref_count(const SafeCountable* sc, void* /*ptr*/) + { + --sc->m_count; + kak_assert(sc->m_count >= 0); + #ifdef SAFE_PTR_TRACK_CALLSTACKS + auto it = std::find_if(sc->m_callstacks.begin(), sc->m_callstacks.end(), + [=](const Callstack& cs) { return cs.ptr == ptr; }); + kak_assert(it != sc->m_callstacks.end()); + sc->m_callstacks.erase(it); + #endif + } + + friend void ref_ptr_moved(const SafeCountable* /*sc*/, void* /*from*/, void* /*to*/) + { + #ifdef SAFE_PTR_TRACK_CALLSTACKS + auto it = std::find_if(sc->m_callstacks.begin(), sc->m_callstacks.end(), + [=](const Callstack& cs) { return cs.ptr == from; }); + kak_assert(it != sc->m_callstacks.end()); + it->ptr = to; + #endif + } + +private: + #ifdef SAFE_PTR_TRACK_CALLSTACKS + struct Callstack + { + Callstack(void* p) : ptr(p) {} + void* ptr; + Backtrace bt; + }; + + mutable Vector m_callstacks; + #endif + mutable int m_count; +#else + [[gnu::always_inline]] + friend void inc_ref_count(const SafeCountable* /*sc*/, void* /*ptr*/) {} + + [[gnu::always_inline]] + friend void dec_ref_count(const SafeCountable* /*sc*/, void* /*ptr*/) {} +#endif +}; + +template +using PropagateConst = typename std::conditional::value, const U, U>::type; + +template +using SafePtr = RefPtr>; + +} + +#if defined(KAK_DEBUG) +# undef KAK_DEBUG +#endif +#undef kak_assert + +#endif // safe_ptr_hh_INCLUDED diff --git a/src/pathname/pathname.cpp b/src/pathname/pathname.cpp new file mode 100644 index 0000000..6ee899e --- /dev/null +++ b/src/pathname/pathname.cpp @@ -0,0 +1,254 @@ +/* Copyright 2015, 2016, Michele Santullo + * This file is part of "dindexer". + * + * "dindexer" 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 3 of the License, or + * (at your option) any later version. + * + * "dindexer" 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 "dindexer". If not, see . + */ + +#include "pathname.hpp" +#include +#include +#include +#include +#include + +namespace mchlib { + const std::string PathName::m_empty_str(""); + + namespace { + std::string get_joint_atoms ( const StringPool& parPool, bool parAbs, std::size_t parSkipRight=0 ); + std::size_t calc_join_size ( const StringPool& parPool, bool parAbs, std::size_t parSkipRight=0 ); + std::size_t get_adjusted_atom_count ( const StringPool& parPool, std::size_t parSkipRight ); + + std::size_t get_adjusted_atom_count (const StringPool& parPool, std::size_t parSkipRight) { + const auto orig_atom_count = parPool.size(); + const auto atom_count = (parSkipRight >= orig_atom_count ? 0 : orig_atom_count - parSkipRight); + return atom_count; + } + + std::size_t calc_join_size (const StringPool& parPool, bool parAbs, std::size_t parSkipRight) { + const auto atom_count = get_adjusted_atom_count(parPool, parSkipRight); + if (not atom_count) { + if (parPool.empty() and parAbs) { + return 1; + } + else { + return 0; + } + } + + std::size_t reserve = (parAbs ? 1 : 0); + for (std::size_t z = 0; z < atom_count; ++z) { + reserve += parPool[z].size(); + } + reserve += atom_count - 1; + return reserve; + } + + std::size_t count_grouped (boost::string_ref parIn, char parDelim) { + std::size_t retval = 0; + char prev = '\0'; + for (auto c : parIn) { + retval += (parDelim == c and prev != parDelim ? 1 : 0); + prev = c; + } + return retval; + } + + void split_path (std::vector* parOut, boost::string_ref parPath) { + auto from = parPath.begin(); + boost::string_ref::const_iterator next; + const auto end = parPath.end(); + const auto beg = parPath.begin(); + while (end != (next = std::find(from, end, '/'))) { + if (next != from) { + parOut->push_back(parPath.substr(from - beg, next - from)); + from = next; + } + ++from; + } + if (next != from) { + parOut->push_back(parPath.substr(from - beg, next - from)); + } + } + + std::string get_joint_atoms (const StringPool& parPool, bool parAbs, std::size_t parSkipRight) { + const auto reserve = calc_join_size(parPool, parAbs, parSkipRight); + switch (reserve) { + case 0: + //reserve 0 means the resulting string is empty + return std::string(""); + case 1: + //when reserve is 1 and we're talking about an absolute path, + //the resulting string can only be "/" + if (parAbs) { + return std::string("/"); + } + }; + + std::string out; + out.reserve(reserve); + const char* slash = (parAbs ? "/" : ""); + const auto atom_count = get_adjusted_atom_count(parPool, parSkipRight); + for (std::size_t z = 0; z < atom_count; ++z) { + out += slash; + const auto& curr_itm = parPool[z]; + out.insert(out.end(), curr_itm.begin(), curr_itm.end()); + slash = "/"; + } + assert(reserve == out.size()); + return out; + } + } //unnamed namespace + + PathName::PathName (boost::string_ref parPath) { + if (not parPath.empty()) { + m_absolute = ('/' == parPath.front()); + std::string path(parPath.begin(), parPath.end()); + + const auto count = count_grouped(path, '/'); + const std::size_t trailing = (path.back() == '/' ? 1 : 0); + const std::size_t absolute = (m_absolute ? 1 : 0); + const auto res = count + 1 - trailing - absolute; + std::vector atoms; + atoms.reserve(res); + split_path(&atoms, path); + m_pool.insert(atoms, &path); + } + else { + m_original_path = nullptr; + m_absolute = false; + } + } + + std::string PathName::path() const { + return get_joint_atoms(m_pool, m_absolute); + } + + void PathName::join (const PathName& parOther) { + m_pool.update(parOther.m_pool); + } + + const boost::string_ref PathName::operator[] (std::size_t parIndex) const { + return *(m_pool.begin() + parIndex); + } + + std::size_t PathName::atom_count ( void ) const { + return m_pool.size(); + } + + void PathName::join (const char* parOther) { + const std::string src(parOther); + const boost::string_ref ref(src); + m_pool.insert(ref, &src); + } + + void PathName::join (boost::string_ref parOther, const std::string* parSource) { + m_pool.insert(parOther, parSource); + } + + PathName make_relative_path (const PathName& parBasePath, const PathName& parOtherPath) { + if (not parBasePath.is_absolute() and parOtherPath.is_absolute()) { + return parOtherPath; + } + + std::size_t common_atoms = 0; + { + const std::size_t shortest = std::min(parOtherPath.atom_count(), parBasePath.atom_count()); + for (std::size_t z = 0; z < shortest; ++z) { + if (parOtherPath[z] == parBasePath[z]) { + ++common_atoms; + } + else { + break; + } + } + } + + PathName retval(""); + const auto ellipses_count = parBasePath.atom_count() - common_atoms; + for (std::size_t z = 0; z < ellipses_count; ++z) { + retval.join(".."); + } + + const auto remaining_atoms = parOtherPath.atom_count() - common_atoms; + for (std::size_t z = 0; z < remaining_atoms; ++z) { + retval.join(parOtherPath[z + common_atoms], parOtherPath.get_stringref_source(z + common_atoms)); + } + return retval; + } + + const std::string* PathName::get_stringref_source (std::size_t parIndex) const { + return m_pool.get_stringref_source(parIndex); + } + + std::string PathName::dirname() const { + if (this->atom_count() == 0) + return std::string(); + + return get_joint_atoms(m_pool, m_absolute, 1); + } + + std::ostream& operator<< (std::ostream& parStream, const PathName& parPath) { + parStream << parPath.path(); + return parStream; + } + + const boost::string_ref basename (const PathName& parPath) { + static const char* const empty = ""; + const auto sz = parPath.atom_count(); + if (not sz) { + return boost::string_ref(empty); + } + + assert(sz > 0); + return parPath[sz - 1]; + } + + PathName& PathName::pop_right() { + m_pool.pop(); + return *this; + } + + bool PathName::operator!= (const PathName& parOther) const { + const auto count = atom_count(); + if (count != parOther.atom_count()) { + return true; + } + + for (std::size_t z = 0; z < count; ++z) { + if ((*this)[z] != parOther[z]) { + return true; + } + } + return false; + } + + bool PathName::operator== (const PathName& parOther) const { + const auto count = atom_count(); + if (count != parOther.atom_count()) { + return false; + } + + for (std::size_t z = 0; z < count; ++z) { + if ((*this)[z] != parOther[z]) { + return false; + } + } + return true; + } + + std::size_t PathName::str_path_size() const { + return calc_join_size(m_pool, is_absolute()); + } +} //namespace mchlib diff --git a/src/pathname/pathname.hpp b/src/pathname/pathname.hpp new file mode 100644 index 0000000..f9e0d5e --- /dev/null +++ b/src/pathname/pathname.hpp @@ -0,0 +1,65 @@ +/* Copyright 2015, 2016, Michele Santullo + * This file is part of "dindexer". + * + * "dindexer" 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 3 of the License, or + * (at your option) any later version. + * + * "dindexer" 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 "dindexer". If not, see . + */ + +#ifndef id279E04E31E2C4D98B8C902781A3CE018 +#define id279E04E31E2C4D98B8C902781A3CE018 + +#include "stringpool.hpp" +#include "kakoune/safe_ptr.hh" +#include +#include +#include +#include +#include + +namespace mchlib { + class PathName : public Kakoune::SafeCountable { + public: + PathName ( PathName&& ) = default; + PathName ( const PathName& ) = default; + explicit PathName ( boost::string_ref parPath ); + ~PathName ( void ) noexcept = default; + + bool is_absolute ( void ) const { return m_absolute; } + std::string path ( void ) const; + std::size_t str_path_size ( void ) const; + const std::string& original_path ( void ) const { return (m_original_path ? *m_original_path : m_empty_str); } + std::size_t atom_count ( void ) const; + const boost::string_ref operator[] ( std::size_t parIndex ) const; + void join ( const PathName& parOther ); + void join ( const char* parOther ); + void join ( boost::string_ref parOther, const std::string* parSource ); + const std::string* get_stringref_source ( std::size_t parIndex ) const; + std::string dirname ( void ) const; + PathName& pop_right ( void ); + bool operator!= ( const PathName& parOther ) const; + bool operator== ( const PathName& parOther ) const; + + private: + static const std::string m_empty_str; + + StringPool m_pool; + const std::string* m_original_path; + bool m_absolute; + }; + + PathName make_relative_path ( const PathName& parBasePath, const PathName& parOtherPath ); + std::ostream& operator<< ( std::ostream& parStream, const PathName& parPath ); + const boost::string_ref basename ( const PathName& parPath ); +} //namespace mchlib + +#endif diff --git a/src/pathname/stringpool.hpp b/src/pathname/stringpool.hpp new file mode 100644 index 0000000..16c15dd --- /dev/null +++ b/src/pathname/stringpool.hpp @@ -0,0 +1,70 @@ +/* Copyright 2015, 2016, Michele Santullo + * This file is part of "dindexer". + * + * "dindexer" 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 3 of the License, or + * (at your option) any later version. + * + * "dindexer" 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 "dindexer". If not, see . + */ + +#ifndef id9CF5E6FA7E334DF09559C2968C494CB9 +#define id9CF5E6FA7E334DF09559C2968C494CB9 + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace mchlib { + template , typename StrRef=boost::basic_string_ref> + class StringPool { + typedef std::pair StringListPair; + typedef std::vector> PoolType; + typedef std::vector StringListType; + typedef std::function FuncGetFirst; + + public: + typedef C char_type; + typedef Str string_type; + typedef StrRef stringref_type; + typedef boost::transform_iterator const_iterator; + + StringPool ( void ) = default; + ~StringPool ( void ) noexcept = default; + + template + void update ( ItR parDataBeg, ItR parDataEnd ); + void update ( const StringPool& parOther ); + void insert ( const std::vector& parStrings, const string_type* parBaseString ); + void insert ( stringref_type parString, const string_type* parBaseString ); + const string_type* ptr_to_literal ( const char* parLiteral ); + std::size_t size ( void ) const { return m_strings.size(); } + bool empty ( void ) const { return m_strings.empty(); } + const_iterator begin ( void ) const; + const_iterator end ( void ) const; + const string_type* get_stringref_source ( std::size_t parIndex ) const; + const stringref_type& operator[] ( std::size_t parIndex ) const; + void pop ( void ); + + private: + PoolType m_pool; + StringListType m_strings; + }; +} //namespace mchlib + +#include "stringpool.inl" + +#endif diff --git a/src/pathname/stringpool.inl b/src/pathname/stringpool.inl new file mode 100644 index 0000000..5842ff2 --- /dev/null +++ b/src/pathname/stringpool.inl @@ -0,0 +1,140 @@ +/* Copyright 2015, 2016, Michele Santullo + * This file is part of "dindexer". + * + * "dindexer" 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 3 of the License, or + * (at your option) any later version. + * + * "dindexer" 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 "dindexer". If not, see . + */ + +namespace mchlib { + namespace implem { + template + std::pair clone_ifp (const StrRef& parClone, StrRef parSource) { + const auto offset = parSource.find(parClone); + if (parSource.npos != offset) { + return std::make_pair(parSource.substr(offset, parClone.size()), true); + } + else { + return std::make_pair(parClone, false); + } + } + } //namespace implem + + template + auto StringPool::ptr_to_literal (const char* parLiteral) -> const string_type* { + if (not parLiteral) + return nullptr; + + for (const auto& p : m_pool) { + if (m_pool.first == parLiteral) { + return &m_pool.first; + } + } + return nullptr; + } + + template + template + void StringPool::update (ItR parDataBeg, ItR parDataEnd) { + typedef std::pair PoolPair; + + while (parDataBeg != parDataEnd) { + const auto& remote_str = parDataBeg->first; + const auto* remote_source_str = parDataBeg->second; + bool cloned = false; + + for (auto& local_src : m_pool) { + const string_type& local_str = local_src.first; + auto& local_ref_count = local_src.second; + + auto cloned_result = implem::clone_ifp(remote_str, local_str); + cloned = cloned_result.second; + const auto& cloned_str = cloned_result.first; + if (cloned) { + ++local_ref_count; + m_strings.push_back(StringListPair(cloned_str, &local_str)); + break; + } + } + + if (not cloned) { + m_pool.push_back(PoolPair(*remote_source_str, static_cast(1))); + const auto offset = remote_str.data() - remote_source_str->data(); + m_strings.push_back(StringListPair(stringref_type(m_pool.back().first).substr(offset, remote_str.size()), &m_pool.back().first)); + } + ++parDataBeg; + } + } + + template + void StringPool::update (const StringPool& parOther) { + this->update(parOther.m_strings.begin(), parOther.m_strings.end()); + } + + template + auto StringPool::begin() const -> const_iterator { + return const_iterator(m_strings.cbegin(), [](const StringListPair& parItm) { return parItm.first; }); + } + + template + auto StringPool::end() const -> const_iterator { + return const_iterator(m_strings.cend(), [](const StringListPair& parItm) { return parItm.first; }); + } + + template + void StringPool::insert (const std::vector& parStrings, const string_type* parBaseString) { + StringListType dummy; + dummy.reserve(parStrings.size()); + for (const auto& itm : parStrings) { + dummy.push_back(StringListPair(itm, parBaseString)); + } + this->update(dummy.begin(), dummy.end()); + } + + template + void StringPool::insert (stringref_type parString, const string_type* parBaseString) { + StringListType dummy; + dummy.reserve(1); + dummy.push_back(StringListPair(parString, parBaseString)); + this->update(dummy.begin(), dummy.end()); + } + + template + auto StringPool::get_stringref_source (std::size_t parIndex) const -> const string_type* { + return m_strings[parIndex].second; + } + + template + auto StringPool::operator[] (std::size_t parIndex) const -> const stringref_type& { + return m_strings[parIndex].first; + } + + template + void StringPool::pop() { + if (m_strings.empty()) { + return; + } + + for (auto z = m_pool.size(); z > 0; --z) { + auto& pool_itm = m_pool[z - 1]; + if (&pool_itm.first == m_strings.back().second) { + m_strings.resize(m_strings.size() - 1); + --pool_itm.second; + if (0 == pool_itm.second) { + m_pool.erase(m_pool.begin() + (z - 1)); + } + break; + } + } + return; + } +} //namespace mchlib