diff options
Diffstat (limited to 'src/line.cc')
| -rw-r--r-- | src/line.cc | 127 |
1 files changed, 127 insertions, 0 deletions
diff --git a/src/line.cc b/src/line.cc new file mode 100644 index 0000000..23370fc --- /dev/null +++ b/src/line.cc @@ -0,0 +1,127 @@ +#include "line.hh" + +#include "check.hh" + +#include <algorithm> +#include <cassert> +#include <cstdint> +#include <cstring> +#include <expected> +#include <memory> +#include <string_view> +#include <utility> + +namespace line { + +namespace { + +const char kLineTerminators[] = "\r\n"; + +class ReaderImpl : public Reader { + public: + ReaderImpl(std::unique_ptr<io::Reader> reader, size_t max_len) + : reader_(std::move(reader)), + max_len_(max_len), + buffer_(std::make_unique_for_overwrite<char[]>( + check::add(max_len, static_cast<size_t>(2)))), + rptr_(buffer_.get()), + wptr_(buffer_.get()), + search_(rptr_), + end_(buffer_.get() + check::add(max_len, static_cast<size_t>(2))) {} + + [[nodiscard]] std::expected<std::string_view, io::ReadError> read() override { + while (true) { + search_ = std::find_first_of(search_, wptr_, kLineTerminators, + kLineTerminators + 2); + if (search_ < wptr_) { + if (std::cmp_greater(search_ - rptr_, max_len_)) { + return line(max_len_, 0); + } + + size_t tlen; + if (*search_ == '\n') { + tlen = 1; + } else { + if (search_ + 1 == wptr_) { + make_space_if_needed(); + auto got = fill(); + if (!got.has_value()) { + if (got.error() == io::ReadError::Eof) { + return line(search_ - rptr_, 1); + } + return std::unexpected(got.error()); + } + } + if (search_[1] == '\n') { + tlen = 2; + } else { + tlen = 1; + } + } + return line(search_ - rptr_, tlen); + } + if (std::cmp_greater_equal(wptr_ - rptr_, max_len_)) { + return line(max_len_, 0); + } + + make_space_if_needed(); + auto got = fill(); + if (!got.has_value()) { + if (got.error() == io::ReadError::Eof && rptr_ != wptr_) { + return line(wptr_ - rptr_, 0); + } + return std::unexpected(got.error()); + } + } + } + + [[nodiscard]] uint64_t number() const override { return number_; } + + private: + std::string_view line(size_t len, size_t terminator_len) { + assert(len <= max_len_); + auto ret = std::string_view(rptr_, len); + rptr_ += len + terminator_len; + search_ = rptr_; + ++number_; + return ret; + } + + void make_space_if_needed() { + size_t free = rptr_ - buffer_.get(); + if (free == 0) + return; + size_t avail = end_ - wptr_; + if (avail > 1024) + return; + memmove(buffer_.get(), rptr_, wptr_ - rptr_); + search_ -= free; + wptr_ -= free; + rptr_ = buffer_.get(); + } + + std::expected<size_t, io::ReadError> fill() { + auto ret = reader_->read(wptr_, end_ - wptr_); + if (ret.has_value()) + wptr_ += ret.value(); + return ret; + } + + std::unique_ptr<io::Reader> reader_; + size_t const max_len_; + uint64_t number_{0}; + std::unique_ptr<char[]> buffer_; + char* rptr_; + char* wptr_; + char* search_; + char* const end_; +}; + +} // namespace + +std::unique_ptr<Reader> open(std::unique_ptr<io::Reader> reader, + size_t max_len) { + return std::make_unique<ReaderImpl>(std::move(reader), max_len); +} + +} // namespace line |
