blob: d897c5e1b4cc62c96602101cd52cd9630661c043 [file] [log] [blame]
// 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_PARSER_H
#define MCPACK2PB_MCPACK_PARSER_H
#include <limits> // std::numeric_limits
#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 {
// Read bytes from ZeroCopyInputStream
class InputStream {
public:
InputStream(google::protobuf::io::ZeroCopyInputStream* stream)
: _good(true)
, _size(0)
, _data(NULL)
, _zc_stream(stream)
, _popped_bytes(0)
{}
~InputStream() { }
// Pop at-most n bytes from front side.
// Returns bytes popped.
size_t popn(size_t n);
// Cut off at-most n bytes from front side and copy to `out'.
// Returns bytes cut.
size_t cutn(void* out, size_t n);
template <typename T> size_t cut_packed_pod(T* packed_pod);
template <typename T> T cut_packed_pod();
// Cut off at-most n bytes from front side. If the data is stored in
// continuous memory, return the reference directly, otherwise copy
// the data into `aux' and return reference of `aux'.
// Returns a StringPiece referencing the cut-off data.
butil::StringPiece ref_cut(std::string* aux, size_t n);
// Peek at the first character. If the stream is empty, 0 is returned.
uint8_t peek1();
// Returns bytes popped and cut since creation of this stream.
size_t popped_bytes() const { return _popped_bytes; }
// Returns false if error occurred in other consuming functions.
bool good() const { return _good; }
// If the error prevents parsing from going on, call this method.
// This method is also called in other functions in this class.
void set_bad() { _good = false; }
private:
bool _good;
int _size;
const void* _data;
google::protobuf::io::ZeroCopyInputStream* _zc_stream;
size_t _popped_bytes;
};
class ObjectIterator;
class ArrayIterator;
class ISOArrayIterator;
// Represent a piece of unparsed(and unread) data of InputStream.
struct UnparsedValue {
UnparsedValue()
: _type(FIELD_UNKNOWN), _stream(NULL), _size(0) {}
UnparsedValue(FieldType type, InputStream* stream, size_t size)
: _type(type), _stream(stream), _size(size) {}
void set(FieldType type, InputStream* stream, size_t size) {
_type = type;
_stream = stream;
_size = size;
}
FieldType type() const { return _type; }
InputStream* stream() { return _stream; }
const InputStream* stream() const { return _stream; }
size_t size() const { return _size; }
// Convert to concrete value. These functions can only be called once!
ObjectIterator as_object();
ArrayIterator as_array();
ISOArrayIterator as_iso_array();
// `var' in following methods should be a description of the field being
// parsed, for better error reporting. It has nothing to do with the result.
int64_t as_int64(const char* var);
uint64_t as_uint64(const char* var);
int32_t as_int32(const char* var);
uint32_t as_uint32(const char* var);
bool as_bool(const char* var);
float as_float(const char* var);
double as_double(const char* var);
void as_string(std::string* out, const char* var);
std::string as_string(const char* var);
void as_binary(std::string* out, const char* var);
std::string as_binary(const char* var);
private:
friend class ObjectIterator;
friend class ArrayIterator;
void set_end() { _type = FIELD_UNKNOWN; }
FieldType _type;
InputStream* _stream;
size_t _size;
};
std::ostream& operator<<(std::ostream& os, const UnparsedValue& value);
// Remove the header of the wrapping object.
// Returns the value size.
size_t unbox(InputStream* stream);
// Iterator all fields in an object which should be created like this:
// ObjectIterator it(unparsed_value);
// for (; it != NULL; ++it) {
// std::cout << "name=" << it->name << " value=" << it->value << std::endl;
// }
class ObjectIterator {
public:
struct Field {
butil::StringPiece name;
UnparsedValue value;
};
// Parse `n' bytes from `stream' as fields of an object.
ObjectIterator(InputStream* stream, size_t n) { init(stream, n); }
explicit ObjectIterator(UnparsedValue& value)
{ init(value.stream(), value.size()); }
~ObjectIterator() {}
Field* operator->() { return &_current_field; }
Field& operator*() { return _current_field; }
void operator++();
operator void*() const { return (void*)_current_field.value.type(); }
// Number of fields in the object.
uint32_t field_count() const { return _field_count; }
private:
void init(InputStream* stream, size_t n);
void set_bad() {
set_end();
_stream->set_bad();
}
void set_end() { _current_field.value._type = FIELD_UNKNOWN; }
size_t left_size() const
{ return _expected_popped_end - _expected_popped_bytes; }
Field _current_field;
uint32_t _field_count;
std::string _name_backup_string;
InputStream* _stream;
size_t _expected_popped_bytes;
size_t _expected_popped_end;
};
// Iterator all items in a (mcpack) array which should be created like this:
// ArrayIterator it(unparsed_value);
// for (; it != NULL; ++it) {
// std::cout << " item=" << *it << std::endl;
// }
class ArrayIterator {
public:
typedef UnparsedValue Field;
ArrayIterator(InputStream* stream, size_t size) { init(stream, size); }
explicit ArrayIterator(UnparsedValue& value)
{ init(value.stream(), value.size()); }
~ArrayIterator() {}
Field* operator->() { return &_current_field; }
Field& operator*() { return _current_field; }
void operator++();
operator void*() const { return (void*)_current_field.type(); }
// Number of items in the array.
uint32_t item_count() const { return _item_count; }
private:
void init(InputStream* stream, size_t n);
void set_bad() {
set_end();
_stream->set_bad();
}
void set_end() { _current_field._type = FIELD_UNKNOWN; }
size_t left_size() const
{ return _expected_popped_end - _expected_popped_bytes; }
Field _current_field;
uint32_t _item_count;
InputStream* _stream;
size_t _expected_popped_bytes;
size_t _expected_popped_end;
};
// Iterator all items in an isomorphic array which should be created like this:
// ISOArrayIterator it(unparsed_value);
class ISOArrayIterator {
public:
ISOArrayIterator(InputStream* stream, size_t size) { init(stream, size); }
explicit ISOArrayIterator(UnparsedValue& value)
{ init(value.stream(), value.size()); }
~ISOArrayIterator() {}
void operator++();
operator void*() const { return (void*)(uintptr_t)_item_type; }
template <typename T> T as_integer() const;
template <typename T> T as_fp() const;
int64_t as_int64() const { return as_integer<int64_t>(); }
uint64_t as_uint64() const { return as_integer<uint64_t>(); }
int32_t as_int32() const { return as_integer<int32_t>(); }
uint32_t as_uint32() const { return as_integer<uint32_t>(); }
bool as_bool() const { return as_integer<bool>(); }
float as_float() const { return as_fp<float>(); }
double as_double() const { return as_fp<double>(); }
PrimitiveFieldType item_type() const { return _item_type; }
// Number of items in the array.
uint32_t item_count() const { return _item_count; }
private:
void init(InputStream* stream, size_t n);
void set_bad() {
set_end();
_stream->set_bad();
}
void set_end() { _item_type = (PrimitiveFieldType)0; }
int _buf_index;
int _buf_count;
InputStream* _stream;
PrimitiveFieldType _item_type;
uint32_t _item_size;
uint32_t _item_count;
uint32_t _left_item_count;
char _item_buf[1024];
};
} // namespace mcpack2pb
#include "mcpack2pb/parser-inl.h"
#endif // MCPACK2PB_MCPACK_PARSER_H