2
1
mirror of https://github.com/qpdf/qpdf.git synced 2024-12-22 19:08:59 +00:00
qpdf/libqpdf/JSON.cc
Jay Berkenbilt 1a7d3700a6 Fix unnecessary copies in auto iter (fixes #426)
Also switch to colon-style iteration in some cases. Thanks to Dean
Scarff for drawing this to my attention after detecting some
unnecessary copies with
https://clang.llvm.org/extra/clang-tidy/checks/performance-for-range-copy.html
2020-04-08 20:45:26 -04:00

423 lines
9.4 KiB
C++

#include <qpdf/JSON.hh>
#include <qpdf/QUtil.hh>
#include <qpdf/QTC.hh>
#include <stdexcept>
JSON::Members::~Members()
{
}
JSON::Members::Members(PointerHolder<JSON_value> value) :
value(value)
{
}
JSON::JSON(PointerHolder<JSON_value> value) :
m(new Members(value))
{
}
JSON::JSON_value::~JSON_value()
{
}
JSON::JSON_dictionary::~JSON_dictionary()
{
}
std::string JSON::JSON_dictionary::unparse(size_t depth) const
{
std::string result = "{";
bool first = true;
for (std::map<std::string, PointerHolder<JSON_value> >::const_iterator
iter = members.begin();
iter != members.end(); ++iter)
{
if (first)
{
first = false;
}
else
{
result.append(1, ',');
}
result.append(1, '\n');
result.append(2 * (1 + depth), ' ');
result += ("\"" + (*iter).first + "\": " +
(*iter).second->unparse(1 + depth));
}
if (! first)
{
result.append(1, '\n');
result.append(2 * depth, ' ');
}
result.append(1, '}');
return result;
}
JSON::JSON_array::~JSON_array()
{
}
std::string JSON::JSON_array::unparse(size_t depth) const
{
std::string result = "[";
bool first = true;
for (std::vector<PointerHolder<JSON_value> >::const_iterator iter =
elements.begin();
iter != elements.end(); ++iter)
{
if (first)
{
first = false;
}
else
{
result.append(1, ',');
}
result.append(1, '\n');
result.append(2 * (1 + depth), ' ');
result += (*iter)->unparse(1 + depth);
}
if (! first)
{
result.append(1, '\n');
result.append(2 * depth, ' ');
}
result.append(1, ']');
return result;
}
JSON::JSON_string::JSON_string(std::string const& utf8) :
encoded(encode_string(utf8))
{
}
JSON::JSON_string::~JSON_string()
{
}
std::string JSON::JSON_string::unparse(size_t) const
{
return "\"" + encoded + "\"";
}
JSON::JSON_number::JSON_number(long long value) :
encoded(QUtil::int_to_string(value))
{
}
JSON::JSON_number::JSON_number(double value) :
encoded(QUtil::double_to_string(value, 6))
{
}
JSON::JSON_number::JSON_number(std::string const& value) :
encoded(value)
{
}
JSON::JSON_number::~JSON_number()
{
}
std::string JSON::JSON_number::unparse(size_t) const
{
return encoded;
}
JSON::JSON_bool::JSON_bool(bool val) :
value(val)
{
}
JSON::JSON_bool::~JSON_bool()
{
}
std::string JSON::JSON_bool::unparse(size_t) const
{
return value ? "true" : "false";
}
JSON::JSON_null::~JSON_null()
{
}
std::string JSON::JSON_null::unparse(size_t) const
{
return "null";
}
std::string
JSON::unparse() const
{
if (0 == this->m->value.getPointer())
{
return "null";
}
else
{
return this->m->value->unparse(0);
}
}
std::string
JSON::encode_string(std::string const& str)
{
std::string result;
size_t len = str.length();
for (size_t i = 0; i < len; ++i)
{
unsigned char ch = static_cast<unsigned char>(str.at(i));
switch (ch)
{
case '\\':
result += "\\\\";
break;
case '\"':
result += "\\\"";
break;
case '\b':
result += "\\b";
break;
case '\n':
result += "\\n";
break;
case '\r':
result += "\\r";
break;
case '\t':
result += "\\t";
break;
default:
if (ch < 32)
{
result += "\\u" + QUtil::int_to_string_base(ch, 16, 4);
}
else
{
result.append(1, static_cast<char>(ch));
}
}
}
return result;
}
JSON
JSON::makeDictionary()
{
return JSON(new JSON_dictionary());
}
JSON
JSON::addDictionaryMember(std::string const& key, JSON const& val)
{
JSON_dictionary* obj = dynamic_cast<JSON_dictionary*>(
this->m->value.getPointer());
if (0 == obj)
{
throw std::runtime_error(
"JSON::addDictionaryMember called on non-dictionary");
}
if (val.m->value.getPointer())
{
obj->members[encode_string(key)] = val.m->value;
}
else
{
obj->members[encode_string(key)] = new JSON_null();
}
return obj->members[encode_string(key)];
}
JSON
JSON::makeArray()
{
return JSON(new JSON_array());
}
JSON
JSON::addArrayElement(JSON const& val)
{
JSON_array* arr = dynamic_cast<JSON_array*>(
this->m->value.getPointer());
if (0 == arr)
{
throw std::runtime_error("JSON::addArrayElement called on non-array");
}
if (val.m->value.getPointer())
{
arr->elements.push_back(val.m->value);
}
else
{
arr->elements.push_back(new JSON_null());
}
return arr->elements.back();
}
JSON
JSON::makeString(std::string const& utf8)
{
return JSON(new JSON_string(utf8));
}
JSON
JSON::makeInt(long long int value)
{
return JSON(new JSON_number(value));
}
JSON
JSON::makeReal(double value)
{
return JSON(new JSON_number(value));
}
JSON
JSON::makeNumber(std::string const& encoded)
{
return JSON(new JSON_number(encoded));
}
JSON
JSON::makeBool(bool value)
{
return JSON(new JSON_bool(value));
}
JSON
JSON::makeNull()
{
return JSON(new JSON_null());
}
bool
JSON::checkSchema(JSON schema, std::list<std::string>& errors)
{
return checkSchemaInternal(this->m->value.getPointer(),
schema.m->value.getPointer(),
errors, "");
}
bool
JSON::checkSchemaInternal(JSON_value* this_v, JSON_value* sch_v,
std::list<std::string>& errors,
std::string prefix)
{
JSON_array* this_arr = dynamic_cast<JSON_array*>(this_v);
JSON_dictionary* this_dict = dynamic_cast<JSON_dictionary*>(this_v);
JSON_array* sch_arr = dynamic_cast<JSON_array*>(sch_v);
JSON_dictionary* sch_dict = dynamic_cast<JSON_dictionary*>(sch_v);
std::string err_prefix;
if (prefix.empty())
{
err_prefix = "top-level object";
}
else
{
err_prefix = "json key \"" + prefix + "\"";
}
std::string pattern_key;
if (sch_dict)
{
if (! this_dict)
{
QTC::TC("libtests", "JSON wanted dictionary");
errors.push_back(err_prefix + " is supposed to be a dictionary");
return false;
}
auto members = sch_dict->members;
std::string key;
if ((members.size() == 1) &&
((key = members.begin()->first, key.length() > 2) &&
(key.at(0) == '<') &&
(key.at(key.length() - 1) == '>')))
{
pattern_key = key;
}
}
if (sch_dict && (! pattern_key.empty()))
{
auto pattern_schema = sch_dict->members[pattern_key].getPointer();
for (auto const& iter: this_dict->members)
{
std::string const& key = iter.first;
checkSchemaInternal(
this_dict->members[key].getPointer(),
pattern_schema,
errors, prefix + "." + key);
}
}
else if (sch_dict)
{
for (std::map<std::string, PointerHolder<JSON_value> >::iterator iter =
sch_dict->members.begin();
iter != sch_dict->members.end(); ++iter)
{
std::string const& key = (*iter).first;
if (this_dict->members.count(key))
{
checkSchemaInternal(
this_dict->members[key].getPointer(),
(*iter).second.getPointer(),
errors, prefix + "." + key);
}
else
{
QTC::TC("libtests", "JSON key missing in object");
errors.push_back(
err_prefix + ": key \"" + key +
"\" is present in schema but missing in object");
}
}
for (std::map<std::string, PointerHolder<JSON_value> >::iterator iter =
this_dict->members.begin();
iter != this_dict->members.end(); ++iter)
{
std::string const& key = (*iter).first;
if (sch_dict->members.count(key) == 0)
{
QTC::TC("libtests", "JSON key extra in object");
errors.push_back(
err_prefix + ": key \"" + key +
"\" is not present in schema but appears in object");
}
}
}
else if (sch_arr)
{
if (! this_arr)
{
QTC::TC("libtests", "JSON wanted array");
errors.push_back(err_prefix + " is supposed to be an array");
return false;
}
if (sch_arr->elements.size() != 1)
{
QTC::TC("libtests", "JSON schema array error");
errors.push_back(err_prefix +
" schema array contains other than one item");
return false;
}
int i = 0;
for (std::vector<PointerHolder<JSON_value> >::iterator iter =
this_arr->elements.begin();
iter != this_arr->elements.end(); ++iter, ++i)
{
checkSchemaInternal(
(*iter).getPointer(),
sch_arr->elements.at(0).getPointer(),
errors, prefix + "." + QUtil::int_to_string(i));
}
}
return errors.empty();
}