blob: 6f9678946c256872ba1f447f54a43f0d0951c5ac [file] [log] [blame]
/*
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
/*
* This header file defines miscellaneous utility classes.
*
* @author Tian Xia <tianx@fb.com>
*
* this file is copied from
* https://github.com/facebook/mysql-5.6/blob/fb-mysql-5.6.35/fbson/FbsonUtil.h
* and modified by Doris
*/
#ifndef JSONB_JSONBUTIL_H
#define JSONB_JSONBUTIL_H
#include <sstream>
#include "common/exception.h"
#include "jsonb_document.h"
#include "jsonb_stream.h"
#include "jsonb_writer.h"
namespace doris {
#define OUT_BUF_SIZE 1024
/*
* JsonbToJson converts an JsonbValue object to a JSON string.
*/
class JsonbToJson {
public:
JsonbToJson() : os_(buffer_, OUT_BUF_SIZE) {}
// get json string
const std::string to_json_string(const char* data, size_t size) {
JsonbDocument* pdoc = doris::JsonbDocument::checkAndCreateDocument(data, size);
if (!pdoc) {
throw Exception(Status::FatalError("invalid json binary value: {}",
std::string_view(data, size)));
}
return to_json_string(pdoc->getValue());
}
const std::string to_json_string(const JsonbValue* val) {
os_.clear();
os_.seekp(0);
if (val) {
intern_json(val);
}
os_.put(0);
std::string json_string {os_.getBuffer()};
return json_string;
}
static const std::string jsonb_to_json_string(const char* data, size_t size) {
JsonbToJson jsonb_to_json;
return jsonb_to_json.to_json_string(data, size);
}
private:
// recursively convert JsonbValue
void intern_json(const JsonbValue* val) {
switch (val->type()) {
case JsonbType::T_Null: {
os_.write("null", 4);
break;
}
case JsonbType::T_True: {
os_.write("true", 4);
break;
}
case JsonbType::T_False: {
os_.write("false", 5);
break;
}
case JsonbType::T_Int8: {
os_.write(((JsonbInt8Val*)val)->val());
break;
}
case JsonbType::T_Int16: {
os_.write(((JsonbInt16Val*)val)->val());
break;
}
case JsonbType::T_Int32: {
os_.write(((JsonbInt32Val*)val)->val());
break;
}
case JsonbType::T_Int64: {
os_.write(((JsonbInt64Val*)val)->val());
break;
}
case JsonbType::T_Double: {
os_.write(((JsonbDoubleVal*)val)->val());
break;
}
case JsonbType::T_Float: {
os_.write(((JsonbFloatVal*)val)->val());
break;
}
case JsonbType::T_Int128: {
os_.write(((JsonbInt128Val*)val)->val());
break;
}
case JsonbType::T_String: {
string_to_json(((JsonbStringVal*)val)->getBlob(), ((JsonbStringVal*)val)->length());
break;
}
case JsonbType::T_Binary: {
os_.write("\"<BINARY>", 9);
os_.write(((JsonbBinaryVal*)val)->getBlob(), ((JsonbBinaryVal*)val)->getBlobLen());
os_.write("<BINARY>\"", 9);
break;
}
case JsonbType::T_Object: {
object_to_json((ObjectVal*)val);
break;
}
case JsonbType::T_Array: {
array_to_json((ArrayVal*)val);
break;
}
default:
break;
}
}
void string_to_json(const char* str, size_t len) {
os_.put('"');
if (nullptr == str) {
os_.put('"');
return;
}
char char_buffer[16];
for (const char* ptr = str; ptr != str + len && *ptr; ++ptr) {
if ((unsigned char)*ptr > 31 && *ptr != '\"' && *ptr != '\\')
os_.put(*ptr);
else {
os_.put('\\');
unsigned char token;
switch (token = *ptr) {
case '\\':
os_.put('\\');
break;
case '\"':
os_.put('\"');
break;
case '\b':
os_.put('b');
break;
case '\f':
os_.put('f');
break;
case '\n':
os_.put('n');
break;
case '\r':
os_.put('r');
break;
case '\t':
os_.put('t');
break;
default: {
int char_num = snprintf(char_buffer, sizeof(char_buffer), "u%04x", token);
os_.write(char_buffer, char_num);
break;
}
}
}
}
os_.put('"');
}
// convert object
void object_to_json(const ObjectVal* val) {
os_.put('{');
auto iter = val->begin();
auto iter_fence = val->end();
while (iter < iter_fence) {
// write key
if (iter->klen()) {
string_to_json(iter->getKeyStr(), iter->klen());
} else {
// NOTE: we use sMaxKeyId to represent an empty key. see jsonb_writer.h
if (iter->getKeyId() == JsonbKeyValue::sMaxKeyId) {
string_to_json(nullptr, 0);
} else {
os_.write(iter->getKeyId());
}
}
os_.put(':');
// convert value
intern_json(iter->value());
++iter;
if (iter != iter_fence) {
os_.put(',');
}
}
assert(iter == iter_fence);
os_.put('}');
}
// convert array to json
void array_to_json(const ArrayVal* val) {
os_.put('[');
auto iter = val->begin();
auto iter_fence = val->end();
while (iter != iter_fence) {
// convert value
intern_json((const JsonbValue*)iter);
++iter;
if (iter != iter_fence) {
os_.put(',');
}
}
assert(iter == iter_fence);
os_.put(']');
}
private:
JsonbOutStream os_;
char buffer_[OUT_BUF_SIZE];
};
// This class is a utility to create a JsonbValue.
template <class OS_TYPE>
class JsonbValueCreaterT {
public:
JsonbValue* operator()(int32_t val) { return operator()((int64_t)val); }
JsonbValue* operator()(int64_t val) {
writer_.reset();
writer_.writeStartArray();
writer_.writeInt(val);
writer_.writeEndArray();
return extractValue();
}
JsonbValue* operator()(double val) {
writer_.reset();
writer_.writeStartArray();
writer_.writeDouble(val);
writer_.writeEndArray();
return extractValue();
}
JsonbValue* operator()(const char* str) { return operator()(str, (unsigned int)strlen(str)); }
JsonbValue* operator()(const char* str, unsigned int str_len) {
writer_.reset();
writer_.writeStartArray();
writer_.writeStartString();
writer_.writeString(str, str_len);
writer_.writeEndString();
writer_.writeEndArray();
return extractValue();
}
JsonbValue* operator()(bool val) {
writer_.reset();
writer_.writeStartArray();
writer_.writeBool(val);
writer_.writeEndArray();
return extractValue();
}
JsonbValue* operator()() {
writer_.reset();
writer_.writeStartArray();
writer_.writeNull();
writer_.writeEndArray();
return extractValue();
}
private:
JsonbValue* extractValue() {
return static_cast<ArrayVal*>(
JsonbDocument::createValue(writer_.getOutput()->getBuffer(),
(int)writer_.getOutput()->getSize()))
->get(0);
}
JsonbWriterT<OS_TYPE> writer_;
};
typedef JsonbValueCreaterT<JsonbOutStream> JsonbValueCreater;
} // namespace doris
#endif // JSONB_JSONBUTIL_H