Added Path type (WIP).
This commit is contained in:
parent
d0be5f7739
commit
cbd4efb0f9
129
source/mijin/types/path.hpp
Normal file
129
source/mijin/types/path.hpp
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#if !defined(MIJIN_TYPES_PATH_HPP_INCLUDED)
|
||||||
|
#define MIJIN_TYPES_PATH_HPP_INCLUDED 1
|
||||||
|
|
||||||
|
#include "../internal/common.hpp"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
namespace mijin
|
||||||
|
{
|
||||||
|
template<typename TChar = char, TChar separator = TChar('/'), typename TTraits = std::char_traits<TChar>, template<typename> typename TAllocator = MIJIN_DEFAULT_ALLOCATOR>
|
||||||
|
class BasePath
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using string_t = std::basic_string<TChar, TTraits, TAllocator<char>>;
|
||||||
|
using size_type = string_t::size_type;
|
||||||
|
using difference_type = string_t::difference_type;
|
||||||
|
private:
|
||||||
|
string_t storage_;
|
||||||
|
public:
|
||||||
|
BasePath() = default;
|
||||||
|
BasePath(const BasePath&) = default;
|
||||||
|
BasePath(BasePath&&) = default;
|
||||||
|
BasePath(string_t string) MIJIN_NOEXCEPT_IF(std::is_nothrow_move_constructible<string_t>()) : storage_(std::move(string)) {
|
||||||
|
simplify();
|
||||||
|
}
|
||||||
|
|
||||||
|
BasePath& operator=(const BasePath&) = default;
|
||||||
|
BasePath& operator=(BasePath&&) = default;
|
||||||
|
|
||||||
|
auto operator<=>(const BasePath&) const noexcept = default;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
const string_t& string() const MIJIN_NOEXCEPT
|
||||||
|
{
|
||||||
|
return storage_;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
void simplify() MIJIN_NOEXCEPT;
|
||||||
|
};
|
||||||
|
|
||||||
|
using Path = BasePath<char>;
|
||||||
|
using WPath = BasePath<wchar_t>;
|
||||||
|
|
||||||
|
template<typename TChar, TChar separator, typename TTraits, template<typename> typename TAllocator>
|
||||||
|
void BasePath<TChar, separator, TTraits, TAllocator>::simplify() MIJIN_NOEXCEPT
|
||||||
|
{
|
||||||
|
// step 1: remove double slashes
|
||||||
|
difference_type moveBy = 0;
|
||||||
|
for (auto it = std::next(storage_.begin()); it < storage_.end(); ++it)
|
||||||
|
{
|
||||||
|
const bool doubleSlash = (*it == separator && *std::prev(it) == separator); // check this before moving the current character, as that might create a double slash itself
|
||||||
|
if (moveBy > 0) {
|
||||||
|
*std::prev(it, moveBy) = *it;
|
||||||
|
}
|
||||||
|
if (doubleSlash) {
|
||||||
|
++moveBy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// step 1.5: remove trailing slash (but only if it's not the only remaining char)
|
||||||
|
if (moveBy < static_cast<difference_type>(storage_.size() - 1) && storage_[storage_.size() - moveBy - 1] == separator) {
|
||||||
|
++moveBy;
|
||||||
|
}
|
||||||
|
storage_.resize(storage_.size() - moveBy);
|
||||||
|
|
||||||
|
// step 2: get rid of any "/.." together with the preceeding segment
|
||||||
|
moveBy = 0;
|
||||||
|
for (auto it = std::next(storage_.begin(), 2); it < storage_.end(); ++it)
|
||||||
|
{
|
||||||
|
if (moveBy > 0)
|
||||||
|
{
|
||||||
|
*std::prev(it, moveBy) = *it;
|
||||||
|
}
|
||||||
|
if (*std::prev(it, moveBy) == TChar('.') && *std::prev(it, moveBy + 1) == TChar('.') && *std::prev(it, moveBy + 2) == separator
|
||||||
|
&& (std::next(it) == storage_.end() || *std::next(it) == separator))
|
||||||
|
{
|
||||||
|
if (std::prev(it, moveBy + 2) == storage_.begin())
|
||||||
|
{
|
||||||
|
// leading "/.." -> just remove it
|
||||||
|
moveBy += 3;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// find the start of the preceeding segment
|
||||||
|
for (auto itStart = std::prev(it, moveBy + 3);; --itStart)
|
||||||
|
{
|
||||||
|
if (*std::prev(itStart) == separator || std::prev(itStart) == storage_.begin())
|
||||||
|
{
|
||||||
|
// /path/with/../double/dot
|
||||||
|
// itStart --A A
|
||||||
|
// it -------------|
|
||||||
|
// remove everything from itStart to it + two slashes
|
||||||
|
moveBy += std::distance(itStart, std::prev(it, moveBy)) + 2; // skip it all
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
storage_.resize(storage_.size() - moveBy);
|
||||||
|
|
||||||
|
// step 3: eliminate any segments that are just "."
|
||||||
|
moveBy = 0;
|
||||||
|
if (storage_.size() == 1) {
|
||||||
|
return; // just stop it here
|
||||||
|
}
|
||||||
|
for (auto it = storage_.begin(); it < storage_.end(); ++it)
|
||||||
|
{
|
||||||
|
const bool atStart = (it == storage_.begin());
|
||||||
|
const bool atEnd = (std::next(it) == storage_.end());
|
||||||
|
const bool emptyEle = (*it == TChar('.') // char is a dot
|
||||||
|
&& (atStart || *std::prev(it, moveBy + 1) == separator) // previous is a slash or doesn't exist
|
||||||
|
&& (atEnd || *std::next(it) == separator)); // next is a slash or doesn't exist
|
||||||
|
if (moveBy > 0) {
|
||||||
|
*std::prev(it, moveBy) = *it;
|
||||||
|
}
|
||||||
|
if (emptyEle) {
|
||||||
|
moveBy += 2;
|
||||||
|
if (!atEnd) {
|
||||||
|
++it; // skip the next one
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
storage_.resize(storage_.size() - moveBy);
|
||||||
|
}
|
||||||
|
} // namespace shiken
|
||||||
|
|
||||||
|
#endif // !defined(MIJIN_TYPES_PATH_HPP_INCLUDED)
|
Loading…
x
Reference in New Issue
Block a user