blob: 8a826e59ed911585ee3dc3a7ccaa2a622d1a10f2 [file] [log] [blame]
// Copyright 2010-2015, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Provides CodeGenByteArrayStream class which generates C/C++ source code
// to define a byte array as a C string literal.
//
// Usage:
// ofstream ofs("output.cc");
// CodeGenByteArrayOutputStream codegen_stream(&ofs,
// codegenstream::NOT_OWN_STREAM);
// codegen_stream.OpenVarDef("MyVar");
// codegen_stream.put(single_byte_data);
// codegen_stream.write(large_data, large_data_size);
// codegen_stream.CloseVarDef();
#ifndef MOZC_BASE_CODEGEN_BYTEARRAY_STREAM_H_
#define MOZC_BASE_CODEGEN_BYTEARRAY_STREAM_H_
#include <algorithm>
#include <ostream>
#include <streambuf>
#include <string>
#include "base/port.h"
#include "base/scoped_ptr.h"
#ifdef OS_ANDROID
// This is used only for code generation, so shouldn't be used from android
// platform.
#error "base/codegen_bytearray_stream.h shouldn't be used from android platform."
#endif
#ifdef OS_WIN
// Visual C++ does not support string literals longer than 65535 characters
// so integer arrays (e.g. arrays of uint64) are used to represent byte arrays
// on Windows.
//
// The generated code looks like:
// const uint64 kVAR_data_wordtype[] = {
// 0x0123456789ABCDEF, ...
// };
// const char * const kVAR_data =
// reinterpret_cast<const char *>(kVAR_data_wordtype);
// const size_t kVAR_size = 123;
//
// This implementation works well with other toolchains, too, but we use
// string literals for other toolchains.
//
// The generated code with a string literal looks like:
// const char kVAR_data[] =
// "\\x12\\x34\\x56\\x78...";
// const size_t kVAR_size = 123;
#define MOZC_CODEGEN_BYTEARRAY_STREAM_USES_WORD_ARRAY
#endif // OS_WIN
#ifdef MOZC_CODEGEN_BYTEARRAY_STREAM_USES_WORD_ARRAY
#include <iomanip>
#endif // MOZC_CODEGEN_BYTEARRAY_STREAM_USES_WORD_ARRAY
#ifndef MOZC_CODEGEN_BYTEARRAY_STREAM_WORD_TYPE
#define MOZC_CODEGEN_BYTEARRAY_STREAM_WORD_TYPE uint64
#endif // MOZC_CODEGEN_BYTEARRAY_STREAM_WORD_TYPE
namespace mozc {
namespace codegenstream {
enum StreamOwner {
NOT_OWN_STREAM,
OWN_STREAM,
};
} // namespace codegenstream
class BasicCodeGenByteArrayStreamBuf : public std::streambuf {
public:
typedef char_traits<char> traits_type;
// Args:
// output_stream: The output stream to which generated code is written.
// own_output_stream: The object pointed to by |output_stream| will be
// destroyed if |own_output_stream| equals to |OWN_STREAM|.
BasicCodeGenByteArrayStreamBuf(std::ostream *output_stream,
codegenstream::StreamOwner own_output_stream)
: internal_output_buffer_size_(kDefaultInternalBufferSize),
internal_output_buffer_(new char[internal_output_buffer_size_]),
output_stream_(output_stream), own_output_stream_(own_output_stream),
is_open_(false), output_count_(0) {
this->setp(internal_output_buffer_.get(),
internal_output_buffer_.get() + internal_output_buffer_size_);
}
virtual ~BasicCodeGenByteArrayStreamBuf() {
CloseVarDef();
if (own_output_stream_ == codegenstream::OWN_STREAM) {
delete output_stream_;
}
}
// Writes the beginning of a variable definition.
bool OpenVarDef(const string &var_name_base) {
if (is_open_ || var_name_base.empty()) {
return false;
}
var_name_base_ = var_name_base;
#ifdef MOZC_CODEGEN_BYTEARRAY_STREAM_USES_WORD_ARRAY
*output_stream_ << "const "
<< AS_STRING(MOZC_CODEGEN_BYTEARRAY_STREAM_WORD_TYPE)
<< " k" << var_name_base_
<< "_data_wordtype[] = {\n";
output_stream_format_flags_ = output_stream_->flags();
// Set the output format in the form of "0x000012340000ABCD".
output_stream_->setf(ios_base::hex, ios_base::basefield);
output_stream_->setf(ios_base::uppercase);
output_stream_->setf(ios_base::right);
output_stream_format_fill_ = output_stream_->fill('0');
// Put the prefix "0x" by ourselves, otherwise it becomes "0X".
output_stream_->unsetf(ios_base::showbase);
word_buffer_ = 0;
#else
*output_stream_ << "const char k" << var_name_base_ << "_data[] =\n";
#endif
output_count_ = 0;
return is_open_ = !output_stream_->fail();
}
// Writes the end of a variable definition.
bool CloseVarDef() {
if (!is_open_) {
return false;
}
overflow();
#ifdef MOZC_CODEGEN_BYTEARRAY_STREAM_USES_WORD_ARRAY
if (output_count_ % sizeof word_buffer_ != 0) {
// Flush the remaining in |word_buffer_|.
WriteWordBuffer();
}
output_stream_->flags(output_stream_format_flags_);
output_stream_->fill(output_stream_format_fill_);
*output_stream_ << "};\n"
<< "const char * const k" << var_name_base_ << "_data = "
<< "reinterpret_cast<const char *>("
<< "k" << var_name_base_ << "_data_wordtype);\n";
#else
if (output_count_ == 0) {
*output_stream_ << "\"\"\n";
} else if (output_count_ % kNumOfBytesOnOneLine != 0) {
*output_stream_ << "\"\n";
}
*output_stream_ << ";\n";
#endif
*output_stream_ << "const size_t k" << var_name_base_ << "_size = "
<< output_count_ << ";\n";
is_open_ = false;
return !output_stream_->fail();
}
protected:
// Flushes.
virtual int sync() {
return (overflow() != traits_type::eof() &&
!output_stream_->flush().fail())
? 0 : -1;
}
// Writes a given character sequence. The implementation is expected to be
// more efficient than the one of the base class.
virtual std::streamsize xsputn(const char *s, std::streamsize n) {
if (n <= this->epptr() - this->pptr()) {
traits_type::copy(this->pptr(), s, n);
this->pbump(n);
return n;
} else {
overflow();
WriteBytes(reinterpret_cast<const char *>(s),
reinterpret_cast<const char *>(s + n));
return n;
}
}
// Writes the data body of a variable definition.
virtual int overflow(int c = traits_type::eof()) {
if (!is_open_) {
return traits_type::eof();
}
if (this->pbase() && this->pptr() && this->pbase() < this->pptr()) {
WriteBytes(reinterpret_cast<const char *>(this->pbase()),
reinterpret_cast<const char *>(this->pptr()));
}
if (!traits_type::eq_int_type(c, traits_type::eof())) {
char buf = static_cast<char>(c);
WriteBytes(reinterpret_cast<const char *>(&buf),
reinterpret_cast<const char *>(&buf + 1));
}
this->setp(internal_output_buffer_.get(),
internal_output_buffer_.get() + internal_output_buffer_size_);
return output_stream_->fail()
? traits_type::eof()
: (traits_type::eq_int_type(c, traits_type::eof())
? traits_type::not_eof(c) : c);
}
private:
static const size_t kDefaultInternalBufferSize = 4000 * 1024; // 4 mega chars
#ifdef MOZC_CODEGEN_BYTEARRAY_STREAM_USES_WORD_ARRAY
typedef MOZC_CODEGEN_BYTEARRAY_STREAM_WORD_TYPE WordType;
static const size_t kNumOfBytesOnOneLine = 4 * sizeof(WordType);
#else
static const size_t kNumOfBytesOnOneLine = 20;
#endif
// Converts a raw byte stream to C source code.
void WriteBytes(const char *begin, const char *end) {
#ifdef MOZC_CODEGEN_BYTEARRAY_STREAM_USES_WORD_ARRAY
char * const buf = reinterpret_cast<char *>(&word_buffer_);
const size_t kWordSize = sizeof word_buffer_;
while (begin < end) {
size_t output_length = min(static_cast<size_t>(end - begin),
kWordSize - output_count_ % kWordSize);
for (size_t i = 0; i < output_length; ++i) {
buf[output_count_ % kWordSize + i] = *begin++;
}
output_count_ += output_length;
if (output_count_ % kWordSize == 0) {
WriteWordBuffer();
*output_stream_ << (output_count_ % kNumOfBytesOnOneLine == 0
? ",\n" : ", ");
}
}
#else
static const char kHex[] = "0123456789ABCDEF";
while (begin < end) {
size_t bucket_size = min(static_cast<size_t>(end - begin),
kNumOfBytesOnOneLine
- output_count_ % kNumOfBytesOnOneLine);
if (output_count_ % kNumOfBytesOnOneLine == 0) {
*output_stream_ << '\"';
}
for (size_t i = 0; i < bucket_size; ++i) {
*output_stream_ << "\\x"
<< kHex[(*begin & 0xF0) >> 4]
<< kHex[(*begin & 0x0F) >> 0];
++begin;
}
output_count_ += bucket_size;
if (output_count_ % kNumOfBytesOnOneLine == 0) {
*output_stream_ << "\"\n";
}
}
#endif
}
#ifdef MOZC_CODEGEN_BYTEARRAY_STREAM_USES_WORD_ARRAY
void WriteWordBuffer() {
*output_stream_ << "0x" << setw(2 * sizeof word_buffer_) << word_buffer_;
word_buffer_ = 0;
}
#endif
size_t internal_output_buffer_size_;
scoped_ptr<char[]> internal_output_buffer_;
std::basic_ostream<char> *output_stream_;
codegenstream::StreamOwner own_output_stream_;
#ifdef MOZC_CODEGEN_BYTEARRAY_STREAM_USES_WORD_ARRAY
ios_base::fmtflags output_stream_format_flags_;
char output_stream_format_fill_;
#endif
bool is_open_;
string var_name_base_;
size_t output_count_;
#ifdef MOZC_CODEGEN_BYTEARRAY_STREAM_USES_WORD_ARRAY
WordType word_buffer_;
#endif
DISALLOW_COPY_AND_ASSIGN(BasicCodeGenByteArrayStreamBuf);
};
class CodeGenByteArrayOutputStream : public std::ostream {
public:
// Args:
// output_stream: The output stream to which generated code is written.
// own_output_stream: The object pointed to by |output_stream| will be
// destroyed if |own_output_stream| equals to |OWN_STREAM|.
CodeGenByteArrayOutputStream(
std::ostream *output_stream,
codegenstream::StreamOwner own_output_stream)
: std::ostream(NULL),
streambuf_(output_stream, own_output_stream) {
this->rdbuf(&streambuf_);
}
// Writes the beginning of a variable definition.
// A call to |OpenVarDef| must precede any output to the instance.
void OpenVarDef(const string &var_name_base) {
if (!streambuf_.OpenVarDef(var_name_base)) {
this->setstate(ios_base::failbit);
}
}
// Writes the end of a variable definition.
// An output to the instance after a call to |CloseVarDef| is not allowed
// unless |OpenVarDef| is called with a different variable name.
void CloseVarDef() {
if (!streambuf_.CloseVarDef()) {
this->setstate(ios_base::failbit);
}
}
private:
BasicCodeGenByteArrayStreamBuf streambuf_;
DISALLOW_COPY_AND_ASSIGN(CodeGenByteArrayOutputStream);
};
} // namespace mozc
#endif // MOZC_BASE_CODEGEN_BYTEARRAY_STREAM_H_