Introduction
Proposed template class is lightweight and fast utf8 generator - output stream. The main purpose of the stream is to provide an easy way to generate XML/HTML "on the fly".
Example:
html::viewer hv;
utf8::oxstream out;
out << "<HTML>"
<< "<TITLE>" << L"Hello world!" << "</TITLE>"
<< "<BODY>" << L"Hello world!" << "</BODY>"
<< "<HTML>";
hv.show(out.data());
Implementation
Main class is a template declared as
template <class T, bool X> class ostream_t : public T {}
Where T is a class - implementation of output (write) buffer.
And X is a boolean parameter. If it is true
then stream will do conversion of characters having special XML/HTML meaning, e.g. '<','>', etc.
Class T is an output buffer and shall provide implementation of two write methods.
void push(unsigned char c);
void push(const unsigned char *pc, size_t sz);
For details of implementation see source of byte_buffer class below.
Source Code
Source code is pretty compact and you are free to grab it from here and paste anywhere you want:
namespace aux
{
class byte_buffer
{
unsigned char* _body;
size_t _allocated;
size_t _size;
unsigned char *reserve(size_t size)
{
size_t newsize = _size + size;
if( newsize > _allocated )
{
_allocated *= 2;
if(_allocated < newsize)
_allocated = newsize;
unsigned char *newbody = new unsigned char[_allocated];
memcpy(newbody,_body,_size);
delete[] _body;
_body = newbody;
}
return _body + _size;
}
public:
byte_buffer():_size(0)
{ _body = new unsigned char[_allocated = 256]; }
~byte_buffer() { delete[] _body; }
const unsigned char * data() {
if(_size == _allocated) reserve(1);
_body[_size] = '\0'; return _body;
}
size_t length() const { return _size; }
void push(unsigned char c) { *reserve(1) = c; ++_size; }
void push(const unsigned char *pc, size_t sz)
{ memcpy(reserve(sz),pc,sz); _size += sz; }
};
}
namespace utf8
{
template <class T, bool X = true>
class ostream_t : public T
{
public:
ostream_t()
{
static unsigned char BOM[] = { 0xEF, 0xBB, 0xBF };
T::push(BOM, sizeof(BOM));
}
ostream_t& operator << (const char* str)
{
T::push((const unsigned char*)str,strlen(str)); return *this;
}
ostream_t& operator << (const wchar_t* wstr)
{
const wchar_t *pc = wstr;
for(unsigned int c = *pc; c ; c = *(++pc))
{
if(X)
switch(c)
{
case '<': *this << "<"; continue;
case '>': *this << ">"; continue;
case '&': *this << "&"; continue;
case '"': *this << """; continue;
case '\'': *this << "'"; continue;
}
if (c < (1 << 7)) {
T::push (c);
} else if (c < (1 << 11)) {
T::push ((c >> 6) | 0xc0);
T::push ((c & 0x3f) | 0x80);
} else if (c < (1 << 16)) {
T::push ((c >> 12) | 0xe0);
T::push (((c >> 6) & 0x3f) | 0x80);
T::push ((c & 0x3f) | 0x80);
} else if (c < (1 << 21)) {
T::push ((c >> 18) | 0xe0);
T::push (((c >> 12) & 0x3f) | 0x80);
T::push (((c >> 6) & 0x3f) | 0x80);
T::push ((c & 0x3f) | 0x80);
}
}
return *this;
}
};
// raw ASCII/UNICODE -> UTF8 converter
typedef ostream_t<aux::byte_buffer,false> ostream;
// ASCII/UNICODE -> UTF8 converter with XML support
typedef ostream_t<aux::byte_buffer,true> oxstream;
}
We are using this code in HTMEngine SDK for creating HTML dialogs and popup windows. Hope it might be used in other places where you need dynamic XML/HTML creation.