1 // -*- coding: utf-8 -*-
2 //
3 // CharArrayStream.cxx --- IOStreams classes for reading from, and writing to
4 // char arrays
5 //
6 // Copyright (C) 2017 Florent Rougon
7 //
8 // This library is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU Library General Public
10 // License as published by the Free Software Foundation; either
11 // version 2 of the License, or (at your option) any later version.
12 //
13 // This library is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 // Library General Public License for more details.
17 //
18 // You should have received a copy of the GNU Library General Public
19 // License along with this library; if not, write to the Free Software
20 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
21 // MA 02110-1301 USA.
22
23 #include <simgear_config.h>
24
25 #include <ios> // std::streamsize
26 #include <istream> // std::istream and std::iostream
27 #include <ostream> // std::ostream
28 #include <type_traits> // std::make_unsigned()
29 #include <cstddef> // std::size_t, std::ptrdiff_t
30 #include <cassert>
31
32 #include "CharArrayStream.hxx"
33
34 using traits = std::char_traits<char>;
35
36
37 namespace simgear
38 {
39
40 // ***************************************************************************
41 // * CharArrayStreambuf class *
42 // ***************************************************************************
43
CharArrayStreambuf(char * buf,std::size_t bufSize)44 CharArrayStreambuf::CharArrayStreambuf(char* buf, std::size_t bufSize)
45 : _buf(buf),
46 _bufSize(bufSize)
47 {
48 setg(_buf, _buf, _buf + _bufSize);
49 setp(_buf, _buf + _bufSize);
50 }
51
data() const52 char* CharArrayStreambuf::data() const
53 {
54 return _buf;
55 }
56
size() const57 std::size_t CharArrayStreambuf::size() const
58 {
59 return _bufSize;
60 }
61
underflow()62 int CharArrayStreambuf::underflow()
63 {
64 return (gptr() == egptr()) ? traits::eof() : traits::to_int_type(*gptr());
65 }
66
overflow(int c)67 int CharArrayStreambuf::overflow(int c)
68 {
69 // cf. §27.7.1, footnote 309 of the C++11 standard
70 if (traits::eq_int_type(c, traits::eof())) {
71 return traits::not_eof(c);
72 } else {
73 // This class never writes beyond the end of the array (_buf + _bufSize)
74 return traits::eof();
75 }
76 }
77
xsgetn(char * dest,std::streamsize n)78 std::streamsize CharArrayStreambuf::xsgetn(char* dest, std::streamsize n)
79 {
80 assert(n >= 0);
81 std::ptrdiff_t avail = egptr() - gptr();
82 // Compute min(avail, n). The cast is safe, because in its branch, one has
83 // 0 <= n < avail, which is of type std::ptrdiff_t.
84 std::ptrdiff_t nbChars = ( (n >= avail) ?
85 avail : static_cast<std::ptrdiff_t>(n) );
86 std::copy(gptr(), gptr() + nbChars, dest);
87 // eback() == _buf and egptr() == _buf + _bufSize
88 // I don't use gbump(), because it takes an int...
89 setg(eback(), gptr() + nbChars, egptr());
90
91 // Cast safe because 0 <= nbChars <= n, which is of type std::streamsize
92 return static_cast<std::streamsize>(nbChars); // number of chars copied
93 }
94
xsputn(const char * s,std::streamsize n)95 std::streamsize CharArrayStreambuf::xsputn(const char* s, std::streamsize n)
96 {
97 assert(n >= 0);
98 std::ptrdiff_t availSpace = epptr() - pptr();
99 // Compute min(availSpace, n). The cast is safe, because in its branch, one
100 // has 0 <= n < availSpace, which is of type std::ptrdiff_t.
101 std::ptrdiff_t nbChars = ( (n >= availSpace) ?
102 availSpace : static_cast<std::ptrdiff_t>(n) );
103 std::copy(s, s + nbChars, pptr());
104 // epptr() == _buf + _bufSize
105 // I don't use pbump(), because it takes an int...
106 setp(pptr() + nbChars, epptr());
107
108 // Cast safe because 0 <= nbChars <= n, which is of type std::streamsize
109 return static_cast<std::streamsize>(nbChars); // number of chars copied
110 }
111
showmanyc()112 std::streamsize CharArrayStreambuf::showmanyc()
113 {
114 // It is certain that underflow() will return EOF if gptr() == egptr().
115 return -1;
116 }
117
seekoff(std::streamoff off,std::ios_base::seekdir way,std::ios_base::openmode which)118 std::streampos CharArrayStreambuf::seekoff(std::streamoff off,
119 std::ios_base::seekdir way,
120 std::ios_base::openmode which)
121 {
122 bool positionInputSeq = false;
123 bool positionOutputSeq = false;
124 char* ptr = nullptr;
125
126 // cf. §27.8.2.4 of the C++11 standard
127 if ((which & std::ios_base::in) == std::ios_base::in) {
128 positionInputSeq = true;
129 ptr = gptr();
130 }
131
132 if ((which & std::ios_base::out) == std::ios_base::out) {
133 positionOutputSeq = true;
134 ptr = pptr();
135 }
136
137 if ((!positionInputSeq && !positionOutputSeq) ||
138 (positionInputSeq && positionOutputSeq &&
139 way != std::ios_base::beg && way != std::ios_base::end)) {
140 return std::streampos(std::streamoff(-1));
141 }
142
143 // If we reached this point and (positionInputSeq && positionOutputSeq),
144 // then (way == std::ios_base::beg || way == std::ios_base::end) and
145 // therefore 'ptr' won't be used.
146 std::streamoff refOffset;
147 static_assert(sizeof(std::streamoff) >= sizeof(std::ptrdiff_t),
148 "Unexpected: sizeof(std::streamoff) < sizeof(std::ptrdiff_t)");
149 static_assert(sizeof(std::streamoff) >= sizeof(std::size_t),
150 "Unexpected: sizeof(std::streamoff) < sizeof(std::size_t)");
151
152 if (way == std::ios_base::beg) {
153 refOffset = 0;
154 } else if (way == std::ios_base::cur) {
155 refOffset = static_cast<std::streamoff>(ptr - _buf);
156 } else {
157 assert(way == std::ios_base::end);
158 refOffset = static_cast<std::streamoff>(_bufSize);
159 }
160
161 // Offset, relatively to _buf, where we are supposed to seek
162 std::streamoff totalOffset = refOffset + off;
163 typedef typename std::make_unsigned<std::streamoff>::type uStreamOff;
164
165 if (totalOffset < 0 || static_cast<uStreamOff>(totalOffset) > _bufSize) {
166 return std::streampos(std::streamoff(-1));
167 } else {
168 // Safe because 0 <= totalOffset <= _bufSize, which is an std::size_t
169 char* newPtr = _buf + static_cast<std::size_t>(totalOffset);
170
171 if (positionInputSeq) {
172 // eback() == _buf and egptr() == _buf + _bufSize
173 setg(eback(), newPtr, egptr());
174 }
175
176 if (positionOutputSeq) {
177 // epptr() == _buf + _bufSize
178 setp(newPtr, epptr());
179 }
180
181 // C++11's §27.8.2.4 item 12 (for stringbuf) would return refOffset. This
182 // makes no sense IMHO, in particular when 'way' is std::ios_base::beg or
183 // std::ios_base::end. Return the new offset (from the beginning of
184 // '_buf') instead. Note that this doesn't violate anything, because
185 // §27.6.3.4.2 grants full freedom as to the semantics of seekoff() to
186 // classes derived from basic_streambuf.
187 //
188 // My interpretation is consistent with items 13 and 14 of §27.8.2.4
189 // concerning seekpos(), whereas item 12 is not (if item 12 were followed
190 // to the letter, seekoff() would always return 0 on success when
191 // way == std::ios_base::beg, and therefore items 13 and 14 would be
192 // incompatible).
193 return std::streampos(totalOffset);
194 }
195 }
196
seekpos(std::streampos pos,std::ios_base::openmode which)197 std::streampos CharArrayStreambuf::seekpos(std::streampos pos,
198 std::ios_base::openmode which)
199 {
200 return seekoff(std::streamoff(pos), std::ios_base::beg, which);
201 }
202
203 // ***************************************************************************
204 // * ROCharArrayStreambuf class *
205 // ***************************************************************************
ROCharArrayStreambuf(const char * buf,std::size_t bufSize)206 ROCharArrayStreambuf::ROCharArrayStreambuf(const char* buf, std::size_t bufSize)
207 : CharArrayStreambuf(const_cast<char*>(buf), bufSize)
208 { }
209
data() const210 const char* ROCharArrayStreambuf::data() const
211 {
212 return const_cast<const char*>(CharArrayStreambuf::data());
213 }
214
overflow(int c)215 int ROCharArrayStreambuf::overflow(int c)
216 {
217 return traits::eof(); // indicate failure
218 }
219
xsputn(const char * s,std::streamsize n)220 std::streamsize ROCharArrayStreambuf::xsputn(const char* s, std::streamsize n)
221 {
222 return 0; // number of chars written
223 }
224
225 // ***************************************************************************
226 // * CharArrayIStream class *
227 // ***************************************************************************
228
CharArrayIStream(const char * buf,std::size_t bufSize)229 CharArrayIStream::CharArrayIStream(const char* buf, std::size_t bufSize)
230 : std::istream(nullptr),
231 _streamBuf(buf, bufSize)
232 {
233 // Associate _streamBuf to 'this' and clear the error state flags
234 rdbuf(&_streamBuf);
235 }
236
data() const237 const char* CharArrayIStream::data() const
238 {
239 return _streamBuf.data();
240 }
241
size() const242 std::size_t CharArrayIStream::size() const
243 {
244 return _streamBuf.size();
245 }
246
247 // ***************************************************************************
248 // * CharArrayOStream class *
249 // ***************************************************************************
250
CharArrayOStream(char * buf,std::size_t bufSize)251 CharArrayOStream::CharArrayOStream(char* buf, std::size_t bufSize)
252 : std::ostream(nullptr),
253 _streamBuf(buf, bufSize)
254 {
255 // Associate _streamBuf to 'this' and clear the error state flags
256 rdbuf(&_streamBuf);
257 }
258
data() const259 char* CharArrayOStream::data() const
260 {
261 return _streamBuf.data();
262 }
263
size() const264 std::size_t CharArrayOStream::size() const
265 {
266 return _streamBuf.size();
267 }
268
269 // ***************************************************************************
270 // * CharArrayIOStream class *
271 // ***************************************************************************
272
CharArrayIOStream(char * buf,std::size_t bufSize)273 CharArrayIOStream::CharArrayIOStream(char* buf, std::size_t bufSize)
274 : std::iostream(nullptr),
275 _streamBuf(buf, bufSize)
276 {
277 // Associate _streamBuf to 'this' and clear the error state flags
278 rdbuf(&_streamBuf);
279 }
280
data() const281 char* CharArrayIOStream::data() const
282 {
283 return _streamBuf.data();
284 }
285
size() const286 std::size_t CharArrayIOStream::size() const
287 {
288 return _streamBuf.size();
289 }
290
291 } // of namespace simgear
292