libSDL2pp
C++11 bindings/wrapper for SDL2
StreamRWops.hh
1/*
2 libSDL2pp - C++11 bindings/wrapper for SDL2
3 Copyright (C) 2014-2016 Dmitry Marakasov <amdmi3@amdmi3.ru>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21
22#ifndef SDL2PP_STREAMRWOPS_HH
23#define SDL2PP_STREAMRWOPS_HH
24
25#include <SDL2pp/RWops.hh>
26
27#include <cassert>
28#include <stdexcept>
29#include <iostream>
30#include <type_traits>
31
32namespace SDL2pp {
33
50template <class S>
51class StreamRWops : public CustomRWops {
52 // since STL streams have different pointers for reading and writing,
53 // supporting both at the same time is impossible
54 static_assert(!(std::is_base_of<std::istream, S>::value && std::is_base_of<std::ostream, S>::value), "StreamRWops does not support reading and writing at the same time");
55
56protected:
58
59private:
60 template <class SS>
61 typename std::enable_if<std::is_base_of<std::istream, SS>::value && !std::is_base_of<std::ostream, SS>::value, void>::type SeekHelper(typename SS::off_type off, std::ios_base::seekdir dir) {
62 stream_.seekg(off, dir);
63 }
64
65 template <class SS>
66 typename std::enable_if<!std::is_base_of<std::istream, SS>::value && std::is_base_of<std::ostream, SS>::value, void>::type SeekHelper(typename SS::off_type off, std::ios_base::seekdir dir) {
67 stream_.seekp(off, dir);
68 }
69
70 template <class SS>
71 typename std::enable_if<std::is_base_of<std::istream, SS>::value && !std::is_base_of<std::ostream, SS>::value, typename SS::off_type>::type TellHelper() {
72 return stream_.tellg();
73 }
74
75 template <class SS>
76 typename std::enable_if<!std::is_base_of<std::istream, SS>::value && std::is_base_of<std::ostream, SS>::value, typename SS::off_type>::type TellHelper() {
77 return stream_.tellp();
78 }
79
80 template <class SS>
81 typename std::enable_if<std::is_base_of<std::istream, SS>::value, size_t>::type ReadHelper(void* ptr, size_t size, size_t maxnum) {
82 stream_.read(static_cast<char*>(ptr), size * maxnum);
83
84 // http://en.cppreference.com/w/cpp/io/streamsize:
85 // "Except in the constructors of std::strstreambuf,
86 // negative values of std::streamsize are never used"
87 size_t nread = static_cast<size_t>(stream_.gcount());
88
89 // eof is OK
90 if (stream_.rdstate() == (std::ios_base::eofbit | std::ios_base::failbit))
91 stream_.clear();
92
93 if (nread != size * maxnum) {
94 // short read
95 char* pos = static_cast<char*>(ptr);
96 pos += nread;
97
98 size_t count = nread % size;
99
100 // put partially read object back into the stream
101 while (count != 0) {
102 stream_.putback(*--pos);
103 --count;
104 }
105 }
106
107 return nread / size;
108 }
109
110 template <class SS>
111 typename std::enable_if<!std::is_base_of<std::istream, SS>::value, size_t>::type ReadHelper(void*, size_t, size_t) {
112 return 0;
113 }
114
115 template <class SS>
116 typename std::enable_if<std::is_base_of<std::ostream, SS>::value, size_t>::type WriteHelper(const void* ptr, size_t size, size_t num) {
117 stream_.write(static_cast<const char*>(ptr), size * num);
118 // XXX: there seem to be no reliable way to tell how much
119 // was actually written
120 if (stream_.rdstate() & std::ios_base::badbit)
121 return 0;
122 return num;
123 }
124
125 template <class SS>
126 typename std::enable_if<!std::is_base_of<std::ostream, SS>::value, size_t>::type WriteHelper(const void*, size_t, size_t) {
127 return 0;
128 }
129
130 template <class SS>
131 typename std::enable_if<std::is_base_of<std::ostream, SS>::value, int>::type CloseHelper() {
132 stream_.flush();
133 return (stream_.rdstate() & std::ios_base::badbit) ? -1 : 0;
134 }
135
136 template <class SS>
137 typename std::enable_if<!std::is_base_of<std::ostream, SS>::value, int>::type CloseHelper() {
138 return 0;
139 }
140
141public:
148 StreamRWops(S& stream) : stream_(stream) {
149 }
150
160 virtual Sint64 Size() override {
161 Sint64 old_pos = TellHelper<S>();
162 if (old_pos == -1) // not seekable?
163 return -1;
164 SeekHelper<S>(0, std::ios_base::end);
165 Sint64 size = TellHelper<S>();
166 SeekHelper<S>(old_pos, std::ios_base::beg);
167 assert(TellHelper<S>() == old_pos); // make sure we're back to where we were
168 return size;
169 }
170
184 virtual Sint64 Seek(Sint64 offset, int whence) override {
185 switch (whence) {
186 case RW_SEEK_SET:
187 SeekHelper<S>(offset, std::ios_base::beg);
188 break;
189 case RW_SEEK_CUR:
190 SeekHelper<S>(offset, std::ios_base::cur);
191 break;
192 case RW_SEEK_END:
193 SeekHelper<S>(offset, std::ios_base::end);
194 break;
195 default:
196 throw std::logic_error("Unexpected whence value for StreamRWops::Seek");
197 }
198 return TellHelper<S>();
199 }
200
214 virtual size_t Read(void* ptr, size_t size, size_t maxnum) override {
215 return ReadHelper<S>(ptr, size, maxnum);
216 }
217
231 virtual size_t Write(const void* ptr, size_t size, size_t num) override {
232 return WriteHelper<S>(ptr, size, num);
233 }
234
244 virtual int Close() override {
245 return CloseHelper<S>();
246 }
247};
248
249}
250
251#endif
Base class for custom RWops.
Definition: RWops.hh:48
RWops adaptor for STL streams.
Definition: StreamRWops.hh:51
virtual Sint64 Size() override
Get the size of the data stream.
Definition: StreamRWops.hh:160
StreamRWops(S &stream)
Construct StreamRWops for specified stream.
Definition: StreamRWops.hh:148
virtual int Close() override
Close stream.
Definition: StreamRWops.hh:244
S & stream_
Reference to stream.
Definition: StreamRWops.hh:57
virtual size_t Read(void *ptr, size_t size, size_t maxnum) override
Read from a stream.
Definition: StreamRWops.hh:214
virtual size_t Write(const void *ptr, size_t size, size_t num) override
Write to a stream.
Definition: StreamRWops.hh:231
virtual Sint64 Seek(Sint64 offset, int whence) override
Seek within the stream.
Definition: StreamRWops.hh:184