587 lines
15 KiB
C++
587 lines
15 KiB
C++
/*
|
|
* MIT License
|
|
*
|
|
* Copyright (c) Robin E.R. Davies
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
* this software and associated documentation files (the "Software"), to deal in
|
|
* the Software without restriction, including without limitation the rights to
|
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
* of the Software, and to permit persons to whom the Software is furnished to do
|
|
* so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in all
|
|
* copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
* SOFTWARE.
|
|
*/
|
|
|
|
#include "json_variant.hpp"
|
|
#include <limits>
|
|
#include <cmath>
|
|
#include <cstddef>
|
|
#include <memory>
|
|
|
|
using namespace pipedal;
|
|
|
|
|
|
json_variant::~json_variant()
|
|
{
|
|
free();
|
|
}
|
|
|
|
void json_variant::free()
|
|
{
|
|
// in-place deletion.
|
|
switch (content_type)
|
|
{
|
|
case ContentType::String:
|
|
memString().std::string::~string();
|
|
break;
|
|
case ContentType::Object:
|
|
memObject().std::shared_ptr<json_object>::~shared_ptr();
|
|
break;
|
|
case ContentType::Array:
|
|
memArray().std::shared_ptr<json_array>::~shared_ptr();
|
|
break;
|
|
default:
|
|
break;
|
|
|
|
}
|
|
content_type = ContentType::Null;
|
|
}
|
|
|
|
|
|
json_variant::json_variant(json_variant &&other)
|
|
{
|
|
this->content_type = ContentType::Null;
|
|
switch (other.content_type)
|
|
{
|
|
case ContentType::Null:
|
|
break;
|
|
case ContentType::Bool:
|
|
this->content.bool_value = other.content.bool_value;
|
|
break;
|
|
case ContentType::Number:
|
|
this->content.double_value = other.content.double_value;
|
|
break;
|
|
case ContentType::String:
|
|
new (content.mem) std::string(std::move(other.memString()));
|
|
this->content_type = ContentType::String;
|
|
return;
|
|
case ContentType::Object:
|
|
new (content.mem) std::shared_ptr<json_object>{std::move(other.memObject())};
|
|
this->content_type = ContentType::Object;
|
|
return;
|
|
case ContentType::Array:
|
|
new (content.mem) std::shared_ptr<json_array>{std::move(other.memArray())};
|
|
this->content_type = ContentType::Array;
|
|
return;
|
|
}
|
|
this->content_type = other.content_type;
|
|
other.content_type = ContentType::Null;
|
|
}
|
|
|
|
json_variant &json_variant::operator=(const json_variant &other)
|
|
{
|
|
free();
|
|
switch (other.content_type)
|
|
{
|
|
case ContentType::Null:
|
|
break;
|
|
case ContentType::Bool:
|
|
this->content.bool_value = other.content.bool_value;
|
|
break;
|
|
case ContentType::Number:
|
|
this->content.double_value = other.content.double_value;
|
|
break;
|
|
case ContentType::String:
|
|
new (content.mem) std::string(other.memString());
|
|
break;
|
|
case ContentType::Object:
|
|
new (content.mem) std::shared_ptr<json_object>{other.memObject()};
|
|
break;
|
|
case ContentType::Array:
|
|
new (content.mem) std::shared_ptr<json_array>{other.memArray()};
|
|
this->content_type = ContentType::Array;
|
|
break;
|
|
}
|
|
this->content_type = other.content_type;
|
|
return *this;
|
|
|
|
}
|
|
|
|
json_variant::json_variant(const json_variant &other)
|
|
{
|
|
this->content_type = ContentType::Null;
|
|
switch (other.content_type)
|
|
{
|
|
case ContentType::Null:
|
|
break;
|
|
case ContentType::Bool:
|
|
this->content.bool_value = other.content.bool_value;
|
|
break;
|
|
case ContentType::Number:
|
|
this->content.double_value = other.content.double_value;
|
|
break;
|
|
case ContentType::String:
|
|
new (content.mem) std::string(other.memString());
|
|
this->content_type = ContentType::String;
|
|
return;
|
|
case ContentType::Object:
|
|
new (content.mem) std::shared_ptr<json_object>{other.memObject()};
|
|
this->content_type = ContentType::Object;
|
|
return;
|
|
case ContentType::Array:
|
|
new (content.mem) std::shared_ptr<json_array>{other.memArray()};
|
|
this->content_type = ContentType::Array;
|
|
return;
|
|
}
|
|
this->content_type = other.content_type;
|
|
}
|
|
|
|
void json_variant::write_float_value(json_writer &writer, double value) const
|
|
{
|
|
writer.write(value);
|
|
}
|
|
|
|
void json_variant::write_double_value(json_writer &writer, double value) const
|
|
{
|
|
writer.write(value);
|
|
}
|
|
|
|
void json_variant::read_json(json_reader &reader)
|
|
{
|
|
int v = reader.peek();
|
|
if (v == '[')
|
|
{
|
|
json_array array;
|
|
reader.read(&array);
|
|
(*this) = std::move(array);
|
|
}
|
|
else if (v == '{')
|
|
{
|
|
json_object object;
|
|
reader.read(&object);
|
|
(*this) = std::move(object);
|
|
}
|
|
else if (v == '\"')
|
|
{
|
|
std::string s;
|
|
reader.read(&s);
|
|
(*this) = std::move(s);
|
|
}
|
|
else if (v == 'n')
|
|
{
|
|
reader.read_null();
|
|
(*this) = json_null();
|
|
}
|
|
else if (v == 't' || v == 'f')
|
|
{
|
|
bool b;
|
|
reader.read(&b);
|
|
(*this) = b;
|
|
}
|
|
else
|
|
{
|
|
// it's a number.
|
|
double v;
|
|
reader.read(&v);
|
|
(*this) = v;
|
|
}
|
|
}
|
|
void json_variant::write_json(json_writer &writer) const
|
|
{
|
|
switch (content_type)
|
|
{
|
|
case ContentType::Null:
|
|
writer.write_raw("null");
|
|
break;
|
|
case ContentType::Bool:
|
|
writer.write(as_bool());
|
|
break;
|
|
case ContentType::Number:
|
|
write_double_value(writer, as_number());
|
|
break;
|
|
case ContentType::String:
|
|
writer.write(as_string());
|
|
break;
|
|
case ContentType::Object:
|
|
writer.write(as_object());
|
|
break;
|
|
case ContentType::Array:
|
|
writer.write(as_array());
|
|
break;
|
|
default:
|
|
throw std::logic_error("Invalid variant type");
|
|
}
|
|
}
|
|
|
|
bool json_array::operator==(const json_array &other) const
|
|
{
|
|
if (!(this->size() == other.size()))
|
|
return false;
|
|
for (size_t i = 0; i < this->size(); ++i)
|
|
{
|
|
if (!((*this)[i] == other[i]))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void json_array::read_json(json_reader &reader)
|
|
{
|
|
reader.read(&(this->values));
|
|
}
|
|
|
|
void json_array::write_json(json_writer &writer) const
|
|
{
|
|
writer.start_array();
|
|
bool first = true;
|
|
for (auto &value : values)
|
|
{
|
|
if (!first)
|
|
writer.write_raw(",");
|
|
first = false;
|
|
writer.write(value);
|
|
}
|
|
writer.end_array();
|
|
}
|
|
|
|
|
|
json_object::iterator json_object::find(const std::string& key)
|
|
{
|
|
for (auto i = begin(); i != end(); ++i)
|
|
{
|
|
if (i->first == key)
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
return end();
|
|
}
|
|
json_object::const_iterator json_object::find(const std::string&key) const
|
|
{
|
|
for (auto i = begin(); i != end(); ++i)
|
|
{
|
|
if (i->first == key)
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
return end();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool json_object::operator==(const json_object &other) const
|
|
{
|
|
|
|
for (const auto &pair : this->values)
|
|
{
|
|
auto index = other.find(pair.first);
|
|
if (index == other.end())
|
|
return false;
|
|
if (!(index->second == pair.second))
|
|
return false;
|
|
}
|
|
for (const auto &pair : other.values)
|
|
{
|
|
auto index = this->find(pair.first);
|
|
if (index == this->end())
|
|
return false;
|
|
if (!(index->second == pair.second))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
void json_object::read_json(json_reader &reader)
|
|
{
|
|
reader.start_object();
|
|
|
|
while (true)
|
|
{
|
|
if (reader.peek() == '}') break;
|
|
|
|
std::string key;
|
|
json_variant value;
|
|
reader.read(&key);
|
|
reader.consume(':');
|
|
reader.read(&value);
|
|
|
|
(*this)[key] = value;
|
|
if (reader.peek() == ',')
|
|
{
|
|
reader.consume(',');
|
|
}
|
|
}
|
|
|
|
reader.end_object();
|
|
}
|
|
void json_object::write_json(json_writer &writer) const
|
|
{
|
|
writer.start_object();
|
|
bool first = true;
|
|
for (auto &value : values)
|
|
{
|
|
if (!first)
|
|
{
|
|
writer.write_raw(",");
|
|
}
|
|
first = false;
|
|
writer.write(value.first);
|
|
writer.write_raw(": ");
|
|
writer.writeRawWritable(value.second);
|
|
}
|
|
writer.end_object();
|
|
}
|
|
|
|
json_variant &json_array::at(size_t index)
|
|
{
|
|
check_index(index);
|
|
return values.at(index);
|
|
}
|
|
const json_variant &json_array::at(size_t index) const
|
|
{
|
|
check_index(index);
|
|
return values.at(index);
|
|
}
|
|
|
|
size_t json_variant::size() const
|
|
{
|
|
if (content_type == ContentType::Array)
|
|
return as_array()->size();
|
|
if (content_type == ContentType::Object)
|
|
{
|
|
return as_object()->size();
|
|
}
|
|
throw std::logic_error("Not supported.");
|
|
}
|
|
|
|
json_variant &json_object::at(const std::string &index) {
|
|
for (auto&entry: values)
|
|
{
|
|
if (entry.first == index)
|
|
{
|
|
return entry.second;
|
|
}
|
|
}
|
|
throw std::logic_error("Not found.");
|
|
values.push_back(std::pair(index,json_variant()));
|
|
return values[values.size()-1].second;
|
|
}
|
|
const json_variant &json_object::at(const std::string &index) const {
|
|
for (const auto&entry: values)
|
|
{
|
|
if (entry.first == index)
|
|
{
|
|
return entry.second;
|
|
}
|
|
}
|
|
throw std::logic_error("Not found.");
|
|
}
|
|
|
|
json_variant &json_object::operator[](const std::string &index)
|
|
{
|
|
for (auto&entry: values)
|
|
{
|
|
if (entry.first == index)
|
|
{
|
|
return entry.second;
|
|
}
|
|
}
|
|
values.push_back(std::pair(index,json_variant()));
|
|
return values[values.size()-1].second;
|
|
}
|
|
const json_variant &json_object::operator[](const std::string &index) const
|
|
{
|
|
return at(index);
|
|
}
|
|
bool json_object::contains(const std::string &index) const
|
|
{
|
|
return find(index) != end();
|
|
}
|
|
|
|
json_variant &json_variant::operator=(bool value)
|
|
{
|
|
free();
|
|
this->content_type = ContentType::Bool;
|
|
this->content.bool_value = value;
|
|
return *this;
|
|
}
|
|
|
|
json_variant &json_variant::operator=(double value)
|
|
{
|
|
free();
|
|
this->content_type = ContentType::Number;
|
|
this->content.double_value = value;
|
|
return *this;
|
|
}
|
|
json_variant &json_variant::operator=(const std::string &value)
|
|
{
|
|
free();
|
|
this->content_type = ContentType::String;
|
|
new (this->content.mem) std::string(value); // in-place constructor.
|
|
return *this;
|
|
}
|
|
json_variant &json_variant::operator=(std::string &&value)
|
|
{
|
|
free();
|
|
this->content_type = ContentType::String;
|
|
new (this->content.mem) std::string(std::move(value)); // in-place constructor.
|
|
return *this;
|
|
}
|
|
json_variant &json_variant::operator=(json_object &&value)
|
|
{
|
|
free();
|
|
new (this->content.mem) std::shared_ptr<json_object>{new json_object(std::move(value))};
|
|
this->content_type = ContentType::Object;
|
|
return *this;
|
|
}
|
|
json_variant &json_variant::operator=(json_array &&value)
|
|
{
|
|
free();
|
|
this->content_type = ContentType::Array;
|
|
new (this->content.mem) std::shared_ptr<json_array>{new json_array(std::move(value))};
|
|
return *this;
|
|
}
|
|
json_variant &json_variant::operator=(json_variant &&value)
|
|
{
|
|
if (this->content_type == value.content_type)
|
|
{
|
|
switch (this->content_type)
|
|
{
|
|
case ContentType::String:
|
|
std::swap(this->memString(), value.memString());
|
|
return *this;
|
|
case ContentType::Array:
|
|
std::swap(this->memArray(), value.memArray());
|
|
return *this;
|
|
case ContentType::Object:
|
|
std::swap(this->memObject(), value.memObject());
|
|
return *this;
|
|
case ContentType::Null:
|
|
return *this; // nothing to do.
|
|
case ContentType::Bool:
|
|
std::swap(this->content.bool_value, value.content.bool_value);
|
|
return *this;
|
|
case ContentType::Number:
|
|
std::swap(this->content.double_value, value.content.double_value);
|
|
return *this;
|
|
|
|
}
|
|
}
|
|
free();
|
|
switch (value.content_type)
|
|
{
|
|
case ContentType::String:
|
|
new (content.mem) std::string(std::move(value.memString()));
|
|
value.memString().std::string::~string();
|
|
break;
|
|
case ContentType::Object:
|
|
new (content.mem) std::shared_ptr<json_object>(std::move(value.memObject()));
|
|
value.memObject().std::shared_ptr<json_object>::~shared_ptr();
|
|
break;
|
|
case ContentType::Array:
|
|
new (content.mem) std::shared_ptr<json_array>(std::move(value.memArray()));
|
|
value.memArray().std::shared_ptr<json_array>::~shared_ptr();
|
|
break;
|
|
default:
|
|
// undifferentiated copy of POD types.
|
|
*(uint64_t *)(this->content.mem) = *(uint64_t *)(value.content.mem);
|
|
break;
|
|
}
|
|
this->content_type = value.content_type;
|
|
value.content_type = ContentType::Null;
|
|
return *this;
|
|
}
|
|
|
|
bool json_variant::operator==(const json_variant &other) const
|
|
{
|
|
if (this->content_type != other.content_type)
|
|
return false;
|
|
|
|
switch (this->content_type)
|
|
{
|
|
case ContentType::Null:
|
|
return true;
|
|
case ContentType::Bool:
|
|
return as_bool() == other.as_bool();
|
|
break;
|
|
case ContentType::Number:
|
|
return as_number() == other.as_number();
|
|
case ContentType::String:
|
|
return as_string() == other.as_string();
|
|
case ContentType::Array:
|
|
return *(as_array().get()) == *(other.as_array().get());
|
|
case ContentType::Object:
|
|
return *(as_object().get()) == *(other.as_object().get());;
|
|
default:
|
|
throw std::logic_error("Invalid content_type.");
|
|
}
|
|
}
|
|
json_array::json_array(json_array&&other)
|
|
: values(std::move(other.values))
|
|
{
|
|
++allocation_count_;
|
|
}
|
|
json_object::json_object(json_object&&other)
|
|
:values(std::move(other.values))
|
|
{
|
|
++allocation_count_;
|
|
}
|
|
|
|
bool json_variant::contains(const std::string &index) const
|
|
{
|
|
if (is_object())
|
|
{
|
|
return as_object()->contains(index);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
std::string json_variant::to_string() const
|
|
{
|
|
std::stringstream ss;
|
|
json_writer writer(ss);
|
|
writer.write(*this);
|
|
return ss.str();
|
|
}
|
|
|
|
json_variant::json_variant(const char*sz)
|
|
: json_variant(std::string(sz))
|
|
{
|
|
|
|
}
|
|
|
|
json_variant::json_variant(json_reader&reader)
|
|
{
|
|
this->read_json(reader);
|
|
}
|
|
|
|
|
|
json_variant json_variant::parse(const std::string&jsonText)
|
|
{
|
|
std::istringstream s(jsonText);
|
|
json_reader reader(s);
|
|
json_variant result;
|
|
reader.read(&result);
|
|
return result;
|
|
}
|
|
|
|
|
|
/*static*/ json_null json_null::instance;
|
|
/*static*/ int64_t json_array::allocation_count_ = 0; // strictly for testing purposes. not thread safe.
|
|
/*static*/ int64_t json_object::allocation_count_ = 0; // strictly for testing purposes. not thread safe.
|
|
|