├── .gitmodules ├── test ├── test.h ├── main.cpp ├── string_builder_test.cpp ├── json_writer_test.cpp ├── slice_test.cpp ├── string_test.cpp ├── json_any_test.cpp ├── json_parser_test.h └── string_test.h ├── include └── native │ ├── config.h │ ├── json.h │ ├── detail │ ├── implicit_cast.h │ ├── likely.h │ ├── stream_type.h │ ├── container_ostream.h │ ├── range_istream.h │ ├── real.h │ ├── string_builder_core.h │ ├── double-conversion │ │ └── diy-fp.h │ └── number_parse.h │ ├── string_base.h │ ├── json │ ├── detail │ │ ├── real.h │ │ └── number_parse.h │ ├── handler.h │ ├── parser.h │ ├── types.h │ ├── input_streams.h │ ├── exceptions.h │ ├── conversion.h │ ├── any.h │ └── writer.h │ ├── string_conversion.h │ ├── utf.h │ └── hash.h ├── README.md ├── docs ├── JSON.md └── Strings.md ├── benchmarks ├── benchmark.cpp ├── json_any_benchmark.cpp ├── string_builder_benchmarks.cpp ├── string_benchmarks.h ├── json_parser_benchmark.cpp ├── benchmark.h └── string_benchmarks.cpp ├── .clang-format ├── .gitignore └── LICENSE /.gitmodules: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/test.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013 Mike Naquin. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | #ifndef NATIVE_TEST_H__ 18 | #define NATIVE_TEST_H__ 19 | 20 | #include "gtest/gtest.h" 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /include/native/config.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013 Mike Naquin. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | #ifndef NATIVE_CONFIG_H__ 18 | #define NATIVE_CONFIG_H__ 19 | 20 | namespace native 21 | { 22 | 23 | } // namespace native 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /include/native/json.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2015 Mike Naquin. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | #ifndef NATIVE_JSON_H__ 18 | #define NATIVE_JSON_H__ 19 | 20 | #include "native/config.h" 21 | 22 | #include "native/json/any.h" 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /test/main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013 Mike Naquin. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | #include 18 | 19 | #include "test.h" 20 | 21 | int main(int argc, char **argv) 22 | { 23 | ::testing::InitGoogleTest(&argc, argv); 24 | return RUN_ALL_TESTS(); 25 | } 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | native 2 | ====== 3 | 4 | The Native Library is intended to provide first class libraries for C++11. The 5 | code here is designed from the ground up with C++11 so that we can make smarter 6 | choices and tradeoffs to provide code that will stand the test of time. 7 | 8 | Requirements 9 | ------------ 10 | 11 | Compiler that supports C++11. 12 | 13 | Install 14 | ------- 15 | 16 | This library is header-only, so just reference the ./include in the include paths for your project. 17 | 18 | Unit Test Dependencies 19 | ---------------------- 20 | 21 | - boost >= 1.55.0 22 | 23 | [Download](http://www.boost.org/) and install for benchmarking. 24 | 25 | Lower versions may also work, but are not tested. Use at your own risk. 26 | 27 | - googletest (Google C++ Testing Framework) 28 | 29 | [Download](http://code.google.com/p/googletest/) and install to run the unit tests. 30 | 31 | Docs 32 | ---- 33 | - [native::istring](docs/Strings.md) 34 | - [native::json](docs/JSON.md) 35 | -------------------------------------------------------------------------------- /include/native/detail/implicit_cast.h: -------------------------------------------------------------------------------- 1 | // Adapted from http://www.boost.org/doc/libs/1_56_0/boost/implicit_cast.hpp 2 | 3 | // Copyright David Abrahams 2003. 4 | // Distributed under the Boost Software License, Version 1.0. (See 5 | // accompanying file LICENSE_1_0.txt or copy at 6 | // http://www.boost.org/LICENSE_1_0.txt) 7 | #pragma once 8 | 9 | namespace native 10 | { 11 | namespace detail 12 | { 13 | 14 | template 15 | struct icast_identity 16 | { 17 | using type = T; 18 | }; 19 | 20 | } // namespace detail 21 | 22 | // implementation originally suggested by C. Green in 23 | // http://lists.boost.org/MailArchives/boost/msg00886.php 24 | 25 | // The use of identity creates a non-deduced form, so that the 26 | // explicit template argument must be supplied 27 | template 28 | inline T implicit_cast(typename detail::icast_identity::type x) 29 | { 30 | return x; 31 | } 32 | 33 | // incomplete return type now is here 34 | // template 35 | // void implicit_cast (...); 36 | 37 | } // namespace native 38 | -------------------------------------------------------------------------------- /include/native/detail/likely.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Facebook, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * Compiler hints to indicate the fast path of an "if" branch: whether 19 | * the if condition is likely to be true or false. 20 | * 21 | * @author Tudor Bosman (tudorb@fb.com) 22 | */ 23 | 24 | #ifndef NATIVE_LIKELY_H_ 25 | #define NATIVE_LIKELY_H_ 26 | 27 | #if defined(__GNUC__) && __GNUC__ >= 4 28 | #define NATIVE_LIKELY(x) (__builtin_expect((x), 1)) 29 | #define NATIVE_UNLIKELY(x) (__builtin_expect((x), 0)) 30 | #else 31 | #define NATIVE_LIKELY(x) (x) 32 | #define NATIVE_UNLIKELY(x) (x) 33 | #endif 34 | 35 | #endif /* FOLLY_BASE_LIKELY_H_ */ 36 | -------------------------------------------------------------------------------- /include/native/detail/stream_type.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2015 Mike Naquin. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | #ifndef NATIVE_JSON_DETAIL_STREAM_TYPE_H__ 18 | #define NATIVE_JSON_DETAIL_STREAM_TYPE_H__ 19 | 20 | #include "native/config.h" 21 | 22 | #include "native/istring.h" 23 | 24 | namespace native 25 | { 26 | namespace detail 27 | { 28 | 29 | template 30 | struct stream_type; 31 | 32 | template <> 33 | struct stream_type 34 | { 35 | using type = string_builder; 36 | }; 37 | 38 | template <> 39 | struct stream_type 40 | { 41 | using type = std::ostringstream; 42 | }; 43 | 44 | } // namespace detail 45 | } // namespace native 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /include/native/detail/container_ostream.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2015 Mike Naquin. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | #ifndef NATIVE_DETAIL_CONTAINER_OSTREAM_H__ 18 | #define NATIVE_DETAIL_CONTAINER_OSTREAM_H__ 19 | 20 | #include "native/config.h" 21 | 22 | #include 23 | 24 | namespace native 25 | { 26 | namespace detail 27 | { 28 | 29 | template 30 | class container_ostream 31 | { 32 | public: 33 | using container_type = Container; 34 | using char_type = typename container_type::value_type; 35 | 36 | container_ostream(Container& container) 37 | : _container{container} 38 | { 39 | } 40 | 41 | inline void put(char_type value) { _container.push_back(value); } 42 | 43 | private: 44 | container_type& _container; 45 | }; 46 | 47 | } // namespace detail 48 | } // namespace native 49 | 50 | #endif 51 | -------------------------------------------------------------------------------- /docs/JSON.md: -------------------------------------------------------------------------------- 1 | C++11 JSON 2 | ========== 3 | 4 | Many C++ JSON libraries are either not completely type safe or too slow (or 5 | both). native::json aims to provide a modern C++11 interface with 6 | uncompromising performance. 7 | 8 | DOM-style 9 | --------- 10 | 11 | native::json::any is a very simple and generic interface for JSON parsing 12 | and writing. 13 | 14 | ``` 15 | auto any = native::json::parse(R"json({ 16 | "array": [ 17 | 2, 18 | "c++", 19 | 7, 20 | { 21 | "color": "orange", 22 | "problems": 99 23 | } 24 | ], 25 | "bar": "string", 26 | "foo": 42 27 | })json"); 28 | 29 | any.is_object(); // == true 30 | any["array"][2].string_value(); // == "c++"; 31 | any["foo"].int_value(); // == 42; 32 | 33 | any.dump(std::cout); // write formatted json to stream 34 | 35 | // initialization 36 | json::any object{{{"foo", 42}, {"bar", "string"}}}; 37 | json::any array{{42, "hello", true, nullptr}}; 38 | ``` 39 | 40 | SAX-style 41 | --------- 42 | 43 | For better performance and complete type safety, use the SAX-style parser. 44 | 45 | ``` 46 | struct person 47 | { 48 | string name; 49 | unsigned short age; 50 | }; 51 | 52 | struct myhandler: native::json::handler<> 53 | { 54 | person& _p; 55 | 56 | myhandler(person& p): _p(p) { } 57 | 58 | void value(unsigned short val) { _p.age = value; } 59 | void value(const char* val, size_t len) { _p.name = val; } 60 | }; 61 | 62 | auto input = istring::literal("{\"name\": \"jack\", \"age\": 5"); 63 | person jack; 64 | myhandler handler(jack); 65 | 66 | native::json::parser parser; 67 | parser.parse(input, jack); 68 | 69 | jack.name == "jack"; 70 | jack.age == 5; 71 | ``` 72 | -------------------------------------------------------------------------------- /benchmarks/benchmark.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013 Mike Naquin. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | #include "benchmark.h" 18 | 19 | #include 20 | #include 21 | 22 | benchmark_test::benchmark_test(const char* fixture, const char* name, 23 | std::size_t trial_count) 24 | : fixture(fixture) 25 | , name(name) 26 | , timers(trial_count) 27 | { 28 | } 29 | 30 | void benchmark_test::SetUp() {} 31 | 32 | void benchmark_test::TearDown() {} 33 | 34 | void benchmark_test::report(const boost::timer::cpu_times& fastestElapsed, 35 | const boost::timer::nanosecond_type& total, 36 | const std::string& name) 37 | { 38 | auto fastest = fastestElapsed.wall; 39 | auto average = (total / timers.size()); 40 | double seconds = total / 1e9; 41 | 42 | std::ofstream ostr("benchmarks.csv", std::ios::app); 43 | 44 | ostr << name << ',' << fastest << ',' << average << std::endl; 45 | 46 | std::cout << "Fastest: " << fastest << " ns" << std::endl; 47 | std::cout << "Average: " << average << " ns" << std::endl; 48 | std::cout << "Total: " << seconds << " s" << std::endl; 49 | } 50 | -------------------------------------------------------------------------------- /include/native/string_base.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013 Mike Naquin. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | #ifndef NATIVE_STRING_BASE_H__ 18 | #define NATIVE_STRING_BASE_H__ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | namespace native { 27 | 28 | template 29 | struct basic_string_core; 30 | 31 | template 32 | class basic_istring; 33 | 34 | template 35 | class basic_string_slice; 36 | 37 | template 38 | class basic_string_builder; 39 | 40 | class string_base; 41 | 42 | template 43 | struct is_string_class { 44 | enum { 45 | value = std::is_base_of::value || 46 | std::is_same::value || 47 | std::is_same::value || 48 | std::is_same::value || 49 | std::is_same::value 50 | }; 51 | }; 52 | 53 | class string_base { 54 | public: 55 | void throw_out_of_range() const; 56 | }; 57 | 58 | inline void string_base::throw_out_of_range() const 59 | { 60 | throw std::out_of_range("native::string_base"); 61 | } 62 | 63 | 64 | } // namespace native 65 | 66 | #endif 67 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | # BasedOnStyle: LLVM 4 | AccessModifierOffset: -4 5 | AlignEscapedNewlinesLeft: false 6 | AlignTrailingComments: true 7 | AllowAllParametersOfDeclarationOnNextLine: true 8 | AllowShortBlocksOnASingleLine: false 9 | AllowShortFunctionsOnASingleLine: All 10 | AllowShortIfStatementsOnASingleLine: false 11 | AllowShortLoopsOnASingleLine: false 12 | AlwaysBreakBeforeMultilineStrings: false 13 | AlwaysBreakTemplateDeclarations: true 14 | BinPackParameters: true 15 | BreakBeforeBinaryOperators: false 16 | BreakBeforeBraces: Allman 17 | BreakBeforeTernaryOperators: true 18 | BreakConstructorInitializersBeforeComma: true 19 | ColumnLimit: 80 20 | CommentPragmas: '^ IWYU pragma:' 21 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 22 | ConstructorInitializerIndentWidth: 4 23 | ContinuationIndentWidth: 4 24 | Cpp11BracedListStyle: true 25 | DerivePointerAlignment: false 26 | DisableFormat: false 27 | ExperimentalAutoDetectBinPacking: false 28 | ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] 29 | IndentCaseLabels: true 30 | IndentFunctionDeclarationAfterType: false 31 | IndentWidth: 4 32 | IndentWrappedFunctionNames: false 33 | KeepEmptyLinesAtTheStartOfBlocks: true 34 | MaxEmptyLinesToKeep: 1 35 | NamespaceIndentation: None 36 | ObjCSpaceAfterProperty: false 37 | ObjCSpaceBeforeProtocolList: true 38 | PenaltyBreakBeforeFirstCallParameter: 19 39 | PenaltyBreakComment: 300 40 | PenaltyBreakFirstLessLess: 120 41 | PenaltyBreakString: 1000 42 | PenaltyExcessCharacter: 1000000 43 | PenaltyReturnTypeOnItsOwnLine: 60 44 | PointerAlignment: Left 45 | SpaceBeforeAssignmentOperators: true 46 | SpaceBeforeParens: ControlStatements 47 | SpaceInEmptyParentheses: false 48 | SpacesBeforeTrailingComments: 1 49 | SpacesInAngles: false 50 | SpacesInContainerLiterals: true 51 | SpacesInCStyleCastParentheses: false 52 | SpacesInParentheses: false 53 | Standard: Cpp11 54 | TabWidth: 4 55 | UseTab: Never 56 | ... 57 | 58 | -------------------------------------------------------------------------------- /include/native/json/detail/real.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013 Mike Naquin. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | #ifndef NATIVE_JSON_REAL_H__ 18 | #define NATIVE_JSON_REAL_H__ 19 | 20 | #include "native/config.h" 21 | 22 | #include "native/json/detail/integers.h" 23 | 24 | #include "native/detail/double-conversion/strtod.h" 25 | 26 | namespace native 27 | { 28 | namespace json 29 | { 30 | namespace detail 31 | { 32 | 33 | template 34 | T string_to_real(const number_parse& attribs); 35 | 36 | template <> 37 | inline long double 38 | string_to_real(const number_parse& attribs) 39 | { 40 | using namespace double_conversion; 41 | double converted = Strtod( 42 | Vector(attribs.buffer, attribs.length), attribs.exponent); 43 | return attribs.sign ? -converted : converted; 44 | } 45 | 46 | template <> 47 | inline double string_to_real(const number_parse& attribs) 48 | { 49 | using namespace double_conversion; 50 | double converted = Strtod( 51 | Vector(attribs.buffer, attribs.length), attribs.exponent); 52 | return attribs.sign ? -converted : converted; 53 | } 54 | 55 | template <> 56 | inline float string_to_real(const number_parse& attribs) 57 | { 58 | using namespace double_conversion; 59 | float converted = Strtof(Vector(attribs.buffer, attribs.length), 60 | attribs.exponent); 61 | return attribs.sign ? -converted : converted; 62 | } 63 | 64 | } // namespace detail 65 | } // namespace json 66 | } // namespace native 67 | 68 | #endif 69 | -------------------------------------------------------------------------------- /include/native/detail/range_istream.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2015 Mike Naquin. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | #ifndef NATIVE_RANGE_STREAM_H__ 18 | #define NATIVE_RANGE_STREAM_H__ 19 | 20 | #include "native/config.h" 21 | 22 | namespace native 23 | { 24 | namespace detail 25 | { 26 | 27 | template 28 | class range_istream 29 | { 30 | public: 31 | using iterator_type = Iterator; 32 | using char_type = typename std::remove_cv< 33 | typename std::remove_reference::type>::type; 34 | 35 | range_istream(iterator_type first, iterator_type last) 36 | : _head{first} 37 | , _first{first} 38 | , _last{last} 39 | { 40 | } 41 | 42 | inline std::size_t tellg() const 43 | { 44 | return static_cast(_first - _head); 45 | } 46 | 47 | inline bool good() const { return _first != _last; } 48 | 49 | inline bool fail() const { return _first == _last; } 50 | 51 | inline bool eof() const { return _first == _last; } 52 | 53 | inline bool bad() const { return _first == _last; } 54 | 55 | inline operator bool() const { return _first != _last; } 56 | 57 | inline bool operator!() const { return _first == _last; } 58 | 59 | inline char_type peek() const { return *_first; } 60 | 61 | inline void next() { ++_first; } 62 | 63 | inline char_type get() 64 | { 65 | const auto ch = peek(); 66 | next(); 67 | return ch; 68 | } 69 | 70 | private: 71 | iterator_type _head; 72 | iterator_type _first; 73 | iterator_type _last; 74 | }; 75 | 76 | } // namespace detail 77 | } // namespace native 78 | 79 | #endif 80 | -------------------------------------------------------------------------------- /include/native/string_conversion.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2014 Mike Naquin. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | #ifndef NATIVE_STRING_CONVERSION_H__ 18 | #define NATIVE_STRING_CONVERSION_H__ 19 | 20 | #include "native/config.h" 21 | 22 | #include 23 | 24 | namespace native { 25 | 26 | // similar to http://stackoverflow.com/a/24000041/766172 27 | namespace detail 28 | { 29 | 30 | template 31 | struct unsigned_to_chars 32 | { 33 | static const char value[]; 34 | }; 35 | 36 | template 37 | const char unsigned_to_chars::value[] = {('0' + digits)..., 0}; 38 | 39 | 40 | template 41 | struct signed_to_chars 42 | { 43 | static const char value[]; 44 | }; 45 | 46 | template 47 | const char signed_to_chars::value[] = {'-', ('0' + digits)..., 0}; 48 | 49 | template 50 | struct to_chars: unsigned_to_chars 51 | { 52 | }; 53 | 54 | template 55 | struct to_chars: signed_to_chars 56 | { 57 | 58 | }; 59 | 60 | template 61 | struct expand: expand 62 | { 63 | 64 | }; 65 | 66 | template 67 | struct expand: to_chars 68 | { 69 | 70 | }; 71 | 72 | template 73 | constexpr uintmax_t const_abs(T num) 74 | { 75 | return (num < 0) ? -num : num; 76 | } 77 | 78 | } // namespace detail 79 | 80 | 81 | template 82 | struct to_string_literal: detail::expand<(num < 0), detail::const_abs(num)> 83 | { 84 | 85 | }; 86 | 87 | } // namespace native 88 | 89 | #endif 90 | -------------------------------------------------------------------------------- /benchmarks/json_any_benchmark.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2015 Mike Naquin. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | #include "benchmark.h" 18 | 19 | #include "native/json/any.h" 20 | 21 | #include 22 | 23 | #include 24 | #include 25 | 26 | #if defined(JSON11) 27 | #include "json11.hpp" 28 | #endif 29 | 30 | class json_any_benchmark : public benchmark_test 31 | { 32 | public: 33 | json_any_benchmark(const char* fixture, const char* name) 34 | : benchmark_test{fixture, name, 10} 35 | { 36 | } 37 | 38 | void SetUp() override 39 | { 40 | std::ifstream istr(_filename); 41 | 42 | istr.seekg(0, std::ios::seek_dir::end); 43 | auto size = istr.tellg(); 44 | 45 | _text.resize(size); 46 | 47 | istr.seekg(0, std::ios::seek_dir::beg); 48 | 49 | istr.read(&_text[0], size); 50 | } 51 | 52 | std::string _filename = "sample.json"; 53 | std::string _text; 54 | }; 55 | 56 | BENCHMARK(json_any_benchmark, parse_string) 57 | { 58 | auto func = [&]() 59 | { 60 | native::json::parse(_text); 61 | }; 62 | benchmark("parse_string", func); 63 | } 64 | 65 | BENCHMARK(json_any_benchmark, parse_istringstream) 66 | { 67 | auto func = [&]() 68 | { 69 | std::istringstream istr(_text); 70 | native::json::parse_stream(istr); 71 | }; 72 | 73 | benchmark("parse_stream", func); 74 | } 75 | 76 | BENCHMARK(json_any_benchmark, parse_ifstream) 77 | { 78 | auto func = [&]() 79 | { 80 | std::ifstream istr(_filename); 81 | native::json::parse_stream(istr); 82 | }; 83 | 84 | benchmark("parse_stream", func); 85 | } 86 | 87 | #if defined(JSON11) 88 | BENCHMARK(json_any_benchmark, json11_parse_string) 89 | { 90 | auto func = [&]() 91 | { 92 | json11::Json json; 93 | std::string error; 94 | json.parse(_text, error); 95 | EXPECT_EQ("", error); 96 | }; 97 | benchmark("parse_string", func); 98 | } 99 | 100 | #endif -------------------------------------------------------------------------------- /benchmarks/string_builder_benchmarks.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013 Mike Naquin. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | #include "test.h" 18 | 19 | #include "benchmark.h" 20 | 21 | #include "native/string_builder.h" 22 | 23 | #include 24 | 25 | using namespace native; 26 | using std::to_string; 27 | 28 | template 29 | void test_strings() 30 | { 31 | Stream ostr; 32 | ostr << "The" << ' ' << "quick" << ' ' << "brown" << ' ' << "fox" << ' ' 33 | << "jumps" << ' ' << "over" << ' ' << "the" << ' ' << "lazy" << ' ' 34 | << "dog"; 35 | ostr.str(); 36 | } 37 | 38 | template 39 | void test_integers() 40 | { 41 | Stream ostr; 42 | ostr << 0 << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8 << 9; 43 | ostr.str(); 44 | } 45 | 46 | template 47 | void test_floats() 48 | { 49 | Stream ostr; 50 | ostr << 1.1 << 2.2 << 3.3 << 4.4 << 5.5 << 6.6 << 7.7 << 8.8 << 9.9; 51 | ostr.str(); 52 | } 53 | 54 | BENCHMARK(benchmark_test, build_std_stringstream) 55 | { 56 | 57 | benchmark([]() 58 | { 59 | test_strings(); 60 | }); 61 | } 62 | 63 | BENCHMARK(benchmark_test, build_native_string_builder) 64 | { 65 | benchmark([]() 66 | { 67 | test_strings(); 68 | }); 69 | } 70 | 71 | BENCHMARK(benchmark_test, build_integers_std_stringstream) 72 | { 73 | 74 | benchmark([]() 75 | { 76 | test_integers(); 77 | }); 78 | } 79 | 80 | BENCHMARK(benchmark_test, build_integers_native_string_builder) 81 | { 82 | benchmark([]() 83 | { 84 | test_integers(); 85 | }); 86 | } 87 | 88 | BENCHMARK(benchmark_test, build_floats_std_stringstream) 89 | { 90 | 91 | benchmark([]() 92 | { 93 | test_floats(); 94 | }); 95 | } 96 | 97 | BENCHMARK(benchmark_test, build_floats_native_string_builder) 98 | { 99 | benchmark([]() 100 | { 101 | test_floats(); 102 | }); 103 | } 104 | -------------------------------------------------------------------------------- /benchmarks/string_benchmarks.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013 Mike Naquin. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | #ifndef NATIVE_STRING_BENCHMARKS_H__ 18 | #define NATIVE_STRING_BENCHMARKS_H__ 19 | 20 | #include "test.h" 21 | 22 | #include "native/istring.h" 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | template 29 | void benchmark_string_copy(String s, size_t n) 30 | { 31 | for (size_t i = 0; i < n; ++i) 32 | { 33 | String s2 = s; 34 | } 35 | } 36 | 37 | template 38 | void benchmark_string_hash(String& s) 39 | { 40 | size_t hash = std::hash::type>()(s); 41 | if (hash == 0) 42 | { 43 | std::cout << "fail" << std::endl; 44 | } 45 | } 46 | 47 | template 48 | void benchmark_string_index(String& s) 49 | { 50 | typename String::value_type ch = s[0]; 51 | for (std::size_t i = 1; i < s.size(); ++i) 52 | { 53 | ch = std::max(ch, s[i]); 54 | } 55 | } 56 | 57 | template 58 | void benchmark_string_substr(const String& s) 59 | { 60 | auto sub = s.substr(5, 30); 61 | } 62 | 63 | template 64 | std::vector benchmark_create_unordered_map_keys() 65 | { 66 | return { 67 | {"zero"}, 68 | {"one"}, 69 | {"two"}, 70 | {"three"}, 71 | {"four"}, 72 | {"five"}, 73 | {"six"}, 74 | {"seven"}, 75 | {"eight"}, 76 | {"nine"}, 77 | {"ten"}, 78 | {"eleven"}, 79 | {"twelve"}, 80 | {"thirteen"}, 81 | {"fourteen"}, 82 | {"fifteen"}, 83 | }; 84 | } 85 | 86 | template 87 | std::unordered_map benchmark_create_unordered_map(const std::vector& keys) 88 | { 89 | std::unordered_map result; 90 | unsigned i = 0; 91 | for (const auto& key : keys) { 92 | result.emplace(key, i++); 93 | } 94 | return result; 95 | } 96 | 97 | template 98 | void benchmark_unordered_map(const std::vector& keys, const std::unordered_map& map) 99 | { 100 | for (const auto& key : keys) { 101 | auto it = map.find(key); 102 | if (it == map.end()) 103 | { 104 | std::cout << "not found" << std::endl; 105 | } 106 | } 107 | } 108 | 109 | 110 | #endif 111 | -------------------------------------------------------------------------------- /test/string_builder_test.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013 Mike Naquin. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | #include "test.h" 18 | #include "string_test.h" 19 | 20 | #include "benchmark.h" 21 | 22 | #include "native/istring.h" 23 | #include "native/string_builder.h" 24 | 25 | #include 26 | #include 27 | 28 | #include 29 | #include 30 | 31 | using namespace native; 32 | using std::to_string; 33 | 34 | template 35 | void test_append() 36 | { 37 | StringBuilder sb; 38 | EXPECT_EQ("", sb.slice()); 39 | 40 | sb << "foo"; 41 | EXPECT_EQ("foo", sb.slice()); 42 | 43 | sb << "bar"; 44 | EXPECT_EQ("foobar", sb.slice()); 45 | 46 | sb.clear(); 47 | sb << "abcdefghijklmnopqrstuvwxyz"; 48 | EXPECT_EQ("abcdefghijklmnopqrstuvwxyz", sb.slice()); 49 | 50 | sb << "0123456789"; 51 | sb << "0123456789"; 52 | sb << "0123456789"; 53 | EXPECT_EQ("abcdefghijklmnopqrstuvwxyz012345678901234567890123456789", sb.slice()); 54 | 55 | sb.clear(); 56 | sb << "abcdefghijklmnopqrstuvwxyz"; 57 | EXPECT_EQ("abcdefghijklmnopqrstuvwxyz", sb.slice()); 58 | 59 | sb.clear(); 60 | sb << 4294967296u; 61 | EXPECT_EQ("4294967296", sb.slice()); 62 | 63 | sb.clear(); 64 | sb << 18446744073709551615u; 65 | EXPECT_EQ("18446744073709551615", sb.slice()); 66 | 67 | sb.clear(); 68 | sb << -32767; 69 | EXPECT_EQ("-32767", sb.slice()); 70 | 71 | sb.clear(); 72 | sb << -2147483647; 73 | EXPECT_EQ("-2147483647", sb.slice()); 74 | 75 | sb.clear(); 76 | sb << -9223372036854775807; 77 | EXPECT_EQ("-9223372036854775807", sb.slice()); 78 | 79 | sb.clear(); 80 | sb << 1.2345f; 81 | EXPECT_EQ("1.2345000505447388", sb.slice()); 82 | 83 | sb.clear(); 84 | sb << 123456.789; 85 | EXPECT_EQ("123456.789000", sb.slice()); 86 | 87 | sb.clear(); 88 | sb << 'f' << 'o' << 'o' << 'b' << 'a' << 'r'; 89 | EXPECT_EQ("foobar", sb.slice()); 90 | 91 | istring move_it(std::move(sb)); 92 | EXPECT_EQ("foobar", move_it); 93 | 94 | auto syntax = (string_builder() << "The" << " quick" << " brown" << " fox" << " jumps" << " over" << " the" << " lazy" << " dog").str(); 95 | EXPECT_EQ("The quick brown fox jumps over the lazy dog", syntax); 96 | } 97 | 98 | TEST(StringBuilder, Append) 99 | { 100 | test_append(); 101 | } 102 | 103 | TEST(StringBuilder, str) 104 | { 105 | string_builder sb; 106 | sb << "foo"; 107 | sb << "bar"; 108 | 109 | auto s = sb.str(); 110 | EXPECT_EQ("foobar", s); 111 | EXPECT_EQ("", sb.slice()); 112 | } 113 | -------------------------------------------------------------------------------- /benchmarks/json_parser_benchmark.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2015 Mike Naquin. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | #include "benchmark.h" 18 | 19 | #include "native/json/parser.h" 20 | 21 | #include 22 | 23 | #if defined(RAPID_JSON) 24 | #include "rapidjson/reader.h" 25 | #endif 26 | 27 | #include 28 | 29 | class json_parser_benchmark : public benchmark_test 30 | { 31 | public: 32 | json_parser_benchmark(const char* fixture, const char* name) 33 | : benchmark_test{fixture, name, 1000} 34 | { 35 | } 36 | 37 | void SetUp() override 38 | { 39 | std::ifstream istr(_filename); 40 | 41 | istr.seekg(0, std::ios::seek_dir::end); 42 | auto size = istr.tellg(); 43 | 44 | _text.resize(size); 45 | 46 | istr.seekg(0, std::ios::seek_dir::beg); 47 | 48 | istr.read(&_text[0], size); 49 | } 50 | 51 | std::string _filename = "sample.json"; 52 | std::string _text; 53 | }; 54 | 55 | BENCHMARK(json_parser_benchmark, parse_string_empty_handler) 56 | { 57 | native::json::parser parser; 58 | native::json::handler<> handler; 59 | auto func = [&]() 60 | { 61 | parser.parse(_text, handler); 62 | }; 63 | benchmark("parse_string_empty_handler", func); 64 | } 65 | 66 | BENCHMARK(json_parser_benchmark, parse_iterator_empty_handler) 67 | { 68 | native::json::parser parser; 69 | native::json::handler<> handler; 70 | auto func = [&]() 71 | { 72 | parser.parse(_text.cbegin(), _text.cend(), handler); 73 | }; 74 | benchmark("parse_iterator_empty_handler", func); 75 | } 76 | 77 | BENCHMARK(json_parser_benchmark, parse_raw_string_empty_handler) 78 | { 79 | native::json::parser parser; 80 | native::json::handler<> handler; 81 | auto func = [&]() 82 | { 83 | parser.parse(_text.c_str(), _text.size(), handler); 84 | }; 85 | benchmark("parse_raw_string_empty_handler", func); 86 | } 87 | 88 | BENCHMARK(json_parser_benchmark, parse_stream_empty_handler) 89 | { 90 | native::json::parser parser; 91 | native::json::handler<> handler; 92 | auto func = [&]() 93 | { 94 | std::istringstream istr(_text); 95 | parser.parse_stream(istr, handler); 96 | }; 97 | 98 | benchmark("parse_stream_empty_handler", func); 99 | } 100 | 101 | #if defined(RAPID_JSON) 102 | BENCHMARK(json_parser_benchmark, rapidjson_parse_string_empty_handler) 103 | { 104 | rapidjson::BaseReaderHandler<> h; 105 | rapidjson::Reader reader; 106 | auto func = [&]() 107 | { 108 | rapidjson::StringStream s(_text.c_str()); 109 | reader.Parse<0>(s, h); 110 | }; 111 | benchmark("rapid_json_empty_handler", func); 112 | } 113 | #endif 114 | -------------------------------------------------------------------------------- /test/json_writer_test.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2015 Mike Naquin. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | #include "test.h" 18 | 19 | #include "native/json/writer.h" 20 | 21 | #include 22 | 23 | using namespace native; 24 | 25 | TEST(json_writer_test, write_object) 26 | { 27 | std::ostringstream ostr; 28 | json::writer writer(ostr, 2); 29 | writer.open_object(); 30 | writer.append("foo", 42); 31 | writer.append("bar", "string"); 32 | writer.append("bool", true); 33 | writer.close_object(); 34 | 35 | EXPECT_EQ(R"json({ 36 | "foo": 42, 37 | "bar": "string", 38 | "bool": true 39 | })json", 40 | ostr.str()); 41 | } 42 | 43 | TEST(json_writer_test, write_array) 44 | { 45 | std::ostringstream ostr; 46 | json::writer writer(ostr, 2); 47 | writer.open_array(); 48 | writer.append("foo"); 49 | writer.append(42); 50 | writer.close_array(); 51 | 52 | EXPECT_EQ(R"json([ 53 | "foo", 54 | 42 55 | ])json", 56 | ostr.str()); 57 | } 58 | 59 | TEST(json_writer_test, write_complex_objects) 60 | { 61 | std::ostringstream ostr; 62 | json::writer writer(ostr, 2); 63 | writer.open_object(); 64 | writer.append("foo", 42); 65 | writer.append("bar", "string"); 66 | writer.key("array"); 67 | writer.open_array(); 68 | writer.append("foo"); 69 | writer.append(42); 70 | writer.open_object(); 71 | writer.append("foo", 42); 72 | writer.append("bar", "string"); 73 | writer.close_object(); 74 | writer.close_array(); 75 | writer.close_object(); 76 | 77 | EXPECT_EQ(R"json({ 78 | "foo": 42, 79 | "bar": "string", 80 | "array": [ 81 | "foo", 82 | 42, 83 | { 84 | "foo": 42, 85 | "bar": "string" 86 | } 87 | ] 88 | })json", 89 | ostr.str()); 90 | } 91 | 92 | TEST(json_writer_test, write_utf_8) 93 | { 94 | { 95 | std::ostringstream ostr; 96 | json::writer writer(ostr, 2); 97 | writer.append("κόσμε"); 98 | 99 | EXPECT_EQ(R"json("\u03ba\u03cc\u03c3\u03bc\u03b5")json", ostr.str()); 100 | } 101 | 102 | { 103 | std::ostringstream ostr; 104 | json::writer writer(ostr, 2); 105 | 106 | writer.open_object(); 107 | writer.append("dollar", "\x24"); 108 | writer.append("cents", "\xC2\xA2"); 109 | writer.append("euro", "\xE2\x82\xAC"); 110 | writer.append("G clef", "\xF0\x9D\x84\x9E"); 111 | writer.close_object(); 112 | 113 | EXPECT_EQ(R"json({ 114 | "dollar": "$", 115 | "cents": "\u00a2", 116 | "euro": "\u20ac", 117 | "G clef": "\ud834\udd1e" 118 | })json", 119 | ostr.str()); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /test/slice_test.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013 Mike Naquin. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | #include "test.h" 18 | #include "string_test.h" 19 | 20 | #include "native/istring.h" 21 | 22 | #include 23 | 24 | using namespace native; 25 | 26 | TEST(splice_string, compare) { test_string_compare(); } 27 | 28 | TEST(splice_string, constructors) 29 | { 30 | istring ctor2(5, 'a'); 31 | 32 | string_slice ctor1; 33 | string_slice ctor3c(ctor2, 1); 34 | string_slice ctor3a(ctor3c, 1); 35 | string_slice ctor3b(ctor2.std_str(), 1); 36 | string_slice ctor4a("foobar", 3); 37 | string_slice ctor4b("foobar", 3); 38 | string_slice ctor5("foobar"); 39 | string_slice ctor7a(ctor5); 40 | string_slice ctor7b(ctor5.std_str()); 41 | // String ctor7c((string(ctor5))); 42 | string_slice ctor8Original("foobar"); 43 | string_slice ctor8(std::move(ctor8Original)); 44 | string_slice ctor9({'f', 'o', 'o', 'b', 'a', 'r'}); 45 | 46 | EXPECT_EQ("", ctor1); 47 | EXPECT_EQ("aaaaa", ctor2); 48 | EXPECT_EQ("aaaa", ctor3a); 49 | EXPECT_EQ("aaaa", ctor3b); 50 | EXPECT_EQ("aaaa", ctor3c); 51 | EXPECT_EQ("foo", ctor4a); 52 | EXPECT_EQ("foo", ctor4b); 53 | EXPECT_EQ("foobar", ctor5); 54 | EXPECT_EQ(ctor5, ctor7a); 55 | EXPECT_EQ(ctor5, ctor7b); 56 | // EXPECT_EQ(ctor5, ctor7c); 57 | EXPECT_EQ("foobar", ctor8Original); 58 | EXPECT_EQ("foobar", ctor8); 59 | EXPECT_EQ("foobar", ctor9); 60 | 61 | istring ctor7c((istring(ctor2))); 62 | 63 | EXPECT_EQ("aaaa", ctor3c); 64 | EXPECT_EQ(ctor2, ctor7c); 65 | } 66 | 67 | TEST(splice_string, assignment_operators) 68 | { 69 | test_string_assignment_operators(); 70 | 71 | string_slice slice_expected("lions"); 72 | istring s = slice_expected; 73 | EXPECT_EQ(slice_expected, s); 74 | } 75 | 76 | TEST(splice_string, iterators) { test_string_iterators(); } 77 | 78 | TEST(splice_string, attributes) 79 | { 80 | test_string_attributes(); 81 | istring s("foobar"); 82 | EXPECT_EQ(0, strcmp("foobar", s.c_str())); 83 | } 84 | 85 | TEST(splice_string, find) { test_string_find(); } 86 | 87 | TEST(splice_string, rfind) { test_string_rfind(); } 88 | 89 | TEST(splice_string, find_first_of) 90 | { 91 | test_string_find_first_of(); 92 | } 93 | 94 | TEST(splice_string, find_last_of) { test_string_find_last_of(); } 95 | 96 | TEST(splice_string, find_first_not_of) 97 | { 98 | test_string_find_first_not_of(); 99 | } 100 | 101 | TEST(splice_string, find_last_not_of) 102 | { 103 | test_string_find_last_not_of(); 104 | } 105 | 106 | TEST(splice_string, split) { test_string_split(); } 107 | 108 | TEST(splice_string, ostream) { test_string_ostream(); } 109 | -------------------------------------------------------------------------------- /include/native/json/handler.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013 Mike Naquin. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | #ifndef NATIVE_JSON_HANDLER_H__ 18 | #define NATIVE_JSON_HANDLER_H__ 19 | 20 | #include "native/config.h" 21 | 22 | #include "native/json/types.h" 23 | 24 | namespace native 25 | { 26 | namespace json 27 | { 28 | 29 | // When JSON input is parsed, the types are passed into the defined handler. 30 | // 31 | // When a key is parsed, the key() method will be called before the value 32 | // is parsed. This gives us the opportunity to check the key for an expected 33 | // data type. Here we can be smart about our type checking. If you expect a 34 | // uint16_t value, it's bounds are also checked. 35 | // 36 | // If the expected type is unknown, then the value will be parsed as either 37 | // a 32-bit integer first. If it's too big to fit as a 32-bit integer, then 38 | // a 64-bit integer is used. The same goes for floating point types. 39 | // 40 | // If a value cannot be converted, then a std::range_error is thrown. 41 | // 42 | // Note that a handler does not have to derive from this handler. This means 43 | // that we can use template methods to pull out the values. 44 | // 45 | // struct simple_handler 46 | // { 47 | // using char_type = char; 48 | // 49 | // data_type start_array() { return type_unknown; } 50 | // void end_array() {} 51 | // 52 | // void start_object() {} 53 | // void end_object() {} 54 | // 55 | // data_type key(const char_type* key, std::size_t length) 56 | // { 57 | // return type_unknown; 58 | // } 59 | // 60 | // void value(const char_type* val, std::size_t length) {} 61 | // void value(std::nullptr_t) {} 62 | // void value(bool val) {} 63 | // 64 | // // parse any integer 65 | // template 66 | // std::enable_if::value, void>::type 67 | // value(T val) {} 68 | // 69 | // // parse any floating point 70 | // template 71 | // std::enable_if::value, void>::type 72 | // value(T val) {} 73 | // }; 74 | template 75 | class handler 76 | { 77 | public: 78 | using char_type = Ch; 79 | 80 | handler() = default; 81 | 82 | // Return expected data type or type_unknown if not known. 83 | data_type start_array() { return type_unknown; } 84 | void end_array() {} 85 | 86 | void start_object() {} 87 | void end_object() {} 88 | 89 | // This method is called when a key is parsed. 90 | // 91 | // The character pointer to key will remain valid until the next key is 92 | // parsed. This means that it is safe to store for data types, but not 93 | // object types! 94 | data_type key(const char_type* key, std::size_t length) 95 | { 96 | return type_unknown; // return the expected type 97 | } 98 | 99 | void value(const char_type* val, std::size_t length) {} 100 | void value(std::nullptr_t) {} 101 | void value(bool val) {} 102 | void value(short val) {} 103 | void value(unsigned short val) {} 104 | void value(int val) {} 105 | void value(unsigned val) {} 106 | void value(long val) {} 107 | void value(unsigned long val) {} 108 | void value(long long val) {} 109 | void value(unsigned long long val) {} 110 | void value(float val) {} 111 | void value(double val) {} 112 | void value(long double val) {} 113 | }; 114 | 115 | } // namespace json 116 | } // namespace native 117 | 118 | #endif 119 | -------------------------------------------------------------------------------- /include/native/detail/real.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013 Mike Naquin. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | #ifndef NATIVE_REAL_H__ 18 | #define NATIVE_REAL_H__ 19 | 20 | #include "native/config.h" 21 | 22 | #include "native/detail/integers.h" 23 | 24 | #include "native/detail/double-conversion/strtod.h" 25 | #include "native/detail/double-conversion/double-conversion.h" 26 | 27 | namespace native 28 | { 29 | namespace detail 30 | { 31 | 32 | template 33 | T string_to_real(const number_parse& attribs); 34 | 35 | template <> 36 | inline long double 37 | string_to_real(const number_parse& attribs) 38 | { 39 | using namespace double_conversion; 40 | double converted = Strtod( 41 | Vector(attribs.buffer, attribs.length), attribs.exponent); 42 | return attribs.sign ? -converted : converted; 43 | } 44 | 45 | template <> 46 | inline double string_to_real(const number_parse& attribs) 47 | { 48 | using namespace double_conversion; 49 | double converted = Strtod( 50 | Vector(attribs.buffer, attribs.length), attribs.exponent); 51 | return attribs.sign ? -converted : converted; 52 | } 53 | 54 | template <> 55 | inline float string_to_real(const number_parse& attribs) 56 | { 57 | using namespace double_conversion; 58 | float converted = Strtof(Vector(attribs.buffer, attribs.length), 59 | attribs.exponent); 60 | return attribs.sign ? -converted : converted; 61 | } 62 | 63 | constexpr int kConvMaxDecimalInShortestLow = -6; 64 | constexpr int kConvMaxDecimalInShortestHigh = 21; 65 | 66 | // Wrapper around DoubleToStringConverter 67 | template 68 | typename std::enable_if::value>::type 69 | stream_append(Stream& stream, Source value, 70 | double_conversion::DoubleToStringConverter::DtoaMode mode, 71 | unsigned int numDigits) 72 | { 73 | using namespace double_conversion; 74 | DoubleToStringConverter conv(DoubleToStringConverter::NO_FLAGS, "infinity", 75 | "NaN", 'E', kConvMaxDecimalInShortestLow, 76 | kConvMaxDecimalInShortestHigh, 77 | 6, // max leading padding zeros 78 | 1); // max trailing padding zeros 79 | char buffer[256]; 80 | StringBuilder builder(buffer, sizeof(buffer)); 81 | switch (mode) 82 | { 83 | case DoubleToStringConverter::SHORTEST: 84 | conv.ToShortest(value, &builder); 85 | break; 86 | case DoubleToStringConverter::SHORTEST_SINGLE: 87 | conv.ToShortestSingle(value, &builder); 88 | break; 89 | case DoubleToStringConverter::FIXED: 90 | conv.ToFixed(value, numDigits, &builder); 91 | break; 92 | case DoubleToStringConverter::PRECISION: 93 | conv.ToPrecision(value, numDigits, &builder); 94 | break; 95 | } 96 | const size_t length = builder.position(); 97 | builder.Finalize(); 98 | stream.write(buffer, length); 99 | } 100 | 101 | // As above, but for floating point 102 | template 103 | typename std::enable_if::value>::type 104 | stream_append(Stream& stream, Source value) 105 | { 106 | stream_append(stream, value, 107 | double_conversion::DoubleToStringConverter::SHORTEST, 0); 108 | } 109 | 110 | } // namespace detail 111 | } // namespace native 112 | 113 | #endif 114 | -------------------------------------------------------------------------------- /include/native/json/parser.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013 Mike Naquin. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | #ifndef NATIVE_JSON_PARSER_H__ 18 | #define NATIVE_JSON_PARSER_H__ 19 | 20 | #include "native/config.h" 21 | 22 | #include "native/json/detail/parser_impl.h" 23 | 24 | #include "native/utf.h" 25 | 26 | namespace native 27 | { 28 | namespace json 29 | { 30 | 31 | // basic_parser takes an input source and parses the JSON data into the given 32 | // handler. 33 | // 34 | // See native/json/exceptions.h for specific exception types that are thrown. 35 | template 36 | class basic_parser 37 | { 38 | public: 39 | using source_encoding_type = SourceEncoding; 40 | using target_encoding_type = TargetEncoding; 41 | using char_type = typename source_encoding_type::char_type; 42 | 43 | // Parses JSON source as a string with the given handler. 44 | // 45 | // Throws json_exception on error, 46 | template 47 | void parse(const String& source, Handler& handler) 48 | { 49 | using iterator_type = typename String::const_iterator; 50 | using stream_type = iterator_stream; 51 | stream_type stream(source.cbegin(), source.cend()); 52 | detail::parser_impl parser(std::move(stream), 54 | handler); 55 | parser.parse_whole(); 56 | } 57 | 58 | // Parses JSON source as a const char* with the given handler. 59 | // 60 | // Throws json_exception on error, 61 | template 62 | void parse(const char_type* source, std::size_t length, Handler& handler) 63 | { 64 | using iterator_type = const char_type*; 65 | using stream_type = iterator_stream; 66 | 67 | stream_type stream(source, source + length); 68 | detail::parser_impl parser(std::move(stream), 70 | handler); 71 | parser.parse_whole(); 72 | } 73 | 74 | // Parses JSON from an iterator range with the given handler. 75 | // 76 | // Throws json_exception on error, 77 | template 78 | void parse(Iterator first, Iterator last, Handler& handler) 79 | { 80 | using iterator_type = Iterator; 81 | using stream_type = iterator_stream; 82 | stream_type stream(first, last); 83 | detail::parser_impl parser(std::move(stream), 85 | handler); 86 | parser.parse(); 87 | } 88 | 89 | // Parses JSON from an input stream with the given handler. 90 | // 91 | // Throws json_exception on error, 92 | template 93 | void parse_stream(IStream& istr, Handler& handler) 94 | { 95 | using stream_type = istream_stream; 96 | stream_type stream(istr); 97 | detail::parser_impl parser(std::move(stream), 99 | handler); 100 | parser.parse(); 101 | } 102 | }; 103 | 104 | using parser = basic_parser; 105 | 106 | } // namespace json 107 | } // namespace native 108 | 109 | #endif 110 | -------------------------------------------------------------------------------- /include/native/json/types.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013 Mike Naquin. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | #ifndef NATIVE_JSON_TYPES_H__ 18 | #define NATIVE_JSON_TYPES_H__ 19 | 20 | #include "native/config.h" 21 | 22 | #include 23 | 24 | namespace native 25 | { 26 | namespace json 27 | { 28 | 29 | // enumeration for JSON-types 30 | enum element_type : unsigned char 31 | { 32 | json_null, 33 | json_array, 34 | json_bool, 35 | json_integer, 36 | json_real, 37 | json_object, 38 | json_string, 39 | }; 40 | 41 | // enumeration for C++ types 42 | enum data_type : unsigned char 43 | { 44 | type_unknown, 45 | type_object, 46 | type_array, 47 | type_string, 48 | type_bool, 49 | type_short, 50 | type_unsigned_short, 51 | type_int, 52 | type_long, 53 | type_long_long, 54 | type_unsigned, 55 | type_unsigned_long, 56 | type_unsigned_long_long, 57 | type_float, 58 | type_double, 59 | type_long_double, 60 | }; 61 | 62 | template 63 | struct type_mapper; 64 | 65 | template <> 66 | struct type_mapper 67 | { 68 | static constexpr data_type value = type_string; 69 | }; 70 | 71 | template <> 72 | struct type_mapper> 73 | { 74 | static constexpr data_type value = type_string; 75 | }; 76 | 77 | template <> 78 | struct type_mapper 79 | { 80 | static constexpr data_type value = type_string; 81 | }; 82 | 83 | template <> 84 | struct type_mapper> 85 | { 86 | static constexpr data_type value = type_string; 87 | }; 88 | 89 | template <> 90 | struct type_mapper 91 | { 92 | static constexpr data_type value = type_string; 93 | }; 94 | 95 | template <> 96 | struct type_mapper> 97 | { 98 | static constexpr data_type value = type_string; 99 | }; 100 | 101 | template <> 102 | struct type_mapper 103 | { 104 | static constexpr data_type value = type_string; 105 | }; 106 | 107 | template <> 108 | struct type_mapper> 109 | { 110 | static constexpr data_type value = type_string; 111 | }; 112 | 113 | template <> 114 | struct type_mapper 115 | { 116 | static constexpr data_type value = type_bool; 117 | }; 118 | 119 | template <> 120 | struct type_mapper 121 | { 122 | static constexpr data_type value = type_short; 123 | }; 124 | 125 | template <> 126 | struct type_mapper 127 | { 128 | static constexpr data_type value = type_unsigned_short; 129 | }; 130 | 131 | template <> 132 | struct type_mapper 133 | { 134 | static constexpr data_type value = type_int; 135 | }; 136 | 137 | template <> 138 | struct type_mapper 139 | { 140 | static constexpr data_type value = type_long; 141 | }; 142 | 143 | template <> 144 | struct type_mapper 145 | { 146 | static constexpr data_type value = type_long_long; 147 | }; 148 | 149 | template <> 150 | struct type_mapper 151 | { 152 | static constexpr data_type value = type_unsigned; 153 | }; 154 | 155 | template <> 156 | struct type_mapper 157 | { 158 | static constexpr data_type value = type_unsigned_long; 159 | }; 160 | 161 | template <> 162 | struct type_mapper 163 | { 164 | static constexpr data_type value = type_unsigned_long_long; 165 | }; 166 | 167 | template <> 168 | struct type_mapper 169 | { 170 | static constexpr data_type value = type_float; 171 | }; 172 | 173 | template <> 174 | struct type_mapper 175 | { 176 | static constexpr data_type value = type_double; 177 | }; 178 | 179 | template <> 180 | struct type_mapper 181 | { 182 | static constexpr data_type value = type_long_double; 183 | }; 184 | } 185 | } // namespace native::json 186 | 187 | #endif 188 | -------------------------------------------------------------------------------- /docs/Strings.md: -------------------------------------------------------------------------------- 1 | Strings 2 | ======== 3 | 4 | What's wrong with std::string? 5 | ------------------------------ 6 | 7 | While std::string is perfectly servicable, there are many drawbacks to it being 8 | a mutable container. Many people have written customized string classes over 9 | the years--for various reasons--but chiefly among them is efficiency. 10 | 11 | C++ developers don't use C++ to write quick disposable code. C++ code is meant to 12 | maximize the use of your hardware at a high level of abstraction. 13 | 14 | More than any other container, it seems that std::string falls short of this 15 | ideal. 16 | 17 | Why should we use an immutable string? 18 | -------------------------------------- 19 | 20 | Many other less efficient languages define their string types as immutable for 21 | very good reasons. 22 | 23 | - *Thread safety:* Since immutable strings are thread safe, they are safe to be 24 | used in other threads without having to make a deep copy. No need to worry 25 | about moving a string, or wrapping it in a std::shared_ptr to go between 26 | lambdas. 27 | - *Less overhead:* Without needing to worry about reallocating memory when the 28 | string grows, the implementation can be more efficient. Less memory is used 29 | per string (no need to worry about capacity and such), and there's no need 30 | to allocate more memory than necessary. 31 | - *Hashing:* Since the string will never change, we can compute the hash once 32 | and save ourselves the need to re-hash every time we need it. For longer 33 | strings this is a hugh win. Also, as an added benefit, the hash of a string 34 | literal is known _at compile time_. 35 | - *Slicing or substrings:* Given their immutability, strings can be efficiently 36 | "sliced" without having to allocate extra memory and copy over the relevant 37 | data. We can take a substring almost for free! 38 | 39 | What good is native::string_builder over std::string or std::ostringstream? 40 | --------------------------------------------------------------------------- 41 | 42 | Since we are using an immutable string, we can't compose a string within one 43 | class like std::string does. That means that `operator+=` overloads won't do 44 | us any good. 45 | 46 | Also, since we want to avoid unnecessary copies and other inefficiencies, 47 | we want to avoid having to copy values from std::ostringstream. 48 | 49 | Immutable String 50 | ---------------- 51 | 52 | ``` 53 | #include "native/istring.h" 54 | 55 | using native::istring; // 'i' for immutable 56 | 57 | // immutable string 58 | istring s = "some really long string"; 59 | 60 | // shallow copy, immutable strings can share memory 61 | istring copy = s; 62 | 63 | // istring::literal performs no allocations!!! 64 | s = istring::literal("this is a string literal"); 65 | 66 | auto lambda = [s]() 67 | { 68 | // efficiently copied into lambda 69 | // immutable and thread safe - return this lambda anywhere 70 | std::cout << s << std::endl; 71 | }; 72 | ``` 73 | 74 | String Slices 75 | ------------- 76 | 77 | ``` 78 | istring s = "foobar"; 79 | 80 | // take a slice of a string from characters 3 to 6 81 | string_splice splice_s = s(3,6); // "bar" 82 | ``` 83 | 84 | Hashing 85 | ------- 86 | 87 | ``` 88 | std::unordered_map hash_table{{ 89 | // ... 90 | {"some key", 42}, 91 | // ... 92 | }}; 93 | 94 | // hash for istring "some key" only done once 95 | auto value1 = hash_table["some key"]; 96 | auto value2 = hash_table["some key"]; 97 | auto value3 = hash_table["some key"]; 98 | 99 | 100 | // even better - the key is only hashed once 101 | native::istring key{"some key"}; 102 | 103 | value1 = hash_table["some key"]; 104 | value2 = hash_table["some key"]; 105 | value3 = hash_table["some key"]; 106 | 107 | // best - the key is only hashed at compile time 108 | auto key = native::istring::literal("some key"); 109 | 110 | value1 = hash_table["some key"]; 111 | value2 = hash_table["some key"]; 112 | value3 = hash_table["some key"]; 113 | ``` 114 | 115 | String Builder 116 | -------------- 117 | 118 | 119 | Benchmarks 120 | ========== 121 | 122 | - Mac OS 10.9 2.4 GHz Intel Core i5 123 | - Results taken from fastest performance in 100,000 iterations 124 | 125 | ![](https://raw.github.com/syvex/native-wiki/master/benchmark-assign.png) 126 | 127 | String hashes are cached in `istring` for values that do not fit within 128 | the small string optimization. Therefore, we do not have to recalculate them. 129 | 130 | Hashes for strings created with `istring::literal` are computed at compile time. 131 | 132 | ![](https://raw.github.com/syvex/native-wiki/master/benchmark-istring-hash.png) 133 | -------------------------------------------------------------------------------- /benchmarks/benchmark.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013 Mike Naquin. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | #ifndef NATIVE_BENCHMARK_H__ 18 | #define NATIVE_BENCHMARK_H__ 19 | 20 | #include "test.h" 21 | 22 | #include 23 | 24 | #include 25 | 26 | #define BENCHMARK(test_fixture, test_name) \ 27 | BENCHMARK_IMPL(test_fixture, test_name, test_fixture, \ 28 | ::testing::internal::GetTypeId()) 29 | 30 | #define BENCHMARK_IMPL(test_case_name, test_name, parent_class, parent_id) \ 31 | class GTEST_TEST_CLASS_NAME_(test_case_name, test_name) \ 32 | : public parent_class \ 33 | { \ 34 | public: \ 35 | GTEST_TEST_CLASS_NAME_(test_case_name, test_name)() \ 36 | : parent_class(#test_case_name, #test_name) \ 37 | { \ 38 | } \ 39 | \ 40 | private: \ 41 | virtual void TestBody(); \ 42 | static ::testing::TestInfo* const test_info_ GTEST_ATTRIBUTE_UNUSED_; \ 43 | GTEST_DISALLOW_COPY_AND_ASSIGN_(GTEST_TEST_CLASS_NAME_(test_case_name, \ 44 | test_name)); \ 45 | }; \ 46 | \ 47 | ::testing::TestInfo* const GTEST_TEST_CLASS_NAME_(test_case_name, \ 48 | test_name)::test_info_ = \ 49 | ::testing::internal::MakeAndRegisterTestInfo( \ 50 | #test_case_name, #test_name, NULL, NULL, (parent_id), \ 51 | parent_class::SetUpTestCase, parent_class::TearDownTestCase, \ 52 | new ::testing::internal::TestFactoryImpl< \ 53 | GTEST_TEST_CLASS_NAME_(test_case_name, test_name)>); \ 54 | void GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::TestBody() 55 | 56 | class benchmark_test : public ::testing::Test 57 | { 58 | public: 59 | benchmark_test(const char* fixture, const char* name, 60 | std::size_t trial_count = 100000); 61 | 62 | virtual void SetUp(); 63 | virtual void TearDown(); 64 | 65 | template 66 | void benchmark(const std::string& name, Func func) 67 | { 68 | auto fastest = 69 | std::numeric_limits::max(); 70 | boost::timer::nanosecond_type total = 0; 71 | boost::timer::cpu_times fastestElapsed; 72 | for (std::size_t i = 0; i < timers.size(); ++i) 73 | { 74 | boost::timer::cpu_timer timer; 75 | func(); 76 | timer.stop(); 77 | const auto elapsed = timer.elapsed(); 78 | if (elapsed.wall < fastest) 79 | { 80 | fastestElapsed = elapsed; 81 | fastest = elapsed.wall; 82 | } 83 | total += elapsed.wall; 84 | } 85 | 86 | report(fastestElapsed, total, name); 87 | } 88 | 89 | template 90 | void benchmark(Func func) 91 | { 92 | benchmark(this->name, func); 93 | } 94 | 95 | virtual void report(const boost::timer::cpu_times& fastestElapsed, 96 | const boost::timer::nanosecond_type& total, 97 | const std::string& name); 98 | 99 | protected: 100 | std::string fixture; 101 | std::string name; 102 | std::vector timers; 103 | }; 104 | 105 | #endif 106 | -------------------------------------------------------------------------------- /include/native/json/input_streams.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013 Mike Naquin. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | #ifndef NATIVE_JSON_INPUT_STREAMS_H__ 18 | #define NATIVE_JSON_INPUT_STREAMS_H__ 19 | 20 | #include "native/config.h" 21 | 22 | #include 23 | 24 | namespace native 25 | { 26 | namespace json 27 | { 28 | 29 | // input_stream interface 30 | template 31 | class input_stream 32 | { 33 | public: 34 | using char_type = Ch; 35 | 36 | // Return the current line number. 37 | inline std::size_t line() const; 38 | 39 | // Return the current column number. 40 | inline std::size_t column() const; 41 | 42 | // Return the current position. 43 | inline std::size_t position() const; 44 | 45 | // Return true if this is the end of the stream. 46 | inline bool eof() const; 47 | 48 | // Return the current character. 49 | inline char_type get() const; 50 | 51 | // Move ahead one position in the stream. 52 | inline void next(); 53 | 54 | // Return the current character, then increment ahead one position. 55 | inline char_type get(); 56 | 57 | // Increment the line number when a new line is found. 58 | inline void increment_line(); 59 | }; 60 | 61 | template 62 | class iterator_stream 63 | { 64 | public: 65 | using iterator_type = Iterator; 66 | using char_type = typename std::remove_cv< 67 | typename std::remove_reference::type>::type; 68 | 69 | iterator_stream(iterator_type first, iterator_type last) 70 | : _head(first) 71 | , _first(first) 72 | , _last(last) 73 | , _line(1) 74 | , _col_start(first) 75 | { 76 | } 77 | 78 | inline std::size_t line() const { return _line; } 79 | 80 | inline std::size_t column() const 81 | { 82 | return static_cast(_first - _col_start); 83 | } 84 | 85 | inline std::size_t position() const 86 | { 87 | return static_cast(_first - _head); 88 | } 89 | 90 | inline bool eof() const { return _first == _last; } 91 | 92 | inline char_type peek() const { return *_first; } 93 | 94 | inline void next() { ++_first; } 95 | 96 | inline char_type get() 97 | { 98 | const auto ch = peek(); 99 | next(); 100 | return ch; 101 | } 102 | 103 | inline void increment_line() 104 | { 105 | ++_line; 106 | _col_start = _first; 107 | } 108 | 109 | private: 110 | iterator_type _head; 111 | iterator_type _first; 112 | iterator_type _last; 113 | std::size_t _line; 114 | iterator_type _col_start; 115 | }; 116 | 117 | template 118 | class istream_stream 119 | { 120 | public: 121 | using istream_type = IStream; 122 | using char_type = typename istream_type::char_type; 123 | using pos_type = typename istream_type::pos_type; 124 | 125 | istream_stream(istream_type& istr) 126 | : _istr(istr) 127 | , _line() 128 | , _current(istr.get()) 129 | , _col_start() 130 | { 131 | } 132 | 133 | inline std::size_t line() const { return _line; } 134 | 135 | inline std::size_t column() const { return _istr.tellg() - _col_start; } 136 | 137 | inline bool eof() const { return _istr.eof(); } 138 | 139 | inline char_type peek() const { return _current; } 140 | 141 | inline void next() { _current = _istr.get(); } 142 | 143 | inline char_type get() 144 | { 145 | const auto ch = peek(); 146 | next(); 147 | return ch; 148 | } 149 | 150 | inline void increment_line() 151 | { 152 | ++_line; 153 | _col_start = _istr.tellg(); 154 | } 155 | 156 | private: 157 | istream_type& _istr; 158 | char_type _current; 159 | std::size_t _line; 160 | pos_type _col_start; 161 | }; 162 | 163 | template 164 | iterator_stream make_parser_range_iterator(Iterator first, 165 | Iterator last) 166 | { 167 | return std::move(iterator_stream(first, last)); 168 | } 169 | 170 | template 171 | istream_stream make_parser_range_string(IStream& istr) 172 | { 173 | return std::move(istream_stream(istr)); 174 | } 175 | } 176 | } // namespace native::json 177 | 178 | #endif 179 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | 6 | # Compiled Dynamic libraries 7 | *.so 8 | *.dylib 9 | 10 | # Compiled Static libraries 11 | *.lai 12 | *.la 13 | *.a 14 | 15 | 16 | ######################### 17 | # .gitignore file for Xcode4 / OS X Source projects 18 | # 19 | # Version 2.1 20 | # For latest version, see: http://stackoverflow.com/questions/49478/git-ignore-file-for-xcode-projects 21 | # 22 | # 2013 updates: 23 | # - fixed the broken "save personal Schemes" 24 | # - added line-by-line explanations for EVERYTHING (some were missing) 25 | # 26 | # NB: if you are storing "built" products, this WILL NOT WORK, 27 | # and you should use a different .gitignore (or none at all) 28 | # This file is for SOURCE projects, where there are many extra 29 | # files that we want to exclude 30 | # 31 | ######################### 32 | 33 | ##### 34 | # OS X temporary files that should never be committed 35 | # 36 | # c.f. http://www.westwind.com/reference/os-x/invisibles.html 37 | 38 | .DS_Store 39 | 40 | # c.f. http://www.westwind.com/reference/os-x/invisibles.html 41 | 42 | .Trashes 43 | 44 | # c.f. http://www.westwind.com/reference/os-x/invisibles.html 45 | 46 | *.swp 47 | 48 | # *.lock - this is used and abused by many editors for many different things. 49 | # For the main ones I use (e.g. Eclipse), it should be excluded 50 | # from source-control, but YMMV 51 | 52 | *.lock 53 | 54 | # 55 | # profile - REMOVED temporarily (on double-checking, this seems incorrect; I can't find it in OS X docs?) 56 | #profile 57 | 58 | 59 | #### 60 | # Xcode temporary files that should never be committed 61 | # 62 | # NB: NIB/XIB files still exist even on Storyboard projects, so we want this... 63 | 64 | *~.nib 65 | 66 | 67 | #### 68 | # Xcode build files - 69 | # 70 | # NB: slash on the end, so we only remove the FOLDER, not any files that were badly named "DerivedData" 71 | 72 | DerivedData/ 73 | 74 | # NB: slash on the end, so we only remove the FOLDER, not any files that were badly named "build" 75 | 76 | build/ 77 | 78 | 79 | ##### 80 | # Xcode private settings (window sizes, bookmarks, breakpoints, custom executables, smart groups) 81 | # 82 | # This is complicated: 83 | # 84 | # SOMETIMES you need to put this file in version control. 85 | # Apple designed it poorly - if you use "custom executables", they are 86 | # saved in this file. 87 | # 99% of projects do NOT use those, so they do NOT want to version control this file. 88 | # ..but if you're in the 1%, comment out the line "*.pbxuser" 89 | 90 | # .pbxuser: http://lists.apple.com/archives/xcode-users/2004/Jan/msg00193.html 91 | 92 | *.pbxuser 93 | 94 | # .mode1v3: http://lists.apple.com/archives/xcode-users/2007/Oct/msg00465.html 95 | 96 | *.mode1v3 97 | 98 | # .mode2v3: http://lists.apple.com/archives/xcode-users/2007/Oct/msg00465.html 99 | 100 | *.mode2v3 101 | 102 | # .perspectivev3: http://stackoverflow.com/questions/5223297/xcode-projects-what-is-a-perspectivev3-file 103 | 104 | *.perspectivev3 105 | 106 | # NB: also, whitelist the default ones, some projects need to use these 107 | !default.pbxuser 108 | !default.mode1v3 109 | !default.mode2v3 110 | !default.perspectivev3 111 | 112 | 113 | #### 114 | # Xcode 4 - semi-personal settings 115 | # 116 | # 117 | # OPTION 1: --------------------------------- 118 | # throw away ALL personal settings (including custom schemes! 119 | # - unless they are "shared") 120 | # 121 | # NB: this is exclusive with OPTION 2 below 122 | xcuserdata 123 | 124 | # OPTION 2: --------------------------------- 125 | # get rid of ALL personal settings, but KEEP SOME OF THEM 126 | # - NB: you must manually uncomment the bits you want to keep 127 | # 128 | # NB: this is exclusive with OPTION 1 above 129 | # 130 | #xcuserdata/**/* 131 | 132 | # (requires option 2 above): Personal Schemes 133 | # 134 | #!xcuserdata/**/xcschemes/* 135 | 136 | #### 137 | # XCode 4 workspaces - more detailed 138 | # 139 | # Workspaces are important! They are a core feature of Xcode - don't exclude them :) 140 | # 141 | # Workspace layout is quite spammy. For reference: 142 | # 143 | # /(root)/ 144 | # /(project-name).xcodeproj/ 145 | # project.pbxproj 146 | # /project.xcworkspace/ 147 | # contents.xcworkspacedata 148 | # /xcuserdata/ 149 | # /(your name)/xcuserdatad/ 150 | # UserInterfaceState.xcuserstate 151 | # /xcsshareddata/ 152 | # /xcschemes/ 153 | # (shared scheme name).xcscheme 154 | # /xcuserdata/ 155 | # /(your name)/xcuserdatad/ 156 | # (private scheme).xcscheme 157 | # xcschememanagement.plist 158 | # 159 | # 160 | 161 | #### 162 | # Xcode 4 - Deprecated classes 163 | # 164 | # Allegedly, if you manually "deprecate" your classes, they get moved here. 165 | # 166 | # We're using source-control, so this is a "feature" that we do not want! 167 | 168 | *.moved-aside 169 | 170 | #### 171 | # UNKNOWN: recommended by others, but I can't discover what these files are 172 | # 173 | # ...none. Everything is now explained. 174 | -------------------------------------------------------------------------------- /include/native/json/exceptions.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013 Mike Naquin. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | #ifndef NATIVE_JSON_EXCEPTIONS_H__ 18 | #define NATIVE_JSON_EXCEPTIONS_H__ 19 | 20 | #include "native/config.h" 21 | 22 | #include 23 | 24 | namespace native 25 | { 26 | namespace json 27 | { 28 | 29 | struct json_exception : public std::runtime_error 30 | { 31 | json_exception(const std::string& message, std::size_t line, 32 | std::size_t column) 33 | : std::runtime_error{message + ". Line " + std::to_string(line) + 34 | ", column " + std::to_string(column) + "."} 35 | , _line(line) 36 | , _column(column) 37 | { 38 | } 39 | 40 | std::size_t line() const { return _line; } 41 | 42 | std::size_t column() const { return _column; } 43 | 44 | private: 45 | std::size_t _line; 46 | std::size_t _column; 47 | }; 48 | 49 | #define NATIVE_JSON_EXCEPTION_DECL(className, messageText) \ 50 | struct className : json_exception \ 51 | { \ 52 | className(std::size_t line, std::size_t column) \ 53 | : json_exception{messageText, line, column} \ 54 | { \ 55 | } \ 56 | className(const std::string& message, std::size_t line, \ 57 | std::size_t column) \ 58 | : json_exception{messageText ": " + message, line, column} \ 59 | { \ 60 | } \ 61 | }; 62 | 63 | NATIVE_JSON_EXCEPTION_DECL(expected_end_of_stream, "Expected end of stream") 64 | NATIVE_JSON_EXCEPTION_DECL(unexpected_end_of_stream, "Unexpected end of stream") 65 | NATIVE_JSON_EXCEPTION_DECL(expected_object_or_array, "Expected object or array") 66 | NATIVE_JSON_EXCEPTION_DECL(expected_colon_after_key, "Expected colon after key") 67 | NATIVE_JSON_EXCEPTION_DECL(expected_comma_or_close_curly_brace, 68 | "Expected comma or close curly brace") 69 | NATIVE_JSON_EXCEPTION_DECL(expected_comma_or_close_bracket, 70 | "Expected comman or close bracket") 71 | NATIVE_JSON_EXCEPTION_DECL(expected_null_value, "Expected null value") 72 | NATIVE_JSON_EXCEPTION_DECL(expected_true_value, "Expected true value") 73 | NATIVE_JSON_EXCEPTION_DECL(expected_false_value, "Expected false value") 74 | NATIVE_JSON_EXCEPTION_DECL(incorrect_hex_digit, "Incorrect hex digit") 75 | NATIVE_JSON_EXCEPTION_DECL(missing_second_in_surrogate_pair, 76 | "Missing second in surrogate pair") 77 | NATIVE_JSON_EXCEPTION_DECL(invalid_second_in_surrogate_pair, 78 | "Invalid second in surrogate pair") 79 | NATIVE_JSON_EXCEPTION_DECL(unknown_escape_character, "Unknown escape character") 80 | NATIVE_JSON_EXCEPTION_DECL(missing_start_quote, "Missig start quote") 81 | NATIVE_JSON_EXCEPTION_DECL(missing_end_quote, "Missing end quote") 82 | NATIVE_JSON_EXCEPTION_DECL(incorrect_unescaped_character, 83 | "Incorrect unescaped character") 84 | NATIVE_JSON_EXCEPTION_DECL(invalid_encoding, "Invalid encoding") 85 | NATIVE_JSON_EXCEPTION_DECL(expected_value, "Expected value") 86 | NATIVE_JSON_EXCEPTION_DECL( 87 | unexpected_signed_value, 88 | "The number given was signed when only unsigned was expected") 89 | NATIVE_JSON_EXCEPTION_DECL(number_too_big_for_expected_type, 90 | "The number given was too big for the expected type") 91 | NATIVE_JSON_EXCEPTION_DECL( 92 | unexpected_decimal_value, 93 | "The number given was a decimal instead of an integer") 94 | NATIVE_JSON_EXCEPTION_DECL(at_least_one_digit_in_fraction_part, 95 | "At least one digit in fraction part") 96 | NATIVE_JSON_EXCEPTION_DECL(at_least_one_digit_in_exponent, 97 | "At least one digit in exponent") 98 | NATIVE_JSON_EXCEPTION_DECL(unexpected_character, 99 | "An unexpected character was found") 100 | } 101 | } // namespace native::json 102 | 103 | #endif 104 | -------------------------------------------------------------------------------- /test/string_test.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013 Mike Naquin. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | #include "test.h" 18 | #include "string_test.h" 19 | 20 | #include "benchmark.h" 21 | 22 | #include "native/istring.h" 23 | #include "native/string_conversion.h" 24 | 25 | #include 26 | #include 27 | 28 | #include 29 | #include 30 | 31 | using namespace native; 32 | using std::to_string; 33 | 34 | TEST(string, compare) { test_string_compare(); } 35 | 36 | TEST(string, constructors) 37 | { 38 | test_string_constructors(); 39 | 40 | istring ctor2(5, 'a'); 41 | istring ctor3c(string_slice(ctor2), 1); 42 | istring ctor7c((string_slice(ctor2))); 43 | 44 | EXPECT_EQ("aaaaa", ctor2); 45 | EXPECT_EQ("aaaa", ctor3c); 46 | EXPECT_EQ(ctor2, ctor7c); 47 | } 48 | 49 | TEST(string, assignment_operators) 50 | { 51 | test_string_assignment_operators(); 52 | 53 | string_slice slice_expected("lions"); 54 | istring s = slice_expected; 55 | EXPECT_EQ(slice_expected, s); 56 | } 57 | 58 | TEST(string, iterators) { test_string_iterators(); } 59 | 60 | TEST(string, literal) 61 | { 62 | auto literal = istring::literal("literal"); 63 | istring literalCopy = literal; 64 | 65 | EXPECT_EQ("literal", literal); 66 | EXPECT_EQ("literal", literalCopy); 67 | EXPECT_EQ(literal, literalCopy); 68 | EXPECT_EQ(literalCopy, literal); 69 | } 70 | 71 | TEST(string, attributes) 72 | { 73 | test_string_attributes(); 74 | istring s("foobar"); 75 | EXPECT_EQ(0, strcmp("foobar", s.c_str())); 76 | } 77 | 78 | TEST(string, slice) 79 | { 80 | istring s("foobar"); 81 | 82 | auto slice = s(1, 2); 83 | EXPECT_EQ("oo", slice); 84 | 85 | slice = s(3, 6); 86 | EXPECT_EQ("bar", slice); 87 | } 88 | 89 | TEST(string, hash) 90 | { 91 | istring short_s("foobar"); 92 | EXPECT_EQ("foobar"_hash, short_s.hash()); 93 | 94 | istring dynamic_s("this string needs to be longer than 23 characters"); 95 | EXPECT_EQ("this string needs to be longer than 23 characters"_hash, 96 | dynamic_s.hash()); 97 | 98 | istring static_s = istring::literal("foobar"); 99 | EXPECT_EQ("foobar"_hash, short_s.hash()); 100 | } 101 | 102 | TEST(string, find) { test_string_find(); } 103 | 104 | TEST(string, rfind) { test_string_rfind(); } 105 | 106 | TEST(string, find_first_of) { test_string_find_first_of(); } 107 | 108 | TEST(string, find_last_of) { test_string_find_last_of(); } 109 | 110 | TEST(string, find_first_not_of) { test_string_find_first_not_of(); } 111 | 112 | TEST(string, find_last_not_of) { test_string_find_last_not_of(); } 113 | 114 | TEST(string, split) { test_string_split(); } 115 | 116 | TEST(string, OStream) { test_string_ostream(); } 117 | 118 | TEST(string, static_coversion) 119 | { 120 | { 121 | constexpr auto actual = to_string_literal::value; 122 | static_assert(sizeof(to_string_literal::value) == 2, ""); 123 | std::string expected = "1"; 124 | EXPECT_EQ(actual, expected); 125 | } 126 | 127 | { 128 | constexpr auto actual = to_string_literal::value; 129 | static_assert(sizeof(to_string_literal::value) == 3, ""); 130 | std::string expected = "12"; 131 | EXPECT_EQ(actual, expected); 132 | } 133 | 134 | { 135 | constexpr auto actual = to_string_literal::value; 136 | static_assert(sizeof(to_string_literal::value) == 4, ""); 137 | std::string expected = "123"; 138 | EXPECT_EQ(actual, expected); 139 | } 140 | 141 | { 142 | constexpr auto actual = to_string_literal::value; 143 | static_assert(sizeof(to_string_literal::value) == 3, ""); 144 | std::string expected = "-1"; 145 | EXPECT_EQ(actual, expected); 146 | } 147 | 148 | { 149 | constexpr auto actual = to_string_literal::value; 150 | static_assert(sizeof(to_string_literal::value) == 4, ""); 151 | std::string expected = "-12"; 152 | EXPECT_EQ(actual, expected); 153 | } 154 | 155 | { 156 | constexpr auto actual = to_string_literal::value; 157 | static_assert(sizeof(to_string_literal::value) == 5, ""); 158 | std::string expected = "-123"; 159 | EXPECT_EQ(actual, expected); 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /include/native/json/conversion.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013 Mike Naquin. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | #ifndef NATIVE_JSON_CONVERSION_H__ 18 | #define NATIVE_JSON_CONVERSION_H__ 19 | 20 | #include "native/config.h" 21 | 22 | #include "native/string_base.h" 23 | 24 | #include "native/json/input_streams.h" 25 | 26 | #include "native/json/detail/number_parse.h" 27 | #include "native/json/detail/integers.h" 28 | #include "native/json/detail/real.h" 29 | 30 | #include "native/detail/implicit_cast.h" 31 | 32 | #include 33 | #include 34 | 35 | namespace native 36 | { 37 | namespace json 38 | { 39 | 40 | // integer to integer 41 | template 42 | typename std::enable_if< 43 | std::is_integral::value && std::is_integral::value, T>::type 44 | to(U value) 45 | { 46 | // no range check 47 | return static_cast(value); 48 | } 49 | 50 | // real to real 51 | template 52 | typename std::enable_if::value && 53 | std::is_floating_point::value, 54 | T>::type 55 | to(U value) 56 | { 57 | // no range check 58 | return implicit_cast(value); 59 | } 60 | 61 | // integer to real 62 | template 63 | typename std::enable_if< 64 | std::is_integral::value && std::is_floating_point::value, T>::type 65 | to(U value) 66 | { 67 | // no range check 68 | return implicit_cast(value); 69 | } 70 | 71 | // real to integer 72 | template 73 | typename std::enable_if< 74 | std::is_floating_point::value && std::is_integral::value, T>::type 75 | to(U value) 76 | { 77 | // no range check 78 | return implicit_cast(value); 79 | } 80 | 81 | // integer to string 82 | template 83 | typename std::enable_if::value && std::is_integral::value, 84 | T>::type 85 | to(U value) 86 | { 87 | // TODO: optimize 88 | return std::to_string(value); 89 | } 90 | 91 | // real to string 92 | template 93 | typename std::enable_if< 94 | is_string_class::value && std::is_floating_point::value, T>::type 95 | to(U value) 96 | { 97 | // TODO: optimize 98 | return std::to_string(value); 99 | } 100 | 101 | // bool to string 102 | template 103 | typename std::enable_if::value, T>::type to(bool value) 104 | { 105 | return value ? "true" : "false"; 106 | } 107 | 108 | // string to string 109 | template 110 | const typename std::enable_if< 111 | is_string_class::value && is_string_class::value, T>::type& 112 | to(const U& value) 113 | { 114 | return value; 115 | } 116 | 117 | // char array to string 118 | template 119 | typename std::enable_if::value, T>::type 120 | to(const char* value) 121 | { 122 | return value; 123 | } 124 | 125 | // string to bool 126 | template 127 | typename std::enable_if< 128 | std::is_same::value && is_string_class::value, T>::type 129 | to(const U& value) 130 | { 131 | if (value.empty()) 132 | { 133 | return false; 134 | } 135 | else 136 | { 137 | return value[0] == 't'; // lazy 138 | } 139 | } 140 | 141 | // string to integer 142 | template 143 | typename std::enable_if::value && 144 | !std::is_same::value && 145 | is_string_class::value, 146 | T>::type 147 | to(const U& value) 148 | { 149 | detail::number_parse p; 150 | auto stream = make_parser_range_iterator(value.begin(), value.end()); 151 | p.to_buffer(stream); 152 | return detail::string_to_integer(p); 153 | } 154 | 155 | // string to real 156 | template 157 | typename std::enable_if< 158 | std::is_floating_point::value && is_string_class::value, T>::type 159 | to(const U& value) 160 | { 161 | detail::number_parse p; 162 | auto stream = make_parser_range_iterator(value.begin(), value.end()); 163 | p.to_buffer(stream); 164 | return detail::string_to_real(p); 165 | } 166 | 167 | namespace detail 168 | { 169 | template 170 | typename std::enable_if::value, T>::type append_to() 171 | { 172 | return T{}; 173 | } 174 | 175 | template 176 | typename std::enable_if::value, T>::type append_to(U&& value) 177 | { 178 | return to(value); 179 | } 180 | 181 | template 182 | typename std::enable_if::value, T>::type 183 | append_to(U&& value, Args&&... args) 184 | { 185 | return to(value) + append_to(std::forward(args)...); 186 | } 187 | } // namespace detail 188 | 189 | template 190 | typename std::enable_if::value, T>::type to(U&& value, 191 | Args&&... args) 192 | { 193 | return detail::append_to(std::forward(value), 194 | std::forward(args)...); 195 | } 196 | 197 | } // namespace json 198 | } // namespace native 199 | 200 | #endif 201 | -------------------------------------------------------------------------------- /include/native/detail/string_builder_core.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2014 Mike Naquin. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | #ifndef NATIVE_STRING_BUILDER_CORE_H__ 18 | #define NATIVE_STRING_BUILDER_CORE_H__ 19 | 20 | #include "native/hash.h" 21 | 22 | #include "native/detail/string_core.h" 23 | 24 | namespace native { 25 | 26 | template 27 | struct string_builder_core : string_core_base { 28 | using base_type = string_core_base; 29 | 30 | using traits_type = std::char_traits; 31 | using value_type = typename traits_type::char_type; 32 | using allocator_type = std::allocator; 33 | using size_type = typename allocator_type::size_type; 34 | using difference_type = typename allocator_type::difference_type; 35 | using reference = typename allocator_type::reference; 36 | using const_reference = typename allocator_type::const_reference; 37 | using pointer = typename allocator_type::pointer; 38 | using const_pointer = typename allocator_type::const_pointer; 39 | using const_iterator = const_pointer; 40 | using const_reverse_iterator = std::reverse_iterator; 41 | 42 | using dynamic_data = typename base_type::dynamic_data; 43 | 44 | inline constexpr string_builder_core(size_type capacity): 45 | _dynamic(capacity) 46 | { 47 | 48 | } 49 | 50 | inline string_builder_core(const_pointer ptr, size_type length) : _dynamic(ptr, length) 51 | { 52 | 53 | } 54 | 55 | inline string_builder_core(const_pointer ptr): 56 | string_builder_core(ptr, traits_type::length(ptr)) 57 | { 58 | 59 | } 60 | 61 | inline string_builder_core(size_type length, value_type c) : _dynamic(length, c) { } 62 | 63 | template 64 | string_builder_core(InputIterator first, InputIterator last, size_type length) : _dynamic(first, last, length) { } 65 | 66 | template 67 | inline string_builder_core(InputIterator first, InputIterator last): 68 | string_builder_core(first, last, iterator_length(first, last)) 69 | { 70 | 71 | } 72 | 73 | ~string_builder_core() 74 | { 75 | if (_dynamic.head) 76 | { 77 | _dynamic.decrement_shared_count(); 78 | } 79 | } 80 | 81 | // 82 | // immutable methods 83 | // 84 | inline size_type size() const { return _dynamic.length; } 85 | inline size_type capacity() const { return _dynamic.capacity(); } 86 | inline const_pointer data() const { return _dynamic.head; } 87 | 88 | // 89 | // mutable methods 90 | // 91 | pointer mutable_data() 92 | { 93 | if (_dynamic.reference_count() > 1) 94 | { 95 | size_t effective_capacity = _dynamic.capacity(); 96 | const auto new_head = dynamic_data::create(effective_capacity); 97 | base_type::raw_copy(_dynamic.head, _dynamic.head + _dynamic.length + 1, new_head); 98 | _dynamic.decrement_shared_count(); 99 | _dynamic.head = new_head; 100 | } 101 | return _dynamic.head; 102 | } 103 | 104 | void reserve(size_t capacity) 105 | { 106 | if (_dynamic.head) 107 | { 108 | if (_dynamic.capacity() >= capacity) 109 | { 110 | return; // has enough capacity 111 | } 112 | 113 | dynamic_data new_dynamic{&_dynamic.head[0], _dynamic.length, capacity}; 114 | _dynamic.decrement_shared_count(); 115 | _dynamic = std::move(new_dynamic); 116 | } 117 | else 118 | { 119 | _dynamic.assign(size_type{0}, capacity); 120 | } 121 | } 122 | 123 | void resize(size_t n) 124 | { 125 | if (!_dynamic.head) { 126 | _dynamic.assign(n, n); 127 | return; 128 | } 129 | 130 | if (n > capacity()) 131 | { 132 | reserve(n); 133 | } 134 | 135 | _dynamic.length = n; 136 | _dynamic.null_terminate(); 137 | } 138 | 139 | void push_back(value_type ch) 140 | { 141 | if (!_dynamic.head) 142 | { 143 | _dynamic.assign(size_type{0}, size_type{16}); 144 | } 145 | 146 | const auto current_size = _dynamic.length; 147 | if (current_size == _dynamic.capacity()) 148 | { 149 | reserve(1 + current_size * 3 / 2); 150 | } 151 | 152 | ++_dynamic.length; 153 | _dynamic.head[current_size] = ch; 154 | } 155 | 156 | void clear() 157 | { 158 | if (_dynamic.head) 159 | { 160 | _dynamic.length = 0; 161 | _dynamic.null_terminate(); 162 | } 163 | } 164 | 165 | void null_terminate() 166 | { 167 | if (_dynamic.head) 168 | { 169 | _dynamic.null_terminate(); 170 | } 171 | } 172 | 173 | void detach(dynamic_data& data) 174 | { 175 | if (_dynamic.head) 176 | { 177 | data = std::move(_dynamic); 178 | _dynamic.length = 0; 179 | _dynamic.head = nullptr; 180 | } 181 | else 182 | { 183 | data.length = 0; 184 | data.null_terminate(); 185 | } 186 | } 187 | 188 | void detach(dynamic_data& data, size_type new_capacity) 189 | { 190 | if (_dynamic.head) 191 | { 192 | data = std::move(_dynamic); 193 | } 194 | else 195 | { 196 | data.length = 0; 197 | data.null_terminate(); 198 | } 199 | 200 | _dynamic.create(new_capacity); 201 | } 202 | 203 | private: 204 | dynamic_data _dynamic; 205 | }; 206 | 207 | } // namespace native 208 | 209 | #endif 210 | -------------------------------------------------------------------------------- /include/native/detail/double-conversion/diy-fp.h: -------------------------------------------------------------------------------- 1 | // !!! 2 | // generated from /native/deps/double-conversion/setup.py 3 | // !!! 4 | // easy compile for double-conversion 5 | 6 | // ** diy-fp.h ** 7 | // Copyright 2010 the V8 project authors. All rights reserved. 8 | // Redistribution and use in source and binary forms, with or without 9 | // modification, are permitted provided that the following conditions are 10 | // met: 11 | // 12 | // * Redistributions of source code must retain the above copyright 13 | // notice, this list of conditions and the following disclaimer. 14 | // * Redistributions in binary form must reproduce the above 15 | // copyright notice, this list of conditions and the following 16 | // disclaimer in the documentation and/or other materials provided 17 | // with the distribution. 18 | // * Neither the name of Google Inc. nor the names of its 19 | // contributors may be used to endorse or promote products derived 20 | // from this software without specific prior written permission. 21 | // 22 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 23 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 24 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 25 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 26 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 27 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 28 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 29 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 30 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 31 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 32 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 | 34 | #ifndef DOUBLE_CONVERSION_DIY_FP_H_ 35 | #define DOUBLE_CONVERSION_DIY_FP_H_ 36 | 37 | #include "native/detail/double-conversion/utils.h" 38 | 39 | namespace native 40 | { 41 | namespace double_conversion 42 | { 43 | 44 | // This "Do It Yourself Floating Point" class implements a floating-point number 45 | // with a uint64 significand and an int exponent. Normalized DiyFp numbers will 46 | // have the most significant bit of the significand set. 47 | // Multiplication and Subtraction do not normalize their results. 48 | // DiyFp are not designed to contain special doubles (NaN and Infinity). 49 | class DiyFp 50 | { 51 | public: 52 | static const int kSignificandSize = 64; 53 | 54 | DiyFp() 55 | : f_(0) 56 | , e_(0) 57 | { 58 | } 59 | DiyFp(uint64_t significand, int exponent) 60 | : f_(significand) 61 | , e_(exponent) 62 | { 63 | } 64 | 65 | // this = this - other. 66 | // The exponents of both numbers must be the same and the significand of 67 | // this 68 | // must be bigger than the significand of other. 69 | // The result will not be normalized. 70 | void Subtract(const DiyFp& other) 71 | { 72 | ASSERT(e_ == other.e_); 73 | ASSERT(f_ >= other.f_); 74 | f_ -= other.f_; 75 | } 76 | 77 | // Returns a - b. 78 | // The exponents of both numbers must be the same and this must be bigger 79 | // than other. The result will not be normalized. 80 | static DiyFp Minus(const DiyFp& a, const DiyFp& b) 81 | { 82 | DiyFp result = a; 83 | result.Subtract(b); 84 | return result; 85 | } 86 | 87 | // this = this * other. 88 | void Multiply(const DiyFp& other); 89 | 90 | // returns a * b; 91 | static DiyFp Times(const DiyFp& a, const DiyFp& b) 92 | { 93 | DiyFp result = a; 94 | result.Multiply(b); 95 | return result; 96 | } 97 | 98 | void Normalize() 99 | { 100 | ASSERT(f_ != 0); 101 | uint64_t significand = f_; 102 | int exponent = e_; 103 | 104 | // This method is mainly called for normalizing boundaries. In general 105 | // boundaries need to be shifted by 10 bits. We thus optimize for this 106 | // case. 107 | const uint64_t k10MSBits = UINT64_2PART_C(0xFFC00000, 00000000); 108 | while ((significand & k10MSBits) == 0) 109 | { 110 | significand <<= 10; 111 | exponent -= 10; 112 | } 113 | while ((significand & kUint64MSB) == 0) 114 | { 115 | significand <<= 1; 116 | exponent--; 117 | } 118 | f_ = significand; 119 | e_ = exponent; 120 | } 121 | 122 | static DiyFp Normalize(const DiyFp& a) 123 | { 124 | DiyFp result = a; 125 | result.Normalize(); 126 | return result; 127 | } 128 | 129 | uint64_t f() const { return f_; } 130 | int e() const { return e_; } 131 | 132 | void set_f(uint64_t new_value) { f_ = new_value; } 133 | void set_e(int new_value) { e_ = new_value; } 134 | 135 | private: 136 | static const uint64_t kUint64MSB = UINT64_2PART_C(0x80000000, 00000000); 137 | 138 | uint64_t f_; 139 | int e_; 140 | }; 141 | 142 | } // namespace double_conversion 143 | } // namespace native 144 | 145 | // ** diy-fp.cc ** 146 | // Copyright 2010 the V8 project authors. All rights reserved. 147 | // Redistribution and use in source and binary forms, with or without 148 | // modification, are permitted provided that the following conditions are 149 | // met: 150 | // 151 | // * Redistributions of source code must retain the above copyright 152 | // notice, this list of conditions and the following disclaimer. 153 | // * Redistributions in binary form must reproduce the above 154 | // copyright notice, this list of conditions and the following 155 | // disclaimer in the documentation and/or other materials provided 156 | // with the distribution. 157 | // * Neither the name of Google Inc. nor the names of its 158 | // contributors may be used to endorse or promote products derived 159 | // from this software without specific prior written permission. 160 | // 161 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 162 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 163 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 164 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 165 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 166 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 167 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 168 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 169 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 170 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 171 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 172 | 173 | #include "native/detail/double-conversion/diy-fp.h" 174 | #include "native/detail/double-conversion/utils.h" 175 | 176 | namespace native 177 | { 178 | namespace double_conversion 179 | { 180 | 181 | inline void DiyFp::Multiply(const DiyFp& other) 182 | { 183 | // Simply "emulates" a 128 bit multiplication. 184 | // However: the resulting number only contains 64 bits. The least 185 | // significant 64 bits are only used for rounding the most significant 64 186 | // bits. 187 | const uint64_t kM32 = 0xFFFFFFFFU; 188 | uint64_t a = f_ >> 32; 189 | uint64_t b = f_ & kM32; 190 | uint64_t c = other.f_ >> 32; 191 | uint64_t d = other.f_ & kM32; 192 | uint64_t ac = a * c; 193 | uint64_t bc = b * c; 194 | uint64_t ad = a * d; 195 | uint64_t bd = b * d; 196 | uint64_t tmp = (bd >> 32) + (ad & kM32) + (bc & kM32); 197 | // By adding 1U << 31 to tmp we round the final result. 198 | // Halfway cases will be round up. 199 | tmp += 1U << 31; 200 | uint64_t result_f = ac + (ad >> 32) + (bc >> 32) + (tmp >> 32); 201 | e_ += other.e_ + 64; 202 | f_ = result_f; 203 | } 204 | 205 | } // namespace double_conversion 206 | } // namespace native 207 | #endif // DOUBLE_CONVERSION_DIY_FP_H_ 208 | -------------------------------------------------------------------------------- /benchmarks/string_benchmarks.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013 Mike Naquin. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | #include "test.h" 18 | #include "string_benchmarks.h" 19 | 20 | #include "benchmark.h" 21 | 22 | #include 23 | #include 24 | 25 | #include 26 | #include 27 | 28 | using namespace native; 29 | using std::to_string; 30 | 31 | #define STRING_10 "0123456789" 32 | 33 | #define STRING_100 \ 34 | STRING_10 STRING_10 STRING_10 STRING_10 STRING_10 STRING_10 STRING_10 \ 35 | STRING_10 STRING_10 STRING_10 36 | 37 | #define STRING_1000 \ 38 | STRING_100 STRING_100 STRING_100 STRING_100 STRING_100 STRING_100 \ 39 | STRING_100 STRING_100 STRING_100 STRING_100 40 | 41 | #define STRING_10000 \ 42 | STRING_1000 STRING_1000 STRING_1000 STRING_1000 STRING_1000 STRING_1000 \ 43 | STRING_1000 STRING_1000 STRING_1000 STRING_1000 44 | 45 | #define STRING_100000 \ 46 | STRING_10000 STRING_10000 STRING_10000 STRING_10000 STRING_10000 \ 47 | STRING_10000 STRING_10000 STRING_10000 STRING_10000 STRING_10000 48 | 49 | #define STRING_1000000 \ 50 | STRING_100000 STRING_100000 STRING_100000 STRING_100000 STRING_100000 \ 51 | STRING_100000 STRING_100000 STRING_100000 STRING_100000 STRING_100000 52 | 53 | static const size_t NUM_ASSIGNS = 10; 54 | 55 | static std::vector SMALL_STRINGS = {STRING_10, STRING_100, 56 | STRING_1000}; 57 | static std::vector LARGE_STRINGS = { 58 | STRING_10, STRING_100, STRING_1000, 59 | STRING_10000, STRING_100000, STRING_1000000, 60 | }; 61 | 62 | BENCHMARK(benchmark_test, assign_std_string) 63 | { 64 | for (std::string s : SMALL_STRINGS) 65 | { 66 | for (std::size_t i = 0; i < NUM_ASSIGNS; ++i) 67 | { 68 | auto benchmark_name = "assign_std_string," + to_string(s.size()) + 69 | "," + to_string(i + 1); 70 | benchmark(benchmark_name, [&s, i]() 71 | { 72 | benchmark_string_copy(s, i + 1); 73 | }); 74 | } 75 | } 76 | } 77 | 78 | BENCHMARK(benchmark_test, assign_istring) 79 | { 80 | for (istring s : SMALL_STRINGS) 81 | { 82 | for (std::size_t i = 0; i < NUM_ASSIGNS; ++i) 83 | { 84 | auto benchmark_name = "assign_istring," + to_string(s.size()) + 85 | "," + to_string(i + 1); 86 | benchmark(benchmark_name, [&s, i]() 87 | { 88 | benchmark_string_copy(s, i + 1); 89 | }); 90 | } 91 | } 92 | } 93 | 94 | BENCHMARK(benchmark_test, assign_istring_literal) 95 | { 96 | auto do_benchmark = [&](const istring& s) 97 | { 98 | for (std::size_t i = 0; i < NUM_ASSIGNS; ++i) 99 | { 100 | auto benchmark_name = "assign_istring_literal," + 101 | to_string(s.size()) + "," + to_string(i + 1); 102 | benchmark(benchmark_name, [&s, i]() 103 | { 104 | benchmark_string_copy(s, i + 1); 105 | }); 106 | } 107 | }; 108 | 109 | do_benchmark(istring::literal(STRING_10)); 110 | do_benchmark(istring::literal(STRING_100)); 111 | do_benchmark(istring::literal(STRING_1000)); 112 | } 113 | 114 | BENCHMARK(benchmark_test, hash_std_string) 115 | { 116 | auto do_benchmark = [&](const std::string& s) 117 | { 118 | auto benchmark_name = "hash_std_string," + to_string(s.size()); 119 | 120 | benchmark(benchmark_name, [&s]() 121 | { 122 | benchmark_string_hash(s); 123 | }); 124 | }; 125 | 126 | for (std::string s : LARGE_STRINGS) 127 | { 128 | do_benchmark(s); 129 | } 130 | } 131 | 132 | BENCHMARK(benchmark_test, hash_istring) 133 | { 134 | auto do_benchmark = [&](const istring& s) 135 | { 136 | auto benchmark_name = "hash_istring," + to_string(s.size()); 137 | 138 | benchmark(benchmark_name, [&s]() 139 | { 140 | benchmark_string_hash(s); 141 | }); 142 | }; 143 | 144 | for (istring s : LARGE_STRINGS) 145 | { 146 | do_benchmark(s); 147 | } 148 | } 149 | 150 | BENCHMARK(benchmark_test, hash_istring_literal) 151 | { 152 | auto do_benchmark = [&](const istring& s) 153 | { 154 | auto benchmark_name = "hash_istring_literal," + to_string(s.size()); 155 | 156 | benchmark(benchmark_name, [&s]() 157 | { 158 | benchmark_string_hash(s); 159 | }); 160 | }; 161 | 162 | do_benchmark(istring::literal(STRING_10)); 163 | do_benchmark(istring::literal(STRING_100)); 164 | do_benchmark(istring::literal(STRING_1000)); 165 | do_benchmark(istring::literal(STRING_10000)); 166 | do_benchmark(istring::literal(STRING_100000)); 167 | do_benchmark(istring::literal(STRING_1000000)); 168 | } 169 | 170 | BENCHMARK(benchmark_test, index_std_string) 171 | { 172 | std::string s = STRING_100; 173 | benchmark([&s]() 174 | { 175 | benchmark_string_index(s); 176 | }); 177 | } 178 | 179 | BENCHMARK(benchmark_test, index_native_string) 180 | { 181 | istring s = STRING_100; 182 | benchmark([&s]() 183 | { 184 | benchmark_string_index(s); 185 | }); 186 | } 187 | 188 | BENCHMARK(benchmark_test, split_std_string) 189 | { 190 | std::string s = STRING_100; 191 | std::vector result; 192 | benchmark([&s, &result]() 193 | { 194 | boost::algorithm::split(result, s, 195 | boost::algorithm::is_any_of("5")); 196 | }); 197 | } 198 | 199 | BENCHMARK(benchmark_test, split_native_string) 200 | { 201 | istring s = STRING_100; 202 | benchmark([&s]() 203 | { 204 | auto result = string_slice(s).split('5'); 205 | }); 206 | } 207 | 208 | BENCHMARK(benchmark_test, substr_std_string) 209 | { 210 | std::string s = STRING_100; 211 | benchmark([&s]() 212 | { 213 | benchmark_string_substr(s); 214 | }); 215 | } 216 | 217 | BENCHMARK(benchmark_test, substr_native_string) 218 | { 219 | istring s = STRING_100; 220 | benchmark([&s]() 221 | { 222 | benchmark_string_substr(s); 223 | }); 224 | } 225 | 226 | BENCHMARK(benchmark_test, unordered_map_std_string) 227 | { 228 | const auto keys = benchmark_create_unordered_map_keys(); 229 | const auto map = benchmark_create_unordered_map(keys); 230 | benchmark([&keys, &map]() 231 | { 232 | benchmark_unordered_map(keys, map); 233 | }); 234 | } 235 | 236 | BENCHMARK(benchmark_test, unordered_map_native_string) 237 | { 238 | const auto keys = benchmark_create_unordered_map_keys(); 239 | const auto map = benchmark_create_unordered_map(keys); 240 | benchmark([&keys, &map]() 241 | { 242 | benchmark_unordered_map(keys, map); 243 | }); 244 | } 245 | -------------------------------------------------------------------------------- /test/json_any_test.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2015 Mike Naquin. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | #include "test.h" 18 | 19 | #include "native/json.h" 20 | 21 | using namespace native; 22 | 23 | TEST(json_any_test, pod_types) 24 | { 25 | json::any null_value{}; 26 | EXPECT_TRUE(null_value.is_null()); 27 | EXPECT_TRUE(null_value.empty()); 28 | EXPECT_EQ("", null_value.string_value()); 29 | EXPECT_EQ(0.0, null_value.double_value()); 30 | EXPECT_EQ(0, null_value.int_value()); 31 | EXPECT_FALSE(null_value.bool_value()); 32 | 33 | json::any bool_value{true}; 34 | EXPECT_TRUE(bool_value.is_bool()); 35 | EXPECT_FALSE(bool_value.empty()); 36 | EXPECT_EQ("true", bool_value.string_value()); 37 | EXPECT_EQ(1.0, bool_value.double_value()); 38 | EXPECT_EQ(1, bool_value.int_value()); 39 | EXPECT_TRUE(bool_value.bool_value()); 40 | 41 | json::any int_value{-42}; 42 | EXPECT_TRUE(int_value.is_int()); 43 | EXPECT_FALSE(int_value.empty()); 44 | EXPECT_EQ("-42", int_value.string_value()); 45 | EXPECT_EQ(-42.0, int_value.double_value()); 46 | EXPECT_EQ(-42, int_value.int_value()); 47 | EXPECT_TRUE(int_value.bool_value()); 48 | 49 | json::any double_value{-42.0}; 50 | EXPECT_TRUE(double_value.is_double()); 51 | EXPECT_FALSE(double_value.empty()); 52 | EXPECT_EQ("-42.000000", double_value.string_value()); 53 | EXPECT_EQ(-42.0, double_value.double_value()); 54 | EXPECT_EQ(-42, double_value.int_value()); 55 | EXPECT_TRUE(double_value.bool_value()); 56 | 57 | json::any sting_value{"hello"}; 58 | EXPECT_TRUE(sting_value.is_string()); 59 | EXPECT_FALSE(sting_value.empty()); 60 | EXPECT_EQ("hello", sting_value.string_value()); 61 | EXPECT_THROW(sting_value.double_value(), std::range_error); 62 | EXPECT_THROW(sting_value.int_value(), std::range_error); 63 | EXPECT_FALSE(sting_value.bool_value()); 64 | } 65 | 66 | TEST(json_any_test, array) 67 | { 68 | json::any value{{42, "hello", true}}; 69 | EXPECT_TRUE(value.is_array()); 70 | EXPECT_EQ(42, value[0].int_value()); 71 | EXPECT_EQ("hello", value[1].string_value()); 72 | EXPECT_EQ(true, value[2].bool_value()); 73 | 74 | EXPECT_EQ(3, value.size()); 75 | EXPECT_FALSE(value.empty()); 76 | 77 | json::any copy = value; 78 | EXPECT_TRUE(copy.is_array()); 79 | EXPECT_EQ(42, copy[0].int_value()); 80 | EXPECT_EQ("hello", copy[1].string_value()); 81 | EXPECT_EQ(true, copy[2].bool_value()); 82 | 83 | json::any move = std::move(copy); 84 | EXPECT_TRUE(move.is_array()); 85 | EXPECT_EQ(42, move[0].int_value()); 86 | EXPECT_EQ("hello", move[1].string_value()); 87 | EXPECT_EQ(true, move[2].bool_value()); 88 | 89 | value.push_back("next"); 90 | value.push_back(false); 91 | value.push_back(5.5); 92 | 93 | EXPECT_EQ("next", value[3].string_value()); 94 | EXPECT_FALSE(value[4].bool_value()); 95 | EXPECT_EQ(5.5, value[5].double_value()); 96 | 97 | EXPECT_EQ(6, value.size()); 98 | 99 | EXPECT_EQ(42, value.begin()->int_value()); 100 | EXPECT_EQ(5.5, (value.end() - 1)->double_value()); 101 | 102 | value[4] = "blue"; 103 | EXPECT_EQ("blue", value[4].string_value()); 104 | 105 | value.pop_back(); 106 | 107 | EXPECT_EQ(5, value.size()); 108 | EXPECT_EQ(42, value.front().int_value()); 109 | EXPECT_EQ("blue", value.back().string_value()); 110 | 111 | value.emplace_back(std::move(move)); 112 | 113 | EXPECT_TRUE(value.back().is_array()); 114 | 115 | EXPECT_EQ(42, value.back().front().int_value()); 116 | EXPECT_EQ("hello", value.back()[1].string_value()); 117 | 118 | value.resize(10); 119 | 120 | EXPECT_EQ(10, value.size()); 121 | 122 | EXPECT_TRUE(value.back().is_null()); 123 | 124 | auto it = value.erase(value.begin() + 1); 125 | EXPECT_EQ(value.begin() + 1, it); 126 | 127 | it = value.erase(value.begin() + 1, value.end()); 128 | EXPECT_EQ(value.end(), it); 129 | EXPECT_EQ(1, value.size()); 130 | } 131 | 132 | TEST(json_any_test, object) 133 | { 134 | json::any value{{{"foo", 42}, {"bar", "string"}}}; 135 | EXPECT_TRUE(value.is_object()); 136 | EXPECT_EQ(42, value["foo"].int_value()); 137 | EXPECT_EQ("string", value["bar"].string_value()); 138 | 139 | EXPECT_EQ(2, value.size()); 140 | EXPECT_FALSE(value.empty()); 141 | 142 | json::any copy = value; 143 | EXPECT_TRUE(copy.is_object()); 144 | EXPECT_EQ(42, copy["foo"].int_value()); 145 | EXPECT_EQ("string", copy["bar"].string_value()); 146 | 147 | json::any move = std::move(copy); 148 | EXPECT_TRUE(move.is_object()); 149 | EXPECT_EQ(42, move["foo"].int_value()); 150 | EXPECT_EQ("string", move["bar"].string_value()); 151 | 152 | value["three"] = 3; 153 | value["four"] = 4.0; 154 | 155 | EXPECT_EQ(4, value.size()); 156 | 157 | EXPECT_EQ(3, value["three"].int_value()); 158 | EXPECT_EQ(4.0, value["four"].double_value()); 159 | 160 | EXPECT_EQ("string", move.find("bar")->second.string_value()); 161 | 162 | EXPECT_EQ(1, value.erase("four")); 163 | EXPECT_EQ(3, value.size()); 164 | 165 | auto it = value.find("three"); 166 | it = value.erase(it); 167 | EXPECT_EQ(2, value.size()); 168 | 169 | auto keys = value.keys(); 170 | EXPECT_NE(keys.end(), std::find(keys.begin(), keys.end(), "foo")); 171 | EXPECT_NE(keys.end(), std::find(keys.begin(), keys.end(), "bar")); 172 | 173 | auto values = value.values(); 174 | EXPECT_NE(values.end(), std::find(values.begin(), values.end(), "string")); 175 | EXPECT_NE(values.end(), std::find(values.begin(), values.end(), 42)); 176 | 177 | value["array"] = json::any{{2, "c++", 7.0}}; 178 | EXPECT_TRUE(value["array"].is_array()); 179 | EXPECT_EQ(2, value["array"][0].int_value()); 180 | EXPECT_EQ("c++", value["array"][1].string_value()); 181 | EXPECT_EQ(7.0, value["array"][2].double_value()); 182 | value["array"].push_back( 183 | native::json::any::object{{"problems", 99}, {"color", "orange"}}); 184 | EXPECT_EQ(99, value["array"].back()["problems"].int_value()); 185 | EXPECT_EQ("orange", value["array"].back()["color"].string_value()); 186 | } 187 | 188 | TEST(json_any_test, dump) 189 | { 190 | json::any value{{{"foo", 42}, {"bar", "string"}, {"null", nullptr}}}; 191 | auto json = value.dump(true, 2); 192 | EXPECT_EQ( 193 | R"json({ 194 | "bar": "string", 195 | "foo": 42, 196 | "null": null 197 | })json", 198 | json); 199 | 200 | value["array"] = json::any{{2, "c++", 1.5}}; 201 | value["array"].push_back( 202 | json::any::object{{"problems", 99}, {"color", "orange"}}); 203 | json = value.dump(true, 2); 204 | EXPECT_EQ(R"json({ 205 | "array": [ 206 | 2, 207 | "c++", 208 | 1.5, 209 | { 210 | "color": "orange", 211 | "problems": 99 212 | } 213 | ], 214 | "bar": "string", 215 | "foo": 42, 216 | "null": null 217 | })json", 218 | json); 219 | } 220 | 221 | TEST(json_any_test, parse) 222 | { 223 | auto json = istring::literal(R"json({ 224 | "array": [ 225 | 2, 226 | "c++", 227 | 1.5, 228 | { 229 | "color": "orange", 230 | "problems": 99 231 | } 232 | ], 233 | "bar": "string", 234 | "foo": 42 235 | })json"); 236 | 237 | auto value = json::parse(json); 238 | 239 | EXPECT_EQ(json, value.dump(true, 2)); 240 | } 241 | 242 | TEST(json_any_test, parse_std_string) 243 | { 244 | auto json = R"json({ 245 | "array": [ 246 | 2, 247 | "c++", 248 | 1.5, 249 | { 250 | "color": "orange", 251 | "problems": 99 252 | } 253 | ], 254 | "bar": "string", 255 | "foo": 42 256 | })json"; 257 | 258 | auto value = json::parse(json); 259 | 260 | EXPECT_EQ(json, value.dump(true, 2)); 261 | } 262 | -------------------------------------------------------------------------------- /include/native/json/any.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2015 Mike Naquin. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | #ifndef NATIVE_JSON_ANY_H__ 18 | #define NATIVE_JSON_ANY_H__ 19 | 20 | #include "native/config.h" 21 | 22 | #include "native/istring.h" 23 | 24 | #include "native/json/conversion.h" 25 | #include "native/json/types.h" 26 | 27 | #include 28 | #include 29 | 30 | namespace native 31 | { 32 | namespace json 33 | { 34 | 35 | // A JSON-like value class. 36 | template 37 | class basic_any 38 | { 39 | using this_type = basic_any; 40 | using array = std::vector; 41 | 42 | public: 43 | using string_type = String; 44 | using const_iterator = typename array::const_iterator; 45 | using value_type = this_type; 46 | using object = std::unordered_map; 47 | struct handler; 48 | struct object_key_iterator; 49 | struct object_value_iterator; 50 | struct object_item_iterator; 51 | 52 | template 53 | struct object_range; 54 | 55 | // Default constructor. Sets as null. 56 | basic_any() noexcept; 57 | 58 | // nullptr_t overload. Sets as null. 59 | basic_any(std::nullptr_t) noexcept; 60 | 61 | // Initialize as the given type with that type's default constructor. 62 | basic_any(element_type type); 63 | 64 | // Initialize with bool value. 65 | basic_any(bool value); 66 | 67 | // Initialize with an integral value. 68 | basic_any(short value); 69 | basic_any(unsigned short value); 70 | basic_any(int value); 71 | basic_any(unsigned value); 72 | basic_any(long value); 73 | basic_any(unsigned long value); 74 | basic_any(long long value); 75 | basic_any(unsigned long long value); 76 | 77 | // Initialize with a floating point value. 78 | basic_any(float value); 79 | basic_any(double value); 80 | basic_any(long double value); 81 | 82 | // Initialize with a string value. 83 | basic_any(const char* value); 84 | basic_any(const string_type& value); 85 | 86 | // Initialize with an array. Works with initializer_list too. 87 | // basic_any x{{42, "foo", 5.0, true, nullptr}}; 88 | basic_any(const array& value); 89 | basic_any(array&& value); 90 | 91 | // Initialize with an object. 92 | // basic_any x{{{"foo", 42}, {"bar", 5.0}}}; 93 | basic_any(const object& value); 94 | basic_any(object&& value); 95 | 96 | basic_any(const basic_any& right); 97 | basic_any(basic_any&& right) noexcept; 98 | 99 | basic_any& operator=(const basic_any& right); 100 | basic_any& operator=(basic_any&& right) noexcept; 101 | 102 | ~basic_any(); 103 | 104 | // Returns the data type of this object. 105 | element_type type() const; 106 | 107 | // Easy type checks. 108 | bool is_null() const; 109 | bool is_object() const; 110 | bool is_array() const; 111 | bool is_bool() const; 112 | bool is_double() const; 113 | bool is_int() const; 114 | bool is_string() const; 115 | 116 | // Returns true if this object is an integral or a floating point. 117 | bool is_number() const; 118 | 119 | // Return the value of this object as a useful type. If the value of the 120 | // object 121 | // can be converted, then a conversion is performed on the specified type. 122 | // 123 | // If no conversion can be performed, then std::logic_error is thrown. 124 | string_type string_value() const; 125 | long double double_value() const; 126 | long long int_value() const; 127 | bool bool_value() const; 128 | 129 | // If this object is a container type (string, array, object) and is empty 130 | // then true is returned. False otherwise. 131 | bool empty() const; 132 | 133 | // If this object is a container type, then the number of elements is 134 | // returned. 135 | // 136 | // Otherwise std::logic_error is thrown. 137 | std::size_t size() const; 138 | 139 | // 140 | // Array methods. If this object is not an array, then std::logic_error is 141 | // thrown. 142 | // 143 | const_iterator begin() const; 144 | const_iterator end() const; 145 | 146 | basic_any& front(); 147 | const basic_any& front() const; 148 | 149 | basic_any& back(); 150 | const basic_any& back() const; 151 | 152 | basic_any& operator[](std::size_t index); 153 | const basic_any& operator[](std::size_t index) const; 154 | 155 | void push_back(const basic_any& value); 156 | void emplace_back(basic_any&& value); 157 | 158 | void pop_back(); 159 | 160 | void resize(std::size_t n, const basic_any& defaultValue = nullptr); 161 | 162 | const_iterator erase(const_iterator it); 163 | const_iterator erase(const_iterator first, const_iterator last); 164 | 165 | // 166 | // Object methods. If this object is not an array, then std::logic_error is 167 | // thrown. 168 | // 169 | basic_any& operator[](const string_type& key); 170 | const basic_any& operator[](const string_type& key) const; 171 | 172 | typename object::const_iterator find(const string_type& key) const; 173 | 174 | std::size_t erase(const string_type& key); 175 | 176 | typename object::const_iterator erase(typename object::const_iterator it); 177 | 178 | object_range keys() const; 179 | object_range values() const; 180 | object_range items() const; 181 | 182 | // 183 | // Deep comparisons. 184 | // 185 | bool operator==(const basic_any& right) const; 186 | bool operator!=(const basic_any& right) const; 187 | bool operator<(const basic_any& right) const; 188 | bool operator>(const basic_any& right) const; 189 | bool operator<=(const basic_any& right) const; 190 | bool operator>=(const basic_any& right) const; 191 | 192 | // Convert to a JSON string. 193 | void dump(std::ostream& ostr, bool sort_keys = false, 194 | std::size_t indent = 0) const; 195 | 196 | string_type dump(bool sort_keys = false, std::size_t indent = 0) const; 197 | void dump(std::string& str, bool sort_keys = false, 198 | std::size_t indent = 0) const; 199 | 200 | private: 201 | template 202 | void _dump(Writer& writer, bool sort_keys) const; 203 | 204 | void _throw_invalid_type() const; 205 | 206 | element_type _type; 207 | union data 208 | { 209 | data(std::nullptr_t); 210 | data(bool value); 211 | data(long long value); 212 | data(long double value); 213 | data(const string_type& value); 214 | data(const array& value); 215 | data(array&& value); 216 | data(const object& value); 217 | data(object&& value); 218 | data(element_type type, const data& right); 219 | data(element_type type, data&& right); 220 | ~data(); 221 | 222 | void assign(element_type, const data& right); 223 | void assign(element_type, data&& right); 224 | 225 | std::nullptr_t _null; 226 | bool _bool; 227 | long long _int; 228 | long double _double; 229 | string_type _string; 230 | array _array; 231 | object _object; 232 | } _data; 233 | }; 234 | 235 | using any = basic_any; 236 | 237 | // Initialize by parsing JSON from the given source. 238 | template 239 | any_type parse_stream(IStream& istr); 240 | 241 | template 242 | any_type parse(const typename any_type::string_type& str); 243 | 244 | template 245 | any_type parse(const Ch* str, std::size_t length); 246 | 247 | template 248 | any parse(const Ch (&str)[Size]); 249 | 250 | } // namespace json 251 | } // namespace native 252 | 253 | #include "native/json/detail/any_impl.h" 254 | 255 | #endif 256 | -------------------------------------------------------------------------------- /include/native/utf.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013 Mike Naquin. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | #ifndef NATIVE_UTF_H__ 18 | #define NATIVE_UTF_H__ 19 | 20 | #include "native/config.h" 21 | 22 | namespace native 23 | { 24 | 25 | // UTF-8 encoding 26 | // http://en.wikipedia.org/wiki/UTF-8 27 | // http://tools.ietf.org/html/rfc3629 28 | // 29 | // Bits 30 | // First Last Byte 1 Byte 2 Byte 3 Byte 4 Byte 5 Byte 6 31 | // 7 U+0000 U+007F 0xxxxxxx 32 | // 11 U+0080 U+07FF 110xxxxx 10xxxxxx 33 | // 16 U+0800 U+FFFF 1110xxxx 10xxxxxx 10xxxxxx 34 | // 21 U+10000 U+1FFFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 35 | // 26 U+200000 U+3FFFFFF 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 36 | // 31 U+4000000 U+7FFFFFFF 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 37 | struct utf8 38 | { 39 | using char_type = char; 40 | 41 | template 42 | static void encode(OStream& ostr, char32_t codepoint) 43 | { 44 | static_assert(sizeof(typename OStream::char_type) == 1, ""); 45 | 46 | if (codepoint <= 0x7f) 47 | { 48 | ostr.put(codepoint & 0xff); 49 | } 50 | else if (codepoint <= 0x7ff) 51 | { 52 | ostr.put(0xc0 | ((codepoint >> 6) & 0xff)); 53 | ostr.put(0x80 | ((codepoint & 0x3f))); 54 | } 55 | else if (codepoint <= 0xffff) 56 | { 57 | ostr.put(0xe0 | ((codepoint >> 12) & 0xff)); 58 | ostr.put(0x80 | ((codepoint >> 6) & 0x3f)); 59 | ostr.put(0x80 | (codepoint & 0x3f)); 60 | } 61 | else if (codepoint <= 0x10ffff) 62 | { 63 | ostr.put(0xf0 | ((codepoint >> 18) & 0xff)); 64 | ostr.put(0x80 | ((codepoint >> 12) & 0x3f)); 65 | ostr.put(0x80 | ((codepoint >> 6) & 0x3f)); 66 | ostr.put(0x80 | (codepoint & 0x3f)); 67 | } 68 | else 69 | { 70 | throw std::runtime_error("invalid utf-8 codepoint"); 71 | } 72 | } 73 | 74 | template 75 | static char32_t decode(IStream& istr) 76 | { 77 | static_assert(sizeof(typename IStream::char_type) == 1, ""); 78 | 79 | static const std::uint32_t byte_1_map[] = { 80 | // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f 81 | /* 0 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 82 | /* 1 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 83 | /* 2 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 84 | /* 3 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 85 | /* 4 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 86 | /* 5 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 87 | /* 6 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 88 | /* 7 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 89 | /* 8 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 90 | /* 9 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 91 | /* a */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 92 | /* b */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 93 | /* c */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 94 | /* d */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 95 | /* e */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 96 | /* f */ 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 97 | }; 98 | 99 | static const std::uint8_t byte_1_mask[] = { 100 | 0, // invalid 101 | 0xff, // 1 102 | 0x3f, // 2 103 | 0x1f, // 3 104 | 0x0f, // 4 105 | 0x07, // 5 106 | 0x03, // 6 107 | }; 108 | 109 | const auto first_byte = static_cast(istr.get()); 110 | const auto num_bytes = byte_1_map[first_byte]; 111 | 112 | const auto read_byte = [](IStream& istr) 113 | { 114 | const auto ch = static_cast(istr.get()); 115 | if ((ch & 0xc0) != 0x80) 116 | { 117 | throw std::runtime_error("invalid utf-8 sequence"); 118 | } 119 | return ch; 120 | }; 121 | 122 | char32_t codepoint = first_byte & byte_1_mask[num_bytes]; 123 | switch (num_bytes) 124 | { 125 | case 6: 126 | codepoint = (codepoint << 6) | (read_byte(istr) & 0x3fu); 127 | case 5: 128 | codepoint = (codepoint << 6) | (read_byte(istr) & 0x3fu); 129 | case 4: 130 | codepoint = (codepoint << 6) | (read_byte(istr) & 0x3fu); 131 | case 3: 132 | codepoint = (codepoint << 6) | (read_byte(istr) & 0x3fu); 133 | case 2: 134 | codepoint = (codepoint << 6) | (read_byte(istr) & 0x3fu); 135 | case 1: 136 | return codepoint; 137 | default: 138 | throw std::runtime_error("invalid utf-8 sequence"); 139 | } 140 | } 141 | }; 142 | 143 | // UTF-16 encoding 144 | // http://en.wikipedia.org/wiki/UTF-16 145 | // http://tools.ietf.org/html/rfc2781 146 | struct utf16 147 | { 148 | using char_type = char16_t; 149 | 150 | static_assert(sizeof(char_type) >= 2, 151 | "UTF-16 needs at least 2 byte character"); 152 | 153 | template 154 | static void encode(OStream& ostr, char32_t codepoint) 155 | { 156 | if (codepoint < 0x10000) 157 | { 158 | if (codepoint >= 0xd800 && codepoint <= 0xdfff) 159 | { 160 | throw std::runtime_error("invalid utf-16 codepoint"); 161 | } 162 | ostr.put(static_cast(codepoint)); 163 | } 164 | else if (codepoint <= 0x10ffff) 165 | { 166 | ostr.put(static_cast((codepoint >> 10) + 0xd7c0)); 167 | ostr.put((codepoint & 0x3ff) + 0xdc00); 168 | } 169 | else 170 | { 171 | throw std::runtime_error("invalid utf-16 codepoint"); 172 | } 173 | } 174 | 175 | template 176 | static char32_t decode(IStream& istr) 177 | { 178 | static_assert(sizeof(typename IStream::char_type) == 2, ""); 179 | const char_type first_code_unit = istr.get(); 180 | if (first_code_unit < 0xd800 || first_code_unit > 0xdfff) 181 | { 182 | return first_code_unit; 183 | } 184 | else if (first_code_unit <= 0xdbff) 185 | { 186 | const char_type second_code_unit = istr.get(); 187 | if (second_code_unit < 0xdc00 || second_code_unit > 0xdfff) 188 | { 189 | throw std::runtime_error("invalid utf-16 sequence"); 190 | } 191 | return (first_code_unit << 10) + second_code_unit - 0x35fdc00; 192 | } 193 | else 194 | { 195 | throw std::runtime_error("invalid utf-16 sequence"); 196 | } 197 | } 198 | }; 199 | 200 | // UTF-32 encoding. 201 | struct utf32 202 | { 203 | using char_type = char32_t; 204 | 205 | template 206 | static void encode(OStream& ostr, char32_t codepoint) 207 | { 208 | if (codepoint > 0x10ffff) 209 | { 210 | throw std::runtime_error("invalid utf-32 sequence"); 211 | } 212 | ostr.put(codepoint); 213 | } 214 | 215 | template 216 | static char32_t decode(IStream& istr) 217 | { 218 | static_assert(sizeof(typename IStream::char_type) == 4, ""); 219 | char_type ch = istr.get(); 220 | if (ch > 0x10ffff) 221 | { 222 | throw std::runtime_error("invalid utf-32 sequence"); 223 | } 224 | 225 | return ch; 226 | } 227 | }; 228 | 229 | // deduce encoding type... 230 | template 231 | struct encoding; 232 | 233 | template <> 234 | struct encoding 235 | { 236 | using type = utf8; 237 | }; 238 | 239 | template <> 240 | struct encoding 241 | { 242 | using type = utf16; 243 | }; 244 | 245 | template <> 246 | struct encoding 247 | { 248 | using type = utf32; 249 | }; 250 | 251 | } // namespace native 252 | 253 | #endif 254 | -------------------------------------------------------------------------------- /include/native/detail/number_parse.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013 Mike Naquin. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | #ifndef native_number_parse_h__ 18 | #define native_number_parse_h__ 19 | 20 | #include "native/config.h" 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | namespace native 29 | { 30 | namespace detail 31 | { 32 | 33 | template 34 | struct number_parse 35 | { 36 | using value_type = T; 37 | 38 | using numeric_limitis = std::numeric_limits; 39 | 40 | constexpr static std::uint16_t max_significant_digits = 41 | 2 + numeric_limitis::digits - numeric_limitis::min_exponent; 42 | 43 | constexpr static std::uint16_t max_buffer_length = 44 | max_significant_digits + 10; 45 | 46 | constexpr static std::uint16_t buffer_size = max_significant_digits + 10; 47 | char buffer[buffer_size]; // NOLINT: size is known at compile time. 48 | std::uint32_t length = 0; 49 | std::int32_t exponent = 0; 50 | std::int32_t significant_digits = 0; 51 | std::int32_t insignificant_digits = 0; 52 | bool nonzero_digit_dropped = false; 53 | bool sign = false; 54 | 55 | bool is_real() const { return !(exponent == 0); } 56 | 57 | // Adapted from V8 double-conversion 58 | template 59 | void to_buffer(Stream& stream) 60 | { 61 | // The longest form of simplified number is: "-.1eXXX\0". 63 | 64 | // Exponent will be adjusted if insignificant digits of the integer part 65 | // or insignificant leading zeros of the fractional part are dropped. 66 | 67 | if (stream.peek() == '-') 68 | { 69 | sign = true; 70 | stream.next(); 71 | if (stream.peek() < '0' || stream.peek() > '9') 72 | { 73 | throw std::range_error("Expected a digit after minus sign"); 74 | } 75 | } 76 | 77 | const bool leading_zero = stream.peek() == '0'; 78 | if (leading_zero) 79 | { 80 | stream.next(); 81 | 82 | switch (stream.peek()) 83 | { 84 | case 'e': 85 | case 'E': // exponent 86 | case '.': // this is a decimal 87 | // A single leading zero is only allowed for decimals 88 | break; 89 | case ']': 90 | case ',': 91 | case ' ': 92 | case '\t': 93 | case '\r': 94 | case '\n': 95 | return; // we've reached the end of the number 96 | case '0': 97 | case '1': 98 | case '2': 99 | case '3': 100 | case '4': 101 | case '5': 102 | case '6': 103 | case '7': 104 | case '8': 105 | case '9': 106 | throw std::range_error("Leading zeros are not allowed."); 107 | } 108 | } 109 | 110 | // Copy significant digits of the integer part (if any) to the buffer. 111 | while (stream.peek() >= '0' && stream.peek() <= '9') 112 | { 113 | if (significant_digits < max_significant_digits) 114 | { 115 | assert(length < buffer_size); 116 | buffer[length++] = static_cast(stream.peek()); 117 | ++significant_digits; 118 | } 119 | else 120 | { 121 | ++insignificant_digits; // Move the digit into the exponential 122 | // part. 123 | nonzero_digit_dropped = 124 | nonzero_digit_dropped || stream.peek() != '0'; 125 | } 126 | stream.next(); 127 | } 128 | 129 | if (!leading_zero && significant_digits == 0) 130 | { 131 | throw std::range_error("Expected a digit"); 132 | } 133 | 134 | if (stream.peek() == '.') 135 | { 136 | stream.next(); 137 | 138 | switch (stream.peek()) // check for digit after dot 139 | { 140 | case '0': 141 | case '1': 142 | case '2': 143 | case '3': 144 | case '4': 145 | case '5': 146 | case '6': 147 | case '7': 148 | case '8': 149 | case '9': 150 | break; 151 | default: 152 | throw std::range_error("Expected digit after decimal."); 153 | } 154 | 155 | if (significant_digits == 0) 156 | { 157 | // Integer part consists of 0 or is absent. Significant digits 158 | // start after 159 | // leading zeros (if any). 160 | while (stream.peek() == '0') 161 | { 162 | stream.next(); 163 | --exponent; // Move this 0 into the exponent. 164 | } 165 | } 166 | 167 | // There is a fractional part. 168 | // We don't emit a '.', but adjust the exponent instead. 169 | while (stream.peek() >= '0' && stream.peek() <= '9') 170 | { 171 | if (significant_digits < max_significant_digits) 172 | { 173 | assert(length < buffer_size); 174 | buffer[length++] = static_cast(stream.peek()); 175 | ++significant_digits; 176 | --exponent; 177 | } 178 | else 179 | { 180 | // Ignore insignificant digits in the fractional part. 181 | nonzero_digit_dropped = 182 | nonzero_digit_dropped || stream.peek() != '0'; 183 | } 184 | stream.next(); 185 | } 186 | } 187 | 188 | if (!leading_zero && exponent == 0 && significant_digits == 0) 189 | { 190 | // If leading_zeros is true then the string contains zeros. 191 | // If exponent < 0 then string was [+-]\.0*... 192 | // If significant_digits != 0 the string is not equal to 0. 193 | // Otherwise there are no digits in the string. 194 | return; 195 | } 196 | 197 | // Parse exponential part. 198 | if (stream.peek() == 'e' || stream.peek() == 'E') 199 | { 200 | stream.next(); 201 | char sign = '+'; 202 | if (stream.peek() == '+' || stream.peek() == '-') 203 | { 204 | sign = static_cast(stream.peek()); 205 | stream.next(); 206 | } 207 | 208 | if (stream.peek() < '0' || stream.peek() > '9') 209 | { 210 | throw std::range_error("Expected digit after exponent start"); 211 | } 212 | 213 | const int max_exponent = std::numeric_limits::max_exponent; 214 | assert(-max_exponent / 2 <= exponent && 215 | exponent <= max_exponent / 2); 216 | int num = 0; 217 | do 218 | { 219 | // Check overflow. 220 | int digit = stream.peek() - '0'; 221 | if (num >= max_exponent / 10 && 222 | !(num == max_exponent / 10 && digit <= max_exponent % 10)) 223 | { 224 | num = max_exponent; 225 | } 226 | else 227 | { 228 | num = num * 10 + digit; 229 | } 230 | stream.next(); 231 | } while (stream.peek() >= '0' && stream.peek() <= '9'); 232 | 233 | exponent += (sign == '-' ? -num : num); 234 | } 235 | 236 | exponent += insignificant_digits; 237 | 238 | if (nonzero_digit_dropped) 239 | { 240 | buffer[length++] = '1'; 241 | exponent--; 242 | } 243 | 244 | assert(length < buffer_size); 245 | buffer[length] = '\0'; 246 | } 247 | }; 248 | } 249 | } // namespace native::json::detail 250 | 251 | #endif 252 | -------------------------------------------------------------------------------- /include/native/json/detail/number_parse.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013 Mike Naquin. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | #ifndef native_json_number_parse_h__ 18 | #define native_json_number_parse_h__ 19 | 20 | #include "native/config.h" 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | namespace native 29 | { 30 | namespace json 31 | { 32 | namespace detail 33 | { 34 | 35 | template 36 | struct number_parse 37 | { 38 | using value_type = T; 39 | 40 | using numeric_limitis = std::numeric_limits; 41 | 42 | constexpr static std::uint16_t max_significant_digits = 43 | 2 + numeric_limitis::digits - numeric_limitis::min_exponent; 44 | 45 | constexpr static std::uint16_t max_buffer_length = 46 | max_significant_digits + 10; 47 | 48 | constexpr static std::uint16_t buffer_size = max_significant_digits + 10; 49 | char buffer[buffer_size]; // NOLINT: size is known at compile time. 50 | std::uint32_t length = 0; 51 | std::int32_t exponent = 0; 52 | std::int32_t significant_digits = 0; 53 | std::int32_t insignificant_digits = 0; 54 | bool nonzero_digit_dropped = false; 55 | bool sign = false; 56 | 57 | bool is_real() const { return !(exponent == 0); } 58 | 59 | // Adapted from V8 double-conversion 60 | template 61 | void to_buffer(Stream& stream) 62 | { 63 | // The longest form of simplified number is: "-.1eXXX\0". 65 | 66 | // Exponent will be adjusted if insignificant digits of the integer part 67 | // or insignificant leading zeros of the fractional part are dropped. 68 | 69 | if (stream.peek() == '-') 70 | { 71 | sign = true; 72 | stream.next(); 73 | if (stream.peek() < '0' || stream.peek() > '9') 74 | { 75 | throw std::range_error("Expected a digit after minus sign"); 76 | } 77 | } 78 | 79 | const bool leading_zero = stream.peek() == '0'; 80 | if (leading_zero) 81 | { 82 | stream.next(); 83 | 84 | switch (stream.peek()) 85 | { 86 | case 'e': 87 | case 'E': // exponent 88 | case '.': // this is a decimal 89 | // A single leading zero is only allowed for decimals 90 | break; 91 | case ']': 92 | case ',': 93 | case ' ': 94 | case '\t': 95 | case '\r': 96 | case '\n': 97 | return; // we've reached the end of the number 98 | case '0': 99 | case '1': 100 | case '2': 101 | case '3': 102 | case '4': 103 | case '5': 104 | case '6': 105 | case '7': 106 | case '8': 107 | case '9': 108 | throw std::range_error("Leading zeros are not allowed."); 109 | } 110 | } 111 | 112 | // Copy significant digits of the integer part (if any) to the buffer. 113 | while (stream.peek() >= '0' && stream.peek() <= '9') 114 | { 115 | if (significant_digits < max_significant_digits) 116 | { 117 | assert(length < buffer_size); 118 | buffer[length++] = static_cast(stream.peek()); 119 | ++significant_digits; 120 | } 121 | else 122 | { 123 | ++insignificant_digits; // Move the digit into the exponential 124 | // part. 125 | nonzero_digit_dropped = 126 | nonzero_digit_dropped || stream.peek() != '0'; 127 | } 128 | stream.next(); 129 | } 130 | 131 | if (!leading_zero && significant_digits == 0) 132 | { 133 | throw std::range_error("Expected a digit"); 134 | } 135 | 136 | if (stream.peek() == '.') 137 | { 138 | stream.next(); 139 | 140 | switch (stream.peek()) // check for digit after dot 141 | { 142 | case '0': 143 | case '1': 144 | case '2': 145 | case '3': 146 | case '4': 147 | case '5': 148 | case '6': 149 | case '7': 150 | case '8': 151 | case '9': 152 | break; 153 | default: 154 | throw std::range_error("Expected digit after decimal."); 155 | } 156 | 157 | if (significant_digits == 0) 158 | { 159 | // Integer part consists of 0 or is absent. Significant digits 160 | // start after 161 | // leading zeros (if any). 162 | while (stream.peek() == '0') 163 | { 164 | stream.next(); 165 | --exponent; // Move this 0 into the exponent. 166 | } 167 | } 168 | 169 | // There is a fractional part. 170 | // We don't emit a '.', but adjust the exponent instead. 171 | while (stream.peek() >= '0' && stream.peek() <= '9') 172 | { 173 | if (significant_digits < max_significant_digits) 174 | { 175 | assert(length < buffer_size); 176 | buffer[length++] = static_cast(stream.peek()); 177 | ++significant_digits; 178 | --exponent; 179 | } 180 | else 181 | { 182 | // Ignore insignificant digits in the fractional part. 183 | nonzero_digit_dropped = 184 | nonzero_digit_dropped || stream.peek() != '0'; 185 | } 186 | stream.next(); 187 | } 188 | } 189 | 190 | if (!leading_zero && exponent == 0 && significant_digits == 0) 191 | { 192 | // If leading_zeros is true then the string contains zeros. 193 | // If exponent < 0 then string was [+-]\.0*... 194 | // If significant_digits != 0 the string is not equal to 0. 195 | // Otherwise there are no digits in the string. 196 | return; 197 | } 198 | 199 | // Parse exponential part. 200 | if (stream.peek() == 'e' || stream.peek() == 'E') 201 | { 202 | stream.next(); 203 | char sign = '+'; 204 | if (stream.peek() == '+' || stream.peek() == '-') 205 | { 206 | sign = static_cast(stream.peek()); 207 | stream.next(); 208 | } 209 | 210 | if (stream.peek() < '0' || stream.peek() > '9') 211 | { 212 | throw std::range_error("Expected digit after exponent start"); 213 | } 214 | 215 | const int max_exponent = std::numeric_limits::max_exponent; 216 | assert(-max_exponent / 2 <= exponent && 217 | exponent <= max_exponent / 2); 218 | int num = 0; 219 | do 220 | { 221 | // Check overflow. 222 | int digit = stream.peek() - '0'; 223 | if (num >= max_exponent / 10 && 224 | !(num == max_exponent / 10 && digit <= max_exponent % 10)) 225 | { 226 | num = max_exponent; 227 | } 228 | else 229 | { 230 | num = num * 10 + digit; 231 | } 232 | stream.next(); 233 | } while (stream.peek() >= '0' && stream.peek() <= '9'); 234 | 235 | exponent += (sign == '-' ? -num : num); 236 | } 237 | 238 | exponent += insignificant_digits; 239 | 240 | if (nonzero_digit_dropped) 241 | { 242 | buffer[length++] = '1'; 243 | exponent--; 244 | } 245 | 246 | assert(length < buffer_size); 247 | buffer[length] = '\0'; 248 | } 249 | }; 250 | 251 | } // namespace detail 252 | } // namespace json 253 | } // namespace native 254 | 255 | #endif 256 | -------------------------------------------------------------------------------- /include/native/json/writer.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2015 Mike Naquin. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | #ifndef NATIVE_JSON_WRITER_H__ 18 | #define NATIVE_JSON_WRITER_H__ 19 | 20 | #include "native/config.h" 21 | 22 | #include "native/istring.h" 23 | #include "native/utf.h" 24 | 25 | #include "native/detail/range_istream.h" 26 | 27 | #include "native/json/conversion.h" 28 | 29 | #include 30 | 31 | namespace native 32 | { 33 | namespace json 34 | { 35 | 36 | template 37 | class writer 38 | { 39 | public: 40 | writer(Stream& ostr, std::size_t indent = 0); 41 | 42 | void open_array(); 43 | void close_array(); 44 | 45 | template 46 | void append(T&& value); 47 | 48 | void open_object(); 49 | void close_object(); 50 | 51 | template 52 | void key(T&& key); 53 | 54 | template 55 | void append(T&& key, U&& value); 56 | 57 | private: 58 | struct state 59 | { 60 | enum t : unsigned char 61 | { 62 | array, 63 | object, 64 | } type; 65 | bool has_elements = false; 66 | 67 | state(t type) 68 | : type{type} 69 | { 70 | } 71 | }; 72 | 73 | void _comma(); 74 | void _indent(); 75 | void _close(); 76 | 77 | void _encode(const unsigned char* first, const unsigned char* last); 78 | void _write(std::nullptr_t); 79 | void _write(bool value); 80 | void _write(const char* value); 81 | template 82 | typename std::enable_if::value || 83 | std::is_floating_point::value, 84 | void>::type 85 | _write(T value); 86 | template 87 | typename std::enable_if::value, void>::type 88 | _write(const T& value); 89 | 90 | Stream& _ostr; 91 | istring _indent_string; 92 | std::vector _state; 93 | }; 94 | 95 | template 96 | writer::writer(Stream& ostr, std::size_t indent) 97 | : _ostr{ostr} 98 | , _indent_string(indent, ' ') 99 | { 100 | } 101 | 102 | template 103 | void writer::open_array() 104 | { 105 | _ostr.put('['); 106 | _state.push_back(state::array); 107 | } 108 | 109 | template 110 | void writer::close_array() 111 | { 112 | _close(); 113 | } 114 | 115 | template 116 | void writer::open_object() 117 | { 118 | if (!_state.empty()) 119 | { 120 | if (_state.back().type == state::array && _state.back().has_elements) 121 | { 122 | _comma(); 123 | } 124 | _indent(); 125 | } 126 | _ostr.put('{'); 127 | _state.push_back(state::object); 128 | } 129 | 130 | template 131 | void writer::close_object() 132 | { 133 | _close(); 134 | } 135 | 136 | template 137 | template 138 | void writer::key(T&& key) 139 | { 140 | if (!_state.empty() && _state.back().has_elements) 141 | { 142 | _comma(); 143 | } 144 | _indent(); 145 | _write(key); 146 | _ostr.put(':'); 147 | if (!_indent_string.empty()) 148 | { 149 | _ostr.put(' '); 150 | } 151 | } 152 | 153 | template 154 | template 155 | void writer::append(T&& label, U&& value) 156 | { 157 | key(label); 158 | append(value); 159 | } 160 | 161 | template 162 | template 163 | void writer::append(T&& value) 164 | { 165 | if (!_state.empty()) 166 | { 167 | if (_state.back().type == state::array) 168 | { 169 | if (_state.back().has_elements) 170 | { 171 | _comma(); 172 | } 173 | _indent(); 174 | } 175 | 176 | _state.back().has_elements = true; 177 | } 178 | _write(value); 179 | } 180 | 181 | template 182 | template 183 | typename std::enable_if< 184 | std::is_integral::value || std::is_floating_point::value, void>::type 185 | writer::_write(T value) 186 | { 187 | _ostr << value; 188 | } 189 | 190 | template 191 | void writer::_write(std::nullptr_t) 192 | { 193 | _ostr.write("null", 4); 194 | } 195 | 196 | template 197 | void writer::_write(bool value) 198 | { 199 | if (value) 200 | { 201 | _ostr.write("true", 4); 202 | } 203 | else 204 | { 205 | _ostr.write("false", 5); 206 | } 207 | } 208 | 209 | template 210 | void writer::_encode(const unsigned char* first, 211 | const unsigned char* last) 212 | { 213 | auto write_utf = [=](uint32_t codepoint) 214 | { 215 | auto hex_digit = [](uint32_t ch) -> char 216 | { 217 | return ch < 10 ? ch + '0' : ch - 10 + 'a'; 218 | }; 219 | 220 | _ostr.put('\\'); 221 | _ostr.put('u'); 222 | _ostr.put(hex_digit(codepoint >> 0xC)); 223 | _ostr.put(hex_digit((codepoint >> 0x8) & 0x0f)); 224 | _ostr.put(hex_digit((codepoint >> 0x4) & 0x0f)); 225 | _ostr.put(hex_digit(codepoint & 0x0f)); 226 | }; 227 | 228 | ::native::detail::range_istream istr{first, last}; 229 | _ostr.put('"'); 230 | for (; istr;) 231 | { 232 | if (istr.peek() & 0x80) 233 | { 234 | uint32_t codepoint = utf8::decode(istr); 235 | if (codepoint & 0xFFFF0000) 236 | { 237 | write_utf((codepoint >> 10) + 0xD7C0); 238 | write_utf((codepoint & 0x3FF) + 0xDC00); 239 | } 240 | else 241 | { 242 | write_utf(codepoint); 243 | } 244 | } 245 | else 246 | { 247 | switch (istr.peek()) 248 | { 249 | case '\b': 250 | _ostr.write("\\b", 2); 251 | break; 252 | case '\f': 253 | _ostr.write("\\f", 2); 254 | break; 255 | case '\n': 256 | _ostr.write("\\n", 2); 257 | break; 258 | case '\r': 259 | _ostr.write("\\r", 2); 260 | break; 261 | case '\t': 262 | _ostr.write("\\t", 2); 263 | break; 264 | // case '/': // unnecessary & ugly :/ 265 | case '\\': 266 | case '"': 267 | _ostr.put('\\'); 268 | default: 269 | _ostr.put(istr.peek()); 270 | break; 271 | } 272 | istr.next(); 273 | } 274 | } 275 | _ostr.put('"'); 276 | } 277 | 278 | template 279 | void writer::_write(const char* value) 280 | { 281 | _encode(reinterpret_cast(value), 282 | reinterpret_cast(value) + std::strlen(value)); 283 | } 284 | 285 | template 286 | template 287 | typename std::enable_if::value, void>::type 288 | writer::_write(const T& value) 289 | { 290 | _encode(reinterpret_cast(value.data()), 291 | reinterpret_cast(value.data()) + 292 | value.size()); 293 | } 294 | 295 | template 296 | void writer::_comma() 297 | { 298 | _ostr.put(','); 299 | } 300 | 301 | template 302 | void writer::_indent() 303 | { 304 | if (_indent_string.empty()) 305 | { 306 | return; 307 | } 308 | _ostr.put('\n'); 309 | for (size_t i = 0; i < _state.size(); ++i) 310 | { 311 | _ostr.write(_indent_string.data(), _indent_string.size()); 312 | } 313 | } 314 | 315 | template 316 | void writer::_close() 317 | { 318 | if (_state.empty()) 319 | { 320 | return; 321 | } 322 | 323 | const auto current_state = _state.back(); 324 | _state.pop_back(); 325 | if (current_state.has_elements) 326 | { 327 | _indent(); 328 | } 329 | switch (current_state.type) 330 | { 331 | case state::array: 332 | _ostr.put(']'); 333 | break; 334 | case state::object: 335 | _ostr.put('}'); 336 | break; 337 | } 338 | if (!_state.empty() && _state.back().type == state::object) 339 | { 340 | _state.back().has_elements = true; 341 | } 342 | } 343 | 344 | } // namespace json 345 | } // namespace native 346 | 347 | #endif 348 | -------------------------------------------------------------------------------- /include/native/hash.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013 Mike Naquin. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | #ifndef NATIVE_HASH_H__ 18 | #define NATIVE_HASH_H__ 19 | 20 | #include "native/string_base.h" 21 | 22 | namespace native 23 | { 24 | 25 | // 26 | // http://www.isthe.com/chongo/tech/comp/fnv/ 27 | // 28 | 29 | template 30 | struct fnv_traits; 31 | 32 | template 33 | struct fnv_traits 34 | { 35 | using size_type = T; 36 | 37 | static constexpr size_type prime = 16777619UL; 38 | static constexpr size_type basis = 2166136261UL; 39 | 40 | static inline size_type multiply_by_prime(size_type value) 41 | { 42 | return (value << 1) + (value << 4) + (value << 7) + (value << 8) + 43 | (value << 24); 44 | } 45 | }; 46 | 47 | template 48 | struct fnv_traits 49 | { 50 | using size_type = T; 51 | 52 | static constexpr size_type prime = 1099511628211ULL; 53 | static constexpr size_type basis = 14695981039346656037ULL; 54 | 55 | static inline size_type multiply_by_prime(size_type value) 56 | { 57 | return (value << 1) + (value << 4) + (value << 5) + (value << 7) + 58 | (value << 8) + (value << 40); 59 | } 60 | }; 61 | 62 | // 63 | // hashes are from http://www.isthe.com/chongo/tech/comp/fnv/ 64 | // 65 | template 66 | struct basic_fnv1_hasher 67 | { 68 | using this_type = basic_fnv1_hasher; 69 | using traits_type = fnv_traits; 70 | using size_type = typename traits_type::size_type; 71 | 72 | static constexpr size_type prime = traits_type::prime; 73 | static constexpr size_type basis = traits_type::basis; 74 | 75 | static constexpr size_type static_hash(unsigned char ch, const char* s, 76 | size_t size, size_type value) 77 | { 78 | return size == 0 ? value : static_hash(s[0], s + 1, size - 1, 79 | (value * prime) ^ ch); 80 | } 81 | 82 | static constexpr size_type static_hash(const char* s, size_t size) 83 | { 84 | return static_hash(s[0], s + 1, size, basis); 85 | } 86 | 87 | size_type operator()(const char* s) 88 | { 89 | size_type value = basis; 90 | for (; *s; ++s) 91 | { 92 | // multiply by the FNV magic prime 93 | value += traits_type::multiply_by_prime(value); 94 | 95 | // xor the bottom with the current octet 96 | value ^= static_cast(*s); 97 | } 98 | 99 | return value; 100 | } 101 | 102 | size_type operator()(const void* buf, size_t size) 103 | { 104 | size_type value = basis; 105 | const auto* char_buf = reinterpret_cast(buf); 106 | for (size_t i = 0; i < size; ++i) 107 | { 108 | // multiply by the FNV magic prime 109 | value += traits_type::multiply_by_prime(value); 110 | 111 | // xor the bottom with the current octet 112 | value ^= char_buf[i]; 113 | } 114 | 115 | return value; 116 | } 117 | 118 | template 119 | typename std::enable_if::value, size_type>::type 120 | operator()(const String& s) 121 | { 122 | return *this(s.data(), s.size()); 123 | } 124 | }; 125 | 126 | template 127 | struct basic_fnv1a_hasher 128 | { 129 | using this_type = basic_fnv1a_hasher; 130 | using traits_type = fnv_traits; 131 | using size_type = typename traits_type::size_type; 132 | 133 | static constexpr size_type prime = traits_type::prime; 134 | static constexpr size_type basis = traits_type::basis; 135 | 136 | static constexpr size_type static_hash(unsigned char ch, const char* s, 137 | size_t size, size_type value) 138 | { 139 | return size == 0 ? value : static_hash(s[0], s + 1, size - 1, 140 | (value ^ ch) * prime); 141 | } 142 | 143 | static constexpr size_type static_hash(const char* s, size_t size) 144 | { 145 | return static_hash(s[0], s + 1, size, basis); 146 | } 147 | 148 | size_type operator()(const char* s) 149 | { 150 | size_type value = basis; 151 | for (; *s; ++s) 152 | { 153 | // xor the bottom with the current octet 154 | value ^= static_cast(*s); 155 | 156 | // multiply by the FNV magic prime 157 | value += traits_type::multiply_by_prime(value); 158 | } 159 | 160 | return value; 161 | } 162 | 163 | size_type operator()(const void* buf, size_t size) 164 | { 165 | size_type value = basis; 166 | const auto* char_buf = reinterpret_cast(buf); 167 | 168 | for (size_t i = 0; i < size; ++i) 169 | { 170 | // xor the bottom with the current octet 171 | value ^= char_buf[i]; 172 | 173 | // multiply by the FNV magic prime 174 | value += traits_type::multiply_by_prime(value); 175 | } 176 | 177 | return value; 178 | } 179 | 180 | template 181 | typename std::enable_if::value, size_type>::type 182 | operator()(const String& s) 183 | { 184 | return *this(s.data(), s.size()); 185 | } 186 | }; 187 | 188 | using fnv1_hasher = basic_fnv1_hasher; 189 | using fnv1_32_hasher = basic_fnv1_hasher; 190 | using fnv1_64_hasher = basic_fnv1_hasher; 191 | 192 | using fnv1a_hasher = basic_fnv1a_hasher; 193 | using fnv1a_32_hasher = basic_fnv1a_hasher; 194 | using fnv1a_64_hasher = basic_fnv1a_hasher; 195 | 196 | using default_hasher = fnv1a_hasher; 197 | 198 | // 199 | // user-defined literals 200 | // 201 | constexpr size_t operator"" _fnv1(const char* s, size_t size) 202 | { 203 | return fnv1_hasher::static_hash(s, size); 204 | } 205 | 206 | constexpr size_t operator"" _fnv1a(const char* s, size_t size) 207 | { 208 | return fnv1a_hasher::static_hash(s, size); 209 | } 210 | 211 | // fnv1 (32-bit) 212 | constexpr size_t operator"" _fnv32(const char* s, size_t size) 213 | { 214 | return fnv1_32_hasher::static_hash(s, size); 215 | } 216 | 217 | constexpr size_t operator"" _fnv64(const char* s, size_t size) 218 | { 219 | return fnv1_64_hasher::static_hash(s, size); 220 | } 221 | 222 | // fnv1a (32-bit) 223 | constexpr size_t operator"" _fnv32a(const char* s, size_t size) 224 | { 225 | return fnv1a_32_hasher::static_hash(s, size); 226 | } 227 | 228 | constexpr size_t operator"" _fnv64a(const char* s, size_t size) 229 | { 230 | return fnv1a_64_hasher::static_hash(s, size); 231 | } 232 | 233 | // 234 | // generic static hash 235 | // 236 | constexpr size_t operator"" _hash(const char* s, size_t size) 237 | { 238 | return default_hasher::static_hash(s, size); 239 | } 240 | 241 | // 242 | // convenience functions 243 | // 244 | 245 | // generic hash 246 | inline size_t hash(const void* buf, size_t size) 247 | { 248 | return default_hasher()(buf, size); 249 | } 250 | 251 | inline size_t hash(const char* s) { return default_hasher()(s); } 252 | 253 | template 254 | inline typename std::enable_if::value, size_t>::type 255 | hash(const String& s) 256 | { 257 | return default_hasher()(s); 258 | } 259 | 260 | // fnv1 (size_t) 261 | inline size_t fnv1(const char* s) { return fnv1_hasher()(s); } 262 | 263 | inline size_t fnv1(const void* buf, size_t size) 264 | { 265 | return fnv1_hasher()(buf, size); 266 | } 267 | 268 | template 269 | inline typename std::enable_if::value, size_t>::type 270 | fnv1(const String& s) 271 | { 272 | return fnv1_hasher()(s); 273 | } 274 | 275 | // fnv1a (size_t) 276 | inline size_t fnv1a(const char* s) { return fnv1_hasher()(s); } 277 | 278 | inline size_t fnv1a(const void* buf, size_t size) 279 | { 280 | return fnv1_hasher()(buf, size); 281 | } 282 | 283 | template 284 | inline typename std::enable_if::value, size_t>::type 285 | fnv1a(const String& s) 286 | { 287 | return fnv1a_hasher()(s); 288 | } 289 | 290 | // fnv1 (32 bit) 291 | inline std::uint32_t fnv32(const char* s) { return fnv1_32_hasher()(s); } 292 | 293 | inline std::uint32_t fnv32(const void* buf, size_t size) 294 | { 295 | return fnv1_32_hasher()(buf, size); 296 | } 297 | 298 | template 299 | inline typename std::enable_if::value, std::uint32_t>::type 300 | fnv32(const String& s) 301 | { 302 | return fnv1_32_hasher()(s); 303 | } 304 | 305 | // fnv1 (64 bit) 306 | inline std::uint64_t fnv64(const char* s) { return fnv1_64_hasher()(s); } 307 | 308 | inline std::uint64_t fnv64(const void* buf, size_t size) 309 | { 310 | return fnv1_64_hasher()(buf, size); 311 | } 312 | 313 | template 314 | inline typename std::enable_if::value, std::uint64_t>::type 315 | fnv64(const String& s) 316 | { 317 | return fnv1_64_hasher()(s); 318 | } 319 | 320 | // fnv1a (32 bit) 321 | inline std::uint32_t fnv32a(const char* s) { return fnv1a_32_hasher()(s); } 322 | 323 | inline std::uint32_t fnv32a(const void* buf, size_t size) 324 | { 325 | return fnv1a_32_hasher()(buf, size); 326 | } 327 | 328 | template 329 | inline typename std::enable_if::value, std::uint32_t>::type 330 | fnv32a(const String& s) 331 | { 332 | return fnv1a_32_hasher()(s); 333 | } 334 | 335 | // fnv1a (64 bit) 336 | inline std::uint64_t fnv64a(const char* s) { return fnv1a_64_hasher()(s); } 337 | 338 | inline std::uint64_t fnv64a(const void* buf, size_t size) 339 | { 340 | return fnv1a_64_hasher()(buf, size); 341 | } 342 | 343 | template 344 | inline typename std::enable_if::value, std::uint64_t>::type 345 | fnv64a(const String& s) 346 | { 347 | return fnv1a_64_hasher()(s); 348 | } 349 | 350 | } // namespace native 351 | 352 | #endif 353 | -------------------------------------------------------------------------------- /test/json_parser_test.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013 Mike Naquin. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | #ifndef native_json_parser_test_h__ 18 | #define native_json_parser_test_h__ 19 | 20 | #include "test.h" 21 | 22 | #include "native/json/parser.h" 23 | 24 | #include 25 | 26 | template 27 | struct numeric_handler : native::json::handler<> 28 | { 29 | using value_type = T; 30 | 31 | numeric_handler() 32 | : actual() 33 | { 34 | } 35 | void value(T val) { actual = val; } 36 | 37 | T actual; 38 | }; 39 | 40 | template 41 | struct string_handler : native::json::handler 42 | { 43 | using char_type = Ch; 44 | 45 | void value(const char_type* val, std::size_t length) 46 | { 47 | actual.assign(val, length); 48 | } 49 | 50 | std::basic_string actual; 51 | }; 52 | 53 | template 54 | T parse_integer(const std::string& number) 55 | { 56 | using Handler = numeric_handler; 57 | Handler handler; 58 | std::string str = number; 59 | auto parser = native::json::detail::make_parser_impl(str.cbegin(), 60 | str.cend(), handler); 61 | parser.template parse_integer(); 62 | return handler.actual; 63 | }; 64 | 65 | bool parse_bool(const std::string& number, bool expected) 66 | { 67 | using Handler = numeric_handler; 68 | Handler handler; 69 | std::string str = number; 70 | auto parser = native::json::detail::make_parser_impl(str.cbegin(), 71 | str.cend(), handler); 72 | if (expected) 73 | { 74 | parser.parse_true(); 75 | } 76 | else 77 | { 78 | parser.parse_false(); 79 | } 80 | return handler.actual; 81 | } 82 | 83 | void parse_null(const std::string& number) 84 | { 85 | using Handler = numeric_handler; 86 | Handler handler; 87 | std::string str = number; 88 | auto parser = native::json::detail::make_parser_impl(str.cbegin(), 89 | str.cend(), handler); 90 | parser.parse_null(); 91 | // return handler.actual; 92 | } 93 | 94 | template 95 | T parse_real(const std::string& number) 96 | { 97 | using Handler = numeric_handler; 98 | Handler handler; 99 | std::string str = number; 100 | auto parser = native::json::detail::make_parser_impl(str.cbegin(), 101 | str.cend(), handler); 102 | parser.template parse_real(); 103 | return handler.actual; 104 | }; 105 | 106 | template 107 | void test_integer_limits() 108 | { 109 | const auto max = std::numeric_limits::max(); 110 | const auto min = std::numeric_limits::min(); 111 | // put dummy bracket at the end to protect against eof 112 | EXPECT_EQ(max, parse_integer(std::to_string(max))); 113 | EXPECT_EQ(min, parse_integer(std::to_string(min))); 114 | } 115 | 116 | template 117 | std::basic_string parse_string(const std::basic_string& str) 118 | { 119 | using Encoding = typename native::encoding::type; 120 | using Handler = string_handler; 121 | Handler handler; 122 | auto parser = native::json::detail::make_parser_impl(str.cbegin(), 123 | str.cend(), handler); 124 | parser.parse_string(); 125 | return handler.actual; 126 | } 127 | 128 | template 129 | std::basic_string parse_string(const Ch (&str)[Size]) 130 | { 131 | return parse_string(std::basic_string(str, Size - 1)); 132 | } 133 | 134 | template 135 | void parse_any(const std::basic_string& str) 136 | { 137 | using Encoding = typename native::encoding::type; 138 | using Handler = native::json::handler; 139 | Handler handler; 140 | auto parser = native::json::detail::make_parser_impl(str.cbegin(), 141 | str.cend(), handler); 142 | parser.parse(); 143 | } 144 | 145 | template 146 | void parse_any(const Ch (&str)[Size]) 147 | { 148 | return parse_any(std::basic_string(str, Size - 1)); 149 | } 150 | 151 | template 152 | struct numeric_array_handler 153 | : native::json::handler 154 | { 155 | using base_type = native::json::handler; 156 | using encoding_type = Encoding; 157 | using char_type = typename encoding_type::char_type; 158 | 159 | numeric_array_handler() 160 | : start_count() 161 | , end_count() 162 | , actual() 163 | { 164 | } 165 | 166 | native::json::data_type start_array() 167 | { 168 | ++start_count; 169 | return native::json::type_mapper::value; 170 | } 171 | 172 | native::json::data_type key(const char_type* key, std::size_t length) 173 | { 174 | return native::json::type_mapper::value; 175 | } 176 | 177 | void end_array() { ++end_count; } 178 | 179 | using base_type::value; 180 | 181 | void value(T val) { actual.push_back(val); } 182 | 183 | std::size_t start_count, end_count; 184 | std::vector actual; 185 | }; 186 | 187 | template 188 | std::vector parse_array(const Ch (&raw_str)[Size]) 189 | { 190 | using Encoding = typename native::encoding::type; 191 | using Handler = numeric_array_handler; 192 | std::string str(raw_str, Size - 1); 193 | Handler handler; 194 | auto parser = native::json::detail::make_parser_impl(str.cbegin(), 195 | str.cend(), handler); 196 | parser.parse(); 197 | return std::move(handler.actual); 198 | } 199 | 200 | template 201 | struct object_handler : native::json::handler 202 | { 203 | using char_type = Ch; 204 | using base_type = native::json::handler; 205 | using string_type = std::basic_string; 206 | 207 | object_handler() 208 | : start_count() 209 | , end_count() 210 | , step() 211 | { 212 | } 213 | 214 | void start_object() { ++start_count; } 215 | 216 | native::json::data_type start_array() { return native::json::type_int; } 217 | 218 | native::json::data_type key(const char_type* key, std::size_t length) 219 | { 220 | key_actual.push_back(key); 221 | switch (key[0]) 222 | { 223 | case 'a': 224 | case 'i': 225 | return native::json::type_int; 226 | default: 227 | return native::json::type_unknown; 228 | } 229 | } 230 | 231 | void end_object() { ++end_count; } 232 | 233 | using base_type::value; 234 | 235 | void value(std::nullptr_t) 236 | { 237 | ++step; 238 | nulls.emplace_back(step); 239 | } 240 | 241 | void value(bool val) 242 | { 243 | ++step; 244 | bools.emplace_back(step, val); 245 | } 246 | 247 | void value(short val) 248 | { 249 | ++step; 250 | shorts.emplace_back(step, val); 251 | } 252 | 253 | void value(unsigned short val) 254 | { 255 | ++step; 256 | ushorts.emplace_back(step, val); 257 | } 258 | 259 | void value(int val) 260 | { 261 | ++step; 262 | ints.emplace_back(step, val); 263 | } 264 | 265 | void value(long val) 266 | { 267 | ++step; 268 | longs.emplace_back(step, val); 269 | } 270 | 271 | void value(long long val) 272 | { 273 | ++step; 274 | long_longs.emplace_back(step, val); 275 | } 276 | 277 | void value(unsigned val) 278 | { 279 | ++step; 280 | uints.emplace_back(step, val); 281 | } 282 | 283 | void value(unsigned long val) 284 | { 285 | ++step; 286 | ulongs.emplace_back(step, val); 287 | } 288 | 289 | void value(unsigned long long val) 290 | { 291 | ++step; 292 | ulong_longs.emplace_back(step, val); 293 | } 294 | 295 | void value(float val) 296 | { 297 | ++step; 298 | floats.emplace_back(step, val); 299 | } 300 | 301 | void value(double val) 302 | { 303 | ++step; 304 | doubles.emplace_back(step, val); 305 | } 306 | 307 | void value(long double val) 308 | { 309 | ++step; 310 | long_doubles.emplace_back(step, val); 311 | } 312 | 313 | void value(const char_type* val, std::size_t length) 314 | { 315 | ++step; 316 | strings.emplace_back(step, string_type(val, length)); 317 | } 318 | 319 | std::size_t start_count, end_count, step; 320 | std::vector key_actual; 321 | std::vector nulls; 322 | std::vector> bools; 323 | std::vector> shorts; 324 | std::vector> ushorts; 325 | std::vector> ints; 326 | std::vector> uints; 327 | std::vector> longs; 328 | std::vector> ulongs; 329 | std::vector> long_longs; 330 | std::vector> ulong_longs; 331 | std::vector> floats; 332 | std::vector> doubles; 333 | std::vector> long_doubles; 334 | std::vector> strings; 335 | }; 336 | 337 | template 338 | object_handler parse_object(const std::basic_string& str) 339 | { 340 | using Handler = object_handler; 341 | Handler handler; 342 | auto parser = native::json::detail::make_parser_impl(str.cbegin(), 343 | str.cend(), handler); 344 | parser.parse(); 345 | return std::move(handler); 346 | } 347 | 348 | template 349 | object_handler parse_object(const Ch* str) 350 | { 351 | return parse_object(std::basic_string(str)); 352 | } 353 | 354 | #endif 355 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /test/string_test.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013 Mike Naquin. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | #ifndef NATIVE_STRING_TEST_H__ 18 | #define NATIVE_STRING_TEST_H__ 19 | 20 | #include "test.h" 21 | #include "native/istring.h" 22 | #include "native/string_slice.h" 23 | 24 | template 25 | void test_string_compare() 26 | { 27 | String foo("foo"); 28 | String fooCopy = foo; 29 | String fooOther("foo"); 30 | String bar("bar"); 31 | 32 | EXPECT_EQ(0, strcmp("foo", foo.data())); 33 | EXPECT_EQ("foo", foo); 34 | EXPECT_EQ(foo, "foo"); 35 | EXPECT_EQ("foo", foo.std_str()); 36 | EXPECT_EQ(foo.std_str(), "foo"); 37 | EXPECT_EQ(foo, fooCopy); 38 | EXPECT_EQ(fooCopy, foo); 39 | EXPECT_EQ(foo, fooOther); 40 | EXPECT_EQ(fooOther, foo); 41 | EXPECT_EQ("this string needs to be longer than 23 characters", 42 | String("this string needs to be longer than 23 characters")); 43 | 44 | EXPECT_NE(0, strcmp("bar", foo.data())); 45 | EXPECT_NE("bar", foo); 46 | EXPECT_NE(foo, "bar"); 47 | EXPECT_NE("bar", foo.std_str()); 48 | EXPECT_NE(foo.std_str(), "bar"); 49 | EXPECT_NE(bar, foo); 50 | EXPECT_NE(foo, bar); 51 | 52 | EXPECT_LT("bar", foo); 53 | EXPECT_LT("bar", foo.std_str()); 54 | EXPECT_LT(bar, foo); 55 | 56 | EXPECT_GT(foo, "bar"); 57 | EXPECT_GT(foo.std_str(), "bar"); 58 | EXPECT_GT(foo, bar); 59 | 60 | EXPECT_LE("bar", foo); 61 | EXPECT_LE("bar", foo.std_str()); 62 | EXPECT_LE(bar, foo); 63 | EXPECT_LE("foo", foo); 64 | EXPECT_LE(foo, "foo"); 65 | EXPECT_LE("foo", foo.std_str()); 66 | EXPECT_LE(foo.std_str(), "foo"); 67 | EXPECT_LE(foo, fooCopy); 68 | EXPECT_LE(fooCopy, foo); 69 | EXPECT_LE(foo, fooOther); 70 | EXPECT_LE(fooOther, foo); 71 | 72 | EXPECT_GE(foo, "bar"); 73 | EXPECT_GE(foo.std_str(), "bar"); 74 | EXPECT_GE(foo, bar); 75 | EXPECT_GE("foo", foo); 76 | EXPECT_GE(foo, "foo"); 77 | EXPECT_GE("foo", foo.std_str()); 78 | EXPECT_GE(foo.std_str(), "foo"); 79 | EXPECT_GE(foo, fooCopy); 80 | EXPECT_GE(fooCopy, foo); 81 | EXPECT_GE(foo, fooOther); 82 | EXPECT_GE(fooOther, foo); 83 | } 84 | 85 | template 86 | void test_string_constructors() 87 | { 88 | std::vector vector_of_chars = {'f', 'o', 'o', 'b', 'a', 'r'}; 89 | 90 | String ctor1; 91 | String ctor2(5, 'a'); 92 | String ctor3a(ctor2, 1); 93 | String ctor3b(ctor2.std_str(), 1); 94 | // String ctor3c(string(ctor2), 1); 95 | String ctor4a("foobar", 3); 96 | String ctor4b("foobar", 3); 97 | String ctor5("foobar"); 98 | String ctor6(vector_of_chars.begin(), vector_of_chars.end()); 99 | String ctor7a(ctor5); 100 | String ctor7b(ctor5.std_str()); 101 | // String ctor7c((string(ctor5))); 102 | String ctor8Original("foobar"); 103 | String ctor8(std::move(ctor8Original)); 104 | String ctor9({'f', 'o', 'o', 'b', 'a', 'r'}); 105 | 106 | EXPECT_EQ("", ctor1); 107 | EXPECT_EQ("aaaaa", ctor2); 108 | EXPECT_EQ("aaaa", ctor3a); 109 | EXPECT_EQ("aaaa", ctor3b); 110 | // EXPECT_EQ("aaaa", ctor3c); 111 | EXPECT_EQ("foo", ctor4a); 112 | EXPECT_EQ("foo", ctor4b); 113 | EXPECT_EQ("foobar", ctor5); 114 | EXPECT_EQ("foobar", ctor6); 115 | EXPECT_EQ(ctor5, ctor7a); 116 | EXPECT_EQ(ctor5, ctor7b); 117 | // EXPECT_EQ(ctor5, ctor7c); 118 | EXPECT_EQ("foobar", ctor8Original); 119 | EXPECT_EQ("foobar", ctor8); 120 | EXPECT_EQ("foobar", ctor9); 121 | } 122 | 123 | template 124 | void test_string_assignment_operators() 125 | { 126 | String s; 127 | EXPECT_EQ("", s); 128 | 129 | String expected("foobar"); 130 | s = expected; 131 | EXPECT_EQ(expected, s); 132 | 133 | std::string std_expected("tigers"); 134 | s = std_expected; 135 | EXPECT_EQ(std_expected, s); 136 | 137 | s = "literal"; 138 | EXPECT_EQ("literal", s); 139 | } 140 | 141 | template 142 | void test_string_iterators() 143 | { 144 | String s("foobar"); 145 | 146 | { 147 | auto it = s.begin(); 148 | EXPECT_EQ('f', *it); 149 | ++it; 150 | EXPECT_EQ('o', *it); 151 | ++it; 152 | EXPECT_EQ('o', *it); 153 | ++it; 154 | EXPECT_EQ('b', *it); 155 | ++it; 156 | EXPECT_EQ('a', *it); 157 | ++it; 158 | EXPECT_EQ('r', *it); 159 | ++it; 160 | EXPECT_EQ(it, s.end()); 161 | --it; 162 | --it; 163 | EXPECT_EQ('a', *it); 164 | } 165 | 166 | { 167 | auto it = s.cbegin(); 168 | EXPECT_EQ('f', *it); 169 | ++it; 170 | EXPECT_EQ('o', *it); 171 | ++it; 172 | EXPECT_EQ('o', *it); 173 | ++it; 174 | EXPECT_EQ('b', *it); 175 | ++it; 176 | EXPECT_EQ('a', *it); 177 | ++it; 178 | EXPECT_EQ('r', *it); 179 | ++it; 180 | EXPECT_EQ(it, s.end()); 181 | --it; 182 | --it; 183 | EXPECT_EQ('a', *it); 184 | } 185 | 186 | { 187 | auto reverseIt = s.rbegin(); 188 | EXPECT_EQ('r', *reverseIt); 189 | ++reverseIt; 190 | EXPECT_EQ('a', *reverseIt); 191 | ++reverseIt; 192 | EXPECT_EQ('b', *reverseIt); 193 | ++reverseIt; 194 | EXPECT_EQ('o', *reverseIt); 195 | ++reverseIt; 196 | EXPECT_EQ('o', *reverseIt); 197 | ++reverseIt; 198 | EXPECT_EQ('f', *reverseIt); 199 | ++reverseIt; 200 | EXPECT_EQ(reverseIt, s.rend()); 201 | --reverseIt; 202 | --reverseIt; 203 | --reverseIt; 204 | --reverseIt; 205 | EXPECT_EQ('b', *reverseIt); 206 | } 207 | 208 | { 209 | auto reverseIt = s.crbegin(); 210 | EXPECT_EQ('r', *reverseIt); 211 | ++reverseIt; 212 | EXPECT_EQ('a', *reverseIt); 213 | ++reverseIt; 214 | EXPECT_EQ('b', *reverseIt); 215 | ++reverseIt; 216 | EXPECT_EQ('o', *reverseIt); 217 | ++reverseIt; 218 | EXPECT_EQ('o', *reverseIt); 219 | ++reverseIt; 220 | EXPECT_EQ('f', *reverseIt); 221 | ++reverseIt; 222 | EXPECT_EQ(reverseIt, s.rend()); 223 | --reverseIt; 224 | --reverseIt; 225 | --reverseIt; 226 | --reverseIt; 227 | EXPECT_EQ('b', *reverseIt); 228 | } 229 | } 230 | 231 | template 232 | void test_string_attributes() 233 | { 234 | String s("foobar"); 235 | EXPECT_EQ(6, s.size()); 236 | EXPECT_EQ(6, s.length()); 237 | EXPECT_EQ(std::numeric_limits::max(), 238 | s.max_size()); 239 | EXPECT_EQ('b', s[3]); 240 | EXPECT_EQ('b', s.at(3)); 241 | EXPECT_ANY_THROW(s.at(7)); 242 | EXPECT_EQ('f', s.front()); 243 | EXPECT_EQ('r', s.back()); 244 | EXPECT_EQ("oob", s.substr(1, 3)); 245 | EXPECT_EQ("foobar", s.std_str()); 246 | EXPECT_EQ(0, strcmp("foobar", s.data())); 247 | 248 | EXPECT_FALSE(s.empty()); 249 | 250 | s = ""; 251 | 252 | EXPECT_TRUE(s.empty()); 253 | 254 | s = "foobar"; 255 | 256 | char copy[6]; 257 | EXPECT_EQ(6, s.copy(copy)); 258 | EXPECT_EQ(0, strncmp("foobar", copy, 6)); 259 | } 260 | 261 | template 262 | void test_string_find() 263 | { 264 | String s("foobar"); 265 | String search("oob"); 266 | 267 | // 1 268 | EXPECT_EQ(1, s.find(search)); 269 | 270 | // 2 271 | EXPECT_EQ(s.npos, s.find("oo", 2)); 272 | 273 | // 3 274 | EXPECT_EQ(0, s.find("f")); 275 | EXPECT_EQ(1, s.find("o")); 276 | EXPECT_EQ(1, s.find("oo")); 277 | EXPECT_EQ(3, s.find("bar")); 278 | 279 | // 4 280 | EXPECT_EQ(4, s.find('a')); 281 | EXPECT_EQ(s.npos, s.find('a', 6)); 282 | } 283 | 284 | template 285 | void test_string_rfind() 286 | { 287 | String s("foobar"); 288 | String search("oob"); 289 | 290 | // 1 291 | EXPECT_EQ(1, s.rfind(search)); 292 | 293 | // 2 294 | EXPECT_EQ(1, s.rfind("oo", 2)); 295 | 296 | // 3 297 | EXPECT_EQ(0, s.rfind("f")); 298 | EXPECT_EQ(2, s.rfind("o")); 299 | EXPECT_EQ(1, s.rfind("oo")); 300 | EXPECT_EQ(3, s.rfind("bar")); 301 | 302 | // 4 303 | EXPECT_EQ(4, s.rfind('a')); 304 | EXPECT_EQ(4, s.rfind('a', 6)); 305 | } 306 | 307 | template 308 | void test_string_find_first_of() 309 | { 310 | String s("foobar"); 311 | String search("oob"); 312 | 313 | // 1 314 | EXPECT_EQ(1, s.find_first_of(search)); 315 | 316 | // 2 317 | EXPECT_EQ(2, s.find_first_of("oo", 2)); 318 | 319 | // 3 320 | EXPECT_EQ(0, s.find_first_of("f")); 321 | EXPECT_EQ(1, s.find_first_of("o")); 322 | EXPECT_EQ(1, s.find_first_of("oo")); 323 | EXPECT_EQ(3, s.find_first_of("rab")); 324 | 325 | // 4 326 | EXPECT_EQ(4, s.find_first_of('a')); 327 | EXPECT_EQ(s.npos, s.find_first_of('a', 6)); 328 | } 329 | 330 | template 331 | void test_string_find_last_of() 332 | { 333 | String s("foobar"); 334 | String search("oob"); 335 | 336 | // 1 337 | EXPECT_EQ(3, s.find_last_of(search)); 338 | 339 | // 2 340 | EXPECT_EQ(2, s.find_last_of("oo", 2)); 341 | 342 | // 3 343 | EXPECT_EQ(0, s.find_last_of("f")); 344 | EXPECT_EQ(2, s.find_last_of("o")); 345 | EXPECT_EQ(2, s.find_last_of("oo")); 346 | EXPECT_EQ(5, s.find_last_of("rab")); 347 | 348 | // 4 349 | EXPECT_EQ(4, s.find_last_of('a')); 350 | EXPECT_EQ(4, s.find_last_of('a', 6)); 351 | } 352 | 353 | template 354 | void test_string_find_first_not_of() 355 | { 356 | String s("foobar"); 357 | String search("oob"); 358 | 359 | // 1 360 | EXPECT_EQ(0, s.find_first_not_of(search)); 361 | 362 | // 2 363 | EXPECT_EQ(3, s.find_first_not_of("oo", 2)); 364 | 365 | // 3 366 | EXPECT_EQ(1, s.find_first_not_of("f")); 367 | EXPECT_EQ(0, s.find_first_not_of("o")); 368 | EXPECT_EQ(0, s.find_first_not_of("oo")); 369 | EXPECT_EQ(0, s.find_first_not_of("rab")); 370 | 371 | // 4 372 | EXPECT_EQ(0, s.find_first_not_of('a')); 373 | EXPECT_EQ(s.npos, s.find_first_not_of('a', 6)); 374 | } 375 | 376 | template 377 | void test_string_find_last_not_of() 378 | { 379 | String s("foobar"); 380 | String search("oob"); 381 | 382 | // 1 383 | EXPECT_EQ(5, s.find_last_not_of(search)); 384 | 385 | // 2 386 | EXPECT_EQ(0, s.find_last_not_of("oo", 2)); 387 | 388 | // 3 389 | EXPECT_EQ(5, s.find_last_not_of("f")); 390 | EXPECT_EQ(5, s.find_last_not_of("o")); 391 | EXPECT_EQ(5, s.find_last_not_of("oo")); 392 | EXPECT_EQ(2, s.find_last_not_of("rab")); 393 | 394 | // 4 395 | EXPECT_EQ(5, s.find_last_not_of('a')); 396 | EXPECT_EQ(5, s.find_last_not_of('a', 6)); 397 | } 398 | 399 | template 400 | void test_string_split() 401 | { 402 | using namespace native; 403 | String s("one two three four"); 404 | 405 | auto split = string_slice(s).split(' '); 406 | EXPECT_EQ(4, split.size()); 407 | EXPECT_EQ("one", split[0]); 408 | EXPECT_EQ("two", split[1]); 409 | EXPECT_EQ("three", split[2]); 410 | EXPECT_EQ("four", split[3]); 411 | 412 | split = string_slice(s).split("ee"); 413 | EXPECT_EQ(2, split.size()); 414 | EXPECT_EQ("one two thr", split[0]); 415 | EXPECT_EQ(" four", split[1]); 416 | 417 | split = string_slice(s).split(""); 418 | EXPECT_EQ(1, split.size()); 419 | EXPECT_EQ(s, split[0]); 420 | 421 | split = string_slice(s).split(String("two")); 422 | EXPECT_EQ(2, split.size()); 423 | EXPECT_EQ("one ", split[0]); 424 | EXPECT_EQ(" three four", split[1]); 425 | } 426 | 427 | template 428 | void test_string_ostream() 429 | { 430 | String s("foobar"); 431 | 432 | std::ostringstream ostr; 433 | ostr << s; 434 | 435 | EXPECT_EQ("foobar", ostr.str()); 436 | } 437 | 438 | #endif 439 | --------------------------------------------------------------------------------