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