C++ Essence Library 0.1.0
A Utility Library for Modern C++ Programming
Loading...
Searching...
No Matches
spanbuf.hpp
1/*
2 * Copyright (c) 2024 The RefValue Project
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 * THE SOFTWARE.
21 */
22
23#pragma once
24
25#include <cstdint>
26#include <span>
27#include <streambuf>
28
29namespace essence::io {
30 template <typename CharT, typename Traits = std::char_traits<CharT>>
31 class basic_spanbuf : public std::basic_streambuf<CharT, Traits> {
32 public:
33 using char_type = CharT;
34 using traits_type = Traits;
35 using int_type = typename Traits::int_type;
36 using pos_type = typename Traits::pos_type;
37 using off_type = typename Traits::off_type;
38
39 explicit basic_spanbuf() noexcept : basic_spanbuf{std::ios_base::in | std::ios_base::out} {}
40
41 explicit basic_spanbuf(std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) noexcept
42 : basic_spanbuf{std::span<CharT>{}, mode} {}
43
44 explicit basic_spanbuf(
45 std::span<CharT> buffer, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) noexcept
46 : mode_{mode}, buffer_{buffer} {
47 this->span(buffer);
48 }
49
50 basic_spanbuf(const basic_spanbuf&) = delete;
51
52 basic_spanbuf(basic_spanbuf&&) noexcept = default;
53
54 basic_spanbuf& operator=(const basic_spanbuf&) = delete;
55
56 basic_spanbuf& operator=(basic_spanbuf&&) noexcept = default;
57
58 std::basic_streambuf<CharT, Traits>* setbuf(CharT* buffer, std::streamsize size) override {
59 this->span(std::span{buffer, static_cast<std::size_t>(size)});
60
61 return this;
62 }
63
64 [[nodiscard]] std::span<CharT> span() const noexcept {
65 return buffer_;
66 }
67
68 void span(std::span<CharT> buffer) noexcept {
69 buffer_ = buffer;
70
71 if ((mode_ & std::ios_base::in) == std::ios_base::in) {
72 this->setg(buffer_.data(), buffer_.data(), buffer_.data() + buffer_.size());
73 }
74
75 if ((mode_ & std::ios_base::out) == std::ios_base::out) {
76 this->setp(buffer_.data(), buffer_.data() + buffer_.size());
77 this->pbump(
78 (mode_ & std::ios_base::ate) == std::ios_base::ate ? static_cast<std::int32_t>(buffer_.size()) : 0);
79 }
80 }
81
82 protected:
83 pos_type seekoff(off_type offset, std::ios_base::seekdir direction, std::ios_base::openmode mode) override {
84 switch (direction) {
85 case std::ios_base::beg:
86 return seekpos(pos_type{offset}, mode);
87 case std::ios_base::cur:
88 // https://stackoverflow.com/questions/6800811/basic-streambufseekoff-what-should-be-returned-when-ios-basein-ios-baseo?rq=3
89 // If both in and out are specified and the mode is std::ios_base::cur, it shall fail.
90 if ((mode & (std::ios_base::in | std::ios_base::out)) == (std::ios_base::in | std::ios_base::out)) {
91 break;
92 }
93
94 if ((mode & std::ios_base::in) == std::ios_base::in) {
95 return seekpos(this->gptr() + offset - this->eback(), std::ios_base::in);
96 }
97
98 if ((mode & std::ios_base::out) == std::ios_base::out) {
99 return seekpos(this->pptr() + offset - this->pbase(), std::ios_base::out);
100 }
101
102 break;
103 case std::ios_base::end:
104 return seekpos(pos_type{static_cast<off_type>(buffer_.size()) + offset}, mode);
105 default:
106 break;
107 }
108
109 return std::basic_streambuf<CharT, Traits>::seekoff(offset, direction, mode);
110 }
111
112 pos_type seekpos(pos_type pos, std::ios_base::openmode mode) override {
113 // Checks validity.
114 if (pos < 0 || pos >= buffer_.size()) {
115 return std::basic_streambuf<CharT, Traits>::seekpos(pos, mode);
116 }
117
118 if ((mode_ & std::ios_base::in) == std::ios_base::in && (mode & std::ios_base::in) == std::ios_base::in) {
119 this->setg(buffer_.data(), buffer_.data() + pos, buffer_.data() + buffer_.size());
120 }
121
122 if ((mode_ & std::ios_base::out) == std::ios_base::out
123 && (mode & std::ios_base::out) == std::ios_base::out) {
124 this->setp(buffer_.data(), buffer_.data() + buffer_.size());
125 this->pbump(static_cast<std::int32_t>(pos));
126 }
127
128 return pos;
129 }
130
131 private:
132 std::ios_base::openmode mode_;
133 std::span<CharT> buffer_;
134 };
135} // namespace essence::io
Definition spanbuf.hpp:31