290 lines
10 KiB
C++
290 lines
10 KiB
C++
// Licensed to the Apache Software Foundation (ASF) under one
|
|
// or more contributor license agreements. See the NOTICE file
|
|
// distributed with this work for additional information
|
|
// regarding copyright ownership. The ASF licenses this file
|
|
// to you under the Apache License, Version 2.0 (the
|
|
// "License"); you may not use this file except in compliance
|
|
// with the License. You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing,
|
|
// software distributed under the License is distributed on an
|
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
// KIND, either express or implied. See the License for the
|
|
// specific language governing permissions and limitations
|
|
// under the License.
|
|
|
|
// mcpack2pb - Make protobuf be front-end of mcpack/compack
|
|
|
|
// Date: Mon Oct 19 17:17:36 CST 2015
|
|
|
|
#ifndef MCPACK2PB_MCPACK_SERIALIZER_H
|
|
#define MCPACK2PB_MCPACK_SERIALIZER_H
|
|
|
|
#include <limits>
|
|
#include <vector>
|
|
#include <google/protobuf/io/zero_copy_stream.h>
|
|
#include "butil/logging.h"
|
|
#include "butil/strings/string_piece.h"
|
|
#include "mcpack2pb/field_type.h"
|
|
|
|
// CAUTION: Methods in this header is not intended to be public to users of
|
|
// brpc, and subject to change at any future time.
|
|
|
|
namespace mcpack2pb {
|
|
|
|
// Send bytes into ZeroCopyOutputStream
|
|
class OutputStream {
|
|
public:
|
|
class Area {
|
|
public:
|
|
Area() : _addr1(NULL)
|
|
, _addr2(NULL)
|
|
, _size1(0)
|
|
, _size2(0)
|
|
, _addional_area(NULL) {}
|
|
Area(const butil::LinkerInitialized&) {}
|
|
Area(const Area& rhs);
|
|
Area& operator=(const Area& rhs);
|
|
~Area();
|
|
void add(void* data, size_t n);
|
|
void assign(const void* data) const;
|
|
private:
|
|
void* _addr1;
|
|
void* _addr2;
|
|
unsigned _size1;
|
|
unsigned _size2;
|
|
std::vector<butil::StringPiece>* _addional_area;
|
|
};
|
|
|
|
// TODO(gejun): The zero-copy stream MUST return permanent memory blocks
|
|
// to support reserve(). E.g. StringOutputStream can't be used because
|
|
// the string inside invalidates previous memory blocks after resizing.
|
|
OutputStream(google::protobuf::io::ZeroCopyOutputStream* stream)
|
|
: _good(true)
|
|
, _fullsize(0)
|
|
, _size(0)
|
|
, _data(NULL)
|
|
, _zc_stream(stream)
|
|
, _pushed_bytes(0)
|
|
{}
|
|
|
|
~OutputStream() { done(); }
|
|
|
|
// Append n bytes.
|
|
void append(const void* data, int n);
|
|
|
|
// Append a pod.
|
|
template <typename T> void append_packed_pod(const T& packed_pod);
|
|
|
|
template <typename T> T* append_packed_pod();
|
|
|
|
// Append a byte.
|
|
void push_back(char c);
|
|
|
|
// If next n bytes in the zero-copy stream is continuous, consume it
|
|
// and return the begining address. NULL otherwise.
|
|
void* skip_continuous(int n);
|
|
|
|
// Consume n bytes from the stream and return an object representing the
|
|
// consumed area.
|
|
Area reserve(int n);
|
|
|
|
// Change data at the area. `data' must be as long as the reserved area.
|
|
void assign(const Area&, const void* data);
|
|
|
|
// Go back for n bytes.
|
|
void backup(int n);
|
|
|
|
// Returns bytes pushed and cut since creation of this stream.
|
|
size_t pushed_bytes() const { return _pushed_bytes; }
|
|
|
|
// Returns false if error occurred during serialization.
|
|
bool good() { return _good; }
|
|
|
|
void set_bad() { _good = false; }
|
|
|
|
// Optionally called to backup buffered bytes to zero-copy stream.
|
|
void done();
|
|
|
|
private:
|
|
bool _good;
|
|
int _fullsize;
|
|
int _size;
|
|
void* _data;
|
|
google::protobuf::io::ZeroCopyOutputStream* _zc_stream;
|
|
size_t _pushed_bytes;
|
|
};
|
|
|
|
// This class is different from butil::StringPiece that it only can be
|
|
// constructed from std::string or const char* which are both ended with
|
|
// zero, so that in later serialization of the name, we can simply copy one
|
|
// extra byte to end the name with zero(required by compack) rather than
|
|
// appending the zero in a separate function call which is less efficient.
|
|
class StringWrapper {
|
|
public:
|
|
StringWrapper(const std::string& str)
|
|
: _data(str.c_str()), _size(str.size()) {}
|
|
StringWrapper(const char* str) {
|
|
if (str) {
|
|
_data = str;
|
|
_size = strlen(str);
|
|
} else {
|
|
_data = "";
|
|
_size = 0;
|
|
}
|
|
}
|
|
~StringWrapper() { }
|
|
const char* data() const { return _data; }
|
|
size_t size() const { return _size; }
|
|
bool empty() const { return !_size; }
|
|
private:
|
|
const char* _data;
|
|
size_t _size;
|
|
};
|
|
inline std::ostream& operator<<(std::ostream& os, const StringWrapper& sw) {
|
|
return os << butil::StringPiece(sw.data(), sw.size());
|
|
}
|
|
|
|
class Serializer {
|
|
public:
|
|
// Serialize into `output'.
|
|
explicit Serializer(OutputStream* stream);
|
|
~Serializer();
|
|
|
|
bool good() const { return _stream->good(); }
|
|
void set_bad() { _stream->set_bad(); }
|
|
size_t pushed_bytes() const { return _stream->pushed_bytes(); }
|
|
|
|
// WARNING: Names to all methods cannot be longer that 254 bytes.
|
|
|
|
// Append a primitive type with name.
|
|
// Used between begin_object() and end_object().
|
|
void add_int8(const StringWrapper& name, int8_t value);
|
|
void add_int16(const StringWrapper& name, int16_t value);
|
|
void add_int32(const StringWrapper& name, int32_t value);
|
|
void add_int64(const StringWrapper& name, int64_t value);
|
|
void add_uint8(const StringWrapper& name, uint8_t value);
|
|
void add_uint16(const StringWrapper& name, uint16_t value);
|
|
void add_uint32(const StringWrapper& name, uint32_t value);
|
|
void add_uint64(const StringWrapper& name, uint64_t value);
|
|
void add_bool(const StringWrapper& name, bool value);
|
|
void add_float(const StringWrapper& name, float value);
|
|
void add_double(const StringWrapper& name, double value);
|
|
|
|
// Add a primitive type without name.
|
|
// Used between begin_xxx_array() and end_xxx_array().
|
|
void add_int8(int8_t value);
|
|
void add_int16(int16_t value);
|
|
void add_int32(int32_t value);
|
|
void add_int64(int64_t value);
|
|
void add_uint8(uint8_t value);
|
|
void add_uint16(uint16_t value);
|
|
void add_uint32(uint32_t value);
|
|
void add_uint64(uint64_t value);
|
|
void add_bool(bool value);
|
|
void add_float(float value);
|
|
void add_double(double value);
|
|
|
|
// Add multiple primitive types in one call.
|
|
void add_multiple_int8(const int8_t* values, size_t count);
|
|
void add_multiple_int8(const uint8_t* values, size_t count);
|
|
void add_multiple_int16(const int16_t* values, size_t count);
|
|
void add_multiple_int16(const uint16_t* values, size_t count);
|
|
void add_multiple_int32(const int32_t* values, size_t count);
|
|
void add_multiple_int32(const uint32_t* values, size_t count);
|
|
void add_multiple_int64(const int64_t* values, size_t count);
|
|
void add_multiple_int64(const uint64_t* values, size_t count);
|
|
void add_multiple_uint8(const uint8_t* values, size_t count);
|
|
void add_multiple_uint8(const int8_t* values, size_t count);
|
|
void add_multiple_uint16(const uint16_t* values, size_t count);
|
|
void add_multiple_uint16(const int16_t* values, size_t count);
|
|
void add_multiple_uint32(const uint32_t* values, size_t count);
|
|
void add_multiple_uint32(const int32_t* values, size_t count);
|
|
void add_multiple_uint64(const uint64_t* values, size_t count);
|
|
void add_multiple_uint64(const int64_t* values, size_t count);
|
|
void add_multiple_bool(const bool* values, size_t count);
|
|
void add_multiple_float(const float* values, size_t count);
|
|
void add_multiple_double(const double* values, size_t count);
|
|
|
|
// Append a string.
|
|
// The serialized value ends with 0 and the length counts 0.
|
|
void add_string(const StringWrapper& name, const StringWrapper& str);
|
|
void add_string(const StringWrapper& str);
|
|
|
|
// Append binary data.
|
|
void add_binary(const StringWrapper& name, const std::string& data);
|
|
void add_binary(const StringWrapper& name, const void* data, size_t n);
|
|
void add_binary(const std::string& data);
|
|
void add_binary(const void* data, size_t n);
|
|
|
|
// Append a null.
|
|
void add_null(const StringWrapper& name);
|
|
void add_null();
|
|
|
|
// Append an empty array.
|
|
void add_empty_array(const StringWrapper& name);
|
|
void add_empty_array();
|
|
|
|
// Begin/end an array.
|
|
// All items inside the array must be of same type. This is not a
|
|
// restriction of mcpack, but we want to align storage model with protobuf
|
|
// as much as possible.
|
|
// If too many levels of array/object reached, this function fails and
|
|
// leaves the output unchanged.
|
|
void begin_mcpack_array(const StringWrapper& name, FieldType item_type);
|
|
void begin_mcpack_array(FieldType item_type);
|
|
void begin_compack_array(const StringWrapper& name, FieldType item_type);
|
|
void begin_compack_array(FieldType item_type);
|
|
// End any kind of array.
|
|
void end_array();
|
|
|
|
// Begin/end an object.
|
|
// If too many levels of array/object reached, this function fails and
|
|
// leaves the output unchanged.
|
|
void begin_object(const StringWrapper& name);
|
|
void begin_object();
|
|
void end_object();
|
|
// TODO(gejun): move to begin_object
|
|
void end_object_iso();
|
|
|
|
public:
|
|
struct GroupInfo {
|
|
uint32_t item_count;
|
|
bool isomorphic;
|
|
uint8_t item_type;
|
|
uint8_t type;
|
|
uint8_t name_size;
|
|
size_t output_offset;
|
|
int pending_null_count;
|
|
OutputStream::Area head_area;
|
|
OutputStream::Area items_head_area;
|
|
|
|
void print(std::ostream&) const;
|
|
};
|
|
|
|
private:
|
|
DISALLOW_COPY_AND_ASSIGN(Serializer);
|
|
|
|
GroupInfo* push_group_info();
|
|
GroupInfo & peek_group_info();
|
|
void begin_object_internal(const StringWrapper& name);
|
|
void begin_object_internal();
|
|
void end_object_internal(bool objectisoarray);
|
|
void begin_array_internal(FieldType item_type, bool compack);
|
|
void begin_array_internal(const StringWrapper& name,
|
|
FieldType item_type, bool compack);
|
|
|
|
OutputStream* _stream;
|
|
int _ndepth;
|
|
GroupInfo _group_info_fast[15];
|
|
GroupInfo *_group_info_more;
|
|
};
|
|
|
|
} // namespace mcpack2pb
|
|
|
|
#include "mcpack2pb/serializer-inl.h"
|
|
|
|
#endif // MCPACK2PB_MCPACK_SERIALIZER_H
|