PLYwoot
Header-only C++17 library for parsing and writing PLY files
Loading...
Searching...
No Matches
buffered_istream.hpp
Go to the documentation of this file.
1/*
2 This file is part of PLYwoot, a header-only PLY parser.
3
4 Copyright (C) 2023-2025, Ton van den Heuvel
5
6 PLYwoot is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
18*/
19
20#ifndef PLYWOOT_BUFFERED_ISTREAM_HPP
21#define PLYWOOT_BUFFERED_ISTREAM_HPP
22
24
25#include <algorithm>
26#include <cstdint>
27#include <cstring>
28#include <istream>
29#include <memory>
30
31namespace {
32
34constexpr std::size_t IStreamBufferSize{1024 * 1024};
35
36}
37
38namespace plywoot::detail {
39
45class BufferedIStream
46{
47public:
49 explicit BufferedIStream(std::istream &is) : is_{is} { buffer(); }
50
52 BufferedIStream(const BufferedIStream &) = delete;
53 BufferedIStream &operator=(const BufferedIStream &) = delete;
54
56 bool eof() const { return *c_ == EOF; }
57
61 const char *data() const { return c_; }
62 const char *&data() { return c_; }
64
66 template<typename T>
67 inline T read()
68 {
69 // Note; buffer a bit more than strictly necessary, so that we can move the
70 // read head unconditionally after reading the object of type T.
71 if (c_ + sizeof(T) > eob_) buffer(sizeof(T));
72 const char *t = c_;
73 c_ += sizeof(T);
74 return *reinterpret_cast<const T *>(t);
75 }
76
80 template<typename From, typename To, std::size_t N>
81 std::uint8_t *read(std::uint8_t *dest)
82 {
83 if constexpr (std::is_same_v<From, To>)
84 {
85 return this->memcpy(dest, N * sizeof(From));
86 }
87 else
88 {
89 static_assert(
90 N * sizeof(From) <= IStreamBufferSize,
91 "input stream buffer size is too small; increase IStreamBufferSize");
92
93 constexpr std::size_t bytesToRead = N * sizeof(From);
94 if (c_ + bytesToRead > eob_) buffer(bytesToRead);
95
96 const From *from = reinterpret_cast<const From *>(c_);
97 To *to = reinterpret_cast<To *>(dest);
98 for (std::size_t i = 0; i < N; ++i) { *to++ = *from++; }
99
100 c_ += bytesToRead;
101
102 return reinterpret_cast<std::uint8_t *>(to);
103 }
104 }
105
109 inline std::uint8_t *memcpy(std::uint8_t *dest, std::size_t n)
110 {
111 if (n > IStreamBufferSize)
112 {
113 const std::size_t remaining = eob_ - c_;
114 std::memcpy(dest, c_, remaining);
115 is_.read(reinterpret_cast<char *>(dest) + remaining, n - remaining);
116 c_ = eob_;
117 }
118 else
119 {
120 if (c_ + n > eob_) buffer(n);
121
122 std::memcpy(dest, c_, n);
123 c_ += n;
124 }
125
126 return dest + n;
127 }
128
130 void skip(std::size_t n)
131 {
132 const std::size_t remaining = eob_ - c_;
133 if (remaining > n) { c_ += n; }
134 else
135 {
136 is_.seekg(n - remaining, std::ios_base::cur);
137 buffer();
138 }
139 }
140
144 void skipLines(std::size_t n)
145 {
146 while (*c_ != EOF && n > 0)
147 {
148 c_ = static_cast<const char *>(std::memchr(c_, '\n', eob_ - c_));
149 if (c_)
150 {
151 readCharacter();
152 --n;
153 }
154 else { buffer(); }
155 }
156 }
157
161 inline void skipWhitespace()
162 {
163 while (0 <= *c_ && *c_ <= 0x20) { readCharacter(); }
164 }
165
169 inline void skipNonWhitespace()
170 {
171 while (*c_ > 0x20) readCharacter();
172 }
173
178 void buffer(std::size_t minimum)
179 {
180 std::size_t remaining = eob_ - c_;
181 if (remaining < minimum)
182 {
183 std::memcpy(buffer_.get(), c_, remaining);
184 if (!is_.read(buffer_.get() + remaining, IStreamBufferSize - remaining))
185 {
186 // In case the buffer is only partially filled, fill the remainder with
187 // EOF characters.
188 remaining += is_.gcount();
189 std::fill_n(buffer_.get() + remaining, IStreamBufferSize - remaining, EOF);
190 }
191
192 c_ = buffer_.get();
193 }
194 }
195
196private:
198 void buffer()
199 {
200 if (!is_.read(buffer_.get(), IStreamBufferSize))
201 {
202 // In case the buffer is only partially filled, fill the remainder with
203 // EOF characters.
204 auto remaining = is_.gcount();
205 std::fill_n(buffer_.get() + remaining, IStreamBufferSize - remaining, EOF);
206 }
207
208 c_ = buffer_.get();
209 }
210
213 inline void readCharacter()
214 {
215 if (c_++ == eob_) { buffer(); }
216 }
217
219 std::unique_ptr<char[]> buffer_{new char[IStreamBufferSize]};
220
227 const char *c_{buffer_.get() + IStreamBufferSize};
229 const char *eob_{buffer_.get() + IStreamBufferSize};
230
232 std::istream &is_;
233};
234
235}
236
237#endif