TextBuffer.h
1/*
2 * Copyright (C) 2004-2013 Marc Boris Duerner
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * As a special exception, you may use this file as part of a free
10 * software library without restriction. Specifically, if other files
11 * instantiate templates or use macros or inline functions from this
12 * file, or you compile this file and link it with other files to
13 * produce an executable, this file does not by itself cause the
14 * resulting executable to be covered by the GNU General Public
15 * License. This exception does not however invalidate any other
16 * reasons why the executable file might be covered by the GNU Library
17 * General Public License.
18 *
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Lesser General Public License for more details.
23 *
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with this library; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
27 */
28
29#ifndef Pt_TextBuffer_h
30#define Pt_TextBuffer_h
31
32#include <Pt/Api.h>
33#include <Pt/String.h>
34#include <Pt/TextCodec.h>
35#include <Pt/StreamBuffer.h>
36#include <Pt/ConversionError.h>
37
38namespace Pt {
39
52template <typename CharT, typename ByteT>
54{
55 public:
57 typedef ByteT extern_type;
58
60 typedef CharT intern_type;
61
63 typedef CharT char_type;
64
66 typedef typename std::char_traits<CharT> traits_type;
67
69 typedef typename traits_type::int_type int_type;
70
72 typedef typename traits_type::pos_type pos_type;
73
75 typedef typename traits_type::off_type off_type;
76
79
80 typedef typename CodecType::result CodecResult;
81 typedef MBState state_type;
82
83 public:
92 explicit BasicTextBuffer(std::basic_ios<extern_type>& target, CodecType* codec = 0)
93 : _iebufsize(0)
94 , _oebufsize(0)
95 , _codec(codec)
96 , _codecRefs( codec ? codec->refs() : 0 )
97 , _target(&target)
98 {
99 this->setg(0, 0, 0);
100 this->setp(0, 0);
101 }
102
112 : _iebufsize(0)
113 , _oebufsize(0)
114 , _codec(codec)
115 , _codecRefs( codec ? codec->refs() : 0 )
116 , _target(0)
117 {
118 this->setg(0, 0, 0);
119 this->setp(0, 0);
120 }
121
125 {
126 // if _codecRefs is greater than 0, the codec might have been
127 // destroyed before the text buffer, therefore we work with a
128 // separate refcount to mark owned codecs
129
130 if(_codecRefs == 0)
131 delete _codec;
132 }
133
137 { return _codec; }
138
145 {
146 _readState = state_type();
147 _writeState = state_type();
148
149 if(_codecRefs == 0)
150 delete _codec;
151
152 _codec = codec;
153 _codecRefs = codec ? codec->refs() : 0;
154 }
155
158 void attach(std::basic_ios<extern_type>& target)
159 {
160 _target = &target;
161 }
162
165 void detach()
166 {
167 _target = 0;
168 }
169
175 void reset()
176 {
177 discard();
178 detach();
179 }
180
186 void reset(std::basic_ios<extern_type>& target)
187 {
188 discard();
189 attach(target);
190 }
191
194 void discard()
195 {
196 _iebufsize = 0;
197 _oebufsize = 0;
198 this->setp(0, 0);
199 this->setg(0, 0, 0);
200
201 _readState = state_type();
202 _writeState = state_type();
203 }
204
209 std::streamsize import(std::streamsize size = 0)
210 {
211 if( ! this->gptr() )
212 {
213 this->setg(_ibuf, _ibuf, _ibuf);
214 }
215
216 std::streamsize n = 0;
217 std::basic_streambuf<extern_type>* rdbuf = _target ? _target->rdbuf()
218 : 0;
219
220 // special case: read available input
221 if(size == 0 && rdbuf)
222 size = rdbuf->in_avail();
223
224 // not more than input buffer size
225 const std::streamsize ebufavail = _ebufmax - _iebufsize;
226 size = ebufavail < size ? ebufavail : size;
227
228 if(size > 0 && rdbuf)
229 {
230 n = rdbuf->sgetn( _iebuf + _iebufsize, size );
231 _iebufsize += static_cast<int>(n);
232 if(n <= 0)
233 {
234 // rdbuf == 0 means "at end of input"
235 rdbuf = 0;
236 }
237 }
238
239 if( this->gptr() - this->eback() > _pbmax)
240 {
241 std::streamsize movelen = this->egptr() - this->gptr() + _pbmax;
242 std::char_traits<char_type>::move( _ibuf,
243 this->gptr() - _pbmax,
244 static_cast<std::size_t>(movelen));
245 this->setg(_ibuf, _ibuf + _pbmax, _ibuf + movelen);
246 }
247
248 typename CodecType::result r = decode();
249
250 // fail if partial conversion at the end of input
251 // rdbuf == 0 means "at end of input"
252 if( rdbuf == 0 && _iebufsize == 0 && r == CodecType::partial)
253 throw ConversionError("character encoding");
254
255 return n;
256 }
257
262 std::streamsize import(const extern_type* buf, std::streamsize size)
263 {
264 if( ! this->gptr() )
265 {
266 this->setg(_ibuf, _ibuf, _ibuf);
267 }
268
269 const std::streamsize bufavail = _ebufmax - _iebufsize;
270 size = bufavail < size ? bufavail : size;
271 if(size > 0)
272 {
273 std::char_traits<extern_type>::copy( _iebuf + _iebufsize, buf, static_cast<std::size_t>(size) );
274 _iebufsize += static_cast<int>(size);
275 }
276
277 if( this->gptr() - this->eback() > _pbmax)
278 {
279 std::streamsize movelen = this->egptr() - this->gptr() + _pbmax;
280 std::char_traits<char_type>::move( _ibuf,
281 this->gptr() - _pbmax,
282 static_cast<std::size_t>(movelen));
283 this->setg(_ibuf, _ibuf + _pbmax, _ibuf + movelen);
284 }
285
286 decode();
287
288 return size;
289 }
290
291 protected:
292 // inheritdoc
293 virtual int sync()
294 {
295 if( ! this->pptr() )
296 return 0;
297
298 // write the whole buffer to the underlying stream.
299 while( this->pptr() > this->pbase() )
300 {
301 if( this->overflow( traits_type::eof() ) == traits_type::eof() )
302 return -1;
303 }
304
305 // unshift the character sequence
306 if( _target && _target->rdbuf() && _codec )
307 {
308 typename CodecType::result res = CodecType::error;
309
310 do
311 {
312 extern_type* next = 0;
313 res = _codec->unshift(_writeState, _oebuf, _oebuf + _ebufmax, next);
314 if(res == CodecType::noconv)
315 break;
316
317 if(res == CodecType::error)
318 throw ConversionError("character encoding");
319
320 std::streamsize n = static_cast<std::streamsize>(next - _oebuf);
321 if( n != _target->rdbuf()->sputn(_oebuf, n) )
322 return -1;
323 }
324 while(res == CodecType::partial);
325 }
326
327 _oebufsize = 0;
328 this->setp(0, 0);
329 _writeState = state_type();
330
331 return 0;
332 }
333
334 // inheritdoc
335 virtual std::streamsize showmanyc()
336 {
337 //return _target && _target->rdbuf() ? _target->rdbuf()->in_avail()
338 // : -1;
339
340 // Return 0, because we can not predict how many characters
341 // can be decoded from the available external byte sequence.
342 // Also, if we returned a number > 0, the next call to underflow()
343 // must exactly read this number of bytes without blocking.
344
345 return _target && _target->rdbuf() ? 0 : -1;
346 }
347
348 // inheritdoc
349 virtual int_type overflow( int_type ch = traits_type::eof() )
350 {
351 if( ! _target || ! _target->rdbuf() )
352 return traits_type::eof();
353
354 if( ! this->pptr() )
355 {
356 this->setp( _obuf, _obuf + _obufmax );
357 }
358
359 while( this->pptr() > this->pbase() )
360 {
361 const char_type* fromBegin = _obuf;
362 const char_type* fromEnd = this->pptr();
363 const char_type* fromNext = fromBegin;
364 extern_type* toBegin = _oebuf + _oebufsize;
365 extern_type* toEnd = _oebuf + _ebufmax;
366 extern_type* toNext = toBegin;
367
368 typename CodecType::result res = CodecType::noconv;
369 if(_codec)
370 res = _codec->out(_writeState, fromBegin, fromEnd, fromNext, toBegin, toEnd, toNext);
371
372 if(res == CodecType::noconv)
373 {
374 std::size_t fromSize = fromEnd - fromBegin;
375 std::size_t toSize = toEnd - toBegin;
376 std::size_t size = toSize < fromSize ? toSize : fromSize;
377
378 this->copyChars( toBegin, fromBegin, size );
379
380 fromNext += size;
381 toNext += size;
382 }
383
384 _oebufsize += static_cast<int>(toNext - toBegin);
385 std::size_t leftover = fromEnd - fromNext;
386 if(leftover && fromNext > fromBegin)
387 {
388 std::char_traits<char_type>::move(_obuf, fromNext, leftover);
389 }
390
391 this->setp( _obuf, _obuf + _obufmax );
392 this->pbump( static_cast<int>(leftover) );
393
394 if(res == CodecType::error)
395 throw ConversionError("character encoding");
396
397 if(res == CodecType::partial && _oebufsize == 0)
398 break;
399
400 _oebufsize -= static_cast<int>(_target->rdbuf()->sputn(_oebuf, _oebufsize));
401 if(_oebufsize)
402 return traits_type::eof();
403 }
404
405 if( ! traits_type::eq_int_type(ch, traits_type::eof()) )
406 {
407 *( this->pptr() ) = traits_type::to_char_type(ch);
408 this->pbump(1);
409 }
410
411 return traits_type::not_eof(ch);
412 }
413
414 // inheritdoc
415 virtual int_type underflow()
416 {
417 if( this->gptr() < this->egptr() )
418 return traits_type::to_int_type( *this->gptr() );
419
420 import(_ebufmax);
421
422 return this->gptr() < this->egptr() ? traits_type::to_int_type( *this->gptr() )
423 : traits_type::eof();
424 }
425
426 private:
428 CodecResult decode()
429 {
430 const extern_type* fromBegin = _iebuf;
431 const extern_type* fromEnd = _iebuf + _iebufsize;
432 const extern_type* fromNext = fromBegin;
433 char_type* toBegin = this->egptr();
434 char_type* toEnd = _ibuf + _ibufmax;
435 char_type* toNext = toBegin;
436
437 typename CodecType::result r = CodecType::noconv;
438 if(_codec)
439 r = _codec->in(_readState, fromBegin, fromEnd, fromNext, toBegin, toEnd, toNext);
440
441 if(r == CodecType::noconv)
442 {
443 // copy characters and advance fromNext and toNext
444 int n =_iebufsize > _ibufmax ? _ibufmax : _iebufsize ;
445 this->copyChars(toBegin, fromBegin, n);
446 fromNext += n;
447 toNext += n;
448 }
449
450 std::streamsize consumed = fromNext - fromBegin;
451 if(consumed > 0)
452 {
453 _iebufsize -= static_cast<int>(consumed);
454 std::char_traits<extern_type>::move( _iebuf, _iebuf + consumed, _iebufsize);
455 }
456
457 std::streamsize generated = toNext - toBegin;
458 if(generated)
459 {
460 this->setg(this->eback(), // start of read buffer
461 this->gptr(), // gptr position
462 this->egptr() + generated ); // end of read buffer
463 }
464
465 if(r == CodecType::error)
466 throw ConversionError("character encoding");
467
468 return r;
469 }
470
472 template <typename T>
473 void copyChars(T* s1, const T* s2, std::size_t n)
474 {
475 std::char_traits<T>::copy(s1, s2, n);
476 }
477
479 template <typename A, typename B>
480 void copyChars(A* s1, const B* s2, std::size_t n)
481 {
482 while(n-- > 0)
483 {
484 *s1 = *s2;
485 ++s1;
486 ++s2;
487 }
488 }
489
491 template <typename A>
492 void copyChars(A* s1, const Char* s2, std::size_t n)
493 {
494 while(n-- > 0)
495 {
496 *s1 = static_cast<A>(s2->value());
497 ++s1;
498 ++s2;
499 }
500 }
501
502 private:
503 static const int _pbmax = 4;
504
505 static const int _ebufmax = 256;
506 extern_type _iebuf[_ebufmax];
507 int _iebufsize;
508 extern_type _oebuf[_ebufmax];
509 int _oebufsize;
510
511 static const int _ibufmax = 256;
512 intern_type _ibuf[_ibufmax];
513 static const int _obufmax = 256;
514 intern_type _obuf[_obufmax];
515
516 state_type _readState;
517 state_type _writeState;
518 CodecType* _codec;
519 std::size_t _codecRefs;
520 std::basic_ios<extern_type>* _target;
521};
522
535typedef BasicTextBuffer<Pt::Char, char> TextBuffer;
536
538//*/
539//class PT_API TextBuffer : public BasicTextBuffer<Pt::Char, char>
540//{
541// public:
542// typedef TextCodec<Pt::Char, char> Codec;
543//
544// public:
545// /** @brief Constructs a new TextBuffer
546// */
547// explicit TextBuffer(std::ios& buffer, Codec* codec = 0);
548//
549// explicit TextBuffer(Codec* codec = 0);
550//};
551
552} // namespace Pt
553
554#endif // Pt_TextBuffer_h
BasicStreamBuffer()
Default Constructor.
Definition StreamBuffer.h:139
traits_type::off_type off_type
Stream offset type.
Definition TextBuffer.h:75
BasicTextBuffer(std::basic_ios< extern_type > &target, CodecType *codec=0)
Construct with external device and codec.
Definition TextBuffer.h:92
~BasicTextBuffer()
Destructor.
Definition TextBuffer.h:124
void setCodec(CodecType *codec)
Sets the text codec.
Definition TextBuffer.h:144
void discard()
Definition TextBuffer.h:194
CharT intern_type
Internal character type.
Definition TextBuffer.h:60
traits_type::pos_type pos_type
Stream position type.
Definition TextBuffer.h:72
TextCodec< char_type, extern_type > CodecType
Codec type.
Definition TextBuffer.h:78
traits_type::int_type int_type
Integer type.
Definition TextBuffer.h:69
void attach(std::basic_ios< extern_type > &target)
Attach to external target.
Definition TextBuffer.h:158
std::char_traits< CharT > traits_type
Internal character traits.
Definition TextBuffer.h:66
CodecType * codec()
Definition TextBuffer.h:136
void detach()
Detach from external target.
Definition TextBuffer.h:165
CharT char_type
Internal character type.
Definition TextBuffer.h:63
void reset()
Resets the buffer and target.
Definition TextBuffer.h:175
BasicTextBuffer(CodecType *codec=0)
Construct with codec.
Definition TextBuffer.h:111
ByteT extern_type
External character type.
Definition TextBuffer.h:57
void reset(std::basic_ios< extern_type > &target)
Resets the buffer and target.
Definition TextBuffer.h:186
ConversionError(const std::string &msg)
Construct with error message.
Converts character sequences with different encodings.
Converts between character encodings.
Definition Api-TextCodec.h:44
result unshift(MBState &state, ByteT *to, ByteT *to_end, ByteT *&to_next) const
Unshifts a character sequence.
Core module.
Definition Allocator.h:33