forked from KhronosGroup/KTX-Software
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsbufstream.h
225 lines (192 loc) · 7.13 KB
/
sbufstream.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
/* -*- tab-width: 4; -*- */
/* vi: set sw=2 ts=4 expandtab: */
/*
* Copyright 2021 Paolo Jovon.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @internal
* @file
* @~English
*
* @brief A ktxStream that wraps a C++ std::streambuf.
*
* Modified from Paolo's original to make it a template so that the reference to std::streambuf can
* be passed either as a `unique_ptr` or a regular pointer. As our use is to pass the streambuf from
* an istream, potentially that from std::cin, or an istringstream that is not being not allocated by `new`
* we can't use a `unique_ptr`. i.e. can't attempt to delete the stringbuf when done. Also the
* destructor now destructs the stream, if not already destructed.
*
* @author Paolo Jovon
* @author Mark Callow
*/
#include <iostream>
#include <memory>
#include <ktx.h>
static std::ostream cnull(0);
static std::ostream& logstream = cnull;
//static std::ostream logstream = std::cerr;
/// @brief Template for a ktxStream that wraps a C++ std::streambuf.
///
/// The template supports 2 ways of referencing the underlying std::streambuf:
/// as a @c unique_ptr or as a regular pointer. For the former case, the
/// @c unique_ptr std::streambuf and StreambufStream should be created
/// like this:
/// @code
/// // Can use stringbuf or any class derived from it.
/// auto filebuf = std::make_unique<std::filebuf>();
/// StreambufStream<std::unique_ptr<std::streambuf>>ktxStream(
/// std::move(filebuf),
/// ios::in);
/// @endcode
///
///
template <typename T>
class StreambufStream
{
// Doubt this will ever get triggered
static_assert(sizeof(char) == sizeof(uint8_t),
"Chars are != 1 byte in this platform");
public:
StreambufStream(T streambuf,
std::ios::openmode seek_mode = std::ios::in | std::ios::out)
: _streambuf{std::move(streambuf)}
, _seek_mode{seek_mode}
, _stream{std::make_unique<ktxStream>()}
, _destructed{false}
{
initialize_stream();
}
StreambufStream(const StreambufStream&) = delete;
StreambufStream &operator=(const StreambufStream&) = delete;
StreambufStream(StreambufStream&&) = delete;
StreambufStream &operator=(StreambufStream&&) = delete;
virtual ~StreambufStream()
{
if (!_destructed)
stream()->destruct(stream());
}
inline ktxStream* stream() const
{
return _stream.get();
}
std::streambuf* streambuf() const;
inline std::ios::openmode seek_mode() const
{
return _seek_mode;
}
inline void seek_mode(std::ios::openmode newmode)
{
_seek_mode = newmode;
}
inline bool destructed() const
{
return _destructed;
}
protected:
void initialize_stream() {
_stream->type = eStreamTypeCustom;
_stream->closeOnDestruct = false;
auto& custom_ptr = _stream->data.custom_ptr;
custom_ptr.address = this;
custom_ptr.allocatorAddress = nullptr; // N/A
custom_ptr.size = 0; // N/A
_stream->read = read;
_stream->skip = skip;
_stream->write = write;
_stream->getpos = getpos;
_stream->setpos = setpos;
_stream->getsize = getsize;
_stream->destruct = destruct;
}
// C++ streambuf overrides
// ktxStream vtable implementations
inline static StreambufStream* parent(ktxStream *str)
{
return reinterpret_cast<StreambufStream*>(str->data.custom_ptr.address);
}
static KTX_error_code read(ktxStream* str, void* dst, ktx_size_t count)
{
auto self = parent(str);
if (count == 0)
{
return KTX_SUCCESS;
}
logstream << "\t read: " << count << 'B' << std::endl;
const auto stdcount = std::streamsize(count);
const std::streamsize nread = self->_streambuf->sgetn(reinterpret_cast<char*>(dst), stdcount);
return (nread == stdcount) ? KTX_SUCCESS : KTX_FILE_UNEXPECTED_EOF;
}
static KTX_error_code skip(ktxStream* str, ktx_size_t count)
{
auto self = parent(str);
if (count == 0)
{
return KTX_SUCCESS;
}
logstream << "\t skip: " << count << 'B' << std::endl;
const std::streampos curpos = self->_streambuf->pubseekoff(0, std::ios::cur, self->_seek_mode);
const std::streampos newpos = self->_streambuf->pubseekoff(std::streamoff(count), std::ios::cur, self->_seek_mode);
return (curpos > newpos) ? KTX_SUCCESS : KTX_FILE_SEEK_ERROR;
}
static KTX_error_code write(ktxStream* str, const void* src, ktx_size_t size, ktx_size_t count)
{
auto self = parent(str);
if (size == 0 || count == 0)
{
return KTX_SUCCESS;
}
logstream << "\t write: " << count << "*" << size << "B" << std::endl;
const auto ntotal = std::streamsize(size * count);
const std::streamsize nput = self->_streambuf->sputn(reinterpret_cast<const char*>(src), ntotal);
return (nput == ntotal) ? KTX_SUCCESS : KTX_FILE_WRITE_ERROR;
}
static KTX_error_code getpos(ktxStream* str, ktx_off_t *offset)
{
auto self = parent(str);
*offset = ktx_off_t(self->_streambuf->pubseekoff(0, std::ios::cur, self->_seek_mode));
logstream << "\tgetpos: " << *offset << std::endl;
return KTX_SUCCESS;
}
static KTX_error_code setpos(ktxStream* str, ktx_off_t offset)
{
auto self = parent(str);
const auto newpos = std::streamoff(offset);
const std::streampos setpos = self->_streambuf->pubseekoff(newpos, std::ios::beg, self->_seek_mode);
logstream << "\tsetpos: " << offset << std::endl;
return (setpos == newpos) ? KTX_SUCCESS : KTX_FILE_SEEK_ERROR;
}
static KTX_error_code getsize(ktxStream* str, ktx_size_t* size)
{
auto self = parent(str);
const std::streampos oldpos = self->_streambuf->pubseekoff(0, std::ios::cur, self->_seek_mode);
*size = ktx_size_t(self->_streambuf->pubseekoff(0, std::ios::end));
const std::streampos newpos = self->_streambuf->pubseekoff(oldpos, std::ios::beg, self->_seek_mode);
logstream << "\t size: " << *size << 'B' << std::endl;
return (oldpos == newpos) ? KTX_SUCCESS : KTX_FILE_SEEK_ERROR;
}
static void destruct(ktxStream* str)
{
auto self = parent(str);
self->_destructed = true;
}
T _streambuf;
std::ios::openmode _seek_mode;
std::unique_ptr<ktxStream> _stream;
// ktxTexture?_CreateFromStream destructs the ktxStream when finished, if
// KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT was passed. This variable tracks
// if the ktxStream's destructor has been called.
bool _destructed;
};
// I have not yet found a way to do this inside the template definition.
// However `inline` should prevent any multiple definition errors.
template<>
inline std::streambuf* StreambufStream<std::streambuf*>::streambuf() const
{
return _streambuf;
}
template<>
inline std::streambuf* StreambufStream<std::unique_ptr<std::streambuf>>::streambuf() const
{
return _streambuf.get();
}