Skip to content
Merged
10 changes: 10 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,15 @@ project(cpp-json CXX)

enable_testing()

if (MSVC)
add_compile_options(/EHsc /Zc:throwingNew)
if (MSVC_VERSION GREATER_EQUAL 1913)
add_compile_options(/Zc:externConstexpr)
endif()
if (MSVC_VERSION GREATER_EQUAL 1914)
add_compile_options(/Zc:__cplusplus)
endif()
endif()

add_subdirectory(lib)
add_subdirectory(test)
10 changes: 10 additions & 0 deletions lib/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,13 @@ target_include_directories(cpp-json
target_sources(cpp-json INTERFACE
${CMAKE_CURRENT_SOURCE_DIR}/include/cpp-json/json.h
)

if(CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU")
target_compile_options(cpp-json INTERFACE
-Werror=reorder
)
elseif(MSVC)
target_compile_options(cpp-json INTERFACE
/we5038
)
endif()
7 changes: 4 additions & 3 deletions lib/include/cpp-json/json_decode.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "json_error.h"
#include "json_reader.h"
#include "json_value.h"
#include <cstdint>

namespace json {

Expand Down Expand Up @@ -84,7 +85,7 @@ class parser {

while (reader_.peek() != '"' && reader_.peek() != '\n') {

char ch = reader_.read();
const char ch = reader_.read();
if (ch == '\\') {
switch (reader_.read()) {
case '"':
Expand Down Expand Up @@ -120,8 +121,8 @@ class parser {
if (!std::isxdigit(hex[2] = reader_.read())) JSON_THROW(invalid_unicode_character(reader_.index()));
if (!std::isxdigit(hex[3] = reader_.read())) JSON_THROW(invalid_unicode_character(reader_.index()));

uint16_t w1 = 0;
uint16_t w2 = 0;
std::uint_least16_t w1 = 0;
std::uint_least16_t w2 = 0;

w1 |= (detail::to_hex(hex[0]) << 12);
w1 |= (detail::to_hex(hex[1]) << 8);
Expand Down
26 changes: 13 additions & 13 deletions lib/include/cpp-json/json_detail.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,12 @@ bool is_space(Ch ch) {
* @param out
*/
template <class Out>
void surrogate_pair_to_utf8(std::uint16_t w1, std::uint16_t w2, Out &out) {
void surrogate_pair_to_utf8(std::uint_least16_t w1, std::uint_least16_t w2, Out &out) {

std::uint32_t cp;
std::uint_least32_t cp = '\0';
if ((w1 & 0xfc00) == 0xd800) {
if ((w2 & 0xfc00) == 0xdc00) {
cp = 0x10000 + (((static_cast<std::uint32_t>(w1) & 0x3ff) << 10) | (w2 & 0x3ff));
cp = 0x10000 + (((static_cast<std::uint_least32_t>(w1) & 0x3ff) << 10) | (w2 & 0x3ff));
} else {
JSON_THROW(invalid_unicode_character(0));
}
Expand All @@ -59,19 +59,19 @@ void surrogate_pair_to_utf8(std::uint16_t w1, std::uint16_t w2, Out &out) {
}

if (cp < 0x80) {
*out++ = static_cast<std::uint8_t>(cp);
*out++ = static_cast<unsigned char>(cp);
} else if (cp < 0x0800) {
*out++ = static_cast<std::uint8_t>(0xc0 | ((cp >> 6) & 0x1f));
*out++ = static_cast<std::uint8_t>(0x80 | (cp & 0x3f));
*out++ = static_cast<unsigned char>(0xc0 | ((cp >> 6) & 0x1f));
*out++ = static_cast<unsigned char>(0x80 | (cp & 0x3f));
} else if (cp < 0x10000) {
*out++ = static_cast<std::uint8_t>(0xe0 | ((cp >> 12) & 0x0f));
*out++ = static_cast<std::uint8_t>(0x80 | ((cp >> 6) & 0x3f));
*out++ = static_cast<std::uint8_t>(0x80 | (cp & 0x3f));
*out++ = static_cast<unsigned char>(0xe0 | ((cp >> 12) & 0x0f));
*out++ = static_cast<unsigned char>(0x80 | ((cp >> 6) & 0x3f));
*out++ = static_cast<unsigned char>(0x80 | (cp & 0x3f));
} else if (cp < 0x1fffff) {
*out++ = static_cast<std::uint8_t>(0xf0 | ((cp >> 18) & 0x07));
*out++ = static_cast<std::uint8_t>(0x80 | ((cp >> 12) & 0x3f));
*out++ = static_cast<std::uint8_t>(0x80 | ((cp >> 6) & 0x3f));
*out++ = static_cast<std::uint8_t>(0x80 | (cp & 0x3f));
*out++ = static_cast<unsigned char>(0xf0 | ((cp >> 18) & 0x07));
*out++ = static_cast<unsigned char>(0x80 | ((cp >> 12) & 0x3f));
*out++ = static_cast<unsigned char>(0x80 | ((cp >> 6) & 0x3f));
*out++ = static_cast<unsigned char>(0x80 | (cp & 0x3f));
}
}

Expand Down
32 changes: 14 additions & 18 deletions lib/include/cpp-json/json_encode.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
#define JSON_ENCODE_H_

#include "json_value.h"
#include <cstdint>
#include <iterator>
#include <ostream>
#include <sstream>
#include <string>
Expand Down Expand Up @@ -49,12 +51,10 @@ inline std::string escape_string(std::string_view s, Options options) {
reserved : 24;
};

state_t shift_state = {0, 0, 0};
char32_t result = 0;
state_t shift_state = {0, 0, 0};
std::uint_least32_t result = 0;

for (auto it = s.begin(); it != s.end(); ++it) {

const auto ch = static_cast<uint8_t>(*it);
for (const unsigned char ch : s) {

if (shift_state.seen == 0) {

Expand Down Expand Up @@ -88,7 +88,7 @@ inline std::string escape_string(std::string_view s, Options options) {
if (!isprint(ch)) {
r += "\\u";
char buf[5];
snprintf(buf, sizeof(buf), "%04X", ch);
snprintf(buf, std::size(buf), "%04X", ch);
r += buf;
} else {
r += static_cast<char>(ch);
Expand Down Expand Up @@ -133,17 +133,17 @@ inline std::string escape_string(std::string_view s, Options options) {

if (result < 0xd800 || (result >= 0xe000 && result < 0x10000)) {
r += "\\u";
snprintf(buf, sizeof(buf), "%04X", result);
snprintf(buf, std::size(buf), "%04X", result);
r += buf;
} else {
result = (result - 0x10000);

r += "\\u";
snprintf(buf, sizeof(buf), "%04X", 0xd800 + ((result >> 10) & 0x3ff));
snprintf(buf, std::size(buf), "%04X", 0xd800 + ((result >> 10) & 0x3ff));
r += buf;

r += "\\u";
snprintf(buf, sizeof(buf), "%04X", 0xdc00 + (result & 0x3ff));
snprintf(buf, std::size(buf), "%04X", 0xdc00 + (result & 0x3ff));
r += buf;
}

Expand All @@ -161,7 +161,7 @@ inline std::string escape_string(std::string_view s, Options options) {
}
} else {

for (char ch : s) {
for (const char ch : s) {

switch (ch) {
case '\"':
Expand Down Expand Up @@ -216,14 +216,13 @@ inline void value_to_string(std::ostream &os, const object &o, Options options,
os << "{\n";

auto it = o.begin();
auto e = o.end();

++indent;
os << std::string(indent * IndentWidth, ' ') << '"' << escape_string(it->first, options) << "\" : ";
value_to_string(os, it->second, options, indent, true);

++it;
for (; it != e; ++it) {
for (auto e = o.end(); it != e; ++it) {
os << ',';
os << '\n';
os << std::string(indent * IndentWidth, ' ') << '"' << escape_string(it->first, options) << "\" : ";
Expand All @@ -248,12 +247,11 @@ inline void value_to_string(std::ostream &os, const array &a, Options options, i
os << "[\n";

auto it = a.begin();
auto e = a.end();

++indent;
value_to_string(os, *it++, options, indent, false);

for (; it != e; ++it) {
for (auto e = a.end(); it != e; ++it) {
os << ',';
os << '\n';
value_to_string(os, *it, options, indent, false);
Expand Down Expand Up @@ -315,11 +313,10 @@ inline void serialize(std::ostream &os, const array &a, Options options) {
os << "[";
if (!a.empty()) {
auto it = a.begin();
auto e = a.end();

serialize(os, *it++, options);

for (; it != e; ++it) {
for (const auto e = a.end(); it != e; ++it) {
os << ',';
serialize(os, *it, options);
}
Expand All @@ -331,12 +328,11 @@ inline void serialize(std::ostream &os, const object &o, Options options) {
os << "{";
if (!o.empty()) {
auto it = o.begin();
auto e = o.end();

os << '"' << escape_string(it->first, options) << "\":";
serialize(os, it->second, options);
++it;
for (; it != e; ++it) {
for (const auto e = o.end(); it != e; ++it) {
os << ',';
os << '"' << escape_string(it->first, options) << "\":";
serialize(os, it->second, options);
Expand Down
49 changes: 17 additions & 32 deletions lib/include/cpp-json/json_reader.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#include <cassert>
#include <cstddef>
#include <iterator>
#include <optional>
#include <regex>
#include <stack>
Expand Down Expand Up @@ -79,7 +80,7 @@ class basic_reader {
* @return size_t
*/
size_t consume(std::basic_string_view<Ch> chars) noexcept {
return consume_while([chars](Ch ch) {
return consume_while([chars](Ch ch) noexcept {
return chars.find(ch) != std::basic_string_view<Ch>::npos;
});
}
Expand All @@ -91,7 +92,7 @@ class basic_reader {
* @return size_t
*/
size_t consume_whitespace() noexcept {
return consume_while([](Ch ch) {
return consume_while([](Ch ch) noexcept {
return (ch == ' ' || ch == '\t');
});
}
Expand All @@ -104,18 +105,12 @@ class basic_reader {
* @return size_t
*/
template <class Pred>
size_t consume_while(Pred pred) noexcept {
size_t count = 0;
while (!eof()) {
const Ch ch = peek();
if (!pred(ch)) {
break;
}

size_t consume_while(Pred pred) noexcept(noexcept(pred(Ch{'\0'}))) {
const size_t start = index_;
while (!eof() && pred(peek())) {
++index_;
++count;
}
return count;
return index_ - start;
}

/**
Expand Down Expand Up @@ -160,7 +155,7 @@ class basic_reader {
return {};
}

std::basic_string<Ch> m = input_.substr(index_);
std::basic_string<Ch> m{ input_.substr(index_) };
index_ += m.size();
return m;
}
Expand All @@ -173,15 +168,13 @@ class basic_reader {
* @return bool
*/
std::optional<std::basic_string<Ch>> match(const std::basic_regex<Ch> &regex) {
std::match_results<const Ch *> matches;
std::match_results<typename std::basic_string_view<Ch>::const_iterator> matches;

const Ch *first = &input_[index_];
const Ch *last = &input_[input_.size()];
const auto first = std::next(input_.cbegin(), index_);

if (std::regex_search(first, last, matches, regex, std::regex_constants::match_continuous)) {
std::basic_string<Ch> m(matches[0].first, matches[0].second);
index_ += m.size();
return m;
if (std::regex_search(first, input_.cend(), matches, regex, std::regex_constants::match_continuous)) {
index_ += matches[0].length();
return matches[0].str();
}

return {};
Expand All @@ -196,20 +189,12 @@ class basic_reader {
*/
template <class Pred>
std::optional<std::basic_string<Ch>> match_while(Pred pred) {
using return_type = std::optional<std::basic_string<Ch>>;

size_t start = index_;
while (!eof()) {
const Ch ch = peek();
if (!pred(ch)) {
break;
}

++index_;
}
const size_t count = consume_while(std::move(pred));

std::basic_string<Ch> m(&input_[start], &input_[index_]);
if (!m.empty()) {
return m;
if (count > 0) {
return return_type{ input_.substr(index_ - count, count) };
}

return {};
Expand Down
16 changes: 12 additions & 4 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,18 @@ set_property(TARGET example2 PROPERTY CXX_STANDARD 17)
set_property(TARGET example3 PROPERTY CXX_STANDARD 17)
set_property(TARGET example4 PROPERTY CXX_STANDARD 17)

target_compile_options(example1 PUBLIC -pedantic -W -Wall -Wmissing-field-initializers -Wunused -Wshadow)
target_compile_options(example2 PUBLIC -pedantic -W -Wall -Wmissing-field-initializers -Wunused -Wshadow)
target_compile_options(example3 PUBLIC -pedantic -W -Wall -Wmissing-field-initializers -Wunused -Wshadow)
target_compile_options(example4 PUBLIC -pedantic -W -Wall -Wmissing-field-initializers -Wunused -Wshadow)
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU")
target_compile_options(example1 PUBLIC -pedantic -W -Wall -Wmissing-field-initializers -Wunused -Wshadow)
target_compile_options(example2 PUBLIC -pedantic -W -Wall -Wmissing-field-initializers -Wunused -Wshadow)
target_compile_options(example3 PUBLIC -pedantic -W -Wall -Wmissing-field-initializers -Wunused -Wshadow)
target_compile_options(example4 PUBLIC -pedantic -W -Wall -Wmissing-field-initializers -Wunused -Wshadow)
elseif(MSVC)
# multi-processor compilation, warning-level 4
target_compile_options(example1 PUBLIC /MP /W4)
target_compile_options(example2 PUBLIC /MP /W4)
target_compile_options(example3 PUBLIC /MP /W4)
target_compile_options(example4 PUBLIC /MP /W4)
endif()

add_test(
NAME example1
Expand Down