зеркало из https://github.com/microsoft/STL.git
324 строки
13 KiB
C++
324 строки
13 KiB
C++
// wbuffer -- stream buffer for code conversions
|
|
|
|
// Copyright (c) Microsoft Corporation.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
|
|
#pragma once
|
|
#ifndef _CVT_WBUFFER_
|
|
#define _CVT_WBUFFER_
|
|
#include <yvals_core.h>
|
|
#if _STL_COMPILER_PREPROCESSOR
|
|
#include <streambuf>
|
|
#include <xstring>
|
|
|
|
#pragma pack(push, _CRT_PACKING)
|
|
#pragma warning(push, _STL_WARNING_LEVEL)
|
|
#pragma warning(disable : _STL_DISABLED_WARNINGS)
|
|
_STL_DISABLE_CLANG_WARNINGS
|
|
#pragma push_macro("new")
|
|
#undef new
|
|
|
|
// Example: to convert from UCS to UTF-8 and write to cout
|
|
// wbuffer_convert<codecvt_utf8<wchar_t>> mybuf(cout.rdbuf()); // construct wide stream buffer object
|
|
// wostream mywcout(&mybuf); // construct wide ostream object
|
|
// mywcout << static_cast<wchar_t>(0x80); // writes 0xc2 0x80
|
|
|
|
|
|
namespace stdext {
|
|
namespace cvt {
|
|
|
|
// CLASS TEMPLATE wbuffer_convert
|
|
template <class _Codecvt, class _Elem = wchar_t, class _Traits = _STD char_traits<_Elem>>
|
|
class wbuffer_convert
|
|
: public _STD basic_streambuf<_Elem, _Traits> { // stream buffer associated with a codecvt facet
|
|
enum _Mode { _Unused, _Wrote, _Need, _Got, _Eof };
|
|
enum { _STRING_INC = 8 };
|
|
|
|
public:
|
|
using _Mysb = _STD streambuf;
|
|
using _Byte_traits = _STD char_traits<char>;
|
|
|
|
using int_type = typename _Traits::int_type;
|
|
using pos_type = typename _Traits::pos_type;
|
|
using off_type = typename _Traits::off_type;
|
|
using state_type = typename _Traits::state_type;
|
|
|
|
wbuffer_convert() : _Pcvt(new _Codecvt), _Mystrbuf(nullptr), _Status(_Unused), _Nback(0) {
|
|
static state_type _State0;
|
|
|
|
_State = _State0;
|
|
_Loc = _STD locale(_Loc, _Pcvt);
|
|
}
|
|
|
|
explicit wbuffer_convert(_Mysb* _Strbuf)
|
|
: _Pcvt(new _Codecvt), _Mystrbuf(_Strbuf), _Status(_Unused), _Nback(0) {
|
|
static state_type _State0;
|
|
|
|
_State = _State0;
|
|
_Loc = _STD locale(_Loc, _Pcvt);
|
|
}
|
|
|
|
wbuffer_convert(_Mysb* _Strbuf, _Codecvt* _Pcvt_arg)
|
|
: _Pcvt(_Pcvt_arg), _Mystrbuf(_Strbuf), _Status(_Unused), _Nback(0) {
|
|
static state_type _State0;
|
|
|
|
_State = _State0;
|
|
_Loc = _STD locale(_Loc, _Pcvt);
|
|
}
|
|
|
|
wbuffer_convert(_Mysb* _Strbuf, _Codecvt* _Pcvt_arg, state_type _State_arg)
|
|
: _Pcvt(_Pcvt_arg), _Mystrbuf(_Strbuf), _Status(_Unused), _Nback(0) {
|
|
_State = _State_arg;
|
|
_Loc = _STD locale(_Loc, _Pcvt);
|
|
}
|
|
|
|
virtual ~wbuffer_convert() noexcept {
|
|
while (_Status == _Wrote) { // put any trailing homing shift
|
|
if (_Str.size() < _STRING_INC) {
|
|
_Str.assign(_STRING_INC, '\0');
|
|
}
|
|
|
|
char* _Buf = &_Str[0];
|
|
char* _Dest;
|
|
switch (
|
|
_Pcvt->unshift(_State, _Buf, _Buf + _Str.size(), _Dest)) { // test result of homing conversion
|
|
case _Codecvt::ok:
|
|
_Status = _Unused; // homed successfully
|
|
|
|
case _Codecvt::partial: // fall through
|
|
{ // put any generated bytes
|
|
ptrdiff_t _Count = _Dest - _Buf;
|
|
if (0 < _Count
|
|
&& _Byte_traits::eq_int_type(_Byte_traits::eof(),
|
|
static_cast<int_type>(_Mystrbuf->sputn(_Buf, static_cast<_STD streamsize>(_Count))))) {
|
|
return; // write failed
|
|
}
|
|
|
|
if (_Status == _Wrote && _Count == 0) {
|
|
_Str.append(_STRING_INC, '\0'); // try with more space
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case _Codecvt::noconv:
|
|
return; // nothing to do
|
|
|
|
default:
|
|
return; // conversion failed
|
|
}
|
|
}
|
|
}
|
|
|
|
_NODISCARD _Mysb* rdbuf() const { // return byte stream buffer pointer
|
|
return _Mystrbuf;
|
|
}
|
|
|
|
_Mysb* rdbuf(_Mysb* _Strbuf) { // set byte stream buffer pointer
|
|
_Mysb* _Oldstrbuf = _Mystrbuf;
|
|
_Mystrbuf = _Strbuf;
|
|
return _Oldstrbuf;
|
|
}
|
|
|
|
_NODISCARD state_type state() const {
|
|
return _State;
|
|
}
|
|
|
|
wbuffer_convert(const wbuffer_convert&) = delete;
|
|
wbuffer_convert& operator=(const wbuffer_convert&) = delete;
|
|
|
|
protected:
|
|
virtual int_type overflow(int_type _Meta = _Traits::eof()) { // put an element to stream
|
|
if (_Traits::eq_int_type(_Traits::eof(), _Meta)) {
|
|
return _Traits::not_eof(_Meta); // EOF, return success code
|
|
} else if (!_Mystrbuf || 0 < _Nback || (_Status != _Unused && _Status != _Wrote)) {
|
|
return _Traits::eof(); // no buffer or reading, fail
|
|
} else { // put using codecvt facet
|
|
const _Elem _Ch = _Traits::to_char_type(_Meta);
|
|
|
|
if (_Str.size() < _STRING_INC) {
|
|
_Str.assign(_STRING_INC, '\0');
|
|
}
|
|
|
|
for (_Status = _Wrote;;) {
|
|
char* _Buf = &_Str[0];
|
|
const _Elem* _Src;
|
|
char* _Dest;
|
|
switch (_Pcvt->out(_State, &_Ch, &_Ch + 1, _Src, _Buf, _Buf + _Str.size(),
|
|
_Dest)) { // test result of converting one element
|
|
case _Codecvt::partial:
|
|
case _Codecvt::ok: { // converted something, try to put it out
|
|
ptrdiff_t _Count = _Dest - _Buf;
|
|
if (0 < _Count
|
|
&& _Byte_traits::eq_int_type(_Byte_traits::eof(),
|
|
static_cast<int_type>(
|
|
_Mystrbuf->sputn(_Buf, static_cast<_STD streamsize>(_Count))))) {
|
|
return _Traits::eof(); // write failed
|
|
}
|
|
|
|
if (_Src != &_Ch) {
|
|
return _Meta; // converted whole element
|
|
}
|
|
|
|
if (0 >= _Count) {
|
|
if (_Str.size() >= 4 * _STRING_INC) {
|
|
return _Traits::eof(); // conversion failed
|
|
}
|
|
|
|
_Str.append(_STRING_INC, '\0'); // try with more space
|
|
}
|
|
break;
|
|
}
|
|
|
|
case _Codecvt::noconv:
|
|
if (_Traits::eq_int_type(_Traits::eof(),
|
|
static_cast<int_type>(_Mystrbuf->sputn(reinterpret_cast<const char*>(&_Ch),
|
|
static_cast<_STD streamsize>(sizeof(_Elem)))))) {
|
|
return _Traits::eof();
|
|
} else {
|
|
return _Meta; // put native byte order
|
|
}
|
|
|
|
default:
|
|
return _Traits::eof(); // conversion failed
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
virtual int_type pbackfail(int_type _Meta = _Traits::eof()) { // put an element back to stream
|
|
if (sizeof(_Myback) / sizeof(_Myback[0]) <= _Nback || _Status == _Wrote) {
|
|
return _Traits::eof(); // nowhere to put back
|
|
} else { // enough room, put it back
|
|
if (!_Traits::eq_int_type(_Traits::eof(), _Meta)) {
|
|
_Myback[_Nback] = _Traits::to_char_type(_Meta);
|
|
}
|
|
|
|
++_Nback;
|
|
if (_Status == _Unused) {
|
|
_Status = _Got;
|
|
}
|
|
|
|
return _Meta;
|
|
}
|
|
}
|
|
|
|
virtual int_type underflow() { // get an element from stream, but don't point past it
|
|
int_type _Meta;
|
|
|
|
if (0 >= _Nback) {
|
|
if (_Traits::eq_int_type(_Traits::eof(), _Meta = _Get_elem())) {
|
|
return _Meta; // _Get_elem failed, return EOF
|
|
}
|
|
|
|
_Myback[_Nback++] = _Traits::to_char_type(_Meta);
|
|
}
|
|
|
|
return _Traits::to_int_type(_Myback[_Nback - 1]);
|
|
}
|
|
|
|
#pragma warning(push)
|
|
#pragma warning(disable : 6385) // Reading invalid data from 'this->_Myback':
|
|
// the readable size is 'X' bytes, but 'Y' bytes may be read.
|
|
virtual int_type uflow() { // get an element from stream, point past it
|
|
int_type _Meta;
|
|
|
|
if (0 >= _Nback) {
|
|
if (_Traits::eq_int_type(_Traits::eof(), _Meta = _Get_elem())) {
|
|
return _Meta; // _Get_elem failed, return EOF
|
|
}
|
|
|
|
_Myback[_Nback++] = _Traits::to_char_type(_Meta);
|
|
}
|
|
|
|
return _Traits::to_int_type(_Myback[--_Nback]);
|
|
}
|
|
#pragma warning(pop)
|
|
|
|
virtual pos_type seekoff(off_type, _STD ios_base::seekdir,
|
|
_STD ios_base::openmode = static_cast<_STD ios_base::openmode>(
|
|
_STD ios_base::in | _STD ios_base::out)) { // change position by _Off
|
|
return pos_type(-1); // always fail
|
|
}
|
|
|
|
virtual pos_type seekpos(pos_type,
|
|
_STD ios_base::openmode = static_cast<_STD ios_base::openmode>(
|
|
_STD ios_base::in | _STD ios_base::out)) { // change position to _Pos
|
|
return pos_type(-1); // always fail
|
|
}
|
|
|
|
private:
|
|
int_type _Get_elem() { // compose an element from byte stream buffer
|
|
if (_Mystrbuf && _Status != _Wrote) {
|
|
// got buffer, haven't written, try to compose an element
|
|
if (_Status != _Eof) {
|
|
if (_Str.size() == 0) {
|
|
_Status = _Need;
|
|
} else {
|
|
_Status = _Got;
|
|
}
|
|
}
|
|
|
|
while (_Status != _Eof) { // get using codecvt facet
|
|
char* _Buf = &_Str[0];
|
|
_Elem _Ch;
|
|
_Elem* _Dest;
|
|
const char* _Src;
|
|
int _Meta;
|
|
|
|
if (_Status == _Need) {
|
|
if (_Byte_traits::eq_int_type(_Byte_traits::eof(), _Meta = _Mystrbuf->sbumpc())) {
|
|
_Status = _Eof;
|
|
} else {
|
|
_Str.push_back(_Byte_traits::to_char_type(_Meta));
|
|
}
|
|
}
|
|
|
|
switch (_Pcvt->in(_State, _Buf, _Buf + _Str.size(), _Src, &_Ch, &_Ch + 1,
|
|
_Dest)) { // test result of converting one element
|
|
case _Codecvt::partial:
|
|
case _Codecvt::ok:
|
|
_Str.erase(0, static_cast<size_t>(_Src - _Buf)); // discard any used input
|
|
if (_Dest != &_Ch) {
|
|
return _Traits::to_int_type(_Ch);
|
|
}
|
|
|
|
break;
|
|
|
|
case _Codecvt::noconv:
|
|
if (_Str.size() < sizeof(_Elem)) {
|
|
break; // no conversion, but need more chars
|
|
}
|
|
|
|
_CSTD memcpy(&_Ch, _Buf, sizeof(_Elem)); // copy raw bytes to element
|
|
_Str.erase(0, sizeof(_Elem));
|
|
return _Traits::to_int_type(_Ch); // return result
|
|
|
|
default:
|
|
_Status = _Eof; // conversion failed
|
|
}
|
|
}
|
|
}
|
|
|
|
return _Traits::eof();
|
|
}
|
|
|
|
state_type _State; // code conversion state
|
|
_Codecvt* _Pcvt; // the codecvt facet
|
|
_Mysb* _Mystrbuf; // pointer to stream buffer
|
|
_Mode _Status; // buffer read/write status
|
|
size_t _Nback; // number of elements in putback buffer
|
|
_Elem _Myback[8]; // putback buffer
|
|
_STD string _Str; // unconsumed input bytes
|
|
_STD locale _Loc; // manages reference to codecvt facet
|
|
};
|
|
} // namespace cvt
|
|
} // namespace stdext
|
|
#pragma pop_macro("new")
|
|
_STL_RESTORE_CLANG_WARNINGS
|
|
#pragma warning(pop)
|
|
#pragma pack(pop)
|
|
|
|
#endif // _STL_COMPILER_PREPROCESSOR
|
|
#endif // _CVT_WBUFFER_
|