272 lines
6.1 KiB
C++
272 lines
6.1 KiB
C++
#ifndef _HPROSE_ED_HPP_
|
|
#define _HPROSE_ED_HPP_
|
|
|
|
#include <string>
|
|
#include <memory>
|
|
|
|
#include "hprose_tags.hpp"
|
|
|
|
class HproseReader {
|
|
public:
|
|
HproseReader(const unsigned char* ptr, size_t len) : begin(ptr), end(ptr + len), current(ptr) {}
|
|
HproseReader(const unsigned char* ptr, const unsigned char* end) : begin(ptr), end(end), current(ptr) {}
|
|
|
|
unsigned char ReadByte() {
|
|
if(current >= end)
|
|
return 0;
|
|
return *current++;
|
|
}
|
|
|
|
Tags ReadTag() {
|
|
return static_cast<Tags>(ReadByte());
|
|
}
|
|
|
|
bool CheckTag(Tags tag, bool skip) {
|
|
if(current >= end)
|
|
return false;
|
|
if(static_cast<Tags>(*current) != tag)
|
|
return false;
|
|
if(skip)
|
|
current++;
|
|
return true;
|
|
}
|
|
|
|
void Skip(uint32_t len) {
|
|
current += len;
|
|
if(current >= end)
|
|
current = end;
|
|
}
|
|
|
|
std::string ReadUntilAsString(Tags tag) {
|
|
auto start = current;
|
|
while(current != end && *current != static_cast<unsigned char>(tag))
|
|
current++;
|
|
if(current == end) {
|
|
return std::string(start, current);
|
|
} else {
|
|
return std::string(start, current++);
|
|
}
|
|
}
|
|
|
|
std::string ReadBytesAsString(size_t length) {
|
|
auto start = current;
|
|
current += length;
|
|
if(current > end)
|
|
current = end;
|
|
return std::string(start, current);
|
|
}
|
|
|
|
std::string ReadUTF8String(size_t length) {
|
|
auto start = current;
|
|
for(size_t i = 0; i < length && current < end; ++i) {
|
|
if(*current < 0x80) { // 0xxxxxxx
|
|
current++;
|
|
} else if(*current < 0xd0) { // 110xxxxx 10xxxxxx
|
|
if(current + 2 > end)
|
|
break;
|
|
current += 2;
|
|
} else if(*current < 0xf0) { // 1110xxxx 10xxxxxx 10xxxxxx
|
|
if(current + 3 > end)
|
|
break;
|
|
current += 3;
|
|
} else { // 11110xxx 10xxxxx 10xxxxxx 10xxxxxx
|
|
if(current + 4 > end)
|
|
break;
|
|
current += 4;
|
|
}
|
|
}
|
|
auto ptr = current;
|
|
CheckTag(Tags::Quote, true);
|
|
return std::string(start, ptr);
|
|
}
|
|
|
|
int64_t ReadInt64Raw(Tags end) {
|
|
int64_t value = 0;
|
|
auto b = ReadByte();
|
|
if(static_cast<Tags>(b) == end)
|
|
return value;
|
|
bool neg = false;
|
|
if(static_cast<Tags>(b) == Tags::Neg) {
|
|
neg = true;
|
|
b = ReadByte();
|
|
} else if(static_cast<Tags>(b) == Tags::Pos) {
|
|
b = ReadByte();
|
|
}
|
|
while(static_cast<Tags>(b) != end) {
|
|
value = value * 10 + int64_t(b - '0');
|
|
b = ReadByte();
|
|
}
|
|
return neg ? -value : value;
|
|
}
|
|
|
|
void SkipValue() {
|
|
SkipValue(ReadTag());
|
|
}
|
|
|
|
void SkipValue(Tags tag) {
|
|
switch(tag) {
|
|
case Tags::Call:
|
|
case Tags::Result:
|
|
case Tags::Error:
|
|
case Tags::End:
|
|
case Tags::Argument: {
|
|
current--;
|
|
return;
|
|
}
|
|
case Tags::Null:
|
|
case Tags::Empty:
|
|
case Tags::Num0:
|
|
case Tags::Num1:
|
|
case Tags::Num2:
|
|
case Tags::Num3:
|
|
case Tags::Num4:
|
|
case Tags::Num5:
|
|
case Tags::Num6:
|
|
case Tags::Num7:
|
|
case Tags::Num8:
|
|
case Tags::Num9:
|
|
case Tags::False:
|
|
case Tags::True:
|
|
case Tags::NaN: return;
|
|
case Tags::Infinity: {
|
|
ReadTag();
|
|
return;
|
|
}
|
|
case Tags::Integer:
|
|
case Tags::Long: {
|
|
ReadInt64Raw(Tags::Semicolon);
|
|
return;
|
|
}
|
|
case Tags::Double: {
|
|
ReadUntilAsString(Tags::Semicolon);
|
|
return;
|
|
}
|
|
case Tags::UTF8Char: {
|
|
ReadUTF8String(1);
|
|
return;
|
|
}
|
|
case Tags::String: {
|
|
ReadUTF8String(ReadInt64Raw(Tags::Quote));
|
|
return;
|
|
}
|
|
case Tags::Bytes: {
|
|
ReadBytesAsString(ReadInt64Raw(Tags::Quote));
|
|
return;
|
|
}
|
|
case Tags::List: {
|
|
int64_t count = ReadInt64Raw(Tags::Openbrace);
|
|
for(int64_t i = 0; i < count; ++i)
|
|
SkipValue();
|
|
ReadTag(); // Tags::Closebrace
|
|
return;
|
|
}
|
|
case Tags::Map: {
|
|
int64_t count = ReadInt64Raw(Tags::Openbrace) * 2;
|
|
for(int64_t i = 0; i < count; ++i)
|
|
SkipValue();
|
|
ReadTag(); // Tags::Closebrace
|
|
return;
|
|
}
|
|
case Tags::Date: {
|
|
Skip(8); // year-4 m-2 d-2
|
|
if(!CheckTag(Tags::Time, true))
|
|
return;
|
|
}
|
|
case Tags::Time: {
|
|
Skip(6); // h-2 m-2 s-2
|
|
if(CheckTag(Tags::Point, true)) {
|
|
// nano seconds 1-9digits
|
|
while(current < end && *current >= '0' && *current <= '9')
|
|
current++;
|
|
}
|
|
CheckTag(Tags::UTC, true);
|
|
return;
|
|
}
|
|
case Tags::GUID: {
|
|
Skip(38); // '"' + 36 bytes string + '"'
|
|
return;
|
|
}
|
|
case Tags::Class: {
|
|
SkipValue(Tags::String); // name
|
|
SkipValue(Tags::List); // list
|
|
if(!CheckTag(Tags::Object, true))
|
|
return;
|
|
}
|
|
case Tags::Object: {
|
|
SkipValue(); // index
|
|
if(CheckTag(Tags::Openbrace, true)) {
|
|
do {
|
|
SkipValue();
|
|
} while(!CheckTag(Tags::Closebrace, true));
|
|
}
|
|
}
|
|
default: {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void PushFieldRefs(std::vector<std::string> fields) { fieldRefs.emplace_back(std::move(fields)); }
|
|
const std::vector<std::string>& GetFieldRefs(int32_t idx) { return fieldRefs[idx]; }
|
|
|
|
protected:
|
|
const unsigned char* begin;
|
|
const unsigned char* end;
|
|
const unsigned char* current;
|
|
std::vector<std::vector<std::string>> fieldRefs;
|
|
};
|
|
|
|
class HproseWriter {
|
|
public:
|
|
HproseWriter(unsigned char* ptr, size_t len) : begin(ptr), end(ptr + len), current(ptr) {}
|
|
void WriteByte(unsigned char byte) {
|
|
*current++ = byte;
|
|
}
|
|
void WriteBytes(unsigned char* bytes, size_t len) {
|
|
memcpy(current, bytes, len);
|
|
current += len;
|
|
}
|
|
void WriteTag(Tags tag) {
|
|
*current++ = static_cast<unsigned char>(tag);
|
|
}
|
|
void WriteLength(uint32_t length) {
|
|
auto len = sprintf((char*)current, "%u", length);
|
|
current += len;
|
|
}
|
|
void WriteInteger(int64_t val) {
|
|
auto len = sprintf((char*)current, "%ld", val);
|
|
current += len;
|
|
}
|
|
void WriteDouble(double val) {
|
|
auto len = sprintf((char*)current, "%lf", val);
|
|
current += len;
|
|
}
|
|
void WriteString(const std::string& val) {
|
|
memcpy(current, val.data(), val.length());
|
|
current += val.length();
|
|
}
|
|
|
|
size_t Length() { return current - begin; }
|
|
|
|
int32_t GetStructFieldsIndex(const std::string& name) {
|
|
auto iter = fieldIds.find(name);
|
|
if(iter == fieldIds.end())
|
|
return -1;
|
|
return iter->second;
|
|
}
|
|
|
|
int32_t PushFieldRefs(const std::string& name) {
|
|
int32_t index = fieldIds.size();
|
|
fieldIds[name] = index;
|
|
return index;
|
|
}
|
|
|
|
protected:
|
|
const unsigned char* begin;
|
|
const unsigned char* end;
|
|
unsigned char* current;
|
|
std::map<std::string, int32_t> fieldIds;
|
|
};
|
|
|
|
#endif
|