Files
nostalgia/deps/ox/src/ox/fs/filesystem/pathiterator.cpp

190 lines
5.3 KiB
C++

/*
* Copyright 2015 - 2025 gary@drinkingtea.net
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#include <ox/std/memops.hpp>
#include <ox/std/strops.hpp>
#include <ox/std/trace.hpp>
#include "pathiterator.hpp"
OX_CLANG_NOWARN_BEGIN(-Wunsafe-buffer-usage)
namespace ox {
PathIterator::PathIterator(const char *path, std::size_t maxSize, std::size_t iterator) {
m_path = path;
m_maxSize = maxSize;
m_iterator = iterator;
}
PathIterator::PathIterator(const char *path): PathIterator(path, ox::strlen(path)) {
}
PathIterator::PathIterator(StringViewCR path): PathIterator(path.data(), path.bytes()) {
}
/**
* @return 0 if no error
*/
Error PathIterator::dirPath(char *out, std::size_t outSize) {
const auto idx = ox::lastIndexOf(m_path, '/', m_maxSize);
const auto size = static_cast<std::size_t>(idx) + 1;
if (idx >= 0 && size < outSize) {
ox::memcpy(out, m_path, size);
out[size] = 0;
return {};
} else {
return ox::Error(1);
}
}
// Gets the get item in the path
Error PathIterator::get(StringView &fileName) {
std::size_t size = 0;
if (m_iterator >= m_maxSize) {
oxTracef("ox.fs.PathIterator.get", "m_iterator ({}) >= m_maxSize ({})", m_iterator, m_maxSize);
return ox::Error(1);
}
if (!ox::strlen(&m_path[m_iterator])) {
oxTrace("ox.fs.PathIterator.get", "!ox::strlen(&m_path[m_iterator])");
return ox::Error(1);
}
auto start = m_iterator;
if (m_path[start] == '/') {
start++;
}
// end is at the next /
const char *substr = ox::strchr(&m_path[start], '/', m_maxSize - start);
// correct end if it is invalid, which happens if there is no next /
if (!substr) {
substr = ox::strchr(&m_path[start], 0, m_maxSize - start);
}
const auto end = static_cast<size_t>(substr - m_path);
size = end - start;
// cannot fit the output in the output parameter
if (size >= MaxFileNameLength || size == 0) {
return ox::Error(1);
}
fileName = ox::substr(m_path, start, start + size);
// truncate trailing /
if (size && fileName[size - 1] == '/') {
fileName = ox::substr(m_path, start, start + size - 1);
}
oxAssert(fileName[fileName.len()-1] != '/', "name ends in /");
return {};
}
/**
* @return 0 if no error
*/
Error PathIterator::next(StringView &fileName) {
std::size_t size = 0;
auto retval = ox::Error(1);
if (m_iterator < m_maxSize && ox::strlen(&m_path[m_iterator])) {
retval = {};
if (m_path[m_iterator] == '/') {
m_iterator++;
}
const auto start = m_iterator;
// end is at the next /
const char *substr = ox::strchr(&m_path[start], '/', m_maxSize - start);
// correct end if it is invalid, which happens if there is no next /
if (!substr) {
substr = ox::strchr(&m_path[start], 0, m_maxSize - start);
}
const auto end = static_cast<size_t>(substr - m_path);
size = end - start;
// cannot fit the output in the output parameter
if (size >= MaxFileNameLength) {
return ox::Error(1);
}
fileName = ox::substr(m_path, start, start + size);
// truncate trailing /
while (fileName.len() && fileName[fileName.len() - 1] == '/') {
fileName = ox::substr(m_path, start, start + size);
}
m_iterator += size;
oxAssert(fileName.len() == 0 || fileName[fileName.len()-1] != '/', "name ends in /");
}
return retval;
}
Result<std::size_t> PathIterator::nextSize() const {
std::size_t size = 0;
auto retval = ox::Error(1);
auto it = m_iterator;
if (it < m_maxSize && ox::strlen(&m_path[it])) {
retval = {};
if (m_path[it] == '/') {
it++;
}
const auto start = it;
// end is at the next /
const char *substr = ox::strchr(&m_path[start], '/', m_maxSize - start);
// correct end if it is invalid, which happens if there is no next /
if (!substr) {
substr = ox::strchr(&m_path[start], 0, m_maxSize - start);
}
const auto end = static_cast<std::size_t>(substr - m_path);
size = end - start;
}
it += size;
return {size, retval};
}
bool PathIterator::hasNext() const {
std::size_t size = 0;
if (m_iterator < m_maxSize && ox::strlen(&m_path[m_iterator])) {
std::size_t start = m_iterator;
if (m_path[start] == '/') {
start++;
}
// end is at the next /
const char *substr = ox::strchr(&m_path[start], '/', m_maxSize - start);
// correct end if it is invalid, which happens if there is no next /
if (!substr) {
substr = ox::strchr(&m_path[start], 0, m_maxSize - start);
}
const auto end = static_cast<std::size_t>(substr - m_path);
size = end - start;
}
return size > 0;
}
bool PathIterator::valid() const {
return m_iterator < m_maxSize && m_path[m_iterator] != 0;
}
PathIterator PathIterator::next() const {
std::size_t size = 0;
auto iterator = m_iterator;
if (iterator < m_maxSize && ox::strlen(&m_path[iterator])) {
if (m_path[iterator] == '/') {
iterator++;
}
const auto start = iterator;
// end is at the next /
const char *substr = ox::strchr(&m_path[start], '/', m_maxSize - start);
// correct end if it is invalid, which happens if there is no next /
if (!substr) {
substr = ox::strchr(&m_path[start], 0, m_maxSize - start);
}
const auto end = static_cast<std::size_t>(substr - m_path);
size = end - start;
}
iterator += size;
return {m_path, m_maxSize, iterator + 1};
}
const char *PathIterator::fullPath() const {
return m_path;
}
}
OX_CLANG_NOWARN_END