///////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2008-2012 Artyom Beilis (Tonkikh) <artyomtnk@yahoo.com>
//
// See accompanying file COPYING.TXT file for licensing details.
//
///////////////////////////////////////////////////////////////////////////////
#ifndef CPPCMS_STEAL_BUF_H
#define CPPCMS_STEAL_BUF_H
#include <streambuf>
#include <ostream>
#include <stdio.h>
#include <string>
#include <string.h>
#include <stdlib.h>
namespace cppcms {
namespace util {
///
/// Simple std::streambuf to create input from [char const *,char const *) range
///
/// \ver{v1_2}
class const_char_buf : public std::streambuf {
public:
///
/// Create Empty buffer
///
const_char_buf()
{
range(0,0);
}
///
/// Create a buffer from a range
///
const_char_buf(char const *begin,char const *end)
{
range(begin,end);
}
///
/// Define the range for existing buffer, pointer is reset to begin
///
void range(char const *cbegin,char const *cend)
{
char *begin = const_cast<char*>(cbegin);
char *end = const_cast<char*>(cend);
setg(begin,begin,end);
}
///
/// Begin of range
///
char const *begin() const
{
return eback();
}
///
/// End of range
///
char const *end() const
{
return egptr();
}
};
///
/// Simple std::istream implementation for range of [char const *,char const *)
///
/// \ver{v1_2}
class const_char_istream : public std::istream {
public:
///
/// Create new empty stream
///
const_char_istream() : std::istream(0)
{
init(&buf_);
}
///
/// Create stream initialized with range [begin,end)
///
const_char_istream(char const *begin,char const *end) : std::istream(0)
{
buf_.range(begin,end);
init(&buf_);
}
///
/// Get begin of the range
///
char const *begin() const
{
return buf_.begin();
}
///
/// Get end of the range
///
char const *end() const
{
return buf_.end();
}
///
/// Set range, resets pointer to start and clears flags
///
void range(char const *begin,char const *end)
{
buf_.range(begin,end);
clear();
}
private:
const_char_buf buf_;
};
///
/// \brief Very simple output stream buffer that uses stack for small chunks of
/// text and then allocates memory of the default buffer is too small.
///
/// It is something like std::stringbuf with small string optimization, it also
/// allows to access the memory without actually creating the string itself.
///
/// The template parameter defines how many characters should be allocated
/// on stack by default before heap is used.
///
template<size_t OnStackSize = 128>
class stackbuf : public std::streambuf {
stackbuf(stackbuf const &);
void operator=(stackbuf const &);
public:
///
/// get the pointer to the beginning of the output buffer
///
char *begin()
{
return pbase();
}
///
/// get the pointer to the end of the output buffer
///
char *end()
{
return pptr();
}
///
/// get the 0 terminated string from the buffer.
///
char *c_str()
{
*pptr() = 0;
return begin();
}
///
/// get the std::string from the buffer.
///
std::string str()
{
return std::string(begin(),size_t(end()-begin()));
}
stackbuf()
{
init();
}
~stackbuf()
{
free(on_heap_);
}
protected:
int overflow(int c)
{
size_t current_size;
size_t new_size;
if(pbase() == on_stack_) {
current_size = OnStackSize;
new_size = OnStackSize * 2;
on_heap_ = (char *)malloc(new_size + 1);
if(!on_heap_)
throw std::bad_alloc();
memcpy(on_heap_,on_stack_,current_size);
}
else {
current_size = pptr() - pbase();
new_size = current_size * 2;
char *new_ptr = (char *)realloc(on_heap_,new_size + 1);
if(!new_ptr)
throw std::bad_alloc();
on_heap_ = new_ptr;
}
setp(on_heap_,on_heap_ + new_size);
pbump(current_size);
if(c!=EOF)
sputc(c);
return 0;
}
private:
void init()
{
on_heap_ = 0;
setp(on_stack_,on_stack_+OnStackSize);
}
char *on_heap_;
char on_stack_[OnStackSize + 1];
};
///
/// \brief This is a special buffer that allows to "steal" some chunk
/// of text from the output stream.
///
/// It does this by replacing stream's streambuf object with temporary
/// stream buffer that records all data written to the stream and then
/// returns the original buffer upon call to release() member function
/// it steal_buffer destruction.
///
/// The \a Size parameter defines the default chunk of memory allocated
/// on the stack before heap is used.
///
template<size_t Size = 128>
class steal_buffer : public stackbuf<Size> {
public:
///
/// Create the buffer and "Steal" the buffer from \a out
///
steal_buffer(std::ostream &out)
{
stolen_ = 0;
stream_ = 0;
steal(out);
}
///
/// Create an empty buffer
///
steal_buffer()
{
stolen_ = 0;
stream_ = 0;
}
///
/// Steal the buffer from \a out
///
void steal(std::ostream &out)
{
release();
stolen_ = out.rdbuf(this);
stream_ = &out;
}
///
/// Release the "stolen" buffer back, now the buffer contains all
/// the data that was recorded.
///
void release()
{
if(stream_ && stolen_) {
stream_->rdbuf(stolen_);
}
stream_ = 0;
stolen_ = 0;
}
~steal_buffer()
{
release();
}
private:
std::streambuf *stolen_;
std::ostream *stream_;
};
///
/// \brief Fast output stream object.
///
/// This is a special ostream that uses stack in order
/// to receive the data and faster then std::stringstream for
/// small chunks, also it is not limited for any particular size.
///
template<size_t Size = 128>
class stackstream : public std::ostream {
public:
///
/// Create a new stackstream
///
stackstream() : std::ostream(0)
{
rdbuf(&buf_);
}
///
/// Get the pointer to the first character in the range
///
char *begin()
{
return buf_.begin();
}
///
/// Get the pointer to the one past last character in the range
///
char *end()
{
return buf_.end();
}
///
/// Get a NUL terminated recorded string
///
char *c_str()
{
return buf_.c_str();
}
///
/// Get a recorded string
///
std::string str()
{
return buf_.str();
}
private:
stackbuf<Size> buf_;
};
template<typename Filter,int BufferSize=128>
class filterbuf : public std::streambuf {
public:
filterbuf() : output_(0), output_stream_(0)
{
setp(buffer_,buffer_+BufferSize);
}
filterbuf(std::ostream &out) : output_(0), output_stream_(0)
{
setp(buffer_,buffer_+BufferSize);
steal(out);
}
~filterbuf()
{
try {
release();
}
catch(...) {}
}
void steal(std::ostream &out)
{
release();
output_stream_ = &out;
output_ = out.rdbuf(this);
}
int release()
{
int r=0;
if(output_stream_) {
if(write()!=0)
r=-1;
output_stream_->rdbuf(output_);
output_=0;
output_stream_=0;
}
return r;
}
protected:
int overflow(int c)
{
if(write()!=0)
return -1;
if(c!=EOF) {
*pptr()=c;
pbump(1);
}
return 0;
}
private:
int write()
{
if(static_cast<Filter*>(this)->convert(pbase(),pptr(),output_)!=0) {
output_stream_->setstate(std::ios_base::failbit);
return -1;
}
setp(buffer_,buffer_ + BufferSize);
return 0;
}
char buffer_[BufferSize];
std::streambuf *output_;
std::ostream *output_stream_;
};
template<typename Filter>
class filterbuf<Filter,0> : public std::streambuf {
public:
filterbuf() : output_(0), output_stream_(0)
{
}
filterbuf(std::ostream &out) : output_(0), output_stream_(0)
{
steal(out);
}
~filterbuf()
{
release();
}
void steal(std::ostream &out)
{
release();
output_stream_ = &out;
output_ = out.rdbuf(this);
}
int release()
{
int r=0;
if(output_stream_) {
output_stream_->rdbuf(output_);
output_=0;
output_stream_=0;
}
return r;
}
protected:
int overflow(int c)
{
if(c!=EOF) {
char tmp=c;
if(write(&tmp,&tmp+1)!=0)
return -1;
}
return 0;
}
std::streamsize xsputn(char const *s,std::streamsize size)
{
if(write(s,s+size)!=0)
return -1;
return size;
}
private:
int write(char const *begin,char const *end)
{
if(static_cast<Filter*>(this)->convert(begin,end,output_)!=0) {
output_stream_->setstate(std::ios_base::failbit);
return -1;
}
return 0;
}
std::streambuf *output_;
std::ostream *output_stream_;
};
} // util
} // cppcms
#endif