├── test_package ├── .gitignore ├── CMakeLists.txt ├── example.cpp └── conanfile.py ├── .gitignore ├── include └── deco │ ├── all.h │ ├── types │ ├── arithmetic.h │ ├── strings.h │ ├── containers.h │ ├── list.h │ ├── deque.h │ ├── set.h │ ├── multiset.h │ ├── unordered_set.h │ ├── vector.h │ ├── map.h │ ├── unordered_multiset.h │ ├── multimap.h │ ├── unescaped_string.h │ ├── unordered_map.h │ ├── unordered_multimap.h │ ├── forward_list.h │ ├── singleline_string.h │ ├── string.h │ ├── array.h │ ├── floating_point.h │ ├── integral.h │ └── multiline_string.h │ ├── types.h │ ├── traits.h │ ├── file.h │ ├── entry_container.h │ ├── InputStream.h │ ├── escape.h │ ├── OutputStream.h │ ├── list_container.h │ ├── NVP.h │ ├── Deco.h │ └── list.h ├── tests ├── conanfile.py ├── array.cpp ├── set.cpp ├── list.cpp ├── deque.cpp ├── vector.cpp ├── multiset.cpp ├── forward_list.cpp ├── unordered_set.cpp ├── multimap.cpp ├── unordered_multiset.cpp ├── unordered_map.cpp ├── premake5.lua ├── unordered_multimap.cpp ├── multiline_string.cpp ├── singleline_string.cpp ├── unescaped_string.cpp ├── map.cpp ├── floating_point.cpp ├── serialize_nested.cpp ├── parse.cpp ├── string.cpp ├── integral.cpp ├── deco_list.cpp ├── deco_NVP.cpp └── serialize.cpp ├── conanfile.py ├── deco_tutorial.md ├── README.md ├── delimiter collision free format.txt ├── documentation.md └── LICENSE.txt /test_package/.gitignore: -------------------------------------------------------------------------------- 1 | build -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /test_package/CMakeUserPresets.json 2 | -------------------------------------------------------------------------------- /include/deco/all.h: -------------------------------------------------------------------------------- 1 | #ifndef deco_all_h 2 | #define deco_all_h 3 | 4 | #include "NVP.h" 5 | #include "list.h" 6 | #include "types.h" 7 | #include "file.h" 8 | 9 | #endif -------------------------------------------------------------------------------- /include/deco/types/arithmetic.h: -------------------------------------------------------------------------------- 1 | #ifndef deco_arithmetic_h 2 | #define deco_arithmetic_h 3 | 4 | #include "integral.h" 5 | #include "floating_point.h" 6 | 7 | #endif//guard 8 | -------------------------------------------------------------------------------- /include/deco/types.h: -------------------------------------------------------------------------------- 1 | #ifndef deco_types_h 2 | #define deco_types_h 3 | 4 | #include "types/arithmetic.h" 5 | #include "types/containers.h" 6 | #include "types/strings.h" 7 | 8 | #endif -------------------------------------------------------------------------------- /test_package/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15) 2 | project(PackageTest CXX) 3 | 4 | find_package(deco CONFIG REQUIRED) 5 | 6 | add_executable(example example.cpp) 7 | target_link_libraries(example deco::deco) -------------------------------------------------------------------------------- /include/deco/types/strings.h: -------------------------------------------------------------------------------- 1 | #ifndef deco_types_strings_h 2 | #define deco_types_strings_h 3 | 4 | #include "multiline_string.h" 5 | #include "singleline_string.h" 6 | #include "string.h" 7 | #include "unescaped_string.h" 8 | 9 | #endif -------------------------------------------------------------------------------- /test_package/example.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() 6 | { 7 | using namespace std; 8 | 9 | const string sample { 10 | "hello:\n" 11 | " world!\n" 12 | ":\n" 13 | }; 14 | 15 | deco::parse(sample.begin(), sample.end()); 16 | 17 | cout << sample << endl; 18 | } 19 | -------------------------------------------------------------------------------- /tests/conanfile.py: -------------------------------------------------------------------------------- 1 | from conan import ConanFile 2 | 3 | class install(ConanFile): 4 | settings = "os", "compiler", "build_type", "arch" 5 | requires = ( 6 | "generic_serialization/master", 7 | "rang/3.2", 8 | "fmt/11.1.4", 9 | "strong_type/master" 10 | ) 11 | 12 | def requirements(self): 13 | self.requires("fast_float/8.0.0", transitive_headers=True) 14 | 15 | generators = "PremakeDeps" -------------------------------------------------------------------------------- /include/deco/types/containers.h: -------------------------------------------------------------------------------- 1 | #ifndef deco_types_containers_h 2 | #define deco_types_containers_h 3 | 4 | #include "array.h" 5 | #include "deque.h" 6 | #include "forward_list.h" 7 | #include "list.h" 8 | #include "map.h" 9 | #include "multimap.h" 10 | #include "multiset.h" 11 | #include "set.h" 12 | #include "unordered_map.h" 13 | #include "unordered_multimap.h" 14 | #include "unordered_multiset.h" 15 | #include "unordered_set.h" 16 | #include "vector.h" 17 | 18 | #endif -------------------------------------------------------------------------------- /include/deco/types/list.h: -------------------------------------------------------------------------------- 1 | #ifndef deco_std_list_h 2 | #define deco_std_list_h 3 | 4 | #include "../entry_container.h" 5 | #include 6 | 7 | namespace deco 8 | { 9 | template 10 | struct is_entry_container> : std::true_type {}; 11 | 12 | template constexpr 13 | void read_elements(Stream&& stream, std::list& value) 14 | { 15 | while (!stream.peek_list_end()) 16 | serialize(stream, value.emplace_back()); 17 | } 18 | } 19 | 20 | #endif//guard 21 | -------------------------------------------------------------------------------- /include/deco/types/deque.h: -------------------------------------------------------------------------------- 1 | #ifndef deco_std_deque_h 2 | #define deco_std_deque_h 3 | 4 | #include "../entry_container.h" 5 | #include 6 | 7 | namespace deco 8 | { 9 | template 10 | struct is_entry_container> : std::true_type {}; 11 | 12 | template constexpr 13 | void read_elements(Stream&& stream, std::deque& value) 14 | { 15 | while (!stream.peek_list_end()) 16 | serialize(stream, value.emplace_back()); 17 | } 18 | } 19 | 20 | #endif//guard 21 | -------------------------------------------------------------------------------- /include/deco/types/set.h: -------------------------------------------------------------------------------- 1 | #ifndef deco_std_set_h 2 | #define deco_std_set_h 3 | 4 | #include "../entry_container.h" 5 | #include 6 | 7 | namespace deco 8 | { 9 | template 10 | struct is_entry_container> : std::true_type {}; 11 | 12 | template constexpr 13 | void read_elements(Stream& stream, std::set& value) 14 | { 15 | typename std::set::value_type input; 16 | 17 | while (!stream.peek_list_end()) { 18 | serialize(stream, input); 19 | value.emplace(input); 20 | } 21 | } 22 | } 23 | 24 | #endif//guard 25 | -------------------------------------------------------------------------------- /include/deco/types/multiset.h: -------------------------------------------------------------------------------- 1 | #ifndef deco_std_multiset_h 2 | #define deco_std_multiset_h 3 | 4 | #include "../entry_container.h" 5 | #include 6 | 7 | namespace deco 8 | { 9 | template 10 | struct is_entry_container> : std::true_type {}; 11 | 12 | template constexpr 13 | void read_elements(Stream& stream, std::multiset& value) 14 | { 15 | typename std::multiset::value_type input; 16 | 17 | while (!stream.peek_list_end()) { 18 | serialize(stream, input); 19 | value.emplace(input); 20 | } 21 | } 22 | } 23 | 24 | #endif//guard 25 | -------------------------------------------------------------------------------- /include/deco/traits.h: -------------------------------------------------------------------------------- 1 | #ifndef deco_Traits_h 2 | #define deco_Traits_h 3 | 4 | #include 5 | 6 | namespace deco 7 | { 8 | // trait for types that can be serialized as a single entry 9 | template 10 | struct is_single_entry : std::false_type {}; 11 | 12 | template 13 | constexpr bool is_single_entry_v = is_single_entry>::value; 14 | } 15 | 16 | namespace gs 17 | { 18 | // deco trait 19 | template 20 | struct is_deco : std::false_type {}; 21 | 22 | template 23 | constexpr bool is_deco_v = is_deco>::value; 24 | } 25 | 26 | #endif//guard 27 | -------------------------------------------------------------------------------- /include/deco/types/unordered_set.h: -------------------------------------------------------------------------------- 1 | #ifndef deco_std_unordered_set_h 2 | #define deco_std_unordered_set_h 3 | 4 | #include "../entry_container.h" 5 | #include 6 | 7 | namespace deco 8 | { 9 | template 10 | struct is_entry_container> : std::true_type {}; 11 | 12 | template constexpr 13 | void read_elements(Stream& stream, std::unordered_set& value) 14 | { 15 | typename std::unordered_set::value_type input; 16 | 17 | while (!stream.peek_list_end()) { 18 | serialize(stream, input); 19 | value.emplace(input); 20 | } 21 | } 22 | } 23 | 24 | #endif//guard 25 | -------------------------------------------------------------------------------- /include/deco/types/vector.h: -------------------------------------------------------------------------------- 1 | #ifndef deco_std_vector_h 2 | #define deco_std_vector_h 3 | 4 | #include "../entry_container.h" 5 | #include 6 | 7 | namespace deco 8 | { 9 | template 10 | struct is_entry_container> : std::true_type {}; 11 | 12 | template constexpr 13 | void read_elements(Stream&& stream, std::vector& value) 14 | { 15 | //NOTE: list-entry content should've been read already, now reading children 16 | while (!stream.peek_list_end()) 17 | serialize(stream, value.emplace_back()); 18 | //NOTE: list end will be skipped by the caller 19 | } 20 | } 21 | 22 | #endif//guard 23 | -------------------------------------------------------------------------------- /include/deco/types/map.h: -------------------------------------------------------------------------------- 1 | #ifndef deco_std_map_h 2 | #define deco_std_map_h 3 | 4 | #include "../list_container.h" 5 | #include 6 | 7 | namespace deco 8 | { 9 | template 10 | struct is_list_container> : std::true_type {}; 11 | 12 | template constexpr 13 | void write_element_lists(Stream& stream, std::map& value) 14 | { 15 | write_key_value_lists(stream, value); 16 | } 17 | 18 | template constexpr 19 | void read_element_lists(Stream& stream, std::map& value) 20 | { 21 | read_key_value_lists(stream, value); 22 | } 23 | } 24 | 25 | #endif//guard 26 | -------------------------------------------------------------------------------- /include/deco/types/unordered_multiset.h: -------------------------------------------------------------------------------- 1 | #ifndef deco_std_unordered_multiset_h 2 | #define deco_std_unordered_multiset_h 3 | 4 | #include "../entry_container.h" 5 | #include 6 | 7 | namespace deco 8 | { 9 | template 10 | struct is_entry_container> : std::true_type {}; 11 | 12 | template constexpr 13 | void read_elements(Stream& stream, std::unordered_multiset& value) 14 | { 15 | typename std::unordered_multiset::value_type input; 16 | 17 | while (!stream.peek_list_end()) { 18 | serialize(stream, input); 19 | value.emplace(input); 20 | } 21 | } 22 | } 23 | 24 | #endif//guard 25 | -------------------------------------------------------------------------------- /include/deco/types/multimap.h: -------------------------------------------------------------------------------- 1 | #ifndef deco_std_multimap_h 2 | #define deco_std_multimap_h 3 | 4 | #include "../list_container.h" 5 | #include 6 | 7 | namespace deco 8 | { 9 | template 10 | struct is_list_container> : std::true_type {}; 11 | 12 | template constexpr 13 | void write_element_lists(Stream& stream, std::multimap& value) 14 | { 15 | write_key_value_lists(stream, value); 16 | } 17 | 18 | template constexpr 19 | void read_element_lists(Stream& stream, std::multimap& value) 20 | { 21 | read_key_value_lists(stream, value); 22 | } 23 | } 24 | 25 | #endif//guard 26 | -------------------------------------------------------------------------------- /include/deco/types/unescaped_string.h: -------------------------------------------------------------------------------- 1 | #ifndef deco_unescaped_string_h 2 | #define deco_unescaped_string_h 3 | 4 | #include "../InputStream.h" 5 | #include "../OutputStream.h" 6 | #include 7 | #include 8 | 9 | namespace deco 10 | { 11 | STRONG_TYPE(unescaped_string, std::string); 12 | 13 | template constexpr 14 | std::enable_if_t>> 15 | write(Stream& stream, unescaped_string& value) { 16 | stream.entry(value); 17 | } 18 | 19 | template constexpr 20 | void read(InputStream& stream, unescaped_string& value) 21 | { 22 | value = std::string(stream.parse_entry().content); 23 | } 24 | } 25 | 26 | #endif//guard -------------------------------------------------------------------------------- /include/deco/types/unordered_map.h: -------------------------------------------------------------------------------- 1 | #ifndef deco_std_unordered_map_h 2 | #define deco_std_unordered_map_h 3 | 4 | #include "../list_container.h" 5 | #include 6 | 7 | namespace deco 8 | { 9 | template 10 | struct is_list_container> : std::true_type {}; 11 | 12 | template constexpr 13 | void write_element_lists(Stream& stream, std::unordered_map& value) 14 | { 15 | write_key_value_lists(stream, value); 16 | } 17 | 18 | template constexpr 19 | void read_element_lists(Stream& stream, std::unordered_map& value) 20 | { 21 | read_key_value_lists(stream, value); 22 | } 23 | } 24 | 25 | #endif//guard 26 | -------------------------------------------------------------------------------- /test_package/conanfile.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from conan import ConanFile 4 | from conan.tools.cmake import CMake, cmake_layout 5 | from conan.tools.build import can_run 6 | 7 | 8 | class DecoTestConan(ConanFile): 9 | settings = "os", "compiler", "build_type", "arch" 10 | generators = "CMakeDeps", "CMakeToolchain" 11 | 12 | def requirements(self): 13 | self.requires(self.tested_reference_str) 14 | 15 | def build(self): 16 | cmake = CMake(self) 17 | cmake.configure() 18 | cmake.build() 19 | 20 | def layout(self): 21 | cmake_layout(self) 22 | 23 | def test(self): 24 | if can_run(self): 25 | cmd = os.path.join(self.cpp.build.bindir, "example") 26 | self.run(cmd, env="conanrun") -------------------------------------------------------------------------------- /include/deco/types/unordered_multimap.h: -------------------------------------------------------------------------------- 1 | #ifndef deco_std_unordered_multimap_h 2 | #define deco_std_unordered_multimap_h 3 | 4 | #include "../list_container.h" 5 | #include 6 | 7 | namespace deco 8 | { 9 | template 10 | struct is_list_container> : std::true_type {}; 11 | 12 | template constexpr 13 | void write_element_lists(Stream& stream, std::unordered_multimap& value) 14 | { 15 | write_key_value_lists(stream, value); 16 | } 17 | 18 | template constexpr 19 | void read_element_lists(Stream& stream, std::unordered_multimap& value) 20 | { 21 | read_key_value_lists(stream, value); 22 | } 23 | } 24 | 25 | #endif//guard 26 | -------------------------------------------------------------------------------- /tests/array.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | int main() 12 | { 13 | const std::array val{ "1", "2", "3", "4", "5", }; 14 | 15 | // write 16 | { 17 | deco::write_file file("out.deco"); 18 | auto& stream = file.stream; 19 | 20 | deco::serialize(stream, deco::make_list("std::array", val)); 21 | } 22 | 23 | // read 24 | { 25 | auto file = deco::read_file("out.deco"); 26 | auto& stream = file.stream; 27 | 28 | std::cout << file.str; 29 | 30 | std::array in_val; 31 | deco::serialize(stream, deco::make_list("std::array", in_val)); assert(in_val == val); 32 | } 33 | } -------------------------------------------------------------------------------- /include/deco/types/forward_list.h: -------------------------------------------------------------------------------- 1 | #ifndef deco_std_forward_list_h 2 | #define deco_std_forward_list_h 3 | 4 | #include "../entry_container.h" 5 | #include 6 | 7 | namespace deco 8 | { 9 | template 10 | struct is_entry_container> : std::true_type {}; 11 | 12 | template constexpr 13 | void read_elements(Stream&& stream, std::forward_list& value) 14 | { 15 | // find iterator to last element 16 | auto before_end = value.before_begin(); 17 | for(auto i = value.cbegin(); i != value.cend(); ++i) 18 | ++before_end; 19 | 20 | // read elements into the back of the list 21 | while (!stream.peek_list_end()) { 22 | before_end = value.emplace_after(before_end); 23 | serialize(stream, *before_end); 24 | } 25 | } 26 | } 27 | 28 | #endif//guard 29 | -------------------------------------------------------------------------------- /conanfile.py: -------------------------------------------------------------------------------- 1 | from conan import ConanFile, tools 2 | from conan.tools.files import copy 3 | 4 | 5 | class DecoConan(ConanFile): 6 | name = "deco" 7 | version = "master" 8 | license = "Apache-2.0 WITH LLVM-exception" 9 | url = "https://github.com/Enhex/Deco" 10 | description = "Delimiter Collision Free Format" 11 | homepage = "https://github.com/Enhex/Deco" 12 | # No settings/options are necessary, this is header only 13 | exports_sources = "include/*" 14 | package_type = "header-library" 15 | requires = ( 16 | "generic_serialization/master", 17 | "rang/3.2", 18 | "fmt/11.2.0", 19 | "strong_type/master" 20 | ) 21 | 22 | def requirements(self): 23 | self.requires("fast_float/8.0.0", transitive_headers=True) 24 | 25 | def package(self): 26 | copy(self, "*.h", self.source_folder, self.package_folder) 27 | 28 | def package_id(self): 29 | self.info.clear() -------------------------------------------------------------------------------- /tests/set.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | int main() 11 | { 12 | const std::set val{ "1", "2", "3", "4", "5", }; 13 | 14 | // write 15 | { 16 | deco::OutputStream_indent stream; 17 | 18 | deco::serialize(stream, deco::make_list("std::set", val)); 19 | 20 | std::ofstream os("out.deco", std::ios::binary); 21 | os << stream.str; 22 | } 23 | 24 | // read 25 | { 26 | auto file = std::ifstream("out.deco", std::ios::binary); 27 | std::string file_str{ 28 | std::istreambuf_iterator(file), 29 | std::istreambuf_iterator() }; 30 | 31 | std::cout << file_str; 32 | 33 | auto stream = deco::make_InputStream(file_str.cbegin()); 34 | 35 | std::set in_val; 36 | deco::serialize(stream, deco::make_list("std::set", in_val)); assert(in_val == val); 37 | } 38 | } -------------------------------------------------------------------------------- /tests/list.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | int main() 11 | { 12 | const std::list val{ "1", "2", "3", "4", "5", }; 13 | 14 | // write 15 | { 16 | deco::OutputStream_indent stream; 17 | 18 | deco::serialize(stream, deco::make_list("std::list", val)); 19 | 20 | std::ofstream os("out.deco", std::ios::binary); 21 | os << stream.str; 22 | } 23 | 24 | // read 25 | { 26 | auto file = std::ifstream("out.deco", std::ios::binary); 27 | std::string file_str{ 28 | std::istreambuf_iterator(file), 29 | std::istreambuf_iterator() }; 30 | 31 | std::cout << file_str; 32 | 33 | auto stream = deco::make_InputStream(file_str.cbegin()); 34 | 35 | std::list in_val; 36 | deco::serialize(stream, deco::make_list("std::list", in_val)); assert(in_val == val); 37 | } 38 | } -------------------------------------------------------------------------------- /tests/deque.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | int main() 11 | { 12 | const std::deque val{ "1", "2", "3", "4", "5", }; 13 | 14 | // write 15 | { 16 | deco::OutputStream_indent stream; 17 | 18 | deco::serialize(stream, deco::make_list("std::deque", val)); 19 | 20 | std::ofstream os("out.deco", std::ios::binary); 21 | os << stream.str; 22 | } 23 | 24 | // read 25 | { 26 | auto file = std::ifstream("out.deco", std::ios::binary); 27 | std::string file_str{ 28 | std::istreambuf_iterator(file), 29 | std::istreambuf_iterator() }; 30 | 31 | std::cout << file_str; 32 | 33 | auto stream = deco::make_InputStream(file_str.cbegin()); 34 | 35 | std::deque in_val; 36 | deco::serialize(stream, deco::make_list("std::deque", in_val)); assert(in_val == val); 37 | } 38 | } -------------------------------------------------------------------------------- /tests/vector.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | int main() 11 | { 12 | const std::vector val{ "1", "2", "3", "4", "5" }; 13 | 14 | // write 15 | { 16 | deco::OutputStream_indent stream; 17 | 18 | deco::serialize(stream, deco::make_list("std::vector", val)); 19 | 20 | std::ofstream os("out.deco", std::ios::binary); 21 | os << stream.str; 22 | } 23 | 24 | // read 25 | { 26 | auto file = std::ifstream("out.deco", std::ios::binary); 27 | std::string file_str{ 28 | std::istreambuf_iterator(file), 29 | std::istreambuf_iterator() }; 30 | 31 | std::cout << file_str; 32 | 33 | auto stream = deco::make_InputStream(file_str.cbegin()); 34 | 35 | std::vector in_val; 36 | deco::serialize(stream, deco::make_list("std::vector", in_val)); assert(in_val == val); 37 | } 38 | } -------------------------------------------------------------------------------- /include/deco/file.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "InputStream.h" 4 | #include "OutputStream.h" 5 | #include 6 | 7 | namespace deco 8 | { 9 | // helper class for simplifying writing to file 10 | struct write_file 11 | { 12 | deco::OutputStream_indent stream; 13 | std::string path; 14 | 15 | write_file(std::string path) : 16 | path(path) 17 | { 18 | } 19 | 20 | ~write_file() 21 | { 22 | std::ofstream os(path, std::ios::binary); 23 | os << stream.str; 24 | } 25 | }; 26 | 27 | // helper class for simplifying reading from file 28 | struct read_file 29 | { 30 | InputStream stream; 31 | std::string str; 32 | 33 | read_file(std::string const& path) 34 | { 35 | auto file = std::ifstream(path, std::ios::binary); 36 | str = std::string{ 37 | std::istreambuf_iterator(file), 38 | std::istreambuf_iterator() }; 39 | 40 | stream = make_InputStream(str.cbegin()); 41 | } 42 | }; 43 | } -------------------------------------------------------------------------------- /include/deco/types/singleline_string.h: -------------------------------------------------------------------------------- 1 | #ifndef deco_unescaped_string_h 2 | #define deco_unescaped_string_h 3 | 4 | #include "../InputStream.h" 5 | #include "../OutputStream.h" 6 | #include "string.h" 7 | #include 8 | 9 | namespace deco 10 | { 11 | // strong type for serializing string as a single entry. 12 | // does not verify the content is actually a single line. 13 | STRONG_TYPE(singleline_string, std::string); 14 | 15 | template<> 16 | struct is_single_entry : std::true_type {}; 17 | 18 | template constexpr 19 | std::enable_if_t>> 20 | write(Stream& stream, const singleline_string& value) 21 | { 22 | write(stream, static_cast(value)); 23 | } 24 | 25 | template constexpr 26 | void read(InputStream& stream, singleline_string& value) 27 | { 28 | read(stream, static_cast(value)); 29 | } 30 | } 31 | 32 | #endif//guard -------------------------------------------------------------------------------- /tests/multiset.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | int main() 11 | { 12 | const std::multiset val{ "1", "2", "3", "4", "5", }; 13 | 14 | // write 15 | { 16 | deco::OutputStream_indent stream; 17 | 18 | deco::serialize(stream, deco::make_list("std::multiset", val)); 19 | 20 | std::ofstream os("out.deco", std::ios::binary); 21 | os << stream.str; 22 | } 23 | 24 | // read 25 | { 26 | auto file = std::ifstream("out.deco", std::ios::binary); 27 | std::string file_str{ 28 | std::istreambuf_iterator(file), 29 | std::istreambuf_iterator() }; 30 | 31 | std::cout << file_str; 32 | 33 | auto stream = deco::make_InputStream(file_str.cbegin()); 34 | 35 | std::multiset in_val; 36 | deco::serialize(stream, deco::make_list("std::multiset", in_val)); assert(in_val == val); 37 | } 38 | } -------------------------------------------------------------------------------- /tests/forward_list.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | int main() 11 | { 12 | const std::forward_list val{ "1", "2", "3", "4", "5", }; 13 | 14 | // write 15 | { 16 | deco::OutputStream_indent stream; 17 | 18 | deco::serialize(stream, deco::make_list("std::forward_list", val)); 19 | 20 | std::ofstream os("out.deco", std::ios::binary); 21 | os << stream.str; 22 | } 23 | 24 | // read 25 | { 26 | auto file = std::ifstream("out.deco", std::ios::binary); 27 | std::string file_str{ 28 | std::istreambuf_iterator(file), 29 | std::istreambuf_iterator() }; 30 | 31 | std::cout << file_str; 32 | 33 | auto stream = deco::make_InputStream(file_str.cbegin()); 34 | 35 | std::forward_list in_val; 36 | deco::serialize(stream, deco::make_list("std::forward_list", in_val)); assert(in_val == val); 37 | } 38 | } -------------------------------------------------------------------------------- /tests/unordered_set.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | int main() 11 | { 12 | const std::unordered_set val{ "1", "2", "3", "4", "5", }; 13 | 14 | // write 15 | { 16 | deco::OutputStream_indent stream; 17 | 18 | deco::serialize(stream, deco::make_list("std::unordered_set", val)); 19 | 20 | std::ofstream os("out.deco", std::ios::binary); 21 | os << stream.str; 22 | } 23 | 24 | // read 25 | { 26 | auto file = std::ifstream("out.deco", std::ios::binary); 27 | std::string file_str{ 28 | std::istreambuf_iterator(file), 29 | std::istreambuf_iterator() }; 30 | 31 | std::cout << file_str; 32 | 33 | auto stream = deco::make_InputStream(file_str.cbegin()); 34 | 35 | std::unordered_set in_val; 36 | deco::serialize(stream, deco::make_list("std::unordered_set", in_val)); assert(in_val == val); 37 | } 38 | } -------------------------------------------------------------------------------- /include/deco/types/string.h: -------------------------------------------------------------------------------- 1 | #ifndef deco_string_h 2 | #define deco_string_h 3 | 4 | #include "../InputStream.h" 5 | #include "../OutputStream.h" 6 | #include "../escape.h" 7 | #include 8 | 9 | namespace deco 10 | { 11 | template constexpr 12 | std::enable_if_t>> 13 | write(Stream& stream, const std::string& value) 14 | { 15 | stream.entry(escape_content(value)); 16 | } 17 | 18 | template constexpr 19 | void read(InputStream& stream, std::string& value) 20 | { 21 | value = stream.parse_entry().content; // deco parsing will already unescape 22 | } 23 | 24 | constexpr 25 | std::string& to_string(std::string& value) 26 | { 27 | return value; 28 | } 29 | 30 | constexpr 31 | const std::string& to_string(const std::string& value) 32 | { 33 | return value; 34 | } 35 | 36 | constexpr 37 | std::string&& to_string(std::string&& value) 38 | { 39 | return std::move(value); 40 | } 41 | } 42 | 43 | #endif//guard 44 | -------------------------------------------------------------------------------- /tests/multimap.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | int main() 11 | { 12 | const std::multimap val{ {"a", "1"}, {"b", "2"}, {"c", "3"}, {"d", "4"}, {"e", "5"} }; 13 | 14 | // write 15 | { 16 | deco::OutputStream_indent stream; 17 | 18 | deco::serialize(stream, deco::make_list("std::multimap", val)); 19 | 20 | std::ofstream os("out.deco", std::ios::binary); 21 | os << stream.str; 22 | } 23 | 24 | // read 25 | { 26 | auto file = std::ifstream("out.deco", std::ios::binary); 27 | std::string file_str{ 28 | std::istreambuf_iterator(file), 29 | std::istreambuf_iterator() }; 30 | 31 | std::cout << file_str; 32 | 33 | auto stream = deco::make_InputStream(file_str.cbegin()); 34 | 35 | std::multimap in_val; 36 | deco::serialize(stream, deco::make_list("std::multimap", in_val)); assert(in_val == val); 37 | } 38 | } -------------------------------------------------------------------------------- /tests/unordered_multiset.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | int main() 11 | { 12 | const std::unordered_multiset val{ "1", "2", "3", "4", "5", }; 13 | 14 | // write 15 | { 16 | deco::OutputStream_indent stream; 17 | 18 | deco::serialize(stream, deco::make_list("std::unordered_multiset", val)); 19 | 20 | std::ofstream os("out.deco", std::ios::binary); 21 | os << stream.str; 22 | } 23 | 24 | // read 25 | { 26 | auto file = std::ifstream("out.deco", std::ios::binary); 27 | std::string file_str{ 28 | std::istreambuf_iterator(file), 29 | std::istreambuf_iterator() }; 30 | 31 | std::cout << file_str; 32 | 33 | auto stream = deco::make_InputStream(file_str.cbegin()); 34 | 35 | std::unordered_multiset in_val; 36 | deco::serialize(stream, deco::make_list("std::unordered_multiset", in_val)); assert(in_val == val); 37 | } 38 | } -------------------------------------------------------------------------------- /include/deco/types/array.h: -------------------------------------------------------------------------------- 1 | #ifndef deco_std_array_h 2 | #define deco_std_array_h 3 | 4 | #include "../InputStream.h" 5 | #include "../OutputStream.h" 6 | #include 7 | 8 | namespace deco 9 | { 10 | template constexpr 11 | std::enable_if_t< std::is_base_of_v> > 12 | write(Stream& stream, const std::array& value) 13 | { 14 | write_elements(stream, value); 15 | } 16 | 17 | template constexpr 18 | std::enable_if_t< std::is_base_of_v> > 19 | write(Stream& stream, std::array& value) 20 | { 21 | write(stream, const_cast const&>(value)); 22 | } 23 | 24 | 25 | template constexpr 26 | void read(InputStream& stream, std::array& value) 27 | { 28 | typename std::array::size_type index = 0; 29 | 30 | while (!stream.peek_list_end()) 31 | serialize(stream, value[index++]); 32 | } 33 | } 34 | 35 | #endif//guard 36 | -------------------------------------------------------------------------------- /tests/unordered_map.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | int main() 11 | { 12 | const std::unordered_map val{ {"a", "1"}, {"b", "2"}, {"c", "3"}, {"d", "4"}, {"e", "5"} }; 13 | 14 | // write 15 | { 16 | deco::OutputStream_indent stream; 17 | 18 | deco::serialize(stream, deco::make_list("std::unordered_map", val)); 19 | 20 | std::ofstream os("out.deco", std::ios::binary); 21 | os << stream.str; 22 | } 23 | 24 | // read 25 | { 26 | auto file = std::ifstream("out.deco", std::ios::binary); 27 | std::string file_str{ 28 | std::istreambuf_iterator(file), 29 | std::istreambuf_iterator() }; 30 | 31 | std::cout << file_str; 32 | 33 | auto stream = deco::make_InputStream(file_str.cbegin()); 34 | 35 | std::unordered_map in_val; 36 | deco::serialize(stream, deco::make_list("std::unordered_map", in_val)); assert(in_val == val); 37 | } 38 | } -------------------------------------------------------------------------------- /tests/premake5.lua: -------------------------------------------------------------------------------- 1 | newoption { 2 | trigger = "location", 3 | value = "./", 4 | description = "Where to generate the project.", 5 | } 6 | 7 | if not _OPTIONS["location"] then 8 | _OPTIONS["location"] = "./" 9 | end 10 | 11 | location_dir = _OPTIONS["location"] 12 | 13 | include(location_dir .. "conandeps.premake5.lua") 14 | 15 | workspace("Deco") 16 | location(location_dir) 17 | conan_setup() 18 | 19 | -- tests 20 | 21 | function add_test(name) 22 | project(name) 23 | files{ 24 | name .. ".cpp" 25 | } 26 | 27 | kind "ConsoleApp" 28 | language "C++" 29 | cppdialect "C++17" 30 | targetdir = location_dir .. "bin/%{cfg.buildcfg}" 31 | 32 | includedirs{"../include"} 33 | 34 | flags{"MultiProcessorCompile"} 35 | 36 | filter "configurations:Debug" 37 | defines { "DEBUG" } 38 | symbols "On" 39 | 40 | filter "configurations:Release" 41 | defines { "NDEBUG" } 42 | optimize "On" 43 | end 44 | 45 | test_files = os.matchfiles("./*.cpp") 46 | 47 | for k,v in pairs(test_files) do 48 | add_test( string.sub(v, 0, string.len(v) - string.len(".cpp")) ) 49 | end -------------------------------------------------------------------------------- /tests/unordered_multimap.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | int main() 11 | { 12 | const std::unordered_multimap val{ {"a", "1"}, {"b", "2"}, {"c", "3"}, {"d", "4"}, {"e", "5"} }; 13 | 14 | // write 15 | { 16 | deco::OutputStream_indent stream; 17 | 18 | deco::serialize(stream, deco::make_list("std::unordered_multimap", val)); 19 | 20 | std::ofstream os("out.deco", std::ios::binary); 21 | os << stream.str; 22 | } 23 | 24 | // read 25 | { 26 | auto file = std::ifstream("out.deco", std::ios::binary); 27 | std::string file_str{ 28 | std::istreambuf_iterator(file), 29 | std::istreambuf_iterator() }; 30 | 31 | std::cout << file_str; 32 | 33 | auto stream = deco::make_InputStream(file_str.cbegin()); 34 | 35 | std::unordered_multimap in_val; 36 | deco::serialize(stream, deco::make_list("std::unordered_multimap", in_val)); assert(in_val == val); 37 | } 38 | } -------------------------------------------------------------------------------- /include/deco/entry_container.h: -------------------------------------------------------------------------------- 1 | #ifndef deco_entry_container_h 2 | #define deco_entry_container_h 3 | 4 | #include "InputStream.h" 5 | #include "OutputStream.h" 6 | 7 | namespace deco 8 | { 9 | /* entry container: 10 | each element is serialized as a single entry 11 | */ 12 | 13 | // entry container trait 14 | template 15 | struct is_entry_container : std::false_type {}; 16 | 17 | template 18 | constexpr bool is_entry_container_v = is_entry_container>::value; 19 | 20 | // template for serializing entry containers 21 | template constexpr 22 | std::enable_if_t< 23 | std::is_base_of_v> && 24 | is_entry_container_v 25 | > 26 | write(Stream& stream, T& value) //NOTE: calling serialize prevents using const since it's used for both read & write 27 | { 28 | write_elements(stream, value); 29 | } 30 | 31 | 32 | template constexpr 33 | std::enable_if_t> 34 | read(InputStream& stream, T& value) 35 | { 36 | read_elements(stream, value); 37 | } 38 | } 39 | 40 | #endif//guard 41 | -------------------------------------------------------------------------------- /include/deco/types/floating_point.h: -------------------------------------------------------------------------------- 1 | #ifndef deco_floating_point_h 2 | #define deco_floating_point_h 3 | 4 | #include "../InputStream.h" 5 | #include "../OutputStream.h" 6 | 7 | #include 8 | #include 9 | 10 | namespace deco 11 | { 12 | template 13 | struct is_single_entry> > : std::true_type {}; 14 | 15 | template 16 | std::enable_if_t 17 | , std::string> 18 | to_string(const T& value) 19 | { 20 | return fmt::to_string(value); 21 | } 22 | 23 | template constexpr 24 | std::enable_if_t< 25 | std::is_base_of_v> && 26 | std::is_floating_point_v 27 | > 28 | write(Stream& stream, const T& value) 29 | { 30 | stream.entry(to_string(value)); 31 | } 32 | 33 | template constexpr 34 | std::enable_if_t> 35 | read(InputStream& stream, T& value) 36 | { 37 | const auto content = stream.parse_entry().content; 38 | fast_float::from_chars(content.data(), content.data() + content.size(), value); 39 | } 40 | } 41 | 42 | #endif//guard 43 | -------------------------------------------------------------------------------- /include/deco/types/integral.h: -------------------------------------------------------------------------------- 1 | #ifndef deco_integral_h 2 | #define deco_integral_h 3 | 4 | #include "../InputStream.h" 5 | #include "../OutputStream.h" 6 | 7 | #include 8 | 9 | namespace deco 10 | { 11 | template 12 | struct is_single_entry> > : std::true_type {}; 13 | 14 | 15 | template constexpr 16 | std::enable_if_t 17 | , std::string> 18 | to_string(const T& value) 19 | { 20 | return std::to_string(value); 21 | } 22 | 23 | 24 | //automatically provide default serialization implementation for arithmetic types 25 | template constexpr 26 | std::enable_if_t< 27 | std::is_base_of_v> && 28 | std::is_integral_v 29 | > 30 | write(Stream& stream, const T& value) { 31 | stream.entry(to_string(value)); 32 | } 33 | 34 | template constexpr 35 | std::enable_if_t> 36 | read(InputStream& stream, T& value) 37 | { 38 | const auto content = stream.parse_entry().content; 39 | fast_float::from_chars(content.data(), content.data() + content.size(), value); 40 | } 41 | } 42 | 43 | #endif//guard 44 | -------------------------------------------------------------------------------- /tests/multiline_string.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | int main() 11 | { 12 | const deco::multiline_string ml_str_val{ "multi:\n line\nstring'" }; 13 | const deco::multiline_string ml_str_empty{ "" }; 14 | 15 | // write 16 | { 17 | deco::OutputStream_indent stream; 18 | 19 | deco::serialize(stream, deco::make_list("multiline_string", ml_str_val)); 20 | deco::serialize(stream, deco::make_list("empty", ml_str_empty)); 21 | 22 | std::ofstream os("out.deco", std::ios::binary); 23 | os << stream.str; 24 | } 25 | 26 | // read 27 | { 28 | auto file = std::ifstream("out.deco", std::ios::binary); 29 | std::string file_str{ 30 | std::istreambuf_iterator(file), 31 | std::istreambuf_iterator() }; 32 | 33 | std::cout << file_str; 34 | 35 | auto stream = deco::make_InputStream(file_str.cbegin()); 36 | 37 | deco::multiline_string ml_str; 38 | deco::serialize(stream, deco::make_list("multiline_string", ml_str)); assert(ml_str == ml_str_val); 39 | deco::serialize(stream, deco::make_list("empty", ml_str)); assert(ml_str == ml_str_empty); 40 | } 41 | } -------------------------------------------------------------------------------- /tests/singleline_string.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | int main() 12 | { 13 | const std::map val { 14 | { deco::singleline_string("a"), deco::singleline_string("1") }, 15 | { deco::singleline_string("b"), deco::singleline_string("2") }, 16 | { deco::singleline_string("c"), deco::singleline_string("3") } 17 | }; 18 | 19 | // write 20 | { 21 | deco::OutputStream_indent stream; 22 | 23 | deco::serialize(stream, deco::make_list("singleline_string", val)); 24 | 25 | std::ofstream os("out.deco", std::ios::binary); 26 | os << stream.str; 27 | } 28 | 29 | // read 30 | { 31 | auto file = std::ifstream("out.deco", std::ios::binary); 32 | std::string file_str{ 33 | std::istreambuf_iterator(file), 34 | std::istreambuf_iterator() }; 35 | 36 | std::cout << file_str; 37 | 38 | auto stream = deco::make_InputStream(file_str.cbegin()); 39 | 40 | std::map in_val; 41 | deco::serialize(stream, deco::make_list(in_val)); assert(in_val == val); 42 | } 43 | } -------------------------------------------------------------------------------- /tests/unescaped_string.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | int main() 10 | { 11 | // whitespace will be written without escaping, thus skipped when reading 12 | const deco::unescaped_string str_val { "string" }; 13 | const deco::unescaped_string str_space { std::string(" ") + str_val }; 14 | const deco::unescaped_string str_tab { std::string(" ") + str_val }; 15 | 16 | // write 17 | { 18 | deco::OutputStream_indent stream; 19 | 20 | gs::serializer(stream, 21 | str_val, 22 | str_space, 23 | str_tab); 24 | 25 | std::ofstream os("out.deco", std::ios::binary); 26 | os << stream.str; 27 | } 28 | 29 | // read 30 | { 31 | auto file = std::ifstream("out.deco", std::ios::binary); 32 | std::string file_str{ 33 | std::istreambuf_iterator(file), 34 | std::istreambuf_iterator() }; 35 | 36 | std::cout << file_str; 37 | 38 | auto stream = deco::make_InputStream(file_str.cbegin()); 39 | 40 | deco::unescaped_string str; 41 | deco::serialize(stream, str); assert(str == str_val); 42 | deco::serialize(stream, str); assert(str == str_val); 43 | deco::serialize(stream, str); assert(str == str_val); 44 | } 45 | } -------------------------------------------------------------------------------- /tests/map.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | int main() 11 | { 12 | using T1 = std::map; 13 | const T1 val{ {"a", "1"}, {"b", "2"}, {"c", "3"}, {"d", "4"}, {"e", "5"} }; 14 | using T2 = std::map>; 15 | const T2 val2{ 16 | { "a", { { "1", "1" } } }, 17 | { "b", { { "2", "2" } } } 18 | }; 19 | 20 | // write 21 | { 22 | deco::OutputStream_indent stream; 23 | 24 | deco::serialize(stream, deco::make_list("std::map", val)); 25 | deco::serialize(stream, deco::make_list("std::map2", val2)); 26 | 27 | std::ofstream os("out.deco", std::ios::binary); 28 | os << stream.str; 29 | } 30 | 31 | // read 32 | { 33 | auto file = std::ifstream("out.deco", std::ios::binary); 34 | std::string file_str{ 35 | std::istreambuf_iterator(file), 36 | std::istreambuf_iterator() }; 37 | 38 | std::cout << file_str; 39 | 40 | auto stream = deco::make_InputStream(file_str.cbegin()); 41 | 42 | T1 in_val; 43 | deco::serialize(stream, deco::make_list("std::map", in_val)); assert(in_val == val); 44 | T2 in_val2; 45 | deco::serialize(stream, deco::make_list("std::map2", in_val2)); assert(in_val2 == val2); 46 | } 47 | } -------------------------------------------------------------------------------- /tests/floating_point.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | constexpr auto floating_test_value = 123.125; // 0.125 shouldn't have floating point precision error 8 | 9 | int main() 10 | { 11 | // write 12 | { 13 | deco::OutputStream_indent stream; 14 | 15 | { 16 | const float value = floating_test_value; 17 | deco::serialize(stream, value); 18 | } 19 | { 20 | const double value = floating_test_value; 21 | deco::serialize(stream, value); 22 | } 23 | { 24 | const long double value = floating_test_value; 25 | deco::serialize(stream, value); 26 | } 27 | 28 | std::ofstream os("out.deco", std::ios::binary); 29 | os << stream.str; 30 | } 31 | 32 | // read 33 | { 34 | auto file = std::ifstream("out.deco", std::ios::binary); 35 | std::string file_str{ 36 | std::istreambuf_iterator(file), 37 | std::istreambuf_iterator() }; 38 | 39 | std::cout << file_str; 40 | 41 | auto stream = deco::make_InputStream(file_str.cbegin()); 42 | 43 | { 44 | float value; 45 | deco::serialize(stream, value); 46 | assert(value == floating_test_value); 47 | } 48 | { 49 | double value; 50 | deco::serialize(stream, value); 51 | assert(value == floating_test_value); 52 | } 53 | { 54 | long double value; 55 | deco::serialize(stream, value); 56 | assert(value == floating_test_value); 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /include/deco/types/multiline_string.h: -------------------------------------------------------------------------------- 1 | #ifndef deco_multiline_string_h 2 | #define deco_multiline_string_h 3 | 4 | #include "../InputStream.h" 5 | #include "../OutputStream.h" 6 | #include 7 | #include 8 | #include 9 | 10 | namespace deco 11 | { 12 | STRONG_TYPE(multiline_string, std::string); 13 | 14 | template constexpr 15 | std::enable_if_t>> 16 | write(Stream& stream, const multiline_string& value) 17 | { 18 | // in case of an empty string 19 | if (value.empty()) { 20 | stream.entry(value); 21 | return; 22 | } 23 | 24 | auto line_start = value.cbegin(); 25 | auto iter = line_start; 26 | 27 | auto write_line = [&] { 28 | stream.entry( 29 | escape_content(std::string_view(&*line_start, std::distance(line_start, iter)))); 30 | }; 31 | 32 | while (iter != value.cend()) { 33 | if (*iter == entry_delimiter) { 34 | write_line(); 35 | line_start += std::distance(line_start, ++iter); // also skipping the entry delimiter 36 | } 37 | else // to avoid double advancing after skipping entry delimiter 38 | ++iter; 39 | } 40 | 41 | write_line(); 42 | } 43 | 44 | template 45 | void read(InputStream& stream, multiline_string& value) 46 | { 47 | value.clear(); 48 | 49 | //NOTE: list-entry content should've been read already, now reading children 50 | if (!stream.peek_list_end()) { 51 | serialize(stream, static_cast(value)); 52 | } 53 | 54 | std::string str; 55 | 56 | while (!stream.peek_list_end()) { 57 | serialize(stream, str); 58 | (value += '\n') += str; 59 | } 60 | } 61 | } 62 | 63 | #endif//guard 64 | -------------------------------------------------------------------------------- /tests/serialize_nested.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | using namespace std; 12 | 13 | struct A { 14 | int i = 2; 15 | string s = "an entry"; 16 | }; 17 | 18 | namespace deco 19 | { 20 | template constexpr 21 | void serialize(Stream& stream, A& value) // shouldn't be const to allow reading 22 | { 23 | gs::serializer(stream, 24 | begin_list("a"), 25 | make_NVP("i", value.i), 26 | make_NVP("s", value.s), 27 | end_list); 28 | } 29 | } 30 | 31 | 32 | struct B { 33 | A a; 34 | vector v{ 0,1,2,3,4,10 }; 35 | vector va{ A{}, A{}, A{} }; 36 | float f = 3.25; 37 | }; 38 | 39 | namespace deco 40 | { 41 | template constexpr 42 | void serialize(Stream& stream, B& value) 43 | { 44 | gs::serializer(stream, 45 | value.a, 46 | make_list("v", value.v), 47 | make_list("va", value.va), 48 | make_NVP("f", value.f)); 49 | } 50 | } 51 | 52 | 53 | int main() 54 | { 55 | // write 56 | { 57 | B b; 58 | int i = 5; 59 | 60 | ofstream os("out.deco", ios::binary); 61 | deco::OutputStream_indent stream; 62 | 63 | gs::serializer(stream, b, i); 64 | 65 | os << stream.str; 66 | } 67 | 68 | // read 69 | { 70 | B b; 71 | b.f = 0; 72 | b.v.clear(); 73 | b.a.i = 0; 74 | b.a.s = '0'; 75 | 76 | int i = 0; 77 | 78 | auto file = ifstream("out.deco", ios::binary); 79 | string file_str{ 80 | istreambuf_iterator(file), 81 | istreambuf_iterator()}; 82 | 83 | cout << file_str; 84 | 85 | auto stream = deco::make_InputStream(file_str.cbegin()); 86 | 87 | gs::serializer(stream, b, i); 88 | } 89 | } -------------------------------------------------------------------------------- /include/deco/InputStream.h: -------------------------------------------------------------------------------- 1 | #ifndef deco_InputStream_h 2 | #define deco_InputStream_h 3 | 4 | #include "Deco.h" 5 | #include "traits.h" 6 | #include 7 | 8 | namespace deco 9 | { 10 | template 11 | struct InputStream 12 | { 13 | InputStream(Iterator begin) noexcept : position(begin) {} 14 | 15 | InputStream() = default; 16 | 17 | Iterator position; 18 | 19 | constexpr Entry parse_entry() { 20 | return deco::parse_entry(position); 21 | } 22 | 23 | constexpr Entry peek_entry() { 24 | return deco::peek_entry(position); 25 | } 26 | 27 | constexpr bool peek_list_end() { 28 | return deco::peek_list_end(position); 29 | } 30 | }; 31 | 32 | template 33 | constexpr auto make_InputStream(Iterator&& iter) 34 | { 35 | return InputStream{std::forward(iter)}; 36 | } 37 | 38 | // used to skip an entry without parsing it into a type 39 | struct skip_t {}; 40 | constexpr skip_t skip; 41 | } 42 | 43 | namespace gs 44 | { 45 | template struct is_input> : std::true_type {}; 46 | 47 | template struct is_deco> : std::true_type {}; 48 | 49 | template 50 | constexpr auto is_deco_input_v = is_deco_v && is_input_v; 51 | 52 | 53 | } 54 | 55 | namespace deco 56 | { 57 | // serialize input deco 58 | template constexpr 59 | std::enable_if_t> 60 | serialize(Stream& stream, T&& value) 61 | { 62 | read(stream, std::forward(value)); 63 | } 64 | 65 | // skip entry without parsing 66 | template constexpr 67 | std::enable_if_t> 68 | serialize(Stream& stream, const deco::skip_t&) 69 | { 70 | stream.parse_entry(); 71 | } 72 | } 73 | 74 | #endif//guard 75 | -------------------------------------------------------------------------------- /deco_tutorial.md: -------------------------------------------------------------------------------- 1 | # Deco Tutorial 2 | 3 | ### Delimiters 4 | 5 | - `entry delimiter` = `\n` (newline) 6 | - `structure delimiter` = `:` 7 | - `content delimiter` = `'` 8 | 9 | ### Format structure 10 | 11 | #### Entry 12 | Each entry is separated by an `entry delimiter`. 13 | 14 | ``` 15 | first entry 16 | second entry 17 | third entry 18 | ``` 19 | 20 | The whitespace (spaces and tabs) at the beginning of the entry is ignored, and not part of the entry's content. 21 | 22 | Example of an entry starting with whitespace: 23 | ``` 24 | a b c 25 | ``` 26 | The content of the entry is `a b c`. 27 | 28 | The `content delimiter` is used to include whitespace at the beginning of the content. 29 | 30 | For example: 31 | ``` 32 | ' a b c 33 | ``` 34 | The content of the entry is     a b c. 35 | 36 | 37 | #### List Entry 38 | 39 | A list entry can contain child entries, including other list entries. 40 | A list entry is an entry that ends with the `structure delimiter` before the `entry delimiter`. 41 | A list entry's content is called the list's name. 42 | 43 | A list end entry is an entry that only contains, after skipping whitespace, a `structure delimiter` before an `entry delimiter`. 44 | 45 | 46 | ``` 47 | name: 48 | child entry 49 | child list entry: 50 | child entry 51 | : 52 | : 53 | ``` 54 | 55 | The content of the root list entry is `name`. 56 | 57 | To end a regular entry with a `structure delimiter` character, the `content delimiter` can be used: 58 | ``` 59 | not a list:' 60 | ``` 61 | The content of the entry is `not a list:`. 62 | 63 | ##### Anonymous List Entry 64 | 65 | A list entry can be created with empty content by using a `content delimiter` before the `structure delimiter`. 66 | The `content delimiter` isn't part of the content, and it prevents the entry from being interpreted as a list end. 67 | 68 | ``` 69 | ': 70 | child entry 71 | : 72 | ``` 73 | -------------------------------------------------------------------------------- /tests/parse.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | void print(const deco::EntryObject& entry, size_t& depth); 6 | void print(const deco::EntryObject& entry) { 7 | size_t depth = 0; 8 | print(entry, depth); 9 | } 10 | 11 | void print(const deco::EntryObject& entry, size_t& depth) { 12 | using namespace std; 13 | 14 | auto indent = [&depth]() { 15 | for (size_t i = 0; i < depth; ++i) 16 | cout << '\t'; 17 | }; 18 | indent(); 19 | 20 | cout << rang::bg::blue << entry.content << rang::bg::black; 21 | 22 | if (!entry.entries.empty()) 23 | { 24 | cout << rang::fgB::green << ":" << rang::fg::gray << '\n'; 25 | 26 | ++depth; 27 | for (const auto& child : entry.entries) 28 | print(child, depth); 29 | --depth; 30 | 31 | indent(); 32 | cout << rang::fgB::red << ":" << rang::fg::gray; 33 | } 34 | 35 | cout << '\n'; 36 | } 37 | 38 | 39 | int main() 40 | { 41 | using namespace std; 42 | 43 | string sample{ 44 | "hello\n" 45 | "world!:\n" 46 | " from\n" 47 | " Deco:\n" 48 | " Delimiter\n" 49 | " collision\n" 50 | " Free\n" 51 | " Format\n" 52 | " :\n" 53 | ":\n" 54 | "content ending with content delimiter'\n" 55 | "content ending with structure delimiter:'\n" 56 | "' content starting with whitespace\n" 57 | ": structure : delimiter : and ' content ' delimiter ' as : content ' without : escaping\n" 58 | "':\n" 59 | " anonymous\n" 60 | " list\n" 61 | ":\n" 62 | "no:\n" 63 | "need:\n" 64 | "to:\n" 65 | "indent\n" 66 | ":\n" 67 | ":\n" 68 | ":\n" 69 | }; 70 | 71 | cout << rang::fg::yellow << "sample:" << rang::fg::gray << endl; 72 | cout << sample << endl << endl; 73 | 74 | // make sure ending with entry delimiter 75 | if (sample.back() != deco::entry_delimiter) { 76 | sample.push_back(deco::entry_delimiter); 77 | } 78 | 79 | auto entries = deco::parse(sample.begin(), sample.end()); 80 | 81 | cout << rang::fg::yellow << "result:" << rang::fg::gray << endl; 82 | for (const auto& entry : entries) 83 | print(entry); 84 | 85 | cout << endl; 86 | } -------------------------------------------------------------------------------- /tests/string.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | int main() 10 | { 11 | const std::string str_val = "string"; 12 | const std::string str_empty = ""; 13 | const std::string str_space = " string"; 14 | const std::string str_tab = " string"; 15 | const std::string str_spc_tab = " string"; 16 | const std::string str_content_begin = "' string"; 17 | const std::string str_content_end = "string'"; 18 | const std::string str_content_end2 = "string''"; 19 | const std::string str_structure = "string:"; 20 | const std::string str_structure_con_end = "string:'"; 21 | 22 | // write 23 | { 24 | deco::OutputStream_indent stream; 25 | 26 | gs::serializer(stream, 27 | str_val, 28 | str_empty, 29 | str_space, 30 | str_tab, 31 | str_spc_tab, 32 | str_content_begin, 33 | str_content_end, 34 | str_content_end2, 35 | str_structure, 36 | str_structure_con_end); 37 | 38 | std::ofstream os("out.deco", std::ios::binary); 39 | os << stream.str; 40 | } 41 | 42 | // read 43 | { 44 | auto file = std::ifstream("out.deco", std::ios::binary); 45 | std::string file_str{ 46 | std::istreambuf_iterator(file), 47 | std::istreambuf_iterator() }; 48 | 49 | std::cout << file_str; 50 | 51 | auto stream = deco::make_InputStream(file_str.cbegin()); 52 | 53 | const auto serialize = [&stream](auto&& t) { 54 | deco::serialize(stream, t); 55 | }; 56 | 57 | std::string str; 58 | serialize(str); assert(str == str_val); 59 | serialize(str); assert(str == str_empty); 60 | serialize(str); assert(str == str_space); 61 | serialize(str); assert(str == str_tab); 62 | serialize(str); assert(str == str_spc_tab); 63 | serialize(str); assert(str == str_content_begin); 64 | serialize(str); assert(str == str_content_end); 65 | serialize(str); assert(str == str_content_end2); 66 | serialize(str); assert(str == str_structure); 67 | serialize(str); assert(str == str_structure_con_end); 68 | } 69 | } -------------------------------------------------------------------------------- /tests/integral.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | 11 | template 12 | void write_type(Stream& stream) 13 | { 14 | if constexpr (std::is_signed_v) { 15 | auto value = std::numeric_limits::min(); 16 | gs::serializer(stream, value); 17 | } 18 | else { 19 | auto value = std::numeric_limits::max(); 20 | gs::serializer(stream, value); 21 | } 22 | }; 23 | 24 | template 25 | void read_type(deco::InputStream& stream) 26 | { 27 | T value; 28 | gs::serializer(stream, value); 29 | 30 | if constexpr (std::is_signed_v) 31 | assert(value == std::numeric_limits::min()); 32 | else 33 | assert(value == std::numeric_limits::max()); 34 | }; 35 | 36 | 37 | int main() 38 | { 39 | // write 40 | { 41 | deco::OutputStream_indent stream; 42 | 43 | write_type(stream); 44 | write_type(stream); 45 | write_type(stream); 46 | write_type(stream); 47 | write_type(stream); 48 | write_type(stream); 49 | write_type(stream); 50 | write_type(stream); 51 | write_type(stream); 52 | write_type(stream); 53 | 54 | std::ofstream os("out.deco", std::ios::binary); 55 | os << stream.str; 56 | } 57 | 58 | // read 59 | { 60 | auto file = std::ifstream("out.deco", std::ios::binary); 61 | std::string file_str{ 62 | std::istreambuf_iterator(file), 63 | std::istreambuf_iterator() }; 64 | 65 | std::cout << file_str; 66 | 67 | auto stream = deco::make_InputStream(file_str.cbegin()); 68 | 69 | read_type(stream); 70 | read_type(stream); 71 | read_type(stream); 72 | read_type(stream); 73 | read_type(stream); 74 | read_type(stream); 75 | read_type(stream); 76 | read_type(stream); 77 | read_type(stream); 78 | read_type(stream); 79 | } 80 | } -------------------------------------------------------------------------------- /include/deco/escape.h: -------------------------------------------------------------------------------- 1 | #ifndef deco_escape_h 2 | #define deco_escape_h 3 | 4 | namespace deco 5 | { 6 | // escape content delimiters 7 | template 8 | auto escape_content(const std::string_view content) 9 | { 10 | std::string str; 11 | 12 | if (content.empty()) 13 | return str; 14 | 15 | if constexpr(escape_begin) 16 | { 17 | // escape content starting with whitespace or content delimiter 18 | const auto first = content.front(); 19 | if (first == ' ' || 20 | first == '\t' || 21 | first == deco::content_delimiter) 22 | str.insert(str.begin(), deco::content_delimiter); 23 | } 24 | 25 | str += content; 26 | 27 | 28 | if constexpr(escape_end) 29 | { 30 | // escape content ending with content delimiter or structure delimiter 31 | const auto last = content.back(); 32 | if (last == deco::structure_delimiter || 33 | last == deco::content_delimiter) 34 | str += deco::content_delimiter; 35 | } 36 | 37 | return str; 38 | } 39 | 40 | // remove delimiters used to escape content 41 | template constexpr // entry parsing already removes content begin delimiter 42 | void unescape_content(std::string_view& content) 43 | { 44 | // erase start content delimiter 45 | if constexpr (unescape_content_begin) { 46 | if (content.front() == content_delimiter) 47 | content.remove_prefix(1); 48 | } 49 | 50 | // erase end content delimiter 51 | if (content.back() == content_delimiter) 52 | content.remove_suffix(1); 53 | } 54 | 55 | // take const& and return a copy 56 | template constexpr 57 | auto unescape_content(const std::string_view& ref_content) 58 | { 59 | auto content = ref_content; 60 | unescape_content(content); 61 | return content; 62 | } 63 | 64 | // rvalue 65 | template constexpr 66 | auto unescape_content(std::string_view&& content) 67 | { 68 | unescape_content(content); 69 | return content; 70 | } 71 | 72 | // string 73 | template constexpr 74 | void unescape_content(std::string& content) 75 | { 76 | // erase start content delimiter 77 | if constexpr (unescape_content_begin) { 78 | if (content.front() == content_delimiter) 79 | content.erase(0, 1); 80 | } 81 | 82 | // erase end content delimiter 83 | if (content.back() == content_delimiter) 84 | content.pop_back(); 85 | } 86 | } 87 | 88 | #endif//guard -------------------------------------------------------------------------------- /tests/deco_list.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | 10 | int main() 11 | { 12 | std::string i = "2"; 13 | std::string i_ = "0"; 14 | 15 | std::string name = "read name"; 16 | 17 | // write file 18 | { 19 | deco::OutputStream_indent stream; 20 | 21 | deco::serialize(stream, deco::make_list(i)); 22 | deco::serialize(stream, deco::make_list("ignore name", i)); 23 | deco::serialize(stream, deco::make_list(name, i)); 24 | 25 | gs::serializer(stream, deco::make_list(i)); 26 | gs::serializer(stream, deco::make_list("ignore name", i)); 27 | gs::serializer(stream, deco::make_list(name, i)); 28 | 29 | deco::serialize(stream, deco::begin_list()); 30 | deco::serialize(stream, deco::begin_list("ignore name")); 31 | deco::serialize(stream, deco::begin_list(name)); 32 | deco::serialize(stream, deco::end_list); 33 | deco::serialize(stream, deco::end_list); 34 | deco::serialize(stream, deco::end_list); 35 | 36 | gs::serializer(stream, deco::begin_list()); 37 | gs::serializer(stream, deco::begin_list("ignore name")); 38 | gs::serializer(stream, deco::begin_list(name)); 39 | gs::serializer(stream, deco::end_list); 40 | gs::serializer(stream, deco::end_list); 41 | gs::serializer(stream, deco::end_list); 42 | 43 | 44 | std::ofstream os("out.deco", std::ios::binary); 45 | os << stream.str; 46 | } 47 | 48 | // read file 49 | { 50 | auto file = std::ifstream("out.deco", std::ios::binary); 51 | std::string file_str{ 52 | std::istreambuf_iterator(file), 53 | std::istreambuf_iterator()}; 54 | 55 | auto stream = deco::make_InputStream(file_str.cbegin()); 56 | 57 | deco::serialize(stream, deco::make_list(i_)); assert(i_ == i); 58 | deco::serialize(stream, deco::make_list("ignore name", i_));assert(i_ == i); 59 | deco::serialize(stream, deco::make_list(name, i_)); assert(i_ == i); 60 | 61 | gs::serializer(stream, deco::make_list(i_)); assert(i_ == i); 62 | gs::serializer(stream, deco::make_list("ignore name", i_)); assert(i_ == i); 63 | gs::serializer(stream, deco::make_list(name, i_)); assert(i_ == i); 64 | 65 | deco::serialize(stream, deco::begin_list()); 66 | deco::serialize(stream, deco::begin_list("ignore name")); 67 | deco::serialize(stream, deco::begin_list(name)); 68 | deco::serialize(stream, deco::end_list); 69 | deco::serialize(stream, deco::end_list); 70 | deco::serialize(stream, deco::end_list); 71 | 72 | gs::serializer(stream, deco::begin_list()); 73 | gs::serializer(stream, deco::begin_list("ignore name")); 74 | gs::serializer(stream, deco::begin_list(name)); 75 | gs::serializer(stream, deco::end_list); 76 | gs::serializer(stream, deco::end_list); 77 | gs::serializer(stream, deco::end_list); 78 | } 79 | } -------------------------------------------------------------------------------- /include/deco/OutputStream.h: -------------------------------------------------------------------------------- 1 | #ifndef deco_OutputStream_h 2 | #define deco_OutputStream_h 3 | 4 | #include "Deco.h" 5 | #include "traits.h" 6 | #include 7 | 8 | namespace deco 9 | { 10 | struct OutputStream 11 | { 12 | std::string str; 13 | 14 | void entry(const std::string& content) { 15 | (str += content) += '\n'; 16 | } 17 | 18 | void begin_list(std::string&& content) { 19 | entry(content += ':'); 20 | } 21 | void begin_list(const std::string_view& content) { 22 | begin_list(std::string(content)); 23 | } 24 | void begin_list(const char* content) { 25 | begin_list(std::string(content)); 26 | } 27 | 28 | void begin_anonymous_list() { 29 | entry("':"); 30 | } 31 | 32 | void end_list() { 33 | entry(":"); 34 | } 35 | }; 36 | 37 | // Indented output 38 | //NOTE: use own version of functions for indenting 39 | struct OutputStream_indent : OutputStream 40 | { 41 | using OutputStream::OutputStream; 42 | 43 | void entry(const std::string& content) { 44 | indent(); 45 | OutputStream::entry(content); 46 | } 47 | 48 | void begin_list(std::string&& content) { 49 | entry(content += ':'); 50 | ++indent_level; 51 | } 52 | void begin_list(const std::string_view& content) { 53 | begin_list(std::string(content)); 54 | } 55 | void begin_list(const char* content) { 56 | begin_list(std::string(content)); 57 | } 58 | 59 | void begin_anonymous_list() { 60 | entry("':"); 61 | ++indent_level; 62 | } 63 | 64 | void end_list() { 65 | --indent_level; 66 | entry(":"); 67 | } 68 | 69 | protected: 70 | unsigned indent_level = 0; 71 | 72 | void indent() { 73 | for (unsigned n = 0; n < indent_level; ++n) 74 | str += '\t'; 75 | } 76 | }; 77 | 78 | template constexpr 79 | void write_elements(Stream&& stream, T& value) 80 | { 81 | for (auto& e : value) 82 | serialize(stream, e); 83 | } 84 | } 85 | 86 | 87 | namespace gs 88 | { 89 | template<> struct is_output : std::true_type {}; 90 | template<> struct is_output : std::true_type {}; 91 | 92 | template<> struct is_deco : std::true_type {}; 93 | template<> struct is_deco : std::true_type {}; 94 | 95 | template 96 | constexpr auto is_deco_output_v = is_deco_v && is_output_v; 97 | } 98 | 99 | namespace deco 100 | { 101 | // serialize output deco 102 | // Using unqualified name lookup to find serialize(), so it should be called from within a deco namespace 103 | template constexpr 104 | std::enable_if_t> 105 | serialize(Stream& stream, T&& value) 106 | { 107 | write(stream, std::forward(value)); 108 | } 109 | } 110 | 111 | #endif//guard 112 | -------------------------------------------------------------------------------- /include/deco/list_container.h: -------------------------------------------------------------------------------- 1 | #ifndef deco_list_container_h 2 | #define deco_list_container_h 3 | 4 | #include "InputStream.h" 5 | #include "OutputStream.h" 6 | 7 | namespace deco 8 | { 9 | /* list container: 10 | each element is serialized as a list entry 11 | */ 12 | 13 | // list container trait 14 | template 15 | struct is_list_container : std::false_type {}; 16 | 17 | template 18 | constexpr bool is_list_container_v = is_list_container>::value; 19 | 20 | // template for serializing entry containers 21 | template constexpr 22 | std::enable_if_t< 23 | std::is_base_of_v> && 24 | is_list_container_v 25 | > 26 | write(Stream& stream, T& value) 27 | { 28 | write_element_lists(stream, const_cast&>(value)); 29 | } 30 | 31 | 32 | template constexpr 33 | std::enable_if_t> 34 | read(InputStream& stream, T& value) 35 | { 36 | read_element_lists(stream, value); 37 | } 38 | 39 | 40 | // write the elements of a map type as lists. 41 | // If the key type can be serialized as a single entry, write it as a list-entry with the value serialized into it 42 | template constexpr 43 | void write_key_value_lists(Stream& stream, Map& value) 44 | { 45 | for (auto& e : value) 46 | { 47 | if constexpr(is_single_entry_v) 48 | { 49 | if constexpr(std::is_base_of_v>) 50 | stream.begin_list(e.first); 51 | else 52 | stream.begin_list(to_string(e.first)); 53 | serialize(stream, e.second); 54 | stream.end_list(); 55 | } 56 | else 57 | { 58 | // key may require more than a single entry to be serialized 59 | stream.begin_list("key"); 60 | serialize(stream, e.first); 61 | stream.end_list(); 62 | stream.begin_list("value"); 63 | serialize(stream, e.second); 64 | stream.end_list(); 65 | } 66 | } 67 | } 68 | 69 | template constexpr 70 | void read_key_value_lists(Stream& stream, Map& value) 71 | { 72 | while (!stream.peek_list_end()) 73 | { 74 | typename Map::key_type key_input; 75 | typename Map::mapped_type mapped_input; 76 | 77 | if constexpr(is_single_entry_v) 78 | { 79 | serialize(stream, key_input); // read list name 80 | serialize(stream, mapped_input);// read value 81 | stream.parse_entry(); // skip list end 82 | } 83 | else 84 | { 85 | serialize(stream, skip); // skip list name 86 | serialize(stream, key_input); // read value 87 | stream.parse_entry(); // skip list end 88 | 89 | serialize(stream, skip); // skip list name 90 | serialize(stream, mapped_input);// read value 91 | stream.parse_entry(); // skip list end 92 | } 93 | 94 | value.emplace(std::make_pair(key_input, mapped_input)); 95 | } 96 | } 97 | } 98 | 99 | #endif//guard 100 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Deco - Delimiter Collision Free Format 2 | 3 | Deco is a human readable text format which avoids the [delimiter collision problem](https://en.wikipedia.org/wiki/Delimiter#Delimiter_collision). 4 | 5 | Deco has minimal and simple syntax, while still being able to represent grouping and nesting. 6 | It's very easy to read and write by humans. 7 | 8 | For example this is how the [JSON example from Wikipedia](https://en.wikipedia.org/wiki/JSON#Example) would look like in Deco: 9 | 10 | 11 | 12 | 15 | 18 | 19 | 20 | 45 | 77 | 78 |
13 | Deco 14 | 16 | JSON 17 |
21 |
 22 | firstName: John
 23 | lastName: Smith
 24 | isAlive: true
 25 | age: 27
 26 | address:
 27 |     streetAddress: 21 2nd Street
 28 |     city: New York
 29 |     state: NY
 30 |     postalCode: 10021-3100
 31 | :
 32 | phoneNumbers:
 33 |     type: home
 34 |     number: 212 555-1234
 35 |     type: office
 36 |     number: 646 555-4567
 37 |     type: mobile
 38 |     number: 123 456-7890
 39 | :
 40 | children:
 41 | :
 42 | spouse: null
 43 | 
44 |
46 |
 47 | {
 48 |   "firstName": "John",
 49 |   "lastName": "Smith",
 50 |   "isAlive": true,
 51 |   "age": 27,
 52 |   "address": {
 53 |     "streetAddress": "21 2nd Street",
 54 |     "city": "New York",
 55 |     "state": "NY",
 56 |     "postalCode": "10021-3100"
 57 |   },
 58 |   "phoneNumbers": [
 59 |     {
 60 |       "type": "home",
 61 |       "number": "212 555-1234"
 62 |     },
 63 |     {
 64 |       "type": "office",
 65 |       "number": "646 555-4567"
 66 |     },
 67 |     {
 68 |       "type": "mobile",
 69 |       "number": "123 456-7890"
 70 |     }
 71 |   ],
 72 |   "children": [],
 73 |   "spouse": null
 74 | }
 75 | 
76 |
79 | 80 | Since Deco is simple and doesn't have to deal with delimiter collisions it's also efficient. In a [benchmark](https://github.com/Enhex/deco-benchmark) against JSON Deco had: 81 | - ~44% smaller file size 82 | - ~380% faster output serialization 83 | - ~580% faster input parsing 84 | 85 | 86 | To learn how to use the Deco format, see the [tutorial](deco_tutorial.md). 87 | 88 | To learn how the Deco format works, see the [specification](delimiter%20collision%20free%20format.txt). 89 | 90 | ## Deco Library 91 | 92 | The Deco library is header-only and uses C++17. 93 | 94 | A Conan package is available: https://conan.io/center/deco 95 | 96 | 97 | ### Using 98 | 99 | To learn how to use the Deco library see the [documentation](documentation.md), and the [tests](tests) for examples. 100 | 101 | 102 | ### Manual Packaging 103 | 104 | The library uses [Premake](https://premake.github.io/) with [Conan](https://conan.io/) package manager to manage dependencies. 105 | you need to add the following Conan packages manually using the command `conan create . enhex/stable --build=outdated` from their root directory (where the conanfile.py is located): 106 | https://github.com/Enhex/generic_serialization 107 | https://github.com/Enhex/strong_type 108 | 109 | Then use the same command to create a package for Deco. 110 | Using Conan you can consume the library as the package `deco/master@enhex/stable`. 111 | 112 | 113 | ### Note 114 | 115 | While Deco is not absolutely "collision free", 116 | it's only potentially needs escaping once at the start/end of an entry, 117 | compared to other formats that need delimiters to be escaped N times. 118 | So it's O(1) vs O(N) in a sense, which makes Deco fundamentally better. 119 | -------------------------------------------------------------------------------- /include/deco/NVP.h: -------------------------------------------------------------------------------- 1 | #ifndef deco_NVP_h 2 | #define deco_NVP_h 3 | 4 | #include "InputStream.h" 5 | #include "OutputStream.h" 6 | #include 7 | #include 8 | 9 | namespace deco 10 | { 11 | template typename NVP, typename T> constexpr 12 | std::enable_if_t>> 13 | write_NVP(Stream& stream, const NVP& nvp) 14 | { 15 | const auto name_str = [&nvp]() { 16 | return std::string(nvp.name) += ": "; 17 | }; 18 | 19 | /*NOTE: 20 | The name's start and value's end are automatically escaped by the std::string serialization. 21 | The value's internal sub-string start isn't escaped when serialized, so it needs to be escaped manually. 22 | The name's end doesn't need escaping. 23 | */ 24 | if constexpr (std::is_floating_point_v || std::is_integral_v) // no need to escape 25 | serialize(stream, name_str() += to_string(nvp.value)); 26 | else 27 | serialize(stream, name_str() += escape_content(to_string(nvp.value))); 28 | } 29 | 30 | 31 | // Content name value pair: NVP inside the entry content, using the structure delimiter to separate between name and value, which means it can't be inside the name 32 | template 33 | struct NVP_t { 34 | std::string& name; 35 | T& value; 36 | }; 37 | 38 | template 39 | constexpr auto make_NVP(std::string& name, T& value) 40 | { 41 | return NVP_t{ 42 | name, 43 | value 44 | }; 45 | } 46 | 47 | template constexpr 48 | std::enable_if_t>> 49 | write(Stream& stream, const NVP_t& nvp) 50 | { 51 | write_NVP(stream, nvp); 52 | } 53 | 54 | 55 | template constexpr 56 | void read(InputStream& stream, NVP_t& nvp) 57 | { 58 | // skip whitespace 59 | skip_whitespace(stream.position); 60 | 61 | // skip content begin delimiter 62 | if (*stream.position == content_delimiter) 63 | ++stream.position; 64 | 65 | // read name 66 | const auto name_begin = stream.position; 67 | 68 | while (*stream.position != ':') 69 | ++stream.position; 70 | 71 | nvp.name = std::string(name_begin, stream.position); 72 | 73 | // skip NVP delimiter 74 | ++stream.position; // +1 for ':' 75 | 76 | // read value 77 | serialize(stream, nvp.value); 78 | } 79 | template constexpr 80 | void read(InputStream& stream, NVP_t&& nvp) 81 | { 82 | read(stream, nvp); 83 | } 84 | 85 | 86 | // version that doesn't read a name 87 | template 88 | struct NVP_ignore_name_t { 89 | const std::string_view name; 90 | T& value; 91 | }; 92 | 93 | template 94 | constexpr auto make_NVP(std::string_view&& name, T& value) 95 | { 96 | return NVP_ignore_name_t{ 97 | std::forward(name), 98 | value // want to always use T member, so no forwarding 99 | }; 100 | } 101 | 102 | template constexpr 103 | std::enable_if_t>> 104 | write(Stream& stream, const NVP_ignore_name_t& nvp) 105 | { 106 | write_NVP(stream, nvp); 107 | } 108 | 109 | template constexpr 110 | void read(InputStream& stream, NVP_ignore_name_t& nvp) 111 | { 112 | // skip name 113 | while (*(stream.position++) != ':'); 114 | 115 | // read value 116 | serialize(stream, nvp.value); 117 | } 118 | template constexpr 119 | void read(InputStream& stream, NVP_ignore_name_t&& nvp) 120 | { 121 | read(stream, nvp); 122 | } 123 | } 124 | 125 | #endif//guard 126 | -------------------------------------------------------------------------------- /tests/deco_NVP.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | 10 | int main() 11 | { 12 | const std::string str_val = "string"; 13 | const std::string str_empty = ""; 14 | const std::string str_space = " string"; 15 | const std::string str_tab = " string"; 16 | const std::string str_spc_tab = " string"; 17 | const std::string str_content_begin = "' string"; 18 | const std::string str_content_end = "string'"; 19 | const std::string str_content_end2 = "string''"; 20 | const std::string str_structure = "string:"; 21 | const std::string str_structure_con_end = "string:'"; 22 | 23 | const int i = 2; 24 | 25 | // write file 26 | { 27 | deco::OutputStream_indent stream; 28 | 29 | gs::serializer(stream, deco::make_NVP("ignored name", i)); 30 | gs::serializer(stream, deco::make_NVP(str_val, i)); 31 | 32 | deco::serialize(stream, deco::make_NVP("ignored name", "value")); 33 | 34 | deco::serialize(stream, deco::make_NVP(str_val, str_val)); 35 | deco::serialize(stream, deco::make_NVP(str_empty, str_empty)); 36 | deco::serialize(stream, deco::make_NVP(str_space, str_space)); 37 | deco::serialize(stream, deco::make_NVP(str_tab, str_tab)); 38 | deco::serialize(stream, deco::make_NVP(str_spc_tab, str_spc_tab)); 39 | deco::serialize(stream, deco::make_NVP(str_content_begin, str_content_begin)); 40 | deco::serialize(stream, deco::make_NVP(str_content_end, str_content_end)); 41 | deco::serialize(stream, deco::make_NVP(str_content_end2, str_content_end2)); 42 | // name can't contain structure delimiter 43 | deco::serialize(stream, deco::make_NVP(str_val, str_structure)); 44 | deco::serialize(stream, deco::make_NVP(str_val, str_structure_con_end)); 45 | 46 | std::ofstream os("out.deco", std::ios::binary); 47 | os << stream.str; 48 | } 49 | 50 | // read file 51 | { 52 | auto file = std::ifstream("out.deco", std::ios::binary); 53 | std::string file_str{ 54 | std::istreambuf_iterator(file), 55 | std::istreambuf_iterator()}; 56 | 57 | auto stream = deco::make_InputStream(file_str.cbegin()); 58 | 59 | std::string in_name, in_str; 60 | int in_i; 61 | 62 | gs::serializer(stream, deco::make_NVP("ignored name", in_i)); assert(in_i == i); 63 | gs::serializer(stream, deco::make_NVP(in_name, in_i)); assert(in_name == str_val); assert(in_i == i); 64 | 65 | deco::serialize(stream, deco::make_NVP("ignored name", in_str)); assert(in_str == "value"); 66 | 67 | deco::serialize(stream, deco::make_NVP(in_name, in_str)); assert(in_name == str_val); assert(in_str == str_val); 68 | deco::serialize(stream, deco::make_NVP(in_name, in_str)); assert(in_name == str_empty); assert(in_str == str_empty); 69 | deco::serialize(stream, deco::make_NVP(in_name, in_str)); assert(in_name == str_space); assert(in_str == str_space); 70 | deco::serialize(stream, deco::make_NVP(in_name, in_str)); assert(in_name == str_tab); assert(in_str == str_tab); 71 | deco::serialize(stream, deco::make_NVP(in_name, in_str)); assert(in_name == str_spc_tab); assert(in_str == str_spc_tab); 72 | deco::serialize(stream, deco::make_NVP(in_name, in_str)); assert(in_name == str_content_begin); assert(in_str == str_content_begin); 73 | deco::serialize(stream, deco::make_NVP(in_name, in_str)); assert(in_name == str_content_end); assert(in_str == str_content_end); 74 | deco::serialize(stream, deco::make_NVP(in_name, in_str)); assert(in_name == str_content_end2); assert(in_str == str_content_end2); 75 | deco::serialize(stream, deco::make_NVP(in_name, in_str)); assert(in_name == str_val); assert(in_str == str_structure); 76 | deco::serialize(stream, deco::make_NVP(in_name, in_str)); assert(in_name == str_val); assert(in_str == str_structure_con_end); 77 | } 78 | } -------------------------------------------------------------------------------- /delimiter collision free format.txt: -------------------------------------------------------------------------------- 1 | Delimiter Collision Free Format 2 | =============================== 3 | First created 19/7/2017 4 | by Yehonatan Ballas 5 | 6 | 7 | Abbreviation: 8 | Deco 9 | 10 | File extension: 11 | .deco 12 | 13 | 14 | Rules for creating delimiter collision free format: 15 | =================================================== 16 | - using first of delimiter allows having it as content afterwards without collision 17 | - using last of delimiter allows having it as content beforehand without collision 18 | - several first/last of delimiters can be used if they're in fixed order 19 | - use newline character as the entry delimiter. 20 | Newline is used both as a delimiter and implicitly as newline from structure (interpret each entry as a line), and text editors use newline to break lines, so there's no need to explicitly write it as content. 21 | 22 | Notes: 23 | - deduced from the first/last of rules, each delimiter can be used up to 2 times without collision - as first before the content and last after the content 24 | 25 | 26 | The format 27 | ========== 28 | 29 | Standard delimiter characters: 30 | ------------------------------ 31 | entry delimiter = newline (\n) 32 | structure delimiter = : 33 | content delimiter = ' 34 | 35 | 36 | entry structure: 37 | ---------------- 38 | [ignored whitespace][optional content begin delimiter][content][optional structure delimiter][optional content end delimiter][entry delimiter] 39 | 40 | c content = c 41 | c: content = c , structured 42 | 43 | c:' content = c: 44 | c:: content = c: , structured 45 | 46 | c'' content = c' 47 | c': content = c' , structured 48 | 49 | Delimiters aren't part of the content. 50 | 51 | Content begins either with the first non-whitespace character or first of content delimiter. 52 | 53 | Content ends with either: 54 | entry delimiter. 55 | structure delimiter followed by entry delimiter. 56 | content delimiter followed by entry delimiter. 57 | 58 | Content end delimiter allows having the content end with structure delimiter character. 59 | 60 | Ignored whitespace can be used to indent/format the document. 61 | 62 | 63 | list structure 64 | -------------- 65 | A list is a special type of entry used to group entries. 66 | Lists can be used to describe things like hierarchies, objects, arrays, multi-line strings, name value pairs, and so on. 67 | 68 | A list begins when an entry ends with a structure delimiter. The content of the list entry is its name. 69 | 70 | A list ends when an entry only contains a structure delimiter followed by entry delimiter. List ending entries are only used for list structuring and aren't part of the document's content. 71 | 72 | example of a list: 73 | c: content = c , list begin 74 | c child entry 75 | c: child list entry 76 | c child entry 77 | : list end 78 | : list end 79 | 80 | Anonymous lists, that is lists with no name, can be expressed by using content begin delimiter followed by structure delimiter. 81 | Since for an entry to be list end entry it must only contain a structure delimiter, such entry is a list begin. 82 | Since it has no content (between the content begin begin delimiter and structure delimiter), it has no name. 83 | For example: 84 | ': anonymous list begin 85 | : list end 86 | 87 | 88 | document structure 89 | ------------------ 90 | If the document doesn't end with entry delimiter, implicitly end it with one. 91 | 92 | 93 | 94 | 95 | format design decisions 96 | ======================= 97 | 98 | Data type information isn't part of the format 99 | ---------------------------------------------- 100 | Data types shouldn't be part of the format, since the application that uses the file already has to know the data types it's reading. 101 | That means that hard-coding data types into the format is redundant and undesirable because it increases complexity. 102 | The format should only describe structure. 103 | 104 | It's still desirable for a Deco implementation to provide ways to parse & interpret entries as common data structures. perhaps in a standardized way. 105 | such as: 106 | integer/float number, name value pair, array, multi-line string, and delimiter separated values. 107 | 108 | No indentation for structure 109 | ---------------------------- 110 | Not using indentation for structure, since it limits formatting flexibility, brittle when mixing tabs and spaces, and doesn't scale well with deep nesting since the required indentation characters grow linearly with nesting depth (can limit things like minifiers). 111 | -------------------------------------------------------------------------------- /include/deco/Deco.h: -------------------------------------------------------------------------------- 1 | #ifndef Deco_h 2 | #define Deco_h 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | namespace deco 13 | { 14 | // Delimiters 15 | constexpr char entry_delimiter { '\n' }; 16 | constexpr char structure_delimiter { ':' }; 17 | constexpr char content_delimiter { '\'' }; 18 | 19 | // Helps with Argument Dependent Lookup by being in deco namespace 20 | STRONG_TYPE(Content, std::string_view); 21 | 22 | struct EntryObject { 23 | Content content; 24 | std::vector entries; 25 | }; 26 | 27 | 28 | constexpr auto make_whitespace_jump_table() noexcept 29 | { 30 | std::array::max() + 1> table{ 0 }; 31 | table['\t'] = 1; 32 | table[' '] = 1; 33 | return table; 34 | } 35 | constexpr auto whitespace_jump_table = make_whitespace_jump_table(); 36 | 37 | // skip whitespace 38 | template 39 | constexpr void skip_whitespace(Iterator& current) 40 | { 41 | while (whitespace_jump_table[*current]) 42 | ++current; 43 | } 44 | template 45 | constexpr auto skip_whitespace(Iterator&& current) 46 | { 47 | while (whitespace_jump_table[*current]) 48 | ++current; 49 | return current; 50 | } 51 | 52 | 53 | struct Entry 54 | { 55 | enum Type : uint_fast8_t { 56 | entry, // non-list entry 57 | list, // list entry 58 | list_end 59 | }; 60 | 61 | Content content; 62 | Type type = entry; 63 | }; 64 | 65 | 66 | // the part of the parsing after whitespace is skipped 67 | template 68 | constexpr Entry parse_entry_core(Iterator& current) 69 | { 70 | Entry entry; 71 | 72 | // skip content begin delimiter 73 | const bool content_begin_delimiter_found = [&]() { 74 | if (*current == content_delimiter) { 75 | ++current; 76 | return true; 77 | } 78 | return false; 79 | }(); 80 | 81 | // begin content 82 | const auto content_begin = current; // inclusive 83 | 84 | // in case of empty entry 85 | if (*current++ == entry_delimiter) { 86 | // end content 87 | entry.content = std::string_view(&*content_begin, 0); 88 | return entry; 89 | } 90 | 91 | // skip content 92 | while (*current != entry_delimiter) 93 | ++current; 94 | 95 | Iterator content_end; // exclusive 96 | 97 | // the character before the entry delimiter 98 | const auto& one_before_last = *(current - 1); 99 | 100 | // structure 101 | if (one_before_last == structure_delimiter) 102 | { 103 | content_end = current - 1; // exclude the structure delimiter 104 | 105 | // check if list end entry 106 | if (!content_begin_delimiter_found && 107 | content_end == content_begin/*check if content is empty*/) 108 | { 109 | entry.type = Entry::list_end; 110 | } 111 | // begin list 112 | else { 113 | entry.type = Entry::list; 114 | } 115 | } 116 | // content end delimiter 117 | else if (one_before_last == content_delimiter) { 118 | // type = entry by default 119 | content_end = current - 1; // exclude the content delimiter 120 | } 121 | // only entry delimiter 122 | else { 123 | // type = entry by default 124 | content_end = current; 125 | } 126 | 127 | // end content 128 | entry.content = std::string_view(&*content_begin, std::distance(content_begin, content_end)); 129 | // advance to the next entry 130 | ++current; 131 | 132 | return entry; 133 | } 134 | 135 | // parse next entry 136 | template 137 | constexpr Entry parse_entry(Iterator& current) 138 | { 139 | // expecting tabs for indentation 140 | skip_whitespace(current); 141 | 142 | return parse_entry_core(current); 143 | } 144 | 145 | 146 | template 147 | EntryObject parse_object(Iterator& current) 148 | { 149 | return parse_object(current, parse_entry(current)); 150 | } 151 | 152 | template 153 | EntryObject parse_object(Iterator& current, const Entry& entry) 154 | { 155 | switch (entry.type) 156 | { 157 | case Entry::entry: 158 | return { entry.content, {} }; 159 | 160 | case Entry::list: 161 | { 162 | auto object = EntryObject{ entry.content, {} }; 163 | 164 | // Keep consuming entries until reaching the list end. Child lists will consume their own ends, so current list won't run into their ends. 165 | for(auto child_entry = parse_entry(current); child_entry.type != Entry::list_end; child_entry = parse_entry(current)) 166 | object.entries.emplace_back(parse_object(current, child_entry)); 167 | 168 | return object; 169 | } 170 | case Entry::list_end: 171 | throw std::invalid_argument("Unexpected list end"); 172 | } 173 | 174 | throw std::invalid_argument("Invalid entry type"); 175 | } 176 | 177 | template 178 | auto parse(Iterator current, const Iterator last) 179 | { 180 | std::vector objects; 181 | 182 | while (current != last) { 183 | objects.emplace_back(parse_object(current)); 184 | } 185 | 186 | return objects; 187 | } 188 | 189 | 190 | template 191 | constexpr Entry peek_entry(Iterator& current) 192 | { 193 | // can keep whitespace skipping after peeking 194 | skip_whitespace(current); 195 | 196 | // can't advance over content while peeking 197 | Iterator current_copy = current; 198 | 199 | return parse_entry_core(current_copy); 200 | } 201 | 202 | 203 | template 204 | constexpr bool peek_list_end(Iterator& current) 205 | { 206 | // can keep whitespace skipping after peeking 207 | skip_whitespace(current); 208 | 209 | // can't advance over content while peeking 210 | auto ending = current; 211 | 212 | // must be structure delimiter followed by entry delimiter 213 | if (*current == structure_delimiter) { 214 | if(*(++current) == entry_delimiter) { 215 | current = ending; // restore original position 216 | return true; 217 | } 218 | } 219 | 220 | return false; 221 | } 222 | } 223 | 224 | #endif//guard -------------------------------------------------------------------------------- /include/deco/list.h: -------------------------------------------------------------------------------- 1 | #ifndef deco_list_h 2 | #define deco_list_h 3 | 4 | #include "InputStream.h" 5 | #include "OutputStream.h" 6 | #include "types/string.h" // for list name serialization 7 | #include 8 | 9 | namespace deco 10 | { 11 | // Wrapper type to start a list 12 | struct begin_list_t { 13 | std::string& name; 14 | }; 15 | 16 | constexpr auto begin_list(std::string& name) { 17 | return begin_list_t{ name }; 18 | } 19 | 20 | template constexpr 21 | std::enable_if_t>> 22 | write(Stream& stream, const begin_list_t& value) 23 | { 24 | stream.begin_list(value.name); 25 | } 26 | 27 | template constexpr 28 | void read(InputStream& stream, begin_list_t& value) 29 | { 30 | serialize(stream, value.name); // read list name 31 | } 32 | template constexpr 33 | void read(InputStream& stream, begin_list_t&& value) 34 | { 35 | read(stream, value); 36 | } 37 | 38 | 39 | 40 | 41 | // Wrapper type to start a list, and skip reading the name 42 | struct begin_list_ignore_name_t { 43 | const std::string_view name; 44 | }; 45 | 46 | constexpr auto begin_list(std::string_view&& name) { 47 | return begin_list_ignore_name_t{ name }; 48 | } 49 | 50 | template constexpr 51 | std::enable_if_t>> 52 | write(Stream& stream, const begin_list_ignore_name_t& value) 53 | { 54 | stream.begin_list(value.name); 55 | } 56 | 57 | template constexpr 58 | void read(InputStream& stream, begin_list_ignore_name_t) 59 | { 60 | serialize(stream, skip); // skip list name 61 | } 62 | 63 | 64 | 65 | 66 | // type to start an anonymous list 67 | struct begin_list_anonymous_t {}; 68 | 69 | constexpr auto begin_list() { 70 | return begin_list_anonymous_t{}; 71 | } 72 | 73 | template constexpr 74 | std::enable_if_t>> 75 | write(Stream& stream, begin_list_anonymous_t) 76 | { 77 | stream.begin_anonymous_list(); 78 | } 79 | 80 | template constexpr 81 | void read(InputStream& stream, begin_list_anonymous_t) 82 | { 83 | serialize(stream, skip); // skip list name 84 | } 85 | 86 | 87 | // type to end a list 88 | struct end_list_t {}; 89 | constexpr end_list_t end_list; 90 | 91 | template constexpr 92 | std::enable_if_t>> 93 | write(Stream& stream, end_list_t) 94 | { 95 | stream.end_list(); 96 | } 97 | 98 | template constexpr 99 | void read(InputStream& stream, end_list_t) 100 | { 101 | stream.parse_entry(); // skip list end 102 | } 103 | 104 | 105 | 106 | 107 | // Wrapper type to serialize another type inside a list 108 | template 109 | struct list_t 110 | { 111 | std::string& name; 112 | T& value; 113 | }; 114 | 115 | //NOTE: class template argument deduction not supported yet 116 | template 117 | constexpr auto make_list(std::string& name, T& value) 118 | { 119 | return list_t{name, value}; 120 | } 121 | 122 | template constexpr 123 | std::enable_if_t>> 124 | write(Stream& stream, const list_t& nvp) 125 | { 126 | stream.begin_list(nvp.name); 127 | serialize(stream, nvp.value); 128 | stream.end_list(); 129 | } 130 | template constexpr 131 | std::enable_if_t>> 132 | write(Stream& stream, const list_t&& nvp) 133 | { 134 | write(stream, nvp); 135 | } 136 | 137 | template constexpr 138 | void read(InputStream& stream, list_t& nvp) 139 | { 140 | serialize(stream, nvp.name); // read entry name 141 | serialize(stream, nvp.value); // read child entry 142 | stream.parse_entry(); // skip list end 143 | } 144 | template constexpr 145 | void read(InputStream& stream, list_t&& nvp) 146 | { 147 | read(stream, nvp); 148 | } 149 | 150 | 151 | 152 | 153 | // version that doesn't read a name 154 | template 155 | struct list_ignore_name_t 156 | { 157 | const std::string_view name; 158 | T& value; 159 | }; 160 | 161 | //NOTE: class template argument deduction not supported yet 162 | template 163 | constexpr auto make_list(std::string_view&& name, T& value) 164 | { 165 | return list_ignore_name_t{std::forward(name), value}; 166 | } 167 | 168 | template constexpr 169 | std::enable_if_t>> 170 | write(Stream& stream, const list_ignore_name_t& nvp) 171 | { 172 | stream.begin_list(nvp.name); 173 | serialize(stream, nvp.value); 174 | stream.end_list(); 175 | } 176 | template constexpr 177 | std::enable_if_t>> 178 | write(Stream& stream, list_ignore_name_t&& nvp) 179 | { 180 | write(stream, nvp); 181 | } 182 | 183 | template constexpr 184 | void read(InputStream& stream, list_ignore_name_t& nvp) 185 | { 186 | serialize(stream, skip); // skip list entry name 187 | serialize(stream, nvp.value); // read child entry 188 | stream.parse_entry(); // skip list end 189 | } 190 | template constexpr 191 | void read(InputStream& stream, list_ignore_name_t&& nvp) 192 | { 193 | read(stream, nvp); 194 | } 195 | 196 | 197 | 198 | 199 | // anonymous list case 200 | template 201 | struct list_anonymous_t 202 | { 203 | T& value; 204 | }; 205 | 206 | //NOTE: class template argument deduction not supported yet 207 | template 208 | constexpr auto make_list(T& value) 209 | { 210 | return list_anonymous_t{value}; 211 | } 212 | 213 | template constexpr 214 | std::enable_if_t>> 215 | write(Stream& stream, const list_anonymous_t& nvp) 216 | { 217 | stream.begin_anonymous_list(); 218 | serialize(stream, nvp.value); 219 | stream.end_list(); 220 | } 221 | template constexpr 222 | std::enable_if_t>> 223 | write(Stream& stream, const list_anonymous_t&& nvp) 224 | { 225 | write(stream, nvp); 226 | } 227 | 228 | template constexpr 229 | void read(InputStream& stream, list_anonymous_t& nvp) 230 | { 231 | serialize(stream, skip); // skip list entry name 232 | serialize(stream, nvp.value); // read child entry 233 | stream.parse_entry(); // skip list end 234 | } 235 | template constexpr 236 | void read(InputStream& stream, list_anonymous_t&& nvp) 237 | { 238 | read(stream, nvp); 239 | } 240 | } 241 | 242 | #endif//guard -------------------------------------------------------------------------------- /documentation.md: -------------------------------------------------------------------------------- 1 | # Deco Library Documentation 2 | 3 | ## Writing serialization functions for new types 4 | 5 | To be able to serialize a new type, you need to provide a `serialize` function for the type in the `deco` namespace. 6 | `serialize` is used for both reading and writing. 7 | 8 | For example: 9 | ```C++ 10 | #include // for int serialization 11 | #include // for gs::serializer 12 | #include 13 | 14 | struct A { 15 | int x, y, z; 16 | }; 17 | 18 | namespace deco 19 | { 20 | template constexpr 21 | void serialize(Stream& stream, A& value) 22 | { 23 | deco::serialize(stream, value.x); // serialize single variable 24 | gs::serializer(stream, value.y, value.z); // variadic template to serialize multiple variables 25 | } 26 | } 27 | 28 | int main() 29 | { 30 | A a{1,2,3}; 31 | 32 | // write 33 | { 34 | deco::write_file file("out.deco"); 35 | deco::serialize(file.stream, a); 36 | } 37 | 38 | a = {0,0,0}; // change values 39 | 40 | // read 41 | { 42 | auto file = deco::read_file("out.deco"); 43 | deco::serialize(file.stream, a); 44 | } 45 | } 46 | ``` 47 | 48 | If you need different behavior on read and on write, you can instead provide `deco::read` and `deco::write` functions. 49 | See `deco/types/string.h` for example. 50 | 51 | 52 | ## List entry serialization 53 | 54 | You can serialize a type inside a list by using `deco::make_list`. 55 | For its name parameter it can accept: 56 | - `std::string_view` or string literal, which it will only use to write. 57 | - reference to a `std::string` variable, which it will also read the name into. 58 | - no name argument at all, to create anonymous list. 59 | 60 | For example: 61 | ```C++ 62 | #include 63 | #include 64 | #include 65 | 66 | struct A { 67 | int x, y, z; 68 | std::string name = "y"; 69 | }; 70 | 71 | namespace deco 72 | { 73 | template constexpr 74 | void serialize(Stream& stream, A& value) 75 | { 76 | gs::serializer(stream, 77 | deco::make_list("x", value.x), // write 78 | deco::make_list(name, value.y), // write and read 79 | deco::make_list(value.z)); // anonymous list 80 | } 81 | } 82 | ``` 83 | 84 | If you want to serialize several variables into a list you can use the `deco::begin_list` and `deco::end_list` types. 85 | `deco::begin_list` accepts the same kind of name parameters as `deco::make_list`. 86 | 87 | ```C++ 88 | #include 89 | #include 90 | #include 91 | 92 | struct A { 93 | int x, y, z; 94 | }; 95 | 96 | namespace deco 97 | { 98 | template constexpr 99 | void serialize(Stream& stream, A& value) 100 | { 101 | gs::serializer(stream, 102 | begin_list("A"), 103 | value.x, 104 | value.y, 105 | value.z, 106 | end_list); 107 | } 108 | } 109 | ``` 110 | 111 | 112 | ## Containers 113 | 114 | Serialization for C++'s standard containers is provided with the library. 115 | Containers must be serialized as lists using `deco::make_list`, so they can know which entries are their elements. 116 | 117 | For example: 118 | ```C++ 119 | #include 120 | #include 121 | 122 | int main() 123 | { 124 | std::vector vec; 125 | 126 | // ... 127 | 128 | deco::serialize(stream, deco::make_list("vector", vec)); 129 | 130 | // ... 131 | } 132 | ``` 133 | 134 | ## Escaping 135 | To escape the content means adding a content delimiter at its start if it starts with whitespace or a content delimiter, and at the end if it ends with content or structure delimiter, so the content will be parsed "as is". 136 | 137 | ## Name Value Pair 138 | 139 | You can serialize a name value pair which are separated by a `:` into a single line using `deco::make_NVP`. 140 | `deco::make_NVP` accepts the same kind of name parameters as `deco::make_list`. 141 | 142 | When using NVP: 143 | 144 | - The name must be not contain entry or structure delimiters. 145 | - The value is automatically escaped, and is parsed as an entry. 146 | 147 | For example: 148 | ```C++ 149 | #include 150 | 151 | int main() 152 | { 153 | deco::OutputStream_indent stream; 154 | deco::serialize(stream, deco::make_NVP("hello", "world")); 155 | } 156 | ``` 157 | The example's NVP's content will be `hello: world`. 158 | 159 | For types to be serializable as NVP values, they need to have `deco::to_string` function overload. 160 | 161 | 162 | ## String serialization 163 | 164 | Serialization for `std::string` is provided in the header `deco/types/string.h`. 165 | It will automatically escape structure and entry delimiters. 166 | 167 | 168 | #### Unescaped string 169 | `deco::unescaped_string`, strong type of `std::string`, will not escape delimiters when serialized. 170 | 171 | Header: `deco/types/unescaped_string.h` 172 | 173 | 174 | #### Multi-line string 175 | 176 | Multi-line string can simply be described as a list of entries where each entry is a single line. 177 | Since newline is used as the entry delimiter there's no need to explicitly use it since text editors will already display each entry in a new line. 178 | 179 | For example: 180 | ``` 181 | string: 182 | this is a 183 | multi-line 184 | string 185 | : 186 | ``` 187 | 188 | `deco::multiline_string`, strong type of `std::string`, is provided and it will automatically break down its content into seprate entries and put them back together into a single string when serialized. 189 | 190 | It needs to be serialized as a list to group its entries, using `deco::make_list`. 191 | 192 | Header: `deco/types/multiline_string.h` 193 | 194 | 195 | #### Single-line string 196 | `deco::singleline_string`, strong type of `std::string`, is used to provide compile time information that the string uses a single line only. 197 | It does not perform any runtime checks to make sure it's true. 198 | 199 | It has the `deco::is_single_entry` type trait, which allows it to be safely serialized more compactly in some cases (see `deco::is_single_entry`'s documentation). 200 | 201 | Header: `deco/types/singleline_string.h` 202 | 203 | 204 | #### Manually escaping a string 205 | 206 | If you need to manually escape a string's content, you can use the functions `deco::escape_content` and `deco::unescape_content`, which are defined in `deco/escape.h`. 207 | 208 | Keep in mind that the Deco parser already exclude delimiters from the entry's content, so you may not need to manually unescape it. 209 | 210 | ## Type traits 211 | 212 | #### deco::is_single_entry 213 | 214 | `deco::is_single_entry` type trait is used for types that can be serialized as a single entry, that means their string value must not contain entry delimiter. 215 | It's defined in `deco/traits.h`. 216 | 217 | `deco::is_single_entry` is used by key value containers such as `std::map` to know if the key type can be serialized as a single entry, so the key can be a list entry and the value its child, instead of serializing each in a separate list (in case the type is serialized as several entries). 218 | if you want to make a new type serializable as a single entry key you need to provide an overload for `deco::to_string`, which is used to convert the key into a string. 219 | -------------------------------------------------------------------------------- /tests/serialize.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | constexpr auto floating_test_value = 123.125; // 0.125 shouldn't have floating point precision error 29 | 30 | template 31 | void write_type(Stream& stream) 32 | { 33 | if constexpr (std::is_floating_point_v) { 34 | auto value = T(floating_test_value); 35 | gs::serializer(stream, value); 36 | } 37 | else if constexpr (std::is_integral_v && std::is_signed_v) { 38 | auto value = std::numeric_limits::min(); 39 | gs::serializer(stream, value); 40 | } 41 | else { 42 | auto value = std::numeric_limits::max(); 43 | gs::serializer(stream, value); 44 | } 45 | }; 46 | 47 | template 48 | void read_type(deco::InputStream& stream) 49 | { 50 | T value; 51 | gs::serializer(stream, value); 52 | 53 | if constexpr (std::is_floating_point_v) 54 | assert(value == floating_test_value); 55 | else if constexpr (std::is_integral_v && std::is_signed_v) 56 | assert(value == std::numeric_limits::min()); 57 | else 58 | assert(value == std::numeric_limits::max()); 59 | }; 60 | 61 | 62 | int main() 63 | { 64 | const deco::unescaped_string unesc_str_val{ "string" }; 65 | const std::string str_val = "string"; 66 | const std::string str_space = " string"; 67 | const std::string str_tab = " string"; 68 | const std::string str_spc_tab = " string"; 69 | const std::string str_content_begin = "' string"; 70 | const std::string str_content_end = "string'"; 71 | const std::string str_structure = "string:"; 72 | const std::string str_structure_con_end = "string:'"; 73 | 74 | const deco::multiline_string ml_str_val{ "multi\nline\nstring" }; 75 | 76 | int el = 0; // element value 77 | const std::array arr_val{ el++,el++,el++,el++,el++ }; 78 | const std::vector v_val{ el++,el++,el++,el++,el++ }; 79 | const std::set set_val{ el++,el++,el++,el++,el++ }; 80 | const std::deque deq_val{ el++,el++,el++,el++,el++ }; 81 | const std::forward_list flist_val{ el++,el++,el++,el++,el++ }; 82 | const std::list list_val{ el++,el++,el++,el++,el++ }; 83 | const std::unordered_set uset_val{ el++,el++,el++,el++,el++ }; 84 | const std::multiset mset_val{ el++,el,el++,el,el++ }; 85 | const std::unordered_multiset umset_val{ el++,el++,el++,el++,el++ }; 86 | const std::map map_val{ { el++,el++ },{ el++,el++ },{ el++, el++ } }; 87 | const std::multimap mmap_val{ { el++,el++ },{ el,el },{ el++, el++ } }; 88 | const std::unordered_map umap_val{ { el++,el++ },{ el,el },{ el++, el++ } }; 89 | const std::unordered_multimap ummap_val{ {el++,el++}, {el,el}, {el++, el++} }; 90 | 91 | // write 92 | { 93 | deco::OutputStream_indent stream; 94 | const auto serialize = [&stream](auto&& t) { 95 | deco::serialize(stream, t); 96 | }; 97 | 98 | serialize(unesc_str_val); 99 | 100 | gs::serializer(stream, 101 | str_space, 102 | str_tab, 103 | str_spc_tab, 104 | str_content_begin, 105 | str_content_end, 106 | str_structure, 107 | str_structure_con_end); 108 | 109 | serialize(deco::make_list("list", str_val)); 110 | 111 | serialize(deco::make_NVP("nvp", str_val)); 112 | 113 | serialize(deco::make_list("multiline_string", ml_str_val)); 114 | 115 | stream.begin_list("integral"); 116 | write_type(stream); 117 | write_type(stream); 118 | write_type(stream); 119 | write_type(stream); 120 | write_type(stream); 121 | write_type(stream); 122 | write_type(stream); 123 | write_type(stream); 124 | write_type(stream); 125 | write_type(stream); 126 | stream.end_list(); 127 | 128 | stream.begin_list("floating point"); 129 | write_type(stream); 130 | write_type(stream); 131 | write_type(stream); 132 | stream.end_list(); 133 | 134 | serialize(deco::make_list("std::array", arr_val)); 135 | serialize(deco::make_list("std::vector", v_val)); 136 | serialize(deco::make_list("std::set", set_val)); 137 | serialize(deco::make_list("std::deque", deq_val)); 138 | serialize(deco::make_list("std::forward_list", flist_val)); 139 | serialize(deco::make_list("std::list", list_val)); 140 | serialize(deco::make_list("std::unordered_set", uset_val)); 141 | serialize(deco::make_list("std::multiset", mset_val)); 142 | serialize(deco::make_list("std::unordered_multiset", umset_val)); 143 | serialize(deco::make_list("std::map", map_val)); 144 | serialize(deco::make_list("std::multimap", mmap_val)); 145 | serialize(deco::make_list("std::unordered_map", umap_val)); 146 | serialize(deco::make_list("std::unordered_multimap", ummap_val)); 147 | 148 | std::ofstream os("out.deco", std::ios::binary); 149 | os << stream.str; 150 | } 151 | 152 | // read 153 | { 154 | auto file = std::ifstream("out.deco", std::ios::binary); 155 | std::string file_str{ 156 | std::istreambuf_iterator(file), 157 | std::istreambuf_iterator()}; 158 | 159 | std::cout << file_str; 160 | 161 | auto stream = deco::make_InputStream(file_str.cbegin()); 162 | const auto serialize = [&stream](auto&& t) { 163 | deco::serialize(stream, t); 164 | }; 165 | 166 | deco::unescaped_string unesc_str; // dummy 167 | gs::serializer(stream, unesc_str); assert(unesc_str == unesc_str_val); 168 | 169 | std::string str; // dummy 170 | serialize(str); assert(str == str_space); 171 | serialize(str); assert(str == str_tab); 172 | serialize(str); assert(str == str_spc_tab); 173 | serialize(str); assert(str == str_content_begin); 174 | serialize(str); assert(str == str_content_end); 175 | serialize(str); assert(str == str_structure); 176 | serialize(str); assert(str == str_structure_con_end); 177 | 178 | serialize(deco::make_list("list", str)); assert(str == str_val); 179 | 180 | serialize(deco::make_NVP("nvp", str)); assert(str == str_val); 181 | 182 | deco::multiline_string ml_str; // dummy 183 | serialize(deco::make_list("multiline_string", ml_str)); assert(ml_str == ml_str_val); 184 | 185 | serialize(str); assert(str == "integral"); // list name 186 | read_type(stream); 187 | read_type(stream); 188 | read_type(stream); 189 | read_type(stream); 190 | read_type(stream); 191 | read_type(stream); 192 | read_type(stream); 193 | read_type(stream); 194 | read_type(stream); 195 | read_type(stream); 196 | stream.parse_entry(); // list end 197 | 198 | serialize(str); assert(str == "floating point"); // list name 199 | read_type(stream); 200 | read_type(stream); 201 | read_type(stream); 202 | stream.parse_entry(); // list end 203 | 204 | std::array arr; 205 | serialize(deco::make_list("std::array", arr)); assert(arr == arr_val); 206 | std::vector v; 207 | serialize(deco::make_list("std::vector", v)); assert(v == v_val); 208 | std::set set; 209 | serialize(deco::make_list("std::set", set)); assert(set == set_val); 210 | std::deque deq; 211 | serialize(deco::make_list("std::deque", deq)); assert(deq == deq_val); 212 | std::forward_list flist; 213 | serialize(deco::make_list("std::forward_list", flist)); assert(flist == flist_val); 214 | std::list li; 215 | serialize(deco::make_list("std::list", li)); assert(li == list_val); 216 | std::unordered_set uset; 217 | serialize(deco::make_list("std::unordered_set", uset)); assert(uset == uset_val); 218 | std::multiset mset; 219 | serialize(deco::make_list("std::multiset", mset)); assert(mset == mset_val); 220 | std::unordered_multiset umset; 221 | serialize(deco::make_list("std::unordered_multiset", umset)); assert(umset == umset_val); 222 | std::map map_; 223 | serialize(deco::make_list("std::map", map_)); assert(map_ == map_val); 224 | std::multimap mmap; 225 | serialize(deco::make_list("std::multimap", mmap)); assert(mmap == mmap_val); 226 | std::unordered_map umap; 227 | serialize(deco::make_list("std::unordered_map", umap)); assert(umap == umap_val); 228 | std::unordered_multimap ummap; 229 | serialize(deco::make_list("std::unordered_multimap", ummap)); assert(ummap == ummap_val); 230 | } 231 | } -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017-2022 Yehonatan Ballas 2 | 3 | Apache License 4 | Version 2.0, January 2004 5 | http://www.apache.org/licenses/ 6 | 7 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 8 | 9 | 1. Definitions. 10 | 11 | "License" shall mean the terms and conditions for use, reproduction, 12 | and distribution as defined by Sections 1 through 9 of this document. 13 | 14 | "Licensor" shall mean the copyright owner or entity authorized by 15 | the copyright owner that is granting the License. 16 | 17 | "Legal Entity" shall mean the union of the acting entity and all 18 | other entities that control, are controlled by, or are under common 19 | control with that entity. For the purposes of this definition, 20 | "control" means (i) the power, direct or indirect, to cause the 21 | direction or management of such entity, whether by contract or 22 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 23 | outstanding shares, or (iii) beneficial ownership of such entity. 24 | 25 | "You" (or "Your") shall mean an individual or Legal Entity 26 | exercising permissions granted by this License. 27 | 28 | "Source" form shall mean the preferred form for making modifications, 29 | including but not limited to software source code, documentation 30 | source, and configuration files. 31 | 32 | "Object" form shall mean any form resulting from mechanical 33 | transformation or translation of a Source form, including but 34 | not limited to compiled object code, generated documentation, 35 | and conversions to other media types. 36 | 37 | "Work" shall mean the work of authorship, whether in Source or 38 | Object form, made available under the License, as indicated by a 39 | copyright notice that is included in or attached to the work 40 | (an example is provided in the Appendix below). 41 | 42 | "Derivative Works" shall mean any work, whether in Source or Object 43 | form, that is based on (or derived from) the Work and for which the 44 | editorial revisions, annotations, elaborations, or other modifications 45 | represent, as a whole, an original work of authorship. For the purposes 46 | of this License, Derivative Works shall not include works that remain 47 | separable from, or merely link (or bind by name) to the interfaces of, 48 | the Work and Derivative Works thereof. 49 | 50 | "Contribution" shall mean any work of authorship, including 51 | the original version of the Work and any modifications or additions 52 | to that Work or Derivative Works thereof, that is intentionally 53 | submitted to Licensor for inclusion in the Work by the copyright owner 54 | or by an individual or Legal Entity authorized to submit on behalf of 55 | the copyright owner. For the purposes of this definition, "submitted" 56 | means any form of electronic, verbal, or written communication sent 57 | to the Licensor or its representatives, including but not limited to 58 | communication on electronic mailing lists, source code control systems, 59 | and issue tracking systems that are managed by, or on behalf of, the 60 | Licensor for the purpose of discussing and improving the Work, but 61 | excluding communication that is conspicuously marked or otherwise 62 | designated in writing by the copyright owner as "Not a Contribution." 63 | 64 | "Contributor" shall mean Licensor and any individual or Legal Entity 65 | on behalf of whom a Contribution has been received by Licensor and 66 | subsequently incorporated within the Work. 67 | 68 | 2. Grant of Copyright License. Subject to the terms and conditions of 69 | this License, each Contributor hereby grants to You a perpetual, 70 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 71 | copyright license to reproduce, prepare Derivative Works of, 72 | publicly display, publicly perform, sublicense, and distribute the 73 | Work and such Derivative Works in Source or Object form. 74 | 75 | 3. Grant of Patent License. Subject to the terms and conditions of 76 | this License, each Contributor hereby grants to You a perpetual, 77 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 78 | (except as stated in this section) patent license to make, have made, 79 | use, offer to sell, sell, import, and otherwise transfer the Work, 80 | where such license applies only to those patent claims licensable 81 | by such Contributor that are necessarily infringed by their 82 | Contribution(s) alone or by combination of their Contribution(s) 83 | with the Work to which such Contribution(s) was submitted. If You 84 | institute patent litigation against any entity (including a 85 | cross-claim or counterclaim in a lawsuit) alleging that the Work 86 | or a Contribution incorporated within the Work constitutes direct 87 | or contributory patent infringement, then any patent licenses 88 | granted to You under this License for that Work shall terminate 89 | as of the date such litigation is filed. 90 | 91 | 4. Redistribution. You may reproduce and distribute copies of the 92 | Work or Derivative Works thereof in any medium, with or without 93 | modifications, and in Source or Object form, provided that You 94 | meet the following conditions: 95 | 96 | (a) You must give any other recipients of the Work or 97 | Derivative Works a copy of this License; and 98 | 99 | (b) You must cause any modified files to carry prominent notices 100 | stating that You changed the files; and 101 | 102 | (c) You must retain, in the Source form of any Derivative Works 103 | that You distribute, all copyright, patent, trademark, and 104 | attribution notices from the Source form of the Work, 105 | excluding those notices that do not pertain to any part of 106 | the Derivative Works; and 107 | 108 | (d) If the Work includes a "NOTICE" text file as part of its 109 | distribution, then any Derivative Works that You distribute must 110 | include a readable copy of the attribution notices contained 111 | within such NOTICE file, excluding those notices that do not 112 | pertain to any part of the Derivative Works, in at least one 113 | of the following places: within a NOTICE text file distributed 114 | as part of the Derivative Works; within the Source form or 115 | documentation, if provided along with the Derivative Works; or, 116 | within a display generated by the Derivative Works, if and 117 | wherever such third-party notices normally appear. The contents 118 | of the NOTICE file are for informational purposes only and 119 | do not modify the License. You may add Your own attribution 120 | notices within Derivative Works that You distribute, alongside 121 | or as an addendum to the NOTICE text from the Work, provided 122 | that such additional attribution notices cannot be construed 123 | as modifying the License. 124 | 125 | You may add Your own copyright statement to Your modifications and 126 | may provide additional or different license terms and conditions 127 | for use, reproduction, or distribution of Your modifications, or 128 | for any such Derivative Works as a whole, provided Your use, 129 | reproduction, and distribution of the Work otherwise complies with 130 | the conditions stated in this License. 131 | 132 | 5. Submission of Contributions. Unless You explicitly state otherwise, 133 | any Contribution intentionally submitted for inclusion in the Work 134 | by You to the Licensor shall be under the terms and conditions of 135 | this License, without any additional terms or conditions. 136 | Notwithstanding the above, nothing herein shall supersede or modify 137 | the terms of any separate license agreement you may have executed 138 | with Licensor regarding such Contributions. 139 | 140 | 6. Trademarks. This License does not grant permission to use the trade 141 | names, trademarks, service marks, or product names of the Licensor, 142 | except as required for reasonable and customary use in describing the 143 | origin of the Work and reproducing the content of the NOTICE file. 144 | 145 | 7. Disclaimer of Warranty. Unless required by applicable law or 146 | agreed to in writing, Licensor provides the Work (and each 147 | Contributor provides its Contributions) on an "AS IS" BASIS, 148 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 149 | implied, including, without limitation, any warranties or conditions 150 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 151 | PARTICULAR PURPOSE. You are solely responsible for determining the 152 | appropriateness of using or redistributing the Work and assume any 153 | risks associated with Your exercise of permissions under this License. 154 | 155 | 8. Limitation of Liability. In no event and under no legal theory, 156 | whether in tort (including negligence), contract, or otherwise, 157 | unless required by applicable law (such as deliberate and grossly 158 | negligent acts) or agreed to in writing, shall any Contributor be 159 | liable to You for damages, including any direct, indirect, special, 160 | incidental, or consequential damages of any character arising as a 161 | result of this License or out of the use or inability to use the 162 | Work (including but not limited to damages for loss of goodwill, 163 | work stoppage, computer failure or malfunction, or any and all 164 | other commercial damages or losses), even if such Contributor 165 | has been advised of the possibility of such damages. 166 | 167 | 9. Accepting Warranty or Additional Liability. While redistributing 168 | the Work or Derivative Works thereof, You may choose to offer, 169 | and charge a fee for, acceptance of support, warranty, indemnity, 170 | or other liability obligations and/or rights consistent with this 171 | License. However, in accepting such obligations, You may act only 172 | on Your own behalf and on Your sole responsibility, not on behalf 173 | of any other Contributor, and only if You agree to indemnify, 174 | defend, and hold each Contributor harmless for any liability 175 | incurred by, or claims asserted against, such Contributor by reason 176 | of your accepting any such warranty or additional liability. 177 | 178 | END OF TERMS AND CONDITIONS 179 | 180 | APPENDIX: How to apply the Apache License to your work. 181 | 182 | To apply the Apache License to your work, attach the following 183 | boilerplate notice, with the fields enclosed by brackets "[]" 184 | replaced with your own identifying information. (Don't include 185 | the brackets!) The text should be enclosed in the appropriate 186 | comment syntax for the file format. We also recommend that a 187 | file or class name and description of purpose be included on the 188 | same "printed page" as the copyright notice for easier 189 | identification within third-party archives. 190 | 191 | Copyright [yyyy] [name of copyright owner] 192 | 193 | Licensed under the Apache License, Version 2.0 (the "License"); 194 | you may not use this file except in compliance with the License. 195 | You may obtain a copy of the License at 196 | 197 | http://www.apache.org/licenses/LICENSE-2.0 198 | 199 | Unless required by applicable law or agreed to in writing, software 200 | distributed under the License is distributed on an "AS IS" BASIS, 201 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 202 | See the License for the specific language governing permissions and 203 | limitations under the License. 204 | 205 | ---- LLVM Exceptions to the Apache 2.0 License ---- 206 | 207 | As an exception, if, as a result of your compiling your source code, portions 208 | of this Software are embedded into an Object form of such source code, you 209 | may redistribute such embedded portions in such Object form without complying 210 | with the conditions of Sections 4(a), 4(b) and 4(d) of the License. 211 | 212 | In addition, if you combine or link compiled forms of this Software with 213 | software that is licensed under the GPLv2 ("Combined Software") and if a 214 | court of competent jurisdiction determines that the patent provision (Section 215 | 3), the indemnity provision (Section 9) or other Section of the License 216 | conflicts with the conditions of the GPLv2, you may retroactively and 217 | prospectively choose to deem waived or otherwise exclude such Section(s) of 218 | the License, but only in their entirety and only with respect to the Combined 219 | Software. --------------------------------------------------------------------------------