Repository: nacos-group/nacos-sdk-cpp Branch: master Commit: 2b4104d25247 Files: 239 Total size: 1.2 MB Directory structure: gitextract_q11n9gsq/ ├── .gitignore ├── LICENSE ├── README.md ├── README_zh_CN.md ├── examples/ │ ├── IntegratingIntoYourProject.cpp │ ├── generate_examples.sh │ ├── getAllInstances.cpp │ ├── getConfig.cpp │ ├── listenToKeys.cpp │ ├── registerInstances.cpp │ ├── setConfig.cpp │ └── subscribeServices.cpp ├── include/ │ ├── Compatibility.h │ ├── Nacos.h │ ├── NacosExceptions.h │ ├── NacosString.h │ ├── Properties.h │ ├── ResourceGuard.h │ ├── config/ │ │ └── ConfigService.h │ ├── constant/ │ │ ├── ConfigConstant.h │ │ ├── NamingConstant.h │ │ ├── PropertyKeyConst.h │ │ └── UtilAndComs.h │ ├── factory/ │ │ ├── INacosServiceFactory.h │ │ └── NacosFactoryFactory.h │ ├── listen/ │ │ └── Listener.h │ ├── naming/ │ │ ├── ChangeAdvice.h │ │ ├── Cluster.h │ │ ├── Instance.h │ │ ├── ListView.h │ │ ├── NamingMaintainService.h │ │ ├── NamingService.h │ │ ├── ServiceInfo.h │ │ ├── ServiceInfo2.h │ │ ├── selectors/ │ │ │ ├── HealthInstanceSelector.h │ │ │ ├── RandomByWeightSelector.h │ │ │ ├── RandomSelector.h │ │ │ └── Selector.h │ │ └── subscribe/ │ │ └── EventListener.h │ ├── server/ │ │ └── ServerSelector.h │ └── thread/ │ └── AtomicInt.h ├── nacos-cpp-cli.properties ├── src/ │ ├── NacosExceptions.cpp │ ├── NacosString.cpp │ ├── config/ │ │ ├── AppConfigManager.cpp │ │ ├── AppConfigManager.h │ │ ├── ConcurrentDiskUtil.cpp │ │ ├── ConcurrentDiskUtil.h │ │ ├── ConfigProxy.cpp │ │ ├── ConfigProxy.h │ │ ├── IOUtils.cpp │ │ ├── IOUtils.h │ │ ├── JVMUtil.h │ │ ├── LocalSnapshotManager.cpp │ │ ├── LocalSnapshotManager.h │ │ ├── NacosConfigService.cpp │ │ ├── NacosConfigService.h │ │ ├── SnapShotSwitch.cpp │ │ └── SnapShotSwitch.h │ ├── constant/ │ │ ├── ConfigConstant.cpp │ │ ├── NamingConstant.cpp │ │ ├── PropertyKeyConst.cpp │ │ └── UtilAndComs.cpp │ ├── crypto/ │ │ ├── MACProvider.cpp │ │ ├── MACProvider.h │ │ ├── SignatureTool.h │ │ ├── base64/ │ │ │ └── base64.h │ │ ├── hmac_sha1/ │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ ├── hmac/ │ │ │ │ ├── hmac.h │ │ │ │ └── hmac_sha1.cpp │ │ │ └── sha/ │ │ │ ├── sha.h │ │ │ └── sha1.cpp │ │ └── md5/ │ │ ├── md5.cpp │ │ └── md5.h │ ├── debug/ │ │ └── DebugAssertion.h │ ├── factory/ │ │ ├── NacosFactoryFactory.cpp │ │ ├── NacosServiceFactory.cpp │ │ ├── NacosServiceFactory.h │ │ ├── ObjectConfigData.cpp │ │ └── ObjectConfigData.h │ ├── http/ │ │ ├── HTTPCli.cpp │ │ ├── HTTPCli.h │ │ ├── HttpDelegate.h │ │ ├── HttpStatus.h │ │ ├── IHttpCli.h │ │ └── delegate/ │ │ ├── NacosAuthHttpDelegate.cpp │ │ ├── NacosAuthHttpDelegate.h │ │ ├── NoOpHttpDelegate.cpp │ │ └── NoOpHttpDelegate.h │ ├── init/ │ │ ├── Init.cpp │ │ └── Init.h │ ├── json/ │ │ ├── JSON.cpp │ │ ├── JSON.h │ │ └── rapidjson/ │ │ ├── allocators.h │ │ ├── cursorstreamwrapper.h │ │ ├── document.h │ │ ├── encodedstream.h │ │ ├── encodings.h │ │ ├── error/ │ │ │ ├── en.h │ │ │ └── error.h │ │ ├── filereadstream.h │ │ ├── filewritestream.h │ │ ├── fwd.h │ │ ├── internal/ │ │ │ ├── biginteger.h │ │ │ ├── diyfp.h │ │ │ ├── dtoa.h │ │ │ ├── ieee754.h │ │ │ ├── itoa.h │ │ │ ├── meta.h │ │ │ ├── pow10.h │ │ │ ├── regex.h │ │ │ ├── stack.h │ │ │ ├── strfunc.h │ │ │ ├── strtod.h │ │ │ └── swap.h │ │ ├── istreamwrapper.h │ │ ├── memorybuffer.h │ │ ├── memorystream.h │ │ ├── msinttypes/ │ │ │ ├── inttypes.h │ │ │ └── stdint.h │ │ ├── ostreamwrapper.h │ │ ├── pointer.h │ │ ├── prettywriter.h │ │ ├── rapidjson.h │ │ ├── reader.h │ │ ├── schema.h │ │ ├── stream.h │ │ ├── stringbuffer.h │ │ └── writer.h │ ├── listen/ │ │ ├── ClientWorker.cpp │ │ ├── ClientWorker.h │ │ ├── ListeningData.h │ │ └── OperateItem.h │ ├── log/ │ │ ├── Logger.cpp │ │ └── Logger.h │ ├── naming/ │ │ ├── Cluster.cpp │ │ ├── Instance.cpp │ │ ├── NacosNamingMaintainService.cpp │ │ ├── NacosNamingMaintainService.h │ │ ├── NacosNamingService.cpp │ │ ├── NacosNamingService.h │ │ ├── NamingProxy.cpp │ │ ├── NamingProxy.h │ │ ├── ServiceInfo.cpp │ │ ├── beat/ │ │ │ ├── BeatInfo.cpp │ │ │ ├── BeatInfo.h │ │ │ ├── BeatReactor.cpp │ │ │ ├── BeatReactor.h │ │ │ ├── BeatTask.cpp │ │ │ └── BeatTask.h │ │ ├── cache/ │ │ │ ├── ChangeAdvice.cpp │ │ │ ├── NamingCache.cpp │ │ │ └── NamingCache.h │ │ ├── selectors/ │ │ │ ├── HealthInstanceSelector.cpp │ │ │ ├── RandomByWeightSelector.cpp │ │ │ └── RandomSelector.cpp │ │ └── subscribe/ │ │ ├── EventDispatcher.cpp │ │ ├── EventDispatcher.h │ │ ├── EventListener.cpp │ │ ├── HostReactor.cpp │ │ ├── HostReactor.h │ │ ├── SubscriptionPoller.cpp │ │ ├── SubscriptionPoller.h │ │ ├── UdpNamingServiceListener.cpp │ │ └── UdpNamingServiceListener.h │ ├── security/ │ │ ├── SecurityManager.cpp │ │ └── SecurityManager.h │ ├── server/ │ │ ├── NacosServerInfo.h │ │ ├── ServerListManager.cpp │ │ └── ServerListManager.h │ ├── thread/ │ │ ├── BlockingQueue.h │ │ ├── DelayedThreadPool.cpp │ │ ├── DelayedThreadPool.h │ │ ├── Mutex.h │ │ ├── RWLock.h │ │ ├── Task.h │ │ ├── Thread.cpp │ │ ├── Thread.h │ │ ├── ThreadLocal.h │ │ ├── ThreadPool.cpp │ │ ├── ThreadPool.h │ │ ├── Tid.cpp │ │ └── Tid.h │ └── utils/ │ ├── ConfigParserUtils.cpp │ ├── ConfigParserUtils.h │ ├── DirUtils.cpp │ ├── DirUtils.h │ ├── Env.h │ ├── GroupKey.h │ ├── NamingUtils.h │ ├── NetUtils.cpp │ ├── NetUtils.h │ ├── ParamUtils.h │ ├── RandomUtils.cpp │ ├── RandomUtils.h │ ├── SequenceProvider.h │ ├── TimeUtils.cpp │ ├── TimeUtils.h │ ├── UuidUtils.cpp │ ├── UuidUtils.h │ ├── url.cpp │ └── url.h └── test/ ├── allinone.cpp └── testcase/ ├── AssertString.cpp ├── DebugTest.cpp ├── testAppConfigManager.cpp ├── testCache.cpp ├── testDelayedThreadPool.cpp ├── testDeleteConfig.cpp ├── testDeleteListenedKeys.cpp ├── testEndpointWithNamingSvc.cpp ├── testGetAllInstances.cpp ├── testGetConfig.cpp ├── testGetServiceNames.cpp ├── testHttpRequest.cpp ├── testIOUtils.cpp ├── testInstanceSelector.cpp ├── testJson2BizObjects.cpp ├── testListenWorker.cpp ├── testListeningKeys.cpp ├── testListeningKeysWithHttpPrefix.cpp ├── testMAC.cpp ├── testMD5.cpp ├── testMaintainInstances.cpp ├── testMaintainServices.cpp ├── testNamingService.cpp ├── testNamingSubscribe.cpp ├── testNetUtils.cpp ├── testPublishConfig.cpp ├── testPublishConfigWithHttpPrefix.cpp ├── testRapidJson.cpp ├── testSequenceProvider.cpp ├── testServerListManager.cpp ├── testStringExplode.cpp ├── testThreadLocal.cpp ├── testThreadSmoke.cpp ├── testURL.cpp └── testUUID.cpp ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ # Prerequisites *.d # Compiled Object files *.slo *.lo *.o *.obj # Precompiled Headers *.gch *.pch # Compiled Dynamic libraries *.so *.dylib *.dll # Fortran module files *.mod *.smod # Compiled Static libraries *.lai *.la *.a *.lib # Executables *.exe *.out *.app # temp tmp/ # CMakeFiles CMakeFiles/ CMakeLists.txt CMakeCache.txt cmake_install.cmake install_manifest.txt Makefile # idea project .idea cmake-build-debug *.dat .vscode/ ================================================ FILE: LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: README.md ================================================ [中文版本说明请点这里](https://github.com/nacos-group/nacos-sdk-cpp/blob/master/README_zh_CN.md) # Nacos-sdk-cpp Nacos-sdk-cpp for c++ client allows users to access Nacos service, it supports service discovery and dynamic configuration. [![Gitter](https://badges.gitter.im/alibaba/nacos.svg)](https://gitter.im/alibaba/nacos?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) [![License](https://img.shields.io/badge/license-Apache%202-4EB1BA.svg)](https://www.apache.org/licenses/LICENSE-2.0.html) # Quick Examples ## Setup project Download the source and run the following command in bash: ``` cd nacos-sdk-cpp cmake . make ``` a libnacos-cli.so and a nacos-cli.out will be generated run `./nacos-cli.out` to perform test on the library run `make install` to install the libnacos-cli to your system library path **Note:** You need to run a nacos server on your local machine listening on port 8848 to go through the whole test One of the testcases will test endpoint functionality, so **you also need** to run a simple http server on port 80 which provides the following content: `127.0.0.1:8848` **on path /nacos/endpoint0** **All these examples could be found in nacos-sdk-cpp/examples/** ## Integrate the library into your project Here is an example showing how to integrate the library(.so) into your project: IntegratingIntoYourProject.cpp: ```C++ #include #include "Nacos.h" using namespace std; using namespace nacos; int main() { Properties props; props[PropertyKeyConst::SERVER_ADDR] = "127.0.0.1:8848";//Server address NacosServiceFactory *factory = new NacosServiceFactory(props); ResourceGuard _guardFactory(factory); ConfigService *n = factory->CreateConfigService(); ResourceGuard _serviceFactory(n); NacosString ss = ""; try { ss = n->getConfig("k", NULLSTR, 1000); } catch (NacosException &e) { cout << "Request failed with curl code:" << e.errorcode() << endl << "Reason:" << e.what() << endl; return -1; } cout << ss << endl; return 0; } ``` `g++ -I/usr/local/include/nacos/ IntegratingIntoYourProject.cpp -lnacos-cli -o integrated.out` Start a nacos on your localmachine listening on port 8848, and run `./integrated.out` Then you'll see: `SuccessfullyIntegrated` ## If you are using a static lib(.a): Assume that the file you are compiling resides in the same directory as the .a library, please use the following command: `g++ -I/usr/local/include/nacos/ IntegratingIntoYourProject.cpp -lcurl -lz -L. -lnacos-cli-static -o integrated.out` -lcurl -lz Specifies the curl and lz library used by libnacos -L. -lnacos-cli-static links the static libnacos library resides in the same directory as IntegratingIntoYourProject.cpp ## Configuration ### Get Config getConfig.cpp: ```C++ #include #include "Nacos.h" using namespace std; using namespace nacos; int main() { Properties props; props[PropertyKeyConst::SERVER_ADDR] = "127.0.0.1:8848";//Server address NacosServiceFactory *factory = new NacosServiceFactory(props); ResourceGuard _guardFactory(factory); ConfigService *n = factory->CreateConfigService(); ResourceGuard _serviceFactory(n); NacosString ss = ""; try { ss = n->getConfig("k", NULLSTR, 1000); } catch (NacosException &e) { cout << "Request failed with curl code:" << e.errorcode() << endl << "Reason:" << e.what() << endl; return -1; } cout << ss << endl; return 0; } ``` ### Publish Config setConfig.cpp: ```C++ #include #include "Nacos.h" using namespace std; using namespace nacos; int main() { Properties props; props[PropertyKeyConst::SERVER_ADDR] = "127.0.0.1:8848";//server address NacosServiceFactory *factory = new NacosServiceFactory(props); ResourceGuard _guardFactory(factory); ConfigService *n = factory->CreateConfigService(); ResourceGuard _serviceFactory(n); bool bSucc = false; NacosString ss = ""; try { bSucc = n->publishConfig("Cfg_key", NULLSTR, "Cfg_val"); int retry = 0; ss = n->getConfig("Cfg_key", NULLSTR, 1000); while (!(ss == "Cfg_val") && retry++ < 10) { ss = n->getConfig("Cfg_key", NULLSTR, 1000); } if (!(ss == "Cfg_val")) { throw NacosException(0, "getConfig() failed."); } } catch (NacosException &e) { cout << "Request failed with curl code:" << e.errorcode() << endl << "Reason:" << e.what() << endl; return -1; } cout << "Publishing Key:Cfg_key with value:Cfg_val result:" << bSucc << endl; return 0; } ``` ### Listen to key change & Cancel listening listenToKeys.cpp: ```C++ #include #include "Nacos.h" using namespace std; using namespace nacos; class MyListener : public Listener { private: int num; public: MyListener(int num) { this->num = num; } void receiveConfigInfo(const NacosString &configInfo) { cout << "===================================" << endl; cout << "Watcher" << num << endl; cout << "Watched Key UPDATED:" << configInfo << endl; cout << "===================================" << endl; } }; int main() { Properties props; props[PropertyKeyConst::SERVER_ADDR] = "127.0.0.1:8848"; NacosServiceFactory *factory = new NacosServiceFactory(props); ResourceGuard _guardFactory(factory); ConfigService *n = factory->CreateConfigService(); ResourceGuard _serviceFactory(n); MyListener *theListener = new MyListener(1);//You don't need to free it, since it will be deleted by the function removeListener n->addListener("dqid", NULLSTR, theListener);//All changes on the key dqid will be received by MyListener cout << "Input a character to continue" << endl; getchar(); cout << "remove listener" << endl; n->removeListener("dqid", NULLSTR, theListener);//Cancel listening getchar(); return 0; } ``` ## Naming ### Register Instance & Unregister Instance registerInstances.cpp: ```C++ #include #include #include "Nacos.h" using namespace std; using namespace nacos; int main() { Properties configProps; configProps[PropertyKeyConst::SERVER_ADDR] = "127.0.0.1"; NacosServiceFactory *factory = new NacosServiceFactory(configProps); ResourceGuard _guardFactory(factory); NamingService *namingSvc = factory->CreateNamingService(); ResourceGuard _serviceFactory(namingSvc); Instance instance; instance.clusterName = "DefaultCluster"; instance.ip = "127.0.0.1"; instance.port = 2333; instance.instanceId = "1"; instance.ephemeral = true; //Registers 5 services named TestNamingService1...5 try { for (int i = 0; i < 5; i++) { NacosString serviceName = "TestNamingService" + NacosStringOps::valueOf(i); instance.port = 2000 + i; namingSvc->registerInstance(serviceName, instance); } } catch (NacosException &e) { cout << "encounter exception while registering service instance, raison:" << e.what() << endl; return -1; } sleep(30); try { for (int i = 0; i < 5; i++) { NacosString serviceName = "TestNamingService" + NacosStringOps::valueOf(i); namingSvc->deregisterInstance(serviceName, "127.0.0.1", 2000 + i); sleep(1); } } catch (NacosException &e) { cout << "encounter exception while registering service instance, raison:" << e.what() << endl; return -1; } sleep(30); return 0; } ``` ### Subscribe & Unsubscribe subscribeServices.cpp: ```C++ #include #include "Nacos.h" using namespace std; using namespace nacos; class MyServiceListener : public EventListener { private: int num; public: MyServiceListener(int num) { this->num = num; } void receiveNamingInfo(const ServiceInfo &serviceInfo){ cout << "===================================" << endl; cout << "Watcher: " << num << endl; cout << "Watched service UPDATED: " << serviceInfo.toInstanceString() << endl; cout << "===================================" << endl; } }; int main() { Properties props; props[PropertyKeyConst::SERVER_ADDR] = "127.0.0.1:8848"; //Interval for poller to check the status of subscribed services(unit:Ms), 30000 by default //Here we set it to 5000 to see the output more quick props[PropertyKeyConst::SUBSCRIPTION_POLL_INTERVAL] = "5000"; NacosServiceFactory *factory = new NacosServiceFactory(props); ResourceGuard _guardFactory(factory); NamingService *n = factory->CreateNamingService(); ResourceGuard _serviceFactory(n); n->subscribe("ss", new MyServiceListener(1)); cout << "Press any key to register services" << endl; getchar(); n->registerInstance("ss", "127.0.0.1", 33); n->registerInstance("ss", "127.0.0.1", 34); cout << "Press any key to deregister services" << endl; getchar(); n->deregisterInstance("ss", "127.0.0.1", 33); n->deregisterInstance("ss", "127.0.0.1", 34); cout << "All instances Unregistered, press any key to finish testing" << endl; getchar(); return 0; } ``` ### Get all instances of a service getAllInstances.cpp: ```C++ #include #include #include "Nacos.h" using namespace std; using namespace nacos; int main() { Properties configProps; configProps[PropertyKeyConst::SERVER_ADDR] = "127.0.0.1"; NacosServiceFactory *factory = new NacosServiceFactory(configProps); ResourceGuard _guardFactory(factory); NamingService *namingSvc = factory->CreateNamingService(); ResourceGuard _guardService(namingSvc); list instances = namingSvc->getAllInstances("TestNamingService1"); cout << "getAllInstances from server:" << endl; for (list::iterator it = instances.begin(); it != instances.end(); it++) { cout << "Instance:" << it->toString() << endl; } return 0; } ``` ### Enabling Authentication If your Nacos server is secured with password, you can add the following snippet to any of the examples above to enable authentication: ```C++ using namespace nacos; ...... configProps[PropertyKeyConst::SERVER_ADDR] = "127.0.0.1"; configProps[PropertyKeyConst::AUTH_USERNAME] = "username"; configProps[PropertyKeyConst::AUTH_PASSWORD] = "password"; NacosServiceFactory *factory = new NacosServiceFactory(configProps); ConfigService *n = factory->CreateConfigService(); NamingService *namingSvc = factory->CreateNamingService(); ...... ``` ### Enabling SPAS Authentication ```C++ using namespace nacos; ...... configProps[PropertyKeyConst::SERVER_ADDR] = "127.0.0.1"; configProps[PropertyKeyConst::ACCESS_KEY] = "accessKey"; configProps[PropertyKeyConst::SECRET_KEY] = "secretKey"; NacosServiceFactory *factory = new NacosServiceFactory(configProps); ConfigService *n = factory->CreateConfigService(); NamingService *namingSvc = factory->CreateNamingService(); ...... ``` # Supported System/Compilers | OS/Environment | Compilers | Tested version | | ---- | ---- | ---- | |MacOS Darwin 19.6.0 x86_64|Clang|Apple clang version 12.0.0 (clang-1200.0.26.2)| |Windows 10 WSL|GCC|version 4.8.4| |Windows 10 CYGWIN_NT-10.0 x86_64|GCC|version 10.2.0 (GCC)| |Ubuntu1~16.04.12|GCC|version 5.4.0| |CentOS|GCC|| |Windows|Visual C++|To be done| # About Nacos Nacos (official site: [http://nacos.io](http://nacos.io)) is an easy-to-use platform designed for dynamic service discovery and configuration and service management. It helps you to build cloud native applications and microservices platform easily. Service is a first-class citizen in Nacos. Nacos supports almost all type of services, for example: [Dubbo/gRPC service](https://nacos.io/en-us/docs/use-nacos-with-dubbo.html), [Spring Cloud RESTFul service](https://nacos.io/en-us/docs/use-nacos-with-springcloud.html) and [Kubernetes service](https://nacos.io/en-us/docs/use-nacos-with-kubernetes.html). ================================================ FILE: README_zh_CN.md ================================================ # Nacos-sdk-cpp Nacos-sdk-cpp是nacos客戶端的C++版本,它支持服务发现和动态配置 [![Gitter](https://badges.gitter.im/alibaba/nacos.svg)](https://gitter.im/alibaba/nacos?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) [![License](https://img.shields.io/badge/license-Apache%202-4EB1BA.svg)](https://www.apache.org/licenses/LICENSE-2.0.html) # 快速开始 ## 设置工程 下载工程源代码并且执行下述命令: ``` cd nacos-sdk-cpp cmake . make ``` 将会产生一个libnacos-cli.so 和一个 nacos-cli.out 运行 `./nacos-cli.out` 以执行客户端的所有testcase 运行 `make install` 将libnacos-cli安装到lib目录 **注意:** 你需要在本机运行一个nacos server,监听8848端口以完成所有测试 其中有个测试将会测试端点(endpoint)功能,**所以你还需要**在本机运行一个http服务器,在路径/endpoints/endpoint0提供下述内容: `127.0.0.1:8848` **这些例子你都能在nacos-sdk-cpp/examples/找到** ## 将libnacos-cli集成到你的工程 下面的例子说明了如何将库文件(.so) 集成到你的工程: IntegratingIntoYourProject.cpp: ```C++ #include #include "Nacos.h" using namespace std; using namespace nacos; int main() { Properties props; props[PropertyKeyConst::SERVER_ADDR] = "127.0.0.1:8848";//Server address NacosServiceFactory *factory = new NacosServiceFactory(props); ResourceGuard _guardFactory(factory); ConfigService *n = factory->CreateConfigService(); ResourceGuard _serviceFactory(n); NacosString ss = ""; try { ss = n->getConfig("k", NULLSTR, 1000); } catch (NacosException &e) { cout << "Request failed with curl code:" << e.errorcode() << endl << "Reason:" << e.what() << endl; return -1; } cout << ss << endl; return 0; } ``` `g++ -I/usr/local/include/nacos/ IntegratingIntoYourProject.cpp -lnacos-cli -o integrated.out` 在本机的8848端口启动一个nacos server, 并且运行 `./integrated.out` 你将会看到: `SuccessfullyIntegrated` ## 如果你使用静态库(.a)链接 假设.a文件和待编译文件在同一目录, 请执行下述命令: `g++ -I/usr/local/include/nacos/ IntegratingIntoYourProject.cpp -lcurl -lz -L. -lnacos-cli-static -o integrated.out` 使用-lcurl -lz指定nacos客户端使用的curl和lz库 使用-L. -lnacos-cli-static引用当前目录下的libnacos-cli-static.a ## 配置 ### 获取配置 getConfig.cpp: ```C++ #include #include "Nacos.h" using namespace std; using namespace nacos; int main() { Properties props; props[PropertyKeyConst::SERVER_ADDR] = "127.0.0.1:8848";//Server address NacosServiceFactory *factory = new NacosServiceFactory(props); ResourceGuard _guardFactory(factory); ConfigService *n = factory->CreateConfigService(); ResourceGuard _serviceFactory(n); NacosString ss = ""; try { ss = n->getConfig("k", NULLSTR, 1000); } catch (NacosException &e) { cout << "Request failed with curl code:" << e.errorcode() << endl << "Reason:" << e.what() << endl; return -1; } cout << ss << endl; return 0; } ``` ### 发布配置 setConfig.cpp: ```C++ #include #include "Nacos.h" using namespace std; using namespace nacos; int main() { Properties props; props[PropertyKeyConst::SERVER_ADDR] = "127.0.0.1:8848";//server address NacosServiceFactory *factory = new NacosServiceFactory(props); ResourceGuard _guardFactory(factory); ConfigService *n = factory->CreateConfigService(); ResourceGuard _serviceFactory(n); bool bSucc = false; NacosString ss = ""; try { bSucc = n->publishConfig("Cfg_key", NULLSTR, "Cfg_val"); int retry = 0; ss = n->getConfig("Cfg_key", NULLSTR, 1000); while (!(ss == "Cfg_val") && retry++ < 10) { ss = n->getConfig("Cfg_key", NULLSTR, 1000); } if (!(ss == "Cfg_val")) { throw NacosException(0, "getConfig() failed."); } } catch (NacosException &e) { cout << "Request failed with curl code:" << e.errorcode() << endl << "Reason:" << e.what() << endl; return -1; } cout << "Publishing Key:Cfg_key with value:Cfg_val result:" << bSucc << endl; return 0; } ``` ### 监听配置变化和取消监听 listenToKeys.cpp: ```C++ #include #include "Nacos.h" using namespace std; using namespace nacos; class MyListener : public Listener { private: int num; public: MyListener(int num) { this->num = num; } void receiveConfigInfo(const NacosString &configInfo) { cout << "===================================" << endl; cout << "Watcher" << num << endl; cout << "Watched Key UPDATED:" << configInfo << endl; cout << "===================================" << endl; } }; int main() { Properties props; props[PropertyKeyConst::SERVER_ADDR] = "127.0.0.1:8848"; NacosServiceFactory *factory = new NacosServiceFactory(props); ResourceGuard _guardFactory(factory); ConfigService *n = factory->CreateConfigService(); ResourceGuard _serviceFactory(n); MyListener *theListener = new MyListener(1);//You don't need to free it, since it will be deleted by the function removeListener n->addListener("dqid", NULLSTR, theListener);//All changes on the key dqid will be received by MyListener cout << "Input a character to continue" << endl; getchar(); cout << "remove listener" << endl; n->removeListener("dqid", NULLSTR, theListener);//Cancel listening getchar(); return 0; } ``` ## 命名服务 ### 注册和反注册实例 registerInstances.cpp: ```C++ #include #include #include "Nacos.h" using namespace std; using namespace nacos; int main() { Properties configProps; configProps[PropertyKeyConst::SERVER_ADDR] = "127.0.0.1"; NacosServiceFactory *factory = new NacosServiceFactory(configProps); ResourceGuard _guardFactory(factory); NamingService *namingSvc = factory->CreateNamingService(); ResourceGuard _serviceFactory(namingSvc); Instance instance; instance.clusterName = "DefaultCluster"; instance.ip = "127.0.0.1"; instance.port = 2333; instance.instanceId = "1"; instance.ephemeral = true; //Registers 5 services named TestNamingService1...5 try { for (int i = 0; i < 5; i++) { NacosString serviceName = "TestNamingService" + NacosStringOps::valueOf(i); instance.port = 2000 + i; namingSvc->registerInstance(serviceName, instance); } } catch (NacosException &e) { cout << "encounter exception while registering service instance, raison:" << e.what() << endl; return -1; } sleep(30); try { for (int i = 0; i < 5; i++) { NacosString serviceName = "TestNamingService" + NacosStringOps::valueOf(i); namingSvc->deregisterInstance(serviceName, "127.0.0.1", 2000 + i); sleep(1); } } catch (NacosException &e) { cout << "encounter exception while registering service instance, raison:" << e.what() << endl; return -1; } sleep(30); return 0; } ``` ### 订阅和取消订阅服务 subscribeServices.cpp: ```C++ #include #include "Nacos.h" using namespace std; using namespace nacos; class MyServiceListener : public EventListener { private: int num; public: MyServiceListener(int num) { this->num = num; } void receiveNamingInfo(const ServiceInfo &serviceInfo){ cout << "===================================" << endl; cout << "Watcher: " << num << endl; cout << "Watched service UPDATED: " << serviceInfo.toInstanceString() << endl; cout << "===================================" << endl; } }; int main() { Properties props; props[PropertyKeyConst::SERVER_ADDR] = "127.0.0.1:8848"; //Interval for poller to check the status of subscribed services(unit:Ms), 30000 by default //Here we set it to 5000 to see the output more quick props[PropertyKeyConst::SUBSCRIPTION_POLL_INTERVAL] = "5000"; NacosServiceFactory *factory = new NacosServiceFactory(props); ResourceGuard _guardFactory(factory); NamingService *n = factory->CreateNamingService(); ResourceGuard _serviceFactory(n); n->subscribe("ss", new MyServiceListener(1)); cout << "Press any key to register services" << endl; getchar(); n->registerInstance("ss", "127.0.0.1", 33); n->registerInstance("ss", "127.0.0.1", 34); cout << "Press any key to deregister services" << endl; getchar(); n->deregisterInstance("ss", "127.0.0.1", 33); n->deregisterInstance("ss", "127.0.0.1", 34); cout << "All instances Unregistered, press any key to finish testing" << endl; getchar(); return 0; } ``` ### 获取某个服务的全部实例 getAllInstances.cpp: ```C++ #include #include #include "Nacos.h" using namespace std; using namespace nacos; int main() { Properties configProps; configProps[PropertyKeyConst::SERVER_ADDR] = "127.0.0.1"; NacosServiceFactory *factory = new NacosServiceFactory(configProps); ResourceGuard _guardFactory(factory); NamingService *namingSvc = factory->CreateNamingService(); ResourceGuard _guardService(namingSvc); list instances = namingSvc->getAllInstances("TestNamingService1"); cout << "getAllInstances from server:" << endl; for (list::iterator it = instances.begin(); it != instances.end(); it++) { cout << "Instance:" << it->toString() << endl; } return 0; } ``` ### 启用认证 如果你的服务器启设置了密码,在上述任意一个例子当中加入下述配置,即可启用用户名密码认证: ```C++ using namespace nacos; ...... configProps[PropertyKeyConst::SERVER_ADDR] = "127.0.0.1"; configProps[PropertyKeyConst::AUTH_USERNAME] = "username"; configProps[PropertyKeyConst::AUTH_PASSWORD] = "password"; NacosServiceFactory *factory = new NacosServiceFactory(configProps); ConfigService *n = factory->CreateConfigService(); NamingService *namingSvc = factory->CreateNamingService(); ...... ``` ### 启动SPAS鉴权 ```C++ using namespace nacos; ...... configProps[PropertyKeyConst::SERVER_ADDR] = "127.0.0.1"; configProps[PropertyKeyConst::ACCESS_KEY] = "accessKey"; configProps[PropertyKeyConst::SECRET_KEY] = "secretKey"; NacosServiceFactory *factory = new NacosServiceFactory(configProps); ConfigService *n = factory->CreateConfigService(); NamingService *namingSvc = factory->CreateNamingService(); ...... ``` # 支持的系统/编译器 | 操作系统/环境 | 编译器 | 测试版本 | | ---- | ---- | ---- | |MacOS Darwin 19.6.0 x86_64|Clang|Apple clang version 12.0.0 (clang-1200.0.26.2)| |Windows 10 WSL|GCC|version 4.8.4| |Windows 10 CYGWIN_NT-10.0 x86_64|GCC|version 10.2.0 (GCC)| |Ubuntu1~16.04.12|GCC|version 5.4.0| |CentOS|GCC|| |Windows|Visual C++|计划中| # 关于Nacos Nacos (官方网站: [http://nacos.io](http://nacos.io)) 是一个易用的动态服务发现、配置管理以及服务管理平台。它将助力您轻松建立云上原生应用和微服务。 在Nacos中,服务是一级公民. Nacos 支持几乎所有种类的服务,例如: [Dubbo/gRPC service](https://nacos.io/en-us/docs/use-nacos-with-dubbo.html) [Spring Cloud RESTFul service](https://nacos.io/en-us/docs/use-nacos-with-springcloud.html) [Kubernetes service](https://nacos.io/en-us/docs/use-nacos-with-kubernetes.html). ================================================ FILE: examples/IntegratingIntoYourProject.cpp ================================================ #include #include "Nacos.h" using namespace std; using namespace nacos; int main() { Properties props; props[PropertyKeyConst::SERVER_ADDR] = "127.0.0.1:8848";//Server address INacosServiceFactory *factory = NacosFactoryFactory::getNacosFactory(props); ResourceGuard _guardFactory(factory); ConfigService *n = factory->CreateConfigService(); ResourceGuard _serviceFactory(n); NacosString ss = ""; try { ss = n->getConfig("k", NULLSTR, 1000); } catch (NacosException &e) { cout << "Request failed with curl code:" << e.errorcode() << endl << "Reason:" << e.what() << endl; return -1; } cout << ss << endl; return 0; } ================================================ FILE: examples/generate_examples.sh ================================================ #!/bin/bash g++ -I/usr/local/include/nacos/ IntegratingIntoYourProject.cpp -lnacos-cli -o IntegratingIntoYourProject.out g++ -I/usr/local/include/nacos/ getAllInstances.cpp -lnacos-cli -o getAllInstances.out g++ -I/usr/local/include/nacos/ getConfig.cpp -lnacos-cli -o getConfig.out g++ -I/usr/local/include/nacos/ listenToKeys.cpp -lnacos-cli -o listenToKeys.out g++ -I/usr/local/include/nacos/ registerInstances.cpp -lnacos-cli -o registerInstances.out g++ -I/usr/local/include/nacos/ setConfig.cpp -lnacos-cli -o setConfig.out g++ -I/usr/local/include/nacos/ subscribeServices.cpp -lnacos-cli -o subscribeServices.out ================================================ FILE: examples/getAllInstances.cpp ================================================ #include #include #include "Nacos.h" using namespace std; using namespace nacos; int main() { Properties configProps; configProps[PropertyKeyConst::SERVER_ADDR] = "127.0.0.1"; INacosServiceFactory *factory = NacosFactoryFactory::getNacosFactory(configProps); ResourceGuard _guardFactory(factory); NamingService *namingSvc = factory->CreateNamingService(); ResourceGuard _guardService(namingSvc); list instances = namingSvc->getAllInstances("TestNamingService1"); cout << "getAllInstances from server:" << endl; for (list::iterator it = instances.begin(); it != instances.end(); it++) { cout << "Instance:" << it->toString() << endl; } return 0; } ================================================ FILE: examples/getConfig.cpp ================================================ #include #include "Nacos.h" using namespace std; using namespace nacos; int main() { Properties props; props[PropertyKeyConst::SERVER_ADDR] = "127.0.0.1:8848";//Server address INacosServiceFactory *factory = NacosFactoryFactory::getNacosFactory(props); ResourceGuard _guardFactory(factory); ConfigService *n = factory->CreateConfigService(); ResourceGuard _serviceFactory(n); NacosString ss = ""; try { ss = n->getConfig("k", NULLSTR, 1000); } catch (NacosException &e) { cout << "Request failed with curl code:" << e.errorcode() << endl << "Reason:" << e.what() << endl; return -1; } cout << ss << endl; return 0; } ================================================ FILE: examples/listenToKeys.cpp ================================================ #include #include "Nacos.h" #include using namespace std; using namespace nacos; class MyListener : public Listener { private: int num; public: MyListener(int num) { this->num = num; } void receiveConfigInfo(const NacosString &configInfo) { cout << "===================================" << endl; cout << "Watcher" << num << endl; cout << "Watched Key UPDATED:" << configInfo << endl; cout << "===================================" << endl; } }; int main() { Properties props; props[PropertyKeyConst::SERVER_ADDR] = "127.0.0.1:8848"; INacosServiceFactory *factory = NacosFactoryFactory::getNacosFactory(props); ResourceGuard _guardFactory(factory); ConfigService *n = factory->CreateConfigService(); ResourceGuard _serviceFactory(n); MyListener *theListener = new MyListener(1);//You don't need to free it, since it will be deleted by the function removeListener n->addListener("dqid", NULLSTR, theListener);//All changes on the key dqid will be received by MyListener cout << "Input a character to continue" << endl; getchar(); cout << "remove listener" << endl; n->removeListener("dqid", NULLSTR, theListener);//Cancel listening getchar(); return 0; } ================================================ FILE: examples/registerInstances.cpp ================================================ #include #include #include "Nacos.h" using namespace std; using namespace nacos; int main() { Properties configProps; configProps[PropertyKeyConst::SERVER_ADDR] = "127.0.0.1"; INacosServiceFactory *factory = NacosFactoryFactory::getNacosFactory(configProps); ResourceGuard _guardFactory(factory); NamingService *namingSvc = factory->CreateNamingService(); ResourceGuard _serviceFactory(namingSvc); Instance instance; instance.clusterName = "DefaultCluster"; instance.ip = "127.0.0.1"; instance.port = 2333; instance.instanceId = "1"; instance.ephemeral = true; //Registers 5 services named TestNamingService1...5 try { for (int i = 0; i < 5; i++) { NacosString serviceName = "TestNamingService" + NacosStringOps::valueOf(i); instance.port = 2000 + i; namingSvc->registerInstance(serviceName, instance); } } catch (NacosException &e) { cout << "encounter exception while registering service instance, raison:" << e.what() << endl; return -1; } sleep(30); try { for (int i = 0; i < 5; i++) { NacosString serviceName = "TestNamingService" + NacosStringOps::valueOf(i); namingSvc->deregisterInstance(serviceName, "127.0.0.1", 2000 + i); sleep(1); } } catch (NacosException &e) { cout << "encounter exception while registering service instance, raison:" << e.what() << endl; return -1; } sleep(30); return 0; } ================================================ FILE: examples/setConfig.cpp ================================================ #include #include "Nacos.h" using namespace std; using namespace nacos; int main() { Properties props; props[PropertyKeyConst::SERVER_ADDR] = "127.0.0.1:8848";//server address INacosServiceFactory *factory = NacosFactoryFactory::getNacosFactory(props); ResourceGuard _guardFactory(factory); ConfigService *n = factory->CreateConfigService(); ResourceGuard _serviceFactory(n); bool bSucc = false; NacosString ss = ""; try { bSucc = n->publishConfig("Cfg_key", NULLSTR, "Cfg_val"); int retry = 0; ss = n->getConfig("Cfg_key", NULLSTR, 1000); while (!(ss == "Cfg_val") && retry++ < 10) { ss = n->getConfig("Cfg_key", NULLSTR, 1000); } if (!(ss == "Cfg_val")) { throw NacosException(0, "getConfig() failed."); } } catch (NacosException &e) { cout << "Request failed with curl code:" << e.errorcode() << endl << "Reason:" << e.what() << endl; return -1; } cout << "Publishing Key:Cfg_key with value:Cfg_val result:" << bSucc << endl; return 0; } ================================================ FILE: examples/subscribeServices.cpp ================================================ #include #include "Nacos.h" #include using namespace std; using namespace nacos; class MyServiceListener : public EventListener { private: int num; public: MyServiceListener(int num) { this->num = num; } void receiveNamingInfo(const ServiceInfo &serviceInfo){ cout << "===================================" << endl; cout << "Watcher: " << num << endl; cout << "Watched service UPDATED: " << serviceInfo.toInstanceString() << endl; cout << "===================================" << endl; } }; int main() { Properties props; props[PropertyKeyConst::SERVER_ADDR] = "127.0.0.1:8848"; //Interval for poller to check the status of subscribed services(unit:Ms), 30000 by default //Here we set it to 5000 to see the output more quick props[PropertyKeyConst::SUBSCRIPTION_POLL_INTERVAL] = "5000"; INacosServiceFactory *factory = NacosFactoryFactory::getNacosFactory(props); ResourceGuard _guardFactory(factory); NamingService *n = factory->CreateNamingService(); ResourceGuard _serviceFactory(n); n->subscribe("ss", new MyServiceListener(1)); cout << "Press any key to register services" << endl; getchar(); n->registerInstance("ss", "127.0.0.1", 33); n->registerInstance("ss", "127.0.0.1", 34); cout << "Press any key to deregister services" << endl; getchar(); n->deregisterInstance("ss", "127.0.0.1", 33); n->deregisterInstance("ss", "127.0.0.1", 34); cout << "All instances Unregistered, press any key to finish testing" << endl; getchar(); return 0; } ================================================ FILE: include/Compatibility.h ================================================ // // Created by liuhanyu on 2021/4/7. // Compatibility header #ifndef NACOS_SDK_CPP_COMPATIBILITY_H #define NACOS_SDK_CPP_COMPATIBILITY_H #if __cplusplus >= 201402L //C++ 17 compatibility #define NACOS_THROW(...) #define NACOS_NOTHROW() noexcept #else #define NACOS_THROW throw #define NACOS_NOTHROW throw #endif #endif //NACOS_SDK_CPP_COMPATIBILITY_H ================================================ FILE: include/Nacos.h ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef _NACOS_H_ #define _NACOS_H_ #include "factory/INacosServiceFactory.h" #include "factory/NacosFactoryFactory.h" #include "constant/PropertyKeyConst.h" #include "ResourceGuard.h" #endif ================================================ FILE: include/NacosExceptions.h ================================================ #ifndef __NACOS_EXCEPTIONS_H_ #define __NACOS_EXCEPTIONS_H_ #include #include "NacosString.h" #include "Compatibility.h" namespace nacos{ class NacosException : public std::exception { protected: int _errcode; NacosString _errmsg; public: NacosException(int errorcode, const char *errormsg) NACOS_NOTHROW(); NacosException(int errorcode, const NacosString &errormsg) NACOS_NOTHROW(); ~NacosException() NACOS_NOTHROW() {}; const char *what() const NACOS_NOTHROW() { return _errmsg.c_str(); }; const int errorcode() const NACOS_NOTHROW() { return _errcode; }; static const int CLIENT_INVALID_PARAM = -400; /** * over client threshold(超过server端的限流阈值) */ static const int CLIENT_OVER_THRESHOLD = -503; /** * server error code * 400 403 throw exception to user * 500 502 503 change ip and retry */ /** * invalid param(参数错误) */ static const int INVALID_PARAM = 400; /** * no right(鉴权失败) */ static const int NO_RIGHT = 403; static const int HTTP_NOT_FOUND = 404; /** * conflict(写并发冲突) */ static const int CONFLICT = 409; /** * server error(server异常,如超时) */ static const int SERVER_ERROR = 500; /** * bad gateway(路由异常,如nginx后面的Server挂掉) */ static const int BAD_GATEWAY = 502; /** * over threshold(超过server端的限流阈值) */ static const int OVER_THRESHOLD = 503; /** * Error while parsing the JSON string */ static const int INVALID_JSON_FORMAT = 100001; /** * One or more JSON field is missing */ static const int LACK_JSON_FIELD = 100002; static const int MALFORMED_CONFIG_FILE = 1001; static const int FILE_NOT_FOUND = 1002; static const int INVALID_FACTORY_CONFIG = 1003; static const int ALL_SERVERS_TRIED_AND_FAILED = 1004; static const int NO_SERVER_AVAILABLE = 1005; static const int INVALID_LOGIN_CREDENTIAL = 1006; static const int UNABLE_TO_OPEN_FILE = 1007; static const int UNABLE_TO_GET_HOST_IP = 1008; static const int UNABLE_TO_CREATE_SOCKET = 1009; static const int INVALID_CONFIG_PARAM = 1010; static const int UNABLE_TO_GET_HOST_NAME = 1011; }; class NetworkException : public std::exception { private: int _curlerrcode; NacosString _errmsg; public: NetworkException(int errorcode, const char *errormsg) NACOS_NOTHROW(): _curlerrcode(errorcode), _errmsg(errormsg) {}; NetworkException(int errorcode, const NacosString &errormsg) NACOS_NOTHROW(): _curlerrcode(errorcode), _errmsg(errormsg) {}; ~NetworkException() NACOS_NOTHROW() {}; const char *what() const NACOS_NOTHROW() { return _errmsg.c_str(); }; const int errorcode() const NACOS_NOTHROW() { return _curlerrcode; }; }; class IOException : public NacosException { public: IOException(int errorcode, const char *errormsg) NACOS_NOTHROW() : NacosException(errorcode, errormsg) {}; IOException(int errorcode, const NacosString &errormsg) NACOS_NOTHROW() : NacosException(errorcode, errormsg) {}; ~IOException() NACOS_NOTHROW() {}; }; class MalformedConfigException : public NacosException { public: MalformedConfigException(const NacosString &file, const NacosString &detailedMsg) : NacosException(NacosException::MALFORMED_CONFIG_FILE, "Malformed Config file:" + file + " reason:" + detailedMsg) {}; }; class InvalidFactoryConfigException : public NacosException { public: InvalidFactoryConfigException() : NacosException(NacosException::INVALID_FACTORY_CONFIG, "Either config file or property should be configed.") {}; }; }//namespace nacos #endif ================================================ FILE: include/NacosString.h ================================================ #ifndef __NACOS_STRING_H_ #define __NACOS_STRING_H_ #include #include #define NacosString std::string #define NULLSTR NacosStringOps::nullstr #define isNull NacosStringOps::isNullStr namespace nacos{ class NacosStringOps { public: static const NacosString nullstr; static bool isNullStr(const NacosString &str); template static NacosString valueOf(T val); static const NacosString STR_TRUE; static const NacosString STR_FALSE; }; template NacosString NacosStringOps::valueOf(T val) { std::ostringstream os; if (os << val) { return NacosString(os.str().c_str()); } return NULLSTR; } template<> NacosString NacosStringOps::valueOf(bool val); }//namespace nacos #endif ================================================ FILE: include/Properties.h ================================================ #ifndef __PROPERTIES_H_ #define __PROPERTIES_H_ #include #include "NacosString.h" namespace nacos{ class Properties : public std::map { public: NacosString toString() const { NacosString content = ""; for (std::map::const_iterator it = begin(); it != end(); it++) { content += (it->first + "=" + it->second + "\n"); } return content; } bool contains(const NacosString &key) const { if (count(key) > 0) { return true; } return false; } }; }//namespace nacos #endif ================================================ FILE: include/ResourceGuard.h ================================================ // // Created by liuhanyu on 2020/9/6. // #ifndef NACOS_SDK_CPP_RESOURCEGUARD_H #define NACOS_SDK_CPP_RESOURCEGUARD_H namespace nacos{ template class ResourceGuard { private: T *_obj;//The object being watched ResourceGuard(); public: ResourceGuard(T *obj) { _obj = obj; }; ~ResourceGuard() { if (_obj != NULL) { delete _obj; _obj = NULL; } }; }; }//namespace nacos #endif //NACOS_SDK_CPP_RESOURCEGUARD_H ================================================ FILE: include/config/ConfigService.h ================================================ #ifndef __CFG_SVC_H_ #define __CFG_SVC_H_ #include "NacosExceptions.h" #include "NacosString.h" #include "listen/Listener.h" #include "Compatibility.h" namespace nacos{ class ConfigService { public: /** * Get config * * @param dataId dataId * @param group group * @param timeoutMs read timeout * @return config value * @throws NacosException NacosException */ virtual NacosString getConfig(const NacosString &dataId, const NacosString &group, long timeoutMs) NACOS_THROW(NacosException) = 0; /** * Add a listener to the configuration, after the server modified the * configuration, the client will use the incoming listener callback. * Recommended asynchronous processing, the application can implement the * getExecutor method in the ManagerListener, provide a thread pool of * execution. If provided, use the main thread callback, May block other * configurations or be blocked by other configurations. * * @param dataId dataId * @param group group * @param listener listener * @throws NacosException NacosException */ virtual void addListener(const NacosString &dataId, const NacosString &group, Listener *listener) NACOS_THROW(NacosException) = 0; /** * Publish config. * * @param dataId dataId * @param group group * @param content content * @return Whether publish * @throws NacosException NacosException */ virtual bool publishConfig(const NacosString &dataId, const NacosString &group, const NacosString &content) NACOS_THROW(NacosException) = 0; /** * Remove config * * @param dataId dataId * @param group group * @return whether remove * @throws NacosException NacosException */ virtual bool removeConfig(const NacosString &dataId, const NacosString &group) NACOS_THROW(NacosException) = 0; /** * Remove listener * * @param listener listener */ virtual void removeListener(const NacosString &dataId, const NacosString &group, Listener *listener) = 0; /** * Get server status * * @return whether health */ //virtual NacosString getServerStatus() = 0; virtual ~ConfigService() {}; }; }//namespace nacos #endif ================================================ FILE: include/constant/ConfigConstant.h ================================================ #ifndef __CONFIG_CONSTANTS_H_ #define __CONFIG_CONSTANTS_H_ #include "NacosString.h" /** * Constant * * @author Nacos */ namespace nacos{ //Constants for config service class ConfigConstant { public: const static NacosString DEFAULT_GROUP; const static NacosString DEFAULT_CONTEXT_PATH; const static NacosString PROTOCOL_VERSION; const static NacosString GET_SERVERS_PATH; const static NacosString DATAID; const static NacosString PROBE_MODIFY_REQUEST; const static NacosString PROBE_MODIFY_RESPONSE; const static NacosString BASE_PATH; const static NacosString CONFIG_CONTROLLER_PATH; /** * second */ const static int POLLING_INTERVAL_TIME; const static NacosString ENCODE; const static int FLOW_CONTROL_THRESHOLD; const static NacosString LINE_SEPARATOR; const static NacosString WORD_SEPARATOR; const static NacosString NAMING_INSTANCE_ID_SPLITTER; const static NacosString DEFAULT_CLUSTER_NAME; const static NacosString SERVICE_INFO_SPLITER; const static NacosString FILE_SEPARATOR; const static NacosString CONFIG_NEXT_LINE; const static NacosString CONFIG_KV_SEPARATOR; const static NacosString DEFAULT_CONFIG_FILE; }; }//namespace nacos #endif ================================================ FILE: include/constant/NamingConstant.h ================================================ #ifndef __NAMING_CONSTANT_H_ #define __NAMING_CONSTANT_H_ #include "NacosString.h" namespace nacos{ class NamingConstant { public: static const NacosString SERVICE_NAME; static const NacosString CLUSTER_NAME; static const NacosString UDP_PORT; static const NacosString CLUSTERS; static const NacosString CLIENT_IP; static const NacosString HEALTHY_ONLY; static const NacosString HEALTHY; static const NacosString NAMESPACE_ID; static const NacosString GROUP_NAME; static const NacosString SPLITER; static const NacosString EMPTY; static const NacosString ALL_IPS; static const NacosString BEAT; static const NacosString PAGE_SIZE; static const NacosString PAGE_NO; }; }//namespace nacos #endif ================================================ FILE: include/constant/PropertyKeyConst.h ================================================ #ifndef __PROP_KEY_CONST_H_ #define __PROP_KEY_CONST_H_ #include "NacosString.h" namespace nacos{ class PropertyKeyConst { public: static const NacosString IS_USE_ENDPOINT_PARSING_RULE; static const NacosString ENDPOINT; static const NacosString ENDPOINT_PORT; static const NacosString NAMESPACE; static const NacosString ENDPOINT_QUERY_PARAMS; static const NacosString ACCESS_KEY; static const NacosString SECRET_KEY; static const NacosString APP_NAME; static const NacosString RAM_ROLE_NAME; static const NacosString SERVER_ADDR; static const NacosString CONTEXT_PATH; static const NacosString ENDPOINT_CONTEXT_PATH; static const NacosString CLUSTER_NAME; static const NacosString ENCODE; static const NacosString NAMING_LOAD_CACHE_AT_START; static const NacosString NAMING_CLIENT_BEAT_THREAD_COUNT; static const NacosString NAMING_POLLING_THREAD_COUNT; static const NacosString SRVLISTMGR_REFRESH_INTERVAL; static const NacosString SERVER_REQ_TIMEOUT; static const NacosString SUBSCRIPTION_POLL_INTERVAL; static const NacosString CONFIG_LONGPULLLING_TIMEOUT; static const NacosString HB_FAIL_WAIT_TIME; static const NacosString NACOS_SNAPSHOT_PATH; static const NacosString LOG_PATH; static const NacosString LOG_ROTATE_SIZE; static const NacosString LOG_LEVEL; static const NacosString CLIENT_NAME; static const NacosString AUTH_USERNAME; static const NacosString AUTH_PASSWORD; static const NacosString LOCAL_IP; static const NacosString UDP_RECEIVER_PORT; static const int NACOS_DEFAULT_PORT = 8848; static const NacosString INSTANCE_ID_SEQ_FILE; static const NacosString INSTANCE_ID_PREFIX; /*public static class SystemEnv { static const NacosString ALIBABA_ALIWARE_ENDPOINT_PORT = "ALIBABA_ALIWARE_ENDPOINT_PORT"; static const NacosString ALIBABA_ALIWARE_NAMESPACE = "ALIBABA_ALIWARE_NAMESPACE"; static const NacosString ALIBABA_ALIWARE_ENDPOINT_URL = "ALIBABA_ALIWARE_ENDPOINT_URL"; }*/ }; }//namespace nacos #endif ================================================ FILE: include/constant/UtilAndComs.h ================================================ #ifndef __UTIL_N_COMS_H_ #define __UTIL_N_COMS_H_ #include "NacosString.h" namespace nacos{ class UtilAndComs { public: static NacosString VERSION; static NacosString ENCODING; static NacosString NACOS_URL_BASE; static NacosString NACOS_URL_INSTANCE; static int REQUEST_DOMAIN_RETRY_COUNT; static NacosString SERVER_ADDR_IP_SPLITER; static int DEFAULT_CLIENT_BEAT_THREAD_COUNT;//TODO:Calc this according to nr_processors of the host static int DEFAULT_POLLING_THREAD_COUNT;//TODO:Calc this according to nr_processors of the host static void Init(); }; }//namespace nacos #endif ================================================ FILE: include/factory/INacosServiceFactory.h ================================================ // // Created by liuhanyu on 2020/8/30. // #ifndef NACOS_SDK_CPP_INACOSSERVICEFACTORY_H #define NACOS_SDK_CPP_INACOSSERVICEFACTORY_H #include "naming/NamingService.h" #include "naming/NamingMaintainService.h" #include "config/ConfigService.h" #include "Properties.h" namespace nacos{ class INacosServiceFactory { public: virtual void setConfig(const NacosString &_configFile) = 0; virtual void setProps(Properties &_props) = 0; virtual NamingService *CreateNamingService() = 0; virtual ConfigService *CreateConfigService() = 0; virtual NamingMaintainService *CreateNamingMaintainService() = 0; virtual ~INacosServiceFactory() {}; }; }//namespace nacos #endif //NACOS_SDK_CPP_INACOSSERVICEFACTORY_H ================================================ FILE: include/factory/NacosFactoryFactory.h ================================================ #ifndef NACOS_FACTORY_FACTORY_H #define NACOS_FACTORY_FACTORY_H #include "factory/INacosServiceFactory.h" namespace nacos { class NacosFactoryFactory { public: static INacosServiceFactory *getNacosFactory(const NacosString &_configFile); static INacosServiceFactory *getNacosFactory(Properties &_props); }; } #endif ================================================ FILE: include/listen/Listener.h ================================================ #ifndef __LISTENER_H_ #define __LISTENER_H_ #include "NacosString.h" #include "thread/AtomicInt.h" namespace nacos{ class Listener { private: NacosString listenerName; AtomicInt refCount; public: Listener() { this->listenerName = "theListener"; }; int incRef() { return refCount.inc(); }; int decRef() { return refCount.dec(); }; int refCnt() const { return refCount.get(); }; void setListenerName(const NacosString &name) { this->listenerName = name; }; NacosString getListenerName() const { return listenerName; }; virtual void receiveConfigInfo(const NacosString &configInfo) = 0; virtual ~Listener() {}; }; }//namespace nacos #endif ================================================ FILE: include/naming/ChangeAdvice.h ================================================ #ifndef __CHG_ADVICE_H_ #define __CHG_ADVICE_H_ #include "NacosString.h" #include "naming/ServiceInfo.h" //The wrapper class for service change information namespace nacos{ class ChangeAdvice { public: bool added;//indicates Instance is added bool removed;//indicates Instance is removed bool modified;//indicates Instance is modified //std::list addedInstances; //std::list removedInstances; //std::list modifiedInstances; //ServiceInfo oldServiceInfo; ServiceInfo newServiceInfo; NacosString key; public: ChangeAdvice(); ~ChangeAdvice(); static void compareChange(ServiceInfo &oldInfo, ServiceInfo &newInfo, ChangeAdvice &changeAdvice); NacosString toString(); }; }//namespace nacos #endif ================================================ FILE: include/naming/Cluster.h ================================================ #ifndef __CLUSTER_H_ #define __CLUSTER_H_ #include #include "NacosString.h" namespace nacos{ class HealthChecker { private: NacosString type; public: NacosString getType() const { return type; }; void setType(const NacosString &_type) { type = _type; }; }; class Cluster { private: NacosString name; HealthChecker healthChecker; std::map metadata; public: NacosString getName() const; void setName(const NacosString &name); HealthChecker getHealthChecker() const; void setHealthChecker(const HealthChecker &healthChecker); std::map getMetadata() const; void setMetadata(const std::map &metadata); }; }//namespace nacos #endif ================================================ FILE: include/naming/Instance.h ================================================ #ifndef __INSTANCE_H_ #define __INSTANCE_H_ #include #include "NacosString.h" namespace nacos{ //a service instance class Instance { public: Instance & operator = (const Instance &rhs); bool operator == (const Instance &rhs) const; bool operator != (const Instance &rhs) const; Instance(); /** * unique id of this instance. */ NacosString instanceId; /** * instance ip */ NacosString ip; /** * instance port */ int port; /** * instance weight */ double weight; /** * instance health status */ bool healthy; /** * If instance is enabled to accept request */ bool enabled; /** * If instance is ephemeral * * @since 1.0.0 */ bool ephemeral; /** * cluster information of instance */ NacosString clusterName; /** * Service information of instance */ NacosString serviceName; NacosString groupName; NacosString namespaceId; /** * user extended attributes */ std::map metadata; NacosString toString() const; NacosString toInetAddr(); }; }//namespace nacos #endif ================================================ FILE: include/naming/ListView.h ================================================ #ifndef __NACOS_LIST_VUE_H_ #define __NACOS_LIST_VUE_H_ #include #include "NacosString.h" namespace nacos{ template class ListView{ private: int count; std::list data; public: ListView() { count = 0; }; int getCount() const { return count; }; std::list getData() const { return data; }; void setCount(int _count) { count = _count; }; void setData(const std::list &_data) { data = _data; }; }; }//namespace nacos #endif ================================================ FILE: include/naming/NamingMaintainService.h ================================================ #ifndef __NAM_MAINTN_SVC_H_ #define __NAM_MAINTN_SVC_H_ #include #include "naming/Instance.h" #include "naming/ServiceInfo2.h" #include "NacosString.h" #include "NacosExceptions.h" #include "ListView.h" #include "Compatibility.h" namespace nacos{ class NamingMaintainService { public: virtual bool updateInstance(const NacosString &serviceName, const NacosString & groupName, const Instance &instance) NACOS_THROW(NacosException) = 0; virtual ServiceInfo2 queryService(const NacosString &serviceName, const NacosString &groupName) NACOS_THROW(NacosException) = 0; virtual bool createService(const ServiceInfo2 &service, naming::Selector *selector) NACOS_THROW(NacosException) = 0; virtual bool deleteService(const NacosString &serviceName, const NacosString &groupName) NACOS_THROW(NacosException) = 0; virtual bool updateService(const ServiceInfo2 &service, naming::Selector *selector) NACOS_THROW(NacosException) = 0; virtual ~NamingMaintainService() {}; }; }//namespace nacos #endif ================================================ FILE: include/naming/NamingService.h ================================================ #ifndef __NAM_SVC_H_ #define __NAM_SVC_H_ #include #include "naming/Instance.h" #include "naming/selectors/Selector.h" #include "naming/subscribe/EventListener.h" #include "NacosString.h" #include "NacosExceptions.h" #include "ListView.h" #include "Compatibility.h" namespace nacos{ class NamingService { public: /** * register a instance to service * * @param serviceName name of service * @param ip instance ip * @param port instance port * @throw (NacosException) = 0 */ virtual void registerInstance(const NacosString &serviceName, const NacosString &ip, int port) NACOS_THROW(NacosException) = 0; /** * register a instance to service * * @param serviceName name of service * @param groupName group of service * @param ip instance ip * @param port instance port * @throw (NacosException) = 0 */ virtual void registerInstance(const NacosString &serviceName, const NacosString &groupName, const NacosString &ip, int port) NACOS_THROW(NacosException) = 0; /** * register a instance to service with specified cluster name * * @param serviceName name of service * @param ip instance ip * @param port instance port * @param clusterName instance cluster name * @throw (NacosException) = 0 */ virtual void registerInstance(const NacosString &serviceName, const NacosString &ip, int port, const NacosString &clusterName) NACOS_THROW(NacosException) = 0; /** * register a instance to service with specified cluster name * * @param serviceName name of service * @param groupName group of service * @param ip instance ip * @param port instance port * @param clusterName instance cluster name * @throw (NacosException) = 0 */ virtual void registerInstance(const NacosString &serviceName, const NacosString &groupName, const NacosString &ip, int port, const NacosString &clusterName) NACOS_THROW(NacosException) = 0; /** * register a instance to service with specified instance properties * * @param serviceName name of service * @param instance instance to register * @throw (NacosException) = 0 */ virtual void registerInstance(const NacosString &serviceName, Instance &instance) NACOS_THROW(NacosException) = 0; /** * register a instance to service with specified instance properties * * @param serviceName name of service * @param groupName group of service * @param instance instance to register * @throw (NacosException) = 0 */ virtual void registerInstance(const NacosString &serviceName, const NacosString &groupName, Instance &instance) NACOS_THROW(NacosException) = 0; /** * deregister instance from a service * * @param serviceName name of service * @param ip instance ip * @param port instance port * @throw (NacosException) = 0 */ virtual void deregisterInstance(const NacosString &serviceName, const NacosString &ip, int port) NACOS_THROW(NacosException) = 0; /** * deregister instance from a service * * @param serviceName name of service * @param groupName group of service * @param ip instance ip * @param port instance port * @throw (NacosException) = 0 */ virtual void deregisterInstance(const NacosString &serviceName, const NacosString &groupName, const NacosString &ip, int port) NACOS_THROW(NacosException) = 0; /** * deregister instance with specified cluster name from a service * * @param serviceName name of service * @param ip instance ip * @param port instance port * @param clusterName instance cluster name * @throw (NacosException) = 0 */ virtual void deregisterInstance(const NacosString &serviceName, const NacosString &ip, int port, const NacosString &clusterName) NACOS_THROW(NacosException) = 0; /** * deregister instance with specified cluster name from a service * * @param serviceName name of service * @param groupName group of service * @param ip instance ip * @param port instance port * @param clusterName instance cluster name * @throw (NacosException) = 0 */ virtual void deregisterInstance(const NacosString &serviceName, const NacosString &groupName, const NacosString &ip, int port, const NacosString &clusterName) NACOS_THROW(NacosException) = 0; /** * deregister instance with full instance information * * @param serviceName name of service * @param groupName group of service * @param instance instance information * @throw (NacosException) = 0 */ virtual void deregisterInstance(const NacosString &serviceName, const NacosString &groupName, Instance &instance) NACOS_THROW(NacosException) = 0; /** * get all instances of a service * * @param serviceName name of service * @return A list of instance * @throw (NacosException) = 0 */ virtual std::list getAllInstances(const NacosString &serviceName) NACOS_THROW(NacosException) = 0; /** * get all instances of a service * * @param serviceName name of service * @param groupName group of service * @return A list of instance * @throw (NacosException) = 0 */ virtual std::list getAllInstances(const NacosString &serviceName, const NacosString &groupName) NACOS_THROW(NacosException) = 0; /** * Get all instances within specified clusters of a service * * @param serviceName name of service * @param clusters list of cluster * @return A list of qualified instance * @throw (NacosException) = 0 */ virtual std::list getAllInstances(const NacosString &serviceName, const std::list &clusters) NACOS_THROW(NacosException) = 0; /** * Get all instances within specified clusters of a service * * @param serviceName name of service * @param groupName group of service * @param clusters list of cluster * @return A list of qualified instance * @throw (NacosException) = 0 */ virtual std::list getAllInstances(const NacosString &serviceName, const NacosString &groupName, const std::list &clusters) NACOS_THROW(NacosException) = 0; /** * Get instances with the predicate specified * * @param serviceName name of service * @param groupName group of service * @param clusters list of cluster * @param predicate predicate to filter instances * @return A list of qualified instance * @throw (NacosException) = 0 */ virtual std::list getInstanceWithPredicate(const NacosString &serviceName, const NacosString &groupName, const std::list &clusters, nacos::naming::selectors::Selector *predicate) NACOS_THROW(NacosException) = 0; /** * Get instances with the predicate specified * * @param serviceName name of service * @param clusters list of cluster * @param predicate predicate to filter instances * @return A list of qualified instance * @throw (NacosException) = 0 */ virtual std::list getInstanceWithPredicate(const NacosString &serviceName, const std::list &clusters, nacos::naming::selectors::Selector *predicate) NACOS_THROW(NacosException) = 0; /** * Get instances with the predicate specified * * @param serviceName name of service * @param groupName group of service * @param predicate predicate to filter instances * @return A list of qualified instance * @throw (NacosException) = 0 */ virtual std::list getInstanceWithPredicate(const NacosString &serviceName, const NacosString &groupName, nacos::naming::selectors::Selector *predicate) NACOS_THROW(NacosException) = 0; /** * Get instances with the predicate specified * * @param serviceName name of service * @param predicate predicate to filter instances * @return A list of qualified instance * @throw (NacosException) = 0 */ virtual std::list getInstanceWithPredicate(const NacosString &serviceName, nacos::naming::selectors::Selector *predicate) NACOS_THROW(NacosException) = 0; /** * Get qualified instances of service * * @param serviceName name of service * @param healthy a flag to indicate returning healthy or unhealthy instances * @return A qualified list of instance * @throw (NacosException) = 0 */ //virtual std::list selectInstances(const NacosString &serviceName, bool healthy) throw (NacosException) = 0; /** * Get qualified instances of service * * @param serviceName name of service * @param groupName group of service * @param healthy a flag to indicate returning healthy or unhealthy instances * @return A qualified list of instance * @throw (NacosException) = 0 */ //virtual std::list selectInstances(const NacosString &serviceName, const NacosString &groupName, bool healthy) throw (NacosException) = 0; /** * Get qualified instances of service * * @param serviceName name of service * @param healthy a flag to indicate returning healthy or unhealthy instances * @param subscribe if subscribe the service * @return A qualified list of instance * @throw (NacosException) = 0 */ //virtual std::list selectInstances(const NacosString &serviceName, bool healthy, bool subscribe) throw (NacosException) = 0; /** * Get qualified instances of service * * @param serviceName name of service * @param groupName group of service * @param healthy a flag to indicate returning healthy or unhealthy instances * @param subscribe if subscribe the service * @return A qualified list of instance * @throw (NacosException) = 0 */ //virtual std::list selectInstances(const NacosString &serviceName, const NacosString &groupName, bool healthy, bool subscribe) throw (NacosException) = 0; /** * Get qualified instances within specified clusters of service * * @param serviceName name of service * @param clusters list of cluster * @param healthy a flag to indicate returning healthy or unhealthy instances * @return A qualified list of instance * @throw (NacosException) = 0 */ //virtual std::list selectInstances(const NacosString &serviceName, std::list clusters, bool healthy) throw (NacosException) = 0; /** * Get qualified instances within specified clusters of service * * @param serviceName name of service * @param groupName group of service * @param clusters list of cluster * @param healthy a flag to indicate returning healthy or unhealthy instances * @return A qualified list of instance * @throw (NacosException) = 0 */ //virtual std::list selectInstances(const NacosString &serviceName, const NacosString &groupName, std::list clusters, bool healthy) throw (NacosException) = 0; /** * Get qualified instances within specified clusters of service * * @param serviceName name of service * @param clusters list of cluster * @param healthy a flag to indicate returning healthy or unhealthy instances * @param subscribe if subscribe the service * @return A qualified list of instance * @throw (NacosException) = 0 */ //virtual std::list selectInstances(const NacosString &serviceName, std::list clusters, bool healthy, bool subscribe) throw (NacosException) = 0; /** * Get qualified instances within specified clusters of service * * @param serviceName name of service * @param groupName group of service * @param clusters list of cluster * @param healthy a flag to indicate returning healthy or unhealthy instances * @param subscribe if subscribe the service * @return A qualified list of instance * @throw (NacosException) = 0 */ //virtual std::list selectInstances(const NacosString &serviceName, const NacosString &groupName, std::list clusters, bool healthy, bool subscribe) throw (NacosException) = 0; /** * Select one healthy instance of service using predefined load balance strategy * * @param serviceName name of service * @return qualified instance * @throw (NacosException) = 0 */ //virtual Instance selectOneHealthyInstance(const NacosString &serviceName) throw (NacosException) = 0; /** * Select one healthy instance of service using predefined load balance strategy * * @param serviceName name of service * @param groupName group of service * @return qualified instance * @throw (NacosException) = 0 */ //virtual Instance selectOneHealthyInstance(const NacosString &serviceName, const NacosString &groupName) throw (NacosException) = 0; /** * select one healthy instance of service using predefined load balance strategy * * @param serviceName name of service * @param subscribe if subscribe the service * @return qualified instance * @throw (NacosException) = 0 */ //virtual Instance selectOneHealthyInstance(const NacosString &serviceName, bool subscribe) throw (NacosException) = 0; /** * select one healthy instance of service using predefined load balance strategy * * @param serviceName name of service * @param groupName group of service * @param subscribe if subscribe the service * @return qualified instance * @throw (NacosException) = 0 */ //virtual Instance selectOneHealthyInstance(const NacosString &serviceName, const NacosString &groupName, bool subscribe) throw (NacosException) = 0; /** * Select one healthy instance of service using predefined load balance strategy * * @param serviceName name of service * @param clusters a list of clusters should the instance belongs to * @return qualified instance * @throw (NacosException) = 0 */ //virtual Instance selectOneHealthyInstance(const NacosString &serviceName, std::list clusters) throw (NacosException) = 0; /** * Select one healthy instance of service using predefined load balance strategy * * @param serviceName name of service * @param groupName group of service * @param clusters a list of clusters should the instance belongs to * @return qualified instance * @throw (NacosException) = 0 */ //virtual Instance selectOneHealthyInstance(const NacosString &serviceName, const NacosString &groupName, std::list clusters) throw (NacosException) = 0; /** * Select one healthy instance of service using predefined load balance strategy * * @param serviceName name of service * @param clusters a list of clusters should the instance belongs to * @param subscribe if subscribe the service * @return qualified instance * @throw (NacosException) = 0 */ //virtual Instance selectOneHealthyInstance(const NacosString &serviceName, std::list clusters, bool subscribe) throw (NacosException) = 0; /** * Select one healthy instance of service using predefined load balance strategy * * @param serviceName name of service * @param groupName group of service * @param clusters a list of clusters should the instance belongs to * @param subscribe if subscribe the service * @return qualified instance * @throw (NacosException) = 0 */ //virtual Instance selectOneHealthyInstance(const NacosString &serviceName, const NacosString &groupName, std::list clusters, bool subscribe) throw (NacosException) = 0; /** * Subscribe service to receive events of instances alteration * * @param serviceName name of service * @param listener event listener * @throw (NacosException) = 0 */ virtual void subscribe(const NacosString &serviceName, EventListener *listener) NACOS_THROW (NacosException) = 0; /** * Subscribe service to receive events of instances alteration * * @param serviceName name of service * @param groupName group of service * @param listener event listener * @throw (NacosException) = 0 */ virtual void subscribe(const NacosString &serviceName, const NacosString &groupName, EventListener *listener) NACOS_THROW (NacosException) = 0; /** * Subscribe service to receive events of instances alteration * * @param serviceName name of service * @param clusters list of cluster * @param listener event listener * @throw (NacosException) = 0 */ virtual void subscribe(const NacosString &serviceName, const std::list &clusters, EventListener *listener) NACOS_THROW (NacosException) = 0; /** * Subscribe service to receive events of instances alteration * * @param serviceName name of service * @param groupName group of service * @param clusters list of cluster * @param listener event listener * @throw (NacosException) = 0 */ virtual void subscribe(const NacosString &serviceName, const NacosString &groupName, const std::list &clusters, EventListener *listener) NACOS_THROW (NacosException) = 0; /** * Unsubscribe event listener of service * * @param serviceName name of service * @param listener event listener * @throw (NacosException) = 0 */ virtual void unsubscribe(const NacosString &serviceName, EventListener *listener) NACOS_THROW (NacosException) = 0; /** * unsubscribe event listener of service * * @param serviceName name of service * @param groupName group of service * @param listener event listener * @throw (NacosException) = 0 */ virtual void unsubscribe(const NacosString &serviceName, const NacosString &groupName, EventListener *listener) NACOS_THROW (NacosException) = 0; /** * Unsubscribe event listener of service * * @param serviceName name of service * @param clusters list of cluster * @param listener event listener * @throw (NacosException) = 0 */ virtual void unsubscribe(const NacosString &serviceName, const std::list &clusters, EventListener *listener) NACOS_THROW (NacosException) = 0; /** * Unsubscribe event listener of service * * @param serviceName name of service * @param groupName group of service * @param clusters list of cluster * @param listener event listener * @throw (NacosException) = 0 */ virtual void unsubscribe(const NacosString &serviceName, const NacosString &groupName, const std::list &clusters, EventListener *listener) NACOS_THROW (NacosException) = 0; /** * Get all service names from server * * @param pageNo page index * @param pageSize page size * @return list of service names * @throw (NacosException) = 0 */ virtual ListView getServiceList(int pageNo, int pageSize) NACOS_THROW (NacosException) = 0; /** * Get all service names from server * * @param pageNo page index * @param pageSize page size * @param groupName group name * @return list of service names * @throw (NacosException) = 0 */ virtual ListView getServiceList(int pageNo, int pageSize, const NacosString &groupName) NACOS_THROW (NacosException) = 0; /** * Get all service names from server with selector * * @param pageNo page index * @param pageSize page size * @param selector selector to filter the resource * @return list of service names * @throw (NacosException) = 0 * @since 0.7.0 */ //virtual ListView getServicesOfServer(int pageNo, int pageSize, AbstractSelector selector) throw (NacosException) = 0; /** * Get all service names from server with selector * * @param pageNo page index * @param pageSize page size * @param groupName group name * @param selector selector to filter the resource * @return list of service names * @throw (NacosException) = 0 */ //virtual ListView getServicesOfServer(int pageNo, int pageSize, const NacosString &groupName, AbstractSelector selector) throw (NacosException) = 0; /** * Get all subscribed services of current client * * @return subscribed services * @throw (NacosException) = 0 */ //virtual std::list getSubscribeServices() throw (NacosException) = 0; /** * get server health status * * @return is server healthy */ //virtual NacosString getServerStatus() = 0; virtual ~NamingService() {}; }; }//namespace nacos #endif ================================================ FILE: include/naming/ServiceInfo.h ================================================ #ifndef __SVC_INFO_H_ #define __SVC_INFO_H_ #include #include "NacosString.h" #include "naming/Instance.h" namespace nacos{ class ServiceInfo { private: //@JSONField(serialize = false) NacosString _jsonFromServer; NacosString _name; NacosString _groupName; NacosString _clusters; long _cacheMillis; //@JSONField(name = "hosts") std::list _hosts; long _lastRefTime; NacosString _checksum; volatile bool _allIPs; public: ServiceInfo(); bool isAllIPs() const; void setAllIPs(bool allIPs); explicit ServiceInfo(const NacosString &key); ServiceInfo(const NacosString &name, const NacosString &clusters); int ipCount(); bool expired() const; void setHosts(std::list &hosts); bool isValid(); NacosString getName(); void setName(const NacosString &name); NacosString getGroupName(); void setGroupName(const NacosString &groupName); void setLastRefTime(long lastRefTime); long getLastRefTime(); NacosString getClusters(); void setClusters(const NacosString &clusters); long getCacheMillis(); void setCacheMillis(long cacheMillis); std::list getHosts(); std::list *getHostsNocopy(); bool validate() const; //@JSONField(serialize = false) NacosString getJsonFromServer() const; void setJsonFromServer(const NacosString &jsonFromServer); //@JSONField(serialize = false) NacosString getKey() const; //@JSONField(serialize = false) NacosString getKeyEncoded() const; //@JSONField(serialize = false) static void fromKey(ServiceInfo &serviceInfo, const NacosString &key); //@JSONField(serialize = false) static NacosString getKey(const NacosString &name, const NacosString &clusters); //@Override NacosString toString() const; //!!BE CAREFUL!! //This function is very expensive!! call it with care! NacosString toInstanceString() const; NacosString getChecksum() const; void setChecksum(const NacosString &checksum); }; }//namespace nacos #endif ================================================ FILE: include/naming/ServiceInfo2.h ================================================ #ifndef __SVC_INFO_2_H_ #define __SVC_INFO_2_H_ #include #include "NacosString.h" #include "naming/Cluster.h" namespace nacos{ namespace naming { class Selector { private: NacosString type; public: NacosString getType() const { return type; }; void setType(const NacosString &_type) { type = _type; }; virtual NacosString getSelectorString() = 0; }; } /*naming*/ //Actually this should be exactly the same as ServiceInfo but I designed this for compatibility //If you check the API reference in detail you'll find that: //the ServiceInfo returned by /nacos/v1/ns/service differs from the one returned by /nacos/v1/ns/instance/list //for more details, please refer : //https://nacos.io/zh-cn/docs/open-api.html#2.4 (Query instances) //https://nacos.io/zh-cn/docs/open-api.html#2.11 (Query service list) class ServiceInfo2 { private: bool nullObj; bool namespaceIdIsSet; NacosString namespaceId; bool groupNameIsSet; NacosString groupName; bool nameIsSet; NacosString name; bool selectorIsSet; NacosString selector; bool protectThresholdIsSet; double protectThreshold; bool clustersIsSet; std::list clusters; bool metadataIsSet; std::map metadata; public: static ServiceInfo2 nullServiceInfo2; ServiceInfo2() { nullObj = true; namespaceIdIsSet = false; groupNameIsSet = false; nameIsSet = false; selectorIsSet = false; protectThresholdIsSet = false; clustersIsSet = false; metadataIsSet = false; } void setNull(bool _isNull) { nullObj = _isNull; }; bool isNullObject() const { return nullObj; }; const NacosString &getNamespaceId() const { return namespaceId; } void setNamespaceId(const NacosString &namespaceId) { namespaceIdIsSet = true; ServiceInfo2::namespaceId = namespaceId; } const NacosString &getGroupName() const { return groupName; } void setGroupName(const NacosString &groupName) { groupNameIsSet = true; ServiceInfo2::groupName = groupName; } const NacosString &getName() const { return name; } void setName(const NacosString &name) { nameIsSet = true; ServiceInfo2::name = name; } const NacosString &getSelector() const { return selector; } void setSelector(const NacosString &aSelector) { selectorIsSet = true; selector = aSelector; } int getProtectThreshold() const { return protectThreshold; } void setProtectThreshold(double protectThreshold) { protectThresholdIsSet = true; ServiceInfo2::protectThreshold = protectThreshold; } const std::list &getClusters() const { return clusters; } void setClusters(const std::list &clusters) { clustersIsSet = true; ServiceInfo2::clusters = clusters; } const std::map &getMetadata() const { return metadata; } void setMetadata(const std::map &metadata) { metadataIsSet = true; ServiceInfo2::metadata = metadata; } bool isNamespaceIdSet() const { return namespaceIdIsSet; } bool isGroupNameSet() const { return groupNameIsSet; } bool isNameSet() const { return nameIsSet; } bool isSelectorSet() const { return selectorIsSet; } bool isProtectThresholdSet() const { return protectThresholdIsSet; } bool isClustersSet() const { return clustersIsSet; } bool isMetadataSet() const { return metadataIsSet; } }; }/*namespace nacos*/ #endif ================================================ FILE: include/naming/selectors/HealthInstanceSelector.h ================================================ #ifndef __HEALTH_INST_SELECTOR_H_ #define __HEALTH_INST_SELECTOR_H_ #include #include "naming/selectors/Selector.h" namespace nacos { namespace naming { namespace selectors { class HealthInstanceSelector : public Selector{ private: public: std::list select(const std::list &instancesToSelect); }; } /*selectors*/ } /*naming*/ }/*nacos*/ #endif ================================================ FILE: include/naming/selectors/RandomByWeightSelector.h ================================================ #ifndef __WEIGHTED_RND_SELECTOR_H_ #define __WEIGHTED_RND_SELECTOR_H_ #include #include "naming/selectors/Selector.h" #define BASIC_WEIGHT 65536 namespace nacos { namespace naming { namespace selectors { class RandomByWeightSelector : public Selector{ private: public: std::list select(const std::list &instancesToSelect); }; } /*selectors*/ } /*naming*/ }/*nacos*/ #endif ================================================ FILE: include/naming/selectors/RandomSelector.h ================================================ #ifndef __RND_SELECTOR_H_ #define __RND_SELECTOR_H_ #include #include "naming/selectors/Selector.h" namespace nacos { namespace naming { namespace selectors { class RandomSelector : public Selector{ private: public: std::list select(const std::list &instancesToSelect); }; } /*selectors*/ } /*naming*/ }/*nacos*/ #endif ================================================ FILE: include/naming/selectors/Selector.h ================================================ #ifndef __SELECTOR_H_ #define __SELECTOR_H_ #include #include "naming/Instance.h" namespace nacos { namespace naming { namespace selectors { //Selector interface definition //All Selectors shall be THREAD-SAFE! template class Selector { private: public: virtual std::list select(const std::list &itemsToSelect) = 0; virtual ~Selector() {}; }; } /*selectors*/ } /*naming*/ }/*nacos*/ #endif ================================================ FILE: include/naming/subscribe/EventListener.h ================================================ // // Created by liuhanyu on 2020/9/24. // #ifndef NACOS_SDK_CPP_EVENTLISTENER_H #define NACOS_SDK_CPP_EVENTLISTENER_H #include "NacosString.h" #include "thread/AtomicInt.h" #include "naming/ChangeAdvice.h" namespace nacos{ class EventListener { private: NacosString listenerName; AtomicInt refCount; public: EventListener() { this->listenerName = "theListener"; }; int incRef() { return refCount.inc(); }; int decRef() { return refCount.dec(); }; int refCnt() const { return refCount.get(); }; void setListenerName(const NacosString &name) { this->listenerName = name; }; NacosString getListenerName() const { return listenerName; }; virtual void receiveNamingInfo(const ServiceInfo &changeAdvice) = 0; virtual ~EventListener(); }; }//namespace nacos #endif //NACOS_SDK_CPP_EVENTLISTENER_H ================================================ FILE: include/server/ServerSelector.h ================================================ #ifndef NACOS_SDK_CPP_SERVERSELECTOR_H #define NACOS_SDK_CPP_SERVERSELECTOR_H #include "NacosServerInfo.h" #include /* * ServerSelector * Author: Liu, Hanyu * Selector interface, to select one server from the select list * User can implement different selecting strategies (e.g.: Weighed, Random, Least used) by implementing select() method */ namespace nacos{ class ServerSelector { public: virtual NacosServerInfo select(std::list &serverList) = 0; virtual ~ServerSelector() {}; }; }//namespace nacos #endif //NACOS_SDK_CPP_SERVERSELECTOR_H ================================================ FILE: include/thread/AtomicInt.h ================================================ #ifndef __ATOMIC_INT_H_ #define __ATOMIC_INT_H_ namespace nacos{ template class AtomicInt { private: volatile T _curval; public: AtomicInt(T curval = 0) : _curval(curval) {}; void set(T val) { _curval = val; }; T inc(T incval = 1) { T oldValue = getAndInc(incval); return incval + oldValue; }; T getAndInc(T incval = 1) { T oldValue = __sync_fetch_and_add(&_curval, incval); return oldValue; } T dec(int decval = 1) { return inc(-decval); }; T get() const { return _curval; }; }; }//namespace nacos #endif ================================================ FILE: nacos-cpp-cli.properties ================================================ #!!please NOTE that the file format for this properties should be UNIX!! #specify nacos server address here(separated by ,) #serverAddr= #log path, default is ~/nacos/logs/ #nacos.log.path= #rotate size (number [+ unit], unit can be k/K m/M g/G. the size is regarded as byte if there is no unit) #nacos.log.rotateSize=100m #log level, default is ERROR #nacos.log.level=ERROR #client IP, the client will automatically detect NIC and find the best IP #but if that doesn't work, you can override it here manually #nacos.client.ip= #if the server is secured with password, you may specify it here #nacos.auth.username= #nacos.auth.password= #specify this if you are using an endpoint #endpoint= #endpointPort= #specify context path for nacos server ("nacos" by default) #nacos.server.contextpath= #advanced settings #port for udp listener (to receive service change information for subscribed services) #nacos.udp.port= #poller interval (10000 ms by default) to refresh subscribed service information from server #naming.poller.interval= #accessKey/secretKey for SPAS #accessKey= #secretKey= ================================================ FILE: src/NacosExceptions.cpp ================================================ #include "NacosExceptions.h" namespace nacos{ NacosException::NacosException(int errorcode, const char *errormsg) NACOS_NOTHROW() { _errcode = errorcode; _errmsg = errormsg; } NacosException::NacosException(int errorcode, const NacosString &errormsg) NACOS_NOTHROW() { _errcode = errorcode; _errmsg = errormsg; } }//namespace nacos ================================================ FILE: src/NacosString.cpp ================================================ #include #include #include #include "NacosString.h" namespace nacos{ const NacosString NacosStringOps::nullstr = ""; const NacosString NacosStringOps::STR_TRUE = "true"; const NacosString NacosStringOps::STR_FALSE = "false"; //Returns true if str refers to nullstr bool NacosStringOps::isNullStr(const NacosString &str) { return (&str == &nullstr) || (str == ""); } template<> NacosString NacosStringOps::valueOf(bool val) { if (val) { return STR_TRUE; } else { return STR_FALSE; } } }//namespace nacos ================================================ FILE: src/config/AppConfigManager.cpp ================================================ #include "src/utils/ParamUtils.h" #include "AppConfigManager.h" #include "NacosString.h" #include "Properties.h" #include "src/utils/NetUtils.h" #include "constant/PropertyKeyConst.h" #include "NacosExceptions.h" #include "src/utils/DirUtils.h" #include #include #include "src/log/Logger.h" #include "src/utils/ConfigParserUtils.h" #include "src/utils/Env.h" using namespace std; namespace nacos{ bool AppConfigManager::nacosAuthEnabled() { if (contains(PropertyKeyConst::AUTH_USERNAME) && contains(PropertyKeyConst::AUTH_PASSWORD)) { return true; } else { return false; } } void AppConfigManager::checkReloadable() NACOS_THROW(NacosException) { if (!reloadable) { throw NacosException(0, "This object is initialized as a non-reloadable one"); } } size_t AppConfigManager::loadConfig() NACOS_THROW(NacosException) { checkReloadable(); initDefaults(); Properties parsedConfig = ConfigParserUtils::parseConfigFile(configFile); applyConfig(parsedConfig); log_debug("[AppConfigManager]-loadConfig:loaded config file:%s\n", appConfig.toString().c_str()); return appConfig.size(); } size_t AppConfigManager::loadConfig(const NacosString &_configFile) NACOS_THROW(NacosException) { checkReloadable(); configFile = _configFile; loadConfig(); return appConfig.size(); } void AppConfigManager::clearConfig() { appConfig.clear(); } const NacosString& AppConfigManager::get(const NacosString &key) { if (appConfig.count(key) == 0) { return NULLSTR; } if (key.compare(PropertyKeyConst::NAMESPACE) == 0 && appConfig[PropertyKeyConst::NAMESPACE].compare("Public") == 0) { return NULLSTR; } Properties::iterator iter = appConfig.find(key); if (iter == appConfig.end()) { return NULLSTR; } return iter->second; } void AppConfigManager::set(const NacosString &key, const NacosString &value) { //Special case handle if (key.compare(PropertyKeyConst::SERVER_REQ_TIMEOUT) == 0) { _serverReqTimeout = atoi(value.c_str()); } else if (key.compare(PropertyKeyConst::CONTEXT_PATH) == 0) { _contextPath = value; } appConfig[key] = value; } bool AppConfigManager::contains(const std::string &key) const { return appConfig.contains(key); } AppConfigManager::AppConfigManager(Properties &props) { reloadable = false; initDefaults(); applyConfig(props); } AppConfigManager::AppConfigManager(const NacosString &_configFile) { reloadable = true; configFile = _configFile; } NacosString getAppNameFromEnv() { const char* env = getEnv("APP_NAME"); if (env != NULL && std::char_traits::length(env) > 0) { return NacosString(env); } return NacosStringOps::nullstr; } void AppConfigManager::initDefaults() { appConfig.clear(); //appConfig[PropertyKeyConst::NAMESPACE] = "public"; set(PropertyKeyConst::SRVLISTMGR_REFRESH_INTERVAL, "30000"); set(PropertyKeyConst::SERVER_REQ_TIMEOUT, "3000"); set(PropertyKeyConst::CONTEXT_PATH, ConfigConstant::DEFAULT_CONTEXT_PATH); set(PropertyKeyConst::SUBSCRIPTION_POLL_INTERVAL, "10000");//10 secs by default set(PropertyKeyConst::CONFIG_LONGPULLLING_TIMEOUT, "30000");//ms set(PropertyKeyConst::HB_FAIL_WAIT_TIME, "20000");//ms set(PropertyKeyConst::CLIENT_NAME, "default"); set(PropertyKeyConst::LOCAL_IP, NetUtils::getHostIp()); set(PropertyKeyConst::UDP_RECEIVER_PORT, "30620"); NacosString appName = getAppNameFromEnv(); if (!NacosStringOps::isNullStr(appName)) { set(PropertyKeyConst::APP_NAME, appName); } NacosString homedir = DirUtils::getHome(); set(PropertyKeyConst::INSTANCE_ID_PREFIX, NetUtils::getHostName()); set(PropertyKeyConst::INSTANCE_ID_SEQ_FILE, homedir + ConfigConstant::FILE_SEPARATOR + "nacos" + ConfigConstant::FILE_SEPARATOR + "instance_seq.dat"); set(PropertyKeyConst::NACOS_SNAPSHOT_PATH, homedir + ConfigConstant::FILE_SEPARATOR + "nacos" + ConfigConstant::FILE_SEPARATOR + "snapshot"); log_info("[AppConfigManager]-initDefaults:DEFAULT_SNAPSHOT_PATH:%s\n", appConfig[PropertyKeyConst::NACOS_SNAPSHOT_PATH].c_str()); } void AppConfigManager::applyConfig(Properties &rhs) { for (map::iterator it = rhs.begin(); it != rhs.end(); it++) { set(it->first, it->second); } } }//namespace nacos ================================================ FILE: src/config/AppConfigManager.h ================================================ #ifndef __APP_CFG_MGR_H_ #define __APP_CFG_MGR_H_ #include "NacosExceptions.h" #include "NacosString.h" #include "Properties.h" #include "Compatibility.h" namespace nacos{ class AppConfigManager { private: bool reloadable; Properties appConfig; NacosString configFile; //Cached contextpath NacosString _contextPath; volatile long _serverReqTimeout; AppConfigManager(); void checkReloadable() NACOS_THROW(NacosException); void initDefaults(); void applyConfig(Properties &rhs); public: bool nacosAuthEnabled(); long getServeReqTimeout() const { return _serverReqTimeout; }; bool isReloadable() const { return reloadable; }; AppConfigManager(Properties &props); AppConfigManager(const NacosString &configFile); size_t loadConfig(const NacosString &configFile) NACOS_THROW(NacosException); size_t loadConfig() NACOS_THROW(NacosException); void clearConfig(); const NacosString &get(const NacosString &key); bool contains(const NacosString &key) const; const Properties& getAllConfig() { return appConfig; }; const NacosString & getContextPath() const { return _contextPath; }; void set(const NacosString &key, const NacosString &value); }; }//namespace nacos #endif ================================================ FILE: src/config/ConcurrentDiskUtil.cpp ================================================ #include #include #include #include #include "ConcurrentDiskUtil.h" #include "IOUtils.h" /** * get file content * * @param file file * @param charsetName charsetName * @return content * @throws IOException IOException */ namespace nacos{ NacosString ConcurrentDiskUtil::getFileContent(const NacosString &file, const NacosString &charsetName) NACOS_THROW(IOException) { if (IOUtils::checkNotExistOrNotFile(file)) { //TODO:add errorcode throw IOException(NacosException::FILE_NOT_FOUND, "checkNotExistOrNotFile failed, unable to access the file, maybe it doesn't exist."); } size_t toRead = IOUtils::getFileSize(file); FILE *fp = fopen(file.c_str(), "rb"); if (fp == NULL) { char errbuf[100]; snprintf(errbuf, sizeof(errbuf), "Failed to open file for read, errno: %d", errno); //TODO:add errorcode throw IOException(NacosException::UNABLE_TO_OPEN_FILE, errbuf); } flock(fileno(fp), LOCK_SH); char buf[toRead + 1]; fread(buf, toRead, 1, fp); buf[toRead] = '\0'; flock(fileno(fp), LOCK_UN); fclose(fp); return NacosString(buf); } /** * write file content * * @param file file * @param content content * @param charsetName charsetName * @return whether write ok * @throws IOException IOException */ bool ConcurrentDiskUtil::writeFileContent ( const NacosString &path, const NacosString &content, const NacosString &charsetName ) NACOS_THROW(IOException) { FILE *fp = fopen(path.c_str(), "wb"); if (fp == NULL) { char errbuf[100]; snprintf(errbuf, sizeof(errbuf), "Failed to open file for write, errno: %d", errno); //TODO:add errorcode throw IOException(NacosException::UNABLE_TO_OPEN_FILE, errbuf); } flock(fileno(fp), LOCK_SH); fwrite(content.c_str(), content.size(), 1, fp); flock(fileno(fp), LOCK_UN); fclose(fp); return true; } }//namespace nacos ================================================ FILE: src/config/ConcurrentDiskUtil.h ================================================ #ifndef __CONC_DISK_UTIL_H_ #define __CONC_DISK_UTIL_H_ #include "NacosString.h" #include "NacosExceptions.h" #include "Compatibility.h" namespace nacos{ class ConcurrentDiskUtil { public: /** * get file content * * @param file file * @param charsetName charsetName * @return content * @throws IOException IOException */ static NacosString getFileContent(const NacosString &file, const NacosString &charsetName) NACOS_THROW(IOException); /** * write file content * * @param file file * @param content content * @param charsetName charsetName * @return whether write ok * @throws IOException IOException */ static bool writeFileContent(const NacosString &path, const NacosString &content, const NacosString &charsetName) NACOS_THROW(IOException); }; }//namespace nacos #endif ================================================ FILE: src/config/ConfigProxy.cpp ================================================ // // Created by liuhanyu on 2021/7/6. // #include "ConfigProxy.h" #include "src/http/HttpDelegate.h" #include "src/utils/ParamUtils.h" #include "src/utils/TimeUtils.h" #include "src/config/AppConfigManager.h" #include "constant/PropertyKeyConst.h" #include "src/crypto/SignatureTool.h" namespace nacos { NacosString ConfigProxy::getDataToSign(const std::list ¶mValues, const NacosString &nowTimeMs) { const NacosString & group = ParamUtils::findByKey(paramValues, "group"); const NacosString & tenant = ParamUtils::findByKey(paramValues, "tenant"); NacosString dataToSign = ""; if (!NacosStringOps::isNullStr(tenant)) { dataToSign = tenant + "+"; } if (!NacosStringOps::isNullStr(group)) { dataToSign += group; } if (!NacosStringOps::isNullStr(dataToSign)) { dataToSign += "+" + nowTimeMs; } else { dataToSign = nowTimeMs; } return dataToSign; } HttpResult ConfigProxy::reqAPI ( int method, const NacosString &path, std::list &headers, std::list ¶mValues, const NacosString &encoding, long readTimeoutMs ) NACOS_THROW(NetworkException) { HttpDelegate *_httpDelegate = _objectConfigData->_httpDelegate; //TODO: refactor to a common procedure const NacosString& secretKey = _objectConfigData->_appConfigManager->get(PropertyKeyConst::SECRET_KEY); const NacosString& accessKey = _objectConfigData->_appConfigManager->get(PropertyKeyConst::ACCESS_KEY); const NacosString& appName = _objectConfigData->_appConfigManager->get(PropertyKeyConst::APP_NAME); //If SPAS security credentials are set, SPAS is enabled if (!ParamUtils::isBlank(secretKey) && !ParamUtils::isBlank(accessKey)) { NacosString nowTimeMs = NacosStringOps::valueOf(TimeUtils::getCurrentTimeInMs()); NacosString dataToSign = getDataToSign(paramValues, nowTimeMs); NacosString signature = SignatureTool::SignWithHMAC_SHA1(dataToSign, secretKey); ParamUtils::addKV(headers, "Spas-Signature", signature); ParamUtils::addKV(headers, "Timestamp", nowTimeMs); ParamUtils::addKV(headers, "Spas-AccessKey", accessKey); } if (!NacosStringOps::isNullStr(appName)) { ParamUtils::addKV(headers, "Client-AppName", appName); } switch (method) { case IHttpCli::GET: return _httpDelegate->httpGet(path, headers, paramValues, encoding, readTimeoutMs); case IHttpCli::POST: return _httpDelegate->httpPost(path, headers, paramValues, encoding, readTimeoutMs); case IHttpCli::PUT: return _httpDelegate->httpPut(path, headers, paramValues, encoding, readTimeoutMs); case IHttpCli::DELETE: return _httpDelegate->httpDelete(path, headers, paramValues, encoding, readTimeoutMs); default://should never happen abort(); } } } ================================================ FILE: src/config/ConfigProxy.h ================================================ // // Created by liuhanyu on 2021/7/6. // #ifndef NACOS_SDK_CPP_CONFIGPROXY_H #define NACOS_SDK_CPP_CONFIGPROXY_H #include "src/factory/ObjectConfigData.h" #include "src/http/IHttpCli.h" namespace nacos { class ConfigProxy { private: ObjectConfigData *_objectConfigData; NacosString getDataToSign(const std::list ¶mValues, const NacosString &nowTimeMs); public: ConfigProxy(ObjectConfigData *objectConfigData) : _objectConfigData(objectConfigData) {}; HttpResult reqAPI(int method, const NacosString &path, std::list &headers, std::list ¶mValues, const NacosString &encoding, long readTimeoutMs) NACOS_THROW(NetworkException); }; } #endif //NACOS_SDK_CPP_CONFIGPROXY_H ================================================ FILE: src/config/IOUtils.cpp ================================================ #include "IOUtils.h" #include #include #include #include #include #include #include #include #include #include "src/log/Logger.h" namespace nacos{ size_t IOUtils::getFileSize(const NacosString &file) { struct stat statbuf; if (stat(file.c_str(), &statbuf) == -1) { return 0; } return statbuf.st_size; } NacosString IOUtils::readStringFromFile(const NacosString &file, const NacosString &encoding) NACOS_THROW(IOException) { size_t toRead = getFileSize(file); FILE *fp = fopen(file.c_str(), "rb"); if (fp == NULL) { throw IOException(NacosException::FILE_NOT_FOUND, "File not found:" + file); } char buf[toRead + 1]; fread(buf, toRead, 1, fp); buf[toRead] = '\0'; fclose(fp); return NacosString(buf); } void IOUtils::writeStringToFile(const NacosString &file, const NacosString &data, const NacosString &encoding) NACOS_THROW(IOException) { FILE *fp = fopen(file.c_str(), "wb"); fwrite(data.c_str(), data.size(), 1, fp); fclose(fp); } //Returns true if: //a. the file doesn't exist //b. the file is not a regular file bool IOUtils::checkNotExistOrNotFile(const NacosString &pathname) { struct stat thestat = {0}; int res = stat(pathname.c_str(), &thestat); if (res != 0) { if (errno == ENOENT) { //a. the file doesn't exist return true; } else { //Maybe something's wrong with the permission //Anyway, we have no access to this file return true; } } if (!S_ISREG(thestat.st_mode)) { //b. the file is not a regular file return true; } else { //This IS a regular file return false; } } //Returns true if: //a. the file doesn't exist //b. the file is not a directory bool IOUtils::checkNotExistOrNotDir(const NacosString &pathname) { struct stat thestat = {0}; int res = stat(pathname.c_str(), &thestat); if (res != 0) { if (errno == ENOENT) { //a. the file doesn't exist return true; } else { //Maybe something's wrong with the permission //Anyway, we have no access to this file return true; } } if (!S_ISDIR(thestat.st_mode)) { //b. the file is not a directory return true; } else { //This IS a directory return false; } } //TODO:To provide compability across different platforms NacosString IOUtils::getParentFile(const NacosString &thefile) { size_t parentFilePos = thefile.rfind('/'); //Invalid Directory/Filename, returning empty if (parentFilePos == std::string::npos || parentFilePos == 0) { return NULLSTR; } NacosString parentFile = thefile.substr(0, parentFilePos); return parentFile; } //Upon success, return true //Upon failure, return false bool IOUtils::recursivelyRemove(const NacosString &file) { struct stat thestat; if (stat(file.c_str(), &thestat) == -1 && errno != ENOENT) { //Something's wrong, and it's not "FileNotExist", we should record this and exit log_error("Failed to stat() file, errno: %d\n", errno); return false; } if (S_ISDIR(thestat.st_mode)) { DIR *curdir = opendir(file.c_str()); struct dirent *direntp = readdir(curdir); while (direntp != NULL) { if (!strcmp(direntp->d_name, ".") || !strcmp(direntp->d_name, "..")) { //skip this dir and parent direntp = readdir(curdir); continue; } struct stat subfilestat; NacosString subfilepath = file + "/" + direntp->d_name; if (stat(subfilepath.c_str(), &subfilestat) == -1 && errno != ENOENT) { log_error("Failed to stat() file, errno: %d\n", errno); closedir(curdir); return false; } if (S_ISREG(subfilestat.st_mode)) { remove(subfilepath.c_str()); } else if (S_ISDIR(subfilestat.st_mode)) { recursivelyRemove(subfilepath); } //get to the next entry direntp = readdir(curdir); } closedir(curdir); remove(file.c_str()); } else if (S_ISREG(thestat.st_mode)) { remove(file.c_str()); } return true; } bool IOUtils::cleanDirectory(const NacosString &file) { struct stat thestat; if (stat(file.c_str(), &thestat) == -1 && errno != ENOENT) { //Something's wrong, and it's not "FileNotExist", we should record this and exit log_error("Failed to stat() file, errno: %d\n", errno); return false; } if (!S_ISDIR(thestat.st_mode)) { log_error("Call cleanDirectory() on non-directory entity: %s\n", file.c_str()); return false; } DIR *curdir = opendir(file.c_str()); struct dirent *direntp = readdir(curdir); while (direntp != NULL) { if (!strcmp(direntp->d_name, ".") || !strcmp(direntp->d_name, "..")) { //skip this dir and parent direntp = readdir(curdir); continue; } NacosString subfilepath = file + "/" + direntp->d_name; recursivelyRemove(subfilepath); //get to the next entry direntp = readdir(curdir); } closedir(curdir); return true; } void IOUtils::recursivelyCreate(const NacosString &file) { NacosString parentFile = getParentFile(file); if (!isNull(parentFile)) { recursivelyCreate(parentFile); } if (checkNotExistOrNotDir(file)) { mkdir(file.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); } } std::list IOUtils::listFiles(const NacosString &path) { struct stat thestat; std::list filelist; if (stat(path.c_str(), &thestat) == -1 && errno != ENOENT) { //Something's wrong, and it's not "FileNotExist", we should record this and exit log_error("Failed to stat() file, errno: %d\n", errno); return filelist; } if (!S_ISDIR(thestat.st_mode)) { log_error("Call listFiles() on non-directory entity: %s\n", path.c_str()); return filelist; } DIR *curdir = opendir(path.c_str()); struct dirent *direntp = readdir(curdir); while (direntp != NULL) { if (!strcmp(direntp->d_name, ".") || !strcmp(direntp->d_name, "..")) { //skip this dir and parent direntp = readdir(curdir); continue; } NacosString curitem = direntp->d_name; filelist.push_back(curitem); //get to the next entry direntp = readdir(curdir); } closedir(curdir); return filelist; } }//namespace nacos ================================================ FILE: src/config/IOUtils.h ================================================ #ifndef __IO_UTILS_H_ #define __IO_UTILS_H_ #include #include "NacosString.h" #include "NacosExceptions.h" #include "Compatibility.h" namespace nacos{ class IOUtils { private: public: static size_t getFileSize(const NacosString &file); static NacosString readStringFromFile(const NacosString &file, const NacosString &encoding) NACOS_THROW(IOException); static void writeStringToFile(const NacosString &file, const NacosString &data, const NacosString &encoding) NACOS_THROW(IOException); //Returns true if: //a. the file doesn't exist //b. the file is not a regular file static bool checkNotExistOrNotFile(const NacosString &pathname); //Returns true if: //a. the file doesn't exist //b. the file is not a directory static bool checkNotExistOrNotDir(const NacosString &pathname); //TODO:To provide compability across different platforms static NacosString getParentFile(const NacosString &thefile); //Upon success, return true //Upon failure, return false static bool recursivelyRemove(const NacosString &file); static bool cleanDirectory(const NacosString &file); static void recursivelyCreate(const NacosString &file); static std::list listFiles(const NacosString &path); }; }//namespace nacos #endif ================================================ FILE: src/config/JVMUtil.h ================================================ #ifndef __JVMUTIL_H_ #define __JVMUTIL_H_ namespace nacos{ class JVMUtil { public: /** * whether is multi instance * * @return whether multi */ static bool isMultiInstance() { return _isMultiInstance; }; private: static bool _isMultiInstance; /*static { NacosString multiDeploy = System.getProperty("isMultiInstance", "false"); if (TRUE.equals(multiDeploy)) { isMultiInstance = true; } LOGGER.info("isMultiInstance:{}", isMultiInstance); }*/ }; }//namespace nacos #endif ================================================ FILE: src/config/LocalSnapshotManager.cpp ================================================ #include "LocalSnapshotManager.h" #include #include #include #include #include #include "NacosExceptions.h" #include "NacosString.h" #include "src/utils/ParamUtils.h" #include "src/utils/DirUtils.h" #include "SnapShotSwitch.h" #include "JVMUtil.h" #include "ConcurrentDiskUtil.h" #include "IOUtils.h" #include "constant/ConfigConstant.h" #include "constant/PropertyKeyConst.h" #include "src/log/Logger.h" namespace nacos{ NacosString LocalSnapshotManager::getFailover(const NacosString &serverName, const NacosString &dataId, const NacosString &group, const NacosString &tenant) { NacosString localPath = getFailoverFile(serverName, dataId, group, tenant); if (IOUtils::checkNotExistOrNotFile(localPath)) { log_debug("[LocalSnapshotManager]-getFailover:[servername=%s] failover file %s doesn't exist\n", serverName.c_str(), localPath.c_str()); return NULLSTR; } try { return readFile(localPath); } catch (IOException &ioe) { log_error("[LocalSnapshotManager]-getFailover:[servername=%s] get failover error, file: %s, exception:%s\n", serverName.c_str(), localPath.c_str(), ioe.what()); return NULLSTR; } }; /** * 获取本地缓存文件内容。NULL表示没有本地文件或抛出异常。 */ NacosString LocalSnapshotManager::getSnapshot ( const NacosString &name, const NacosString &dataId, const NacosString &group, const NacosString &tenant ) { if (!SnapShotSwitch::getIsSnapShot()) { return NULLSTR; } NacosString file = getSnapshotFile(name, dataId, group, tenant); if (IOUtils::checkNotExistOrNotFile(file)) { return NULLSTR; } try { return readFile(file); } catch (IOException &ioe) { log_error("[LocalSnapshotManager]-getSnapshot:[servername=%s]+get snapshot error, file:%s what:%s\n", name.c_str(), file.c_str(), ioe.what()); return NULLSTR; } }; NacosString LocalSnapshotManager::readFile(const NacosString &file) NACOS_THROW(IOException) { if (IOUtils::checkNotExistOrNotFile(file)) { return NULLSTR; } if (JVMUtil::isMultiInstance()) { return ConcurrentDiskUtil::getFileContent(file, ConfigConstant::ENCODE); } else { return IOUtils::readStringFromFile(file, ConfigConstant::ENCODE); } }; void LocalSnapshotManager::saveSnapshot(const NacosString &envName, const NacosString &dataId, const NacosString &group, const NacosString &tenant, const NacosString &config) { if (!SnapShotSwitch::getIsSnapShot()) { return; } NacosString file = getSnapshotFile(envName, dataId, group, tenant); if (isNull(config)) { int remove_result = remove(file.c_str()); if (remove_result)//error happens when removing the file { //usually we get this error because we are deleting a non-existent file log_debug("[LocalSnapshotManager]-saveSnapshot:[servername=%s] delete snapshot error, remove() returns %d, errno = %d\n", envName.c_str(), remove_result, errno); } } else { NacosString parentFile = IOUtils::getParentFile(file); if (IOUtils::checkNotExistOrNotDir(parentFile)) { IOUtils::recursivelyCreate(parentFile); //LOGGER.error("[{}] save snapshot error", envName); } if (JVMUtil::isMultiInstance()) { ConcurrentDiskUtil::writeFileContent(file, config, ConfigConstant::ENCODE); } else { IOUtils::writeStringToFile(file, config, ConfigConstant::ENCODE); } //LOGGER.error("[" + envName + "] save snapshot error, " + file, ioe); } }; /** * 清除snapshot目录下所有缓存文件。 */ void LocalSnapshotManager::cleanAllSnapshot() { std::list rootFile = IOUtils::listFiles(LOCAL_SNAPSHOT_PATH); for (std::list::iterator it = rootFile.begin(); it != rootFile.end(); it++) { //endsWith("_nacos") if (it->length() >= 6 && (it->rfind("_nacos") == it->length() - 6)) { IOUtils::cleanDirectory(LOCAL_SNAPSHOT_PATH + "/" + *it); } } //LOGGER.error("clean all snapshot error, " + ioe.toString(), ioe); }; void LocalSnapshotManager::cleanEnvSnapshot(const NacosString &envName) { NacosString tmp = LOCAL_SNAPSHOT_PATH + "/" + envName + "_nacos"; tmp += "/snapshot"; //I think we should remove -tenant also, so for one envname, cache for all tenants within the environment will be purged NacosString tmp_tenant = tmp + "-tenant"; IOUtils::cleanDirectory(tmp); IOUtils::cleanDirectory(tmp_tenant); log_info("[LocalSnapshotManager]-cleanEnvSnapshot:success delete %s-snapshot: %s\n", envName.c_str(), tmp.c_str()); }; NacosString LocalSnapshotManager::getFailoverFile(const NacosString &serverName, const NacosString &dataId, const NacosString &group, const NacosString &tenant) { NacosString Failoverfile = LOCAL_SNAPSHOT_PATH + "/" + serverName + "_nacos"; Failoverfile += "/data"; if (ParamUtils::isBlank(tenant)) { Failoverfile += "/config-data"; } else { Failoverfile += "/config-data-tenant/"; Failoverfile += tenant; } if (NacosStringOps::isNullStr(group)) { Failoverfile += "/" + ConfigConstant::DEFAULT_GROUP + "/" + dataId; } else { Failoverfile += "/" + group + "/" + dataId; } return Failoverfile; }; NacosString LocalSnapshotManager::getSnapshotFile(const NacosString &envName, const NacosString &dataId, const NacosString &group, const NacosString &tenant) { NacosString filename = LOCAL_SNAPSHOT_PATH + "/" + envName + "_nacos"; if (isNull(tenant)) { filename += "/snapshot"; } else { filename += "/snapshot-tenant/" + tenant; } if (NacosStringOps::isNullStr(group)) { filename += "/" + ConfigConstant::DEFAULT_GROUP + "/" + dataId; } else { filename += "/" + group + "/" + dataId; } return filename; }; LocalSnapshotManager::LocalSnapshotManager(AppConfigManager *appConfigManager) { this->_appCfgMgr = appConfigManager; LOCAL_SNAPSHOT_PATH = _appCfgMgr->get(PropertyKeyConst::NACOS_SNAPSHOT_PATH); LOCAL_FAILOVER_PATH = _appCfgMgr->get(PropertyKeyConst::NACOS_SNAPSHOT_PATH); log_debug("LocalConfigInfoProcessor::LocalConfigInfoProcessor() LOCAL_SNAPSHOT_PATH = %s\n", LocalSnapshotManager::LOCAL_SNAPSHOT_PATH.c_str()); } }//namespace nacos ================================================ FILE: src/config/LocalSnapshotManager.h ================================================ #ifndef __LOCAL_SNAPSHOT_MGR_H_ #define __LOCAL_SNAPSHOT_MGR_H_ #include "NacosString.h" #include "NacosExceptions.h" #include "src/config/AppConfigManager.h" #include "Compatibility.h" /** * Snapshot/Failover manager * * @author Nacos */ namespace nacos{ class LocalSnapshotManager { private: AppConfigManager *_appCfgMgr; NacosString LOCAL_SNAPSHOT_PATH; NacosString LOCAL_FAILOVER_PATH; public: LocalSnapshotManager(AppConfigManager *appConfigManager); NacosString getFailover(const NacosString &serverName, const NacosString &dataId, const NacosString &group, const NacosString &tenant); /** * Accuire local cache content, returns NULLSTR when the file does not exist or an exception is thrown * 获取本地缓存文件内容。NULL表示没有本地文件或抛出异常。 */ NacosString getSnapshot(const NacosString &name, const NacosString &dataId, const NacosString &group, const NacosString &tenant); NacosString readFile(const NacosString &file) NACOS_THROW(IOException); void saveSnapshot(const NacosString &envName, const NacosString &dataId, const NacosString &group, const NacosString &tenant, const NacosString &config); /** * Purge all cached files in snapshot directory * 清除snapshot目录下所有缓存文件。 */ void cleanAllSnapshot(); void cleanEnvSnapshot(const NacosString &envName); NacosString getFailoverFile(const NacosString &serverName, const NacosString &dataId, const NacosString &group, const NacosString &tenant); NacosString getSnapshotFile(const NacosString &envName, const NacosString &dataId, const NacosString &group, const NacosString &tenant); }; }//namespace nacos #endif ================================================ FILE: src/config/NacosConfigService.cpp ================================================ #include "NacosConfigService.h" #include "src/security/SecurityManager.h" #include "src/log/Logger.h" #include "ConfigProxy.h" #include "src/utils/ParamUtils.h" using namespace std; namespace nacos{ NacosConfigService::NacosConfigService(ObjectConfigData *objectConfigData) NACOS_THROW(NacosException) { _objectConfigData = objectConfigData; if (_objectConfigData->_appConfigManager->nacosAuthEnabled()) { _objectConfigData->_securityManager->login(); _objectConfigData->_securityManager->start(); } } NacosConfigService::~NacosConfigService() { log_debug("[NacosConfigService]:~NacosConfigService()\n"); delete _objectConfigData; } NacosString NacosConfigService::getConfig ( const NacosString &dataId, const NacosString &group, long timeoutMs ) NACOS_THROW(NacosException) { return getConfigInner(getNamespace(), dataId, group, timeoutMs); } bool NacosConfigService::publishConfig ( const NacosString &dataId, const NacosString &group, const NacosString &content ) NACOS_THROW(NacosException) { return publishConfigInner(getNamespace(), dataId, group, NULLSTR, NULLSTR, NULLSTR, content); } bool NacosConfigService::removeConfig ( const NacosString &dataId, const NacosString &group ) NACOS_THROW(NacosException) { return removeConfigInner(getNamespace(), dataId, group, NULLSTR); } NacosString NacosConfigService::getConfigInner ( const NacosString &tenant, const NacosString &dataId, const NacosString &group, long timeoutMs ) NACOS_THROW(NacosException) { NacosString result = NULLSTR; AppConfigManager *_appConfigManager = _objectConfigData->_appConfigManager; LocalSnapshotManager *_localSnapshotManager = _objectConfigData->_localSnapshotManager; NacosString clientName = _appConfigManager->get(PropertyKeyConst::CLIENT_NAME); result = _localSnapshotManager->getFailover(clientName.c_str(), dataId, group, tenant); if (!NacosStringOps::isNullStr(result)) { log_warn("[NacosConfigService]-getConfig:[clientName=%s] get failover ok, dataId=%s, group=%s, tenant=%s, config=%s", clientName.c_str(), dataId.c_str(), group.c_str(), tenant.c_str(), result.c_str()); return result; } try { result = _objectConfigData->_clientWorker->getServerConfig(tenant, dataId, group, timeoutMs); } catch (NacosException &e) { if (e.errorcode() == NacosException::NO_RIGHT) { log_error("Invalid credential, e: %d = %s\n", e.errorcode(), e.what()); } const NacosString &clientName = _appConfigManager->get(PropertyKeyConst::CLIENT_NAME); result = _localSnapshotManager->getSnapshot(clientName, dataId, group, tenant); if (e.errorcode() == NacosException::NO_RIGHT && NacosStringOps::isNullStr(result)) { //permission denied and no failback, let user decide what to do throw e; } } return result; } bool NacosConfigService::removeConfigInner ( const NacosString &tenant, const NacosString &dataId, const NacosString &group, const NacosString &tag ) NACOS_THROW(NacosException) { std::list headers; std::list paramValues; //Get the request url NacosString path = _objectConfigData->_appConfigManager->getContextPath() + ConfigConstant::CONFIG_CONTROLLER_PATH; HttpResult res; paramValues.push_back("dataId"); paramValues.push_back(dataId); NacosString parmGroupid = ParamUtils::null2defaultGroup(group); paramValues.push_back("group"); paramValues.push_back(parmGroupid); if (!isNull(tenant)) { paramValues.push_back("tenant"); paramValues.push_back(tenant); } NacosString serverAddr = _objectConfigData->_serverListManager->getCurrentServerAddr(); NacosString url = serverAddr + "/" + path; log_debug("[NacosConfigService]-removeConfigInner: Assembled URL:%s\n", url.c_str()); ConfigProxy *_configProxy = _objectConfigData->_configProxy; try { res = _configProxy->reqAPI(IHttpCli::DELETE, url, headers, paramValues, _objectConfigData->encoding, POST_TIMEOUT); } catch (NetworkException &e) { log_warn("[NacosConfigService]-removeConfigInner: error, %s, %s, %s, msg: %s\n", dataId.c_str(), group.c_str(), tenant.c_str(), e.what()); return false; } //If the server returns true, then this call succeeds if (res.content.compare("true") == 0) { return true; } else { return false; } } bool NacosConfigService::publishConfigInner ( const NacosString &tenant, const NacosString &dataId, const NacosString &group, const NacosString &tag, const NacosString &appName, const NacosString &betaIps, const NacosString &content ) NACOS_THROW(NacosException) { //TODO:More stringent check, need to improve checkParam() function ParamUtils::checkParam(dataId, group, content); std::list headers; std::list paramValues; NacosString parmGroupid; //Get the request url NacosString path = _objectConfigData->_appConfigManager->getContextPath() + ConfigConstant::CONFIG_CONTROLLER_PATH; HttpResult res; parmGroupid = ParamUtils::null2defaultGroup(group); ParamUtils::addKV(paramValues, "group", parmGroupid); ParamUtils::addKV(paramValues, "dataId", dataId); ParamUtils::addKV(paramValues, "content", content); if (!isNull(tenant)) { ParamUtils::addKV(paramValues, "tenant", tenant); } if (!isNull(appName)) { ParamUtils::addKV(paramValues, "appName", appName); } if (!isNull(tag)) { ParamUtils::addKV(paramValues, "tag", tag); } if (!isNull(betaIps)) { ParamUtils::addKV(paramValues, "betaIps", betaIps); } NacosString serverAddr = _objectConfigData->_serverListManager->getCurrentServerAddr(); NacosString url = serverAddr + "/" + path; log_debug("[NacosConfigService]-publishConfigInner:httpPost Assembled URL:%s\n", url.c_str()); ConfigProxy *_configProxy = _objectConfigData->_configProxy; try { res = _configProxy->reqAPI(IHttpCli::POST, url, headers, paramValues, _objectConfigData->encoding, POST_TIMEOUT); } catch (NetworkException &e) { // log_warn("[NacosConfigService]-publishConfigInner: exception, dataId=%s, group=%s, msg=%s\n", dataId.c_str(), group.c_str(), tenant.c_str(), e.what()); return false; } //If the server returns true, then this call succeeds if (res.content.compare("true") == 0) { return true; } else { return false; } } void NacosConfigService::addListener ( const NacosString &dataId, const NacosString &group, Listener *listener ) NACOS_THROW(NacosException) { NacosString parmgroup = ConfigConstant::DEFAULT_GROUP; if (!isNull(group)) { parmgroup = group; } //TODO:give a constant to this hard-coded number NacosString cfgcontent; try { cfgcontent = getConfig(dataId, group, 3000); } catch (NacosException &e) { cfgcontent = ""; } _objectConfigData->_clientWorker->addListener(dataId, parmgroup, getNamespace(), cfgcontent, listener); _objectConfigData->_clientWorker->startListening(); } void NacosConfigService::removeListener ( const NacosString &dataId, const NacosString &group, Listener *listener ) { NacosString parmgroup = ConfigConstant::DEFAULT_GROUP; if (!isNull(group)) { parmgroup = group; } log_debug("[NacosConfigService]-removeListener: calling client worker\n"); _objectConfigData->_clientWorker->removeListener(dataId, parmgroup, getNamespace(), listener); } }//namespace nacos ================================================ FILE: src/config/NacosConfigService.h ================================================ #ifndef __NACOS_CFG_SVC_H_ #define __NACOS_CFG_SVC_H_ #include "config/ConfigService.h" #include "src/http/HttpDelegate.h" #include "src/listen/ClientWorker.h" #include "NacosString.h" #include "src/server/ServerListManager.h" #include "Properties.h" #include "src/factory/ObjectConfigData.h" #include "Compatibility.h" namespace nacos{ class NacosConfigService : public ConfigService { private: ObjectConfigData *_objectConfigData; //Private Methods NacosConfigService(); NacosString getConfigInner(const NacosString &tenant, const NacosString &dataId, const NacosString &group, long timeoutMs) NACOS_THROW(NacosException); bool removeConfigInner(const NacosString &tenant, const NacosString &dataId, const NacosString &group, const NacosString &tag) NACOS_THROW(NacosException); bool publishConfigInner(const NacosString &tenant, const NacosString &dataId, const NacosString &group, const NacosString &tag, const NacosString &appName, const NacosString &betaIps, const NacosString &content) NACOS_THROW(NacosException); //NacosString monitorChange(std::map &keysAndContents, long timeoutMs) throw (NacosException); //static NacosString monitorListToString(std::map &keysAndContents); NacosString getNamespace() const { return _objectConfigData->_appConfigManager->get(PropertyKeyConst::NAMESPACE); }; public: const static long POST_TIMEOUT = 3000L; //Public Methods NacosString getConfig(const NacosString &dataId, const NacosString &group, long timeoutMs) NACOS_THROW(NacosException); bool publishConfig(const NacosString &dataId, const NacosString &group, const NacosString &content) NACOS_THROW(NacosException); bool removeConfig(const NacosString &dataId, const NacosString &group) NACOS_THROW(NacosException); void addListener(const NacosString &dataId, const NacosString &group, Listener *listener) NACOS_THROW(NacosException); void removeListener(const NacosString &dataId, const NacosString &group, Listener *listener); HttpDelegate *getHttpDelegate() const { return _objectConfigData->_httpDelegate; }; IHttpCli *getHttpCli() const { return _objectConfigData->_httpCli; }; ServerListManager *getServerListManager() const { return _objectConfigData->_serverListManager; }; ClientWorker *getClientWorker() const { return _objectConfigData->_clientWorker; }; AppConfigManager *getAppConfigManager() const { return _objectConfigData->_appConfigManager; }; void setHttpDelegate(HttpDelegate *httpDelegate) { _objectConfigData->_httpDelegate = httpDelegate; }; void setHttpCli(IHttpCli *httpCli) { _objectConfigData->_httpCli = httpCli; }; void setServerListManager(ServerListManager *svrListMgr) { _objectConfigData->_serverListManager = svrListMgr; }; void setClientWorker(ClientWorker *clientWorker) { _objectConfigData->_clientWorker = clientWorker; }; void setAppConfigManager(AppConfigManager *appConfigManager) { _objectConfigData->_appConfigManager = appConfigManager; }; //ctors/dtor NacosConfigService(ObjectConfigData *objectConfigData) NACOS_THROW(NacosException); virtual ~NacosConfigService(); }; }//namespace nacos #endif ================================================ FILE: src/config/SnapShotSwitch.cpp ================================================ #include "LocalSnapshotManager.h" #include "SnapShotSwitch.h" namespace nacos{ bool SnapShotSwitch::getIsSnapShot() { return isSnapShot; }; void SnapShotSwitch::setIsSnapShot(bool isSnapShot) { SnapShotSwitch::isSnapShot = isSnapShot; //LocalConfigInfoProcessor::cleanAllSnapshot(); }; }//namespace nacos ================================================ FILE: src/config/SnapShotSwitch.h ================================================ #ifndef __SS_SWITCH_H_ #define __SS_SWITCH_H_ namespace nacos{ class SnapShotSwitch { private: /** * whether use local cache */ static bool isSnapShot; public: static bool getIsSnapShot(); static void setIsSnapShot(bool isSnapShot); }; }//namespace nacos #endif ================================================ FILE: src/constant/ConfigConstant.cpp ================================================ #include "constant/ConfigConstant.h" /** * Constant * * @author Nacos */ namespace nacos{ const NacosString ConfigConstant::DEFAULT_GROUP = "DEFAULT_GROUP"; const NacosString ConfigConstant::DEFAULT_CONTEXT_PATH = "nacos"; const NacosString ConfigConstant::PROTOCOL_VERSION = "v1"; const NacosString ConfigConstant::GET_SERVERS_PATH = "ns/operator/servers"; const NacosString ConfigConstant::DATAID = "dataId"; const NacosString ConfigConstant::PROBE_MODIFY_REQUEST = "Listening-Configs"; const NacosString ConfigConstant::PROBE_MODIFY_RESPONSE = "Probe-Modify-Response"; const NacosString ConfigConstant::BASE_PATH = "/v1/cs"; const NacosString ConfigConstant::CONFIG_CONTROLLER_PATH = BASE_PATH + "/configs"; /** * second */ const int ConfigConstant::POLLING_INTERVAL_TIME = 15; const NacosString ConfigConstant::ENCODE = "UTF-8"; const int ConfigConstant::FLOW_CONTROL_THRESHOLD = 20; const NacosString ConfigConstant::LINE_SEPARATOR = "\x1"; const NacosString ConfigConstant::WORD_SEPARATOR = "\x2"; const NacosString ConfigConstant::NAMING_INSTANCE_ID_SPLITTER = "#"; const NacosString ConfigConstant::DEFAULT_CLUSTER_NAME = "DEFAULT"; const NacosString ConfigConstant::SERVICE_INFO_SPLITER = "@@"; const NacosString ConfigConstant::FILE_SEPARATOR = "/"; const NacosString ConfigConstant::CONFIG_NEXT_LINE = "\n"; const NacosString ConfigConstant::CONFIG_KV_SEPARATOR = "="; const NacosString ConfigConstant::DEFAULT_CONFIG_FILE = "nacos-cpp-cli.properties"; }//namespace nacos ================================================ FILE: src/constant/NamingConstant.cpp ================================================ #include "constant/NamingConstant.h" namespace nacos{ const NacosString NamingConstant::SERVICE_NAME = "serviceName"; const NacosString NamingConstant::CLUSTER_NAME = "clusterName"; const NacosString NamingConstant::UDP_PORT = "udpPort"; const NacosString NamingConstant::CLUSTERS = "clusters"; const NacosString NamingConstant::CLIENT_IP = "clientIP"; const NacosString NamingConstant::HEALTHY_ONLY = "healthyOnly"; const NacosString NamingConstant::HEALTHY = "healthy"; const NacosString NamingConstant::NAMESPACE_ID = "namespaceId"; const NacosString NamingConstant::GROUP_NAME = "groupName"; const NacosString NamingConstant::SPLITER = "@@"; const NacosString NamingConstant::EMPTY = ""; const NacosString NamingConstant::ALL_IPS = "000--00-ALL_IPS--00--000"; const NacosString NamingConstant::BEAT = "beat"; const NacosString NamingConstant::PAGE_SIZE = "pageSize"; const NacosString NamingConstant::PAGE_NO = "pageNo"; }//namespace nacos ================================================ FILE: src/constant/PropertyKeyConst.cpp ================================================ #include "constant/PropertyKeyConst.h" namespace nacos{ const NacosString PropertyKeyConst::IS_USE_ENDPOINT_PARSING_RULE = "isUseEndpointParsingRule"; const NacosString PropertyKeyConst::ENDPOINT = "endpoint"; const NacosString PropertyKeyConst::ENDPOINT_PORT = "endpointPort"; const NacosString PropertyKeyConst::NAMESPACE = "namespace"; const NacosString PropertyKeyConst::ENDPOINT_QUERY_PARAMS = "endpointQueryParams"; const NacosString PropertyKeyConst::ACCESS_KEY = "accessKey"; const NacosString PropertyKeyConst::SECRET_KEY = "secretKey"; const NacosString PropertyKeyConst::APP_NAME = "appName"; const NacosString PropertyKeyConst::RAM_ROLE_NAME = "ramRoleName"; const NacosString PropertyKeyConst::SERVER_ADDR = "serverAddr"; const NacosString PropertyKeyConst::CONTEXT_PATH = "nacos.server.contextpath"; const NacosString PropertyKeyConst::ENDPOINT_CONTEXT_PATH = "endpointContextPath"; const NacosString PropertyKeyConst::CLUSTER_NAME = "clusterName"; const NacosString PropertyKeyConst::ENCODE = "encode"; const NacosString PropertyKeyConst::NAMING_LOAD_CACHE_AT_START = "namingLoadCacheAtStart"; const NacosString PropertyKeyConst::NAMING_CLIENT_BEAT_THREAD_COUNT = "namingClientBeatThreadCount"; const NacosString PropertyKeyConst::NAMING_POLLING_THREAD_COUNT = "namingPollingThreadCount"; const NacosString PropertyKeyConst::SRVLISTMGR_REFRESH_INTERVAL = "serverListMgr.refreshInterval"; const NacosString PropertyKeyConst::SERVER_REQ_TIMEOUT = "nacos.server.reqtimeout"; const NacosString PropertyKeyConst::SUBSCRIPTION_POLL_INTERVAL = "naming.poller.interval"; const NacosString PropertyKeyConst::UDP_RECEIVER_PORT = "nacos.udp.port"; const NacosString PropertyKeyConst::CONFIG_LONGPULLLING_TIMEOUT = "config.longpulling.timeout"; const NacosString PropertyKeyConst::HB_FAIL_WAIT_TIME = "naming.heartbeat.failwait"; const NacosString PropertyKeyConst::NACOS_SNAPSHOT_PATH = "nacos.snapshot.path"; const NacosString PropertyKeyConst::LOG_PATH = "nacos.log.path"; const NacosString PropertyKeyConst::LOG_ROTATE_SIZE = "nacos.log.rotateSize"; const NacosString PropertyKeyConst::LOG_LEVEL = "nacos.log.level"; const NacosString PropertyKeyConst::CLIENT_NAME = "nacos.client.name"; const NacosString PropertyKeyConst::AUTH_USERNAME = "nacos.auth.username"; const NacosString PropertyKeyConst::AUTH_PASSWORD = "nacos.auth.password"; const NacosString PropertyKeyConst::LOCAL_IP = "nacos.client.ip"; const NacosString PropertyKeyConst::INSTANCE_ID_SEQ_FILE = "nacos.instId.seq.file"; const NacosString PropertyKeyConst::INSTANCE_ID_PREFIX = "nacos.instId.prefix"; }//namespace nacos ================================================ FILE: src/constant/UtilAndComs.cpp ================================================ #include "constant/UtilAndComs.h" namespace nacos{ NacosString UtilAndComs::VERSION = "Nacos-C-Client:v1.0.21";//TODO:fix nacos trunk code for cpp client NacosString UtilAndComs::ENCODING = "UTF-8"; NacosString UtilAndComs::NACOS_URL_BASE = "/v1/ns"; NacosString UtilAndComs::NACOS_URL_INSTANCE = NACOS_URL_BASE + "/instance"; int UtilAndComs::REQUEST_DOMAIN_RETRY_COUNT = 3; NacosString UtilAndComs::SERVER_ADDR_IP_SPLITER = ":"; int UtilAndComs::DEFAULT_CLIENT_BEAT_THREAD_COUNT = 4;//TODO:Calc this according to nr_processors of the host int UtilAndComs::DEFAULT_POLLING_THREAD_COUNT = 1;//TODO:Calc this according to nr_processors of the host //Underlying logic: /*int UtilAndComs::DEFAULT_CLIENT_BEAT_THREAD_COUNT = Runtime.getRuntime() .availableProcessors() > 1 ? Runtime.getRuntime().availableProcessors() / 2 : 1; int UtilAndComs::DEFAULT_POLLING_THREAD_COUNT = Runtime.getRuntime() .availableProcessors() > 1 ? Runtime.getRuntime().availableProcessors() / 2 : 1;*/ void UtilAndComs::Init() { } }//namespace nacos ================================================ FILE: src/crypto/MACProvider.cpp ================================================ // // Created by liuhanyu on 2021/7/8. // #include "MACProvider.h" #include "src/crypto/hmac_sha1/hmac/hmac.h" namespace nacos { std::map *MACProvider::MACRegistry; const int MACProvider::HMAC_SHA1; class HMACSha1 : public IMAC { public: void getMac(const void *k, /* secret key */ size_t lk, /* length of the key in bytes */ const void *d, /* data */ size_t ld, /* length of data in bytes */ void *out, /* output buffer, at least "t" bytes */ size_t *t); }; void HMACSha1::getMac(const void *k, /* secret key */ size_t lk, /* length of the key in bytes */ const void *d, /* data */ size_t ld, /* length of data in bytes */ void *out, /* output buffer, at least "t" bytes */ size_t *t) { hmac_sha1((const uint8_t*)k, lk, (const uint8_t*)d, ld, (uint8_t*)out, t); } void MACProvider::Init() { MACRegistry = new std::map(); (*MACRegistry)[MACProvider::HMAC_SHA1] = new HMACSha1(); } void MACProvider::DeInit() { for (std::map::iterator it = MACRegistry->begin(); it != MACRegistry->end(); it++) { IMAC * curMACProvider = it->second; delete curMACProvider; } delete MACRegistry; } IMAC *MACProvider::getMAC(int algorithm) { if (MACRegistry->count(algorithm) > 0) { return (*MACRegistry)[algorithm]; } return NULL; } } ================================================ FILE: src/crypto/MACProvider.h ================================================ // // Created by liuhanyu on 2021/7/8. // #ifndef NACOS_SDK_CPP_MACPROVIDER_H #define NACOS_SDK_CPP_MACPROVIDER_H #include "NacosString.h" #include namespace nacos { class IMAC { public: virtual void getMac(const void *k, /* secret key */ size_t lk, /* length of the key in bytes */ const void *d, /* data */ size_t ld, /* length of data in bytes */ void *out, /* output buffer, at least "t" bytes */ size_t *t) = 0; virtual ~IMAC() {}; }; class MACProvider { private: static std::map *MACRegistry; public: static void Init(); static void DeInit(); static IMAC *getMAC(int algorithm); static const int HMAC_SHA1 = 930620; }; } #endif //NACOS_SDK_CPP_MACPROVIDER_H ================================================ FILE: src/crypto/SignatureTool.h ================================================ // // Created by liuhanyu on 2021/7/8. // #ifndef SIGNATURE_TOOL #define SIGNATURE_TOOL #include "NacosString.h" #include "MACProvider.h" #include "base64/base64.h" #include "src/debug/DebugAssertion.h" namespace nacos { /** * SignatureTool * * @author Liu, Hanyu * Signature tool */ class SignatureTool { public: //Returns a base64-encoded signature string static NacosString SignWithHMAC_SHA1(const NacosString &dataToSign, const NacosString &secretKey) { IMAC *digester = MACProvider::getMAC(MACProvider::HMAC_SHA1); unsigned char signature[20]; size_t outlen = sizeof(signature); digester->getMac(secretKey.c_str(), secretKey.length(), dataToSign.c_str(), dataToSign.length(), (void*)signature, &outlen); NACOS_ASSERT(outlen == 20);//must be 20 since we're using HMAC_SHA1 NacosString encoded_signature = base64_encode(signature, sizeof(signature)); return encoded_signature; } }; } #endif //NACOS_SDK_CPP_MACPROVIDER_H ================================================ FILE: src/crypto/base64/base64.h ================================================ /* * Base64 encoding/decoding (RFC1341) * Copyright (c) 2005-2011, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ // 2016-12-12 - Gaspard Petit : Slightly modified to return a std::string // instead of a buffer allocated with malloc. #include namespace nacos { static const unsigned char base64_table[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; /** * base64_encode - Base64 encode * @src: Data to be encoded * @len: Length of the data to be encoded * @out_len: Pointer to output length variable, or %NULL if not used * Returns: Allocated buffer of out_len bytes of encoded data, * or empty string on failure */ static std::string base64_encode(const unsigned char *src, size_t len) { unsigned char *out, *pos; const unsigned char *end, *in; size_t olen; olen = 4*((len + 2) / 3); /* 3-byte blocks to 4-byte */ if (olen < len) return std::string(); /* integer overflow */ std::string outStr; outStr.resize(olen); out = (unsigned char*)&outStr[0]; end = src + len; in = src; pos = out; while (end - in >= 3) { *pos++ = base64_table[in[0] >> 2]; *pos++ = base64_table[((in[0] & 0x03) << 4) | (in[1] >> 4)]; *pos++ = base64_table[((in[1] & 0x0f) << 2) | (in[2] >> 6)]; *pos++ = base64_table[in[2] & 0x3f]; in += 3; } if (end - in) { *pos++ = base64_table[in[0] >> 2]; if (end - in == 1) { *pos++ = base64_table[(in[0] & 0x03) << 4]; *pos++ = '='; } else { *pos++ = base64_table[((in[0] & 0x03) << 4) | (in[1] >> 4)]; *pos++ = base64_table[(in[1] & 0x0f) << 2]; } *pos++ = '='; } return outStr; } } ================================================ FILE: src/crypto/hmac_sha1/LICENSE ================================================ The MIT License (MIT) Copyright (c) 2014 Bob Liu Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: src/crypto/hmac_sha1/README.md ================================================ hmac-sha1 ========= [![Build Status](https://travis-ci.org/Akagi201/hmac-sha1.svg)](https://travis-ci.org/Akagi201/hmac-sha1) Standalone implementation of `HMAC()` + `EVP_sha1()` in `OpenSSL` ## API ``` #include "hmac/hmac.h" void hmac_sha1(const uint8_t *k, /* secret key */ size_t lk, /* length of the key in bytes */ const uint8_t *d, /* data */ size_t ld, /* length of data in bytes */ uint8_t *out, /* output buffer, at least "t" bytes */ size_t *t); ``` ================================================ FILE: src/crypto/hmac_sha1/hmac/hmac.h ================================================ /** * @file re_hmac.h Interface to HMAC functions * * Copyright (C) 2010 Creytiv.com */ #ifndef HMAC_H_ #define HMAC_H_ (1) #include void hmac_sha1(const uint8_t *k, /* secret key */ size_t lk, /* length of the key in bytes */ const uint8_t *d, /* data */ size_t ld, /* length of data in bytes */ uint8_t *out, /* output buffer, at least "t" bytes */ size_t *t); #endif // HMAC_H_ ================================================ FILE: src/crypto/hmac_sha1/hmac/hmac_sha1.cpp ================================================ /** * @file hmac_sha1.c Implements HMAC-SHA1 as of RFC 2202 * * Copyright (C) 2010 Creytiv.com */ #include #include #ifdef USE_OPENSSL #include #include #include #else #include "../sha/sha.h" #endif #include "hmac.h" /** SHA-1 Block size */ #ifndef SHA_BLOCKSIZE #define SHA_BLOCKSIZE (64) #endif /** * Function to compute the digest * * @param k Secret key * @param lk Length of the key in bytes * @param d Data * @param ld Length of data in bytes * @param out Digest output * @param t Size of digest output */ void hmac_sha1(const uint8_t *k, /* secret key */ size_t lk, /* length of the key in bytes */ const uint8_t *d, /* data */ size_t ld, /* length of data in bytes */ uint8_t *out, /* output buffer, at least "t" bytes */ size_t *t) { #ifdef USE_OPENSSL if (!HMAC(EVP_sha1(), k, (int)lk, d, ld, out, t)) { ERR_clear_error(); } #else SHA_CTX ictx, octx; uint8_t isha[SHA_DIGEST_LENGTH], osha[SHA_DIGEST_LENGTH]; uint8_t key[SHA_DIGEST_LENGTH]; uint8_t buf[SHA_BLOCKSIZE]; size_t i; if (lk > SHA_BLOCKSIZE) { SHA_CTX tctx; SHA1_Init(&tctx); SHA1_Update(&tctx, k, lk); SHA1_Final(key, &tctx); k = key; lk = SHA_DIGEST_LENGTH; } /**** Inner Digest ****/ SHA1_Init(&ictx); /* Pad the key for inner digest */ for (i = 0; i < lk; ++i) { buf[i] = k[i] ^ 0x36; } for (i = lk; i < SHA_BLOCKSIZE; ++i) { buf[i] = 0x36; } SHA1_Update(&ictx, buf, SHA_BLOCKSIZE); SHA1_Update(&ictx, d, ld); SHA1_Final(isha, &ictx); /**** Outer Digest ****/ SHA1_Init(&octx); /* Pad the key for outter digest */ for (i = 0; i < lk; ++i) { buf[i] = k[i] ^ 0x5c; } for (i = lk; i < SHA_BLOCKSIZE; ++i) { buf[i] = 0x5c; } SHA1_Update(&octx, buf, SHA_BLOCKSIZE); SHA1_Update(&octx, isha, SHA_DIGEST_LENGTH); SHA1_Final(osha, &octx); /* truncate and print the results */ *t = *t > SHA_DIGEST_LENGTH ? SHA_DIGEST_LENGTH : *t; memcpy(out, osha, *t); #endif } ================================================ FILE: src/crypto/hmac_sha1/sha/sha.h ================================================ /** * @file re_sha.h Interface to SHA (Secure Hash Standard) functions * * Copyright (C) 2010 Creytiv.com */ #ifndef SHA_H_ #define SHA_H_ (1) #ifdef USE_OPENSSL #include #else /* public api for steve reid's public domain SHA-1 implementation */ /* this file is in the public domain */ /** SHA-1 Context */ typedef struct { uint32_t state[5]; /**< Context state */ uint32_t count[2]; /**< Counter */ uint8_t buffer[64]; /**< SHA-1 buffer */ } SHA1_CTX; /** SHA-1 Context (OpenSSL compat) */ typedef SHA1_CTX SHA_CTX; /** SHA-1 Digest size in bytes */ #define SHA1_DIGEST_SIZE 20 /** SHA-1 Digest size in bytes (OpenSSL compat) */ #define SHA_DIGEST_LENGTH SHA1_DIGEST_SIZE void SHA1_Init(SHA1_CTX *context); void SHA1_Update(SHA1_CTX *context, const void *p, size_t len); void SHA1_Final(uint8_t digest[SHA1_DIGEST_SIZE], SHA1_CTX *context); #endif #endif // SHA_H_ ================================================ FILE: src/crypto/hmac_sha1/sha/sha1.cpp ================================================ /** * @file sha1.c SHA-1 in C */ /* By Steve Reid 100% Public Domain ----------------- Modified 7/98 By James H. Brown Still 100% Public Domain Corrected a problem which generated improper hash values on 16 bit machines Routine SHA1Update changed from void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int len) to void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned long len) The 'len' parameter was declared an int which works fine on 32 bit machines. However, on 16 bit machines an int is too small for the shifts being done against it. This caused the hash function to generate incorrect values if len was greater than 8191 (8K - 1) due to the 'len << 3' on line 3 of SHA1Update(). Since the file IO in main() reads 16K at a time, any file 8K or larger would be guaranteed to generate the wrong hash (e.g. Test Vector #3, a million "a"s). I also changed the declaration of variables i & j in SHA1Update to unsigned long from unsigned int for the same reason. These changes should make no difference to any 32 bit implementations since an int and a long are the same size in those environments. -- I also corrected a few compiler warnings generated by Borland C. 1. Added #include for exit() prototype 2. Removed unused variable 'j' in SHA1Final 3. Changed exit(0) to return(0) at end of main. ALL changes I made can be located by searching for comments containing 'JHB' ----------------- Modified 8/98 By Steve Reid Still 100% public domain 1- Removed #include and used return() instead of exit() 2- Fixed overwriting of finalcount in SHA1Final() (discovered by Chris Hall) 3- Changed email address from steve@edmweb.com to sreid@sea-to-sky.net ----------------- Modified 4/01 By Saul Kravitz Still 100% PD Modified to run on Compaq Alpha hardware. ----------------- Modified 07/2002 By Ralph Giles Still 100% public domain modified for use with stdint types, autoconf code cleanup, removed attribution comments switched SHA1Final() argument order for consistency use SHA1_ prefix for public api move public api to sha1.h */ /* Test Vectors (from FIPS PUB 180-1) "abc" A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 A million repetitions of "a" 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F */ #define SHA1HANDSOFF (1) #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include "sha.h" void SHA1_Transform(uint32_t state[5], const uint8_t buffer[64]); #define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) #if defined (BYTE_ORDER) && defined(BIG_ENDIAN) && (BYTE_ORDER == BIG_ENDIAN) #define WORDS_BIGENDIAN 1 #endif #ifdef _BIG_ENDIAN #define WORDS_BIGENDIAN 1 #endif /* blk0() and blk() perform the initial expand. */ /* I got the idea of expanding during the round function from SSLeay */ /* FIXME: can we do this in an endian-proof way? */ #ifdef WORDS_BIGENDIAN #define blk0(i) block->l[i] #else #define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xff00ff00) \ |(rol(block->l[i],8)&0x00ff00ff)) #endif #define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \ ^block->l[(i+2)&15]^block->l[i&15],1)) /* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ #define R0(v, w, x, y, z, i) \ z+=((w&(x^y))^y)+blk0(i)+0x5a827999+rol(v,5);w=rol(w,30); #define R1(v, w, x, y, z, i) \ z+=((w&(x^y))^y)+blk(i)+0x5a827999+rol(v,5);w=rol(w,30); #define R2(v, w, x, y, z, i) \ z+=(w^x^y)+blk(i)+0x6ed9eba1+rol(v,5);w=rol(w,30); #define R3(v, w, x, y, z, i) \ z+=(((w|x)&y)|(w&x))+blk(i)+0x8f1bbcdc+rol(v,5);w=rol(w,30); #define R4(v, w, x, y, z, i) \ z+=(w^x^y)+blk(i)+0xca62c1d6+rol(v,5);w=rol(w,30); /* Hash a single 512-bit block. This is the core of the algorithm. */ void SHA1_Transform(uint32_t state[5], const uint8_t buffer[64]) { uint32_t a, b, c, d, e; typedef union { uint8_t c[64]; uint32_t l[16]; } CHAR64LONG16; CHAR64LONG16 *block; #ifdef SHA1HANDSOFF CHAR64LONG16 workspace; block = &workspace; memcpy(block, buffer, 64); #else block = (CHAR64LONG16*)buffer; #endif /* Copy context->state[] to working vars */ a = state[0]; b = state[1]; c = state[2]; d = state[3]; e = state[4]; /* 4 rounds of 20 operations each. Loop unrolled. */ R0(a, b, c, d, e, 0); R0(e, a, b, c, d, 1); R0(d, e, a, b, c, 2); R0(c, d, e, a, b, 3); R0(b, c, d, e, a, 4); R0(a, b, c, d, e, 5); R0(e, a, b, c, d, 6); R0(d, e, a, b, c, 7); R0(c, d, e, a, b, 8); R0(b, c, d, e, a, 9); R0(a, b, c, d, e, 10); R0(e, a, b, c, d, 11); R0(d, e, a, b, c, 12); R0(c, d, e, a, b, 13); R0(b, c, d, e, a, 14); R0(a, b, c, d, e, 15); R1(e, a, b, c, d, 16); R1(d, e, a, b, c, 17); R1(c, d, e, a, b, 18); R1(b, c, d, e, a, 19); R2(a, b, c, d, e, 20); R2(e, a, b, c, d, 21); R2(d, e, a, b, c, 22); R2(c, d, e, a, b, 23); R2(b, c, d, e, a, 24); R2(a, b, c, d, e, 25); R2(e, a, b, c, d, 26); R2(d, e, a, b, c, 27); R2(c, d, e, a, b, 28); R2(b, c, d, e, a, 29); R2(a, b, c, d, e, 30); R2(e, a, b, c, d, 31); R2(d, e, a, b, c, 32); R2(c, d, e, a, b, 33); R2(b, c, d, e, a, 34); R2(a, b, c, d, e, 35); R2(e, a, b, c, d, 36); R2(d, e, a, b, c, 37); R2(c, d, e, a, b, 38); R2(b, c, d, e, a, 39); R3(a, b, c, d, e, 40); R3(e, a, b, c, d, 41); R3(d, e, a, b, c, 42); R3(c, d, e, a, b, 43); R3(b, c, d, e, a, 44); R3(a, b, c, d, e, 45); R3(e, a, b, c, d, 46); R3(d, e, a, b, c, 47); R3(c, d, e, a, b, 48); R3(b, c, d, e, a, 49); R3(a, b, c, d, e, 50); R3(e, a, b, c, d, 51); R3(d, e, a, b, c, 52); R3(c, d, e, a, b, 53); R3(b, c, d, e, a, 54); R3(a, b, c, d, e, 55); R3(e, a, b, c, d, 56); R3(d, e, a, b, c, 57); R3(c, d, e, a, b, 58); R3(b, c, d, e, a, 59); R4(a, b, c, d, e, 60); R4(e, a, b, c, d, 61); R4(d, e, a, b, c, 62); R4(c, d, e, a, b, 63); R4(b, c, d, e, a, 64); R4(a, b, c, d, e, 65); R4(e, a, b, c, d, 66); R4(d, e, a, b, c, 67); R4(c, d, e, a, b, 68); R4(b, c, d, e, a, 69); R4(a, b, c, d, e, 70); R4(e, a, b, c, d, 71); R4(d, e, a, b, c, 72); R4(c, d, e, a, b, 73); R4(b, c, d, e, a, 74); R4(a, b, c, d, e, 75); R4(e, a, b, c, d, 76); R4(d, e, a, b, c, 77); R4(c, d, e, a, b, 78); R4(b, c, d, e, a, 79); /* Add the working vars back into context.state[] */ state[0] += a; state[1] += b; state[2] += c; state[3] += d; state[4] += e; /* Wipe variables */ a = b = c = d = e = 0; } /** * Initialize new context * * @param context SHA1-Context */ void SHA1_Init(SHA1_CTX *context) { /* SHA1 initialization constants */ context->state[0] = 0x67452301; context->state[1] = 0xefcdab89; context->state[2] = 0x98badcfe; context->state[3] = 0x10325476; context->state[4] = 0xc3d2e1f0; context->count[0] = context->count[1] = 0; } /** * Run your data through this * * @param context SHA1-Context * @param p Buffer to run SHA1 on * @param len Number of bytes */ void SHA1_Update(SHA1_CTX *context, const void *p, size_t len) { const uint8_t *data = (const uint8_t *)p; size_t i, j; j = (context->count[0] >> 3) & 63; if ((context->count[0] += (uint32_t) (len << 3)) < (len << 3)) { context->count[1]++; } context->count[1] += (uint32_t) (len >> 29); if ((j + len) > 63) { memcpy(&context->buffer[j], data, (i = 64 - j)); SHA1_Transform(context->state, context->buffer); for (; i + 63 < len; i += 64) { SHA1_Transform(context->state, data + i); } j = 0; } else i = 0; memcpy(&context->buffer[j], &data[i], len - i); } /** * Add padding and return the message digest * * @param digest Generated message digest * @param context SHA1-Context */ void SHA1_Final(uint8_t digest[SHA1_DIGEST_SIZE], SHA1_CTX *context) { uint32_t i; uint8_t finalcount[8]; for (i = 0; i < 8; i++) { finalcount[i] = (uint8_t) ((context->count[(i >= 4 ? 0 : 1)] >> ((3 - (i & 3)) * 8)) & 255); } SHA1_Update(context, (uint8_t *) "\200", 1); while ((context->count[0] & 504) != 448) { SHA1_Update(context, (uint8_t *) "\0", 1); } SHA1_Update(context, finalcount, 8); /* Should cause SHA1_Transform */ for (i = 0; i < SHA1_DIGEST_SIZE; i++) { digest[i] = (uint8_t) ((context->state[i >> 2] >> ((3 - (i & 3)) * 8)) & 255); } /* Wipe variables */ i = 0; memset(context->buffer, 0, 64); memset(context->state, 0, 20); memset(context->count, 0, 8); memset(finalcount, 0, 8); /* SWR */ #ifdef SHA1HANDSOFF /* make SHA1Transform overwrite its own static vars */ SHA1_Transform(context->state, context->buffer); #endif } ================================================ FILE: src/crypto/md5/md5.cpp ================================================ #include "md5.h" using namespace std; /* Constants for MD5Transform routine. */ #define S11 7 #define S12 12 #define S13 17 #define S14 22 #define S21 5 #define S22 9 #define S23 14 #define S24 20 #define S31 4 #define S32 11 #define S33 16 #define S34 23 #define S41 6 #define S42 10 #define S43 15 #define S44 21 /* F, G, H and I are basic MD5 functions. */ #define F(x, y, z) (((x) & (y)) | ((~x) & (z))) #define G(x, y, z) (((x) & (z)) | ((y) & (~z))) #define H(x, y, z) ((x) ^ (y) ^ (z)) #define I(x, y, z) ((y) ^ ((x) | (~z))) /* ROTATE_LEFT rotates x left n bits. */ #define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) /* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. Rotation is separate from addition to prevent recomputation. */ #define FF(a, b, c, d, x, s, ac) { \ (a) += F ((b), (c), (d)) + (x) + ac; \ (a) = ROTATE_LEFT ((a), (s)); \ (a) += (b); \ } #define GG(a, b, c, d, x, s, ac) { \ (a) += G ((b), (c), (d)) + (x) + ac; \ (a) = ROTATE_LEFT ((a), (s)); \ (a) += (b); \ } #define HH(a, b, c, d, x, s, ac) { \ (a) += H ((b), (c), (d)) + (x) + ac; \ (a) = ROTATE_LEFT ((a), (s)); \ (a) += (b); \ } #define II(a, b, c, d, x, s, ac) { \ (a) += I ((b), (c), (d)) + (x) + ac; \ (a) = ROTATE_LEFT ((a), (s)); \ (a) += (b); \ } namespace nacos{ const byte MD5::PADDING[64] = {0x80}; const char MD5::HEX[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; /* Default construct. */ MD5::MD5() { reset(); } /* Construct a MD5 object with a input buffer. */ MD5::MD5(const void *input, size_t length) { reset(); update(input, length); } /* Construct a MD5 object with a string. */ MD5::MD5(const NacosString &str) { reset(); update(str); } /* Construct a MD5 object with a file. */ MD5::MD5(ifstream &in) { reset(); update(in); } /* Return the message-digest */ const byte *MD5::digest() { if (!_finished) { _finished = true; final(); } return _digest; } /* Reset the calculate state */ void MD5::reset() { _finished = false; /* reset number of bits. */ _count[0] = _count[1] = 0; /* Load magic initialization constants. */ _state[0] = 0x67452301; _state[1] = 0xefcdab89; _state[2] = 0x98badcfe; _state[3] = 0x10325476; } /* Updating the context with a input buffer. */ void MD5::update(const void *input, size_t length) { update((const byte *) input, length); } /* Updating the context with a string. */ void MD5::update(const NacosString &str) { update((const byte *) str.c_str(), str.length()); } /* Updating the context with a file. */ void MD5::update(ifstream &in) { if (!in) { return; } std::streamsize length; char buffer[BUFFER_SIZE]; while (!in.eof()) { in.read(buffer, BUFFER_SIZE); length = in.gcount(); if (length > 0) { update(buffer, length); } } in.close(); } /* MD5 block update operation. Continues an MD5 message-digest operation, processing another message block, and updating the context. */ void MD5::update(const byte *input, size_t length) { uint32 i, index, partLen; _finished = false; /* Compute number of bytes mod 64 */ index = (uint32)((_count[0] >> 3) & 0x3f); /* update number of bits */ if ((_count[0] += ((uint32) length << 3)) < ((uint32) length << 3)) { ++_count[1]; } _count[1] += ((uint32) length >> 29); partLen = 64 - index; /* transform as many times as possible. */ if (length >= partLen) { memcpy(&_buffer[index], input, partLen); transform(_buffer); for (i = partLen; i + 63 < length; i += 64) { transform(&input[i]); } index = 0; } else { i = 0; } /* Buffer remaining input */ memcpy(&_buffer[index], &input[i], length - i); } /* MD5 finalization. Ends an MD5 message-_digest operation, writing the the message _digest and zeroizing the context. */ void MD5::final() { byte bits[8]; uint32 oldState[4]; uint32 oldCount[2]; uint32 index, padLen; /* Save current state and count. */ memcpy(oldState, _state, 16); memcpy(oldCount, _count, 8); /* Save number of bits */ encode(_count, bits, 8); /* Pad out to 56 mod 64. */ index = (uint32)((_count[0] >> 3) & 0x3f); padLen = (index < 56) ? (56 - index) : (120 - index); update(PADDING, padLen); /* Append length (before padding) */ update(bits, 8); /* Store state in digest */ encode(_state, _digest, 16); /* Restore current state and count. */ memcpy(_state, oldState, 16); memcpy(_count, oldCount, 8); } /* MD5 basic transformation. Transforms _state based on block. */ void MD5::transform(const byte block[64]) { uint32 a = _state[0], b = _state[1], c = _state[2], d = _state[3], x[16]; decode(block, x, 64); /* Round 1 */ FF (a, b, c, d, x[0], S11, 0xd76aa478); /* 1 */ FF (d, a, b, c, x[1], S12, 0xe8c7b756); /* 2 */ FF (c, d, a, b, x[2], S13, 0x242070db); /* 3 */ FF (b, c, d, a, x[3], S14, 0xc1bdceee); /* 4 */ FF (a, b, c, d, x[4], S11, 0xf57c0faf); /* 5 */ FF (d, a, b, c, x[5], S12, 0x4787c62a); /* 6 */ FF (c, d, a, b, x[6], S13, 0xa8304613); /* 7 */ FF (b, c, d, a, x[7], S14, 0xfd469501); /* 8 */ FF (a, b, c, d, x[8], S11, 0x698098d8); /* 9 */ FF (d, a, b, c, x[9], S12, 0x8b44f7af); /* 10 */ FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ /* Round 2 */ GG (a, b, c, d, x[1], S21, 0xf61e2562); /* 17 */ GG (d, a, b, c, x[6], S22, 0xc040b340); /* 18 */ GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ GG (b, c, d, a, x[0], S24, 0xe9b6c7aa); /* 20 */ GG (a, b, c, d, x[5], S21, 0xd62f105d); /* 21 */ GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */ GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ GG (b, c, d, a, x[4], S24, 0xe7d3fbc8); /* 24 */ GG (a, b, c, d, x[9], S21, 0x21e1cde6); /* 25 */ GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ GG (c, d, a, b, x[3], S23, 0xf4d50d87); /* 27 */ GG (b, c, d, a, x[8], S24, 0x455a14ed); /* 28 */ GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ GG (d, a, b, c, x[2], S22, 0xfcefa3f8); /* 30 */ GG (c, d, a, b, x[7], S23, 0x676f02d9); /* 31 */ GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ /* Round 3 */ HH (a, b, c, d, x[5], S31, 0xfffa3942); /* 33 */ HH (d, a, b, c, x[8], S32, 0x8771f681); /* 34 */ HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ HH (a, b, c, d, x[1], S31, 0xa4beea44); /* 37 */ HH (d, a, b, c, x[4], S32, 0x4bdecfa9); /* 38 */ HH (c, d, a, b, x[7], S33, 0xf6bb4b60); /* 39 */ HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ HH (d, a, b, c, x[0], S32, 0xeaa127fa); /* 42 */ HH (c, d, a, b, x[3], S33, 0xd4ef3085); /* 43 */ HH (b, c, d, a, x[6], S34, 0x4881d05); /* 44 */ HH (a, b, c, d, x[9], S31, 0xd9d4d039); /* 45 */ HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ HH (b, c, d, a, x[2], S34, 0xc4ac5665); /* 48 */ /* Round 4 */ II (a, b, c, d, x[0], S41, 0xf4292244); /* 49 */ II (d, a, b, c, x[7], S42, 0x432aff97); /* 50 */ II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ II (b, c, d, a, x[5], S44, 0xfc93a039); /* 52 */ II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ II (d, a, b, c, x[3], S42, 0x8f0ccc92); /* 54 */ II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ II (b, c, d, a, x[1], S44, 0x85845dd1); /* 56 */ II (a, b, c, d, x[8], S41, 0x6fa87e4f); /* 57 */ II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ II (c, d, a, b, x[6], S43, 0xa3014314); /* 59 */ II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ II (a, b, c, d, x[4], S41, 0xf7537e82); /* 61 */ II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ II (c, d, a, b, x[2], S43, 0x2ad7d2bb); /* 63 */ II (b, c, d, a, x[9], S44, 0xeb86d391); /* 64 */ _state[0] += a; _state[1] += b; _state[2] += c; _state[3] += d; } /* Encodes input (ulong) into output (byte). Assumes length is a multiple of 4. */ void MD5::encode(const uint32 *input, byte *output, size_t length) { for (size_t i = 0, j = 0; j < length; ++i, j += 4) { output[j] = (byte)(input[i] & 0xff); output[j + 1] = (byte)((input[i] >> 8) & 0xff); output[j + 2] = (byte)((input[i] >> 16) & 0xff); output[j + 3] = (byte)((input[i] >> 24) & 0xff); } } /* Decodes input (byte) into output (ulong). Assumes length is a multiple of 4. */ void MD5::decode(const byte *input, uint32 *output, size_t length) { for (size_t i = 0, j = 0; j < length; ++i, j += 4) { output[i] = ((uint32) input[j]) | (((uint32) input[j + 1]) << 8) | (((uint32) input[j + 2]) << 16) | (((uint32) input[j + 3]) << 24); } } /* Convert byte array to hex string. */ NacosString MD5::bytesToHexString(const byte *input, size_t length) { NacosString str; str.reserve(length << 1); for (size_t i = 0; i < length; ++i) { int t = input[i]; int a = t / 16; int b = t % 16; str.append(1, HEX[a]); str.append(1, HEX[b]); } return str; } /* Convert digest to string value */ NacosString MD5::toString() { return bytesToHexString(digest(), 16); } }//namespace nacos ================================================ FILE: src/crypto/md5/md5.h ================================================ #ifndef MD5_H #define MD5_H #include #include #include #include "NacosString.h" namespace nacos{ /* Type define */ typedef unsigned char byte; typedef unsigned int uint32; using std::string; using std::ifstream; /* MD5 declaration. */ class MD5 { public: MD5(); MD5(const void *input, size_t length); MD5(const NacosString &str); MD5(ifstream &in); void update(const void *input, size_t length); void update(const NacosString &str); void update(ifstream &in); const byte *digest(); NacosString toString(); void reset(); private: void update(const byte *input, size_t length); void final(); void transform(const byte block[64]); void encode(const uint32 *input, byte *output, size_t length); void decode(const byte *input, uint32 *output, size_t length); NacosString bytesToHexString(const byte *input, size_t length); /* class uncopyable */ MD5(const MD5 &); MD5 &operator=(const MD5 &); private: uint32 _state[4]; /* state (ABCD) */ uint32 _count[2]; /* number of bits, modulo 2^64 (low-order word first) */ byte _buffer[64]; /* input buffer */ byte _digest[16]; /* message digest */ bool _finished; /* calculate finished ? */ static const byte PADDING[64]; /* padding for calculate */ static const char HEX[16]; enum { BUFFER_SIZE = 1024 }; }; }//namespace nacos #endif ================================================ FILE: src/debug/DebugAssertion.h ================================================ #ifndef __DEBUG_ASSERTION_H_ #define __DEBUG_ASSERTION_H_ #include #include #include "NacosString.h" namespace nacos{ #define TEST_ITEM_START { #define TEST_ITEM(testName, testfn) {(testName), (testfn)}, #define TEST_ITEM_END }; #define NACOS_ASSERT(x) \ if (!(x)) \ { \ printf("Assertion failed! file:" __FILE__":%d\n", __LINE__); \ abort(); \ } typedef bool (*TESTFN)(); typedef struct tagTestData { NacosString testName; TESTFN testFn; } TestData; #define SHOULD_BE_TRUE(assertion, message) \ do \ { \ if (!(assertion)) \ { \ cout << (message) << "...:failed" << endl; \ return false; \ } \ cout << (message) << "...:passed" << endl; \ } while(0); #define SHOULD_BE_FALSE(assertion, message) SHOULD_BE_TRUE(!(assertion), (message)) #ifdef NACOS_AUTH #define ADD_AUTH_INFO(x) \ do { \ (x)["nacos.auth.username"] = "nacos"; \ (x)["nacos.auth.password"] = "nacos"; \ } while (0) #else #define ADD_AUTH_INFO(x) #endif #ifdef NACOS_SPAS #define ADD_SPAS_INFO(x) \ do { \ (x)["secretKey"] = "nacos"; \ (x)["accessKey"] = "nacos"; \ } while (0) #else #define ADD_SPAS_INFO(x) #endif }//namespace nacos #endif ================================================ FILE: src/factory/NacosFactoryFactory.cpp ================================================ #include "factory/NacosFactoryFactory.h" #include "src/factory/NacosServiceFactory.h" namespace nacos { INacosServiceFactory *NacosFactoryFactory::getNacosFactory(const NacosString &_configFile) { NacosServiceFactory *factory = new NacosServiceFactory(_configFile); return factory; } INacosServiceFactory *NacosFactoryFactory::getNacosFactory(Properties &_props) { NacosServiceFactory *factory = new NacosServiceFactory(_props); return factory; } } ================================================ FILE: src/factory/NacosServiceFactory.cpp ================================================ // // Created by liuhanyu on 2020/8/30. // #include "src/factory/NacosServiceFactory.h" #include "src/init/Init.h" #include "src/naming/NacosNamingService.h" #include "src/naming/NacosNamingMaintainService.h" #include "ObjectConfigData.h" #include "src/config/NacosConfigService.h" #include "src/config/AppConfigManager.h" #include "src/http/HttpDelegate.h" #include "src/http/delegate/NoOpHttpDelegate.h" #include "src/http/delegate/NacosAuthHttpDelegate.h" #include "src/http/HTTPCli.h" #include "src/naming/subscribe/EventDispatcher.h" #include "src/naming/subscribe/SubscriptionPoller.h" #include "src/naming/subscribe/UdpNamingServiceListener.h" #include "src/naming/subscribe/HostReactor.h" #include "src/security/SecurityManager.h" #include "src/utils/ConfigParserUtils.h" #include "src/utils/SequenceProvider.h" #include "src/config/ConfigProxy.h" #include "src/utils/DirUtils.h" //Unlike Java, in cpp, there's no container, no spring to do the ORM job, so I have to handle it myself namespace nacos{ volatile bool NacosServiceFactory::logSystemInitialized = false; void buildSecurityManagerAndHttpDelegate(ObjectConfigData *objectConfigData) { AppConfigManager *appConfigManager = objectConfigData->_appConfigManager; if (appConfigManager->nacosAuthEnabled()) { //nacos authentication is enabled SecurityManager *securityManager = new SecurityManager(objectConfigData); objectConfigData->_securityManager = securityManager; HttpDelegate *httpDelegate = new NacosAuthHttpDelegate(objectConfigData); objectConfigData->_httpDelegate = httpDelegate; } else { HttpDelegate *httpDelegate = new NoOpHttpDelegate(objectConfigData); objectConfigData->_httpDelegate = httpDelegate; } } AppConfigManager *NacosServiceFactory::buildConfigManager(ObjectConfigData *objectConfigData) { //Create configuration data and load configs AppConfigManager *appConfigManager = NULL; if (configIsSet) { appConfigManager = new AppConfigManager(configFile); appConfigManager->loadConfig(configFile); } else { appConfigManager = new AppConfigManager(props); } objectConfigData->_appConfigManager = appConfigManager; return appConfigManager; } void NacosServiceFactory::initializeRuntimeLogSettings(AppConfigManager *_appConfigManager) { if (logSystemInitialized) { return; } { LockGuard __lockLogSystem(logSysInitLock); if (logSystemInitialized) { return; } logSystemInitialized = true; Properties copiedProps = _appConfigManager->getAllConfig(); Logger::applyLogSettings(copiedProps); } } //FIXME:Memory leak at initializing stage, e.g.: //when a HttpDelegate is allocated in CreateConfigService, after that an EXCEPTION is thrown during the initialization of ServerListManager //the resource for HttpDelegate is never released NamingService *NacosServiceFactory::CreateNamingService() NACOS_THROW(NacosException) { Init::doInit(); checkConfig(); ObjectConfigData *objectConfigData = new ObjectConfigData(NAMING); objectConfigData->name = "config"; objectConfigData->encoding = "UTF-8"; AppConfigManager *appConfigManager = buildConfigManager(objectConfigData); initializeRuntimeLogSettings(appConfigManager); //Create http client IHttpCli *httpCli= new HTTPCli(); objectConfigData->_httpCli = httpCli; buildSecurityManagerAndHttpDelegate(objectConfigData); //Create server manager ServerListManager *serverListManager = new ServerListManager(objectConfigData); objectConfigData->_serverListManager = serverListManager; //Create naming service & heartbeat sender NamingProxy *namingProxy = new NamingProxy(objectConfigData); objectConfigData->_serverProxy = namingProxy; BeatReactor *beatReactor = new BeatReactor(objectConfigData); objectConfigData->_beatReactor = beatReactor; EventDispatcher *eventDispatcher = new EventDispatcher(); objectConfigData->_eventDispatcher = eventDispatcher; SubscriptionPoller *subscriptionPoller = new SubscriptionPoller(objectConfigData); objectConfigData->_subscriptionPoller = subscriptionPoller; UdpNamingServiceListener *udpNamingServiceListener = new UdpNamingServiceListener(objectConfigData); objectConfigData->_udpNamingServiceListener = udpNamingServiceListener; HostReactor *hostReactor = new HostReactor(objectConfigData); objectConfigData->_hostReactor = hostReactor; const NacosString &seqConfile = appConfigManager->get(PropertyKeyConst::INSTANCE_ID_SEQ_FILE); SequenceProvider *sequenceProvider = new SequenceProvider(seqConfile, 1, 10); objectConfigData->_sequenceProvider = sequenceProvider; objectConfigData->checkAssembledObject(); NamingService *instance = new NacosNamingService(objectConfigData); log_debug("Created config data: %s", objectConfigData->name.c_str()); return instance; } ConfigService *NacosServiceFactory::CreateConfigService() NACOS_THROW(NacosException) { Init::doInit(); checkConfig(); ObjectConfigData *objectConfigData = new ObjectConfigData(CONFIG); objectConfigData->name = "name"; objectConfigData->encoding = "UTF-8"; AppConfigManager *appConfigManager = buildConfigManager(objectConfigData); initializeRuntimeLogSettings(appConfigManager); //Create http client IHttpCli *httpCli = NULL; httpCli = new HTTPCli(); objectConfigData->_httpCli = httpCli; buildSecurityManagerAndHttpDelegate(objectConfigData); //Create server manager ServerListManager *serverListManager = new ServerListManager(objectConfigData); objectConfigData->_serverListManager = serverListManager; ConfigProxy *configProxy = new ConfigProxy(objectConfigData); objectConfigData->_configProxy = configProxy; LocalSnapshotManager *localSnapshotManager = new LocalSnapshotManager(appConfigManager); objectConfigData->_localSnapshotManager = localSnapshotManager; ClientWorker *clientWorker = new ClientWorker(objectConfigData); objectConfigData->_clientWorker = clientWorker; objectConfigData->checkAssembledObject(); ConfigService *instance = new NacosConfigService(objectConfigData); log_debug("Created config data: %s", objectConfigData->name.c_str()); return instance; } NamingMaintainService *NacosServiceFactory::CreateNamingMaintainService() NACOS_THROW(NacosException){ Init::doInit(); checkConfig(); ObjectConfigData *objectConfigData = new ObjectConfigData(MAINTAIN); objectConfigData->name = "config"; objectConfigData->encoding = "UTF-8"; AppConfigManager *appConfigManager = buildConfigManager(objectConfigData); initializeRuntimeLogSettings(appConfigManager); //Create http client IHttpCli *httpCli= new HTTPCli(); objectConfigData->_httpCli = httpCli; buildSecurityManagerAndHttpDelegate(objectConfigData); //Create server manager ServerListManager *serverListManager = new ServerListManager(objectConfigData); objectConfigData->_serverListManager = serverListManager; //Create naming service & heartbeat sender NamingProxy *namingProxy = new NamingProxy(objectConfigData); objectConfigData->_serverProxy = namingProxy; NacosNamingMaintainService *instance = new NacosNamingMaintainService(objectConfigData); log_debug("Created config data: %s", objectConfigData->name.c_str()); return instance; } NacosServiceFactory::~NacosServiceFactory() { } void NacosServiceFactory::checkConfig() NACOS_THROW(InvalidFactoryConfigException) { if (!configIsSet && !propsIsSet) { throw InvalidFactoryConfigException(); } } void NacosServiceFactory::setConfig(const NacosString &_configFile) { configIsSet = true; configFile = _configFile; }; void NacosServiceFactory::setProps(Properties &_props) { propsIsSet = true; props = _props; }; NacosServiceFactory::NacosServiceFactory() { configIsSet = false; propsIsSet = false; setConfig(DirUtils::getCwd() + "/" + ConfigConstant::DEFAULT_CONFIG_FILE); } NacosServiceFactory::NacosServiceFactory(const NacosString &_configFile) { configIsSet = false; propsIsSet = false; setConfig(_configFile); } NacosServiceFactory::NacosServiceFactory(Properties &_props) { configIsSet = false; propsIsSet = false; setProps(_props); } }//namespace nacos ================================================ FILE: src/factory/NacosServiceFactory.h ================================================ // // Created by liuhanyu on 2020/8/30. // #ifndef NACOS_SDK_CPP_NACOSSERVICEFACTORY_H #define NACOS_SDK_CPP_NACOSSERVICEFACTORY_H #include "factory/INacosServiceFactory.h" #include "Compatibility.h" #include "src/thread/Mutex.h" namespace nacos{ class AppConfigManager; class ObjectConfigData; class NacosServiceFactory : public INacosServiceFactory { private: NacosString configFile; Properties props; bool configIsSet; bool propsIsSet; Mutex logSysInitLock; static volatile bool logSystemInitialized; void initializeRuntimeLogSettings(AppConfigManager *_appConfigManager); void checkConfig() NACOS_THROW(InvalidFactoryConfigException); AppConfigManager *buildConfigManager(ObjectConfigData *objectConfigData); public: void setConfig(const NacosString &_configFile); void setProps(Properties &_props); virtual NamingService *CreateNamingService() NACOS_THROW(NacosException); virtual ConfigService *CreateConfigService() NACOS_THROW(NacosException); virtual NamingMaintainService *CreateNamingMaintainService() NACOS_THROW(NacosException); NacosServiceFactory(); NacosServiceFactory(const NacosString &_configFile); NacosServiceFactory(Properties &_props); virtual ~NacosServiceFactory(); }; }//namespace nacos #endif //NACOS_SDK_CPP_NACOSSERVICEFACTORY_H ================================================ FILE: src/factory/ObjectConfigData.cpp ================================================ #include "ObjectConfigData.h" #include "src/http/HttpDelegate.h" #include "src/naming/NamingProxy.h" #include "src/naming/beat/BeatReactor.h" #include "src/naming/subscribe/EventDispatcher.h" #include "src/naming/subscribe/SubscriptionPoller.h" #include "src/naming/subscribe/UdpNamingServiceListener.h" #include "src/naming/subscribe/HostReactor.h" #include "src/listen/ClientWorker.h" #include "src/security/SecurityManager.h" #include "src/utils/UuidUtils.h" #include "src/utils/SequenceProvider.h" #include "src/config/ConfigProxy.h" #include "src/debug/DebugAssertion.h" namespace nacos{ ObjectConfigData::ObjectConfigData(FactoryType theFactoryType) { objectId = UuidUtils::generateUuid(); factoryType = theFactoryType; _httpDelegate = NULL; _httpCli = NULL; _serverProxy = NULL; _beatReactor = NULL; _eventDispatcher = NULL; _subscriptionPoller = NULL; _appConfigManager = NULL; _serverListManager = NULL; _clientWorker = NULL; _localSnapshotManager = NULL; _securityManager = NULL; _configProxy = NULL; } void ObjectConfigData::checkNamingService() NACOS_THROW(NacosException) { if (factoryType != NAMING) { throw NacosException(NacosException::INVALID_PARAM, "Invalid configuration for naming service, please check"); } NACOS_ASSERT(_httpDelegate != NULL); NACOS_ASSERT(_httpCli != NULL); NACOS_ASSERT(_serverProxy != NULL); NACOS_ASSERT(_beatReactor != NULL); NACOS_ASSERT(_eventDispatcher != NULL); NACOS_ASSERT(_subscriptionPoller != NULL); NACOS_ASSERT(_hostReactor != NULL); NACOS_ASSERT(_appConfigManager != NULL); NACOS_ASSERT(_serverListManager != NULL); NACOS_ASSERT(_udpNamingServiceListener != NULL); NACOS_ASSERT(_udpNamingServiceListener != NULL); NACOS_ASSERT(_sequenceProvider != NULL); } void ObjectConfigData::checkConfigService() NACOS_THROW(NacosException) { if (factoryType != CONFIG) { throw NacosException(NacosException::INVALID_PARAM, "Invalid configuration for config service, please check"); } NACOS_ASSERT(_appConfigManager != NULL); NACOS_ASSERT(_httpCli != NULL); NACOS_ASSERT(_httpDelegate != NULL); NACOS_ASSERT(_serverListManager != NULL); NACOS_ASSERT(_clientWorker != NULL); NACOS_ASSERT(_localSnapshotManager != NULL); NACOS_ASSERT(_configProxy != NULL); } void ObjectConfigData::checkMaintainService() NACOS_THROW(NacosException) { if (factoryType != MAINTAIN) { throw NacosException(NacosException::INVALID_PARAM, "Invalid configuration for maintain service, please check"); } NACOS_ASSERT(_serverProxy != NULL); NACOS_ASSERT(_httpDelegate != NULL); NACOS_ASSERT(_httpCli != NULL); NACOS_ASSERT(_appConfigManager != NULL); NACOS_ASSERT(_serverListManager != NULL); } void ObjectConfigData::destroyConfigService() { if (_clientWorker != NULL) { _clientWorker->stopListening(); } if (_securityManager != NULL) { _securityManager->stop(); } if (_serverListManager) { _serverListManager->stop(); } if (_clientWorker != NULL) { _clientWorker->stopListening(); delete _clientWorker; _clientWorker = NULL; } if (_httpDelegate != NULL) { delete _httpDelegate; _httpDelegate = NULL; } if (_securityManager != NULL) { delete _securityManager; _securityManager = NULL; } if (_serverListManager != NULL) { delete _serverListManager; _serverListManager = NULL; } if (_httpCli != NULL) { delete _httpCli; _httpCli = NULL; } if (_appConfigManager != NULL) { delete _appConfigManager; _appConfigManager = NULL; } if (_configProxy != NULL) { delete _configProxy; _configProxy = NULL; } } void ObjectConfigData::destroyNamingService() { if (_beatReactor != NULL) { _beatReactor->stop(); } if (_subscriptionPoller != NULL) { _subscriptionPoller->stop(); } if (_udpNamingServiceListener != NULL) { _udpNamingServiceListener->stop(); } if (_eventDispatcher != NULL) { _eventDispatcher->stop(); } if (_securityManager != NULL) { _securityManager->stop(); } if (_serverListManager) { _serverListManager->stop(); } if (_httpDelegate != NULL) { delete _httpDelegate; _httpDelegate = NULL; } if (_beatReactor != NULL) { delete _beatReactor; _beatReactor = NULL; } if (_subscriptionPoller != NULL) { delete _subscriptionPoller; _subscriptionPoller = NULL; } if (_udpNamingServiceListener != NULL) { delete _udpNamingServiceListener; _udpNamingServiceListener = NULL; } if (_eventDispatcher != NULL) { delete _eventDispatcher; _eventDispatcher = NULL; } if (_hostReactor != NULL) { delete _hostReactor; _hostReactor = NULL; } if (_serverProxy != NULL) { delete _serverProxy; _serverProxy = NULL; } if (_securityManager != NULL) { delete _securityManager; _securityManager = NULL; } if (_serverListManager != NULL) { delete _serverListManager; _serverListManager = NULL; } if (_httpDelegate != NULL) { delete _httpDelegate; _httpDelegate = NULL; } if (_httpCli != NULL) { delete _httpCli; _httpCli = NULL; } if (_appConfigManager != NULL) { delete _appConfigManager; _appConfigManager = NULL; } if (_sequenceProvider != NULL) { delete _sequenceProvider; _sequenceProvider = NULL; } } void ObjectConfigData::destroyMaintainService() { if (_serverListManager != NULL) { _serverListManager->stop(); } if (_securityManager != NULL) { _securityManager->stop(); } if (_serverProxy != NULL) { delete _serverProxy; _serverProxy = NULL; } if (_serverListManager != NULL) { delete _serverListManager; _serverListManager = NULL; } if (_appConfigManager != NULL) { delete _appConfigManager; _appConfigManager = NULL; } if (_securityManager != NULL) { delete _securityManager; _securityManager = NULL; } if (_httpDelegate != NULL) { delete _httpDelegate; _httpDelegate = NULL; } if (_httpCli != NULL) { delete _httpCli; _httpCli = NULL; } } void ObjectConfigData::checkAssembledObject() NACOS_THROW(NacosException) { switch (factoryType) { case NAMING: checkNamingService(); return; case CONFIG: checkConfigService(); return; case MAINTAIN: checkMaintainService(); break; default: abort();//never happens } } ObjectConfigData::~ObjectConfigData() { switch (factoryType) { case NAMING: destroyNamingService(); return; case CONFIG: destroyConfigService(); return; case MAINTAIN: destroyMaintainService(); break; default: abort();//never happens } } }//namespace nacos ================================================ FILE: src/factory/ObjectConfigData.h ================================================ #ifndef __OBJ_CFG_DATA_H_ #define __OBJ_CFG_DATA_H_ #include "naming/NamingService.h" #include "config/ConfigService.h" #include "NacosExceptions.h" #include "Compatibility.h" #include namespace nacos{ class HttpDelegate; class IHttpCli; class NamingProxy; class BeatReactor; class EventDispatcher; class SubscriptionPoller; class AppConfigManager; class ServerListManager; class ClientWorker; class LocalSnapshotManager; class SecurityManager; class UdpNamingServiceListener; class HostReactor; class ConfigProxy; template class SequenceProvider; enum FactoryType { CONFIG = 0, NAMING = 1, MAINTAIN = 2 }; class ObjectConfigData { private: FactoryType factoryType; void destroyConfigService(); void destroyNamingService(); void destroyMaintainService(); //These functions are designed to prevent coding problems //(i.e.: forget to initialize HttpDelegate for a ConfigService) rather than run-time errors void checkConfigService() NACOS_THROW(NacosException); void checkNamingService() NACOS_THROW(NacosException); void checkMaintainService() NACOS_THROW(NacosException); NacosString objectId; public: const NacosString &getObjectId() const { return objectId; }; ObjectConfigData(FactoryType theFactoryType); void checkAssembledObject() NACOS_THROW(NacosException); ~ObjectConfigData(); NacosString name; NacosString encoding; HttpDelegate *_httpDelegate; IHttpCli *_httpCli; NamingProxy *_serverProxy; BeatReactor *_beatReactor; EventDispatcher *_eventDispatcher; SubscriptionPoller *_subscriptionPoller; AppConfigManager *_appConfigManager; ServerListManager *_serverListManager; ClientWorker *_clientWorker; LocalSnapshotManager *_localSnapshotManager; SecurityManager *_securityManager; UdpNamingServiceListener *_udpNamingServiceListener; HostReactor *_hostReactor; SequenceProvider *_sequenceProvider; ConfigProxy *_configProxy; }; }//namespace nacos #endif ================================================ FILE: src/http/HTTPCli.cpp ================================================ #include #include "HTTPCli.h" #include "src/utils/url.h" #include "constant/UtilAndComs.h" #include "src/log/Logger.h" using namespace std; namespace nacos{ static size_t receiveResponseCallback( void *contents, size_t size, size_t nmemb, void *userp ) { size_t realsize = size * nmemb; NacosString *strbuf = (NacosString *) userp; strbuf->append((char *) contents, realsize); return realsize; } static size_t receiveHeaderCallback( void *contents, size_t size, size_t nmemb, void *userp ) { char *content_s = (char *) contents; //Parse the 'HeaderName: HeaderContent' format char *pos = strchr(content_s, ':'); if (pos != NULL)//Skip status { std::map *respheaders = (std::map *) userp; NacosString k = NacosString(content_s, pos - content_s); NacosString v = NacosString(pos + 1); (*respheaders)[k] = v; } size_t realsize = size * nmemb; log_debug("[HTTPCli]-receivedHeaders: %s", (char *) contents); return realsize; } HTTPCli::HTTPCli() { /* init the curl session */ pthread_key_create(&pthreadKey, HTTPCli::destroyCurlHandle); } CURL *HTTPCli::getCurlHandle() { CURL *curlHandle = pthread_getspecific(pthreadKey); if (curlHandle == NULL) { curlHandle = curl_easy_init(); pthread_setspecific(pthreadKey, reinterpret_cast(curlHandle)); } return curlHandle; } void HTTPCli::destroyCurlHandle(void *arg) { CURL *curlHandle = reinterpret_cast(arg); if (curlHandle != NULL) { curl_easy_cleanup(curlHandle); curlHandle = NULL; } } void HTTPCli::HTTPBasicSettings(CURL *curlHandle) { curl_easy_setopt(curlHandle, CURLOPT_USERAGENT, UtilAndComs::VERSION.c_str()); curl_easy_setopt(curlHandle, CURLOPT_WRITEFUNCTION, receiveResponseCallback); curl_easy_setopt(curlHandle, CURLOPT_HEADERFUNCTION, receiveHeaderCallback); curl_easy_setopt(curlHandle, CURLOPT_TCP_KEEPALIVE, 1L); /* keep-alive idle time to 120 seconds */ curl_easy_setopt(curlHandle, CURLOPT_TCP_KEEPIDLE, 120L); /* interval time between keep-alive probes: 60 seconds */ curl_easy_setopt(curlHandle, CURLOPT_TCP_KEEPINTVL, 60L); } NacosString HTTPCli::encodingParams(list ¶ms) { NacosString encodedParms = ""; for (list::iterator it = params.begin(); it != params.end(); it++) { if (encodedParms.compare("") != 0) { encodedParms.append("&"); } encodedParms.append(urlencode(*it)); it++; encodedParms.append("=" + urlencode(*it)); } return encodedParms; } NacosString HTTPCli::encodingParams(map ¶ms) { NacosString encodedParms = ""; for (map::iterator it = params.begin(); it != params.end(); it++) { if (encodedParms.compare("") != 0) { encodedParms.append("&"); } encodedParms.append(it->first + "=" + it->second); } return encodedParms; } void HTTPCli::assembleHeaders(list &assembledHeaders, list &headers) { for (list::iterator it = headers.begin(); it != headers.end(); it++) { NacosString curHeader = ""; curHeader.append(*it); curHeader += ": "; it++; curHeader.append(*it); assembledHeaders.push_back(curHeader); } } HttpResult HTTPCli::httpGet ( const NacosString &path, list &headers, list ¶mValues, const NacosString &encoding, long readTimeoutMs ) NACOS_THROW(NetworkException) { NacosString parmVal; parmVal = encodingParams(paramValues); return httpGetInternal(path, headers, parmVal, encoding, readTimeoutMs); } HttpResult HTTPCli::httpGet ( const NacosString &path, list &headers, map ¶mValues, const NacosString &encoding, long readTimeoutMs ) NACOS_THROW(NetworkException) { NacosString parmVal; parmVal = encodingParams(paramValues); return httpGetInternal(path, headers, parmVal, encoding, readTimeoutMs); } HttpResult HTTPCli::httpGetInternal ( const NacosString &path, list &headers, const NacosString ¶mValues, const NacosString &encoding, long readTimeoutMs ) NACOS_THROW(NetworkException) { CURL *curlHandle = getCurlHandle(); CURLcode curlres; NacosString Url = path; if (paramValues.compare("") != 0) { Url += "?" + paramValues; } log_debug("[HTTPCli]-Get:Assembled URL with parms:%s\n", Url.c_str()); /*Headers look like: foo bar bax lol We convert it into sth like with assembleHeaders(): foo: bar bax: lol */ list assembledHeaders; assembleHeaders(assembledHeaders, headers); //clear-ups curl_easy_reset(curlHandle); /* specify URL to get */ curl_easy_setopt(curlHandle, CURLOPT_URL, Url.c_str()); //Setup common parameters HTTPBasicSettings(curlHandle); /* send all data to this function */ NacosString strbuf = ""; /* we pass our 'strbuf' struct to the callback function */ curl_easy_setopt(curlHandle, CURLOPT_WRITEDATA, (void *) &strbuf); /* Get response headers from the response */ std::map respheaders; curl_easy_setopt(curlHandle, CURLOPT_HEADERDATA, (void *) &respheaders); //TODO:Time out in a more precise way curl_easy_setopt(curlHandle, CURLOPT_TIMEOUT, readTimeoutMs / 1000); /*Add the request headers to the request*/ struct curl_slist *headerlist = NULL; for (list::iterator it = assembledHeaders.begin(); it != assembledHeaders.end(); it++) { headerlist = curl_slist_append(headerlist, it->c_str()); } if (headerlist != NULL) { curl_easy_setopt(curlHandle, CURLOPT_HTTPHEADER, headerlist); } /* get it! */ curlres = curl_easy_perform(curlHandle); /*Since the headerlist is not needed anymore, free it to prevent mem leak*/ if (headerlist != NULL) { curl_slist_free_all(headerlist); headerlist = NULL; } if (curlres != CURLE_OK) { log_error("[HTTPCli]-Get:curl_easy_perform() failed: %d - %s\n", curlres, curl_easy_strerror(curlres)); throw NetworkException(curlres, curl_easy_strerror(curlres)); } long response_code; curl_easy_getinfo(curlHandle, CURLINFO_RESPONSE_CODE, &response_code); HttpResult httpresp = HttpResult(response_code, strbuf, respheaders); httpresp.curlcode = curlres; log_debug("[HTTPCli]-Get:%lu bytes retrieved\n", (unsigned long) strbuf.length()); log_debug("[HTTPCli]-Get:content:%s\n", strbuf.c_str()); log_debug("[HTTPCli]-Get:resp-code:%d\n", response_code); return httpresp; } /*httpPost, post data are passed in list form like this: foo bar bax lol We convert it into sth like this: foo=bar&bax=lol */ HttpResult HTTPCli::httpPost ( const NacosString &path, list &headers, list ¶mValues, const NacosString &encoding, long readTimeoutMs ) NACOS_THROW(NetworkException) { NacosString parmVal; parmVal = encodingParams(paramValues); return httpPostInternal(path, headers, parmVal, encoding, readTimeoutMs); } //httpPost, post data are passed in map form HttpResult HTTPCli::httpPost ( const NacosString &path, list &headers, map ¶mValues, const NacosString &encoding, long readTimeoutMs ) NACOS_THROW(NetworkException) { NacosString parmVal; parmVal = encodingParams(paramValues); return httpPostInternal(path, headers, parmVal, encoding, readTimeoutMs); } //Implement of httpPost HttpResult HTTPCli::httpPostInternal ( const NacosString &path, list &headers, const NacosString ¶mValues, const NacosString &encoding, long readTimeoutMs ) NACOS_THROW(NetworkException) { CURL *curlHandle = getCurlHandle(); CURLcode curlres; NacosString Url = path; log_debug("[HTTPCli]-POST:Assembled URL with parms:%s\n", Url.c_str()); /*Headers look like: foo bar bax lol We convert it into sth like with assembleHeaders(): foo: bar bax: lol */ list assembledHeaders; assembleHeaders(assembledHeaders, headers); log_debug("[HTTPCli]-POST:Post data:%s\n", paramValues.c_str()); //clear-ups curl_easy_reset(curlHandle); /* specify URL to get */ curl_easy_setopt(curlHandle, CURLOPT_URL, Url.c_str()); curl_easy_setopt(curlHandle, CURLOPT_CUSTOMREQUEST, "POST"); curl_easy_setopt(curlHandle, CURLOPT_POSTFIELDS, paramValues.c_str()); curl_easy_setopt(curlHandle, CURLOPT_POSTFIELDSIZE, paramValues.size()); //Setup common parameters HTTPBasicSettings(curlHandle); NacosString strbuf = ""; /* we pass our 'strbuf' struct to the callback function */ curl_easy_setopt(curlHandle, CURLOPT_WRITEDATA, (void *) &strbuf); /* Get response headers from the response */ std::map respheaders; curl_easy_setopt(curlHandle, CURLOPT_HEADERDATA, (void *) &respheaders); //TODO:Time out in a more precise way curl_easy_setopt(curlHandle, CURLOPT_TIMEOUT, readTimeoutMs / 1000); /*Add the request headers to the request*/ struct curl_slist *headerlist = NULL; for (list::iterator it = assembledHeaders.begin(); it != assembledHeaders.end(); it++) { headerlist = curl_slist_append(headerlist, it->c_str()); log_debug("[HTTPCli]-POST:RequestHeaders:%s\n", it->c_str()); } if (headerlist != NULL) { curl_easy_setopt(curlHandle, CURLOPT_HTTPHEADER, headerlist); } /* get it! */ curlres = curl_easy_perform(curlHandle); /*Since the headerlist is not needed anymore, free it to prevent mem leak*/ if (headerlist != NULL) { curl_slist_free_all(headerlist); headerlist = NULL; } if (curlres != CURLE_OK) { log_error("[HTTPCli]-POST:curl_easy_perform() failed: %d - %s\n", curlres, curl_easy_strerror(curlres)); throw NetworkException(curlres, curl_easy_strerror(curlres)); } long response_code; curl_easy_getinfo(curlHandle, CURLINFO_RESPONSE_CODE, &response_code); HttpResult httpresp = HttpResult(response_code, strbuf, respheaders); httpresp.curlcode = curlres; log_debug("[HTTPCli]-POST:%lu bytes retrieved\n", (unsigned long) strbuf.length()); log_debug("[HTTPCli]-POST:content:%s\n", strbuf.c_str()); log_debug("[HTTPCli]-POST:resp-code:%d\n", response_code); return httpresp; } /*httpPut, put data are passed in list form like this: foo bar bax lol We convert it into sth like this: foo=bar&bax=lol */ HttpResult HTTPCli::httpPut ( const NacosString &path, list &headers, list ¶mValues, const NacosString &encoding, long readTimeoutMs ) NACOS_THROW(NetworkException) { NacosString parmVal; parmVal = encodingParams(paramValues); return httpPutInternal(path, headers, parmVal, encoding, readTimeoutMs); } //httpPut, post data are passed in map form HttpResult HTTPCli::httpPut ( const NacosString &path, list &headers, map ¶mValues, const NacosString &encoding, long readTimeoutMs ) NACOS_THROW(NetworkException) { NacosString parmVal; parmVal = encodingParams(paramValues); return httpPutInternal(path, headers, parmVal, encoding, readTimeoutMs); } //Implement of httpPut HttpResult HTTPCli::httpPutInternal ( const NacosString &path, list &headers, const NacosString ¶mValues, const NacosString &encoding, long readTimeoutMs ) NACOS_THROW(NetworkException) { CURL *curlHandle = getCurlHandle(); CURLcode curlres; NacosString Url = path; log_debug("[HTTPCli]-PUT:Assembled URL with parms:%s\n", Url.c_str()); /*Headers look like: foo bar bax lol We convert it into sth like with assembleHeaders(): foo: bar bax: lol */ list assembledHeaders; assembleHeaders(assembledHeaders, headers); log_debug("[HTTPCli]-PUT:Put data:%s\n", paramValues.c_str()); //clear-ups curl_easy_reset(curlHandle); /* specify URL to get */ curl_easy_setopt(curlHandle, CURLOPT_URL, Url.c_str()); curl_easy_setopt(curlHandle, CURLOPT_CUSTOMREQUEST, "PUT"); curl_easy_setopt(curlHandle, CURLOPT_POSTFIELDS, paramValues.c_str()); curl_easy_setopt(curlHandle, CURLOPT_POSTFIELDSIZE, paramValues.size()); //Setup common parameters HTTPBasicSettings(curlHandle); NacosString strbuf = ""; /* we pass our 'strbuf' struct to the callback function */ curl_easy_setopt(curlHandle, CURLOPT_WRITEDATA, (void *) &strbuf); /* Get response headers from the response */ std::map respheaders; curl_easy_setopt(curlHandle, CURLOPT_HEADERDATA, (void *) &respheaders); //TODO:Time out in a more precise way curl_easy_setopt(curlHandle, CURLOPT_TIMEOUT, readTimeoutMs / 1000); /*Add the request headers to the request*/ struct curl_slist *headerlist = NULL; for (list::iterator it = assembledHeaders.begin(); it != assembledHeaders.end(); it++) { headerlist = curl_slist_append(headerlist, it->c_str()); log_debug("[HTTPCli]-PUT:RequestHeaders:%s\n", it->c_str()); } if (headerlist != NULL) { curl_easy_setopt(curlHandle, CURLOPT_HTTPHEADER, headerlist); } /* get it! */ curlres = curl_easy_perform(curlHandle); /*Since the headerlist is not needed anymore, free it to prevent mem leak*/ if (headerlist != NULL) { curl_slist_free_all(headerlist); headerlist = NULL; } if (curlres != CURLE_OK) { log_error("[HTTPCli]-PUT:curl_easy_perform() failed: %d - %s\n", curlres, curl_easy_strerror(curlres)); throw NetworkException(curlres, curl_easy_strerror(curlres)); } long response_code; curl_easy_getinfo(curlHandle, CURLINFO_RESPONSE_CODE, &response_code); HttpResult httpresp = HttpResult(response_code, strbuf, respheaders); httpresp.curlcode = curlres; log_debug("[HTTPCli]-PUT:%lu bytes retrieved\n", (unsigned long) strbuf.length()); log_debug("[HTTPCli]-PUT:content:%s\n", strbuf.c_str()); log_debug("[HTTPCli]-PUT:resp-code:%d\n", response_code); return httpresp; } HttpResult HTTPCli::httpDelete ( const NacosString &path, list &headers, list ¶mValues, const NacosString &encoding, long readTimeoutMs ) NACOS_THROW(NetworkException) { NacosString parmVal; parmVal = encodingParams(paramValues); return httpDeleteInternal(path, headers, parmVal, encoding, readTimeoutMs); } HttpResult HTTPCli::httpDelete ( const NacosString &path, list &headers, map ¶mValues, const NacosString &encoding, long readTimeoutMs ) NACOS_THROW(NetworkException) { NacosString parmVal; parmVal = encodingParams(paramValues); return httpDeleteInternal(path, headers, parmVal, encoding, readTimeoutMs); } HttpResult HTTPCli::httpDeleteInternal ( const NacosString &path, list &headers, const NacosString ¶mValues, const NacosString &encoding, long readTimeoutMs ) NACOS_THROW(NetworkException) { CURL *curlHandle = getCurlHandle(); CURLcode curlres; NacosString Url = path; Url += "?" + paramValues; log_debug("[HTTPCli]-DELETE:Assembled URL with parms:%s\n", Url.c_str()); /*Headers look like: foo bar bax lol We convert it into sth like with assembleHeaders(): foo: bar bax: lol */ list assembledHeaders; assembleHeaders(assembledHeaders, headers); //clear-ups curl_easy_reset(curlHandle); /* specify URL to get */ curl_easy_setopt(curlHandle, CURLOPT_URL, Url.c_str()); //Setup common parameters HTTPBasicSettings(curlHandle); /* Set to DELETE, since this is a delete request*/ curl_easy_setopt(curlHandle, CURLOPT_CUSTOMREQUEST, "DELETE"); NacosString strbuf = ""; /* we pass our 'strbuf' struct to the callback function */ curl_easy_setopt(curlHandle, CURLOPT_WRITEDATA, (void *) &strbuf); /* Get response headers from the response */ std::map respheaders; curl_easy_setopt(curlHandle, CURLOPT_HEADERDATA, (void *) &respheaders); //TODO:Time out in a more precise way curl_easy_setopt(curlHandle, CURLOPT_TIMEOUT, readTimeoutMs / 1000); /*Add the request headers to the request*/ struct curl_slist *headerlist = NULL; for (list::iterator it = assembledHeaders.begin(); it != assembledHeaders.end(); it++) { headerlist = curl_slist_append(headerlist, it->c_str()); } if (headerlist != NULL) { curl_easy_setopt(curlHandle, CURLOPT_HTTPHEADER, headerlist); } /* get it! */ curlres = curl_easy_perform(curlHandle); /*Since the headerlist is not needed anymore, free it to prevent mem leak*/ if (headerlist != NULL) { curl_slist_free_all(headerlist); headerlist = NULL; } if (curlres != CURLE_OK) { log_error("[HTTPCli]-DELETE:curl_easy_perform() failed: %d - %s\n", curlres, curl_easy_strerror(curlres)); throw NetworkException(curlres, curl_easy_strerror(curlres)); } long response_code; curl_easy_getinfo(curlHandle, CURLINFO_RESPONSE_CODE, &response_code); HttpResult httpresp = HttpResult(response_code, strbuf, respheaders); httpresp.curlcode = curlres; log_debug("[HTTPCli]-DELETE:%lu bytes retrieved\n", (unsigned long) strbuf.length()); log_debug("[HTTPCli]-DELETE:content:%s\n", strbuf.c_str()); log_debug("[HTTPCli]-DELETE:resp-code:%d\n", response_code); return httpresp; } HTTPCli::~HTTPCli() { CURL *curlHandle = pthread_getspecific(pthreadKey); if (curlHandle != NULL) { curl_easy_cleanup(curlHandle); } pthread_key_delete(pthreadKey); } void HTTPCli::HTTP_GLOBAL_INIT() { curl_global_init(CURL_GLOBAL_ALL); } void HTTPCli::HTTP_GLOBAL_DEINIT() { curl_global_cleanup(); } }//namespace nacos ================================================ FILE: src/http/HTTPCli.h ================================================ #ifndef __HTTP_CLI_H_ #define __HTTP_CLI_H_ #include #include #include "NacosString.h" #include "NacosExceptions.h" #include "IHttpCli.h" #include "Compatibility.h" namespace nacos{ class HTTPCli : public IHttpCli { private: //CURL *curlHandle; pthread_key_t pthreadKey; CURL *getCurlHandle(); static void destroyCurlHandle(void *arg); HttpResult httpGetInternal ( const NacosString &path, std::list &headers, const NacosString ¶mValues, const NacosString &encoding, long readTimeoutMs ) NACOS_THROW(NetworkException); HttpResult httpPostInternal ( const NacosString &path, std::list &headers, const NacosString ¶mValues, const NacosString &encoding, long readTimeoutMs ) NACOS_THROW(NetworkException); HttpResult httpPutInternal ( const NacosString &path, std::list &headers, const NacosString ¶mValues, const NacosString &encoding, long readTimeoutMs ) NACOS_THROW(NetworkException); HttpResult httpDeleteInternal ( const NacosString &path, std::list &headers, const NacosString ¶mValues, const NacosString &encoding, long readTimeoutMs ) NACOS_THROW(NetworkException); public: static NacosString encodingParams(std::list ¶ms); static NacosString encodingParams(std::map ¶ms); static void assembleHeaders(std::list &assembledHeaders, std::list &headers); static void HTTPBasicSettings(CURL *curlHandle); static void HTTP_GLOBAL_INIT(); static void HTTP_GLOBAL_DEINIT(); HTTPCli(); ~HTTPCli(); HttpResult httpGet( const NacosString &path, std::list &headers, std::list ¶mValues, const NacosString &encoding, long readTimeoutMs ) NACOS_THROW(NetworkException); HttpResult httpGet( const NacosString &path, std::list &headers, std::map ¶mValues, const NacosString &encoding, long readTimeoutMs ) NACOS_THROW(NetworkException); HttpResult httpDelete( const NacosString &path, std::list &headers, std::list ¶mValues, const NacosString &encoding, long readTimeoutMs ) NACOS_THROW(NetworkException); HttpResult httpDelete( const NacosString &path, std::list &headers, std::map ¶mValues, const NacosString &encoding, long readTimeoutMs ) NACOS_THROW(NetworkException); HttpResult httpPost( const NacosString &path, std::list &headers, std::list ¶mValues, const NacosString &encoding, long readTimeoutMs ) NACOS_THROW(NetworkException); HttpResult httpPost( const NacosString &path, std::list &headers, std::map ¶mValues, const NacosString &encoding, long readTimeoutMs ) NACOS_THROW(NetworkException); HttpResult httpPut( const NacosString &path, std::list &headers, std::list ¶mValues, const NacosString &encoding, long readTimeoutMs ) NACOS_THROW(NetworkException); HttpResult httpPut( const NacosString &path, std::list &headers, std::map ¶mValues, const NacosString &encoding, long readTimeoutMs ) NACOS_THROW(NetworkException); }; }//namespace nacos #endif ================================================ FILE: src/http/HttpDelegate.h ================================================ #ifndef __HTTP_AGENT_H_ #define __HTTP_AGENT_H_ #include "NacosExceptions.h" #include "NacosString.h" #include "IHttpCli.h" #include "Compatibility.h" /** * HttpDelegate * * @author Liu, Hanyu */ namespace nacos{ class HttpDelegate { protected: HttpDelegate *_next; public: HttpDelegate() { _next = NULL; }; /** * invoke http get method * @param path http path * @param headers http headers * @param paramValues http paramValues http * @param encoding http encode * @param readTimeoutMs http timeout * @return HttpResult http response * @throws NetworkException If an input or output exception occurred */ virtual HttpResult httpGet(const NacosString &path, std::list &headers, std::list ¶mValues, const NacosString &encoding, long readTimeoutMs) NACOS_THROW(NetworkException) = 0; /** * invoke http put method * @param path http path * @param headers http headers * @param paramValues http paramValues http * @param encoding http encode * @param readTimeoutMs http timeout * @return HttpResult http response * @throws NetworkException If an input or output exception occurred */ virtual HttpResult httpPut(const NacosString &path, std::list &headers, std::list ¶mValues, const NacosString &encoding, long readTimeoutMs) NACOS_THROW(NetworkException) = 0; /** * invoke http post method * @param path http path * @param headers http headers * @param paramValues http paramValues http * @param encoding http encode * @param readTimeoutMs http timeout * @return HttpResult http response * @throws NetworkException If an input or output exception occurred */ virtual HttpResult httpPost(const NacosString &path, std::list &headers, std::list ¶mValues, const NacosString &encoding, long readTimeoutMs) NACOS_THROW(NetworkException) = 0; /** * invoke http delete method * @param path http path * @param headers http headers * @param paramValues http paramValues http * @param encoding http encode * @param readTimeoutMs http timeout * @return HttpResult http response * @throws NetworkException If an input or output exception occurred */ virtual HttpResult httpDelete(const NacosString &path, std::list &headers, std::list ¶mValues, const NacosString &encoding, long readTimeoutMs) NACOS_THROW(NetworkException) = 0; /** * get encode * @return NacosString */ virtual NacosString getEncode() const = 0; virtual ~HttpDelegate() {}; }; }//namespace nacos #endif ================================================ FILE: src/http/HttpStatus.h ================================================ #ifndef __HTTP_STATUS_H_ #define __HTTP_STATUS_H_ //Copied from JAVA source code HttpURLConnection.java // REMIND: do we want all these?? // Others not here that we do want?? class HttpStatus { public: /* 2XX: generally "OK" */ /** * HTTP Status-Code 200: OK. */ static const int HTTP_OK = 200; /** * HTTP Status-Code 201: Created. */ static const int HTTP_CREATED = 201; /** * HTTP Status-Code 202: Accepted. */ static const int HTTP_ACCEPTED = 202; /** * HTTP Status-Code 203: Non-Authoritative Information. */ static const int HTTP_NOT_AUTHORITATIVE = 203; /** * HTTP Status-Code 204: No Content. */ static const int HTTP_NO_CONTENT = 204; /** * HTTP Status-Code 205: Reset Content. */ static const int HTTP_RESET = 205; /** * HTTP Status-Code 206: Partial Content. */ static const int HTTP_PARTIAL = 206; /* 3XX: relocation/redirect */ /** * HTTP Status-Code 300: Multiple Choices. */ static const int HTTP_MULT_CHOICE = 300; /** * HTTP Status-Code 301: Moved Permanently. */ static const int HTTP_MOVED_PERM = 301; /** * HTTP Status-Code 302: Temporary Redirect. */ static const int HTTP_MOVED_TEMP = 302; /** * HTTP Status-Code 303: See Other. */ static const int HTTP_SEE_OTHER = 303; /** * HTTP Status-Code 304: Not Modified. */ static const int HTTP_NOT_MODIFIED = 304; /** * HTTP Status-Code 305: Use Proxy. */ static const int HTTP_USE_PROXY = 305; /* 4XX: client error */ /** * HTTP Status-Code 400: Bad Request. */ static const int HTTP_BAD_REQUEST = 400; /** * HTTP Status-Code 401: Unauthorized. */ static const int HTTP_UNAUTHORIZED = 401; /** * HTTP Status-Code 402: Payment Required. */ static const int HTTP_PAYMENT_REQUIRED = 402; /** * HTTP Status-Code 403: Forbidden. */ static const int HTTP_FORBIDDEN = 403; /** * HTTP Status-Code 404: Not Found. */ static const int HTTP_NOT_FOUND = 404; /** * HTTP Status-Code 405: Method Not Allowed. */ static const int HTTP_BAD_METHOD = 405; /** * HTTP Status-Code 406: Not Acceptable. */ static const int HTTP_NOT_ACCEPTABLE = 406; /** * HTTP Status-Code 407: Proxy Authentication Required. */ static const int HTTP_PROXY_AUTH = 407; /** * HTTP Status-Code 408: Request Time-Out. */ static const int HTTP_CLIENT_TIMEOUT = 408; /** * HTTP Status-Code 409: Conflict. */ static const int HTTP_CONFLICT = 409; /** * HTTP Status-Code 410: Gone. */ static const int HTTP_GONE = 410; /** * HTTP Status-Code 411: Length Required. */ static const int HTTP_LENGTH_REQUIRED = 411; /** * HTTP Status-Code 412: Precondition Failed. */ static const int HTTP_PRECON_FAILED = 412; /** * HTTP Status-Code 413: Request Entity Too Large. */ static const int HTTP_ENTITY_TOO_LARGE = 413; /** * HTTP Status-Code 414: Request-URI Too Large. */ static const int HTTP_REQ_TOO_LONG = 414; /** * HTTP Status-Code 415: Unsupported Media Type. */ static const int HTTP_UNSUPPORTED_TYPE = 415; /* 5XX: server error */ /** * HTTP Status-Code 500: Internal Server Error. * @deprecated it is misplaced and shouldn't have existed. */ static const int HTTP_SERVER_ERROR = 500; /** * HTTP Status-Code 500: Internal Server Error. */ static const int HTTP_INTERNAL_ERROR = 500; /** * HTTP Status-Code 501: Not Implemented. */ static const int HTTP_NOT_IMPLEMENTED = 501; /** * HTTP Status-Code 502: Bad Gateway. */ static const int HTTP_BAD_GATEWAY = 502; /** * HTTP Status-Code 503: Service Unavailable. */ static const int HTTP_UNAVAILABLE = 503; /** * HTTP Status-Code 504: Gateway Timeout. */ static const int HTTP_GATEWAY_TIMEOUT = 504; /** * HTTP Status-Code 505: HTTP Version Not Supported. */ static const int HTTP_VERSION = 505; }; #endif ================================================ FILE: src/http/IHttpCli.h ================================================ #ifndef __IHTTP_CLI_H_ #define __IHTTP_CLI_H_ #include #include #include #include "NacosExceptions.h" #include "NacosString.h" #include "Compatibility.h" /** * HttpDelegate * * @author Liu, Hanyu */ namespace nacos{ class HttpResult { public: long code; NacosString content; std::map headers; CURLcode curlcode; HttpResult(long _code, const NacosString &_content, std::map &_headers) : code(_code), content(_content) { headers.insert(_headers.begin(), _headers.end()); } HttpResult(long _code, const NacosString &_content) : code(_code), content(_content) {} HttpResult() { code = -1; content = ""; headers.clear(); } HttpResult operator=(HttpResult asignee) { if (this != &asignee) { headers.insert(asignee.headers.begin(), asignee.headers.end()); code = asignee.code; content = asignee.content; curlcode = asignee.curlcode; } return *this; } }; class IHttpCli { public: static const int GET = 0; static const int PUT = 1; static const int POST = 3; static const int DELETE = 4; virtual HttpResult httpGet( const NacosString &path, std::list &headers, std::list ¶mValues, const NacosString &encoding, long readTimeoutMs ) NACOS_THROW(NetworkException) = 0; virtual HttpResult httpGet( const NacosString &path, std::list &headers, std::map ¶mValues, const NacosString &encoding, long readTimeoutMs ) NACOS_THROW(NetworkException) = 0; virtual HttpResult httpDelete( const NacosString &path, std::list &headers, std::list ¶mValues, const NacosString &encoding, long readTimeoutMs ) NACOS_THROW(NetworkException) = 0; virtual HttpResult httpDelete( const NacosString &path, std::list &headers, std::map ¶mValues, const NacosString &encoding, long readTimeoutMs ) NACOS_THROW(NetworkException) = 0; virtual HttpResult httpPost( const NacosString &path, std::list &headers, std::list ¶mValues, const NacosString &encoding, long readTimeoutMs ) NACOS_THROW(NetworkException) = 0; virtual HttpResult httpPost( const NacosString &path, std::list &headers, std::map ¶mValues, const NacosString &encoding, long readTimeoutMs ) NACOS_THROW(NetworkException) = 0; virtual HttpResult httpPut( const NacosString &path, std::list &headers, std::list ¶mValues, const NacosString &encoding, long readTimeoutMs ) NACOS_THROW(NetworkException) = 0; virtual HttpResult httpPut( const NacosString &path, std::list &headers, std::map ¶mValues, const NacosString &encoding, long readTimeoutMs ) NACOS_THROW(NetworkException) = 0; virtual ~IHttpCli() {}; }; }//namespace nacos #endif ================================================ FILE: src/http/delegate/NacosAuthHttpDelegate.cpp ================================================ // // Created by liuhanyu on 2020/12/5. // #include "NacosAuthHttpDelegate.h" #include "src/security/SecurityManager.h" using namespace std; namespace nacos { NacosString NacosAuthHttpDelegate::getEncode() const { return encoding; } NacosAuthHttpDelegate::NacosAuthHttpDelegate(ObjectConfigData *objectConfigData) { _objectConfigData = objectConfigData; } HttpResult NacosAuthHttpDelegate::httpGet ( const NacosString &path, std::list &headers, std::list ¶mValues, const NacosString &_encoding, long readTimeoutMs ) NACOS_THROW(NetworkException) { HttpResult res; _objectConfigData->_securityManager->addAccessToken2Req(paramValues); res = _objectConfigData->_httpCli->httpGet(path, headers, paramValues, _encoding, readTimeoutMs); return res; } HttpResult NacosAuthHttpDelegate::httpDelete ( const NacosString &path, std::list &headers, std::list ¶mValues, const NacosString &_encoding, long readTimeoutMs ) NACOS_THROW(NetworkException) { HttpResult res; _objectConfigData->_securityManager->addAccessToken2Req(paramValues); res = _objectConfigData->_httpCli->httpDelete(path, headers, paramValues, _encoding, readTimeoutMs); return res; } HttpResult NacosAuthHttpDelegate::httpPost ( const NacosString &path, std::list &headers, std::list ¶mValues, const NacosString &_encoding, long readTimeoutMs ) NACOS_THROW(NetworkException) { HttpResult res; _objectConfigData->_securityManager->addAccessToken2Req(paramValues); res = _objectConfigData->_httpCli->httpPost(path, headers, paramValues, _encoding, readTimeoutMs); return res; } HttpResult NacosAuthHttpDelegate::httpPut ( const NacosString &path, std::list &headers, std::list ¶mValues, const NacosString &_encoding, long readTimeoutMs ) NACOS_THROW(NetworkException) { HttpResult res; _objectConfigData->_securityManager->addAccessToken2Req(paramValues); res = _objectConfigData->_httpCli->httpPut(path, headers, paramValues, _encoding, readTimeoutMs); return res; } } ================================================ FILE: src/http/delegate/NacosAuthHttpDelegate.h ================================================ // // Created by liuhanyu on 2020/12/5. // #ifndef NACOS_SDK_CPP_NACOSAUTHHTTPDELEGATE_H #define NACOS_SDK_CPP_NACOSAUTHHTTPDELEGATE_H #include "NacosExceptions.h" #include "NacosString.h" #include "src/http/HttpDelegate.h" #include "src/factory/ObjectConfigData.h" #include "Compatibility.h" /** * NoOpHttpDelegate * * @author Liu, Hanyu * Send a request to the server with authentication header */ namespace nacos{ class NacosAuthHttpDelegate : public HttpDelegate { private: ObjectConfigData *_objectConfigData; NacosString encoding; public: NacosAuthHttpDelegate(ObjectConfigData *objectConfigData); HttpResult httpGet(const NacosString &path, std::list &headers, std::list ¶mValues, const NacosString &encoding, long readTimeoutMs) NACOS_THROW(NetworkException); HttpResult httpPost(const NacosString &path, std::list &headers, std::list ¶mValues, const NacosString &encoding, long readTimeoutMs) NACOS_THROW(NetworkException); virtual HttpResult httpPut(const NacosString &path, std::list &headers, std::list ¶mValues, const NacosString &encoding, long readTimeoutMs) NACOS_THROW(NetworkException); HttpResult httpDelete(const NacosString &path, std::list &headers, std::list ¶mValues, const NacosString &encoding, long readTimeoutMs) NACOS_THROW(NetworkException); NacosString getEncode() const; virtual ~NacosAuthHttpDelegate() { }; }; }//namespace nacos #endif //NACOS_SDK_CPP_NACOSAUTHHTTPDELEGATE_H ================================================ FILE: src/http/delegate/NoOpHttpDelegate.cpp ================================================ #include "NoOpHttpDelegate.h" using namespace std; namespace nacos{ NacosString NoOpHttpDelegate::getEncode() const { return encoding; } NoOpHttpDelegate::NoOpHttpDelegate ( ObjectConfigData *objectConfigData ) { _objectConfigData = objectConfigData; } HttpResult NoOpHttpDelegate::httpGet ( const NacosString &path, std::list &headers, std::list ¶mValues, const NacosString &_encoding, long readTimeoutMs ) NACOS_THROW(NetworkException) { HttpResult res; res = _objectConfigData->_httpCli->httpGet(path, headers, paramValues, _encoding, readTimeoutMs); return res; } HttpResult NoOpHttpDelegate::httpDelete ( const NacosString &path, std::list &headers, std::list ¶mValues, const NacosString &_encoding, long readTimeoutMs ) NACOS_THROW(NetworkException) { HttpResult res; res = _objectConfigData->_httpCli->httpDelete(path, headers, paramValues, _encoding, readTimeoutMs); return res; } HttpResult NoOpHttpDelegate::httpPost ( const NacosString &path, std::list &headers, std::list ¶mValues, const NacosString &_encoding, long readTimeoutMs ) NACOS_THROW(NetworkException) { HttpResult res; res = _objectConfigData->_httpCli->httpPost(path, headers, paramValues, _encoding, readTimeoutMs); return res; } HttpResult NoOpHttpDelegate::httpPut ( const NacosString &path, std::list &headers, std::list ¶mValues, const NacosString &_encoding, long readTimeoutMs ) NACOS_THROW(NetworkException) { HttpResult res; res = _objectConfigData->_httpCli->httpPut(path, headers, paramValues, _encoding, readTimeoutMs); return res; } }//namespace nacos ================================================ FILE: src/http/delegate/NoOpHttpDelegate.h ================================================ #ifndef __SVR_HTTP_AGENT_H_ #define __SVR_HTTP_AGENT_H_ #include "NacosExceptions.h" #include "NacosString.h" #include "src/http/HttpDelegate.h" #include "src/factory/ObjectConfigData.h" #include "Compatibility.h" /** * NoOpHttpDelegate * * @author Liu, Hanyu * Directly send request to HttpCli without any operation */ namespace nacos{ class NoOpHttpDelegate : public HttpDelegate { private: ObjectConfigData *_objectConfigData; NacosString encoding; public: NoOpHttpDelegate(ObjectConfigData *objectConfigData); HttpResult httpGet(const NacosString &path, std::list &headers, std::list ¶mValues, const NacosString &encoding, long readTimeoutMs) NACOS_THROW(NetworkException); HttpResult httpPost(const NacosString &path, std::list &headers, std::list ¶mValues, const NacosString &encoding, long readTimeoutMs) NACOS_THROW(NetworkException); virtual HttpResult httpPut(const NacosString &path, std::list &headers, std::list ¶mValues, const NacosString &encoding, long readTimeoutMs) NACOS_THROW(NetworkException); HttpResult httpDelete(const NacosString &path, std::list &headers, std::list ¶mValues, const NacosString &encoding, long readTimeoutMs) NACOS_THROW(NetworkException); NacosString getEncode() const; virtual ~NoOpHttpDelegate() { }; }; }//namespace nacos #endif ================================================ FILE: src/init/Init.cpp ================================================ #include "Init.h" #include "src/http/HTTPCli.h" #include "src/config/SnapShotSwitch.h" #include "src/config/JVMUtil.h" #include "naming/ServiceInfo2.h" #include "constant/UtilAndComs.h" #include "src/utils/UuidUtils.h" #include "src/utils/RandomUtils.h" #include "src/thread/Thread.h" #include "src/crypto/MACProvider.h" static nacos::Init initobj;//Implicitly call the constructors namespace nacos{ Mutex Init::initflagMutex; bool Init::inited = false; bool SnapShotSwitch::isSnapShot = true; bool JVMUtil::_isMultiInstance = false; ServiceInfo2 ServiceInfo2::nullServiceInfo2; void Init::doInit() { if (inited) { return; } { LockGuard _initGuard(initflagMutex); if (inited) { return; } Logger::Init(); MACProvider::Init(); HTTPCli::HTTP_GLOBAL_INIT(); UtilAndComs::Init(); RandomUtils::Init(); UuidUtils::Init(); Thread::Init(); ServiceInfo2::nullServiceInfo2.setNull(true); inited = true; } } void Init::doDeinit() { if (!inited) { return; } { LockGuard _initGuard(initflagMutex); if (!inited) { return; } MACProvider::DeInit(); Thread::DeInit(); UuidUtils::DeInit(); RandomUtils::DeInit(); HTTPCli::HTTP_GLOBAL_DEINIT(); Logger::deInit(); inited = false; } } }//namespace nacos ================================================ FILE: src/init/Init.h ================================================ #ifndef __INIT_H_ #define __INIT_H_ #include "src/thread/Mutex.h" namespace nacos{ class Init { private: static bool inited; static Mutex initflagMutex; public: Init() {}; ~Init() { doDeinit(); }; static void doInit(); static void doDeinit(); }; }//namespace nacos #endif ================================================ FILE: src/json/JSON.cpp ================================================ #include "src/json/JSON.h" #include "src/naming/beat/BeatInfo.h" #include "NacosString.h" /** * JSON * * @author Liu, Hanyu * Adapter from nacos-cpp-cli to a json parser */ using namespace std; using namespace rapidjson; using nacos::naming::Selector; namespace nacos{ NacosString documentToString(const Document &d) { StringBuffer buffer; Writer writer(buffer); d.Accept(writer); NacosString result = buffer.GetString(); return result; } NacosString valueToString(const Value &d) { StringBuffer buffer; Writer writer(buffer); d.Accept(writer); NacosString result = buffer.GetString(); return result; } NacosString JSON::toJSONString(const map &mapinfo) { Document d; d.SetObject(); for (map::const_iterator it = mapinfo.begin(); it != mapinfo.end(); it++) { Value k; k.SetString(it->first.c_str(), d.GetAllocator()); Value v; v.SetString(it->second.c_str(), d.GetAllocator()); d.AddMember(k, v, d.GetAllocator()); } return documentToString(d); } void JSON::Map2JSONObject(Document &d, Value &jsonOb, map &mapinfo) { jsonOb.SetObject(); for (map::iterator it = mapinfo.begin(); it != mapinfo.end(); it++) { Value k; k.SetString(it->first.c_str(), d.GetAllocator()); Value v; v.SetString(it->second.c_str(), d.GetAllocator()); jsonOb.AddMember(k, v, d.GetAllocator()); } } void JSON::JSONObject2Map(std::map &mapinfo, const Value &jsonOb) { for (Value::ConstMemberIterator iter = jsonOb.MemberBegin(); iter != jsonOb.MemberEnd(); ++iter) { NacosString name = iter->name.GetString(); NacosString value = iter->value.GetString(); mapinfo[name] = value; } } //Add key-value void AddKV(Document &d, const NacosString &k, const NacosString &v) { Value k_, v_; k_.SetString(k.c_str(), d.GetAllocator()); v_.SetString(v.c_str(), d.GetAllocator()); d.AddMember(k_, v_, d.GetAllocator()); } //Add key-Object void AddKO(Document &d, const NacosString &k, Value &o) { Value k_; k_.SetString(k.c_str(), d.GetAllocator()); d.AddMember(k_, o, d.GetAllocator()); } NacosString JSON::toJSONString(BeatInfo &beatInfo) { Document d; d.SetObject(); AddKV(d, "port", NacosStringOps::valueOf(beatInfo.port)); AddKV(d, "ip", beatInfo.ip); AddKV(d, "weight", NacosStringOps::valueOf(beatInfo.weight)); AddKV(d, "serviceName", beatInfo.serviceName); AddKV(d, "cluster", beatInfo.cluster); AddKV(d, "scheduled", NacosStringOps::valueOf(beatInfo.scheduled)); Value metadata; Map2JSONObject(d, metadata, beatInfo.metadata); AddKO(d, "metadata", metadata); //d["port"] = NacosStringOps::valueOf(beatInfo.port); //d["ip"] = beatInfo.ip; //d["weight"] = NacosStringOps::valueOf(beatInfo.weight); //d["serviceName"] = beatInfo.serviceName; //d["cluster"] = beatInfo.cluster; //d["scheduled"] = beatInfo.scheduled; //d["metadata"] = toJSONString(beatInfo.metadata); return documentToString(d); } long JSON::getLong(const NacosString &jsonString, const NacosString &fieldname) { Document d; d.Parse(jsonString.c_str()); Value &s = d[fieldname.c_str()]; return s.GetInt64(); } Instance JSON::Json2Instance(const Value &host) NACOS_THROW(NacosException) { Instance theinstance; if (host.HasMember("instanceId")) { const Value &instanceId = host["instanceId"]; theinstance.instanceId = instanceId.GetString(); } markRequired(host, "port"); const Value &port = host["port"]; if (!port.IsInt()) { throw NacosException(NacosException::INVALID_JSON_FORMAT, "Error while parsing port for Instance!"); } theinstance.port = port.GetInt(); markRequired(host, "ip"); const Value &ip = host["ip"]; theinstance.ip = ip.GetString(); markRequired(host, "weight"); const Value &weight = host["weight"]; if (!weight.IsDouble()) { throw NacosException(NacosException::INVALID_JSON_FORMAT, "Error while parsing weight for Instance!"); } theinstance.weight = weight.GetDouble(); markRequired(host, "metadata"); const Value &metadata = host["metadata"]; std::map mtdata; JSONObject2Map(mtdata, metadata); theinstance.metadata = mtdata; markRequired(host, "healthy"); const Value &healthy = host["healthy"]; if (!healthy.IsBool()) { throw NacosException(NacosException::INVALID_JSON_FORMAT, "Error while parsing healthy for Instance!"); } theinstance.healthy = healthy.GetBool(); markRequired(host, "enabled"); const Value &enabled = host["enabled"]; if (!enabled.IsBool()) { throw NacosException(NacosException::INVALID_JSON_FORMAT, "Error while parsing enabled for Instance!"); } theinstance.enabled = enabled.GetBool(); if (host.HasMember("clusterName")) { const Value &clusterName = host["clusterName"]; theinstance.clusterName = clusterName.GetString(); } return theinstance; } Instance JSON::Json2Instance(const NacosString &jsonString) NACOS_THROW(NacosException) { Document d; d.Parse(jsonString.c_str()); Instance theinstance; markRequired(d, "instanceId"); const Value &instanceId = d["instanceId"]; theinstance.instanceId = instanceId.GetString(); markRequired(d, "port"); const Value &port = d["port"]; if (!port.IsInt()) { throw NacosException(NacosException::INVALID_JSON_FORMAT, "Error while parsing port for Instance!"); } theinstance.port = port.GetInt(); markRequired(d, "ip"); const Value &ip = d["ip"]; theinstance.ip = ip.GetString(); markRequired(d, "weight"); const Value &weight = d["weight"]; if (!weight.IsDouble()) { throw NacosException(NacosException::INVALID_JSON_FORMAT, "Error while parsing weight for Instance!"); } theinstance.weight = weight.GetDouble(); markRequired(d, "metadata"); const Value &metadata = d["metadata"]; std::map mtdata; JSONObject2Map(mtdata, metadata); theinstance.metadata = mtdata; markRequired(d, "healthy"); const Value &healthy = d["healthy"]; if (!healthy.IsBool()) { throw NacosException(NacosException::INVALID_JSON_FORMAT, "Error while parsing healthy for Instance!"); } theinstance.healthy = healthy.GetBool(); markRequired(d, "service"); const Value &service = d["service"]; theinstance.serviceName = service.GetString(); markRequired(d, "clusterName"); const Value &clusterName = d["clusterName"]; theinstance.clusterName = clusterName.GetString(); return theinstance; } void JSON::markRequired(const Document &d, const NacosString &requiredField) NACOS_THROW(NacosException) { if (!d.HasMember(requiredField.c_str())) { throw NacosException(NacosException::LACK_JSON_FIELD, "Missing required field:" + requiredField); } } void JSON::markRequired(const Value &v, const NacosString &requiredField) NACOS_THROW(NacosException) { if (!v.HasMember(requiredField.c_str())) { throw NacosException(NacosException::LACK_JSON_FIELD, "Missing required field:" + requiredField); } } ServiceInfo JSON::JsonStr2ServiceInfo(const NacosString &jsonString) NACOS_THROW(NacosException) { ServiceInfo si; Document d; d.Parse(jsonString.c_str()); if (d.HasParseError()) { throw NacosException(NacosException::INVALID_JSON_FORMAT, "Error while parsing the JSON String for ServiceInfo!"); } //bugfix #75, need to refresh the lastRefTime if it is present in the json if (d.HasMember("lastRefTime")) { const Value &lastRefTime = d["lastRefTime"]; if (!lastRefTime.IsInt64()) { throw NacosException(NacosException::INVALID_JSON_FORMAT, "Error while parsing lastRefTime for ServiceInfo!"); } si.setLastRefTime(lastRefTime.GetInt64()); } markRequired(d, "name"); const Value &name = d["name"]; ServiceInfo::fromKey(si, name.GetString()); markRequired(d, "clusters"); const Value &clusters = d["clusters"]; si.setClusters(clusters.GetString()); markRequired(d, "cacheMillis"); const Value &cacheMillis = d["cacheMillis"]; if (!cacheMillis.IsInt64()) { throw NacosException(NacosException::INVALID_JSON_FORMAT, "Error while parsing cacheMillis for ServiceInfo!"); } si.setCacheMillis(cacheMillis.GetInt64()); markRequired(d, "hosts"); const Value &hosts = d["hosts"]; if (hosts.Size() == 0) { return si; } std::list hostlist; for (SizeType i = 0; i < hosts.Size(); i++) { const Value &curhost = hosts[i]; Instance curinstance = Json2Instance(curhost); hostlist.push_back(curinstance); } si.setHosts(hostlist); markRequired(d, "checksum"); const Value &checkSum = d["checksum"]; si.setChecksum(checkSum.GetString()); return si; } NacosServerInfo parseOneNacosSvr(const Value &curSvr) { NacosServerInfo res; res.setIp(curSvr["ip"].GetString()); res.setPort(curSvr["servePort"].GetInt()); res.setSite(curSvr["site"].GetString()); res.setWeight(curSvr["weight"].GetFloat()); res.setAdWeight(curSvr["adWeight"].GetFloat()); res.setAlive(curSvr["alive"].GetBool()); res.setLastRefTime(curSvr["lastRefTime"].GetInt64()); if (!curSvr["lastRefTimeStr"].IsNull()) { res.setLastRefTimeStr(curSvr["lastRefTimeStr"].GetString()); } res.setKey(curSvr["key"].GetString()); return res; } list JSON::Json2NacosServerInfo(const NacosString &nacosString) NACOS_THROW(NacosException) { list nacosServerList; ServiceInfo si; Document d; d.Parse(nacosString.c_str()); if (d.HasParseError()) { throw NacosException(NacosException::INVALID_JSON_FORMAT, "Error while parsing the JSON String for NacosServerInfo!"); } markRequired(d, "servers"); const Value &servers = d["servers"]; if (!servers.IsArray()) { throw NacosException(NacosException::INVALID_JSON_FORMAT, "Error while parsing servers for NacosServerInfo!"); } for (SizeType i = 0; i < servers.Size(); i++) { const Value &curSvr = servers[i]; NacosServerInfo curSvrInfo = parseOneNacosSvr(curSvr); nacosServerList.push_back(curSvrInfo); } return nacosServerList; } ListView JSON::Json2ServiceList(const NacosString &nacosString) NACOS_THROW(NacosException) { ListView serviceList; ServiceInfo si; Document d; d.Parse(nacosString.c_str()); if (d.HasParseError()) { throw NacosException(NacosException::INVALID_JSON_FORMAT, "Error while parsing the JSON String for ServiceList!"); } markRequired(d, "count"); markRequired(d, "doms"); const Value &count = d["count"]; if (!count.IsInt()) { throw NacosException(NacosException::INVALID_JSON_FORMAT, "Error while parsing servers for ServiceList.count!"); } serviceList.setCount(count.GetInt()); const Value &doms = d["doms"]; if (!doms.IsArray()) { throw NacosException(NacosException::INVALID_JSON_FORMAT, "Error while parsing servers for ServiceList.doms!"); } list names; for (SizeType i = 0; i < doms.Size(); i++) { const Value &curName = doms[i]; names.push_back(curName.GetString()); } serviceList.setData(names); return serviceList; } map parseMetadata(const Value &value) { map metadata; for (Value::ConstMemberIterator iter = value.MemberBegin(); iter != value.MemberEnd(); ++iter){ metadata[iter->name.GetString()] = iter->value.GetString(); } return metadata; } ServiceInfo2 JSON::Json2ServiceInfo2(const NacosString &nacosString) NACOS_THROW(NacosException) { ServiceInfo2 serviceInfo2; Document d; d.Parse(nacosString.c_str()); markRequired(d, "groupName"); markRequired(d, "namespaceId"); markRequired(d, "name"); const Value &groupName = d["groupName"]; const Value &namespaceId = d["namespaceId"]; const Value &name = d["name"]; serviceInfo2.setGroupName(groupName.GetString()); serviceInfo2.setNamespaceId(namespaceId.GetString()); serviceInfo2.setName(name.GetString()); const Value &selector = d["selector"]; serviceInfo2.setSelector(valueToString(selector)); markRequired(d, "protectThreshold"); const Value &protectThreshold = d["protectThreshold"]; serviceInfo2.setProtectThreshold(protectThreshold.GetDouble()); //service info metadata const Value &serviceMetadata = d["metadata"]; if (!serviceMetadata.IsObject()) { throw NacosException(NacosException::INVALID_JSON_FORMAT, "Error while parsing metadata for ServiceInfo2!"); } serviceInfo2.setMetadata(parseMetadata(serviceMetadata)); const Value &clusterlist = d["clusters"]; if (!clusterlist.IsArray()) { throw NacosException(NacosException::INVALID_JSON_FORMAT, "Error while parsing clusters for ServiceInfo2.clusters!"); } //cluster metadata for (SizeType i = 0; i < clusterlist.Size(); i++) { const Value &curClusterJson = clusterlist[i]; Cluster curCluster; curCluster.setName(curClusterJson["name"].GetString()); curCluster.setMetadata(parseMetadata(curClusterJson["metadata"])); HealthChecker healthChecker; curCluster.setHealthChecker(healthChecker); } return serviceInfo2; } AccessToken JSON::Json2AccessToken(const NacosString &nacosString) NACOS_THROW(NacosException) { AccessToken accessTokenRes; Document d; d.Parse(nacosString.c_str()); markRequired(d, "accessToken"); const Value &accessToken = d["accessToken"]; accessTokenRes.accessToken = accessToken.GetString(); markRequired(d, "tokenTtl"); const Value &tokenTtl = d["tokenTtl"]; accessTokenRes.tokenTtl = tokenTtl.GetInt(); markRequired(d, "globalAdmin"); const Value &globalAdmin = d["globalAdmin"]; accessTokenRes.globalAdmin = globalAdmin.GetBool(); return accessTokenRes; } PushPacket JSON::Json2PushPacket(const char *jsonString) NACOS_THROW(NacosException) { PushPacket pushPacket; Document d; d.Parse(jsonString); markRequired(d, "data"); const Value &data = d["data"]; pushPacket.data = data.GetString(); markRequired(d, "type"); const Value &type = d["type"]; pushPacket.type = type.GetString(); markRequired(d, "lastRefTime"); const Value &lastRefTime = d["lastRefTime"]; pushPacket.lastRefTime = lastRefTime.GetInt64(); return pushPacket; } }//namespace nacos ================================================ FILE: src/json/JSON.h ================================================ #ifndef __JSON_H_ #define __JSON_H_ #include #include "NacosString.h" #include "src/naming/beat/BeatInfo.h" #include "naming/ServiceInfo.h" #include "src/json/rapidjson/document.h" #include "src/json/rapidjson/writer.h" #include "src/json/rapidjson/stringbuffer.h" #include "naming/Instance.h" #include "src/server/NacosServerInfo.h" #include "naming/ListView.h" #include "naming/ServiceInfo2.h" #include "src/security/SecurityManager.h" #include "src/naming/subscribe/UdpNamingServiceListener.h" #include "Compatibility.h" /** * JSON * * @author Liu, Hanyu * Adapter from nacos-sdk-cpp to a json parser */ namespace nacos{ class JSON { public: static NacosString toJSONString(BeatInfo &beatInfo); static NacosString toJSONString(const std::map &mapinfo); static void Map2JSONObject(rapidjson::Document &d, rapidjson::Value &jsonOb, std::map &mapinfo); static void JSONObject2Map(std::map &mapinfo, const rapidjson::Value &jsonOb); static long getLong(const NacosString &jsonString, const NacosString &fieldname); static ServiceInfo JsonStr2ServiceInfo(const NacosString &jsonString) NACOS_THROW(NacosException); static Instance Json2Instance(const rapidjson::Value &jsonString) NACOS_THROW(NacosException); static Instance Json2Instance(const NacosString &jsonString) NACOS_THROW(NacosException); static void markRequired(const rapidjson::Document &d, const NacosString &requiredField) NACOS_THROW(NacosException); static void markRequired(const rapidjson::Value &d, const NacosString &requiredField) NACOS_THROW(NacosException); static std::list Json2NacosServerInfo(const NacosString &nacosString) NACOS_THROW(NacosException); static ServiceInfo2 Json2ServiceInfo2(const NacosString &nacosString) NACOS_THROW(NacosException); static ListView Json2ServiceList(const NacosString &nacosString) NACOS_THROW(NacosException); static AccessToken Json2AccessToken(const NacosString &nacosString) NACOS_THROW(NacosException); static PushPacket Json2PushPacket(const char *jsonString) NACOS_THROW(NacosException); }; }//namespace nacos #endif ================================================ FILE: src/json/rapidjson/allocators.h ================================================ // Tencent is pleased to support the open source community by making RapidJSON available. // // Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at // // http://opensource.org/licenses/MIT // // Unless required by applicable law or agreed to in writing, software distributed // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. #ifndef RAPIDJSON_ALLOCATORS_H_ #define RAPIDJSON_ALLOCATORS_H_ #include "rapidjson.h" RAPIDJSON_NAMESPACE_BEGIN /////////////////////////////////////////////////////////////////////////////// // Allocator /*! \class rapidjson::Allocator \brief Concept for allocating, resizing and freeing memory block. Note that Malloc() and Realloc() are non-static but Free() is static. So if an allocator need to support Free(), it needs to put its pointer in the header of memory block. \code concept Allocator { static const bool kNeedFree; //!< Whether this allocator needs to call Free(). // Allocate a memory block. // \param size of the memory block in bytes. // \returns pointer to the memory block. void* Malloc(size_t size); // Resize a memory block. // \param originalPtr The pointer to current memory block. Null pointer is permitted. // \param originalSize The current size in bytes. (Design issue: since some allocator may not book-keep this, explicitly pass to it can save memory.) // \param newSize the new size in bytes. void* Realloc(void* originalPtr, size_t originalSize, size_t newSize); // Free a memory block. // \param pointer to the memory block. Null pointer is permitted. static void Free(void *ptr); }; \endcode */ /*! \def RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY \ingroup RAPIDJSON_CONFIG \brief User-defined kDefaultChunkCapacity definition. User can define this as any \c size that is a power of 2. */ #ifndef RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY #define RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY (64 * 1024) #endif /////////////////////////////////////////////////////////////////////////////// // CrtAllocator //! C-runtime library allocator. /*! This class is just wrapper for standard C library memory routines. \note implements Allocator concept */ class CrtAllocator { public: static const bool kNeedFree = true; void* Malloc(size_t size) { if (size) // behavior of malloc(0) is implementation defined. return std::malloc(size); else return NULL; // standardize to returning NULL. } void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) { (void)originalSize; if (newSize == 0) { std::free(originalPtr); return NULL; } return std::realloc(originalPtr, newSize); } static void Free(void *ptr) { std::free(ptr); } }; /////////////////////////////////////////////////////////////////////////////// // MemoryPoolAllocator //! Default memory allocator used by the parser and DOM. /*! This allocator allocate memory blocks from pre-allocated memory chunks. It does not free memory blocks. And Realloc() only allocate new memory. The memory chunks are allocated by BaseAllocator, which is CrtAllocator by default. User may also supply a buffer as the first chunk. If the user-buffer is full then additional chunks are allocated by BaseAllocator. The user-buffer is not deallocated by this allocator. \tparam BaseAllocator the allocator type for allocating memory chunks. Default is CrtAllocator. \note implements Allocator concept */ template class MemoryPoolAllocator { public: static const bool kNeedFree = false; //!< Tell users that no need to call Free() with this allocator. (concept Allocator) //! Constructor with chunkSize. /*! \param chunkSize The size of memory chunk. The default is kDefaultChunkSize. \param baseAllocator The allocator for allocating memory chunks. */ MemoryPoolAllocator(size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) : chunkHead_(0), chunk_capacity_(chunkSize), userBuffer_(0), baseAllocator_(baseAllocator), ownBaseAllocator_(0) { } //! Constructor with user-supplied buffer. /*! The user buffer will be used firstly. When it is full, memory pool allocates new chunk with chunk size. The user buffer will not be deallocated when this allocator is destructed. \param buffer User supplied buffer. \param size Size of the buffer in bytes. It must at least larger than sizeof(ChunkHeader). \param chunkSize The size of memory chunk. The default is kDefaultChunkSize. \param baseAllocator The allocator for allocating memory chunks. */ MemoryPoolAllocator(void *buffer, size_t size, size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) : chunkHead_(0), chunk_capacity_(chunkSize), userBuffer_(buffer), baseAllocator_(baseAllocator), ownBaseAllocator_(0) { RAPIDJSON_ASSERT(buffer != 0); RAPIDJSON_ASSERT(size > sizeof(ChunkHeader)); chunkHead_ = reinterpret_cast(buffer); chunkHead_->capacity = size - sizeof(ChunkHeader); chunkHead_->size = 0; chunkHead_->next = 0; } //! Destructor. /*! This deallocates all memory chunks, excluding the user-supplied buffer. */ ~MemoryPoolAllocator() { Clear(); RAPIDJSON_DELETE(ownBaseAllocator_); } //! Deallocates all memory chunks, excluding the user-supplied buffer. void Clear() { while (chunkHead_ && chunkHead_ != userBuffer_) { ChunkHeader* next = chunkHead_->next; baseAllocator_->Free(chunkHead_); chunkHead_ = next; } if (chunkHead_ && chunkHead_ == userBuffer_) chunkHead_->size = 0; // Clear user buffer } //! Computes the total capacity of allocated memory chunks. /*! \return total capacity in bytes. */ size_t Capacity() const { size_t capacity = 0; for (ChunkHeader* c = chunkHead_; c != 0; c = c->next) capacity += c->capacity; return capacity; } //! Computes the memory blocks allocated. /*! \return total used bytes. */ size_t Size() const { size_t size = 0; for (ChunkHeader* c = chunkHead_; c != 0; c = c->next) size += c->size; return size; } //! Allocates a memory block. (concept Allocator) void* Malloc(size_t size) { if (!size) return NULL; size = RAPIDJSON_ALIGN(size); if (chunkHead_ == 0 || chunkHead_->size + size > chunkHead_->capacity) if (!AddChunk(chunk_capacity_ > size ? chunk_capacity_ : size)) return NULL; void *buffer = reinterpret_cast(chunkHead_) + RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + chunkHead_->size; chunkHead_->size += size; return buffer; } //! Resizes a memory block (concept Allocator) void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) { if (originalPtr == 0) return Malloc(newSize); if (newSize == 0) return NULL; originalSize = RAPIDJSON_ALIGN(originalSize); newSize = RAPIDJSON_ALIGN(newSize); // Do not shrink if new size is smaller than original if (originalSize >= newSize) return originalPtr; // Simply expand it if it is the last allocation and there is sufficient space if (originalPtr == reinterpret_cast(chunkHead_) + RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + chunkHead_->size - originalSize) { size_t increment = static_cast(newSize - originalSize); if (chunkHead_->size + increment <= chunkHead_->capacity) { chunkHead_->size += increment; return originalPtr; } } // Realloc process: allocate and copy memory, do not free original buffer. if (void* newBuffer = Malloc(newSize)) { if (originalSize) std::memcpy(newBuffer, originalPtr, originalSize); return newBuffer; } else return NULL; } //! Frees a memory block (concept Allocator) static void Free(void *ptr) { (void)ptr; } // Do nothing private: //! Copy constructor is not permitted. MemoryPoolAllocator(const MemoryPoolAllocator& rhs) /* = delete */; //! Copy assignment operator is not permitted. MemoryPoolAllocator& operator=(const MemoryPoolAllocator& rhs) /* = delete */; //! Creates a new chunk. /*! \param capacity Capacity of the chunk in bytes. \return true if success. */ bool AddChunk(size_t capacity) { if (!baseAllocator_) ownBaseAllocator_ = baseAllocator_ = RAPIDJSON_NEW(BaseAllocator)(); if (ChunkHeader* chunk = reinterpret_cast(baseAllocator_->Malloc(RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + capacity))) { chunk->capacity = capacity; chunk->size = 0; chunk->next = chunkHead_; chunkHead_ = chunk; return true; } else return false; } static const int kDefaultChunkCapacity = RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY; //!< Default chunk capacity. //! Chunk header for perpending to each chunk. /*! Chunks are stored as a singly linked list. */ struct ChunkHeader { size_t capacity; //!< Capacity of the chunk in bytes (excluding the header itself). size_t size; //!< Current size of allocated memory in bytes. ChunkHeader *next; //!< Next chunk in the linked list. }; ChunkHeader *chunkHead_; //!< Head of the chunk linked-list. Only the head chunk serves allocation. size_t chunk_capacity_; //!< The minimum capacity of chunk when they are allocated. void *userBuffer_; //!< User supplied buffer. BaseAllocator* baseAllocator_; //!< base allocator for allocating memory chunks. BaseAllocator* ownBaseAllocator_; //!< base allocator created by this object. }; RAPIDJSON_NAMESPACE_END #endif // RAPIDJSON_ENCODINGS_H_ ================================================ FILE: src/json/rapidjson/cursorstreamwrapper.h ================================================ // Tencent is pleased to support the open source community by making RapidJSON available. // // Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at // // http://opensource.org/licenses/MIT // // Unless required by applicable law or agreed to in writing, software distributed // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. #ifndef RAPIDJSON_CURSORSTREAMWRAPPER_H_ #define RAPIDJSON_CURSORSTREAMWRAPPER_H_ #include "stream.h" #if defined(__GNUC__) RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(effc++) #endif #if defined(_MSC_VER) && _MSC_VER <= 1800 RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(4702) // unreachable code RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated #endif RAPIDJSON_NAMESPACE_BEGIN //! Cursor stream wrapper for counting line and column number if error exists. /*! \tparam InputStream Any stream that implements Stream Concept */ template > class CursorStreamWrapper : public GenericStreamWrapper { public: typedef typename Encoding::Ch Ch; CursorStreamWrapper(InputStream& is): GenericStreamWrapper(is), line_(1), col_(0) {} // counting line and column number Ch Take() { Ch ch = this->is_.Take(); if(ch == '\n') { line_ ++; col_ = 0; } else { col_ ++; } return ch; } //! Get the error line number, if error exists. size_t GetLine() const { return line_; } //! Get the error column number, if error exists. size_t GetColumn() const { return col_; } private: size_t line_; //!< Current Line size_t col_; //!< Current Column }; #if defined(_MSC_VER) && _MSC_VER <= 1800 RAPIDJSON_DIAG_POP #endif #if defined(__GNUC__) RAPIDJSON_DIAG_POP #endif RAPIDJSON_NAMESPACE_END #endif // RAPIDJSON_CURSORSTREAMWRAPPER_H_ ================================================ FILE: src/json/rapidjson/document.h ================================================ // Tencent is pleased to support the open source community by making RapidJSON available. // // Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at // // http://opensource.org/licenses/MIT // // Unless required by applicable law or agreed to in writing, software distributed // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. #ifndef RAPIDJSON_DOCUMENT_H_ #define RAPIDJSON_DOCUMENT_H_ /*! \file document.h */ #include "reader.h" #include "src/json/rapidjson/internal/meta.h" #include "src/json/rapidjson/internal/strfunc.h" #include "memorystream.h" #include "encodedstream.h" #include // placement new #include RAPIDJSON_DIAG_PUSH #ifdef __clang__ RAPIDJSON_DIAG_OFF(padded) RAPIDJSON_DIAG_OFF(switch-enum) RAPIDJSON_DIAG_OFF(c++98-compat) #elif defined(_MSC_VER) RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant RAPIDJSON_DIAG_OFF(4244) // conversion from kXxxFlags to 'uint16_t', possible loss of data #endif #ifdef __GNUC__ RAPIDJSON_DIAG_OFF(effc++) #endif // __GNUC__ #ifndef RAPIDJSON_NOMEMBERITERATORCLASS #include // std::random_access_iterator_tag #endif #if RAPIDJSON_HAS_CXX11_RVALUE_REFS #include // std::move #endif RAPIDJSON_NAMESPACE_BEGIN // Forward declaration. template class GenericValue; template class GenericDocument; //! Name-value pair in a JSON object value. /*! This class was internal to GenericValue. It used to be a inner struct. But a compiler (IBM XL C/C++ for AIX) have reported to have problem with that so it moved as a namespace scope struct. https://code.google.com/p/rapidjson/issues/detail?id=64 */ template struct GenericMember { GenericValue name; //!< name of member (must be a string) GenericValue value; //!< value of member. // swap() for std::sort() and other potential use in STL. friend inline void swap(GenericMember& a, GenericMember& b) RAPIDJSON_NOEXCEPT { a.name.Swap(b.name); a.value.Swap(b.value); } }; /////////////////////////////////////////////////////////////////////////////// // GenericMemberIterator #ifndef RAPIDJSON_NOMEMBERITERATORCLASS //! (Constant) member iterator for a JSON object value /*! \tparam Const Is this a constant iterator? \tparam Encoding Encoding of the value. (Even non-string values need to have the same encoding in a document) \tparam Allocator Allocator type for allocating memory of object, array and string. This class implements a Random Access Iterator for GenericMember elements of a GenericValue, see ISO/IEC 14882:2003(E) C++ standard, 24.1 [lib.iterator.requirements]. \note This iterator implementation is mainly intended to avoid implicit conversions from iterator values to \c NULL, e.g. from GenericValue::FindMember. \note Define \c RAPIDJSON_NOMEMBERITERATORCLASS to fall back to a pointer-based implementation, if your platform doesn't provide the C++ header. \see GenericMember, GenericValue::MemberIterator, GenericValue::ConstMemberIterator */ template class GenericMemberIterator { friend class GenericValue; template friend class GenericMemberIterator; typedef GenericMember PlainType; typedef typename internal::MaybeAddConst::Type ValueType; public: //! Iterator type itself typedef GenericMemberIterator Iterator; //! Constant iterator type typedef GenericMemberIterator ConstIterator; //! Non-constant iterator type typedef GenericMemberIterator NonConstIterator; /** \name std::iterator_traits support */ //@{ typedef ValueType value_type; typedef ValueType * pointer; typedef ValueType & reference; typedef std::ptrdiff_t difference_type; typedef std::random_access_iterator_tag iterator_category; //@} //! Pointer to (const) GenericMember typedef pointer Pointer; //! Reference to (const) GenericMember typedef reference Reference; //! Signed integer type (e.g. \c ptrdiff_t) typedef difference_type DifferenceType; //! Default constructor (singular value) /*! Creates an iterator pointing to no element. \note All operations, except for comparisons, are undefined on such values. */ GenericMemberIterator() : ptr_() {} //! Iterator conversions to more const /*! \param it (Non-const) iterator to copy from Allows the creation of an iterator from another GenericMemberIterator that is "less const". Especially, creating a non-constant iterator from a constant iterator are disabled: \li const -> non-const (not ok) \li const -> const (ok) \li non-const -> const (ok) \li non-const -> non-const (ok) \note If the \c Const template parameter is already \c false, this constructor effectively defines a regular copy-constructor. Otherwise, the copy constructor is implicitly defined. */ GenericMemberIterator(const NonConstIterator & it) : ptr_(it.ptr_) {} Iterator& operator=(const NonConstIterator & it) { ptr_ = it.ptr_; return *this; } //! @name stepping //@{ Iterator& operator++(){ ++ptr_; return *this; } Iterator& operator--(){ --ptr_; return *this; } Iterator operator++(int){ Iterator old(*this); ++ptr_; return old; } Iterator operator--(int){ Iterator old(*this); --ptr_; return old; } //@} //! @name increment/decrement //@{ Iterator operator+(DifferenceType n) const { return Iterator(ptr_+n); } Iterator operator-(DifferenceType n) const { return Iterator(ptr_-n); } Iterator& operator+=(DifferenceType n) { ptr_+=n; return *this; } Iterator& operator-=(DifferenceType n) { ptr_-=n; return *this; } //@} //! @name relations //@{ bool operator==(ConstIterator that) const { return ptr_ == that.ptr_; } bool operator!=(ConstIterator that) const { return ptr_ != that.ptr_; } bool operator<=(ConstIterator that) const { return ptr_ <= that.ptr_; } bool operator>=(ConstIterator that) const { return ptr_ >= that.ptr_; } bool operator< (ConstIterator that) const { return ptr_ < that.ptr_; } bool operator> (ConstIterator that) const { return ptr_ > that.ptr_; } //@} //! @name dereference //@{ Reference operator*() const { return *ptr_; } Pointer operator->() const { return ptr_; } Reference operator[](DifferenceType n) const { return ptr_[n]; } //@} //! Distance DifferenceType operator-(ConstIterator that) const { return ptr_-that.ptr_; } private: //! Internal constructor from plain pointer explicit GenericMemberIterator(Pointer p) : ptr_(p) {} Pointer ptr_; //!< raw pointer }; #else // RAPIDJSON_NOMEMBERITERATORCLASS // class-based member iterator implementation disabled, use plain pointers template class GenericMemberIterator; //! non-const GenericMemberIterator template class GenericMemberIterator { //! use plain pointer as iterator type typedef GenericMember* Iterator; }; //! const GenericMemberIterator template class GenericMemberIterator { //! use plain const pointer as iterator type typedef const GenericMember* Iterator; }; #endif // RAPIDJSON_NOMEMBERITERATORCLASS /////////////////////////////////////////////////////////////////////////////// // GenericStringRef //! Reference to a constant string (not taking a copy) /*! \tparam CharType character type of the string This helper class is used to automatically infer constant string references for string literals, especially from \c const \b (!) character arrays. The main use is for creating JSON string values without copying the source string via an \ref Allocator. This requires that the referenced string pointers have a sufficient lifetime, which exceeds the lifetime of the associated GenericValue. \b Example \code Value v("foo"); // ok, no need to copy & calculate length const char foo[] = "foo"; v.SetString(foo); // ok const char* bar = foo; // Value x(bar); // not ok, can't rely on bar's lifetime Value x(StringRef(bar)); // lifetime explicitly guaranteed by user Value y(StringRef(bar, 3)); // ok, explicitly pass length \endcode \see StringRef, GenericValue::SetString */ template struct GenericStringRef { typedef CharType Ch; //!< character type of the string //! Create string reference from \c const character array #ifndef __clang__ // -Wdocumentation /*! This constructor implicitly creates a constant string reference from a \c const character array. It has better performance than \ref StringRef(const CharType*) by inferring the string \ref length from the array length, and also supports strings containing null characters. \tparam N length of the string, automatically inferred \param str Constant character array, lifetime assumed to be longer than the use of the string in e.g. a GenericValue \post \ref s == str \note Constant complexity. \note There is a hidden, private overload to disallow references to non-const character arrays to be created via this constructor. By this, e.g. function-scope arrays used to be filled via \c snprintf are excluded from consideration. In such cases, the referenced string should be \b copied to the GenericValue instead. */ #endif template GenericStringRef(const CharType (&str)[N]) RAPIDJSON_NOEXCEPT : s(str), length(N-1) {} //! Explicitly create string reference from \c const character pointer #ifndef __clang__ // -Wdocumentation /*! This constructor can be used to \b explicitly create a reference to a constant string pointer. \see StringRef(const CharType*) \param str Constant character pointer, lifetime assumed to be longer than the use of the string in e.g. a GenericValue \post \ref s == str \note There is a hidden, private overload to disallow references to non-const character arrays to be created via this constructor. By this, e.g. function-scope arrays used to be filled via \c snprintf are excluded from consideration. In such cases, the referenced string should be \b copied to the GenericValue instead. */ #endif explicit GenericStringRef(const CharType* str) : s(str), length(NotNullStrLen(str)) {} //! Create constant string reference from pointer and length #ifndef __clang__ // -Wdocumentation /*! \param str constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue \param len length of the string, excluding the trailing NULL terminator \post \ref s == str && \ref length == len \note Constant complexity. */ #endif GenericStringRef(const CharType* str, SizeType len) : s(RAPIDJSON_LIKELY(str) ? str : emptyString), length(len) { RAPIDJSON_ASSERT(str != 0 || len == 0u); } GenericStringRef(const GenericStringRef& rhs) : s(rhs.s), length(rhs.length) {} //! implicit conversion to plain CharType pointer operator const Ch *() const { return s; } const Ch* const s; //!< plain CharType pointer const SizeType length; //!< length of the string (excluding the trailing NULL terminator) private: SizeType NotNullStrLen(const CharType* str) { RAPIDJSON_ASSERT(str != 0); return internal::StrLen(str); } /// Empty string - used when passing in a NULL pointer static const Ch emptyString[]; //! Disallow construction from non-const array template GenericStringRef(CharType (&str)[N]) /* = delete */; //! Copy assignment operator not permitted - immutable type GenericStringRef& operator=(const GenericStringRef& rhs) /* = delete */; }; template const CharType GenericStringRef::emptyString[] = { CharType() }; //! Mark a character pointer as constant string /*! Mark a plain character pointer as a "string literal". This function can be used to avoid copying a character string to be referenced as a value in a JSON GenericValue object, if the string's lifetime is known to be valid long enough. \tparam CharType Character type of the string \param str Constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue \return GenericStringRef string reference object \relatesalso GenericStringRef \see GenericValue::GenericValue(StringRefType), GenericValue::operator=(StringRefType), GenericValue::SetString(StringRefType), GenericValue::PushBack(StringRefType, Allocator&), GenericValue::AddMember */ template inline GenericStringRef StringRef(const CharType* str) { return GenericStringRef(str); } //! Mark a character pointer as constant string /*! Mark a plain character pointer as a "string literal". This function can be used to avoid copying a character string to be referenced as a value in a JSON GenericValue object, if the string's lifetime is known to be valid long enough. This version has better performance with supplied length, and also supports string containing null characters. \tparam CharType character type of the string \param str Constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue \param length The length of source string. \return GenericStringRef string reference object \relatesalso GenericStringRef */ template inline GenericStringRef StringRef(const CharType* str, size_t length) { return GenericStringRef(str, SizeType(length)); } #if RAPIDJSON_HAS_STDSTRING //! Mark a string object as constant string /*! Mark a string object (e.g. \c std::string) as a "string literal". This function can be used to avoid copying a string to be referenced as a value in a JSON GenericValue object, if the string's lifetime is known to be valid long enough. \tparam CharType character type of the string \param str Constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue \return GenericStringRef string reference object \relatesalso GenericStringRef \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. */ template inline GenericStringRef StringRef(const std::basic_string& str) { return GenericStringRef(str.data(), SizeType(str.size())); } #endif /////////////////////////////////////////////////////////////////////////////// // GenericValue type traits namespace internal { template struct IsGenericValueImpl : FalseType {}; // select candidates according to nested encoding and allocator types template struct IsGenericValueImpl::Type, typename Void::Type> : IsBaseOf, T>::Type {}; // helper to match arbitrary GenericValue instantiations, including derived classes template struct IsGenericValue : IsGenericValueImpl::Type {}; } // namespace internal /////////////////////////////////////////////////////////////////////////////// // TypeHelper namespace internal { template struct TypeHelper {}; template struct TypeHelper { static bool Is(const ValueType& v) { return v.IsBool(); } static bool Get(const ValueType& v) { return v.GetBool(); } static ValueType& Set(ValueType& v, bool data) { return v.SetBool(data); } static ValueType& Set(ValueType& v, bool data, typename ValueType::AllocatorType&) { return v.SetBool(data); } }; template struct TypeHelper { static bool Is(const ValueType& v) { return v.IsInt(); } static int Get(const ValueType& v) { return v.GetInt(); } static ValueType& Set(ValueType& v, int data) { return v.SetInt(data); } static ValueType& Set(ValueType& v, int data, typename ValueType::AllocatorType&) { return v.SetInt(data); } }; template struct TypeHelper { static bool Is(const ValueType& v) { return v.IsUint(); } static unsigned Get(const ValueType& v) { return v.GetUint(); } static ValueType& Set(ValueType& v, unsigned data) { return v.SetUint(data); } static ValueType& Set(ValueType& v, unsigned data, typename ValueType::AllocatorType&) { return v.SetUint(data); } }; #ifdef _MSC_VER RAPIDJSON_STATIC_ASSERT(sizeof(long) == sizeof(int)); template struct TypeHelper { static bool Is(const ValueType& v) { return v.IsInt(); } static long Get(const ValueType& v) { return v.GetInt(); } static ValueType& Set(ValueType& v, long data) { return v.SetInt(data); } static ValueType& Set(ValueType& v, long data, typename ValueType::AllocatorType&) { return v.SetInt(data); } }; RAPIDJSON_STATIC_ASSERT(sizeof(unsigned long) == sizeof(unsigned)); template struct TypeHelper { static bool Is(const ValueType& v) { return v.IsUint(); } static unsigned long Get(const ValueType& v) { return v.GetUint(); } static ValueType& Set(ValueType& v, unsigned long data) { return v.SetUint(data); } static ValueType& Set(ValueType& v, unsigned long data, typename ValueType::AllocatorType&) { return v.SetUint(data); } }; #endif template struct TypeHelper { static bool Is(const ValueType& v) { return v.IsInt64(); } static int64_t Get(const ValueType& v) { return v.GetInt64(); } static ValueType& Set(ValueType& v, int64_t data) { return v.SetInt64(data); } static ValueType& Set(ValueType& v, int64_t data, typename ValueType::AllocatorType&) { return v.SetInt64(data); } }; template struct TypeHelper { static bool Is(const ValueType& v) { return v.IsUint64(); } static uint64_t Get(const ValueType& v) { return v.GetUint64(); } static ValueType& Set(ValueType& v, uint64_t data) { return v.SetUint64(data); } static ValueType& Set(ValueType& v, uint64_t data, typename ValueType::AllocatorType&) { return v.SetUint64(data); } }; template struct TypeHelper { static bool Is(const ValueType& v) { return v.IsDouble(); } static double Get(const ValueType& v) { return v.GetDouble(); } static ValueType& Set(ValueType& v, double data) { return v.SetDouble(data); } static ValueType& Set(ValueType& v, double data, typename ValueType::AllocatorType&) { return v.SetDouble(data); } }; template struct TypeHelper { static bool Is(const ValueType& v) { return v.IsFloat(); } static float Get(const ValueType& v) { return v.GetFloat(); } static ValueType& Set(ValueType& v, float data) { return v.SetFloat(data); } static ValueType& Set(ValueType& v, float data, typename ValueType::AllocatorType&) { return v.SetFloat(data); } }; template struct TypeHelper { typedef const typename ValueType::Ch* StringType; static bool Is(const ValueType& v) { return v.IsString(); } static StringType Get(const ValueType& v) { return v.GetString(); } static ValueType& Set(ValueType& v, const StringType data) { return v.SetString(typename ValueType::StringRefType(data)); } static ValueType& Set(ValueType& v, const StringType data, typename ValueType::AllocatorType& a) { return v.SetString(data, a); } }; #if RAPIDJSON_HAS_STDSTRING template struct TypeHelper > { typedef std::basic_string StringType; static bool Is(const ValueType& v) { return v.IsString(); } static StringType Get(const ValueType& v) { return StringType(v.GetString(), v.GetStringLength()); } static ValueType& Set(ValueType& v, const StringType& data, typename ValueType::AllocatorType& a) { return v.SetString(data, a); } }; #endif template struct TypeHelper { typedef typename ValueType::Array ArrayType; static bool Is(const ValueType& v) { return v.IsArray(); } static ArrayType Get(ValueType& v) { return v.GetArray(); } static ValueType& Set(ValueType& v, ArrayType data) { return v = data; } static ValueType& Set(ValueType& v, ArrayType data, typename ValueType::AllocatorType&) { return v = data; } }; template struct TypeHelper { typedef typename ValueType::ConstArray ArrayType; static bool Is(const ValueType& v) { return v.IsArray(); } static ArrayType Get(const ValueType& v) { return v.GetArray(); } }; template struct TypeHelper { typedef typename ValueType::Object ObjectType; static bool Is(const ValueType& v) { return v.IsObject(); } static ObjectType Get(ValueType& v) { return v.GetObject(); } static ValueType& Set(ValueType& v, ObjectType data) { return v = data; } static ValueType& Set(ValueType& v, ObjectType data, typename ValueType::AllocatorType&) { return v = data; } }; template struct TypeHelper { typedef typename ValueType::ConstObject ObjectType; static bool Is(const ValueType& v) { return v.IsObject(); } static ObjectType Get(const ValueType& v) { return v.GetObject(); } }; } // namespace internal // Forward declarations template class GenericArray; template class GenericObject; /////////////////////////////////////////////////////////////////////////////// // GenericValue //! Represents a JSON value. Use Value for UTF8 encoding and default allocator. /*! A JSON value can be one of 7 types. This class is a variant type supporting these types. Use the Value if UTF8 and default allocator \tparam Encoding Encoding of the value. (Even non-string values need to have the same encoding in a document) \tparam Allocator Allocator type for allocating memory of object, array and string. */ template > class GenericValue { public: //! Name-value pair in an object. typedef GenericMember Member; typedef Encoding EncodingType; //!< Encoding type from template parameter. typedef Allocator AllocatorType; //!< Allocator type from template parameter. typedef typename Encoding::Ch Ch; //!< Character type derived from Encoding. typedef GenericStringRef StringRefType; //!< Reference to a constant string typedef typename GenericMemberIterator::Iterator MemberIterator; //!< Member iterator for iterating in object. typedef typename GenericMemberIterator::Iterator ConstMemberIterator; //!< Constant member iterator for iterating in object. typedef GenericValue* ValueIterator; //!< Value iterator for iterating in array. typedef const GenericValue* ConstValueIterator; //!< Constant value iterator for iterating in array. typedef GenericValue ValueType; //!< Value type of itself. typedef GenericArray Array; typedef GenericArray ConstArray; typedef GenericObject Object; typedef GenericObject ConstObject; //!@name Constructors and destructor. //@{ //! Default constructor creates a null value. GenericValue() RAPIDJSON_NOEXCEPT : data_() { data_.f.flags = kNullFlag; } #if RAPIDJSON_HAS_CXX11_RVALUE_REFS //! Move constructor in C++11 GenericValue(GenericValue&& rhs) RAPIDJSON_NOEXCEPT : data_(rhs.data_) { rhs.data_.f.flags = kNullFlag; // give up contents } #endif private: //! Copy constructor is not permitted. GenericValue(const GenericValue& rhs); #if RAPIDJSON_HAS_CXX11_RVALUE_REFS //! Moving from a GenericDocument is not permitted. template GenericValue(GenericDocument&& rhs); //! Move assignment from a GenericDocument is not permitted. template GenericValue& operator=(GenericDocument&& rhs); #endif public: //! Constructor with JSON value type. /*! This creates a Value of specified type with default content. \param type Type of the value. \note Default content for number is zero. */ explicit GenericValue(Type type) RAPIDJSON_NOEXCEPT : data_() { static const uint16_t defaultFlags[] = { kNullFlag, kFalseFlag, kTrueFlag, kObjectFlag, kArrayFlag, kShortStringFlag, kNumberAnyFlag }; RAPIDJSON_NOEXCEPT_ASSERT(type >= kNullType && type <= kNumberType); data_.f.flags = defaultFlags[type]; // Use ShortString to store empty string. if (type == kStringType) data_.ss.SetLength(0); } //! Explicit copy constructor (with allocator) /*! Creates a copy of a Value by using the given Allocator \tparam SourceAllocator allocator of \c rhs \param rhs Value to copy from (read-only) \param allocator Allocator for allocating copied elements and buffers. Commonly use GenericDocument::GetAllocator(). \param copyConstStrings Force copying of constant strings (e.g. referencing an in-situ buffer) \see CopyFrom() */ template GenericValue(const GenericValue& rhs, Allocator& allocator, bool copyConstStrings = false) { switch (rhs.GetType()) { case kObjectType: { SizeType count = rhs.data_.o.size; Member* lm = reinterpret_cast(allocator.Malloc(count * sizeof(Member))); const typename GenericValue::Member* rm = rhs.GetMembersPointer(); for (SizeType i = 0; i < count; i++) { new (&lm[i].name) GenericValue(rm[i].name, allocator, copyConstStrings); new (&lm[i].value) GenericValue(rm[i].value, allocator, copyConstStrings); } data_.f.flags = kObjectFlag; data_.o.size = data_.o.capacity = count; SetMembersPointer(lm); } break; case kArrayType: { SizeType count = rhs.data_.a.size; GenericValue* le = reinterpret_cast(allocator.Malloc(count * sizeof(GenericValue))); const GenericValue* re = rhs.GetElementsPointer(); for (SizeType i = 0; i < count; i++) new (&le[i]) GenericValue(re[i], allocator, copyConstStrings); data_.f.flags = kArrayFlag; data_.a.size = data_.a.capacity = count; SetElementsPointer(le); } break; case kStringType: if (rhs.data_.f.flags == kConstStringFlag && !copyConstStrings) { data_.f.flags = rhs.data_.f.flags; data_ = *reinterpret_cast(&rhs.data_); } else SetStringRaw(StringRef(rhs.GetString(), rhs.GetStringLength()), allocator); break; default: data_.f.flags = rhs.data_.f.flags; data_ = *reinterpret_cast(&rhs.data_); break; } } //! Constructor for boolean value. /*! \param b Boolean value \note This constructor is limited to \em real boolean values and rejects implicitly converted types like arbitrary pointers. Use an explicit cast to \c bool, if you want to construct a boolean JSON value in such cases. */ #ifndef RAPIDJSON_DOXYGEN_RUNNING // hide SFINAE from Doxygen template explicit GenericValue(T b, RAPIDJSON_ENABLEIF((internal::IsSame))) RAPIDJSON_NOEXCEPT // See #472 #else explicit GenericValue(bool b) RAPIDJSON_NOEXCEPT #endif : data_() { // safe-guard against failing SFINAE RAPIDJSON_STATIC_ASSERT((internal::IsSame::Value)); data_.f.flags = b ? kTrueFlag : kFalseFlag; } //! Constructor for int value. explicit GenericValue(int i) RAPIDJSON_NOEXCEPT : data_() { data_.n.i64 = i; data_.f.flags = (i >= 0) ? (kNumberIntFlag | kUintFlag | kUint64Flag) : kNumberIntFlag; } //! Constructor for unsigned value. explicit GenericValue(unsigned u) RAPIDJSON_NOEXCEPT : data_() { data_.n.u64 = u; data_.f.flags = (u & 0x80000000) ? kNumberUintFlag : (kNumberUintFlag | kIntFlag | kInt64Flag); } //! Constructor for int64_t value. explicit GenericValue(int64_t i64) RAPIDJSON_NOEXCEPT : data_() { data_.n.i64 = i64; data_.f.flags = kNumberInt64Flag; if (i64 >= 0) { data_.f.flags |= kNumberUint64Flag; if (!(static_cast(i64) & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x00000000))) data_.f.flags |= kUintFlag; if (!(static_cast(i64) & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000))) data_.f.flags |= kIntFlag; } else if (i64 >= static_cast(RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000))) data_.f.flags |= kIntFlag; } //! Constructor for uint64_t value. explicit GenericValue(uint64_t u64) RAPIDJSON_NOEXCEPT : data_() { data_.n.u64 = u64; data_.f.flags = kNumberUint64Flag; if (!(u64 & RAPIDJSON_UINT64_C2(0x80000000, 0x00000000))) data_.f.flags |= kInt64Flag; if (!(u64 & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x00000000))) data_.f.flags |= kUintFlag; if (!(u64 & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000))) data_.f.flags |= kIntFlag; } //! Constructor for double value. explicit GenericValue(double d) RAPIDJSON_NOEXCEPT : data_() { data_.n.d = d; data_.f.flags = kNumberDoubleFlag; } //! Constructor for float value. explicit GenericValue(float f) RAPIDJSON_NOEXCEPT : data_() { data_.n.d = static_cast(f); data_.f.flags = kNumberDoubleFlag; } //! Constructor for constant string (i.e. do not make a copy of string) GenericValue(const Ch* s, SizeType length) RAPIDJSON_NOEXCEPT : data_() { SetStringRaw(StringRef(s, length)); } //! Constructor for constant string (i.e. do not make a copy of string) explicit GenericValue(StringRefType s) RAPIDJSON_NOEXCEPT : data_() { SetStringRaw(s); } //! Constructor for copy-string (i.e. do make a copy of string) GenericValue(const Ch* s, SizeType length, Allocator& allocator) : data_() { SetStringRaw(StringRef(s, length), allocator); } //! Constructor for copy-string (i.e. do make a copy of string) GenericValue(const Ch*s, Allocator& allocator) : data_() { SetStringRaw(StringRef(s), allocator); } #if RAPIDJSON_HAS_STDSTRING //! Constructor for copy-string from a string object (i.e. do make a copy of string) /*! \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. */ GenericValue(const std::basic_string& s, Allocator& allocator) : data_() { SetStringRaw(StringRef(s), allocator); } #endif //! Constructor for Array. /*! \param a An array obtained by \c GetArray(). \note \c Array is always pass-by-value. \note the source array is moved into this value and the sourec array becomes empty. */ GenericValue(Array a) RAPIDJSON_NOEXCEPT : data_(a.value_.data_) { a.value_.data_ = Data(); a.value_.data_.f.flags = kArrayFlag; } //! Constructor for Object. /*! \param o An object obtained by \c GetObject(). \note \c Object is always pass-by-value. \note the source object is moved into this value and the sourec object becomes empty. */ GenericValue(Object o) RAPIDJSON_NOEXCEPT : data_(o.value_.data_) { o.value_.data_ = Data(); o.value_.data_.f.flags = kObjectFlag; } //! Destructor. /*! Need to destruct elements of array, members of object, or copy-string. */ ~GenericValue() { if (Allocator::kNeedFree) { // Shortcut by Allocator's trait switch(data_.f.flags) { case kArrayFlag: { GenericValue* e = GetElementsPointer(); for (GenericValue* v = e; v != e + data_.a.size; ++v) v->~GenericValue(); Allocator::Free(e); } break; case kObjectFlag: for (MemberIterator m = MemberBegin(); m != MemberEnd(); ++m) m->~Member(); Allocator::Free(GetMembersPointer()); break; case kCopyStringFlag: Allocator::Free(const_cast(GetStringPointer())); break; default: break; // Do nothing for other types. } } } //@} //!@name Assignment operators //@{ //! Assignment with move semantics. /*! \param rhs Source of the assignment. It will become a null value after assignment. */ GenericValue& operator=(GenericValue& rhs) RAPIDJSON_NOEXCEPT { if (RAPIDJSON_LIKELY(this != &rhs)) { this->~GenericValue(); RawAssign(rhs); } return *this; } #if RAPIDJSON_HAS_CXX11_RVALUE_REFS //! Move assignment in C++11 GenericValue& operator=(GenericValue&& rhs) RAPIDJSON_NOEXCEPT { return *this = rhs.Move(); } #endif //! Assignment of constant string reference (no copy) /*! \param str Constant string reference to be assigned \note This overload is needed to avoid clashes with the generic primitive type assignment overload below. \see GenericStringRef, operator=(T) */ GenericValue& operator=(StringRefType str) RAPIDJSON_NOEXCEPT { GenericValue s(str); return *this = s; } //! Assignment with primitive types. /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t \param value The value to be assigned. \note The source type \c T explicitly disallows all pointer types, especially (\c const) \ref Ch*. This helps avoiding implicitly referencing character strings with insufficient lifetime, use \ref SetString(const Ch*, Allocator&) (for copying) or \ref StringRef() (to explicitly mark the pointer as constant) instead. All other pointer types would implicitly convert to \c bool, use \ref SetBool() instead. */ template RAPIDJSON_DISABLEIF_RETURN((internal::IsPointer), (GenericValue&)) operator=(T value) { GenericValue v(value); return *this = v; } //! Deep-copy assignment from Value /*! Assigns a \b copy of the Value to the current Value object \tparam SourceAllocator Allocator type of \c rhs \param rhs Value to copy from (read-only) \param allocator Allocator to use for copying \param copyConstStrings Force copying of constant strings (e.g. referencing an in-situ buffer) */ template GenericValue& CopyFrom(const GenericValue& rhs, Allocator& allocator, bool copyConstStrings = false) { RAPIDJSON_ASSERT(static_cast(this) != static_cast(&rhs)); this->~GenericValue(); new (this) GenericValue(rhs, allocator, copyConstStrings); return *this; } //! Exchange the contents of this value with those of other. /*! \param other Another value. \note Constant complexity. */ GenericValue& Swap(GenericValue& other) RAPIDJSON_NOEXCEPT { GenericValue temp; temp.RawAssign(*this); RawAssign(other); other.RawAssign(temp); return *this; } //! free-standing swap function helper /*! Helper function to enable support for common swap implementation pattern based on \c std::swap: \code void swap(MyClass& a, MyClass& b) { using std::swap; swap(a.value, b.value); // ... } \endcode \see Swap() */ friend inline void swap(GenericValue& a, GenericValue& b) RAPIDJSON_NOEXCEPT { a.Swap(b); } //! Prepare Value for move semantics /*! \return *this */ GenericValue& Move() RAPIDJSON_NOEXCEPT { return *this; } //@} //!@name Equal-to and not-equal-to operators //@{ //! Equal-to operator /*! \note If an object contains duplicated named member, comparing equality with any object is always \c false. \note Complexity is quadratic in Object's member number and linear for the rest (number of all values in the subtree and total lengths of all strings). */ template bool operator==(const GenericValue& rhs) const { typedef GenericValue RhsType; if (GetType() != rhs.GetType()) return false; switch (GetType()) { case kObjectType: // Warning: O(n^2) inner-loop if (data_.o.size != rhs.data_.o.size) return false; for (ConstMemberIterator lhsMemberItr = MemberBegin(); lhsMemberItr != MemberEnd(); ++lhsMemberItr) { typename RhsType::ConstMemberIterator rhsMemberItr = rhs.FindMember(lhsMemberItr->name); if (rhsMemberItr == rhs.MemberEnd() || lhsMemberItr->value != rhsMemberItr->value) return false; } return true; case kArrayType: if (data_.a.size != rhs.data_.a.size) return false; for (SizeType i = 0; i < data_.a.size; i++) if ((*this)[i] != rhs[i]) return false; return true; case kStringType: return StringEqual(rhs); case kNumberType: if (IsDouble() || rhs.IsDouble()) { double a = GetDouble(); // May convert from integer to double. double b = rhs.GetDouble(); // Ditto return a >= b && a <= b; // Prevent -Wfloat-equal } else return data_.n.u64 == rhs.data_.n.u64; default: return true; } } //! Equal-to operator with const C-string pointer bool operator==(const Ch* rhs) const { return *this == GenericValue(StringRef(rhs)); } #if RAPIDJSON_HAS_STDSTRING //! Equal-to operator with string object /*! \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. */ bool operator==(const std::basic_string& rhs) const { return *this == GenericValue(StringRef(rhs)); } #endif //! Equal-to operator with primitive types /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c double, \c true, \c false */ template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr,internal::IsGenericValue >), (bool)) operator==(const T& rhs) const { return *this == GenericValue(rhs); } //! Not-equal-to operator /*! \return !(*this == rhs) */ template bool operator!=(const GenericValue& rhs) const { return !(*this == rhs); } //! Not-equal-to operator with const C-string pointer bool operator!=(const Ch* rhs) const { return !(*this == rhs); } //! Not-equal-to operator with arbitrary types /*! \return !(*this == rhs) */ template RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue), (bool)) operator!=(const T& rhs) const { return !(*this == rhs); } //! Equal-to operator with arbitrary types (symmetric version) /*! \return (rhs == lhs) */ template friend RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue), (bool)) operator==(const T& lhs, const GenericValue& rhs) { return rhs == lhs; } //! Not-Equal-to operator with arbitrary types (symmetric version) /*! \return !(rhs == lhs) */ template friend RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue), (bool)) operator!=(const T& lhs, const GenericValue& rhs) { return !(rhs == lhs); } //@} //!@name Type //@{ Type GetType() const { return static_cast(data_.f.flags & kTypeMask); } bool IsNull() const { return data_.f.flags == kNullFlag; } bool IsFalse() const { return data_.f.flags == kFalseFlag; } bool IsTrue() const { return data_.f.flags == kTrueFlag; } bool IsBool() const { return (data_.f.flags & kBoolFlag) != 0; } bool IsObject() const { return data_.f.flags == kObjectFlag; } bool IsArray() const { return data_.f.flags == kArrayFlag; } bool IsNumber() const { return (data_.f.flags & kNumberFlag) != 0; } bool IsInt() const { return (data_.f.flags & kIntFlag) != 0; } bool IsUint() const { return (data_.f.flags & kUintFlag) != 0; } bool IsInt64() const { return (data_.f.flags & kInt64Flag) != 0; } bool IsUint64() const { return (data_.f.flags & kUint64Flag) != 0; } bool IsDouble() const { return (data_.f.flags & kDoubleFlag) != 0; } bool IsString() const { return (data_.f.flags & kStringFlag) != 0; } // Checks whether a number can be losslessly converted to a double. bool IsLosslessDouble() const { if (!IsNumber()) return false; if (IsUint64()) { uint64_t u = GetUint64(); volatile double d = static_cast(u); return (d >= 0.0) && (d < static_cast((std::numeric_limits::max)())) && (u == static_cast(d)); } if (IsInt64()) { int64_t i = GetInt64(); volatile double d = static_cast(i); return (d >= static_cast((std::numeric_limits::min)())) && (d < static_cast((std::numeric_limits::max)())) && (i == static_cast(d)); } return true; // double, int, uint are always lossless } // Checks whether a number is a float (possible lossy). bool IsFloat() const { if ((data_.f.flags & kDoubleFlag) == 0) return false; double d = GetDouble(); return d >= -3.4028234e38 && d <= 3.4028234e38; } // Checks whether a number can be losslessly converted to a float. bool IsLosslessFloat() const { if (!IsNumber()) return false; double a = GetDouble(); if (a < static_cast(-(std::numeric_limits::max)()) || a > static_cast((std::numeric_limits::max)())) return false; double b = static_cast(static_cast(a)); return a >= b && a <= b; // Prevent -Wfloat-equal } //@} //!@name Null //@{ GenericValue& SetNull() { this->~GenericValue(); new (this) GenericValue(); return *this; } //@} //!@name Bool //@{ bool GetBool() const { RAPIDJSON_ASSERT(IsBool()); return data_.f.flags == kTrueFlag; } //!< Set boolean value /*! \post IsBool() == true */ GenericValue& SetBool(bool b) { this->~GenericValue(); new (this) GenericValue(b); return *this; } //@} //!@name Object //@{ //! Set this value as an empty object. /*! \post IsObject() == true */ GenericValue& SetObject() { this->~GenericValue(); new (this) GenericValue(kObjectType); return *this; } //! Get the number of members in the object. SizeType MemberCount() const { RAPIDJSON_ASSERT(IsObject()); return data_.o.size; } //! Get the capacity of object. SizeType MemberCapacity() const { RAPIDJSON_ASSERT(IsObject()); return data_.o.capacity; } //! Check whether the object is empty. bool ObjectEmpty() const { RAPIDJSON_ASSERT(IsObject()); return data_.o.size == 0; } //! Get a value from an object associated with the name. /*! \pre IsObject() == true \tparam T Either \c Ch or \c const \c Ch (template used for disambiguation with \ref operator[](SizeType)) \note In version 0.1x, if the member is not found, this function returns a null value. This makes issue 7. Since 0.2, if the name is not correct, it will assert. If user is unsure whether a member exists, user should use HasMember() first. A better approach is to use FindMember(). \note Linear time complexity. */ template RAPIDJSON_DISABLEIF_RETURN((internal::NotExpr::Type, Ch> >),(GenericValue&)) operator[](T* name) { GenericValue n(StringRef(name)); return (*this)[n]; } template RAPIDJSON_DISABLEIF_RETURN((internal::NotExpr::Type, Ch> >),(const GenericValue&)) operator[](T* name) const { return const_cast(*this)[name]; } //! Get a value from an object associated with the name. /*! \pre IsObject() == true \tparam SourceAllocator Allocator of the \c name value \note Compared to \ref operator[](T*), this version is faster because it does not need a StrLen(). And it can also handle strings with embedded null characters. \note Linear time complexity. */ template GenericValue& operator[](const GenericValue& name) { MemberIterator member = FindMember(name); if (member != MemberEnd()) return member->value; else { RAPIDJSON_ASSERT(false); // see above note // This will generate -Wexit-time-destructors in clang // static GenericValue NullValue; // return NullValue; // Use static buffer and placement-new to prevent destruction static char buffer[sizeof(GenericValue)]; return *new (buffer) GenericValue(); } } template const GenericValue& operator[](const GenericValue& name) const { return const_cast(*this)[name]; } #if RAPIDJSON_HAS_STDSTRING //! Get a value from an object associated with name (string object). GenericValue& operator[](const std::basic_string& name) { return (*this)[GenericValue(StringRef(name))]; } const GenericValue& operator[](const std::basic_string& name) const { return (*this)[GenericValue(StringRef(name))]; } #endif //! Const member iterator /*! \pre IsObject() == true */ ConstMemberIterator MemberBegin() const { RAPIDJSON_ASSERT(IsObject()); return ConstMemberIterator(GetMembersPointer()); } //! Const \em past-the-end member iterator /*! \pre IsObject() == true */ ConstMemberIterator MemberEnd() const { RAPIDJSON_ASSERT(IsObject()); return ConstMemberIterator(GetMembersPointer() + data_.o.size); } //! Member iterator /*! \pre IsObject() == true */ MemberIterator MemberBegin() { RAPIDJSON_ASSERT(IsObject()); return MemberIterator(GetMembersPointer()); } //! \em Past-the-end member iterator /*! \pre IsObject() == true */ MemberIterator MemberEnd() { RAPIDJSON_ASSERT(IsObject()); return MemberIterator(GetMembersPointer() + data_.o.size); } //! Request the object to have enough capacity to store members. /*! \param newCapacity The capacity that the object at least need to have. \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). \return The value itself for fluent API. \note Linear time complexity. */ GenericValue& MemberReserve(SizeType newCapacity, Allocator &allocator) { RAPIDJSON_ASSERT(IsObject()); if (newCapacity > data_.o.capacity) { SetMembersPointer(reinterpret_cast(allocator.Realloc(GetMembersPointer(), data_.o.capacity * sizeof(Member), newCapacity * sizeof(Member)))); data_.o.capacity = newCapacity; } return *this; } //! Check whether a member exists in the object. /*! \param name Member name to be searched. \pre IsObject() == true \return Whether a member with that name exists. \note It is better to use FindMember() directly if you need the obtain the value as well. \note Linear time complexity. */ bool HasMember(const Ch* name) const { return FindMember(name) != MemberEnd(); } #if RAPIDJSON_HAS_STDSTRING //! Check whether a member exists in the object with string object. /*! \param name Member name to be searched. \pre IsObject() == true \return Whether a member with that name exists. \note It is better to use FindMember() directly if you need the obtain the value as well. \note Linear time complexity. */ bool HasMember(const std::basic_string& name) const { return FindMember(name) != MemberEnd(); } #endif //! Check whether a member exists in the object with GenericValue name. /*! This version is faster because it does not need a StrLen(). It can also handle string with null character. \param name Member name to be searched. \pre IsObject() == true \return Whether a member with that name exists. \note It is better to use FindMember() directly if you need the obtain the value as well. \note Linear time complexity. */ template bool HasMember(const GenericValue& name) const { return FindMember(name) != MemberEnd(); } //! Find member by name. /*! \param name Member name to be searched. \pre IsObject() == true \return Iterator to member, if it exists. Otherwise returns \ref MemberEnd(). \note Earlier versions of Rapidjson returned a \c NULL pointer, in case the requested member doesn't exist. For consistency with e.g. \c std::map, this has been changed to MemberEnd() now. \note Linear time complexity. */ MemberIterator FindMember(const Ch* name) { GenericValue n(StringRef(name)); return FindMember(n); } ConstMemberIterator FindMember(const Ch* name) const { return const_cast(*this).FindMember(name); } //! Find member by name. /*! This version is faster because it does not need a StrLen(). It can also handle string with null character. \param name Member name to be searched. \pre IsObject() == true \return Iterator to member, if it exists. Otherwise returns \ref MemberEnd(). \note Earlier versions of Rapidjson returned a \c NULL pointer, in case the requested member doesn't exist. For consistency with e.g. \c std::map, this has been changed to MemberEnd() now. \note Linear time complexity. */ template MemberIterator FindMember(const GenericValue& name) { RAPIDJSON_ASSERT(IsObject()); RAPIDJSON_ASSERT(name.IsString()); MemberIterator member = MemberBegin(); for ( ; member != MemberEnd(); ++member) if (name.StringEqual(member->name)) break; return member; } template ConstMemberIterator FindMember(const GenericValue& name) const { return const_cast(*this).FindMember(name); } #if RAPIDJSON_HAS_STDSTRING //! Find member by string object name. /*! \param name Member name to be searched. \pre IsObject() == true \return Iterator to member, if it exists. Otherwise returns \ref MemberEnd(). */ MemberIterator FindMember(const std::basic_string& name) { return FindMember(GenericValue(StringRef(name))); } ConstMemberIterator FindMember(const std::basic_string& name) const { return FindMember(GenericValue(StringRef(name))); } #endif //! Add a member (name-value pair) to the object. /*! \param name A string value as name of member. \param value Value of any type. \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). \return The value itself for fluent API. \note The ownership of \c name and \c value will be transferred to this object on success. \pre IsObject() && name.IsString() \post name.IsNull() && value.IsNull() \note Amortized Constant time complexity. */ GenericValue& AddMember(GenericValue& name, GenericValue& value, Allocator& allocator) { RAPIDJSON_ASSERT(IsObject()); RAPIDJSON_ASSERT(name.IsString()); ObjectData& o = data_.o; if (o.size >= o.capacity) MemberReserve(o.capacity == 0 ? kDefaultObjectCapacity : (o.capacity + (o.capacity + 1) / 2), allocator); Member* members = GetMembersPointer(); members[o.size].name.RawAssign(name); members[o.size].value.RawAssign(value); o.size++; return *this; } //! Add a constant string value as member (name-value pair) to the object. /*! \param name A string value as name of member. \param value constant string reference as value of member. \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). \return The value itself for fluent API. \pre IsObject() \note This overload is needed to avoid clashes with the generic primitive type AddMember(GenericValue&,T,Allocator&) overload below. \note Amortized Constant time complexity. */ GenericValue& AddMember(GenericValue& name, StringRefType value, Allocator& allocator) { GenericValue v(value); return AddMember(name, v, allocator); } #if RAPIDJSON_HAS_STDSTRING //! Add a string object as member (name-value pair) to the object. /*! \param name A string value as name of member. \param value constant string reference as value of member. \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). \return The value itself for fluent API. \pre IsObject() \note This overload is needed to avoid clashes with the generic primitive type AddMember(GenericValue&,T,Allocator&) overload below. \note Amortized Constant time complexity. */ GenericValue& AddMember(GenericValue& name, std::basic_string& value, Allocator& allocator) { GenericValue v(value, allocator); return AddMember(name, v, allocator); } #endif //! Add any primitive value as member (name-value pair) to the object. /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t \param name A string value as name of member. \param value Value of primitive type \c T as value of member \param allocator Allocator for reallocating memory. Commonly use GenericDocument::GetAllocator(). \return The value itself for fluent API. \pre IsObject() \note The source type \c T explicitly disallows all pointer types, especially (\c const) \ref Ch*. This helps avoiding implicitly referencing character strings with insufficient lifetime, use \ref AddMember(StringRefType, GenericValue&, Allocator&) or \ref AddMember(StringRefType, StringRefType, Allocator&). All other pointer types would implicitly convert to \c bool, use an explicit cast instead, if needed. \note Amortized Constant time complexity. */ template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (GenericValue&)) AddMember(GenericValue& name, T value, Allocator& allocator) { GenericValue v(value); return AddMember(name, v, allocator); } #if RAPIDJSON_HAS_CXX11_RVALUE_REFS GenericValue& AddMember(GenericValue&& name, GenericValue&& value, Allocator& allocator) { return AddMember(name, value, allocator); } GenericValue& AddMember(GenericValue&& name, GenericValue& value, Allocator& allocator) { return AddMember(name, value, allocator); } GenericValue& AddMember(GenericValue& name, GenericValue&& value, Allocator& allocator) { return AddMember(name, value, allocator); } GenericValue& AddMember(StringRefType name, GenericValue&& value, Allocator& allocator) { GenericValue n(name); return AddMember(n, value, allocator); } #endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS //! Add a member (name-value pair) to the object. /*! \param name A constant string reference as name of member. \param value Value of any type. \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). \return The value itself for fluent API. \note The ownership of \c value will be transferred to this object on success. \pre IsObject() \post value.IsNull() \note Amortized Constant time complexity. */ GenericValue& AddMember(StringRefType name, GenericValue& value, Allocator& allocator) { GenericValue n(name); return AddMember(n, value, allocator); } //! Add a constant string value as member (name-value pair) to the object. /*! \param name A constant string reference as name of member. \param value constant string reference as value of member. \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). \return The value itself for fluent API. \pre IsObject() \note This overload is needed to avoid clashes with the generic primitive type AddMember(StringRefType,T,Allocator&) overload below. \note Amortized Constant time complexity. */ GenericValue& AddMember(StringRefType name, StringRefType value, Allocator& allocator) { GenericValue v(value); return AddMember(name, v, allocator); } //! Add any primitive value as member (name-value pair) to the object. /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t \param name A constant string reference as name of member. \param value Value of primitive type \c T as value of member \param allocator Allocator for reallocating memory. Commonly use GenericDocument::GetAllocator(). \return The value itself for fluent API. \pre IsObject() \note The source type \c T explicitly disallows all pointer types, especially (\c const) \ref Ch*. This helps avoiding implicitly referencing character strings with insufficient lifetime, use \ref AddMember(StringRefType, GenericValue&, Allocator&) or \ref AddMember(StringRefType, StringRefType, Allocator&). All other pointer types would implicitly convert to \c bool, use an explicit cast instead, if needed. \note Amortized Constant time complexity. */ template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (GenericValue&)) AddMember(StringRefType name, T value, Allocator& allocator) { GenericValue n(name); return AddMember(n, value, allocator); } //! Remove all members in the object. /*! This function do not deallocate memory in the object, i.e. the capacity is unchanged. \note Linear time complexity. */ void RemoveAllMembers() { RAPIDJSON_ASSERT(IsObject()); for (MemberIterator m = MemberBegin(); m != MemberEnd(); ++m) m->~Member(); data_.o.size = 0; } //! Remove a member in object by its name. /*! \param name Name of member to be removed. \return Whether the member existed. \note This function may reorder the object members. Use \ref EraseMember(ConstMemberIterator) if you need to preserve the relative order of the remaining members. \note Linear time complexity. */ bool RemoveMember(const Ch* name) { GenericValue n(StringRef(name)); return RemoveMember(n); } #if RAPIDJSON_HAS_STDSTRING bool RemoveMember(const std::basic_string& name) { return RemoveMember(GenericValue(StringRef(name))); } #endif template bool RemoveMember(const GenericValue& name) { MemberIterator m = FindMember(name); if (m != MemberEnd()) { RemoveMember(m); return true; } else return false; } //! Remove a member in object by iterator. /*! \param m member iterator (obtained by FindMember() or MemberBegin()). \return the new iterator after removal. \note This function may reorder the object members. Use \ref EraseMember(ConstMemberIterator) if you need to preserve the relative order of the remaining members. \note Constant time complexity. */ MemberIterator RemoveMember(MemberIterator m) { RAPIDJSON_ASSERT(IsObject()); RAPIDJSON_ASSERT(data_.o.size > 0); RAPIDJSON_ASSERT(GetMembersPointer() != 0); RAPIDJSON_ASSERT(m >= MemberBegin() && m < MemberEnd()); MemberIterator last(GetMembersPointer() + (data_.o.size - 1)); if (data_.o.size > 1 && m != last) *m = *last; // Move the last one to this place else m->~Member(); // Only one left, just destroy --data_.o.size; return m; } //! Remove a member from an object by iterator. /*! \param pos iterator to the member to remove \pre IsObject() == true && \ref MemberBegin() <= \c pos < \ref MemberEnd() \return Iterator following the removed element. If the iterator \c pos refers to the last element, the \ref MemberEnd() iterator is returned. \note This function preserves the relative order of the remaining object members. If you do not need this, use the more efficient \ref RemoveMember(MemberIterator). \note Linear time complexity. */ MemberIterator EraseMember(ConstMemberIterator pos) { return EraseMember(pos, pos +1); } //! Remove members in the range [first, last) from an object. /*! \param first iterator to the first member to remove \param last iterator following the last member to remove \pre IsObject() == true && \ref MemberBegin() <= \c first <= \c last <= \ref MemberEnd() \return Iterator following the last removed element. \note This function preserves the relative order of the remaining object members. \note Linear time complexity. */ MemberIterator EraseMember(ConstMemberIterator first, ConstMemberIterator last) { RAPIDJSON_ASSERT(IsObject()); RAPIDJSON_ASSERT(data_.o.size > 0); RAPIDJSON_ASSERT(GetMembersPointer() != 0); RAPIDJSON_ASSERT(first >= MemberBegin()); RAPIDJSON_ASSERT(first <= last); RAPIDJSON_ASSERT(last <= MemberEnd()); MemberIterator pos = MemberBegin() + (first - MemberBegin()); for (MemberIterator itr = pos; itr != last; ++itr) itr->~Member(); std::memmove(static_cast(&*pos), &*last, static_cast(MemberEnd() - last) * sizeof(Member)); data_.o.size -= static_cast(last - first); return pos; } //! Erase a member in object by its name. /*! \param name Name of member to be removed. \return Whether the member existed. \note Linear time complexity. */ bool EraseMember(const Ch* name) { GenericValue n(StringRef(name)); return EraseMember(n); } #if RAPIDJSON_HAS_STDSTRING bool EraseMember(const std::basic_string& name) { return EraseMember(GenericValue(StringRef(name))); } #endif template bool EraseMember(const GenericValue& name) { MemberIterator m = FindMember(name); if (m != MemberEnd()) { EraseMember(m); return true; } else return false; } Object GetObject() { RAPIDJSON_ASSERT(IsObject()); return Object(*this); } ConstObject GetObject() const { RAPIDJSON_ASSERT(IsObject()); return ConstObject(*this); } //@} //!@name Array //@{ //! Set this value as an empty array. /*! \post IsArray == true */ GenericValue& SetArray() { this->~GenericValue(); new (this) GenericValue(kArrayType); return *this; } //! Get the number of elements in array. SizeType Size() const { RAPIDJSON_ASSERT(IsArray()); return data_.a.size; } //! Get the capacity of array. SizeType Capacity() const { RAPIDJSON_ASSERT(IsArray()); return data_.a.capacity; } //! Check whether the array is empty. bool Empty() const { RAPIDJSON_ASSERT(IsArray()); return data_.a.size == 0; } //! Remove all elements in the array. /*! This function do not deallocate memory in the array, i.e. the capacity is unchanged. \note Linear time complexity. */ void Clear() { RAPIDJSON_ASSERT(IsArray()); GenericValue* e = GetElementsPointer(); for (GenericValue* v = e; v != e + data_.a.size; ++v) v->~GenericValue(); data_.a.size = 0; } //! Get an element from array by index. /*! \pre IsArray() == true \param index Zero-based index of element. \see operator[](T*) */ GenericValue& operator[](SizeType index) { RAPIDJSON_ASSERT(IsArray()); RAPIDJSON_ASSERT(index < data_.a.size); return GetElementsPointer()[index]; } const GenericValue& operator[](SizeType index) const { return const_cast(*this)[index]; } //! Element iterator /*! \pre IsArray() == true */ ValueIterator Begin() { RAPIDJSON_ASSERT(IsArray()); return GetElementsPointer(); } //! \em Past-the-end element iterator /*! \pre IsArray() == true */ ValueIterator End() { RAPIDJSON_ASSERT(IsArray()); return GetElementsPointer() + data_.a.size; } //! Constant element iterator /*! \pre IsArray() == true */ ConstValueIterator Begin() const { return const_cast(*this).Begin(); } //! Constant \em past-the-end element iterator /*! \pre IsArray() == true */ ConstValueIterator End() const { return const_cast(*this).End(); } //! Request the array to have enough capacity to store elements. /*! \param newCapacity The capacity that the array at least need to have. \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). \return The value itself for fluent API. \note Linear time complexity. */ GenericValue& Reserve(SizeType newCapacity, Allocator &allocator) { RAPIDJSON_ASSERT(IsArray()); if (newCapacity > data_.a.capacity) { SetElementsPointer(reinterpret_cast(allocator.Realloc(GetElementsPointer(), data_.a.capacity * sizeof(GenericValue), newCapacity * sizeof(GenericValue)))); data_.a.capacity = newCapacity; } return *this; } //! Append a GenericValue at the end of the array. /*! \param value Value to be appended. \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). \pre IsArray() == true \post value.IsNull() == true \return The value itself for fluent API. \note The ownership of \c value will be transferred to this array on success. \note If the number of elements to be appended is known, calls Reserve() once first may be more efficient. \note Amortized constant time complexity. */ GenericValue& PushBack(GenericValue& value, Allocator& allocator) { RAPIDJSON_ASSERT(IsArray()); if (data_.a.size >= data_.a.capacity) Reserve(data_.a.capacity == 0 ? kDefaultArrayCapacity : (data_.a.capacity + (data_.a.capacity + 1) / 2), allocator); GetElementsPointer()[data_.a.size++].RawAssign(value); return *this; } #if RAPIDJSON_HAS_CXX11_RVALUE_REFS GenericValue& PushBack(GenericValue&& value, Allocator& allocator) { return PushBack(value, allocator); } #endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS //! Append a constant string reference at the end of the array. /*! \param value Constant string reference to be appended. \param allocator Allocator for reallocating memory. It must be the same one used previously. Commonly use GenericDocument::GetAllocator(). \pre IsArray() == true \return The value itself for fluent API. \note If the number of elements to be appended is known, calls Reserve() once first may be more efficient. \note Amortized constant time complexity. \see GenericStringRef */ GenericValue& PushBack(StringRefType value, Allocator& allocator) { return (*this).template PushBack(value, allocator); } //! Append a primitive value at the end of the array. /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t \param value Value of primitive type T to be appended. \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). \pre IsArray() == true \return The value itself for fluent API. \note If the number of elements to be appended is known, calls Reserve() once first may be more efficient. \note The source type \c T explicitly disallows all pointer types, especially (\c const) \ref Ch*. This helps avoiding implicitly referencing character strings with insufficient lifetime, use \ref PushBack(GenericValue&, Allocator&) or \ref PushBack(StringRefType, Allocator&). All other pointer types would implicitly convert to \c bool, use an explicit cast instead, if needed. \note Amortized constant time complexity. */ template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (GenericValue&)) PushBack(T value, Allocator& allocator) { GenericValue v(value); return PushBack(v, allocator); } //! Remove the last element in the array. /*! \note Constant time complexity. */ GenericValue& PopBack() { RAPIDJSON_ASSERT(IsArray()); RAPIDJSON_ASSERT(!Empty()); GetElementsPointer()[--data_.a.size].~GenericValue(); return *this; } //! Remove an element of array by iterator. /*! \param pos iterator to the element to remove \pre IsArray() == true && \ref Begin() <= \c pos < \ref End() \return Iterator following the removed element. If the iterator pos refers to the last element, the End() iterator is returned. \note Linear time complexity. */ ValueIterator Erase(ConstValueIterator pos) { return Erase(pos, pos + 1); } //! Remove elements in the range [first, last) of the array. /*! \param first iterator to the first element to remove \param last iterator following the last element to remove \pre IsArray() == true && \ref Begin() <= \c first <= \c last <= \ref End() \return Iterator following the last removed element. \note Linear time complexity. */ ValueIterator Erase(ConstValueIterator first, ConstValueIterator last) { RAPIDJSON_ASSERT(IsArray()); RAPIDJSON_ASSERT(data_.a.size > 0); RAPIDJSON_ASSERT(GetElementsPointer() != 0); RAPIDJSON_ASSERT(first >= Begin()); RAPIDJSON_ASSERT(first <= last); RAPIDJSON_ASSERT(last <= End()); ValueIterator pos = Begin() + (first - Begin()); for (ValueIterator itr = pos; itr != last; ++itr) itr->~GenericValue(); std::memmove(static_cast(pos), last, static_cast(End() - last) * sizeof(GenericValue)); data_.a.size -= static_cast(last - first); return pos; } Array GetArray() { RAPIDJSON_ASSERT(IsArray()); return Array(*this); } ConstArray GetArray() const { RAPIDJSON_ASSERT(IsArray()); return ConstArray(*this); } //@} //!@name Number //@{ int GetInt() const { RAPIDJSON_ASSERT(data_.f.flags & kIntFlag); return data_.n.i.i; } unsigned GetUint() const { RAPIDJSON_ASSERT(data_.f.flags & kUintFlag); return data_.n.u.u; } int64_t GetInt64() const { RAPIDJSON_ASSERT(data_.f.flags & kInt64Flag); return data_.n.i64; } uint64_t GetUint64() const { RAPIDJSON_ASSERT(data_.f.flags & kUint64Flag); return data_.n.u64; } //! Get the value as double type. /*! \note If the value is 64-bit integer type, it may lose precision. Use \c IsLosslessDouble() to check whether the converison is lossless. */ double GetDouble() const { RAPIDJSON_ASSERT(IsNumber()); if ((data_.f.flags & kDoubleFlag) != 0) return data_.n.d; // exact type, no conversion. if ((data_.f.flags & kIntFlag) != 0) return data_.n.i.i; // int -> double if ((data_.f.flags & kUintFlag) != 0) return data_.n.u.u; // unsigned -> double if ((data_.f.flags & kInt64Flag) != 0) return static_cast(data_.n.i64); // int64_t -> double (may lose precision) RAPIDJSON_ASSERT((data_.f.flags & kUint64Flag) != 0); return static_cast(data_.n.u64); // uint64_t -> double (may lose precision) } //! Get the value as float type. /*! \note If the value is 64-bit integer type, it may lose precision. Use \c IsLosslessFloat() to check whether the converison is lossless. */ float GetFloat() const { return static_cast(GetDouble()); } GenericValue& SetInt(int i) { this->~GenericValue(); new (this) GenericValue(i); return *this; } GenericValue& SetUint(unsigned u) { this->~GenericValue(); new (this) GenericValue(u); return *this; } GenericValue& SetInt64(int64_t i64) { this->~GenericValue(); new (this) GenericValue(i64); return *this; } GenericValue& SetUint64(uint64_t u64) { this->~GenericValue(); new (this) GenericValue(u64); return *this; } GenericValue& SetDouble(double d) { this->~GenericValue(); new (this) GenericValue(d); return *this; } GenericValue& SetFloat(float f) { this->~GenericValue(); new (this) GenericValue(static_cast(f)); return *this; } //@} //!@name String //@{ const Ch* GetString() const { RAPIDJSON_ASSERT(IsString()); return (data_.f.flags & kInlineStrFlag) ? data_.ss.str : GetStringPointer(); } //! Get the length of string. /*! Since rapidjson permits "\\u0000" in the json string, strlen(v.GetString()) may not equal to v.GetStringLength(). */ SizeType GetStringLength() const { RAPIDJSON_ASSERT(IsString()); return ((data_.f.flags & kInlineStrFlag) ? (data_.ss.GetLength()) : data_.s.length); } //! Set this value as a string without copying source string. /*! This version has better performance with supplied length, and also support string containing null character. \param s source string pointer. \param length The length of source string, excluding the trailing null terminator. \return The value itself for fluent API. \post IsString() == true && GetString() == s && GetStringLength() == length \see SetString(StringRefType) */ GenericValue& SetString(const Ch* s, SizeType length) { return SetString(StringRef(s, length)); } //! Set this value as a string without copying source string. /*! \param s source string reference \return The value itself for fluent API. \post IsString() == true && GetString() == s && GetStringLength() == s.length */ GenericValue& SetString(StringRefType s) { this->~GenericValue(); SetStringRaw(s); return *this; } //! Set this value as a string by copying from source string. /*! This version has better performance with supplied length, and also support string containing null character. \param s source string. \param length The length of source string, excluding the trailing null terminator. \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator(). \return The value itself for fluent API. \post IsString() == true && GetString() != s && strcmp(GetString(),s) == 0 && GetStringLength() == length */ GenericValue& SetString(const Ch* s, SizeType length, Allocator& allocator) { return SetString(StringRef(s, length), allocator); } //! Set this value as a string by copying from source string. /*! \param s source string. \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator(). \return The value itself for fluent API. \post IsString() == true && GetString() != s && strcmp(GetString(),s) == 0 && GetStringLength() == length */ GenericValue& SetString(const Ch* s, Allocator& allocator) { return SetString(StringRef(s), allocator); } //! Set this value as a string by copying from source string. /*! \param s source string reference \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator(). \return The value itself for fluent API. \post IsString() == true && GetString() != s.s && strcmp(GetString(),s) == 0 && GetStringLength() == length */ GenericValue& SetString(StringRefType s, Allocator& allocator) { this->~GenericValue(); SetStringRaw(s, allocator); return *this; } #if RAPIDJSON_HAS_STDSTRING //! Set this value as a string by copying from source string. /*! \param s source string. \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator(). \return The value itself for fluent API. \post IsString() == true && GetString() != s.data() && strcmp(GetString(),s.data() == 0 && GetStringLength() == s.size() \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. */ GenericValue& SetString(const std::basic_string& s, Allocator& allocator) { return SetString(StringRef(s), allocator); } #endif //@} //!@name Array //@{ //! Templated version for checking whether this value is type T. /*! \tparam T Either \c bool, \c int, \c unsigned, \c int64_t, \c uint64_t, \c double, \c float, \c const \c char*, \c std::basic_string */ template bool Is() const { return internal::TypeHelper::Is(*this); } template T Get() const { return internal::TypeHelper::Get(*this); } template T Get() { return internal::TypeHelper::Get(*this); } template ValueType& Set(const T& data) { return internal::TypeHelper::Set(*this, data); } template ValueType& Set(const T& data, AllocatorType& allocator) { return internal::TypeHelper::Set(*this, data, allocator); } //@} //! Generate events of this value to a Handler. /*! This function adopts the GoF visitor pattern. Typical usage is to output this JSON value as JSON text via Writer, which is a Handler. It can also be used to deep clone this value via GenericDocument, which is also a Handler. \tparam Handler type of handler. \param handler An object implementing concept Handler. */ template bool Accept(Handler& handler) const { switch(GetType()) { case kNullType: return handler.Null(); case kFalseType: return handler.Bool(false); case kTrueType: return handler.Bool(true); case kObjectType: if (RAPIDJSON_UNLIKELY(!handler.StartObject())) return false; for (ConstMemberIterator m = MemberBegin(); m != MemberEnd(); ++m) { RAPIDJSON_ASSERT(m->name.IsString()); // User may change the type of name by MemberIterator. if (RAPIDJSON_UNLIKELY(!handler.Key(m->name.GetString(), m->name.GetStringLength(), (m->name.data_.f.flags & kCopyFlag) != 0))) return false; if (RAPIDJSON_UNLIKELY(!m->value.Accept(handler))) return false; } return handler.EndObject(data_.o.size); case kArrayType: if (RAPIDJSON_UNLIKELY(!handler.StartArray())) return false; for (const GenericValue* v = Begin(); v != End(); ++v) if (RAPIDJSON_UNLIKELY(!v->Accept(handler))) return false; return handler.EndArray(data_.a.size); case kStringType: return handler.String(GetString(), GetStringLength(), (data_.f.flags & kCopyFlag) != 0); default: RAPIDJSON_ASSERT(GetType() == kNumberType); if (IsDouble()) return handler.Double(data_.n.d); else if (IsInt()) return handler.Int(data_.n.i.i); else if (IsUint()) return handler.Uint(data_.n.u.u); else if (IsInt64()) return handler.Int64(data_.n.i64); else return handler.Uint64(data_.n.u64); } } private: template friend class GenericValue; template friend class GenericDocument; enum { kBoolFlag = 0x0008, kNumberFlag = 0x0010, kIntFlag = 0x0020, kUintFlag = 0x0040, kInt64Flag = 0x0080, kUint64Flag = 0x0100, kDoubleFlag = 0x0200, kStringFlag = 0x0400, kCopyFlag = 0x0800, kInlineStrFlag = 0x1000, // Initial flags of different types. kNullFlag = kNullType, kTrueFlag = kTrueType | kBoolFlag, kFalseFlag = kFalseType | kBoolFlag, kNumberIntFlag = kNumberType | kNumberFlag | kIntFlag | kInt64Flag, kNumberUintFlag = kNumberType | kNumberFlag | kUintFlag | kUint64Flag | kInt64Flag, kNumberInt64Flag = kNumberType | kNumberFlag | kInt64Flag, kNumberUint64Flag = kNumberType | kNumberFlag | kUint64Flag, kNumberDoubleFlag = kNumberType | kNumberFlag | kDoubleFlag, kNumberAnyFlag = kNumberType | kNumberFlag | kIntFlag | kInt64Flag | kUintFlag | kUint64Flag | kDoubleFlag, kConstStringFlag = kStringType | kStringFlag, kCopyStringFlag = kStringType | kStringFlag | kCopyFlag, kShortStringFlag = kStringType | kStringFlag | kCopyFlag | kInlineStrFlag, kObjectFlag = kObjectType, kArrayFlag = kArrayType, kTypeMask = 0x07 }; static const SizeType kDefaultArrayCapacity = 16; static const SizeType kDefaultObjectCapacity = 16; struct Flag { #if RAPIDJSON_48BITPOINTER_OPTIMIZATION char payload[sizeof(SizeType) * 2 + 6]; // 2 x SizeType + lower 48-bit pointer #elif RAPIDJSON_64BIT char payload[sizeof(SizeType) * 2 + sizeof(void*) + 6]; // 6 padding bytes #else char payload[sizeof(SizeType) * 2 + sizeof(void*) + 2]; // 2 padding bytes #endif uint16_t flags; }; struct String { SizeType length; SizeType hashcode; //!< reserved const Ch* str; }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode // implementation detail: ShortString can represent zero-terminated strings up to MaxSize chars // (excluding the terminating zero) and store a value to determine the length of the contained // string in the last character str[LenPos] by storing "MaxSize - length" there. If the string // to store has the maximal length of MaxSize then str[LenPos] will be 0 and therefore act as // the string terminator as well. For getting the string length back from that value just use // "MaxSize - str[LenPos]". // This allows to store 13-chars strings in 32-bit mode, 21-chars strings in 64-bit mode, // 13-chars strings for RAPIDJSON_48BITPOINTER_OPTIMIZATION=1 inline (for `UTF8`-encoded strings). struct ShortString { enum { MaxChars = sizeof(static_cast(0)->payload) / sizeof(Ch), MaxSize = MaxChars - 1, LenPos = MaxSize }; Ch str[MaxChars]; inline static bool Usable(SizeType len) { return (MaxSize >= len); } inline void SetLength(SizeType len) { str[LenPos] = static_cast(MaxSize - len); } inline SizeType GetLength() const { return static_cast(MaxSize - str[LenPos]); } }; // at most as many bytes as "String" above => 12 bytes in 32-bit mode, 16 bytes in 64-bit mode // By using proper binary layout, retrieval of different integer types do not need conversions. union Number { #if RAPIDJSON_ENDIAN == RAPIDJSON_LITTLEENDIAN struct I { int i; char padding[4]; }i; struct U { unsigned u; char padding2[4]; }u; #else struct I { char padding[4]; int i; }i; struct U { char padding2[4]; unsigned u; }u; #endif int64_t i64; uint64_t u64; double d; }; // 8 bytes struct ObjectData { SizeType size; SizeType capacity; Member* members; }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode struct ArrayData { SizeType size; SizeType capacity; GenericValue* elements; }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode union Data { String s; ShortString ss; Number n; ObjectData o; ArrayData a; Flag f; }; // 16 bytes in 32-bit mode, 24 bytes in 64-bit mode, 16 bytes in 64-bit with RAPIDJSON_48BITPOINTER_OPTIMIZATION RAPIDJSON_FORCEINLINE const Ch* GetStringPointer() const { return RAPIDJSON_GETPOINTER(Ch, data_.s.str); } RAPIDJSON_FORCEINLINE const Ch* SetStringPointer(const Ch* str) { return RAPIDJSON_SETPOINTER(Ch, data_.s.str, str); } RAPIDJSON_FORCEINLINE GenericValue* GetElementsPointer() const { return RAPIDJSON_GETPOINTER(GenericValue, data_.a.elements); } RAPIDJSON_FORCEINLINE GenericValue* SetElementsPointer(GenericValue* elements) { return RAPIDJSON_SETPOINTER(GenericValue, data_.a.elements, elements); } RAPIDJSON_FORCEINLINE Member* GetMembersPointer() const { return RAPIDJSON_GETPOINTER(Member, data_.o.members); } RAPIDJSON_FORCEINLINE Member* SetMembersPointer(Member* members) { return RAPIDJSON_SETPOINTER(Member, data_.o.members, members); } // Initialize this value as array with initial data, without calling destructor. void SetArrayRaw(GenericValue* values, SizeType count, Allocator& allocator) { data_.f.flags = kArrayFlag; if (count) { GenericValue* e = static_cast(allocator.Malloc(count * sizeof(GenericValue))); SetElementsPointer(e); std::memcpy(static_cast(e), values, count * sizeof(GenericValue)); } else SetElementsPointer(0); data_.a.size = data_.a.capacity = count; } //! Initialize this value as object with initial data, without calling destructor. void SetObjectRaw(Member* members, SizeType count, Allocator& allocator) { data_.f.flags = kObjectFlag; if (count) { Member* m = static_cast(allocator.Malloc(count * sizeof(Member))); SetMembersPointer(m); std::memcpy(static_cast(m), members, count * sizeof(Member)); } else SetMembersPointer(0); data_.o.size = data_.o.capacity = count; } //! Initialize this value as constant string, without calling destructor. void SetStringRaw(StringRefType s) RAPIDJSON_NOEXCEPT { data_.f.flags = kConstStringFlag; SetStringPointer(s); data_.s.length = s.length; } //! Initialize this value as copy string with initial data, without calling destructor. void SetStringRaw(StringRefType s, Allocator& allocator) { Ch* str = 0; if (ShortString::Usable(s.length)) { data_.f.flags = kShortStringFlag; data_.ss.SetLength(s.length); str = data_.ss.str; } else { data_.f.flags = kCopyStringFlag; data_.s.length = s.length; str = static_cast(allocator.Malloc((s.length + 1) * sizeof(Ch))); SetStringPointer(str); } std::memcpy(str, s, s.length * sizeof(Ch)); str[s.length] = '\0'; } //! Assignment without calling destructor void RawAssign(GenericValue& rhs) RAPIDJSON_NOEXCEPT { data_ = rhs.data_; // data_.f.flags = rhs.data_.f.flags; rhs.data_.f.flags = kNullFlag; } template bool StringEqual(const GenericValue& rhs) const { RAPIDJSON_ASSERT(IsString()); RAPIDJSON_ASSERT(rhs.IsString()); const SizeType len1 = GetStringLength(); const SizeType len2 = rhs.GetStringLength(); if(len1 != len2) { return false; } const Ch* const str1 = GetString(); const Ch* const str2 = rhs.GetString(); if(str1 == str2) { return true; } // fast path for constant string return (std::memcmp(str1, str2, sizeof(Ch) * len1) == 0); } Data data_; }; //! GenericValue with UTF8 encoding typedef GenericValue > Value; /////////////////////////////////////////////////////////////////////////////// // GenericDocument //! A document for parsing JSON text as DOM. /*! \note implements Handler concept \tparam Encoding Encoding for both parsing and string storage. \tparam Allocator Allocator for allocating memory for the DOM \tparam StackAllocator Allocator for allocating memory for stack during parsing. \warning Although GenericDocument inherits from GenericValue, the API does \b not provide any virtual functions, especially no virtual destructor. To avoid memory leaks, do not \c delete a GenericDocument object via a pointer to a GenericValue. */ template , typename StackAllocator = CrtAllocator> class GenericDocument : public GenericValue { public: typedef typename Encoding::Ch Ch; //!< Character type derived from Encoding. typedef GenericValue ValueType; //!< Value type of the document. typedef Allocator AllocatorType; //!< Allocator type from template parameter. //! Constructor /*! Creates an empty document of specified type. \param type Mandatory type of object to create. \param allocator Optional allocator for allocating memory. \param stackCapacity Optional initial capacity of stack in bytes. \param stackAllocator Optional allocator for allocating memory for stack. */ explicit GenericDocument(Type type, Allocator* allocator = 0, size_t stackCapacity = kDefaultStackCapacity, StackAllocator* stackAllocator = 0) : GenericValue(type), allocator_(allocator), ownAllocator_(0), stack_(stackAllocator, stackCapacity), parseResult_() { if (!allocator_) ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); } //! Constructor /*! Creates an empty document which type is Null. \param allocator Optional allocator for allocating memory. \param stackCapacity Optional initial capacity of stack in bytes. \param stackAllocator Optional allocator for allocating memory for stack. */ GenericDocument(Allocator* allocator = 0, size_t stackCapacity = kDefaultStackCapacity, StackAllocator* stackAllocator = 0) : allocator_(allocator), ownAllocator_(0), stack_(stackAllocator, stackCapacity), parseResult_() { if (!allocator_) ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); } #if RAPIDJSON_HAS_CXX11_RVALUE_REFS //! Move constructor in C++11 GenericDocument(GenericDocument&& rhs) RAPIDJSON_NOEXCEPT : ValueType(std::forward(rhs)), // explicit cast to avoid prohibited move from Document allocator_(rhs.allocator_), ownAllocator_(rhs.ownAllocator_), stack_(std::move(rhs.stack_)), parseResult_(rhs.parseResult_) { rhs.allocator_ = 0; rhs.ownAllocator_ = 0; rhs.parseResult_ = ParseResult(); } #endif ~GenericDocument() { Destroy(); } #if RAPIDJSON_HAS_CXX11_RVALUE_REFS //! Move assignment in C++11 GenericDocument& operator=(GenericDocument&& rhs) RAPIDJSON_NOEXCEPT { // The cast to ValueType is necessary here, because otherwise it would // attempt to call GenericValue's templated assignment operator. ValueType::operator=(std::forward(rhs)); // Calling the destructor here would prematurely call stack_'s destructor Destroy(); allocator_ = rhs.allocator_; ownAllocator_ = rhs.ownAllocator_; stack_ = std::move(rhs.stack_); parseResult_ = rhs.parseResult_; rhs.allocator_ = 0; rhs.ownAllocator_ = 0; rhs.parseResult_ = ParseResult(); return *this; } #endif //! Exchange the contents of this document with those of another. /*! \param rhs Another document. \note Constant complexity. \see GenericValue::Swap */ GenericDocument& Swap(GenericDocument& rhs) RAPIDJSON_NOEXCEPT { ValueType::Swap(rhs); stack_.Swap(rhs.stack_); internal::Swap(allocator_, rhs.allocator_); internal::Swap(ownAllocator_, rhs.ownAllocator_); internal::Swap(parseResult_, rhs.parseResult_); return *this; } // Allow Swap with ValueType. // Refer to Effective C++ 3rd Edition/Item 33: Avoid hiding inherited names. using ValueType::Swap; //! free-standing swap function helper /*! Helper function to enable support for common swap implementation pattern based on \c std::swap: \code void swap(MyClass& a, MyClass& b) { using std::swap; swap(a.doc, b.doc); // ... } \endcode \see Swap() */ friend inline void swap(GenericDocument& a, GenericDocument& b) RAPIDJSON_NOEXCEPT { a.Swap(b); } //! Populate this document by a generator which produces SAX events. /*! \tparam Generator A functor with bool f(Handler) prototype. \param g Generator functor which sends SAX events to the parameter. \return The document itself for fluent API. */ template GenericDocument& Populate(Generator& g) { ClearStackOnExit scope(*this); if (g(*this)) { RAPIDJSON_ASSERT(stack_.GetSize() == sizeof(ValueType)); // Got one and only one root object ValueType::operator=(*stack_.template Pop(1));// Move value from stack to document } return *this; } //!@name Parse from stream //!@{ //! Parse JSON text from an input stream (with Encoding conversion) /*! \tparam parseFlags Combination of \ref ParseFlag. \tparam SourceEncoding Encoding of input stream \tparam InputStream Type of input stream, implementing Stream concept \param is Input stream to be parsed. \return The document itself for fluent API. */ template GenericDocument& ParseStream(InputStream& is) { GenericReader reader( stack_.HasAllocator() ? &stack_.GetAllocator() : 0); ClearStackOnExit scope(*this); parseResult_ = reader.template Parse(is, *this); if (parseResult_) { RAPIDJSON_ASSERT(stack_.GetSize() == sizeof(ValueType)); // Got one and only one root object ValueType::operator=(*stack_.template Pop(1));// Move value from stack to document } return *this; } //! Parse JSON text from an input stream /*! \tparam parseFlags Combination of \ref ParseFlag. \tparam InputStream Type of input stream, implementing Stream concept \param is Input stream to be parsed. \return The document itself for fluent API. */ template GenericDocument& ParseStream(InputStream& is) { return ParseStream(is); } //! Parse JSON text from an input stream (with \ref kParseDefaultFlags) /*! \tparam InputStream Type of input stream, implementing Stream concept \param is Input stream to be parsed. \return The document itself for fluent API. */ template GenericDocument& ParseStream(InputStream& is) { return ParseStream(is); } //!@} //!@name Parse in-place from mutable string //!@{ //! Parse JSON text from a mutable string /*! \tparam parseFlags Combination of \ref ParseFlag. \param str Mutable zero-terminated string to be parsed. \return The document itself for fluent API. */ template GenericDocument& ParseInsitu(Ch* str) { GenericInsituStringStream s(str); return ParseStream(s); } //! Parse JSON text from a mutable string (with \ref kParseDefaultFlags) /*! \param str Mutable zero-terminated string to be parsed. \return The document itself for fluent API. */ GenericDocument& ParseInsitu(Ch* str) { return ParseInsitu(str); } //!@} //!@name Parse from read-only string //!@{ //! Parse JSON text from a read-only string (with Encoding conversion) /*! \tparam parseFlags Combination of \ref ParseFlag (must not contain \ref kParseInsituFlag). \tparam SourceEncoding Transcoding from input Encoding \param str Read-only zero-terminated string to be parsed. */ template GenericDocument& Parse(const typename SourceEncoding::Ch* str) { RAPIDJSON_ASSERT(!(parseFlags & kParseInsituFlag)); GenericStringStream s(str); return ParseStream(s); } //! Parse JSON text from a read-only string /*! \tparam parseFlags Combination of \ref ParseFlag (must not contain \ref kParseInsituFlag). \param str Read-only zero-terminated string to be parsed. */ template GenericDocument& Parse(const Ch* str) { return Parse(str); } //! Parse JSON text from a read-only string (with \ref kParseDefaultFlags) /*! \param str Read-only zero-terminated string to be parsed. */ GenericDocument& Parse(const Ch* str) { return Parse(str); } template GenericDocument& Parse(const typename SourceEncoding::Ch* str, size_t length) { RAPIDJSON_ASSERT(!(parseFlags & kParseInsituFlag)); MemoryStream ms(reinterpret_cast(str), length * sizeof(typename SourceEncoding::Ch)); EncodedInputStream is(ms); ParseStream(is); return *this; } template GenericDocument& Parse(const Ch* str, size_t length) { return Parse(str, length); } GenericDocument& Parse(const Ch* str, size_t length) { return Parse(str, length); } #if RAPIDJSON_HAS_STDSTRING template GenericDocument& Parse(const std::basic_string& str) { // c_str() is constant complexity according to standard. Should be faster than Parse(const char*, size_t) return Parse(str.c_str()); } template GenericDocument& Parse(const std::basic_string& str) { return Parse(str.c_str()); } GenericDocument& Parse(const std::basic_string& str) { return Parse(str); } #endif // RAPIDJSON_HAS_STDSTRING //!@} //!@name Handling parse errors //!@{ //! Whether a parse error has occurred in the last parsing. bool HasParseError() const { return parseResult_.IsError(); } //! Get the \ref ParseErrorCode of last parsing. ParseErrorCode GetParseError() const { return parseResult_.Code(); } //! Get the position of last parsing error in input, 0 otherwise. size_t GetErrorOffset() const { return parseResult_.Offset(); } //! Implicit conversion to get the last parse result #ifndef __clang // -Wdocumentation /*! \return \ref ParseResult of the last parse operation \code Document doc; ParseResult ok = doc.Parse(json); if (!ok) printf( "JSON parse error: %s (%u)\n", GetParseError_En(ok.Code()), ok.Offset()); \endcode */ #endif operator ParseResult() const { return parseResult_; } //!@} //! Get the allocator of this document. Allocator& GetAllocator() { RAPIDJSON_ASSERT(allocator_); return *allocator_; } //! Get the capacity of stack in bytes. size_t GetStackCapacity() const { return stack_.GetCapacity(); } private: // clear stack on any exit from ParseStream, e.g. due to exception struct ClearStackOnExit { explicit ClearStackOnExit(GenericDocument& d) : d_(d) {} ~ClearStackOnExit() { d_.ClearStack(); } private: ClearStackOnExit(const ClearStackOnExit&); ClearStackOnExit& operator=(const ClearStackOnExit&); GenericDocument& d_; }; // callers of the following private Handler functions // template friend class GenericReader; // for parsing template friend class GenericValue; // for deep copying public: // Implementation of Handler bool Null() { new (stack_.template Push()) ValueType(); return true; } bool Bool(bool b) { new (stack_.template Push()) ValueType(b); return true; } bool Int(int i) { new (stack_.template Push()) ValueType(i); return true; } bool Uint(unsigned i) { new (stack_.template Push()) ValueType(i); return true; } bool Int64(int64_t i) { new (stack_.template Push()) ValueType(i); return true; } bool Uint64(uint64_t i) { new (stack_.template Push()) ValueType(i); return true; } bool Double(double d) { new (stack_.template Push()) ValueType(d); return true; } bool RawNumber(const Ch* str, SizeType length, bool copy) { if (copy) new (stack_.template Push()) ValueType(str, length, GetAllocator()); else new (stack_.template Push()) ValueType(str, length); return true; } bool String(const Ch* str, SizeType length, bool copy) { if (copy) new (stack_.template Push()) ValueType(str, length, GetAllocator()); else new (stack_.template Push()) ValueType(str, length); return true; } bool StartObject() { new (stack_.template Push()) ValueType(kObjectType); return true; } bool Key(const Ch* str, SizeType length, bool copy) { return String(str, length, copy); } bool EndObject(SizeType memberCount) { typename ValueType::Member* members = stack_.template Pop(memberCount); stack_.template Top()->SetObjectRaw(members, memberCount, GetAllocator()); return true; } bool StartArray() { new (stack_.template Push()) ValueType(kArrayType); return true; } bool EndArray(SizeType elementCount) { ValueType* elements = stack_.template Pop(elementCount); stack_.template Top()->SetArrayRaw(elements, elementCount, GetAllocator()); return true; } private: //! Prohibit copying GenericDocument(const GenericDocument&); //! Prohibit assignment GenericDocument& operator=(const GenericDocument&); void ClearStack() { if (Allocator::kNeedFree) while (stack_.GetSize() > 0) // Here assumes all elements in stack array are GenericValue (Member is actually 2 GenericValue objects) (stack_.template Pop(1))->~ValueType(); else stack_.Clear(); stack_.ShrinkToFit(); } void Destroy() { RAPIDJSON_DELETE(ownAllocator_); } static const size_t kDefaultStackCapacity = 1024; Allocator* allocator_; Allocator* ownAllocator_; internal::Stack stack_; ParseResult parseResult_; }; //! GenericDocument with UTF8 encoding typedef GenericDocument > Document; //! Helper class for accessing Value of array type. /*! Instance of this helper class is obtained by \c GenericValue::GetArray(). In addition to all APIs for array type, it provides range-based for loop if \c RAPIDJSON_HAS_CXX11_RANGE_FOR=1. */ template class GenericArray { public: typedef GenericArray ConstArray; typedef GenericArray Array; typedef ValueT PlainType; typedef typename internal::MaybeAddConst::Type ValueType; typedef ValueType* ValueIterator; // This may be const or non-const iterator typedef const ValueT* ConstValueIterator; typedef typename ValueType::AllocatorType AllocatorType; typedef typename ValueType::StringRefType StringRefType; template friend class GenericValue; GenericArray(const GenericArray& rhs) : value_(rhs.value_) {} GenericArray& operator=(const GenericArray& rhs) { value_ = rhs.value_; return *this; } ~GenericArray() {} SizeType Size() const { return value_.Size(); } SizeType Capacity() const { return value_.Capacity(); } bool Empty() const { return value_.Empty(); } void Clear() const { value_.Clear(); } ValueType& operator[](SizeType index) const { return value_[index]; } ValueIterator Begin() const { return value_.Begin(); } ValueIterator End() const { return value_.End(); } GenericArray Reserve(SizeType newCapacity, AllocatorType &allocator) const { value_.Reserve(newCapacity, allocator); return *this; } GenericArray PushBack(ValueType& value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; } #if RAPIDJSON_HAS_CXX11_RVALUE_REFS GenericArray PushBack(ValueType&& value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; } #endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS GenericArray PushBack(StringRefType value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; } template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (const GenericArray&)) PushBack(T value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; } GenericArray PopBack() const { value_.PopBack(); return *this; } ValueIterator Erase(ConstValueIterator pos) const { return value_.Erase(pos); } ValueIterator Erase(ConstValueIterator first, ConstValueIterator last) const { return value_.Erase(first, last); } #if RAPIDJSON_HAS_CXX11_RANGE_FOR ValueIterator begin() const { return value_.Begin(); } ValueIterator end() const { return value_.End(); } #endif private: GenericArray(); GenericArray(ValueType& value) : value_(value) {} ValueType& value_; }; //! Helper class for accessing Value of object type. /*! Instance of this helper class is obtained by \c GenericValue::GetObject(). In addition to all APIs for array type, it provides range-based for loop if \c RAPIDJSON_HAS_CXX11_RANGE_FOR=1. */ template class GenericObject { public: typedef GenericObject ConstObject; typedef GenericObject Object; typedef ValueT PlainType; typedef typename internal::MaybeAddConst::Type ValueType; typedef GenericMemberIterator MemberIterator; // This may be const or non-const iterator typedef GenericMemberIterator ConstMemberIterator; typedef typename ValueType::AllocatorType AllocatorType; typedef typename ValueType::StringRefType StringRefType; typedef typename ValueType::EncodingType EncodingType; typedef typename ValueType::Ch Ch; template friend class GenericValue; GenericObject(const GenericObject& rhs) : value_(rhs.value_) {} GenericObject& operator=(const GenericObject& rhs) { value_ = rhs.value_; return *this; } ~GenericObject() {} SizeType MemberCount() const { return value_.MemberCount(); } SizeType MemberCapacity() const { return value_.MemberCapacity(); } bool ObjectEmpty() const { return value_.ObjectEmpty(); } template ValueType& operator[](T* name) const { return value_[name]; } template ValueType& operator[](const GenericValue& name) const { return value_[name]; } #if RAPIDJSON_HAS_STDSTRING ValueType& operator[](const std::basic_string& name) const { return value_[name]; } #endif MemberIterator MemberBegin() const { return value_.MemberBegin(); } MemberIterator MemberEnd() const { return value_.MemberEnd(); } GenericObject MemberReserve(SizeType newCapacity, AllocatorType &allocator) const { value_.MemberReserve(newCapacity, allocator); return *this; } bool HasMember(const Ch* name) const { return value_.HasMember(name); } #if RAPIDJSON_HAS_STDSTRING bool HasMember(const std::basic_string& name) const { return value_.HasMember(name); } #endif template bool HasMember(const GenericValue& name) const { return value_.HasMember(name); } MemberIterator FindMember(const Ch* name) const { return value_.FindMember(name); } template MemberIterator FindMember(const GenericValue& name) const { return value_.FindMember(name); } #if RAPIDJSON_HAS_STDSTRING MemberIterator FindMember(const std::basic_string& name) const { return value_.FindMember(name); } #endif GenericObject AddMember(ValueType& name, ValueType& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } GenericObject AddMember(ValueType& name, StringRefType value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } #if RAPIDJSON_HAS_STDSTRING GenericObject AddMember(ValueType& name, std::basic_string& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } #endif template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) AddMember(ValueType& name, T value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } #if RAPIDJSON_HAS_CXX11_RVALUE_REFS GenericObject AddMember(ValueType&& name, ValueType&& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } GenericObject AddMember(ValueType&& name, ValueType& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } GenericObject AddMember(ValueType& name, ValueType&& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } GenericObject AddMember(StringRefType name, ValueType&& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } #endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS GenericObject AddMember(StringRefType name, ValueType& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } GenericObject AddMember(StringRefType name, StringRefType value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (GenericObject)) AddMember(StringRefType name, T value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } void RemoveAllMembers() { value_.RemoveAllMembers(); } bool RemoveMember(const Ch* name) const { return value_.RemoveMember(name); } #if RAPIDJSON_HAS_STDSTRING bool RemoveMember(const std::basic_string& name) const { return value_.RemoveMember(name); } #endif template bool RemoveMember(const GenericValue& name) const { return value_.RemoveMember(name); } MemberIterator RemoveMember(MemberIterator m) const { return value_.RemoveMember(m); } MemberIterator EraseMember(ConstMemberIterator pos) const { return value_.EraseMember(pos); } MemberIterator EraseMember(ConstMemberIterator first, ConstMemberIterator last) const { return value_.EraseMember(first, last); } bool EraseMember(const Ch* name) const { return value_.EraseMember(name); } #if RAPIDJSON_HAS_STDSTRING bool EraseMember(const std::basic_string& name) const { return EraseMember(ValueType(StringRef(name))); } #endif template bool EraseMember(const GenericValue& name) const { return value_.EraseMember(name); } #if RAPIDJSON_HAS_CXX11_RANGE_FOR MemberIterator begin() const { return value_.MemberBegin(); } MemberIterator end() const { return value_.MemberEnd(); } #endif private: GenericObject(); GenericObject(ValueType& value) : value_(value) {} ValueType& value_; }; RAPIDJSON_NAMESPACE_END RAPIDJSON_DIAG_POP #endif // RAPIDJSON_DOCUMENT_H_ ================================================ FILE: src/json/rapidjson/encodedstream.h ================================================ // Tencent is pleased to support the open source community by making RapidJSON available. // // Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at // // http://opensource.org/licenses/MIT // // Unless required by applicable law or agreed to in writing, software distributed // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. #ifndef RAPIDJSON_ENCODEDSTREAM_H_ #define RAPIDJSON_ENCODEDSTREAM_H_ #include "stream.h" #include "memorystream.h" #ifdef __GNUC__ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(effc++) #endif #ifdef __clang__ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(padded) #endif RAPIDJSON_NAMESPACE_BEGIN //! Input byte stream wrapper with a statically bound encoding. /*! \tparam Encoding The interpretation of encoding of the stream. Either UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE. \tparam InputByteStream Type of input byte stream. For example, FileReadStream. */ template class EncodedInputStream { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); public: typedef typename Encoding::Ch Ch; EncodedInputStream(InputByteStream& is) : is_(is) { current_ = Encoding::TakeBOM(is_); } Ch Peek() const { return current_; } Ch Take() { Ch c = current_; current_ = Encoding::Take(is_); return c; } size_t Tell() const { return is_.Tell(); } // Not implemented void Put(Ch) { RAPIDJSON_ASSERT(false); } void Flush() { RAPIDJSON_ASSERT(false); } Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } private: EncodedInputStream(const EncodedInputStream&); EncodedInputStream& operator=(const EncodedInputStream&); InputByteStream& is_; Ch current_; }; //! Specialized for UTF8 MemoryStream. template <> class EncodedInputStream, MemoryStream> { public: typedef UTF8<>::Ch Ch; EncodedInputStream(MemoryStream& is) : is_(is) { if (static_cast(is_.Peek()) == 0xEFu) is_.Take(); if (static_cast(is_.Peek()) == 0xBBu) is_.Take(); if (static_cast(is_.Peek()) == 0xBFu) is_.Take(); } Ch Peek() const { return is_.Peek(); } Ch Take() { return is_.Take(); } size_t Tell() const { return is_.Tell(); } // Not implemented void Put(Ch) {} void Flush() {} Ch* PutBegin() { return 0; } size_t PutEnd(Ch*) { return 0; } MemoryStream& is_; private: EncodedInputStream(const EncodedInputStream&); EncodedInputStream& operator=(const EncodedInputStream&); }; //! Output byte stream wrapper with statically bound encoding. /*! \tparam Encoding The interpretation of encoding of the stream. Either UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE. \tparam OutputByteStream Type of input byte stream. For example, FileWriteStream. */ template class EncodedOutputStream { RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); public: typedef typename Encoding::Ch Ch; EncodedOutputStream(OutputByteStream& os, bool putBOM = true) : os_(os) { if (putBOM) Encoding::PutBOM(os_); } void Put(Ch c) { Encoding::Put(os_, c); } void Flush() { os_.Flush(); } // Not implemented Ch Peek() const { RAPIDJSON_ASSERT(false); return 0;} Ch Take() { RAPIDJSON_ASSERT(false); return 0;} size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } private: EncodedOutputStream(const EncodedOutputStream&); EncodedOutputStream& operator=(const EncodedOutputStream&); OutputByteStream& os_; }; #define RAPIDJSON_ENCODINGS_FUNC(x) UTF8::x, UTF16LE::x, UTF16BE::x, UTF32LE::x, UTF32BE::x //! Input stream wrapper with dynamically bound encoding and automatic encoding detection. /*! \tparam CharType Type of character for reading. \tparam InputByteStream type of input byte stream to be wrapped. */ template class AutoUTFInputStream { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); public: typedef CharType Ch; //! Constructor. /*! \param is input stream to be wrapped. \param type UTF encoding type if it is not detected from the stream. */ AutoUTFInputStream(InputByteStream& is, UTFType type = kUTF8) : is_(&is), type_(type), hasBOM_(false) { RAPIDJSON_ASSERT(type >= kUTF8 && type <= kUTF32BE); DetectType(); static const TakeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Take) }; takeFunc_ = f[type_]; current_ = takeFunc_(*is_); } UTFType GetType() const { return type_; } bool HasBOM() const { return hasBOM_; } Ch Peek() const { return current_; } Ch Take() { Ch c = current_; current_ = takeFunc_(*is_); return c; } size_t Tell() const { return is_->Tell(); } // Not implemented void Put(Ch) { RAPIDJSON_ASSERT(false); } void Flush() { RAPIDJSON_ASSERT(false); } Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } private: AutoUTFInputStream(const AutoUTFInputStream&); AutoUTFInputStream& operator=(const AutoUTFInputStream&); // Detect encoding type with BOM or RFC 4627 void DetectType() { // BOM (Byte Order Mark): // 00 00 FE FF UTF-32BE // FF FE 00 00 UTF-32LE // FE FF UTF-16BE // FF FE UTF-16LE // EF BB BF UTF-8 const unsigned char* c = reinterpret_cast(is_->Peek4()); if (!c) return; unsigned bom = static_cast(c[0] | (c[1] << 8) | (c[2] << 16) | (c[3] << 24)); hasBOM_ = false; if (bom == 0xFFFE0000) { type_ = kUTF32BE; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); is_->Take(); } else if (bom == 0x0000FEFF) { type_ = kUTF32LE; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); is_->Take(); } else if ((bom & 0xFFFF) == 0xFFFE) { type_ = kUTF16BE; hasBOM_ = true; is_->Take(); is_->Take(); } else if ((bom & 0xFFFF) == 0xFEFF) { type_ = kUTF16LE; hasBOM_ = true; is_->Take(); is_->Take(); } else if ((bom & 0xFFFFFF) == 0xBFBBEF) { type_ = kUTF8; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); } // RFC 4627: Section 3 // "Since the first two characters of a JSON text will always be ASCII // characters [RFC0020], it is possible to determine whether an octet // stream is UTF-8, UTF-16 (BE or LE), or UTF-32 (BE or LE) by looking // at the pattern of nulls in the first four octets." // 00 00 00 xx UTF-32BE // 00 xx 00 xx UTF-16BE // xx 00 00 00 UTF-32LE // xx 00 xx 00 UTF-16LE // xx xx xx xx UTF-8 if (!hasBOM_) { int pattern = (c[0] ? 1 : 0) | (c[1] ? 2 : 0) | (c[2] ? 4 : 0) | (c[3] ? 8 : 0); switch (pattern) { case 0x08: type_ = kUTF32BE; break; case 0x0A: type_ = kUTF16BE; break; case 0x01: type_ = kUTF32LE; break; case 0x05: type_ = kUTF16LE; break; case 0x0F: type_ = kUTF8; break; default: break; // Use type defined by user. } } // Runtime check whether the size of character type is sufficient. It only perform checks with assertion. if (type_ == kUTF16LE || type_ == kUTF16BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 2); if (type_ == kUTF32LE || type_ == kUTF32BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 4); } typedef Ch (*TakeFunc)(InputByteStream& is); InputByteStream* is_; UTFType type_; Ch current_; TakeFunc takeFunc_; bool hasBOM_; }; //! Output stream wrapper with dynamically bound encoding and automatic encoding detection. /*! \tparam CharType Type of character for writing. \tparam OutputByteStream type of output byte stream to be wrapped. */ template class AutoUTFOutputStream { RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); public: typedef CharType Ch; //! Constructor. /*! \param os output stream to be wrapped. \param type UTF encoding type. \param putBOM Whether to write BOM at the beginning of the stream. */ AutoUTFOutputStream(OutputByteStream& os, UTFType type, bool putBOM) : os_(&os), type_(type) { RAPIDJSON_ASSERT(type >= kUTF8 && type <= kUTF32BE); // Runtime check whether the size of character type is sufficient. It only perform checks with assertion. if (type_ == kUTF16LE || type_ == kUTF16BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 2); if (type_ == kUTF32LE || type_ == kUTF32BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 4); static const PutFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Put) }; putFunc_ = f[type_]; if (putBOM) PutBOM(); } UTFType GetType() const { return type_; } void Put(Ch c) { putFunc_(*os_, c); } void Flush() { os_->Flush(); } // Not implemented Ch Peek() const { RAPIDJSON_ASSERT(false); return 0;} Ch Take() { RAPIDJSON_ASSERT(false); return 0;} size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } private: AutoUTFOutputStream(const AutoUTFOutputStream&); AutoUTFOutputStream& operator=(const AutoUTFOutputStream&); void PutBOM() { typedef void (*PutBOMFunc)(OutputByteStream&); static const PutBOMFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(PutBOM) }; f[type_](*os_); } typedef void (*PutFunc)(OutputByteStream&, Ch); OutputByteStream* os_; UTFType type_; PutFunc putFunc_; }; #undef RAPIDJSON_ENCODINGS_FUNC RAPIDJSON_NAMESPACE_END #ifdef __clang__ RAPIDJSON_DIAG_POP #endif #ifdef __GNUC__ RAPIDJSON_DIAG_POP #endif #endif // RAPIDJSON_FILESTREAM_H_ ================================================ FILE: src/json/rapidjson/encodings.h ================================================ // Tencent is pleased to support the open source community by making RapidJSON available. // // Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at // // http://opensource.org/licenses/MIT // // Unless required by applicable law or agreed to in writing, software distributed // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. #ifndef RAPIDJSON_ENCODINGS_H_ #define RAPIDJSON_ENCODINGS_H_ #include "rapidjson.h" #if defined(_MSC_VER) && !defined(__clang__) RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(4244) // conversion from 'type1' to 'type2', possible loss of data RAPIDJSON_DIAG_OFF(4702) // unreachable code #elif defined(__GNUC__) RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(effc++) RAPIDJSON_DIAG_OFF(overflow) #endif RAPIDJSON_NAMESPACE_BEGIN /////////////////////////////////////////////////////////////////////////////// // Encoding /*! \class rapidjson::Encoding \brief Concept for encoding of Unicode characters. \code concept Encoding { typename Ch; //! Type of character. A "character" is actually a code unit in unicode's definition. enum { supportUnicode = 1 }; // or 0 if not supporting unicode //! \brief Encode a Unicode codepoint to an output stream. //! \param os Output stream. //! \param codepoint An unicode codepoint, ranging from 0x0 to 0x10FFFF inclusively. template static void Encode(OutputStream& os, unsigned codepoint); //! \brief Decode a Unicode codepoint from an input stream. //! \param is Input stream. //! \param codepoint Output of the unicode codepoint. //! \return true if a valid codepoint can be decoded from the stream. template static bool Decode(InputStream& is, unsigned* codepoint); //! \brief Validate one Unicode codepoint from an encoded stream. //! \param is Input stream to obtain codepoint. //! \param os Output for copying one codepoint. //! \return true if it is valid. //! \note This function just validating and copying the codepoint without actually decode it. template static bool Validate(InputStream& is, OutputStream& os); // The following functions are deal with byte streams. //! Take a character from input byte stream, skip BOM if exist. template static CharType TakeBOM(InputByteStream& is); //! Take a character from input byte stream. template static Ch Take(InputByteStream& is); //! Put BOM to output byte stream. template static void PutBOM(OutputByteStream& os); //! Put a character to output byte stream. template static void Put(OutputByteStream& os, Ch c); }; \endcode */ /////////////////////////////////////////////////////////////////////////////// // UTF8 //! UTF-8 encoding. /*! http://en.wikipedia.org/wiki/UTF-8 http://tools.ietf.org/html/rfc3629 \tparam CharType Code unit for storing 8-bit UTF-8 data. Default is char. \note implements Encoding concept */ template struct UTF8 { typedef CharType Ch; enum { supportUnicode = 1 }; template static void Encode(OutputStream& os, unsigned codepoint) { if (codepoint <= 0x7F) os.Put(static_cast(codepoint & 0xFF)); else if (codepoint <= 0x7FF) { os.Put(static_cast(0xC0 | ((codepoint >> 6) & 0xFF))); os.Put(static_cast(0x80 | ((codepoint & 0x3F)))); } else if (codepoint <= 0xFFFF) { os.Put(static_cast(0xE0 | ((codepoint >> 12) & 0xFF))); os.Put(static_cast(0x80 | ((codepoint >> 6) & 0x3F))); os.Put(static_cast(0x80 | (codepoint & 0x3F))); } else { RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); os.Put(static_cast(0xF0 | ((codepoint >> 18) & 0xFF))); os.Put(static_cast(0x80 | ((codepoint >> 12) & 0x3F))); os.Put(static_cast(0x80 | ((codepoint >> 6) & 0x3F))); os.Put(static_cast(0x80 | (codepoint & 0x3F))); } } template static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { if (codepoint <= 0x7F) PutUnsafe(os, static_cast(codepoint & 0xFF)); else if (codepoint <= 0x7FF) { PutUnsafe(os, static_cast(0xC0 | ((codepoint >> 6) & 0xFF))); PutUnsafe(os, static_cast(0x80 | ((codepoint & 0x3F)))); } else if (codepoint <= 0xFFFF) { PutUnsafe(os, static_cast(0xE0 | ((codepoint >> 12) & 0xFF))); PutUnsafe(os, static_cast(0x80 | ((codepoint >> 6) & 0x3F))); PutUnsafe(os, static_cast(0x80 | (codepoint & 0x3F))); } else { RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); PutUnsafe(os, static_cast(0xF0 | ((codepoint >> 18) & 0xFF))); PutUnsafe(os, static_cast(0x80 | ((codepoint >> 12) & 0x3F))); PutUnsafe(os, static_cast(0x80 | ((codepoint >> 6) & 0x3F))); PutUnsafe(os, static_cast(0x80 | (codepoint & 0x3F))); } } template static bool Decode(InputStream& is, unsigned* codepoint) { #define RAPIDJSON_COPY() c = is.Take(); *codepoint = (*codepoint << 6) | (static_cast(c) & 0x3Fu) #define RAPIDJSON_TRANS(mask) result &= ((GetRange(static_cast(c)) & mask) != 0) #define RAPIDJSON_TAIL() RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x70) typename InputStream::Ch c = is.Take(); if (!(c & 0x80)) { *codepoint = static_cast(c); return true; } unsigned char type = GetRange(static_cast(c)); if (type >= 32) { *codepoint = 0; } else { *codepoint = (0xFFu >> type) & static_cast(c); } bool result = true; switch (type) { case 2: RAPIDJSON_TAIL(); return result; case 3: RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; case 4: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x50); RAPIDJSON_TAIL(); return result; case 5: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x10); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; case 6: RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; case 10: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x20); RAPIDJSON_TAIL(); return result; case 11: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x60); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; default: return false; } #undef RAPIDJSON_COPY #undef RAPIDJSON_TRANS #undef RAPIDJSON_TAIL } template static bool Validate(InputStream& is, OutputStream& os) { #define RAPIDJSON_COPY() os.Put(c = is.Take()) #define RAPIDJSON_TRANS(mask) result &= ((GetRange(static_cast(c)) & mask) != 0) #define RAPIDJSON_TAIL() RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x70) Ch c; RAPIDJSON_COPY(); if (!(c & 0x80)) return true; bool result = true; switch (GetRange(static_cast(c))) { case 2: RAPIDJSON_TAIL(); return result; case 3: RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; case 4: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x50); RAPIDJSON_TAIL(); return result; case 5: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x10); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; case 6: RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; case 10: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x20); RAPIDJSON_TAIL(); return result; case 11: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x60); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; default: return false; } #undef RAPIDJSON_COPY #undef RAPIDJSON_TRANS #undef RAPIDJSON_TAIL } static unsigned char GetRange(unsigned char c) { // Referring to DFA of http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ // With new mapping 1 -> 0x10, 7 -> 0x20, 9 -> 0x40, such that AND operation can test multiple types. static const unsigned char type[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, 0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40, 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8, }; return type[c]; } template static CharType TakeBOM(InputByteStream& is) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); typename InputByteStream::Ch c = Take(is); if (static_cast(c) != 0xEFu) return c; c = is.Take(); if (static_cast(c) != 0xBBu) return c; c = is.Take(); if (static_cast(c) != 0xBFu) return c; c = is.Take(); return c; } template static Ch Take(InputByteStream& is) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); return static_cast(is.Take()); } template static void PutBOM(OutputByteStream& os) { RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); os.Put(static_cast(0xEFu)); os.Put(static_cast(0xBBu)); os.Put(static_cast(0xBFu)); } template static void Put(OutputByteStream& os, Ch c) { RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); os.Put(static_cast(c)); } }; /////////////////////////////////////////////////////////////////////////////// // UTF16 //! UTF-16 encoding. /*! http://en.wikipedia.org/wiki/UTF-16 http://tools.ietf.org/html/rfc2781 \tparam CharType Type for storing 16-bit UTF-16 data. Default is wchar_t. C++11 may use char16_t instead. \note implements Encoding concept \note For in-memory access, no need to concern endianness. The code units and code points are represented by CPU's endianness. For streaming, use UTF16LE and UTF16BE, which handle endianness. */ template struct UTF16 { typedef CharType Ch; RAPIDJSON_STATIC_ASSERT(sizeof(Ch) >= 2); enum { supportUnicode = 1 }; template static void Encode(OutputStream& os, unsigned codepoint) { RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2); if (codepoint <= 0xFFFF) { RAPIDJSON_ASSERT(codepoint < 0xD800 || codepoint > 0xDFFF); // Code point itself cannot be surrogate pair os.Put(static_cast(codepoint)); } else { RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); unsigned v = codepoint - 0x10000; os.Put(static_cast((v >> 10) | 0xD800)); os.Put(static_cast((v & 0x3FF) | 0xDC00)); } } template static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2); if (codepoint <= 0xFFFF) { RAPIDJSON_ASSERT(codepoint < 0xD800 || codepoint > 0xDFFF); // Code point itself cannot be surrogate pair PutUnsafe(os, static_cast(codepoint)); } else { RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); unsigned v = codepoint - 0x10000; PutUnsafe(os, static_cast((v >> 10) | 0xD800)); PutUnsafe(os, static_cast((v & 0x3FF) | 0xDC00)); } } template static bool Decode(InputStream& is, unsigned* codepoint) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 2); typename InputStream::Ch c = is.Take(); if (c < 0xD800 || c > 0xDFFF) { *codepoint = static_cast(c); return true; } else if (c <= 0xDBFF) { *codepoint = (static_cast(c) & 0x3FF) << 10; c = is.Take(); *codepoint |= (static_cast(c) & 0x3FF); *codepoint += 0x10000; return c >= 0xDC00 && c <= 0xDFFF; } return false; } template static bool Validate(InputStream& is, OutputStream& os) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 2); RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2); typename InputStream::Ch c; os.Put(static_cast(c = is.Take())); if (c < 0xD800 || c > 0xDFFF) return true; else if (c <= 0xDBFF) { os.Put(c = is.Take()); return c >= 0xDC00 && c <= 0xDFFF; } return false; } }; //! UTF-16 little endian encoding. template struct UTF16LE : UTF16 { template static CharType TakeBOM(InputByteStream& is) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); CharType c = Take(is); return static_cast(c) == 0xFEFFu ? Take(is) : c; } template static CharType Take(InputByteStream& is) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); unsigned c = static_cast(is.Take()); c |= static_cast(static_cast(is.Take())) << 8; return static_cast(c); } template static void PutBOM(OutputByteStream& os) { RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); os.Put(static_cast(0xFFu)); os.Put(static_cast(0xFEu)); } template static void Put(OutputByteStream& os, CharType c) { RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); os.Put(static_cast(static_cast(c) & 0xFFu)); os.Put(static_cast((static_cast(c) >> 8) & 0xFFu)); } }; //! UTF-16 big endian encoding. template struct UTF16BE : UTF16 { template static CharType TakeBOM(InputByteStream& is) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); CharType c = Take(is); return static_cast(c) == 0xFEFFu ? Take(is) : c; } template static CharType Take(InputByteStream& is) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); unsigned c = static_cast(static_cast(is.Take())) << 8; c |= static_cast(static_cast(is.Take())); return static_cast(c); } template static void PutBOM(OutputByteStream& os) { RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); os.Put(static_cast(0xFEu)); os.Put(static_cast(0xFFu)); } template static void Put(OutputByteStream& os, CharType c) { RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); os.Put(static_cast((static_cast(c) >> 8) & 0xFFu)); os.Put(static_cast(static_cast(c) & 0xFFu)); } }; /////////////////////////////////////////////////////////////////////////////// // UTF32 //! UTF-32 encoding. /*! http://en.wikipedia.org/wiki/UTF-32 \tparam CharType Type for storing 32-bit UTF-32 data. Default is unsigned. C++11 may use char32_t instead. \note implements Encoding concept \note For in-memory access, no need to concern endianness. The code units and code points are represented by CPU's endianness. For streaming, use UTF32LE and UTF32BE, which handle endianness. */ template struct UTF32 { typedef CharType Ch; RAPIDJSON_STATIC_ASSERT(sizeof(Ch) >= 4); enum { supportUnicode = 1 }; template static void Encode(OutputStream& os, unsigned codepoint) { RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 4); RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); os.Put(codepoint); } template static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 4); RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); PutUnsafe(os, codepoint); } template static bool Decode(InputStream& is, unsigned* codepoint) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 4); Ch c = is.Take(); *codepoint = c; return c <= 0x10FFFF; } template static bool Validate(InputStream& is, OutputStream& os) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 4); Ch c; os.Put(c = is.Take()); return c <= 0x10FFFF; } }; //! UTF-32 little endian enocoding. template struct UTF32LE : UTF32 { template static CharType TakeBOM(InputByteStream& is) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); CharType c = Take(is); return static_cast(c) == 0x0000FEFFu ? Take(is) : c; } template static CharType Take(InputByteStream& is) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); unsigned c = static_cast(is.Take()); c |= static_cast(static_cast(is.Take())) << 8; c |= static_cast(static_cast(is.Take())) << 16; c |= static_cast(static_cast(is.Take())) << 24; return static_cast(c); } template static void PutBOM(OutputByteStream& os) { RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); os.Put(static_cast(0xFFu)); os.Put(static_cast(0xFEu)); os.Put(static_cast(0x00u)); os.Put(static_cast(0x00u)); } template static void Put(OutputByteStream& os, CharType c) { RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); os.Put(static_cast(c & 0xFFu)); os.Put(static_cast((c >> 8) & 0xFFu)); os.Put(static_cast((c >> 16) & 0xFFu)); os.Put(static_cast((c >> 24) & 0xFFu)); } }; //! UTF-32 big endian encoding. template struct UTF32BE : UTF32 { template static CharType TakeBOM(InputByteStream& is) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); CharType c = Take(is); return static_cast(c) == 0x0000FEFFu ? Take(is) : c; } template static CharType Take(InputByteStream& is) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); unsigned c = static_cast(static_cast(is.Take())) << 24; c |= static_cast(static_cast(is.Take())) << 16; c |= static_cast(static_cast(is.Take())) << 8; c |= static_cast(static_cast(is.Take())); return static_cast(c); } template static void PutBOM(OutputByteStream& os) { RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); os.Put(static_cast(0x00u)); os.Put(static_cast(0x00u)); os.Put(static_cast(0xFEu)); os.Put(static_cast(0xFFu)); } template static void Put(OutputByteStream& os, CharType c) { RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); os.Put(static_cast((c >> 24) & 0xFFu)); os.Put(static_cast((c >> 16) & 0xFFu)); os.Put(static_cast((c >> 8) & 0xFFu)); os.Put(static_cast(c & 0xFFu)); } }; /////////////////////////////////////////////////////////////////////////////// // ASCII //! ASCII encoding. /*! http://en.wikipedia.org/wiki/ASCII \tparam CharType Code unit for storing 7-bit ASCII data. Default is char. \note implements Encoding concept */ template struct ASCII { typedef CharType Ch; enum { supportUnicode = 0 }; template static void Encode(OutputStream& os, unsigned codepoint) { RAPIDJSON_ASSERT(codepoint <= 0x7F); os.Put(static_cast(codepoint & 0xFF)); } template static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { RAPIDJSON_ASSERT(codepoint <= 0x7F); PutUnsafe(os, static_cast(codepoint & 0xFF)); } template static bool Decode(InputStream& is, unsigned* codepoint) { uint8_t c = static_cast(is.Take()); *codepoint = c; return c <= 0X7F; } template static bool Validate(InputStream& is, OutputStream& os) { uint8_t c = static_cast(is.Take()); os.Put(static_cast(c)); return c <= 0x7F; } template static CharType TakeBOM(InputByteStream& is) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); uint8_t c = static_cast(Take(is)); return static_cast(c); } template static Ch Take(InputByteStream& is) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); return static_cast(is.Take()); } template static void PutBOM(OutputByteStream& os) { RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); (void)os; } template static void Put(OutputByteStream& os, Ch c) { RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); os.Put(static_cast(c)); } }; /////////////////////////////////////////////////////////////////////////////// // AutoUTF //! Runtime-specified UTF encoding type of a stream. enum UTFType { kUTF8 = 0, //!< UTF-8. kUTF16LE = 1, //!< UTF-16 little endian. kUTF16BE = 2, //!< UTF-16 big endian. kUTF32LE = 3, //!< UTF-32 little endian. kUTF32BE = 4 //!< UTF-32 big endian. }; //! Dynamically select encoding according to stream's runtime-specified UTF encoding type. /*! \note This class can be used with AutoUTFInputtStream and AutoUTFOutputStream, which provides GetType(). */ template struct AutoUTF { typedef CharType Ch; enum { supportUnicode = 1 }; #define RAPIDJSON_ENCODINGS_FUNC(x) UTF8::x, UTF16LE::x, UTF16BE::x, UTF32LE::x, UTF32BE::x template static RAPIDJSON_FORCEINLINE void Encode(OutputStream& os, unsigned codepoint) { typedef void (*EncodeFunc)(OutputStream&, unsigned); static const EncodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Encode) }; (*f[os.GetType()])(os, codepoint); } template static RAPIDJSON_FORCEINLINE void EncodeUnsafe(OutputStream& os, unsigned codepoint) { typedef void (*EncodeFunc)(OutputStream&, unsigned); static const EncodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(EncodeUnsafe) }; (*f[os.GetType()])(os, codepoint); } template static RAPIDJSON_FORCEINLINE bool Decode(InputStream& is, unsigned* codepoint) { typedef bool (*DecodeFunc)(InputStream&, unsigned*); static const DecodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Decode) }; return (*f[is.GetType()])(is, codepoint); } template static RAPIDJSON_FORCEINLINE bool Validate(InputStream& is, OutputStream& os) { typedef bool (*ValidateFunc)(InputStream&, OutputStream&); static const ValidateFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Validate) }; return (*f[is.GetType()])(is, os); } #undef RAPIDJSON_ENCODINGS_FUNC }; /////////////////////////////////////////////////////////////////////////////// // Transcoder //! Encoding conversion. template struct Transcoder { //! Take one Unicode codepoint from source encoding, convert it to target encoding and put it to the output stream. template static RAPIDJSON_FORCEINLINE bool Transcode(InputStream& is, OutputStream& os) { unsigned codepoint; if (!SourceEncoding::Decode(is, &codepoint)) return false; TargetEncoding::Encode(os, codepoint); return true; } template static RAPIDJSON_FORCEINLINE bool TranscodeUnsafe(InputStream& is, OutputStream& os) { unsigned codepoint; if (!SourceEncoding::Decode(is, &codepoint)) return false; TargetEncoding::EncodeUnsafe(os, codepoint); return true; } //! Validate one Unicode codepoint from an encoded stream. template static RAPIDJSON_FORCEINLINE bool Validate(InputStream& is, OutputStream& os) { return Transcode(is, os); // Since source/target encoding is different, must transcode. } }; // Forward declaration. template inline void PutUnsafe(Stream& stream, typename Stream::Ch c); //! Specialization of Transcoder with same source and target encoding. template struct Transcoder { template static RAPIDJSON_FORCEINLINE bool Transcode(InputStream& is, OutputStream& os) { os.Put(is.Take()); // Just copy one code unit. This semantic is different from primary template class. return true; } template static RAPIDJSON_FORCEINLINE bool TranscodeUnsafe(InputStream& is, OutputStream& os) { PutUnsafe(os, is.Take()); // Just copy one code unit. This semantic is different from primary template class. return true; } template static RAPIDJSON_FORCEINLINE bool Validate(InputStream& is, OutputStream& os) { return Encoding::Validate(is, os); // source/target encoding are the same } }; RAPIDJSON_NAMESPACE_END #if defined(__GNUC__) || (defined(_MSC_VER) && !defined(__clang__)) RAPIDJSON_DIAG_POP #endif #endif // RAPIDJSON_ENCODINGS_H_ ================================================ FILE: src/json/rapidjson/error/en.h ================================================ // Tencent is pleased to support the open source community by making RapidJSON available. // // Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at // // http://opensource.org/licenses/MIT // // Unless required by applicable law or agreed to in writing, software distributed // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. #ifndef RAPIDJSON_ERROR_EN_H_ #define RAPIDJSON_ERROR_EN_H_ #include "error.h" #ifdef __clang__ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(switch-enum) RAPIDJSON_DIAG_OFF(covered-switch-default) #endif RAPIDJSON_NAMESPACE_BEGIN //! Maps error code of parsing into error message. /*! \ingroup RAPIDJSON_ERRORS \param parseErrorCode Error code obtained in parsing. \return the error message. \note User can make a copy of this function for localization. Using switch-case is safer for future modification of error codes. */ inline const RAPIDJSON_ERROR_CHARTYPE* GetParseError_En(ParseErrorCode parseErrorCode) { switch (parseErrorCode) { case kParseErrorNone: return RAPIDJSON_ERROR_STRING("No error."); case kParseErrorDocumentEmpty: return RAPIDJSON_ERROR_STRING("The document is empty."); case kParseErrorDocumentRootNotSingular: return RAPIDJSON_ERROR_STRING("The document root must not be followed by other values."); case kParseErrorValueInvalid: return RAPIDJSON_ERROR_STRING("Invalid value."); case kParseErrorObjectMissName: return RAPIDJSON_ERROR_STRING("Missing a name for object member."); case kParseErrorObjectMissColon: return RAPIDJSON_ERROR_STRING("Missing a colon after a name of object member."); case kParseErrorObjectMissCommaOrCurlyBracket: return RAPIDJSON_ERROR_STRING("Missing a comma or '}' after an object member."); case kParseErrorArrayMissCommaOrSquareBracket: return RAPIDJSON_ERROR_STRING("Missing a comma or ']' after an array element."); case kParseErrorStringUnicodeEscapeInvalidHex: return RAPIDJSON_ERROR_STRING("Incorrect hex digit after \\u escape in string."); case kParseErrorStringUnicodeSurrogateInvalid: return RAPIDJSON_ERROR_STRING("The surrogate pair in string is invalid."); case kParseErrorStringEscapeInvalid: return RAPIDJSON_ERROR_STRING("Invalid escape character in string."); case kParseErrorStringMissQuotationMark: return RAPIDJSON_ERROR_STRING("Missing a closing quotation mark in string."); case kParseErrorStringInvalidEncoding: return RAPIDJSON_ERROR_STRING("Invalid encoding in string."); case kParseErrorNumberTooBig: return RAPIDJSON_ERROR_STRING("Number too big to be stored in double."); case kParseErrorNumberMissFraction: return RAPIDJSON_ERROR_STRING("Miss fraction part in number."); case kParseErrorNumberMissExponent: return RAPIDJSON_ERROR_STRING("Miss exponent in number."); case kParseErrorTermination: return RAPIDJSON_ERROR_STRING("Terminate parsing due to Handler error."); case kParseErrorUnspecificSyntaxError: return RAPIDJSON_ERROR_STRING("Unspecific syntax error."); default: return RAPIDJSON_ERROR_STRING("Unknown error."); } } RAPIDJSON_NAMESPACE_END #ifdef __clang__ RAPIDJSON_DIAG_POP #endif #endif // RAPIDJSON_ERROR_EN_H_ ================================================ FILE: src/json/rapidjson/error/error.h ================================================ // Tencent is pleased to support the open source community by making RapidJSON available. // // Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at // // http://opensource.org/licenses/MIT // // Unless required by applicable law or agreed to in writing, software distributed // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. #ifndef RAPIDJSON_ERROR_ERROR_H_ #define RAPIDJSON_ERROR_ERROR_H_ #include "src/json/rapidjson/rapidjson.h" #ifdef __clang__ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(padded) #endif /*! \file error.h */ /*! \defgroup RAPIDJSON_ERRORS RapidJSON error handling */ /////////////////////////////////////////////////////////////////////////////// // RAPIDJSON_ERROR_CHARTYPE //! Character type of error messages. /*! \ingroup RAPIDJSON_ERRORS The default character type is \c char. On Windows, user can define this macro as \c TCHAR for supporting both unicode/non-unicode settings. */ #ifndef RAPIDJSON_ERROR_CHARTYPE #define RAPIDJSON_ERROR_CHARTYPE char #endif /////////////////////////////////////////////////////////////////////////////// // RAPIDJSON_ERROR_STRING //! Macro for converting string literial to \ref RAPIDJSON_ERROR_CHARTYPE[]. /*! \ingroup RAPIDJSON_ERRORS By default this conversion macro does nothing. On Windows, user can define this macro as \c _T(x) for supporting both unicode/non-unicode settings. */ #ifndef RAPIDJSON_ERROR_STRING #define RAPIDJSON_ERROR_STRING(x) x #endif RAPIDJSON_NAMESPACE_BEGIN /////////////////////////////////////////////////////////////////////////////// // ParseErrorCode //! Error code of parsing. /*! \ingroup RAPIDJSON_ERRORS \see GenericReader::Parse, GenericReader::GetParseErrorCode */ enum ParseErrorCode { kParseErrorNone = 0, //!< No error. kParseErrorDocumentEmpty, //!< The document is empty. kParseErrorDocumentRootNotSingular, //!< The document root must not follow by other values. kParseErrorValueInvalid, //!< Invalid value. kParseErrorObjectMissName, //!< Missing a name for object member. kParseErrorObjectMissColon, //!< Missing a colon after a name of object member. kParseErrorObjectMissCommaOrCurlyBracket, //!< Missing a comma or '}' after an object member. kParseErrorArrayMissCommaOrSquareBracket, //!< Missing a comma or ']' after an array element. kParseErrorStringUnicodeEscapeInvalidHex, //!< Incorrect hex digit after \\u escape in string. kParseErrorStringUnicodeSurrogateInvalid, //!< The surrogate pair in string is invalid. kParseErrorStringEscapeInvalid, //!< Invalid escape character in string. kParseErrorStringMissQuotationMark, //!< Missing a closing quotation mark in string. kParseErrorStringInvalidEncoding, //!< Invalid encoding in string. kParseErrorNumberTooBig, //!< Number too big to be stored in double. kParseErrorNumberMissFraction, //!< Miss fraction part in number. kParseErrorNumberMissExponent, //!< Miss exponent in number. kParseErrorTermination, //!< Parsing was terminated. kParseErrorUnspecificSyntaxError //!< Unspecific syntax error. }; //! Result of parsing (wraps ParseErrorCode) /*! \ingroup RAPIDJSON_ERRORS \code Document doc; ParseResult ok = doc.Parse("[42]"); if (!ok) { fprintf(stderr, "JSON parse error: %s (%u)", GetParseError_En(ok.Code()), ok.Offset()); exit(EXIT_FAILURE); } \endcode \see GenericReader::Parse, GenericDocument::Parse */ struct ParseResult { //!! Unspecified boolean type typedef bool (ParseResult::*BooleanType)() const; public: //! Default constructor, no error. ParseResult() : code_(kParseErrorNone), offset_(0) {} //! Constructor to set an error. ParseResult(ParseErrorCode code, size_t offset) : code_(code), offset_(offset) {} //! Get the error code. ParseErrorCode Code() const { return code_; } //! Get the error offset, if \ref IsError(), 0 otherwise. size_t Offset() const { return offset_; } //! Explicit conversion to \c bool, returns \c true, iff !\ref IsError(). operator BooleanType() const { return !IsError() ? &ParseResult::IsError : NULL; } //! Whether the result is an error. bool IsError() const { return code_ != kParseErrorNone; } bool operator==(const ParseResult& that) const { return code_ == that.code_; } bool operator==(ParseErrorCode code) const { return code_ == code; } friend bool operator==(ParseErrorCode code, const ParseResult & err) { return code == err.code_; } bool operator!=(const ParseResult& that) const { return !(*this == that); } bool operator!=(ParseErrorCode code) const { return !(*this == code); } friend bool operator!=(ParseErrorCode code, const ParseResult & err) { return err != code; } //! Reset error code. void Clear() { Set(kParseErrorNone); } //! Update error code and offset. void Set(ParseErrorCode code, size_t offset = 0) { code_ = code; offset_ = offset; } private: ParseErrorCode code_; size_t offset_; }; //! Function pointer type of GetParseError(). /*! \ingroup RAPIDJSON_ERRORS This is the prototype for \c GetParseError_X(), where \c X is a locale. User can dynamically change locale in runtime, e.g.: \code GetParseErrorFunc GetParseError = GetParseError_En; // or whatever const RAPIDJSON_ERROR_CHARTYPE* s = GetParseError(document.GetParseErrorCode()); \endcode */ typedef const RAPIDJSON_ERROR_CHARTYPE* (*GetParseErrorFunc)(ParseErrorCode); RAPIDJSON_NAMESPACE_END #ifdef __clang__ RAPIDJSON_DIAG_POP #endif #endif // RAPIDJSON_ERROR_ERROR_H_ ================================================ FILE: src/json/rapidjson/filereadstream.h ================================================ // Tencent is pleased to support the open source community by making RapidJSON available. // // Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at // // http://opensource.org/licenses/MIT // // Unless required by applicable law or agreed to in writing, software distributed // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. #ifndef RAPIDJSON_FILEREADSTREAM_H_ #define RAPIDJSON_FILEREADSTREAM_H_ #include "stream.h" #include #ifdef __clang__ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(padded) RAPIDJSON_DIAG_OFF(unreachable-code) RAPIDJSON_DIAG_OFF(missing-noreturn) #endif RAPIDJSON_NAMESPACE_BEGIN //! File byte stream for input using fread(). /*! \note implements Stream concept */ class FileReadStream { public: typedef char Ch; //!< Character type (byte). //! Constructor. /*! \param fp File pointer opened for read. \param buffer user-supplied buffer. \param bufferSize size of buffer in bytes. Must >=4 bytes. */ FileReadStream(std::FILE* fp, char* buffer, size_t bufferSize) : fp_(fp), buffer_(buffer), bufferSize_(bufferSize), bufferLast_(0), current_(buffer_), readCount_(0), count_(0), eof_(false) { RAPIDJSON_ASSERT(fp_ != 0); RAPIDJSON_ASSERT(bufferSize >= 4); Read(); } Ch Peek() const { return *current_; } Ch Take() { Ch c = *current_; Read(); return c; } size_t Tell() const { return count_ + static_cast(current_ - buffer_); } // Not implemented void Put(Ch) { RAPIDJSON_ASSERT(false); } void Flush() { RAPIDJSON_ASSERT(false); } Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } // For encoding detection only. const Ch* Peek4() const { return (current_ + 4 - !eof_ <= bufferLast_) ? current_ : 0; } private: void Read() { if (current_ < bufferLast_) ++current_; else if (!eof_) { count_ += readCount_; readCount_ = std::fread(buffer_, 1, bufferSize_, fp_); bufferLast_ = buffer_ + readCount_ - 1; current_ = buffer_; if (readCount_ < bufferSize_) { buffer_[readCount_] = '\0'; ++bufferLast_; eof_ = true; } } } std::FILE* fp_; Ch *buffer_; size_t bufferSize_; Ch *bufferLast_; Ch *current_; size_t readCount_; size_t count_; //!< Number of characters read bool eof_; }; RAPIDJSON_NAMESPACE_END #ifdef __clang__ RAPIDJSON_DIAG_POP #endif #endif // RAPIDJSON_FILESTREAM_H_ ================================================ FILE: src/json/rapidjson/filewritestream.h ================================================ // Tencent is pleased to support the open source community by making RapidJSON available. // // Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at // // http://opensource.org/licenses/MIT // // Unless required by applicable law or agreed to in writing, software distributed // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. #ifndef RAPIDJSON_FILEWRITESTREAM_H_ #define RAPIDJSON_FILEWRITESTREAM_H_ #include "stream.h" #include #ifdef __clang__ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(unreachable-code) #endif RAPIDJSON_NAMESPACE_BEGIN //! Wrapper of C file stream for output using fwrite(). /*! \note implements Stream concept */ class FileWriteStream { public: typedef char Ch; //!< Character type. Only support char. FileWriteStream(std::FILE* fp, char* buffer, size_t bufferSize) : fp_(fp), buffer_(buffer), bufferEnd_(buffer + bufferSize), current_(buffer_) { RAPIDJSON_ASSERT(fp_ != 0); } void Put(char c) { if (current_ >= bufferEnd_) Flush(); *current_++ = c; } void PutN(char c, size_t n) { size_t avail = static_cast(bufferEnd_ - current_); while (n > avail) { std::memset(current_, c, avail); current_ += avail; Flush(); n -= avail; avail = static_cast(bufferEnd_ - current_); } if (n > 0) { std::memset(current_, c, n); current_ += n; } } void Flush() { if (current_ != buffer_) { size_t result = std::fwrite(buffer_, 1, static_cast(current_ - buffer_), fp_); if (result < static_cast(current_ - buffer_)) { // failure deliberately ignored at this time // added to avoid warn_unused_result build errors } current_ = buffer_; } } // Not implemented char Peek() const { RAPIDJSON_ASSERT(false); return 0; } char Take() { RAPIDJSON_ASSERT(false); return 0; } size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } char* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } size_t PutEnd(char*) { RAPIDJSON_ASSERT(false); return 0; } private: // Prohibit copy constructor & assignment operator. FileWriteStream(const FileWriteStream&); FileWriteStream& operator=(const FileWriteStream&); std::FILE* fp_; char *buffer_; char *bufferEnd_; char *current_; }; //! Implement specialized version of PutN() with memset() for better performance. template<> inline void PutN(FileWriteStream& stream, char c, size_t n) { stream.PutN(c, n); } RAPIDJSON_NAMESPACE_END #ifdef __clang__ RAPIDJSON_DIAG_POP #endif #endif // RAPIDJSON_FILESTREAM_H_ ================================================ FILE: src/json/rapidjson/fwd.h ================================================ // Tencent is pleased to support the open source community by making RapidJSON available. // // Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at // // http://opensource.org/licenses/MIT // // Unless required by applicable law or agreed to in writing, software distributed // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. #ifndef RAPIDJSON_FWD_H_ #define RAPIDJSON_FWD_H_ #include "rapidjson.h" RAPIDJSON_NAMESPACE_BEGIN // encodings.h template struct UTF8; template struct UTF16; template struct UTF16BE; template struct UTF16LE; template struct UTF32; template struct UTF32BE; template struct UTF32LE; template struct ASCII; template struct AutoUTF; template struct Transcoder; // allocators.h class CrtAllocator; template class MemoryPoolAllocator; // stream.h template struct GenericStringStream; typedef GenericStringStream > StringStream; template struct GenericInsituStringStream; typedef GenericInsituStringStream > InsituStringStream; // stringbuffer.h template class GenericStringBuffer; typedef GenericStringBuffer, CrtAllocator> StringBuffer; // filereadstream.h class FileReadStream; // filewritestream.h class FileWriteStream; // memorybuffer.h template struct GenericMemoryBuffer; typedef GenericMemoryBuffer MemoryBuffer; // memorystream.h struct MemoryStream; // reader.h template struct BaseReaderHandler; template class GenericReader; typedef GenericReader, UTF8, CrtAllocator> Reader; // writer.h template class Writer; // prettywriter.h template class PrettyWriter; // document.h template struct GenericMember; template class GenericMemberIterator; template struct GenericStringRef; template class GenericValue; typedef GenericValue, MemoryPoolAllocator > Value; template class GenericDocument; typedef GenericDocument, MemoryPoolAllocator, CrtAllocator> Document; // pointer.h template class GenericPointer; typedef GenericPointer Pointer; // schema.h template class IGenericRemoteSchemaDocumentProvider; template class GenericSchemaDocument; typedef GenericSchemaDocument SchemaDocument; typedef IGenericRemoteSchemaDocumentProvider IRemoteSchemaDocumentProvider; template < typename SchemaDocumentType, typename OutputHandler, typename StateAllocator> class GenericSchemaValidator; typedef GenericSchemaValidator, void>, CrtAllocator> SchemaValidator; RAPIDJSON_NAMESPACE_END #endif // RAPIDJSON_RAPIDJSONFWD_H_ ================================================ FILE: src/json/rapidjson/internal/biginteger.h ================================================ // Tencent is pleased to support the open source community by making RapidJSON available. // // Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at // // http://opensource.org/licenses/MIT // // Unless required by applicable law or agreed to in writing, software distributed // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. #ifndef RAPIDJSON_BIGINTEGER_H_ #define RAPIDJSON_BIGINTEGER_H_ #include "src/json/rapidjson/rapidjson.h" #if defined(_MSC_VER) && !__INTEL_COMPILER && defined(_M_AMD64) #include // for _umul128 #pragma intrinsic(_umul128) #endif RAPIDJSON_NAMESPACE_BEGIN namespace internal { class BigInteger { public: typedef uint64_t Type; BigInteger(const BigInteger& rhs) : count_(rhs.count_) { std::memcpy(digits_, rhs.digits_, count_ * sizeof(Type)); } explicit BigInteger(uint64_t u) : count_(1) { digits_[0] = u; } BigInteger(const char* decimals, size_t length) : count_(1) { RAPIDJSON_ASSERT(length > 0); digits_[0] = 0; size_t i = 0; const size_t kMaxDigitPerIteration = 19; // 2^64 = 18446744073709551616 > 10^19 while (length >= kMaxDigitPerIteration) { AppendDecimal64(decimals + i, decimals + i + kMaxDigitPerIteration); length -= kMaxDigitPerIteration; i += kMaxDigitPerIteration; } if (length > 0) AppendDecimal64(decimals + i, decimals + i + length); } BigInteger& operator=(const BigInteger &rhs) { if (this != &rhs) { count_ = rhs.count_; std::memcpy(digits_, rhs.digits_, count_ * sizeof(Type)); } return *this; } BigInteger& operator=(uint64_t u) { digits_[0] = u; count_ = 1; return *this; } BigInteger& operator+=(uint64_t u) { Type backup = digits_[0]; digits_[0] += u; for (size_t i = 0; i < count_ - 1; i++) { if (digits_[i] >= backup) return *this; // no carry backup = digits_[i + 1]; digits_[i + 1] += 1; } // Last carry if (digits_[count_ - 1] < backup) PushBack(1); return *this; } BigInteger& operator*=(uint64_t u) { if (u == 0) return *this = 0; if (u == 1) return *this; if (*this == 1) return *this = u; uint64_t k = 0; for (size_t i = 0; i < count_; i++) { uint64_t hi; digits_[i] = MulAdd64(digits_[i], u, k, &hi); k = hi; } if (k > 0) PushBack(k); return *this; } BigInteger& operator*=(uint32_t u) { if (u == 0) return *this = 0; if (u == 1) return *this; if (*this == 1) return *this = u; uint64_t k = 0; for (size_t i = 0; i < count_; i++) { const uint64_t c = digits_[i] >> 32; const uint64_t d = digits_[i] & 0xFFFFFFFF; const uint64_t uc = u * c; const uint64_t ud = u * d; const uint64_t p0 = ud + k; const uint64_t p1 = uc + (p0 >> 32); digits_[i] = (p0 & 0xFFFFFFFF) | (p1 << 32); k = p1 >> 32; } if (k > 0) PushBack(k); return *this; } BigInteger& operator<<=(size_t shift) { if (IsZero() || shift == 0) return *this; size_t offset = shift / kTypeBit; size_t interShift = shift % kTypeBit; RAPIDJSON_ASSERT(count_ + offset <= kCapacity); if (interShift == 0) { std::memmove(digits_ + offset, digits_, count_ * sizeof(Type)); count_ += offset; } else { digits_[count_] = 0; for (size_t i = count_; i > 0; i--) digits_[i + offset] = (digits_[i] << interShift) | (digits_[i - 1] >> (kTypeBit - interShift)); digits_[offset] = digits_[0] << interShift; count_ += offset; if (digits_[count_]) count_++; } std::memset(digits_, 0, offset * sizeof(Type)); return *this; } bool operator==(const BigInteger& rhs) const { return count_ == rhs.count_ && std::memcmp(digits_, rhs.digits_, count_ * sizeof(Type)) == 0; } bool operator==(const Type rhs) const { return count_ == 1 && digits_[0] == rhs; } BigInteger& MultiplyPow5(unsigned exp) { static const uint32_t kPow5[12] = { 5, 5 * 5, 5 * 5 * 5, 5 * 5 * 5 * 5, 5 * 5 * 5 * 5 * 5, 5 * 5 * 5 * 5 * 5 * 5, 5 * 5 * 5 * 5 * 5 * 5 * 5, 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 }; if (exp == 0) return *this; for (; exp >= 27; exp -= 27) *this *= RAPIDJSON_UINT64_C2(0X6765C793, 0XFA10079D); // 5^27 for (; exp >= 13; exp -= 13) *this *= static_cast(1220703125u); // 5^13 if (exp > 0) *this *= kPow5[exp - 1]; return *this; } // Compute absolute difference of this and rhs. // Assume this != rhs bool Difference(const BigInteger& rhs, BigInteger* out) const { int cmp = Compare(rhs); RAPIDJSON_ASSERT(cmp != 0); const BigInteger *a, *b; // Makes a > b bool ret; if (cmp < 0) { a = &rhs; b = this; ret = true; } else { a = this; b = &rhs; ret = false; } Type borrow = 0; for (size_t i = 0; i < a->count_; i++) { Type d = a->digits_[i] - borrow; if (i < b->count_) d -= b->digits_[i]; borrow = (d > a->digits_[i]) ? 1 : 0; out->digits_[i] = d; if (d != 0) out->count_ = i + 1; } return ret; } int Compare(const BigInteger& rhs) const { if (count_ != rhs.count_) return count_ < rhs.count_ ? -1 : 1; for (size_t i = count_; i-- > 0;) if (digits_[i] != rhs.digits_[i]) return digits_[i] < rhs.digits_[i] ? -1 : 1; return 0; } size_t GetCount() const { return count_; } Type GetDigit(size_t index) const { RAPIDJSON_ASSERT(index < count_); return digits_[index]; } bool IsZero() const { return count_ == 1 && digits_[0] == 0; } private: void AppendDecimal64(const char* begin, const char* end) { uint64_t u = ParseUint64(begin, end); if (IsZero()) *this = u; else { unsigned exp = static_cast(end - begin); (MultiplyPow5(exp) <<= exp) += u; // *this = *this * 10^exp + u } } void PushBack(Type digit) { RAPIDJSON_ASSERT(count_ < kCapacity); digits_[count_++] = digit; } static uint64_t ParseUint64(const char* begin, const char* end) { uint64_t r = 0; for (const char* p = begin; p != end; ++p) { RAPIDJSON_ASSERT(*p >= '0' && *p <= '9'); r = r * 10u + static_cast(*p - '0'); } return r; } // Assume a * b + k < 2^128 static uint64_t MulAdd64(uint64_t a, uint64_t b, uint64_t k, uint64_t* outHigh) { #if defined(_MSC_VER) && defined(_M_AMD64) uint64_t low = _umul128(a, b, outHigh) + k; if (low < k) (*outHigh)++; return low; #elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && defined(__x86_64__) __extension__ typedef unsigned __int128 uint128; uint128 p = static_cast(a) * static_cast(b); p += k; *outHigh = static_cast(p >> 64); return static_cast(p); #else const uint64_t a0 = a & 0xFFFFFFFF, a1 = a >> 32, b0 = b & 0xFFFFFFFF, b1 = b >> 32; uint64_t x0 = a0 * b0, x1 = a0 * b1, x2 = a1 * b0, x3 = a1 * b1; x1 += (x0 >> 32); // can't give carry x1 += x2; if (x1 < x2) x3 += (static_cast(1) << 32); uint64_t lo = (x1 << 32) + (x0 & 0xFFFFFFFF); uint64_t hi = x3 + (x1 >> 32); lo += k; if (lo < k) hi++; *outHigh = hi; return lo; #endif } static const size_t kBitCount = 3328; // 64bit * 54 > 10^1000 static const size_t kCapacity = kBitCount / sizeof(Type); static const size_t kTypeBit = sizeof(Type) * 8; Type digits_[kCapacity]; size_t count_; }; } // namespace internal RAPIDJSON_NAMESPACE_END #endif // RAPIDJSON_BIGINTEGER_H_ ================================================ FILE: src/json/rapidjson/internal/diyfp.h ================================================ // Tencent is pleased to support the open source community by making RapidJSON available. // // Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at // // http://opensource.org/licenses/MIT // // Unless required by applicable law or agreed to in writing, software distributed // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. // This is a C++ header-only implementation of Grisu2 algorithm from the publication: // Loitsch, Florian. "Printing floating-point numbers quickly and accurately with // integers." ACM Sigplan Notices 45.6 (2010): 233-243. #ifndef RAPIDJSON_DIYFP_H_ #define RAPIDJSON_DIYFP_H_ #include "src/json/rapidjson/rapidjson.h" #include #if defined(_MSC_VER) && defined(_M_AMD64) && !defined(__INTEL_COMPILER) #include #pragma intrinsic(_BitScanReverse64) #pragma intrinsic(_umul128) #endif RAPIDJSON_NAMESPACE_BEGIN namespace internal { #ifdef __GNUC__ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(effc++) #endif #ifdef __clang__ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(padded) #endif struct DiyFp { DiyFp() : f(), e() {} DiyFp(uint64_t fp, int exp) : f(fp), e(exp) {} explicit DiyFp(double d) { union { double d; uint64_t u64; } u = { d }; int biased_e = static_cast((u.u64 & kDpExponentMask) >> kDpSignificandSize); uint64_t significand = (u.u64 & kDpSignificandMask); if (biased_e != 0) { f = significand + kDpHiddenBit; e = biased_e - kDpExponentBias; } else { f = significand; e = kDpMinExponent + 1; } } DiyFp operator-(const DiyFp& rhs) const { return DiyFp(f - rhs.f, e); } DiyFp operator*(const DiyFp& rhs) const { #if defined(_MSC_VER) && defined(_M_AMD64) uint64_t h; uint64_t l = _umul128(f, rhs.f, &h); if (l & (uint64_t(1) << 63)) // rounding h++; return DiyFp(h, e + rhs.e + 64); #elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && defined(__x86_64__) __extension__ typedef unsigned __int128 uint128; uint128 p = static_cast(f) * static_cast(rhs.f); uint64_t h = static_cast(p >> 64); uint64_t l = static_cast(p); if (l & (uint64_t(1) << 63)) // rounding h++; return DiyFp(h, e + rhs.e + 64); #else const uint64_t M32 = 0xFFFFFFFF; const uint64_t a = f >> 32; const uint64_t b = f & M32; const uint64_t c = rhs.f >> 32; const uint64_t d = rhs.f & M32; const uint64_t ac = a * c; const uint64_t bc = b * c; const uint64_t ad = a * d; const uint64_t bd = b * d; uint64_t tmp = (bd >> 32) + (ad & M32) + (bc & M32); tmp += 1U << 31; /// mult_round return DiyFp(ac + (ad >> 32) + (bc >> 32) + (tmp >> 32), e + rhs.e + 64); #endif } DiyFp Normalize() const { RAPIDJSON_ASSERT(f != 0); // https://stackoverflow.com/a/26809183/291737 #if defined(_MSC_VER) && defined(_M_AMD64) unsigned long index; _BitScanReverse64(&index, f); return DiyFp(f << (63 - index), e - (63 - index)); #elif defined(__GNUC__) && __GNUC__ >= 4 int s = __builtin_clzll(f); return DiyFp(f << s, e - s); #else DiyFp res = *this; while (!(res.f & (static_cast(1) << 63))) { res.f <<= 1; res.e--; } return res; #endif } DiyFp NormalizeBoundary() const { DiyFp res = *this; while (!(res.f & (kDpHiddenBit << 1))) { res.f <<= 1; res.e--; } res.f <<= (kDiySignificandSize - kDpSignificandSize - 2); res.e = res.e - (kDiySignificandSize - kDpSignificandSize - 2); return res; } void NormalizedBoundaries(DiyFp* minus, DiyFp* plus) const { DiyFp pl = DiyFp((f << 1) + 1, e - 1).NormalizeBoundary(); DiyFp mi = (f == kDpHiddenBit) ? DiyFp((f << 2) - 1, e - 2) : DiyFp((f << 1) - 1, e - 1); mi.f <<= mi.e - pl.e; mi.e = pl.e; *plus = pl; *minus = mi; } double ToDouble() const { union { double d; uint64_t u64; }u; RAPIDJSON_ASSERT(f <= kDpHiddenBit + kDpSignificandMask); if (e < kDpDenormalExponent) { // Underflow. return 0.0; } if (e >= kDpMaxExponent) { // Overflow. return std::numeric_limits::infinity(); } const uint64_t be = (e == kDpDenormalExponent && (f & kDpHiddenBit) == 0) ? 0 : static_cast(e + kDpExponentBias); u.u64 = (f & kDpSignificandMask) | (be << kDpSignificandSize); return u.d; } static const int kDiySignificandSize = 64; static const int kDpSignificandSize = 52; static const int kDpExponentBias = 0x3FF + kDpSignificandSize; static const int kDpMaxExponent = 0x7FF - kDpExponentBias; static const int kDpMinExponent = -kDpExponentBias; static const int kDpDenormalExponent = -kDpExponentBias + 1; static const uint64_t kDpExponentMask = RAPIDJSON_UINT64_C2(0x7FF00000, 0x00000000); static const uint64_t kDpSignificandMask = RAPIDJSON_UINT64_C2(0x000FFFFF, 0xFFFFFFFF); static const uint64_t kDpHiddenBit = RAPIDJSON_UINT64_C2(0x00100000, 0x00000000); uint64_t f; int e; }; inline DiyFp GetCachedPowerByIndex(size_t index) { // 10^-348, 10^-340, ..., 10^340 static const uint64_t kCachedPowers_F[] = { RAPIDJSON_UINT64_C2(0xfa8fd5a0, 0x081c0288), RAPIDJSON_UINT64_C2(0xbaaee17f, 0xa23ebf76), RAPIDJSON_UINT64_C2(0x8b16fb20, 0x3055ac76), RAPIDJSON_UINT64_C2(0xcf42894a, 0x5dce35ea), RAPIDJSON_UINT64_C2(0x9a6bb0aa, 0x55653b2d), RAPIDJSON_UINT64_C2(0xe61acf03, 0x3d1a45df), RAPIDJSON_UINT64_C2(0xab70fe17, 0xc79ac6ca), RAPIDJSON_UINT64_C2(0xff77b1fc, 0xbebcdc4f), RAPIDJSON_UINT64_C2(0xbe5691ef, 0x416bd60c), RAPIDJSON_UINT64_C2(0x8dd01fad, 0x907ffc3c), RAPIDJSON_UINT64_C2(0xd3515c28, 0x31559a83), RAPIDJSON_UINT64_C2(0x9d71ac8f, 0xada6c9b5), RAPIDJSON_UINT64_C2(0xea9c2277, 0x23ee8bcb), RAPIDJSON_UINT64_C2(0xaecc4991, 0x4078536d), RAPIDJSON_UINT64_C2(0x823c1279, 0x5db6ce57), RAPIDJSON_UINT64_C2(0xc2109436, 0x4dfb5637), RAPIDJSON_UINT64_C2(0x9096ea6f, 0x3848984f), RAPIDJSON_UINT64_C2(0xd77485cb, 0x25823ac7), RAPIDJSON_UINT64_C2(0xa086cfcd, 0x97bf97f4), RAPIDJSON_UINT64_C2(0xef340a98, 0x172aace5), RAPIDJSON_UINT64_C2(0xb23867fb, 0x2a35b28e), RAPIDJSON_UINT64_C2(0x84c8d4df, 0xd2c63f3b), RAPIDJSON_UINT64_C2(0xc5dd4427, 0x1ad3cdba), RAPIDJSON_UINT64_C2(0x936b9fce, 0xbb25c996), RAPIDJSON_UINT64_C2(0xdbac6c24, 0x7d62a584), RAPIDJSON_UINT64_C2(0xa3ab6658, 0x0d5fdaf6), RAPIDJSON_UINT64_C2(0xf3e2f893, 0xdec3f126), RAPIDJSON_UINT64_C2(0xb5b5ada8, 0xaaff80b8), RAPIDJSON_UINT64_C2(0x87625f05, 0x6c7c4a8b), RAPIDJSON_UINT64_C2(0xc9bcff60, 0x34c13053), RAPIDJSON_UINT64_C2(0x964e858c, 0x91ba2655), RAPIDJSON_UINT64_C2(0xdff97724, 0x70297ebd), RAPIDJSON_UINT64_C2(0xa6dfbd9f, 0xb8e5b88f), RAPIDJSON_UINT64_C2(0xf8a95fcf, 0x88747d94), RAPIDJSON_UINT64_C2(0xb9447093, 0x8fa89bcf), RAPIDJSON_UINT64_C2(0x8a08f0f8, 0xbf0f156b), RAPIDJSON_UINT64_C2(0xcdb02555, 0x653131b6), RAPIDJSON_UINT64_C2(0x993fe2c6, 0xd07b7fac), RAPIDJSON_UINT64_C2(0xe45c10c4, 0x2a2b3b06), RAPIDJSON_UINT64_C2(0xaa242499, 0x697392d3), RAPIDJSON_UINT64_C2(0xfd87b5f2, 0x8300ca0e), RAPIDJSON_UINT64_C2(0xbce50864, 0x92111aeb), RAPIDJSON_UINT64_C2(0x8cbccc09, 0x6f5088cc), RAPIDJSON_UINT64_C2(0xd1b71758, 0xe219652c), RAPIDJSON_UINT64_C2(0x9c400000, 0x00000000), RAPIDJSON_UINT64_C2(0xe8d4a510, 0x00000000), RAPIDJSON_UINT64_C2(0xad78ebc5, 0xac620000), RAPIDJSON_UINT64_C2(0x813f3978, 0xf8940984), RAPIDJSON_UINT64_C2(0xc097ce7b, 0xc90715b3), RAPIDJSON_UINT64_C2(0x8f7e32ce, 0x7bea5c70), RAPIDJSON_UINT64_C2(0xd5d238a4, 0xabe98068), RAPIDJSON_UINT64_C2(0x9f4f2726, 0x179a2245), RAPIDJSON_UINT64_C2(0xed63a231, 0xd4c4fb27), RAPIDJSON_UINT64_C2(0xb0de6538, 0x8cc8ada8), RAPIDJSON_UINT64_C2(0x83c7088e, 0x1aab65db), RAPIDJSON_UINT64_C2(0xc45d1df9, 0x42711d9a), RAPIDJSON_UINT64_C2(0x924d692c, 0xa61be758), RAPIDJSON_UINT64_C2(0xda01ee64, 0x1a708dea), RAPIDJSON_UINT64_C2(0xa26da399, 0x9aef774a), RAPIDJSON_UINT64_C2(0xf209787b, 0xb47d6b85), RAPIDJSON_UINT64_C2(0xb454e4a1, 0x79dd1877), RAPIDJSON_UINT64_C2(0x865b8692, 0x5b9bc5c2), RAPIDJSON_UINT64_C2(0xc83553c5, 0xc8965d3d), RAPIDJSON_UINT64_C2(0x952ab45c, 0xfa97a0b3), RAPIDJSON_UINT64_C2(0xde469fbd, 0x99a05fe3), RAPIDJSON_UINT64_C2(0xa59bc234, 0xdb398c25), RAPIDJSON_UINT64_C2(0xf6c69a72, 0xa3989f5c), RAPIDJSON_UINT64_C2(0xb7dcbf53, 0x54e9bece), RAPIDJSON_UINT64_C2(0x88fcf317, 0xf22241e2), RAPIDJSON_UINT64_C2(0xcc20ce9b, 0xd35c78a5), RAPIDJSON_UINT64_C2(0x98165af3, 0x7b2153df), RAPIDJSON_UINT64_C2(0xe2a0b5dc, 0x971f303a), RAPIDJSON_UINT64_C2(0xa8d9d153, 0x5ce3b396), RAPIDJSON_UINT64_C2(0xfb9b7cd9, 0xa4a7443c), RAPIDJSON_UINT64_C2(0xbb764c4c, 0xa7a44410), RAPIDJSON_UINT64_C2(0x8bab8eef, 0xb6409c1a), RAPIDJSON_UINT64_C2(0xd01fef10, 0xa657842c), RAPIDJSON_UINT64_C2(0x9b10a4e5, 0xe9913129), RAPIDJSON_UINT64_C2(0xe7109bfb, 0xa19c0c9d), RAPIDJSON_UINT64_C2(0xac2820d9, 0x623bf429), RAPIDJSON_UINT64_C2(0x80444b5e, 0x7aa7cf85), RAPIDJSON_UINT64_C2(0xbf21e440, 0x03acdd2d), RAPIDJSON_UINT64_C2(0x8e679c2f, 0x5e44ff8f), RAPIDJSON_UINT64_C2(0xd433179d, 0x9c8cb841), RAPIDJSON_UINT64_C2(0x9e19db92, 0xb4e31ba9), RAPIDJSON_UINT64_C2(0xeb96bf6e, 0xbadf77d9), RAPIDJSON_UINT64_C2(0xaf87023b, 0x9bf0ee6b) }; static const int16_t kCachedPowers_E[] = { -1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980, -954, -927, -901, -874, -847, -821, -794, -768, -741, -715, -688, -661, -635, -608, -582, -555, -529, -502, -475, -449, -422, -396, -369, -343, -316, -289, -263, -236, -210, -183, -157, -130, -103, -77, -50, -24, 3, 30, 56, 83, 109, 136, 162, 189, 216, 242, 269, 295, 322, 348, 375, 402, 428, 455, 481, 508, 534, 561, 588, 614, 641, 667, 694, 720, 747, 774, 800, 827, 853, 880, 907, 933, 960, 986, 1013, 1039, 1066 }; RAPIDJSON_ASSERT(index < 87); return DiyFp(kCachedPowers_F[index], kCachedPowers_E[index]); } inline DiyFp GetCachedPower(int e, int* K) { //int k = static_cast(ceil((-61 - e) * 0.30102999566398114)) + 374; double dk = (-61 - e) * 0.30102999566398114 + 347; // dk must be positive, so can do ceiling in positive int k = static_cast(dk); if (dk - k > 0.0) k++; unsigned index = static_cast((k >> 3) + 1); *K = -(-348 + static_cast(index << 3)); // decimal exponent no need lookup table return GetCachedPowerByIndex(index); } inline DiyFp GetCachedPower10(int exp, int *outExp) { RAPIDJSON_ASSERT(exp >= -348); unsigned index = static_cast(exp + 348) / 8u; *outExp = -348 + static_cast(index) * 8; return GetCachedPowerByIndex(index); } #ifdef __GNUC__ RAPIDJSON_DIAG_POP #endif #ifdef __clang__ RAPIDJSON_DIAG_POP RAPIDJSON_DIAG_OFF(padded) #endif } // namespace internal RAPIDJSON_NAMESPACE_END #endif // RAPIDJSON_DIYFP_H_ ================================================ FILE: src/json/rapidjson/internal/dtoa.h ================================================ // Tencent is pleased to support the open source community by making RapidJSON available. // // Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at // // http://opensource.org/licenses/MIT // // Unless required by applicable law or agreed to in writing, software distributed // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. // This is a C++ header-only implementation of Grisu2 algorithm from the publication: // Loitsch, Florian. "Printing floating-point numbers quickly and accurately with // integers." ACM Sigplan Notices 45.6 (2010): 233-243. #ifndef RAPIDJSON_DTOA_ #define RAPIDJSON_DTOA_ #include "itoa.h" // GetDigitsLut() #include "diyfp.h" #include "ieee754.h" RAPIDJSON_NAMESPACE_BEGIN namespace internal { #ifdef __GNUC__ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(effc++) RAPIDJSON_DIAG_OFF(array-bounds) // some gcc versions generate wrong warnings https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59124 #endif inline void GrisuRound(char* buffer, int len, uint64_t delta, uint64_t rest, uint64_t ten_kappa, uint64_t wp_w) { while (rest < wp_w && delta - rest >= ten_kappa && (rest + ten_kappa < wp_w || /// closer wp_w - rest > rest + ten_kappa - wp_w)) { buffer[len - 1]--; rest += ten_kappa; } } inline int CountDecimalDigit32(uint32_t n) { // Simple pure C++ implementation was faster than __builtin_clz version in this situation. if (n < 10) return 1; if (n < 100) return 2; if (n < 1000) return 3; if (n < 10000) return 4; if (n < 100000) return 5; if (n < 1000000) return 6; if (n < 10000000) return 7; if (n < 100000000) return 8; // Will not reach 10 digits in DigitGen() //if (n < 1000000000) return 9; //return 10; return 9; } inline void DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta, char* buffer, int* len, int* K) { static const uint32_t kPow10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 }; const DiyFp one(uint64_t(1) << -Mp.e, Mp.e); const DiyFp wp_w = Mp - W; uint32_t p1 = static_cast(Mp.f >> -one.e); uint64_t p2 = Mp.f & (one.f - 1); int kappa = CountDecimalDigit32(p1); // kappa in [0, 9] *len = 0; while (kappa > 0) { uint32_t d = 0; switch (kappa) { case 9: d = p1 / 100000000; p1 %= 100000000; break; case 8: d = p1 / 10000000; p1 %= 10000000; break; case 7: d = p1 / 1000000; p1 %= 1000000; break; case 6: d = p1 / 100000; p1 %= 100000; break; case 5: d = p1 / 10000; p1 %= 10000; break; case 4: d = p1 / 1000; p1 %= 1000; break; case 3: d = p1 / 100; p1 %= 100; break; case 2: d = p1 / 10; p1 %= 10; break; case 1: d = p1; p1 = 0; break; default:; } if (d || *len) buffer[(*len)++] = static_cast('0' + static_cast(d)); kappa--; uint64_t tmp = (static_cast(p1) << -one.e) + p2; if (tmp <= delta) { *K += kappa; GrisuRound(buffer, *len, delta, tmp, static_cast(kPow10[kappa]) << -one.e, wp_w.f); return; } } // kappa = 0 for (;;) { p2 *= 10; delta *= 10; char d = static_cast(p2 >> -one.e); if (d || *len) buffer[(*len)++] = static_cast('0' + d); p2 &= one.f - 1; kappa--; if (p2 < delta) { *K += kappa; int index = -kappa; GrisuRound(buffer, *len, delta, p2, one.f, wp_w.f * (index < 9 ? kPow10[index] : 0)); return; } } } inline void Grisu2(double value, char* buffer, int* length, int* K) { const DiyFp v(value); DiyFp w_m, w_p; v.NormalizedBoundaries(&w_m, &w_p); const DiyFp c_mk = GetCachedPower(w_p.e, K); const DiyFp W = v.Normalize() * c_mk; DiyFp Wp = w_p * c_mk; DiyFp Wm = w_m * c_mk; Wm.f++; Wp.f--; DigitGen(W, Wp, Wp.f - Wm.f, buffer, length, K); } inline char* WriteExponent(int K, char* buffer) { if (K < 0) { *buffer++ = '-'; K = -K; } if (K >= 100) { *buffer++ = static_cast('0' + static_cast(K / 100)); K %= 100; const char* d = GetDigitsLut() + K * 2; *buffer++ = d[0]; *buffer++ = d[1]; } else if (K >= 10) { const char* d = GetDigitsLut() + K * 2; *buffer++ = d[0]; *buffer++ = d[1]; } else *buffer++ = static_cast('0' + static_cast(K)); return buffer; } inline char* Prettify(char* buffer, int length, int k, int maxDecimalPlaces) { const int kk = length + k; // 10^(kk-1) <= v < 10^kk if (0 <= k && kk <= 21) { // 1234e7 -> 12340000000 for (int i = length; i < kk; i++) buffer[i] = '0'; buffer[kk] = '.'; buffer[kk + 1] = '0'; return &buffer[kk + 2]; } else if (0 < kk && kk <= 21) { // 1234e-2 -> 12.34 std::memmove(&buffer[kk + 1], &buffer[kk], static_cast(length - kk)); buffer[kk] = '.'; if (0 > k + maxDecimalPlaces) { // When maxDecimalPlaces = 2, 1.2345 -> 1.23, 1.102 -> 1.1 // Remove extra trailing zeros (at least one) after truncation. for (int i = kk + maxDecimalPlaces; i > kk + 1; i--) if (buffer[i] != '0') return &buffer[i + 1]; return &buffer[kk + 2]; // Reserve one zero } else return &buffer[length + 1]; } else if (-6 < kk && kk <= 0) { // 1234e-6 -> 0.001234 const int offset = 2 - kk; std::memmove(&buffer[offset], &buffer[0], static_cast(length)); buffer[0] = '0'; buffer[1] = '.'; for (int i = 2; i < offset; i++) buffer[i] = '0'; if (length - kk > maxDecimalPlaces) { // When maxDecimalPlaces = 2, 0.123 -> 0.12, 0.102 -> 0.1 // Remove extra trailing zeros (at least one) after truncation. for (int i = maxDecimalPlaces + 1; i > 2; i--) if (buffer[i] != '0') return &buffer[i + 1]; return &buffer[3]; // Reserve one zero } else return &buffer[length + offset]; } else if (kk < -maxDecimalPlaces) { // Truncate to zero buffer[0] = '0'; buffer[1] = '.'; buffer[2] = '0'; return &buffer[3]; } else if (length == 1) { // 1e30 buffer[1] = 'e'; return WriteExponent(kk - 1, &buffer[2]); } else { // 1234e30 -> 1.234e33 std::memmove(&buffer[2], &buffer[1], static_cast(length - 1)); buffer[1] = '.'; buffer[length + 1] = 'e'; return WriteExponent(kk - 1, &buffer[0 + length + 2]); } } inline char* dtoa(double value, char* buffer, int maxDecimalPlaces = 324) { RAPIDJSON_ASSERT(maxDecimalPlaces >= 1); Double d(value); if (d.IsZero()) { if (d.Sign()) *buffer++ = '-'; // -0.0, Issue #289 buffer[0] = '0'; buffer[1] = '.'; buffer[2] = '0'; return &buffer[3]; } else { if (value < 0) { *buffer++ = '-'; value = -value; } int length, K; Grisu2(value, buffer, &length, &K); return Prettify(buffer, length, K, maxDecimalPlaces); } } #ifdef __GNUC__ RAPIDJSON_DIAG_POP #endif } // namespace internal RAPIDJSON_NAMESPACE_END #endif // RAPIDJSON_DTOA_ ================================================ FILE: src/json/rapidjson/internal/ieee754.h ================================================ // Tencent is pleased to support the open source community by making RapidJSON available. // // Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at // // http://opensource.org/licenses/MIT // // Unless required by applicable law or agreed to in writing, software distributed // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. #ifndef RAPIDJSON_IEEE754_ #define RAPIDJSON_IEEE754_ #include "src/json/rapidjson/rapidjson.h" RAPIDJSON_NAMESPACE_BEGIN namespace internal { class Double { public: Double() {} Double(double d) : d_(d) {} Double(uint64_t u) : u_(u) {} double Value() const { return d_; } uint64_t Uint64Value() const { return u_; } double NextPositiveDouble() const { RAPIDJSON_ASSERT(!Sign()); return Double(u_ + 1).Value(); } bool Sign() const { return (u_ & kSignMask) != 0; } uint64_t Significand() const { return u_ & kSignificandMask; } int Exponent() const { return static_cast(((u_ & kExponentMask) >> kSignificandSize) - kExponentBias); } bool IsNan() const { return (u_ & kExponentMask) == kExponentMask && Significand() != 0; } bool IsInf() const { return (u_ & kExponentMask) == kExponentMask && Significand() == 0; } bool IsNanOrInf() const { return (u_ & kExponentMask) == kExponentMask; } bool IsNormal() const { return (u_ & kExponentMask) != 0 || Significand() == 0; } bool IsZero() const { return (u_ & (kExponentMask | kSignificandMask)) == 0; } uint64_t IntegerSignificand() const { return IsNormal() ? Significand() | kHiddenBit : Significand(); } int IntegerExponent() const { return (IsNormal() ? Exponent() : kDenormalExponent) - kSignificandSize; } uint64_t ToBias() const { return (u_ & kSignMask) ? ~u_ + 1 : u_ | kSignMask; } static int EffectiveSignificandSize(int order) { if (order >= -1021) return 53; else if (order <= -1074) return 0; else return order + 1074; } private: static const int kSignificandSize = 52; static const int kExponentBias = 0x3FF; static const int kDenormalExponent = 1 - kExponentBias; static const uint64_t kSignMask = RAPIDJSON_UINT64_C2(0x80000000, 0x00000000); static const uint64_t kExponentMask = RAPIDJSON_UINT64_C2(0x7FF00000, 0x00000000); static const uint64_t kSignificandMask = RAPIDJSON_UINT64_C2(0x000FFFFF, 0xFFFFFFFF); static const uint64_t kHiddenBit = RAPIDJSON_UINT64_C2(0x00100000, 0x00000000); union { double d_; uint64_t u_; }; }; } // namespace internal RAPIDJSON_NAMESPACE_END #endif // RAPIDJSON_IEEE754_ ================================================ FILE: src/json/rapidjson/internal/itoa.h ================================================ // Tencent is pleased to support the open source community by making RapidJSON available. // // Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at // // http://opensource.org/licenses/MIT // // Unless required by applicable law or agreed to in writing, software distributed // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. #ifndef RAPIDJSON_ITOA_ #define RAPIDJSON_ITOA_ #include "src/json/rapidjson/rapidjson.h" RAPIDJSON_NAMESPACE_BEGIN namespace internal { inline const char* GetDigitsLut() { static const char cDigitsLut[200] = { '0','0','0','1','0','2','0','3','0','4','0','5','0','6','0','7','0','8','0','9', '1','0','1','1','1','2','1','3','1','4','1','5','1','6','1','7','1','8','1','9', '2','0','2','1','2','2','2','3','2','4','2','5','2','6','2','7','2','8','2','9', '3','0','3','1','3','2','3','3','3','4','3','5','3','6','3','7','3','8','3','9', '4','0','4','1','4','2','4','3','4','4','4','5','4','6','4','7','4','8','4','9', '5','0','5','1','5','2','5','3','5','4','5','5','5','6','5','7','5','8','5','9', '6','0','6','1','6','2','6','3','6','4','6','5','6','6','6','7','6','8','6','9', '7','0','7','1','7','2','7','3','7','4','7','5','7','6','7','7','7','8','7','9', '8','0','8','1','8','2','8','3','8','4','8','5','8','6','8','7','8','8','8','9', '9','0','9','1','9','2','9','3','9','4','9','5','9','6','9','7','9','8','9','9' }; return cDigitsLut; } inline char* u32toa(uint32_t value, char* buffer) { RAPIDJSON_ASSERT(buffer != 0); const char* cDigitsLut = GetDigitsLut(); if (value < 10000) { const uint32_t d1 = (value / 100) << 1; const uint32_t d2 = (value % 100) << 1; if (value >= 1000) *buffer++ = cDigitsLut[d1]; if (value >= 100) *buffer++ = cDigitsLut[d1 + 1]; if (value >= 10) *buffer++ = cDigitsLut[d2]; *buffer++ = cDigitsLut[d2 + 1]; } else if (value < 100000000) { // value = bbbbcccc const uint32_t b = value / 10000; const uint32_t c = value % 10000; const uint32_t d1 = (b / 100) << 1; const uint32_t d2 = (b % 100) << 1; const uint32_t d3 = (c / 100) << 1; const uint32_t d4 = (c % 100) << 1; if (value >= 10000000) *buffer++ = cDigitsLut[d1]; if (value >= 1000000) *buffer++ = cDigitsLut[d1 + 1]; if (value >= 100000) *buffer++ = cDigitsLut[d2]; *buffer++ = cDigitsLut[d2 + 1]; *buffer++ = cDigitsLut[d3]; *buffer++ = cDigitsLut[d3 + 1]; *buffer++ = cDigitsLut[d4]; *buffer++ = cDigitsLut[d4 + 1]; } else { // value = aabbbbcccc in decimal const uint32_t a = value / 100000000; // 1 to 42 value %= 100000000; if (a >= 10) { const unsigned i = a << 1; *buffer++ = cDigitsLut[i]; *buffer++ = cDigitsLut[i + 1]; } else *buffer++ = static_cast('0' + static_cast(a)); const uint32_t b = value / 10000; // 0 to 9999 const uint32_t c = value % 10000; // 0 to 9999 const uint32_t d1 = (b / 100) << 1; const uint32_t d2 = (b % 100) << 1; const uint32_t d3 = (c / 100) << 1; const uint32_t d4 = (c % 100) << 1; *buffer++ = cDigitsLut[d1]; *buffer++ = cDigitsLut[d1 + 1]; *buffer++ = cDigitsLut[d2]; *buffer++ = cDigitsLut[d2 + 1]; *buffer++ = cDigitsLut[d3]; *buffer++ = cDigitsLut[d3 + 1]; *buffer++ = cDigitsLut[d4]; *buffer++ = cDigitsLut[d4 + 1]; } return buffer; } inline char* i32toa(int32_t value, char* buffer) { RAPIDJSON_ASSERT(buffer != 0); uint32_t u = static_cast(value); if (value < 0) { *buffer++ = '-'; u = ~u + 1; } return u32toa(u, buffer); } inline char* u64toa(uint64_t value, char* buffer) { RAPIDJSON_ASSERT(buffer != 0); const char* cDigitsLut = GetDigitsLut(); const uint64_t kTen8 = 100000000; const uint64_t kTen9 = kTen8 * 10; const uint64_t kTen10 = kTen8 * 100; const uint64_t kTen11 = kTen8 * 1000; const uint64_t kTen12 = kTen8 * 10000; const uint64_t kTen13 = kTen8 * 100000; const uint64_t kTen14 = kTen8 * 1000000; const uint64_t kTen15 = kTen8 * 10000000; const uint64_t kTen16 = kTen8 * kTen8; if (value < kTen8) { uint32_t v = static_cast(value); if (v < 10000) { const uint32_t d1 = (v / 100) << 1; const uint32_t d2 = (v % 100) << 1; if (v >= 1000) *buffer++ = cDigitsLut[d1]; if (v >= 100) *buffer++ = cDigitsLut[d1 + 1]; if (v >= 10) *buffer++ = cDigitsLut[d2]; *buffer++ = cDigitsLut[d2 + 1]; } else { // value = bbbbcccc const uint32_t b = v / 10000; const uint32_t c = v % 10000; const uint32_t d1 = (b / 100) << 1; const uint32_t d2 = (b % 100) << 1; const uint32_t d3 = (c / 100) << 1; const uint32_t d4 = (c % 100) << 1; if (value >= 10000000) *buffer++ = cDigitsLut[d1]; if (value >= 1000000) *buffer++ = cDigitsLut[d1 + 1]; if (value >= 100000) *buffer++ = cDigitsLut[d2]; *buffer++ = cDigitsLut[d2 + 1]; *buffer++ = cDigitsLut[d3]; *buffer++ = cDigitsLut[d3 + 1]; *buffer++ = cDigitsLut[d4]; *buffer++ = cDigitsLut[d4 + 1]; } } else if (value < kTen16) { const uint32_t v0 = static_cast(value / kTen8); const uint32_t v1 = static_cast(value % kTen8); const uint32_t b0 = v0 / 10000; const uint32_t c0 = v0 % 10000; const uint32_t d1 = (b0 / 100) << 1; const uint32_t d2 = (b0 % 100) << 1; const uint32_t d3 = (c0 / 100) << 1; const uint32_t d4 = (c0 % 100) << 1; const uint32_t b1 = v1 / 10000; const uint32_t c1 = v1 % 10000; const uint32_t d5 = (b1 / 100) << 1; const uint32_t d6 = (b1 % 100) << 1; const uint32_t d7 = (c1 / 100) << 1; const uint32_t d8 = (c1 % 100) << 1; if (value >= kTen15) *buffer++ = cDigitsLut[d1]; if (value >= kTen14) *buffer++ = cDigitsLut[d1 + 1]; if (value >= kTen13) *buffer++ = cDigitsLut[d2]; if (value >= kTen12) *buffer++ = cDigitsLut[d2 + 1]; if (value >= kTen11) *buffer++ = cDigitsLut[d3]; if (value >= kTen10) *buffer++ = cDigitsLut[d3 + 1]; if (value >= kTen9) *buffer++ = cDigitsLut[d4]; *buffer++ = cDigitsLut[d4 + 1]; *buffer++ = cDigitsLut[d5]; *buffer++ = cDigitsLut[d5 + 1]; *buffer++ = cDigitsLut[d6]; *buffer++ = cDigitsLut[d6 + 1]; *buffer++ = cDigitsLut[d7]; *buffer++ = cDigitsLut[d7 + 1]; *buffer++ = cDigitsLut[d8]; *buffer++ = cDigitsLut[d8 + 1]; } else { const uint32_t a = static_cast(value / kTen16); // 1 to 1844 value %= kTen16; if (a < 10) *buffer++ = static_cast('0' + static_cast(a)); else if (a < 100) { const uint32_t i = a << 1; *buffer++ = cDigitsLut[i]; *buffer++ = cDigitsLut[i + 1]; } else if (a < 1000) { *buffer++ = static_cast('0' + static_cast(a / 100)); const uint32_t i = (a % 100) << 1; *buffer++ = cDigitsLut[i]; *buffer++ = cDigitsLut[i + 1]; } else { const uint32_t i = (a / 100) << 1; const uint32_t j = (a % 100) << 1; *buffer++ = cDigitsLut[i]; *buffer++ = cDigitsLut[i + 1]; *buffer++ = cDigitsLut[j]; *buffer++ = cDigitsLut[j + 1]; } const uint32_t v0 = static_cast(value / kTen8); const uint32_t v1 = static_cast(value % kTen8); const uint32_t b0 = v0 / 10000; const uint32_t c0 = v0 % 10000; const uint32_t d1 = (b0 / 100) << 1; const uint32_t d2 = (b0 % 100) << 1; const uint32_t d3 = (c0 / 100) << 1; const uint32_t d4 = (c0 % 100) << 1; const uint32_t b1 = v1 / 10000; const uint32_t c1 = v1 % 10000; const uint32_t d5 = (b1 / 100) << 1; const uint32_t d6 = (b1 % 100) << 1; const uint32_t d7 = (c1 / 100) << 1; const uint32_t d8 = (c1 % 100) << 1; *buffer++ = cDigitsLut[d1]; *buffer++ = cDigitsLut[d1 + 1]; *buffer++ = cDigitsLut[d2]; *buffer++ = cDigitsLut[d2 + 1]; *buffer++ = cDigitsLut[d3]; *buffer++ = cDigitsLut[d3 + 1]; *buffer++ = cDigitsLut[d4]; *buffer++ = cDigitsLut[d4 + 1]; *buffer++ = cDigitsLut[d5]; *buffer++ = cDigitsLut[d5 + 1]; *buffer++ = cDigitsLut[d6]; *buffer++ = cDigitsLut[d6 + 1]; *buffer++ = cDigitsLut[d7]; *buffer++ = cDigitsLut[d7 + 1]; *buffer++ = cDigitsLut[d8]; *buffer++ = cDigitsLut[d8 + 1]; } return buffer; } inline char* i64toa(int64_t value, char* buffer) { RAPIDJSON_ASSERT(buffer != 0); uint64_t u = static_cast(value); if (value < 0) { *buffer++ = '-'; u = ~u + 1; } return u64toa(u, buffer); } } // namespace internal RAPIDJSON_NAMESPACE_END #endif // RAPIDJSON_ITOA_ ================================================ FILE: src/json/rapidjson/internal/meta.h ================================================ // Tencent is pleased to support the open source community by making RapidJSON available. // // Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at // // http://opensource.org/licenses/MIT // // Unless required by applicable law or agreed to in writing, software distributed // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. #ifndef RAPIDJSON_INTERNAL_META_H_ #define RAPIDJSON_INTERNAL_META_H_ #include "src/json/rapidjson/rapidjson.h" #ifdef __GNUC__ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(effc++) #endif #if defined(_MSC_VER) && !defined(__clang__) RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(6334) #endif #if RAPIDJSON_HAS_CXX11_TYPETRAITS #include #endif //@cond RAPIDJSON_INTERNAL RAPIDJSON_NAMESPACE_BEGIN namespace internal { // Helper to wrap/convert arbitrary types to void, useful for arbitrary type matching template struct Void { typedef void Type; }; /////////////////////////////////////////////////////////////////////////////// // BoolType, TrueType, FalseType // template struct BoolType { static const bool Value = Cond; typedef BoolType Type; }; typedef BoolType TrueType; typedef BoolType FalseType; /////////////////////////////////////////////////////////////////////////////// // SelectIf, BoolExpr, NotExpr, AndExpr, OrExpr // template struct SelectIfImpl { template struct Apply { typedef T1 Type; }; }; template <> struct SelectIfImpl { template struct Apply { typedef T2 Type; }; }; template struct SelectIfCond : SelectIfImpl::template Apply {}; template struct SelectIf : SelectIfCond {}; template struct AndExprCond : FalseType {}; template <> struct AndExprCond : TrueType {}; template struct OrExprCond : TrueType {}; template <> struct OrExprCond : FalseType {}; template struct BoolExpr : SelectIf::Type {}; template struct NotExpr : SelectIf::Type {}; template struct AndExpr : AndExprCond::Type {}; template struct OrExpr : OrExprCond::Type {}; /////////////////////////////////////////////////////////////////////////////// // AddConst, MaybeAddConst, RemoveConst template struct AddConst { typedef const T Type; }; template struct MaybeAddConst : SelectIfCond {}; template struct RemoveConst { typedef T Type; }; template struct RemoveConst { typedef T Type; }; /////////////////////////////////////////////////////////////////////////////// // IsSame, IsConst, IsMoreConst, IsPointer // template struct IsSame : FalseType {}; template struct IsSame : TrueType {}; template struct IsConst : FalseType {}; template struct IsConst : TrueType {}; template struct IsMoreConst : AndExpr::Type, typename RemoveConst::Type>, BoolType::Value >= IsConst::Value> >::Type {}; template struct IsPointer : FalseType {}; template struct IsPointer : TrueType {}; /////////////////////////////////////////////////////////////////////////////// // IsBaseOf // #if RAPIDJSON_HAS_CXX11_TYPETRAITS template struct IsBaseOf : BoolType< ::std::is_base_of::value> {}; #else // simplified version adopted from Boost template struct IsBaseOfImpl { RAPIDJSON_STATIC_ASSERT(sizeof(B) != 0); RAPIDJSON_STATIC_ASSERT(sizeof(D) != 0); typedef char (&Yes)[1]; typedef char (&No) [2]; template static Yes Check(const D*, T); static No Check(const B*, int); struct Host { operator const B*() const; operator const D*(); }; enum { Value = (sizeof(Check(Host(), 0)) == sizeof(Yes)) }; }; template struct IsBaseOf : OrExpr, BoolExpr > >::Type {}; #endif // RAPIDJSON_HAS_CXX11_TYPETRAITS ////////////////////////////////////////////////////////////////////////// // EnableIf / DisableIf // template struct EnableIfCond { typedef T Type; }; template struct EnableIfCond { /* empty */ }; template struct DisableIfCond { typedef T Type; }; template struct DisableIfCond { /* empty */ }; template struct EnableIf : EnableIfCond {}; template struct DisableIf : DisableIfCond {}; // SFINAE helpers struct SfinaeTag {}; template struct RemoveSfinaeTag; template struct RemoveSfinaeTag { typedef T Type; }; #define RAPIDJSON_REMOVEFPTR_(type) \ typename ::RAPIDJSON_NAMESPACE::internal::RemoveSfinaeTag \ < ::RAPIDJSON_NAMESPACE::internal::SfinaeTag&(*) type>::Type #define RAPIDJSON_ENABLEIF(cond) \ typename ::RAPIDJSON_NAMESPACE::internal::EnableIf \ ::Type * = NULL #define RAPIDJSON_DISABLEIF(cond) \ typename ::RAPIDJSON_NAMESPACE::internal::DisableIf \ ::Type * = NULL #define RAPIDJSON_ENABLEIF_RETURN(cond,returntype) \ typename ::RAPIDJSON_NAMESPACE::internal::EnableIf \ ::Type #define RAPIDJSON_DISABLEIF_RETURN(cond,returntype) \ typename ::RAPIDJSON_NAMESPACE::internal::DisableIf \ ::Type } // namespace internal RAPIDJSON_NAMESPACE_END //@endcond #if defined(_MSC_VER) && !defined(__clang__) RAPIDJSON_DIAG_POP #endif #ifdef __GNUC__ RAPIDJSON_DIAG_POP #endif #endif // RAPIDJSON_INTERNAL_META_H_ ================================================ FILE: src/json/rapidjson/internal/pow10.h ================================================ // Tencent is pleased to support the open source community by making RapidJSON available. // // Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at // // http://opensource.org/licenses/MIT // // Unless required by applicable law or agreed to in writing, software distributed // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. #ifndef RAPIDJSON_POW10_ #define RAPIDJSON_POW10_ #include "src/json/rapidjson/rapidjson.h" RAPIDJSON_NAMESPACE_BEGIN namespace internal { //! Computes integer powers of 10 in double (10.0^n). /*! This function uses lookup table for fast and accurate results. \param n non-negative exponent. Must <= 308. \return 10.0^n */ inline double Pow10(int n) { static const double e[] = { // 1e-0...1e308: 309 * 8 bytes = 2472 bytes 1e+0, 1e+1, 1e+2, 1e+3, 1e+4, 1e+5, 1e+6, 1e+7, 1e+8, 1e+9, 1e+10, 1e+11, 1e+12, 1e+13, 1e+14, 1e+15, 1e+16, 1e+17, 1e+18, 1e+19, 1e+20, 1e+21, 1e+22, 1e+23, 1e+24, 1e+25, 1e+26, 1e+27, 1e+28, 1e+29, 1e+30, 1e+31, 1e+32, 1e+33, 1e+34, 1e+35, 1e+36, 1e+37, 1e+38, 1e+39, 1e+40, 1e+41, 1e+42, 1e+43, 1e+44, 1e+45, 1e+46, 1e+47, 1e+48, 1e+49, 1e+50, 1e+51, 1e+52, 1e+53, 1e+54, 1e+55, 1e+56, 1e+57, 1e+58, 1e+59, 1e+60, 1e+61, 1e+62, 1e+63, 1e+64, 1e+65, 1e+66, 1e+67, 1e+68, 1e+69, 1e+70, 1e+71, 1e+72, 1e+73, 1e+74, 1e+75, 1e+76, 1e+77, 1e+78, 1e+79, 1e+80, 1e+81, 1e+82, 1e+83, 1e+84, 1e+85, 1e+86, 1e+87, 1e+88, 1e+89, 1e+90, 1e+91, 1e+92, 1e+93, 1e+94, 1e+95, 1e+96, 1e+97, 1e+98, 1e+99, 1e+100, 1e+101,1e+102,1e+103,1e+104,1e+105,1e+106,1e+107,1e+108,1e+109,1e+110,1e+111,1e+112,1e+113,1e+114,1e+115,1e+116,1e+117,1e+118,1e+119,1e+120, 1e+121,1e+122,1e+123,1e+124,1e+125,1e+126,1e+127,1e+128,1e+129,1e+130,1e+131,1e+132,1e+133,1e+134,1e+135,1e+136,1e+137,1e+138,1e+139,1e+140, 1e+141,1e+142,1e+143,1e+144,1e+145,1e+146,1e+147,1e+148,1e+149,1e+150,1e+151,1e+152,1e+153,1e+154,1e+155,1e+156,1e+157,1e+158,1e+159,1e+160, 1e+161,1e+162,1e+163,1e+164,1e+165,1e+166,1e+167,1e+168,1e+169,1e+170,1e+171,1e+172,1e+173,1e+174,1e+175,1e+176,1e+177,1e+178,1e+179,1e+180, 1e+181,1e+182,1e+183,1e+184,1e+185,1e+186,1e+187,1e+188,1e+189,1e+190,1e+191,1e+192,1e+193,1e+194,1e+195,1e+196,1e+197,1e+198,1e+199,1e+200, 1e+201,1e+202,1e+203,1e+204,1e+205,1e+206,1e+207,1e+208,1e+209,1e+210,1e+211,1e+212,1e+213,1e+214,1e+215,1e+216,1e+217,1e+218,1e+219,1e+220, 1e+221,1e+222,1e+223,1e+224,1e+225,1e+226,1e+227,1e+228,1e+229,1e+230,1e+231,1e+232,1e+233,1e+234,1e+235,1e+236,1e+237,1e+238,1e+239,1e+240, 1e+241,1e+242,1e+243,1e+244,1e+245,1e+246,1e+247,1e+248,1e+249,1e+250,1e+251,1e+252,1e+253,1e+254,1e+255,1e+256,1e+257,1e+258,1e+259,1e+260, 1e+261,1e+262,1e+263,1e+264,1e+265,1e+266,1e+267,1e+268,1e+269,1e+270,1e+271,1e+272,1e+273,1e+274,1e+275,1e+276,1e+277,1e+278,1e+279,1e+280, 1e+281,1e+282,1e+283,1e+284,1e+285,1e+286,1e+287,1e+288,1e+289,1e+290,1e+291,1e+292,1e+293,1e+294,1e+295,1e+296,1e+297,1e+298,1e+299,1e+300, 1e+301,1e+302,1e+303,1e+304,1e+305,1e+306,1e+307,1e+308 }; RAPIDJSON_ASSERT(n >= 0 && n <= 308); return e[n]; } } // namespace internal RAPIDJSON_NAMESPACE_END #endif // RAPIDJSON_POW10_ ================================================ FILE: src/json/rapidjson/internal/regex.h ================================================ // Tencent is pleased to support the open source community by making RapidJSON available. // // Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at // // http://opensource.org/licenses/MIT // // Unless required by applicable law or agreed to in writing, software distributed // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. #ifndef RAPIDJSON_INTERNAL_REGEX_H_ #define RAPIDJSON_INTERNAL_REGEX_H_ #include "src/json/rapidjson/allocators.h" #include "src/json/rapidjson/stream.h" #include "stack.h" #ifdef __clang__ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(padded) RAPIDJSON_DIAG_OFF(switch-enum) RAPIDJSON_DIAG_OFF(implicit-fallthrough) #elif defined(_MSC_VER) RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated #endif #ifdef __GNUC__ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(effc++) #if __GNUC__ >= 7 RAPIDJSON_DIAG_OFF(implicit-fallthrough) #endif #endif #ifndef RAPIDJSON_REGEX_VERBOSE #define RAPIDJSON_REGEX_VERBOSE 0 #endif RAPIDJSON_NAMESPACE_BEGIN namespace internal { /////////////////////////////////////////////////////////////////////////////// // DecodedStream template class DecodedStream { public: DecodedStream(SourceStream& ss) : ss_(ss), codepoint_() { Decode(); } unsigned Peek() { return codepoint_; } unsigned Take() { unsigned c = codepoint_; if (c) // No further decoding when '\0' Decode(); return c; } private: void Decode() { if (!Encoding::Decode(ss_, &codepoint_)) codepoint_ = 0; } SourceStream& ss_; unsigned codepoint_; }; /////////////////////////////////////////////////////////////////////////////// // GenericRegex static const SizeType kRegexInvalidState = ~SizeType(0); //!< Represents an invalid index in GenericRegex::State::out, out1 static const SizeType kRegexInvalidRange = ~SizeType(0); template class GenericRegexSearch; //! Regular expression engine with subset of ECMAscript grammar. /*! Supported regular expression syntax: - \c ab Concatenation - \c a|b Alternation - \c a? Zero or one - \c a* Zero or more - \c a+ One or more - \c a{3} Exactly 3 times - \c a{3,} At least 3 times - \c a{3,5} 3 to 5 times - \c (ab) Grouping - \c ^a At the beginning - \c a$ At the end - \c . Any character - \c [abc] Character classes - \c [a-c] Character class range - \c [a-z0-9_] Character class combination - \c [^abc] Negated character classes - \c [^a-c] Negated character class range - \c [\b] Backspace (U+0008) - \c \\| \\\\ ... Escape characters - \c \\f Form feed (U+000C) - \c \\n Line feed (U+000A) - \c \\r Carriage return (U+000D) - \c \\t Tab (U+0009) - \c \\v Vertical tab (U+000B) \note This is a Thompson NFA engine, implemented with reference to Cox, Russ. "Regular Expression Matching Can Be Simple And Fast (but is slow in Java, Perl, PHP, Python, Ruby,...).", https://swtch.com/~rsc/regexp/regexp1.html */ template class GenericRegex { public: typedef Encoding EncodingType; typedef typename Encoding::Ch Ch; template friend class GenericRegexSearch; GenericRegex(const Ch* source, Allocator* allocator = 0) : ownAllocator_(allocator ? 0 : RAPIDJSON_NEW(Allocator)()), allocator_(allocator ? allocator : ownAllocator_), states_(allocator_, 256), ranges_(allocator_, 256), root_(kRegexInvalidState), stateCount_(), rangeCount_(), anchorBegin_(), anchorEnd_() { GenericStringStream ss(source); DecodedStream, Encoding> ds(ss); Parse(ds); } ~GenericRegex() { RAPIDJSON_DELETE(ownAllocator_); } bool IsValid() const { return root_ != kRegexInvalidState; } private: enum Operator { kZeroOrOne, kZeroOrMore, kOneOrMore, kConcatenation, kAlternation, kLeftParenthesis }; static const unsigned kAnyCharacterClass = 0xFFFFFFFF; //!< For '.' static const unsigned kRangeCharacterClass = 0xFFFFFFFE; static const unsigned kRangeNegationFlag = 0x80000000; struct Range { unsigned start; // unsigned end; SizeType next; }; struct State { SizeType out; //!< Equals to kInvalid for matching state SizeType out1; //!< Equals to non-kInvalid for split SizeType rangeStart; unsigned codepoint; }; struct Frag { Frag(SizeType s, SizeType o, SizeType m) : start(s), out(o), minIndex(m) {} SizeType start; SizeType out; //!< link-list of all output states SizeType minIndex; }; State& GetState(SizeType index) { RAPIDJSON_ASSERT(index < stateCount_); return states_.template Bottom()[index]; } const State& GetState(SizeType index) const { RAPIDJSON_ASSERT(index < stateCount_); return states_.template Bottom()[index]; } Range& GetRange(SizeType index) { RAPIDJSON_ASSERT(index < rangeCount_); return ranges_.template Bottom()[index]; } const Range& GetRange(SizeType index) const { RAPIDJSON_ASSERT(index < rangeCount_); return ranges_.template Bottom()[index]; } template void Parse(DecodedStream& ds) { Stack operandStack(allocator_, 256); // Frag Stack operatorStack(allocator_, 256); // Operator Stack atomCountStack(allocator_, 256); // unsigned (Atom per parenthesis) *atomCountStack.template Push() = 0; unsigned codepoint; while (ds.Peek() != 0) { switch (codepoint = ds.Take()) { case '^': anchorBegin_ = true; break; case '$': anchorEnd_ = true; break; case '|': while (!operatorStack.Empty() && *operatorStack.template Top() < kAlternation) if (!Eval(operandStack, *operatorStack.template Pop(1))) return; *operatorStack.template Push() = kAlternation; *atomCountStack.template Top() = 0; break; case '(': *operatorStack.template Push() = kLeftParenthesis; *atomCountStack.template Push() = 0; break; case ')': while (!operatorStack.Empty() && *operatorStack.template Top() != kLeftParenthesis) if (!Eval(operandStack, *operatorStack.template Pop(1))) return; if (operatorStack.Empty()) return; operatorStack.template Pop(1); atomCountStack.template Pop(1); ImplicitConcatenation(atomCountStack, operatorStack); break; case '?': if (!Eval(operandStack, kZeroOrOne)) return; break; case '*': if (!Eval(operandStack, kZeroOrMore)) return; break; case '+': if (!Eval(operandStack, kOneOrMore)) return; break; case '{': { unsigned n, m; if (!ParseUnsigned(ds, &n)) return; if (ds.Peek() == ',') { ds.Take(); if (ds.Peek() == '}') m = kInfinityQuantifier; else if (!ParseUnsigned(ds, &m) || m < n) return; } else m = n; if (!EvalQuantifier(operandStack, n, m) || ds.Peek() != '}') return; ds.Take(); } break; case '.': PushOperand(operandStack, kAnyCharacterClass); ImplicitConcatenation(atomCountStack, operatorStack); break; case '[': { SizeType range; if (!ParseRange(ds, &range)) return; SizeType s = NewState(kRegexInvalidState, kRegexInvalidState, kRangeCharacterClass); GetState(s).rangeStart = range; *operandStack.template Push() = Frag(s, s, s); } ImplicitConcatenation(atomCountStack, operatorStack); break; case '\\': // Escape character if (!CharacterEscape(ds, &codepoint)) return; // Unsupported escape character // fall through to default default: // Pattern character PushOperand(operandStack, codepoint); ImplicitConcatenation(atomCountStack, operatorStack); } } while (!operatorStack.Empty()) if (!Eval(operandStack, *operatorStack.template Pop(1))) return; // Link the operand to matching state. if (operandStack.GetSize() == sizeof(Frag)) { Frag* e = operandStack.template Pop(1); Patch(e->out, NewState(kRegexInvalidState, kRegexInvalidState, 0)); root_ = e->start; #if RAPIDJSON_REGEX_VERBOSE printf("root: %d\n", root_); for (SizeType i = 0; i < stateCount_ ; i++) { State& s = GetState(i); printf("[%2d] out: %2d out1: %2d c: '%c'\n", i, s.out, s.out1, (char)s.codepoint); } printf("\n"); #endif } } SizeType NewState(SizeType out, SizeType out1, unsigned codepoint) { State* s = states_.template Push(); s->out = out; s->out1 = out1; s->codepoint = codepoint; s->rangeStart = kRegexInvalidRange; return stateCount_++; } void PushOperand(Stack& operandStack, unsigned codepoint) { SizeType s = NewState(kRegexInvalidState, kRegexInvalidState, codepoint); *operandStack.template Push() = Frag(s, s, s); } void ImplicitConcatenation(Stack& atomCountStack, Stack& operatorStack) { if (*atomCountStack.template Top()) *operatorStack.template Push() = kConcatenation; (*atomCountStack.template Top())++; } SizeType Append(SizeType l1, SizeType l2) { SizeType old = l1; while (GetState(l1).out != kRegexInvalidState) l1 = GetState(l1).out; GetState(l1).out = l2; return old; } void Patch(SizeType l, SizeType s) { for (SizeType next; l != kRegexInvalidState; l = next) { next = GetState(l).out; GetState(l).out = s; } } bool Eval(Stack& operandStack, Operator op) { switch (op) { case kConcatenation: RAPIDJSON_ASSERT(operandStack.GetSize() >= sizeof(Frag) * 2); { Frag e2 = *operandStack.template Pop(1); Frag e1 = *operandStack.template Pop(1); Patch(e1.out, e2.start); *operandStack.template Push() = Frag(e1.start, e2.out, Min(e1.minIndex, e2.minIndex)); } return true; case kAlternation: if (operandStack.GetSize() >= sizeof(Frag) * 2) { Frag e2 = *operandStack.template Pop(1); Frag e1 = *operandStack.template Pop(1); SizeType s = NewState(e1.start, e2.start, 0); *operandStack.template Push() = Frag(s, Append(e1.out, e2.out), Min(e1.minIndex, e2.minIndex)); return true; } return false; case kZeroOrOne: if (operandStack.GetSize() >= sizeof(Frag)) { Frag e = *operandStack.template Pop(1); SizeType s = NewState(kRegexInvalidState, e.start, 0); *operandStack.template Push() = Frag(s, Append(e.out, s), e.minIndex); return true; } return false; case kZeroOrMore: if (operandStack.GetSize() >= sizeof(Frag)) { Frag e = *operandStack.template Pop(1); SizeType s = NewState(kRegexInvalidState, e.start, 0); Patch(e.out, s); *operandStack.template Push() = Frag(s, s, e.minIndex); return true; } return false; case kOneOrMore: if (operandStack.GetSize() >= sizeof(Frag)) { Frag e = *operandStack.template Pop(1); SizeType s = NewState(kRegexInvalidState, e.start, 0); Patch(e.out, s); *operandStack.template Push() = Frag(e.start, s, e.minIndex); return true; } return false; default: // syntax error (e.g. unclosed kLeftParenthesis) return false; } } bool EvalQuantifier(Stack& operandStack, unsigned n, unsigned m) { RAPIDJSON_ASSERT(n <= m); RAPIDJSON_ASSERT(operandStack.GetSize() >= sizeof(Frag)); if (n == 0) { if (m == 0) // a{0} not support return false; else if (m == kInfinityQuantifier) Eval(operandStack, kZeroOrMore); // a{0,} -> a* else { Eval(operandStack, kZeroOrOne); // a{0,5} -> a? for (unsigned i = 0; i < m - 1; i++) CloneTopOperand(operandStack); // a{0,5} -> a? a? a? a? a? for (unsigned i = 0; i < m - 1; i++) Eval(operandStack, kConcatenation); // a{0,5} -> a?a?a?a?a? } return true; } for (unsigned i = 0; i < n - 1; i++) // a{3} -> a a a CloneTopOperand(operandStack); if (m == kInfinityQuantifier) Eval(operandStack, kOneOrMore); // a{3,} -> a a a+ else if (m > n) { CloneTopOperand(operandStack); // a{3,5} -> a a a a Eval(operandStack, kZeroOrOne); // a{3,5} -> a a a a? for (unsigned i = n; i < m - 1; i++) CloneTopOperand(operandStack); // a{3,5} -> a a a a? a? for (unsigned i = n; i < m; i++) Eval(operandStack, kConcatenation); // a{3,5} -> a a aa?a? } for (unsigned i = 0; i < n - 1; i++) Eval(operandStack, kConcatenation); // a{3} -> aaa, a{3,} -> aaa+, a{3.5} -> aaaa?a? return true; } static SizeType Min(SizeType a, SizeType b) { return a < b ? a : b; } void CloneTopOperand(Stack& operandStack) { const Frag src = *operandStack.template Top(); // Copy constructor to prevent invalidation SizeType count = stateCount_ - src.minIndex; // Assumes top operand contains states in [src->minIndex, stateCount_) State* s = states_.template Push(count); memcpy(s, &GetState(src.minIndex), count * sizeof(State)); for (SizeType j = 0; j < count; j++) { if (s[j].out != kRegexInvalidState) s[j].out += count; if (s[j].out1 != kRegexInvalidState) s[j].out1 += count; } *operandStack.template Push() = Frag(src.start + count, src.out + count, src.minIndex + count); stateCount_ += count; } template bool ParseUnsigned(DecodedStream& ds, unsigned* u) { unsigned r = 0; if (ds.Peek() < '0' || ds.Peek() > '9') return false; while (ds.Peek() >= '0' && ds.Peek() <= '9') { if (r >= 429496729 && ds.Peek() > '5') // 2^32 - 1 = 4294967295 return false; // overflow r = r * 10 + (ds.Take() - '0'); } *u = r; return true; } template bool ParseRange(DecodedStream& ds, SizeType* range) { bool isBegin = true; bool negate = false; int step = 0; SizeType start = kRegexInvalidRange; SizeType current = kRegexInvalidRange; unsigned codepoint; while ((codepoint = ds.Take()) != 0) { if (isBegin) { isBegin = false; if (codepoint == '^') { negate = true; continue; } } switch (codepoint) { case ']': if (start == kRegexInvalidRange) return false; // Error: nothing inside [] if (step == 2) { // Add trailing '-' SizeType r = NewRange('-'); RAPIDJSON_ASSERT(current != kRegexInvalidRange); GetRange(current).next = r; } if (negate) GetRange(start).start |= kRangeNegationFlag; *range = start; return true; case '\\': if (ds.Peek() == 'b') { ds.Take(); codepoint = 0x0008; // Escape backspace character } else if (!CharacterEscape(ds, &codepoint)) return false; // fall through to default default: switch (step) { case 1: if (codepoint == '-') { step++; break; } // fall through to step 0 for other characters case 0: { SizeType r = NewRange(codepoint); if (current != kRegexInvalidRange) GetRange(current).next = r; if (start == kRegexInvalidRange) start = r; current = r; } step = 1; break; default: RAPIDJSON_ASSERT(step == 2); GetRange(current).end = codepoint; step = 0; } } } return false; } SizeType NewRange(unsigned codepoint) { Range* r = ranges_.template Push(); r->start = r->end = codepoint; r->next = kRegexInvalidRange; return rangeCount_++; } template bool CharacterEscape(DecodedStream& ds, unsigned* escapedCodepoint) { unsigned codepoint; switch (codepoint = ds.Take()) { case '^': case '$': case '|': case '(': case ')': case '?': case '*': case '+': case '.': case '[': case ']': case '{': case '}': case '\\': *escapedCodepoint = codepoint; return true; case 'f': *escapedCodepoint = 0x000C; return true; case 'n': *escapedCodepoint = 0x000A; return true; case 'r': *escapedCodepoint = 0x000D; return true; case 't': *escapedCodepoint = 0x0009; return true; case 'v': *escapedCodepoint = 0x000B; return true; default: return false; // Unsupported escape character } } Allocator* ownAllocator_; Allocator* allocator_; Stack states_; Stack ranges_; SizeType root_; SizeType stateCount_; SizeType rangeCount_; static const unsigned kInfinityQuantifier = ~0u; // For SearchWithAnchoring() bool anchorBegin_; bool anchorEnd_; }; template class GenericRegexSearch { public: typedef typename RegexType::EncodingType Encoding; typedef typename Encoding::Ch Ch; GenericRegexSearch(const RegexType& regex, Allocator* allocator = 0) : regex_(regex), allocator_(allocator), ownAllocator_(0), state0_(allocator, 0), state1_(allocator, 0), stateSet_() { RAPIDJSON_ASSERT(regex_.IsValid()); if (!allocator_) ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); stateSet_ = static_cast(allocator_->Malloc(GetStateSetSize())); state0_.template Reserve(regex_.stateCount_); state1_.template Reserve(regex_.stateCount_); } ~GenericRegexSearch() { Allocator::Free(stateSet_); RAPIDJSON_DELETE(ownAllocator_); } template bool Match(InputStream& is) { return SearchWithAnchoring(is, true, true); } bool Match(const Ch* s) { GenericStringStream is(s); return Match(is); } template bool Search(InputStream& is) { return SearchWithAnchoring(is, regex_.anchorBegin_, regex_.anchorEnd_); } bool Search(const Ch* s) { GenericStringStream is(s); return Search(is); } private: typedef typename RegexType::State State; typedef typename RegexType::Range Range; template bool SearchWithAnchoring(InputStream& is, bool anchorBegin, bool anchorEnd) { DecodedStream ds(is); state0_.Clear(); Stack *current = &state0_, *next = &state1_; const size_t stateSetSize = GetStateSetSize(); std::memset(stateSet_, 0, stateSetSize); bool matched = AddState(*current, regex_.root_); unsigned codepoint; while (!current->Empty() && (codepoint = ds.Take()) != 0) { std::memset(stateSet_, 0, stateSetSize); next->Clear(); matched = false; for (const SizeType* s = current->template Bottom(); s != current->template End(); ++s) { const State& sr = regex_.GetState(*s); if (sr.codepoint == codepoint || sr.codepoint == RegexType::kAnyCharacterClass || (sr.codepoint == RegexType::kRangeCharacterClass && MatchRange(sr.rangeStart, codepoint))) { matched = AddState(*next, sr.out) || matched; if (!anchorEnd && matched) return true; } if (!anchorBegin) AddState(*next, regex_.root_); } internal::Swap(current, next); } return matched; } size_t GetStateSetSize() const { return (regex_.stateCount_ + 31) / 32 * 4; } // Return whether the added states is a match state bool AddState(Stack& l, SizeType index) { RAPIDJSON_ASSERT(index != kRegexInvalidState); const State& s = regex_.GetState(index); if (s.out1 != kRegexInvalidState) { // Split bool matched = AddState(l, s.out); return AddState(l, s.out1) || matched; } else if (!(stateSet_[index >> 5] & (1u << (index & 31)))) { stateSet_[index >> 5] |= (1u << (index & 31)); *l.template PushUnsafe() = index; } return s.out == kRegexInvalidState; // by using PushUnsafe() above, we can ensure s is not validated due to reallocation. } bool MatchRange(SizeType rangeIndex, unsigned codepoint) const { bool yes = (regex_.GetRange(rangeIndex).start & RegexType::kRangeNegationFlag) == 0; while (rangeIndex != kRegexInvalidRange) { const Range& r = regex_.GetRange(rangeIndex); if (codepoint >= (r.start & ~RegexType::kRangeNegationFlag) && codepoint <= r.end) return yes; rangeIndex = r.next; } return !yes; } const RegexType& regex_; Allocator* allocator_; Allocator* ownAllocator_; Stack state0_; Stack state1_; uint32_t* stateSet_; }; typedef GenericRegex > Regex; typedef GenericRegexSearch RegexSearch; } // namespace internal RAPIDJSON_NAMESPACE_END #ifdef __GNUC__ RAPIDJSON_DIAG_POP #endif #if defined(__clang__) || defined(_MSC_VER) RAPIDJSON_DIAG_POP #endif #endif // RAPIDJSON_INTERNAL_REGEX_H_ ================================================ FILE: src/json/rapidjson/internal/stack.h ================================================ // Tencent is pleased to support the open source community by making RapidJSON available. // // Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at // // http://opensource.org/licenses/MIT // // Unless required by applicable law or agreed to in writing, software distributed // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. #ifndef RAPIDJSON_INTERNAL_STACK_H_ #define RAPIDJSON_INTERNAL_STACK_H_ #include "src/json/rapidjson/allocators.h" #include "swap.h" #include #if defined(__clang__) RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(c++98-compat) #endif RAPIDJSON_NAMESPACE_BEGIN namespace internal { /////////////////////////////////////////////////////////////////////////////// // Stack //! A type-unsafe stack for storing different types of data. /*! \tparam Allocator Allocator for allocating stack memory. */ template class Stack { public: // Optimization note: Do not allocate memory for stack_ in constructor. // Do it lazily when first Push() -> Expand() -> Resize(). Stack(Allocator* allocator, size_t stackCapacity) : allocator_(allocator), ownAllocator_(0), stack_(0), stackTop_(0), stackEnd_(0), initialCapacity_(stackCapacity) { } #if RAPIDJSON_HAS_CXX11_RVALUE_REFS Stack(Stack&& rhs) : allocator_(rhs.allocator_), ownAllocator_(rhs.ownAllocator_), stack_(rhs.stack_), stackTop_(rhs.stackTop_), stackEnd_(rhs.stackEnd_), initialCapacity_(rhs.initialCapacity_) { rhs.allocator_ = 0; rhs.ownAllocator_ = 0; rhs.stack_ = 0; rhs.stackTop_ = 0; rhs.stackEnd_ = 0; rhs.initialCapacity_ = 0; } #endif ~Stack() { Destroy(); } #if RAPIDJSON_HAS_CXX11_RVALUE_REFS Stack& operator=(Stack&& rhs) { if (&rhs != this) { Destroy(); allocator_ = rhs.allocator_; ownAllocator_ = rhs.ownAllocator_; stack_ = rhs.stack_; stackTop_ = rhs.stackTop_; stackEnd_ = rhs.stackEnd_; initialCapacity_ = rhs.initialCapacity_; rhs.allocator_ = 0; rhs.ownAllocator_ = 0; rhs.stack_ = 0; rhs.stackTop_ = 0; rhs.stackEnd_ = 0; rhs.initialCapacity_ = 0; } return *this; } #endif void Swap(Stack& rhs) RAPIDJSON_NOEXCEPT { internal::Swap(allocator_, rhs.allocator_); internal::Swap(ownAllocator_, rhs.ownAllocator_); internal::Swap(stack_, rhs.stack_); internal::Swap(stackTop_, rhs.stackTop_); internal::Swap(stackEnd_, rhs.stackEnd_); internal::Swap(initialCapacity_, rhs.initialCapacity_); } void Clear() { stackTop_ = stack_; } void ShrinkToFit() { if (Empty()) { // If the stack is empty, completely deallocate the memory. Allocator::Free(stack_); // NOLINT (+clang-analyzer-unix.Malloc) stack_ = 0; stackTop_ = 0; stackEnd_ = 0; } else Resize(GetSize()); } // Optimization note: try to minimize the size of this function for force inline. // Expansion is run very infrequently, so it is moved to another (probably non-inline) function. template RAPIDJSON_FORCEINLINE void Reserve(size_t count = 1) { // Expand the stack if needed if (RAPIDJSON_UNLIKELY(static_cast(sizeof(T) * count) > (stackEnd_ - stackTop_))) Expand(count); } template RAPIDJSON_FORCEINLINE T* Push(size_t count = 1) { Reserve(count); return PushUnsafe(count); } template RAPIDJSON_FORCEINLINE T* PushUnsafe(size_t count = 1) { RAPIDJSON_ASSERT(stackTop_); RAPIDJSON_ASSERT(static_cast(sizeof(T) * count) <= (stackEnd_ - stackTop_)); T* ret = reinterpret_cast(stackTop_); stackTop_ += sizeof(T) * count; return ret; } template T* Pop(size_t count) { RAPIDJSON_ASSERT(GetSize() >= count * sizeof(T)); stackTop_ -= count * sizeof(T); return reinterpret_cast(stackTop_); } template T* Top() { RAPIDJSON_ASSERT(GetSize() >= sizeof(T)); return reinterpret_cast(stackTop_ - sizeof(T)); } template const T* Top() const { RAPIDJSON_ASSERT(GetSize() >= sizeof(T)); return reinterpret_cast(stackTop_ - sizeof(T)); } template T* End() { return reinterpret_cast(stackTop_); } template const T* End() const { return reinterpret_cast(stackTop_); } template T* Bottom() { return reinterpret_cast(stack_); } template const T* Bottom() const { return reinterpret_cast(stack_); } bool HasAllocator() const { return allocator_ != 0; } Allocator& GetAllocator() { RAPIDJSON_ASSERT(allocator_); return *allocator_; } bool Empty() const { return stackTop_ == stack_; } size_t GetSize() const { return static_cast(stackTop_ - stack_); } size_t GetCapacity() const { return static_cast(stackEnd_ - stack_); } private: template void Expand(size_t count) { // Only expand the capacity if the current stack exists. Otherwise just create a stack with initial capacity. size_t newCapacity; if (stack_ == 0) { if (!allocator_) ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); newCapacity = initialCapacity_; } else { newCapacity = GetCapacity(); newCapacity += (newCapacity + 1) / 2; } size_t newSize = GetSize() + sizeof(T) * count; if (newCapacity < newSize) newCapacity = newSize; Resize(newCapacity); } void Resize(size_t newCapacity) { const size_t size = GetSize(); // Backup the current size stack_ = static_cast(allocator_->Realloc(stack_, GetCapacity(), newCapacity)); stackTop_ = stack_ + size; stackEnd_ = stack_ + newCapacity; } void Destroy() { Allocator::Free(stack_); RAPIDJSON_DELETE(ownAllocator_); // Only delete if it is owned by the stack } // Prohibit copy constructor & assignment operator. Stack(const Stack&); Stack& operator=(const Stack&); Allocator* allocator_; Allocator* ownAllocator_; char *stack_; char *stackTop_; char *stackEnd_; size_t initialCapacity_; }; } // namespace internal RAPIDJSON_NAMESPACE_END #if defined(__clang__) RAPIDJSON_DIAG_POP #endif #endif // RAPIDJSON_STACK_H_ ================================================ FILE: src/json/rapidjson/internal/strfunc.h ================================================ // Tencent is pleased to support the open source community by making RapidJSON available. // // Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at // // http://opensource.org/licenses/MIT // // Unless required by applicable law or agreed to in writing, software distributed // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. #ifndef RAPIDJSON_INTERNAL_STRFUNC_H_ #define RAPIDJSON_INTERNAL_STRFUNC_H_ #include "src/json/rapidjson/stream.h" #include RAPIDJSON_NAMESPACE_BEGIN namespace internal { //! Custom strlen() which works on different character types. /*! \tparam Ch Character type (e.g. char, wchar_t, short) \param s Null-terminated input string. \return Number of characters in the string. \note This has the same semantics as strlen(), the return value is not number of Unicode codepoints. */ template inline SizeType StrLen(const Ch* s) { RAPIDJSON_ASSERT(s != 0); const Ch* p = s; while (*p) ++p; return SizeType(p - s); } template <> inline SizeType StrLen(const char* s) { return SizeType(std::strlen(s)); } template <> inline SizeType StrLen(const wchar_t* s) { return SizeType(std::wcslen(s)); } //! Returns number of code points in a encoded string. template bool CountStringCodePoint(const typename Encoding::Ch* s, SizeType length, SizeType* outCount) { RAPIDJSON_ASSERT(s != 0); RAPIDJSON_ASSERT(outCount != 0); GenericStringStream is(s); const typename Encoding::Ch* end = s + length; SizeType count = 0; while (is.src_ < end) { unsigned codepoint; if (!Encoding::Decode(is, &codepoint)) return false; count++; } *outCount = count; return true; } } // namespace internal RAPIDJSON_NAMESPACE_END #endif // RAPIDJSON_INTERNAL_STRFUNC_H_ ================================================ FILE: src/json/rapidjson/internal/strtod.h ================================================ // Tencent is pleased to support the open source community by making RapidJSON available. // // Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at // // http://opensource.org/licenses/MIT // // Unless required by applicable law or agreed to in writing, software distributed // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. #ifndef RAPIDJSON_STRTOD_ #define RAPIDJSON_STRTOD_ #include "ieee754.h" #include "biginteger.h" #include "diyfp.h" #include "pow10.h" #include #include RAPIDJSON_NAMESPACE_BEGIN namespace internal { inline double FastPath(double significand, int exp) { if (exp < -308) return 0.0; else if (exp >= 0) return significand * internal::Pow10(exp); else return significand / internal::Pow10(-exp); } inline double StrtodNormalPrecision(double d, int p) { if (p < -308) { // Prevent expSum < -308, making Pow10(p) = 0 d = FastPath(d, -308); d = FastPath(d, p + 308); } else d = FastPath(d, p); return d; } template inline T Min3(T a, T b, T c) { T m = a; if (m > b) m = b; if (m > c) m = c; return m; } inline int CheckWithinHalfULP(double b, const BigInteger& d, int dExp) { const Double db(b); const uint64_t bInt = db.IntegerSignificand(); const int bExp = db.IntegerExponent(); const int hExp = bExp - 1; int dS_Exp2 = 0, dS_Exp5 = 0, bS_Exp2 = 0, bS_Exp5 = 0, hS_Exp2 = 0, hS_Exp5 = 0; // Adjust for decimal exponent if (dExp >= 0) { dS_Exp2 += dExp; dS_Exp5 += dExp; } else { bS_Exp2 -= dExp; bS_Exp5 -= dExp; hS_Exp2 -= dExp; hS_Exp5 -= dExp; } // Adjust for binary exponent if (bExp >= 0) bS_Exp2 += bExp; else { dS_Exp2 -= bExp; hS_Exp2 -= bExp; } // Adjust for half ulp exponent if (hExp >= 0) hS_Exp2 += hExp; else { dS_Exp2 -= hExp; bS_Exp2 -= hExp; } // Remove common power of two factor from all three scaled values int common_Exp2 = Min3(dS_Exp2, bS_Exp2, hS_Exp2); dS_Exp2 -= common_Exp2; bS_Exp2 -= common_Exp2; hS_Exp2 -= common_Exp2; BigInteger dS = d; dS.MultiplyPow5(static_cast(dS_Exp5)) <<= static_cast(dS_Exp2); BigInteger bS(bInt); bS.MultiplyPow5(static_cast(bS_Exp5)) <<= static_cast(bS_Exp2); BigInteger hS(1); hS.MultiplyPow5(static_cast(hS_Exp5)) <<= static_cast(hS_Exp2); BigInteger delta(0); dS.Difference(bS, &delta); return delta.Compare(hS); } inline bool StrtodFast(double d, int p, double* result) { // Use fast path for string-to-double conversion if possible // see http://www.exploringbinary.com/fast-path-decimal-to-floating-point-conversion/ if (p > 22 && p < 22 + 16) { // Fast Path Cases In Disguise d *= internal::Pow10(p - 22); p = 22; } if (p >= -22 && p <= 22 && d <= 9007199254740991.0) { // 2^53 - 1 *result = FastPath(d, p); return true; } else return false; } // Compute an approximation and see if it is within 1/2 ULP inline bool StrtodDiyFp(const char* decimals, int dLen, int dExp, double* result) { uint64_t significand = 0; int i = 0; // 2^64 - 1 = 18446744073709551615, 1844674407370955161 = 0x1999999999999999 for (; i < dLen; i++) { if (significand > RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) || (significand == RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) && decimals[i] > '5')) break; significand = significand * 10u + static_cast(decimals[i] - '0'); } if (i < dLen && decimals[i] >= '5') // Rounding significand++; int remaining = dLen - i; const int kUlpShift = 3; const int kUlp = 1 << kUlpShift; int64_t error = (remaining == 0) ? 0 : kUlp / 2; DiyFp v(significand, 0); v = v.Normalize(); error <<= -v.e; dExp += remaining; int actualExp; DiyFp cachedPower = GetCachedPower10(dExp, &actualExp); if (actualExp != dExp) { static const DiyFp kPow10[] = { DiyFp(RAPIDJSON_UINT64_C2(0xa0000000, 0x00000000), -60), // 10^1 DiyFp(RAPIDJSON_UINT64_C2(0xc8000000, 0x00000000), -57), // 10^2 DiyFp(RAPIDJSON_UINT64_C2(0xfa000000, 0x00000000), -54), // 10^3 DiyFp(RAPIDJSON_UINT64_C2(0x9c400000, 0x00000000), -50), // 10^4 DiyFp(RAPIDJSON_UINT64_C2(0xc3500000, 0x00000000), -47), // 10^5 DiyFp(RAPIDJSON_UINT64_C2(0xf4240000, 0x00000000), -44), // 10^6 DiyFp(RAPIDJSON_UINT64_C2(0x98968000, 0x00000000), -40) // 10^7 }; int adjustment = dExp - actualExp; RAPIDJSON_ASSERT(adjustment >= 1 && adjustment < 8); v = v * kPow10[adjustment - 1]; if (dLen + adjustment > 19) // has more digits than decimal digits in 64-bit error += kUlp / 2; } v = v * cachedPower; error += kUlp + (error == 0 ? 0 : 1); const int oldExp = v.e; v = v.Normalize(); error <<= oldExp - v.e; const int effectiveSignificandSize = Double::EffectiveSignificandSize(64 + v.e); int precisionSize = 64 - effectiveSignificandSize; if (precisionSize + kUlpShift >= 64) { int scaleExp = (precisionSize + kUlpShift) - 63; v.f >>= scaleExp; v.e += scaleExp; error = (error >> scaleExp) + 1 + kUlp; precisionSize -= scaleExp; } DiyFp rounded(v.f >> precisionSize, v.e + precisionSize); const uint64_t precisionBits = (v.f & ((uint64_t(1) << precisionSize) - 1)) * kUlp; const uint64_t halfWay = (uint64_t(1) << (precisionSize - 1)) * kUlp; if (precisionBits >= halfWay + static_cast(error)) { rounded.f++; if (rounded.f & (DiyFp::kDpHiddenBit << 1)) { // rounding overflows mantissa (issue #340) rounded.f >>= 1; rounded.e++; } } *result = rounded.ToDouble(); return halfWay - static_cast(error) >= precisionBits || precisionBits >= halfWay + static_cast(error); } inline double StrtodBigInteger(double approx, const char* decimals, int dLen, int dExp) { RAPIDJSON_ASSERT(dLen >= 0); const BigInteger dInt(decimals, static_cast(dLen)); Double a(approx); int cmp = CheckWithinHalfULP(a.Value(), dInt, dExp); if (cmp < 0) return a.Value(); // within half ULP else if (cmp == 0) { // Round towards even if (a.Significand() & 1) return a.NextPositiveDouble(); else return a.Value(); } else // adjustment return a.NextPositiveDouble(); } inline double StrtodFullPrecision(double d, int p, const char* decimals, size_t length, size_t decimalPosition, int exp) { RAPIDJSON_ASSERT(d >= 0.0); RAPIDJSON_ASSERT(length >= 1); double result = 0.0; if (StrtodFast(d, p, &result)) return result; RAPIDJSON_ASSERT(length <= INT_MAX); int dLen = static_cast(length); RAPIDJSON_ASSERT(length >= decimalPosition); RAPIDJSON_ASSERT(length - decimalPosition <= INT_MAX); int dExpAdjust = static_cast(length - decimalPosition); RAPIDJSON_ASSERT(exp >= INT_MIN + dExpAdjust); int dExp = exp - dExpAdjust; // Make sure length+dExp does not overflow RAPIDJSON_ASSERT(dExp <= INT_MAX - dLen); // Trim leading zeros while (dLen > 0 && *decimals == '0') { dLen--; decimals++; } // Trim trailing zeros while (dLen > 0 && decimals[dLen - 1] == '0') { dLen--; dExp++; } if (dLen == 0) { // Buffer only contains zeros. return 0.0; } // Trim right-most digits const int kMaxDecimalDigit = 767 + 1; if (dLen > kMaxDecimalDigit) { dExp += dLen - kMaxDecimalDigit; dLen = kMaxDecimalDigit; } // If too small, underflow to zero. // Any x <= 10^-324 is interpreted as zero. if (dLen + dExp <= -324) return 0.0; // If too large, overflow to infinity. // Any x >= 10^309 is interpreted as +infinity. if (dLen + dExp > 309) return std::numeric_limits::infinity(); if (StrtodDiyFp(decimals, dLen, dExp, &result)) return result; // Use approximation from StrtodDiyFp and make adjustment with BigInteger comparison return StrtodBigInteger(result, decimals, dLen, dExp); } } // namespace internal RAPIDJSON_NAMESPACE_END #endif // RAPIDJSON_STRTOD_ ================================================ FILE: src/json/rapidjson/internal/swap.h ================================================ // Tencent is pleased to support the open source community by making RapidJSON available. // // Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at // // http://opensource.org/licenses/MIT // // Unless required by applicable law or agreed to in writing, software distributed // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. #ifndef RAPIDJSON_INTERNAL_SWAP_H_ #define RAPIDJSON_INTERNAL_SWAP_H_ #include "src/json/rapidjson/rapidjson.h" #if defined(__clang__) RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(c++98-compat) #endif RAPIDJSON_NAMESPACE_BEGIN namespace internal { //! Custom swap() to avoid dependency on C++ header /*! \tparam T Type of the arguments to swap, should be instantiated with primitive C++ types only. \note This has the same semantics as std::swap(). */ template inline void Swap(T& a, T& b) RAPIDJSON_NOEXCEPT { T tmp = a; a = b; b = tmp; } } // namespace internal RAPIDJSON_NAMESPACE_END #if defined(__clang__) RAPIDJSON_DIAG_POP #endif #endif // RAPIDJSON_INTERNAL_SWAP_H_ ================================================ FILE: src/json/rapidjson/istreamwrapper.h ================================================ // Tencent is pleased to support the open source community by making RapidJSON available. // // Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at // // http://opensource.org/licenses/MIT // // Unless required by applicable law or agreed to in writing, software distributed // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. #ifndef RAPIDJSON_ISTREAMWRAPPER_H_ #define RAPIDJSON_ISTREAMWRAPPER_H_ #include "stream.h" #include #include #ifdef __clang__ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(padded) #elif defined(_MSC_VER) RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(4351) // new behavior: elements of array 'array' will be default initialized #endif RAPIDJSON_NAMESPACE_BEGIN //! Wrapper of \c std::basic_istream into RapidJSON's Stream concept. /*! The classes can be wrapped including but not limited to: - \c std::istringstream - \c std::stringstream - \c std::wistringstream - \c std::wstringstream - \c std::ifstream - \c std::fstream - \c std::wifstream - \c std::wfstream \tparam StreamType Class derived from \c std::basic_istream. */ template class BasicIStreamWrapper { public: typedef typename StreamType::char_type Ch; //! Constructor. /*! \param stream stream opened for read. */ BasicIStreamWrapper(StreamType &stream) : stream_(stream), buffer_(peekBuffer_), bufferSize_(4), bufferLast_(0), current_(buffer_), readCount_(0), count_(0), eof_(false) { Read(); } //! Constructor. /*! \param stream stream opened for read. \param buffer user-supplied buffer. \param bufferSize size of buffer in bytes. Must >=4 bytes. */ BasicIStreamWrapper(StreamType &stream, char* buffer, size_t bufferSize) : stream_(stream), buffer_(buffer), bufferSize_(bufferSize), bufferLast_(0), current_(buffer_), readCount_(0), count_(0), eof_(false) { RAPIDJSON_ASSERT(bufferSize >= 4); Read(); } Ch Peek() const { return *current_; } Ch Take() { Ch c = *current_; Read(); return c; } size_t Tell() const { return count_ + static_cast(current_ - buffer_); } // Not implemented void Put(Ch) { RAPIDJSON_ASSERT(false); } void Flush() { RAPIDJSON_ASSERT(false); } Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } // For encoding detection only. const Ch* Peek4() const { return (current_ + 4 - !eof_ <= bufferLast_) ? current_ : 0; } private: BasicIStreamWrapper(); BasicIStreamWrapper(const BasicIStreamWrapper&); BasicIStreamWrapper& operator=(const BasicIStreamWrapper&); void Read() { if (current_ < bufferLast_) ++current_; else if (!eof_) { count_ += readCount_; readCount_ = bufferSize_; bufferLast_ = buffer_ + readCount_ - 1; current_ = buffer_; if (!stream_.read(buffer_, static_cast(bufferSize_))) { readCount_ = static_cast(stream_.gcount()); *(bufferLast_ = buffer_ + readCount_) = '\0'; eof_ = true; } } } StreamType &stream_; Ch peekBuffer_[4], *buffer_; size_t bufferSize_; Ch *bufferLast_; Ch *current_; size_t readCount_; size_t count_; //!< Number of characters read bool eof_; }; typedef BasicIStreamWrapper IStreamWrapper; typedef BasicIStreamWrapper WIStreamWrapper; #if defined(__clang__) || defined(_MSC_VER) RAPIDJSON_DIAG_POP #endif RAPIDJSON_NAMESPACE_END #endif // RAPIDJSON_ISTREAMWRAPPER_H_ ================================================ FILE: src/json/rapidjson/memorybuffer.h ================================================ // Tencent is pleased to support the open source community by making RapidJSON available. // // Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at // // http://opensource.org/licenses/MIT // // Unless required by applicable law or agreed to in writing, software distributed // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. #ifndef RAPIDJSON_MEMORYBUFFER_H_ #define RAPIDJSON_MEMORYBUFFER_H_ #include "stream.h" #include "src/json/rapidjson/internal/stack.h" RAPIDJSON_NAMESPACE_BEGIN //! Represents an in-memory output byte stream. /*! This class is mainly for being wrapped by EncodedOutputStream or AutoUTFOutputStream. It is similar to FileWriteBuffer but the destination is an in-memory buffer instead of a file. Differences between MemoryBuffer and StringBuffer: 1. StringBuffer has Encoding but MemoryBuffer is only a byte buffer. 2. StringBuffer::GetString() returns a null-terminated string. MemoryBuffer::GetBuffer() returns a buffer without terminator. \tparam Allocator type for allocating memory buffer. \note implements Stream concept */ template struct GenericMemoryBuffer { typedef char Ch; // byte GenericMemoryBuffer(Allocator* allocator = 0, size_t capacity = kDefaultCapacity) : stack_(allocator, capacity) {} void Put(Ch c) { *stack_.template Push() = c; } void Flush() {} void Clear() { stack_.Clear(); } void ShrinkToFit() { stack_.ShrinkToFit(); } Ch* Push(size_t count) { return stack_.template Push(count); } void Pop(size_t count) { stack_.template Pop(count); } const Ch* GetBuffer() const { return stack_.template Bottom(); } size_t GetSize() const { return stack_.GetSize(); } static const size_t kDefaultCapacity = 256; mutable internal::Stack stack_; }; typedef GenericMemoryBuffer<> MemoryBuffer; //! Implement specialized version of PutN() with memset() for better performance. template<> inline void PutN(MemoryBuffer& memoryBuffer, char c, size_t n) { std::memset(memoryBuffer.stack_.Push(n), c, n * sizeof(c)); } RAPIDJSON_NAMESPACE_END #endif // RAPIDJSON_MEMORYBUFFER_H_ ================================================ FILE: src/json/rapidjson/memorystream.h ================================================ // Tencent is pleased to support the open source community by making RapidJSON available. // // Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at // // http://opensource.org/licenses/MIT // // Unless required by applicable law or agreed to in writing, software distributed // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. #ifndef RAPIDJSON_MEMORYSTREAM_H_ #define RAPIDJSON_MEMORYSTREAM_H_ #include "stream.h" #ifdef __clang__ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(unreachable-code) RAPIDJSON_DIAG_OFF(missing-noreturn) #endif RAPIDJSON_NAMESPACE_BEGIN //! Represents an in-memory input byte stream. /*! This class is mainly for being wrapped by EncodedInputStream or AutoUTFInputStream. It is similar to FileReadBuffer but the source is an in-memory buffer instead of a file. Differences between MemoryStream and StringStream: 1. StringStream has encoding but MemoryStream is a byte stream. 2. MemoryStream needs size of the source buffer and the buffer don't need to be null terminated. StringStream assume null-terminated string as source. 3. MemoryStream supports Peek4() for encoding detection. StringStream is specified with an encoding so it should not have Peek4(). \note implements Stream concept */ struct MemoryStream { typedef char Ch; // byte MemoryStream(const Ch *src, size_t size) : src_(src), begin_(src), end_(src + size), size_(size) {} Ch Peek() const { return RAPIDJSON_UNLIKELY(src_ == end_) ? '\0' : *src_; } Ch Take() { return RAPIDJSON_UNLIKELY(src_ == end_) ? '\0' : *src_++; } size_t Tell() const { return static_cast(src_ - begin_); } Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } void Put(Ch) { RAPIDJSON_ASSERT(false); } void Flush() { RAPIDJSON_ASSERT(false); } size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } // For encoding detection only. const Ch* Peek4() const { return Tell() + 4 <= size_ ? src_ : 0; } const Ch* src_; //!< Current read position. const Ch* begin_; //!< Original head of the string. const Ch* end_; //!< End of stream. size_t size_; //!< Size of the stream. }; RAPIDJSON_NAMESPACE_END #ifdef __clang__ RAPIDJSON_DIAG_POP #endif #endif // RAPIDJSON_MEMORYBUFFER_H_ ================================================ FILE: src/json/rapidjson/msinttypes/inttypes.h ================================================ // ISO C9x compliant inttypes.h for Microsoft Visual Studio // Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 // // Copyright (c) 2006-2013 Alexander Chemeris // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // // 3. Neither the name of the product nor the names of its contributors may // be used to endorse or promote products derived from this software // without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO // EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; // OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // /////////////////////////////////////////////////////////////////////////////// // The above software in this distribution may have been modified by // THL A29 Limited ("Tencent Modifications"). // All Tencent Modifications are Copyright (C) 2015 THL A29 Limited. #ifndef _MSC_VER // [ #error "Use this header only with Microsoft Visual C++ compilers!" #endif // _MSC_VER ] #ifndef _MSC_INTTYPES_H_ // [ #define _MSC_INTTYPES_H_ #if _MSC_VER > 1000 #pragma once #endif #include "stdint.h" // miloyip: VC supports inttypes.h since VC2013 #if _MSC_VER >= 1800 #include #else // 7.8 Format conversion of integer types typedef struct { intmax_t quot; intmax_t rem; } imaxdiv_t; // 7.8.1 Macros for format specifiers #if !defined(__cplusplus) || defined(__STDC_FORMAT_MACROS) // [ See footnote 185 at page 198 // The fprintf macros for signed integers are: #define PRId8 "d" #define PRIi8 "i" #define PRIdLEAST8 "d" #define PRIiLEAST8 "i" #define PRIdFAST8 "d" #define PRIiFAST8 "i" #define PRId16 "hd" #define PRIi16 "hi" #define PRIdLEAST16 "hd" #define PRIiLEAST16 "hi" #define PRIdFAST16 "hd" #define PRIiFAST16 "hi" #define PRId32 "I32d" #define PRIi32 "I32i" #define PRIdLEAST32 "I32d" #define PRIiLEAST32 "I32i" #define PRIdFAST32 "I32d" #define PRIiFAST32 "I32i" #define PRId64 "I64d" #define PRIi64 "I64i" #define PRIdLEAST64 "I64d" #define PRIiLEAST64 "I64i" #define PRIdFAST64 "I64d" #define PRIiFAST64 "I64i" #define PRIdMAX "I64d" #define PRIiMAX "I64i" #define PRIdPTR "Id" #define PRIiPTR "Ii" // The fprintf macros for unsigned integers are: #define PRIo8 "o" #define PRIu8 "u" #define PRIx8 "x" #define PRIX8 "X" #define PRIoLEAST8 "o" #define PRIuLEAST8 "u" #define PRIxLEAST8 "x" #define PRIXLEAST8 "X" #define PRIoFAST8 "o" #define PRIuFAST8 "u" #define PRIxFAST8 "x" #define PRIXFAST8 "X" #define PRIo16 "ho" #define PRIu16 "hu" #define PRIx16 "hx" #define PRIX16 "hX" #define PRIoLEAST16 "ho" #define PRIuLEAST16 "hu" #define PRIxLEAST16 "hx" #define PRIXLEAST16 "hX" #define PRIoFAST16 "ho" #define PRIuFAST16 "hu" #define PRIxFAST16 "hx" #define PRIXFAST16 "hX" #define PRIo32 "I32o" #define PRIu32 "I32u" #define PRIx32 "I32x" #define PRIX32 "I32X" #define PRIoLEAST32 "I32o" #define PRIuLEAST32 "I32u" #define PRIxLEAST32 "I32x" #define PRIXLEAST32 "I32X" #define PRIoFAST32 "I32o" #define PRIuFAST32 "I32u" #define PRIxFAST32 "I32x" #define PRIXFAST32 "I32X" #define PRIo64 "I64o" #define PRIu64 "I64u" #define PRIx64 "I64x" #define PRIX64 "I64X" #define PRIoLEAST64 "I64o" #define PRIuLEAST64 "I64u" #define PRIxLEAST64 "I64x" #define PRIXLEAST64 "I64X" #define PRIoFAST64 "I64o" #define PRIuFAST64 "I64u" #define PRIxFAST64 "I64x" #define PRIXFAST64 "I64X" #define PRIoMAX "I64o" #define PRIuMAX "I64u" #define PRIxMAX "I64x" #define PRIXMAX "I64X" #define PRIoPTR "Io" #define PRIuPTR "Iu" #define PRIxPTR "Ix" #define PRIXPTR "IX" // The fscanf macros for signed integers are: #define SCNd8 "d" #define SCNi8 "i" #define SCNdLEAST8 "d" #define SCNiLEAST8 "i" #define SCNdFAST8 "d" #define SCNiFAST8 "i" #define SCNd16 "hd" #define SCNi16 "hi" #define SCNdLEAST16 "hd" #define SCNiLEAST16 "hi" #define SCNdFAST16 "hd" #define SCNiFAST16 "hi" #define SCNd32 "ld" #define SCNi32 "li" #define SCNdLEAST32 "ld" #define SCNiLEAST32 "li" #define SCNdFAST32 "ld" #define SCNiFAST32 "li" #define SCNd64 "I64d" #define SCNi64 "I64i" #define SCNdLEAST64 "I64d" #define SCNiLEAST64 "I64i" #define SCNdFAST64 "I64d" #define SCNiFAST64 "I64i" #define SCNdMAX "I64d" #define SCNiMAX "I64i" #ifdef _WIN64 // [ # define SCNdPTR "I64d" # define SCNiPTR "I64i" #else // _WIN64 ][ # define SCNdPTR "ld" # define SCNiPTR "li" #endif // _WIN64 ] // The fscanf macros for unsigned integers are: #define SCNo8 "o" #define SCNu8 "u" #define SCNx8 "x" #define SCNX8 "X" #define SCNoLEAST8 "o" #define SCNuLEAST8 "u" #define SCNxLEAST8 "x" #define SCNXLEAST8 "X" #define SCNoFAST8 "o" #define SCNuFAST8 "u" #define SCNxFAST8 "x" #define SCNXFAST8 "X" #define SCNo16 "ho" #define SCNu16 "hu" #define SCNx16 "hx" #define SCNX16 "hX" #define SCNoLEAST16 "ho" #define SCNuLEAST16 "hu" #define SCNxLEAST16 "hx" #define SCNXLEAST16 "hX" #define SCNoFAST16 "ho" #define SCNuFAST16 "hu" #define SCNxFAST16 "hx" #define SCNXFAST16 "hX" #define SCNo32 "lo" #define SCNu32 "lu" #define SCNx32 "lx" #define SCNX32 "lX" #define SCNoLEAST32 "lo" #define SCNuLEAST32 "lu" #define SCNxLEAST32 "lx" #define SCNXLEAST32 "lX" #define SCNoFAST32 "lo" #define SCNuFAST32 "lu" #define SCNxFAST32 "lx" #define SCNXFAST32 "lX" #define SCNo64 "I64o" #define SCNu64 "I64u" #define SCNx64 "I64x" #define SCNX64 "I64X" #define SCNoLEAST64 "I64o" #define SCNuLEAST64 "I64u" #define SCNxLEAST64 "I64x" #define SCNXLEAST64 "I64X" #define SCNoFAST64 "I64o" #define SCNuFAST64 "I64u" #define SCNxFAST64 "I64x" #define SCNXFAST64 "I64X" #define SCNoMAX "I64o" #define SCNuMAX "I64u" #define SCNxMAX "I64x" #define SCNXMAX "I64X" #ifdef _WIN64 // [ # define SCNoPTR "I64o" # define SCNuPTR "I64u" # define SCNxPTR "I64x" # define SCNXPTR "I64X" #else // _WIN64 ][ # define SCNoPTR "lo" # define SCNuPTR "lu" # define SCNxPTR "lx" # define SCNXPTR "lX" #endif // _WIN64 ] #endif // __STDC_FORMAT_MACROS ] // 7.8.2 Functions for greatest-width integer types // 7.8.2.1 The imaxabs function #define imaxabs _abs64 // 7.8.2.2 The imaxdiv function // This is modified version of div() function from Microsoft's div.c found // in %MSVC.NET%\crt\src\div.c #ifdef STATIC_IMAXDIV // [ static #else // STATIC_IMAXDIV ][ _inline #endif // STATIC_IMAXDIV ] imaxdiv_t __cdecl imaxdiv(intmax_t numer, intmax_t denom) { imaxdiv_t result; result.quot = numer / denom; result.rem = numer % denom; if (numer < 0 && result.rem > 0) { // did division wrong; must fix up ++result.quot; result.rem -= denom; } return result; } // 7.8.2.3 The strtoimax and strtoumax functions #define strtoimax _strtoi64 #define strtoumax _strtoui64 // 7.8.2.4 The wcstoimax and wcstoumax functions #define wcstoimax _wcstoi64 #define wcstoumax _wcstoui64 #endif // _MSC_VER >= 1800 #endif // _MSC_INTTYPES_H_ ] ================================================ FILE: src/json/rapidjson/msinttypes/stdint.h ================================================ // ISO C9x compliant stdint.h for Microsoft Visual Studio // Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 // // Copyright (c) 2006-2013 Alexander Chemeris // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // // 3. Neither the name of the product nor the names of its contributors may // be used to endorse or promote products derived from this software // without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO // EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; // OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // /////////////////////////////////////////////////////////////////////////////// // The above software in this distribution may have been modified by // THL A29 Limited ("Tencent Modifications"). // All Tencent Modifications are Copyright (C) 2015 THL A29 Limited. #ifndef _MSC_VER // [ #error "Use this header only with Microsoft Visual C++ compilers!" #endif // _MSC_VER ] #ifndef _MSC_STDINT_H_ // [ #define _MSC_STDINT_H_ #if _MSC_VER > 1000 #pragma once #endif // miloyip: Originally Visual Studio 2010 uses its own stdint.h. However it generates warning with INT64_C(), so change to use this file for vs2010. #if _MSC_VER >= 1600 // [ #include #if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260 #undef INT8_C #undef INT16_C #undef INT32_C #undef INT64_C #undef UINT8_C #undef UINT16_C #undef UINT32_C #undef UINT64_C // 7.18.4.1 Macros for minimum-width integer constants #define INT8_C(val) val##i8 #define INT16_C(val) val##i16 #define INT32_C(val) val##i32 #define INT64_C(val) val##i64 #define UINT8_C(val) val##ui8 #define UINT16_C(val) val##ui16 #define UINT32_C(val) val##ui32 #define UINT64_C(val) val##ui64 // 7.18.4.2 Macros for greatest-width integer constants // These #ifndef's are needed to prevent collisions with . // Check out Issue 9 for the details. #ifndef INTMAX_C // [ # define INTMAX_C INT64_C #endif // INTMAX_C ] #ifndef UINTMAX_C // [ # define UINTMAX_C UINT64_C #endif // UINTMAX_C ] #endif // __STDC_CONSTANT_MACROS ] #else // ] _MSC_VER >= 1700 [ #include // For Visual Studio 6 in C++ mode and for many Visual Studio versions when // compiling for ARM we have to wrap include with 'extern "C++" {}' // or compiler would give many errors like this: // error C2733: second C linkage of overloaded function 'wmemchr' not allowed #if defined(__cplusplus) && !defined(_M_ARM) extern "C" { #endif # include #if defined(__cplusplus) && !defined(_M_ARM) } #endif // Define _W64 macros to mark types changing their size, like intptr_t. #ifndef _W64 # if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300 # define _W64 __w64 # else # define _W64 # endif #endif // 7.18.1 Integer types // 7.18.1.1 Exact-width integer types // Visual Studio 6 and Embedded Visual C++ 4 doesn't // realize that, e.g. char has the same size as __int8 // so we give up on __intX for them. #if (_MSC_VER < 1300) typedef signed char int8_t; typedef signed short int16_t; typedef signed int int32_t; typedef unsigned char uint8_t; typedef unsigned short uint16_t; typedef unsigned int uint32_t; #else typedef signed __int8 int8_t; typedef signed __int16 int16_t; typedef signed __int32 int32_t; typedef unsigned __int8 uint8_t; typedef unsigned __int16 uint16_t; typedef unsigned __int32 uint32_t; #endif typedef signed __int64 int64_t; typedef unsigned __int64 uint64_t; // 7.18.1.2 Minimum-width integer types typedef int8_t int_least8_t; typedef int16_t int_least16_t; typedef int32_t int_least32_t; typedef int64_t int_least64_t; typedef uint8_t uint_least8_t; typedef uint16_t uint_least16_t; typedef uint32_t uint_least32_t; typedef uint64_t uint_least64_t; // 7.18.1.3 Fastest minimum-width integer types typedef int8_t int_fast8_t; typedef int16_t int_fast16_t; typedef int32_t int_fast32_t; typedef int64_t int_fast64_t; typedef uint8_t uint_fast8_t; typedef uint16_t uint_fast16_t; typedef uint32_t uint_fast32_t; typedef uint64_t uint_fast64_t; // 7.18.1.4 Integer types capable of holding object pointers #ifdef _WIN64 // [ typedef signed __int64 intptr_t; typedef unsigned __int64 uintptr_t; #else // _WIN64 ][ typedef _W64 signed int intptr_t; typedef _W64 unsigned int uintptr_t; #endif // _WIN64 ] // 7.18.1.5 Greatest-width integer types typedef int64_t intmax_t; typedef uint64_t uintmax_t; // 7.18.2 Limits of specified-width integer types #if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259 // 7.18.2.1 Limits of exact-width integer types #define INT8_MIN ((int8_t)_I8_MIN) #define INT8_MAX _I8_MAX #define INT16_MIN ((int16_t)_I16_MIN) #define INT16_MAX _I16_MAX #define INT32_MIN ((int32_t)_I32_MIN) #define INT32_MAX _I32_MAX #define INT64_MIN ((int64_t)_I64_MIN) #define INT64_MAX _I64_MAX #define UINT8_MAX _UI8_MAX #define UINT16_MAX _UI16_MAX #define UINT32_MAX _UI32_MAX #define UINT64_MAX _UI64_MAX // 7.18.2.2 Limits of minimum-width integer types #define INT_LEAST8_MIN INT8_MIN #define INT_LEAST8_MAX INT8_MAX #define INT_LEAST16_MIN INT16_MIN #define INT_LEAST16_MAX INT16_MAX #define INT_LEAST32_MIN INT32_MIN #define INT_LEAST32_MAX INT32_MAX #define INT_LEAST64_MIN INT64_MIN #define INT_LEAST64_MAX INT64_MAX #define UINT_LEAST8_MAX UINT8_MAX #define UINT_LEAST16_MAX UINT16_MAX #define UINT_LEAST32_MAX UINT32_MAX #define UINT_LEAST64_MAX UINT64_MAX // 7.18.2.3 Limits of fastest minimum-width integer types #define INT_FAST8_MIN INT8_MIN #define INT_FAST8_MAX INT8_MAX #define INT_FAST16_MIN INT16_MIN #define INT_FAST16_MAX INT16_MAX #define INT_FAST32_MIN INT32_MIN #define INT_FAST32_MAX INT32_MAX #define INT_FAST64_MIN INT64_MIN #define INT_FAST64_MAX INT64_MAX #define UINT_FAST8_MAX UINT8_MAX #define UINT_FAST16_MAX UINT16_MAX #define UINT_FAST32_MAX UINT32_MAX #define UINT_FAST64_MAX UINT64_MAX // 7.18.2.4 Limits of integer types capable of holding object pointers #ifdef _WIN64 // [ # define INTPTR_MIN INT64_MIN # define INTPTR_MAX INT64_MAX # define UINTPTR_MAX UINT64_MAX #else // _WIN64 ][ # define INTPTR_MIN INT32_MIN # define INTPTR_MAX INT32_MAX # define UINTPTR_MAX UINT32_MAX #endif // _WIN64 ] // 7.18.2.5 Limits of greatest-width integer types #define INTMAX_MIN INT64_MIN #define INTMAX_MAX INT64_MAX #define UINTMAX_MAX UINT64_MAX // 7.18.3 Limits of other integer types #ifdef _WIN64 // [ # define PTRDIFF_MIN _I64_MIN # define PTRDIFF_MAX _I64_MAX #else // _WIN64 ][ # define PTRDIFF_MIN _I32_MIN # define PTRDIFF_MAX _I32_MAX #endif // _WIN64 ] #define SIG_ATOMIC_MIN INT_MIN #define SIG_ATOMIC_MAX INT_MAX #ifndef SIZE_MAX // [ # ifdef _WIN64 // [ # define SIZE_MAX _UI64_MAX # else // _WIN64 ][ # define SIZE_MAX _UI32_MAX # endif // _WIN64 ] #endif // SIZE_MAX ] // WCHAR_MIN and WCHAR_MAX are also defined in #ifndef WCHAR_MIN // [ # define WCHAR_MIN 0 #endif // WCHAR_MIN ] #ifndef WCHAR_MAX // [ # define WCHAR_MAX _UI16_MAX #endif // WCHAR_MAX ] #define WINT_MIN 0 #define WINT_MAX _UI16_MAX #endif // __STDC_LIMIT_MACROS ] // 7.18.4 Limits of other integer types #if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260 // 7.18.4.1 Macros for minimum-width integer constants #define INT8_C(val) val##i8 #define INT16_C(val) val##i16 #define INT32_C(val) val##i32 #define INT64_C(val) val##i64 #define UINT8_C(val) val##ui8 #define UINT16_C(val) val##ui16 #define UINT32_C(val) val##ui32 #define UINT64_C(val) val##ui64 // 7.18.4.2 Macros for greatest-width integer constants // These #ifndef's are needed to prevent collisions with . // Check out Issue 9 for the details. #ifndef INTMAX_C // [ # define INTMAX_C INT64_C #endif // INTMAX_C ] #ifndef UINTMAX_C // [ # define UINTMAX_C UINT64_C #endif // UINTMAX_C ] #endif // __STDC_CONSTANT_MACROS ] #endif // _MSC_VER >= 1600 ] #endif // _MSC_STDINT_H_ ] ================================================ FILE: src/json/rapidjson/ostreamwrapper.h ================================================ // Tencent is pleased to support the open source community by making RapidJSON available. // // Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at // // http://opensource.org/licenses/MIT // // Unless required by applicable law or agreed to in writing, software distributed // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. #ifndef RAPIDJSON_OSTREAMWRAPPER_H_ #define RAPIDJSON_OSTREAMWRAPPER_H_ #include "stream.h" #include #ifdef __clang__ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(padded) #endif RAPIDJSON_NAMESPACE_BEGIN //! Wrapper of \c std::basic_ostream into RapidJSON's Stream concept. /*! The classes can be wrapped including but not limited to: - \c std::ostringstream - \c std::stringstream - \c std::wpstringstream - \c std::wstringstream - \c std::ifstream - \c std::fstream - \c std::wofstream - \c std::wfstream \tparam StreamType Class derived from \c std::basic_ostream. */ template class BasicOStreamWrapper { public: typedef typename StreamType::char_type Ch; BasicOStreamWrapper(StreamType& stream) : stream_(stream) {} void Put(Ch c) { stream_.put(c); } void Flush() { stream_.flush(); } // Not implemented char Peek() const { RAPIDJSON_ASSERT(false); return 0; } char Take() { RAPIDJSON_ASSERT(false); return 0; } size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } char* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } size_t PutEnd(char*) { RAPIDJSON_ASSERT(false); return 0; } private: BasicOStreamWrapper(const BasicOStreamWrapper&); BasicOStreamWrapper& operator=(const BasicOStreamWrapper&); StreamType& stream_; }; typedef BasicOStreamWrapper OStreamWrapper; typedef BasicOStreamWrapper WOStreamWrapper; #ifdef __clang__ RAPIDJSON_DIAG_POP #endif RAPIDJSON_NAMESPACE_END #endif // RAPIDJSON_OSTREAMWRAPPER_H_ ================================================ FILE: src/json/rapidjson/pointer.h ================================================ // Tencent is pleased to support the open source community by making RapidJSON available. // // Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at // // http://opensource.org/licenses/MIT // // Unless required by applicable law or agreed to in writing, software distributed // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. #ifndef RAPIDJSON_POINTER_H_ #define RAPIDJSON_POINTER_H_ #include "document.h" #include "src/json/rapidjson/internal/itoa.h" #ifdef __clang__ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(switch-enum) #elif defined(_MSC_VER) RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated #endif RAPIDJSON_NAMESPACE_BEGIN static const SizeType kPointerInvalidIndex = ~SizeType(0); //!< Represents an invalid index in GenericPointer::Token //! Error code of parsing. /*! \ingroup RAPIDJSON_ERRORS \see GenericPointer::GenericPointer, GenericPointer::GetParseErrorCode */ enum PointerParseErrorCode { kPointerParseErrorNone = 0, //!< The parse is successful kPointerParseErrorTokenMustBeginWithSolidus, //!< A token must begin with a '/' kPointerParseErrorInvalidEscape, //!< Invalid escape kPointerParseErrorInvalidPercentEncoding, //!< Invalid percent encoding in URI fragment kPointerParseErrorCharacterMustPercentEncode //!< A character must percent encoded in URI fragment }; /////////////////////////////////////////////////////////////////////////////// // GenericPointer //! Represents a JSON Pointer. Use Pointer for UTF8 encoding and default allocator. /*! This class implements RFC 6901 "JavaScript Object Notation (JSON) Pointer" (https://tools.ietf.org/html/rfc6901). A JSON pointer is for identifying a specific value in a JSON document (GenericDocument). It can simplify coding of DOM tree manipulation, because it can access multiple-level depth of DOM tree with single API call. After it parses a string representation (e.g. "/foo/0" or URI fragment representation (e.g. "#/foo/0") into its internal representation (tokens), it can be used to resolve a specific value in multiple documents, or sub-tree of documents. Contrary to GenericValue, Pointer can be copy constructed and copy assigned. Apart from assignment, a Pointer cannot be modified after construction. Although Pointer is very convenient, please aware that constructing Pointer involves parsing and dynamic memory allocation. A special constructor with user- supplied tokens eliminates these. GenericPointer depends on GenericDocument and GenericValue. \tparam ValueType The value type of the DOM tree. E.g. GenericValue > \tparam Allocator The allocator type for allocating memory for internal representation. \note GenericPointer uses same encoding of ValueType. However, Allocator of GenericPointer is independent of Allocator of Value. */ template class GenericPointer { public: typedef typename ValueType::EncodingType EncodingType; //!< Encoding type from Value typedef typename ValueType::Ch Ch; //!< Character type from Value //! A token is the basic units of internal representation. /*! A JSON pointer string representation "/foo/123" is parsed to two tokens: "foo" and 123. 123 will be represented in both numeric form and string form. They are resolved according to the actual value type (object or array). For token that are not numbers, or the numeric value is out of bound (greater than limits of SizeType), they are only treated as string form (i.e. the token's index will be equal to kPointerInvalidIndex). This struct is public so that user can create a Pointer without parsing and allocation, using a special constructor. */ struct Token { const Ch* name; //!< Name of the token. It has null character at the end but it can contain null character. SizeType length; //!< Length of the name. SizeType index; //!< A valid array index, if it is not equal to kPointerInvalidIndex. }; //!@name Constructors and destructor. //@{ //! Default constructor. GenericPointer(Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {} //! Constructor that parses a string or URI fragment representation. /*! \param source A null-terminated, string or URI fragment representation of JSON pointer. \param allocator User supplied allocator for this pointer. If no allocator is provided, it creates a self-owned one. */ explicit GenericPointer(const Ch* source, Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { Parse(source, internal::StrLen(source)); } #if RAPIDJSON_HAS_STDSTRING //! Constructor that parses a string or URI fragment representation. /*! \param source A string or URI fragment representation of JSON pointer. \param allocator User supplied allocator for this pointer. If no allocator is provided, it creates a self-owned one. \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. */ explicit GenericPointer(const std::basic_string& source, Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { Parse(source.c_str(), source.size()); } #endif //! Constructor that parses a string or URI fragment representation, with length of the source string. /*! \param source A string or URI fragment representation of JSON pointer. \param length Length of source. \param allocator User supplied allocator for this pointer. If no allocator is provided, it creates a self-owned one. \note Slightly faster than the overload without length. */ GenericPointer(const Ch* source, size_t length, Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { Parse(source, length); } //! Constructor with user-supplied tokens. /*! This constructor let user supplies const array of tokens. This prevents the parsing process and eliminates allocation. This is preferred for memory constrained environments. \param tokens An constant array of tokens representing the JSON pointer. \param tokenCount Number of tokens. \b Example \code #define NAME(s) { s, sizeof(s) / sizeof(s[0]) - 1, kPointerInvalidIndex } #define INDEX(i) { #i, sizeof(#i) - 1, i } static const Pointer::Token kTokens[] = { NAME("foo"), INDEX(123) }; static const Pointer p(kTokens, sizeof(kTokens) / sizeof(kTokens[0])); // Equivalent to static const Pointer p("/foo/123"); #undef NAME #undef INDEX \endcode */ GenericPointer(const Token* tokens, size_t tokenCount) : allocator_(), ownAllocator_(), nameBuffer_(), tokens_(const_cast(tokens)), tokenCount_(tokenCount), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {} //! Copy constructor. GenericPointer(const GenericPointer& rhs) : allocator_(rhs.allocator_), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { *this = rhs; } //! Copy constructor. GenericPointer(const GenericPointer& rhs, Allocator* allocator) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { *this = rhs; } //! Destructor. ~GenericPointer() { if (nameBuffer_) // If user-supplied tokens constructor is used, nameBuffer_ is nullptr and tokens_ are not deallocated. Allocator::Free(tokens_); RAPIDJSON_DELETE(ownAllocator_); } //! Assignment operator. GenericPointer& operator=(const GenericPointer& rhs) { if (this != &rhs) { // Do not delete ownAllcator if (nameBuffer_) Allocator::Free(tokens_); tokenCount_ = rhs.tokenCount_; parseErrorOffset_ = rhs.parseErrorOffset_; parseErrorCode_ = rhs.parseErrorCode_; if (rhs.nameBuffer_) CopyFromRaw(rhs); // Normally parsed tokens. else { tokens_ = rhs.tokens_; // User supplied const tokens. nameBuffer_ = 0; } } return *this; } //! Swap the content of this pointer with an other. /*! \param other The pointer to swap with. \note Constant complexity. */ GenericPointer& Swap(GenericPointer& other) RAPIDJSON_NOEXCEPT { internal::Swap(allocator_, other.allocator_); internal::Swap(ownAllocator_, other.ownAllocator_); internal::Swap(nameBuffer_, other.nameBuffer_); internal::Swap(tokens_, other.tokens_); internal::Swap(tokenCount_, other.tokenCount_); internal::Swap(parseErrorOffset_, other.parseErrorOffset_); internal::Swap(parseErrorCode_, other.parseErrorCode_); return *this; } //! free-standing swap function helper /*! Helper function to enable support for common swap implementation pattern based on \c std::swap: \code void swap(MyClass& a, MyClass& b) { using std::swap; swap(a.pointer, b.pointer); // ... } \endcode \see Swap() */ friend inline void swap(GenericPointer& a, GenericPointer& b) RAPIDJSON_NOEXCEPT { a.Swap(b); } //@} //!@name Append token //@{ //! Append a token and return a new Pointer /*! \param token Token to be appended. \param allocator Allocator for the newly return Pointer. \return A new Pointer with appended token. */ GenericPointer Append(const Token& token, Allocator* allocator = 0) const { GenericPointer r; r.allocator_ = allocator; Ch *p = r.CopyFromRaw(*this, 1, token.length + 1); std::memcpy(p, token.name, (token.length + 1) * sizeof(Ch)); r.tokens_[tokenCount_].name = p; r.tokens_[tokenCount_].length = token.length; r.tokens_[tokenCount_].index = token.index; return r; } //! Append a name token with length, and return a new Pointer /*! \param name Name to be appended. \param length Length of name. \param allocator Allocator for the newly return Pointer. \return A new Pointer with appended token. */ GenericPointer Append(const Ch* name, SizeType length, Allocator* allocator = 0) const { Token token = { name, length, kPointerInvalidIndex }; return Append(token, allocator); } //! Append a name token without length, and return a new Pointer /*! \param name Name (const Ch*) to be appended. \param allocator Allocator for the newly return Pointer. \return A new Pointer with appended token. */ template RAPIDJSON_DISABLEIF_RETURN((internal::NotExpr::Type, Ch> >), (GenericPointer)) Append(T* name, Allocator* allocator = 0) const { return Append(name, internal::StrLen(name), allocator); } #if RAPIDJSON_HAS_STDSTRING //! Append a name token, and return a new Pointer /*! \param name Name to be appended. \param allocator Allocator for the newly return Pointer. \return A new Pointer with appended token. */ GenericPointer Append(const std::basic_string& name, Allocator* allocator = 0) const { return Append(name.c_str(), static_cast(name.size()), allocator); } #endif //! Append a index token, and return a new Pointer /*! \param index Index to be appended. \param allocator Allocator for the newly return Pointer. \return A new Pointer with appended token. */ GenericPointer Append(SizeType index, Allocator* allocator = 0) const { char buffer[21]; char* end = sizeof(SizeType) == 4 ? internal::u32toa(index, buffer) : internal::u64toa(index, buffer); SizeType length = static_cast(end - buffer); buffer[length] = '\0'; if (sizeof(Ch) == 1) { Token token = { reinterpret_cast(buffer), length, index }; return Append(token, allocator); } else { Ch name[21]; for (size_t i = 0; i <= length; i++) name[i] = static_cast(buffer[i]); Token token = { name, length, index }; return Append(token, allocator); } } //! Append a token by value, and return a new Pointer /*! \param token token to be appended. \param allocator Allocator for the newly return Pointer. \return A new Pointer with appended token. */ GenericPointer Append(const ValueType& token, Allocator* allocator = 0) const { if (token.IsString()) return Append(token.GetString(), token.GetStringLength(), allocator); else { RAPIDJSON_ASSERT(token.IsUint64()); RAPIDJSON_ASSERT(token.GetUint64() <= SizeType(~0)); return Append(static_cast(token.GetUint64()), allocator); } } //!@name Handling Parse Error //@{ //! Check whether this is a valid pointer. bool IsValid() const { return parseErrorCode_ == kPointerParseErrorNone; } //! Get the parsing error offset in code unit. size_t GetParseErrorOffset() const { return parseErrorOffset_; } //! Get the parsing error code. PointerParseErrorCode GetParseErrorCode() const { return parseErrorCode_; } //@} //! Get the allocator of this pointer. Allocator& GetAllocator() { return *allocator_; } //!@name Tokens //@{ //! Get the token array (const version only). const Token* GetTokens() const { return tokens_; } //! Get the number of tokens. size_t GetTokenCount() const { return tokenCount_; } //@} //!@name Equality/inequality operators //@{ //! Equality operator. /*! \note When any pointers are invalid, always returns false. */ bool operator==(const GenericPointer& rhs) const { if (!IsValid() || !rhs.IsValid() || tokenCount_ != rhs.tokenCount_) return false; for (size_t i = 0; i < tokenCount_; i++) { if (tokens_[i].index != rhs.tokens_[i].index || tokens_[i].length != rhs.tokens_[i].length || (tokens_[i].length != 0 && std::memcmp(tokens_[i].name, rhs.tokens_[i].name, sizeof(Ch)* tokens_[i].length) != 0)) { return false; } } return true; } //! Inequality operator. /*! \note When any pointers are invalid, always returns true. */ bool operator!=(const GenericPointer& rhs) const { return !(*this == rhs); } //! Less than operator. /*! \note Invalid pointers are always greater than valid ones. */ bool operator<(const GenericPointer& rhs) const { if (!IsValid()) return false; if (!rhs.IsValid()) return true; if (tokenCount_ != rhs.tokenCount_) return tokenCount_ < rhs.tokenCount_; for (size_t i = 0; i < tokenCount_; i++) { if (tokens_[i].index != rhs.tokens_[i].index) return tokens_[i].index < rhs.tokens_[i].index; if (tokens_[i].length != rhs.tokens_[i].length) return tokens_[i].length < rhs.tokens_[i].length; if (int cmp = std::memcmp(tokens_[i].name, rhs.tokens_[i].name, sizeof(Ch) * tokens_[i].length)) return cmp < 0; } return false; } //@} //!@name Stringify //@{ //! Stringify the pointer into string representation. /*! \tparam OutputStream Type of output stream. \param os The output stream. */ template bool Stringify(OutputStream& os) const { return Stringify(os); } //! Stringify the pointer into URI fragment representation. /*! \tparam OutputStream Type of output stream. \param os The output stream. */ template bool StringifyUriFragment(OutputStream& os) const { return Stringify(os); } //@} //!@name Create value //@{ //! Create a value in a subtree. /*! If the value is not exist, it creates all parent values and a JSON Null value. So it always succeed and return the newly created or existing value. Remind that it may change types of parents according to tokens, so it potentially removes previously stored values. For example, if a document was an array, and "/foo" is used to create a value, then the document will be changed to an object, and all existing array elements are lost. \param root Root value of a DOM subtree to be resolved. It can be any value other than document root. \param allocator Allocator for creating the values if the specified value or its parents are not exist. \param alreadyExist If non-null, it stores whether the resolved value is already exist. \return The resolved newly created (a JSON Null value), or already exists value. */ ValueType& Create(ValueType& root, typename ValueType::AllocatorType& allocator, bool* alreadyExist = 0) const { RAPIDJSON_ASSERT(IsValid()); ValueType* v = &root; bool exist = true; for (const Token *t = tokens_; t != tokens_ + tokenCount_; ++t) { if (v->IsArray() && t->name[0] == '-' && t->length == 1) { v->PushBack(ValueType().Move(), allocator); v = &((*v)[v->Size() - 1]); exist = false; } else { if (t->index == kPointerInvalidIndex) { // must be object name if (!v->IsObject()) v->SetObject(); // Change to Object } else { // object name or array index if (!v->IsArray() && !v->IsObject()) v->SetArray(); // Change to Array } if (v->IsArray()) { if (t->index >= v->Size()) { v->Reserve(t->index + 1, allocator); while (t->index >= v->Size()) v->PushBack(ValueType().Move(), allocator); exist = false; } v = &((*v)[t->index]); } else { typename ValueType::MemberIterator m = v->FindMember(GenericStringRef(t->name, t->length)); if (m == v->MemberEnd()) { v->AddMember(ValueType(t->name, t->length, allocator).Move(), ValueType().Move(), allocator); v = &(--v->MemberEnd())->value; // Assumes AddMember() appends at the end exist = false; } else v = &m->value; } } } if (alreadyExist) *alreadyExist = exist; return *v; } //! Creates a value in a document. /*! \param document A document to be resolved. \param alreadyExist If non-null, it stores whether the resolved value is already exist. \return The resolved newly created, or already exists value. */ template ValueType& Create(GenericDocument& document, bool* alreadyExist = 0) const { return Create(document, document.GetAllocator(), alreadyExist); } //@} //!@name Query value //@{ //! Query a value in a subtree. /*! \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. \param unresolvedTokenIndex If the pointer cannot resolve a token in the pointer, this parameter can obtain the index of unresolved token. \return Pointer to the value if it can be resolved. Otherwise null. \note There are only 3 situations when a value cannot be resolved: 1. A value in the path is not an array nor object. 2. An object value does not contain the token. 3. A token is out of range of an array value. Use unresolvedTokenIndex to retrieve the token index. */ ValueType* Get(ValueType& root, size_t* unresolvedTokenIndex = 0) const { RAPIDJSON_ASSERT(IsValid()); ValueType* v = &root; for (const Token *t = tokens_; t != tokens_ + tokenCount_; ++t) { switch (v->GetType()) { case kObjectType: { typename ValueType::MemberIterator m = v->FindMember(GenericStringRef(t->name, t->length)); if (m == v->MemberEnd()) break; v = &m->value; } continue; case kArrayType: if (t->index == kPointerInvalidIndex || t->index >= v->Size()) break; v = &((*v)[t->index]); continue; default: break; } // Error: unresolved token if (unresolvedTokenIndex) *unresolvedTokenIndex = static_cast(t - tokens_); return 0; } return v; } //! Query a const value in a const subtree. /*! \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. \return Pointer to the value if it can be resolved. Otherwise null. */ const ValueType* Get(const ValueType& root, size_t* unresolvedTokenIndex = 0) const { return Get(const_cast(root), unresolvedTokenIndex); } //@} //!@name Query a value with default //@{ //! Query a value in a subtree with default value. /*! Similar to Get(), but if the specified value do not exists, it creates all parents and clone the default value. So that this function always succeed. \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. \param defaultValue Default value to be cloned if the value was not exists. \param allocator Allocator for creating the values if the specified value or its parents are not exist. \see Create() */ ValueType& GetWithDefault(ValueType& root, const ValueType& defaultValue, typename ValueType::AllocatorType& allocator) const { bool alreadyExist; ValueType& v = Create(root, allocator, &alreadyExist); return alreadyExist ? v : v.CopyFrom(defaultValue, allocator); } //! Query a value in a subtree with default null-terminated string. ValueType& GetWithDefault(ValueType& root, const Ch* defaultValue, typename ValueType::AllocatorType& allocator) const { bool alreadyExist; ValueType& v = Create(root, allocator, &alreadyExist); return alreadyExist ? v : v.SetString(defaultValue, allocator); } #if RAPIDJSON_HAS_STDSTRING //! Query a value in a subtree with default std::basic_string. ValueType& GetWithDefault(ValueType& root, const std::basic_string& defaultValue, typename ValueType::AllocatorType& allocator) const { bool alreadyExist; ValueType& v = Create(root, allocator, &alreadyExist); return alreadyExist ? v : v.SetString(defaultValue, allocator); } #endif //! Query a value in a subtree with default primitive value. /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool */ template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) GetWithDefault(ValueType& root, T defaultValue, typename ValueType::AllocatorType& allocator) const { return GetWithDefault(root, ValueType(defaultValue).Move(), allocator); } //! Query a value in a document with default value. template ValueType& GetWithDefault(GenericDocument& document, const ValueType& defaultValue) const { return GetWithDefault(document, defaultValue, document.GetAllocator()); } //! Query a value in a document with default null-terminated string. template ValueType& GetWithDefault(GenericDocument& document, const Ch* defaultValue) const { return GetWithDefault(document, defaultValue, document.GetAllocator()); } #if RAPIDJSON_HAS_STDSTRING //! Query a value in a document with default std::basic_string. template ValueType& GetWithDefault(GenericDocument& document, const std::basic_string& defaultValue) const { return GetWithDefault(document, defaultValue, document.GetAllocator()); } #endif //! Query a value in a document with default primitive value. /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool */ template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) GetWithDefault(GenericDocument& document, T defaultValue) const { return GetWithDefault(document, defaultValue, document.GetAllocator()); } //@} //!@name Set a value //@{ //! Set a value in a subtree, with move semantics. /*! It creates all parents if they are not exist or types are different to the tokens. So this function always succeeds but potentially remove existing values. \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. \param value Value to be set. \param allocator Allocator for creating the values if the specified value or its parents are not exist. \see Create() */ ValueType& Set(ValueType& root, ValueType& value, typename ValueType::AllocatorType& allocator) const { return Create(root, allocator) = value; } //! Set a value in a subtree, with copy semantics. ValueType& Set(ValueType& root, const ValueType& value, typename ValueType::AllocatorType& allocator) const { return Create(root, allocator).CopyFrom(value, allocator); } //! Set a null-terminated string in a subtree. ValueType& Set(ValueType& root, const Ch* value, typename ValueType::AllocatorType& allocator) const { return Create(root, allocator) = ValueType(value, allocator).Move(); } #if RAPIDJSON_HAS_STDSTRING //! Set a std::basic_string in a subtree. ValueType& Set(ValueType& root, const std::basic_string& value, typename ValueType::AllocatorType& allocator) const { return Create(root, allocator) = ValueType(value, allocator).Move(); } #endif //! Set a primitive value in a subtree. /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool */ template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) Set(ValueType& root, T value, typename ValueType::AllocatorType& allocator) const { return Create(root, allocator) = ValueType(value).Move(); } //! Set a value in a document, with move semantics. template ValueType& Set(GenericDocument& document, ValueType& value) const { return Create(document) = value; } //! Set a value in a document, with copy semantics. template ValueType& Set(GenericDocument& document, const ValueType& value) const { return Create(document).CopyFrom(value, document.GetAllocator()); } //! Set a null-terminated string in a document. template ValueType& Set(GenericDocument& document, const Ch* value) const { return Create(document) = ValueType(value, document.GetAllocator()).Move(); } #if RAPIDJSON_HAS_STDSTRING //! Sets a std::basic_string in a document. template ValueType& Set(GenericDocument& document, const std::basic_string& value) const { return Create(document) = ValueType(value, document.GetAllocator()).Move(); } #endif //! Set a primitive value in a document. /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool */ template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) Set(GenericDocument& document, T value) const { return Create(document) = value; } //@} //!@name Swap a value //@{ //! Swap a value with a value in a subtree. /*! It creates all parents if they are not exist or types are different to the tokens. So this function always succeeds but potentially remove existing values. \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. \param value Value to be swapped. \param allocator Allocator for creating the values if the specified value or its parents are not exist. \see Create() */ ValueType& Swap(ValueType& root, ValueType& value, typename ValueType::AllocatorType& allocator) const { return Create(root, allocator).Swap(value); } //! Swap a value with a value in a document. template ValueType& Swap(GenericDocument& document, ValueType& value) const { return Create(document).Swap(value); } //@} //! Erase a value in a subtree. /*! \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. \return Whether the resolved value is found and erased. \note Erasing with an empty pointer \c Pointer(""), i.e. the root, always fail and return false. */ bool Erase(ValueType& root) const { RAPIDJSON_ASSERT(IsValid()); if (tokenCount_ == 0) // Cannot erase the root return false; ValueType* v = &root; const Token* last = tokens_ + (tokenCount_ - 1); for (const Token *t = tokens_; t != last; ++t) { switch (v->GetType()) { case kObjectType: { typename ValueType::MemberIterator m = v->FindMember(GenericStringRef(t->name, t->length)); if (m == v->MemberEnd()) return false; v = &m->value; } break; case kArrayType: if (t->index == kPointerInvalidIndex || t->index >= v->Size()) return false; v = &((*v)[t->index]); break; default: return false; } } switch (v->GetType()) { case kObjectType: return v->EraseMember(GenericStringRef(last->name, last->length)); case kArrayType: if (last->index == kPointerInvalidIndex || last->index >= v->Size()) return false; v->Erase(v->Begin() + last->index); return true; default: return false; } } private: //! Clone the content from rhs to this. /*! \param rhs Source pointer. \param extraToken Extra tokens to be allocated. \param extraNameBufferSize Extra name buffer size (in number of Ch) to be allocated. \return Start of non-occupied name buffer, for storing extra names. */ Ch* CopyFromRaw(const GenericPointer& rhs, size_t extraToken = 0, size_t extraNameBufferSize = 0) { if (!allocator_) // allocator is independently owned. ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); size_t nameBufferSize = rhs.tokenCount_; // null terminators for tokens for (Token *t = rhs.tokens_; t != rhs.tokens_ + rhs.tokenCount_; ++t) nameBufferSize += t->length; tokenCount_ = rhs.tokenCount_ + extraToken; tokens_ = static_cast(allocator_->Malloc(tokenCount_ * sizeof(Token) + (nameBufferSize + extraNameBufferSize) * sizeof(Ch))); nameBuffer_ = reinterpret_cast(tokens_ + tokenCount_); if (rhs.tokenCount_ > 0) { std::memcpy(tokens_, rhs.tokens_, rhs.tokenCount_ * sizeof(Token)); } if (nameBufferSize > 0) { std::memcpy(nameBuffer_, rhs.nameBuffer_, nameBufferSize * sizeof(Ch)); } // Adjust pointers to name buffer std::ptrdiff_t diff = nameBuffer_ - rhs.nameBuffer_; for (Token *t = tokens_; t != tokens_ + rhs.tokenCount_; ++t) t->name += diff; return nameBuffer_ + nameBufferSize; } //! Check whether a character should be percent-encoded. /*! According to RFC 3986 2.3 Unreserved Characters. \param c The character (code unit) to be tested. */ bool NeedPercentEncode(Ch c) const { return !((c >= '0' && c <= '9') || (c >= 'A' && c <='Z') || (c >= 'a' && c <= 'z') || c == '-' || c == '.' || c == '_' || c =='~'); } //! Parse a JSON String or its URI fragment representation into tokens. #ifndef __clang__ // -Wdocumentation /*! \param source Either a JSON Pointer string, or its URI fragment representation. Not need to be null terminated. \param length Length of the source string. \note Source cannot be JSON String Representation of JSON Pointer, e.g. In "/\u0000", \u0000 will not be unescaped. */ #endif void Parse(const Ch* source, size_t length) { RAPIDJSON_ASSERT(source != NULL); RAPIDJSON_ASSERT(nameBuffer_ == 0); RAPIDJSON_ASSERT(tokens_ == 0); // Create own allocator if user did not supply. if (!allocator_) ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); // Count number of '/' as tokenCount tokenCount_ = 0; for (const Ch* s = source; s != source + length; s++) if (*s == '/') tokenCount_++; Token* token = tokens_ = static_cast(allocator_->Malloc(tokenCount_ * sizeof(Token) + length * sizeof(Ch))); Ch* name = nameBuffer_ = reinterpret_cast(tokens_ + tokenCount_); size_t i = 0; // Detect if it is a URI fragment bool uriFragment = false; if (source[i] == '#') { uriFragment = true; i++; } if (i != length && source[i] != '/') { parseErrorCode_ = kPointerParseErrorTokenMustBeginWithSolidus; goto error; } while (i < length) { RAPIDJSON_ASSERT(source[i] == '/'); i++; // consumes '/' token->name = name; bool isNumber = true; while (i < length && source[i] != '/') { Ch c = source[i]; if (uriFragment) { // Decoding percent-encoding for URI fragment if (c == '%') { PercentDecodeStream is(&source[i], source + length); GenericInsituStringStream os(name); Ch* begin = os.PutBegin(); if (!Transcoder, EncodingType>().Validate(is, os) || !is.IsValid()) { parseErrorCode_ = kPointerParseErrorInvalidPercentEncoding; goto error; } size_t len = os.PutEnd(begin); i += is.Tell() - 1; if (len == 1) c = *name; else { name += len; isNumber = false; i++; continue; } } else if (NeedPercentEncode(c)) { parseErrorCode_ = kPointerParseErrorCharacterMustPercentEncode; goto error; } } i++; // Escaping "~0" -> '~', "~1" -> '/' if (c == '~') { if (i < length) { c = source[i]; if (c == '0') c = '~'; else if (c == '1') c = '/'; else { parseErrorCode_ = kPointerParseErrorInvalidEscape; goto error; } i++; } else { parseErrorCode_ = kPointerParseErrorInvalidEscape; goto error; } } // First check for index: all of characters are digit if (c < '0' || c > '9') isNumber = false; *name++ = c; } token->length = static_cast(name - token->name); if (token->length == 0) isNumber = false; *name++ = '\0'; // Null terminator // Second check for index: more than one digit cannot have leading zero if (isNumber && token->length > 1 && token->name[0] == '0') isNumber = false; // String to SizeType conversion SizeType n = 0; if (isNumber) { for (size_t j = 0; j < token->length; j++) { SizeType m = n * 10 + static_cast(token->name[j] - '0'); if (m < n) { // overflow detection isNumber = false; break; } n = m; } } token->index = isNumber ? n : kPointerInvalidIndex; token++; } RAPIDJSON_ASSERT(name <= nameBuffer_ + length); // Should not overflow buffer parseErrorCode_ = kPointerParseErrorNone; return; error: Allocator::Free(tokens_); nameBuffer_ = 0; tokens_ = 0; tokenCount_ = 0; parseErrorOffset_ = i; return; } //! Stringify to string or URI fragment representation. /*! \tparam uriFragment True for stringifying to URI fragment representation. False for string representation. \tparam OutputStream type of output stream. \param os The output stream. */ template bool Stringify(OutputStream& os) const { RAPIDJSON_ASSERT(IsValid()); if (uriFragment) os.Put('#'); for (Token *t = tokens_; t != tokens_ + tokenCount_; ++t) { os.Put('/'); for (size_t j = 0; j < t->length; j++) { Ch c = t->name[j]; if (c == '~') { os.Put('~'); os.Put('0'); } else if (c == '/') { os.Put('~'); os.Put('1'); } else if (uriFragment && NeedPercentEncode(c)) { // Transcode to UTF8 sequence GenericStringStream source(&t->name[j]); PercentEncodeStream target(os); if (!Transcoder >().Validate(source, target)) return false; j += source.Tell() - 1; } else os.Put(c); } } return true; } //! A helper stream for decoding a percent-encoded sequence into code unit. /*! This stream decodes %XY triplet into code unit (0-255). If it encounters invalid characters, it sets output code unit as 0 and mark invalid, and to be checked by IsValid(). */ class PercentDecodeStream { public: typedef typename ValueType::Ch Ch; //! Constructor /*! \param source Start of the stream \param end Past-the-end of the stream. */ PercentDecodeStream(const Ch* source, const Ch* end) : src_(source), head_(source), end_(end), valid_(true) {} Ch Take() { if (*src_ != '%' || src_ + 3 > end_) { // %XY triplet valid_ = false; return 0; } src_++; Ch c = 0; for (int j = 0; j < 2; j++) { c = static_cast(c << 4); Ch h = *src_; if (h >= '0' && h <= '9') c = static_cast(c + h - '0'); else if (h >= 'A' && h <= 'F') c = static_cast(c + h - 'A' + 10); else if (h >= 'a' && h <= 'f') c = static_cast(c + h - 'a' + 10); else { valid_ = false; return 0; } src_++; } return c; } size_t Tell() const { return static_cast(src_ - head_); } bool IsValid() const { return valid_; } private: const Ch* src_; //!< Current read position. const Ch* head_; //!< Original head of the string. const Ch* end_; //!< Past-the-end position. bool valid_; //!< Whether the parsing is valid. }; //! A helper stream to encode character (UTF-8 code unit) into percent-encoded sequence. template class PercentEncodeStream { public: PercentEncodeStream(OutputStream& os) : os_(os) {} void Put(char c) { // UTF-8 must be byte unsigned char u = static_cast(c); static const char hexDigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; os_.Put('%'); os_.Put(static_cast(hexDigits[u >> 4])); os_.Put(static_cast(hexDigits[u & 15])); } private: OutputStream& os_; }; Allocator* allocator_; //!< The current allocator. It is either user-supplied or equal to ownAllocator_. Allocator* ownAllocator_; //!< Allocator owned by this Pointer. Ch* nameBuffer_; //!< A buffer containing all names in tokens. Token* tokens_; //!< A list of tokens. size_t tokenCount_; //!< Number of tokens in tokens_. size_t parseErrorOffset_; //!< Offset in code unit when parsing fail. PointerParseErrorCode parseErrorCode_; //!< Parsing error code. }; //! GenericPointer for Value (UTF-8, default allocator). typedef GenericPointer Pointer; //!@name Helper functions for GenericPointer //@{ ////////////////////////////////////////////////////////////////////////////// template typename T::ValueType& CreateValueByPointer(T& root, const GenericPointer& pointer, typename T::AllocatorType& a) { return pointer.Create(root, a); } template typename T::ValueType& CreateValueByPointer(T& root, const CharType(&source)[N], typename T::AllocatorType& a) { return GenericPointer(source, N - 1).Create(root, a); } // No allocator parameter template typename DocumentType::ValueType& CreateValueByPointer(DocumentType& document, const GenericPointer& pointer) { return pointer.Create(document); } template typename DocumentType::ValueType& CreateValueByPointer(DocumentType& document, const CharType(&source)[N]) { return GenericPointer(source, N - 1).Create(document); } ////////////////////////////////////////////////////////////////////////////// template typename T::ValueType* GetValueByPointer(T& root, const GenericPointer& pointer, size_t* unresolvedTokenIndex = 0) { return pointer.Get(root, unresolvedTokenIndex); } template const typename T::ValueType* GetValueByPointer(const T& root, const GenericPointer& pointer, size_t* unresolvedTokenIndex = 0) { return pointer.Get(root, unresolvedTokenIndex); } template typename T::ValueType* GetValueByPointer(T& root, const CharType (&source)[N], size_t* unresolvedTokenIndex = 0) { return GenericPointer(source, N - 1).Get(root, unresolvedTokenIndex); } template const typename T::ValueType* GetValueByPointer(const T& root, const CharType(&source)[N], size_t* unresolvedTokenIndex = 0) { return GenericPointer(source, N - 1).Get(root, unresolvedTokenIndex); } ////////////////////////////////////////////////////////////////////////////// template typename T::ValueType& GetValueByPointerWithDefault(T& root, const GenericPointer& pointer, const typename T::ValueType& defaultValue, typename T::AllocatorType& a) { return pointer.GetWithDefault(root, defaultValue, a); } template typename T::ValueType& GetValueByPointerWithDefault(T& root, const GenericPointer& pointer, const typename T::Ch* defaultValue, typename T::AllocatorType& a) { return pointer.GetWithDefault(root, defaultValue, a); } #if RAPIDJSON_HAS_STDSTRING template typename T::ValueType& GetValueByPointerWithDefault(T& root, const GenericPointer& pointer, const std::basic_string& defaultValue, typename T::AllocatorType& a) { return pointer.GetWithDefault(root, defaultValue, a); } #endif template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename T::ValueType&)) GetValueByPointerWithDefault(T& root, const GenericPointer& pointer, T2 defaultValue, typename T::AllocatorType& a) { return pointer.GetWithDefault(root, defaultValue, a); } template typename T::ValueType& GetValueByPointerWithDefault(T& root, const CharType(&source)[N], const typename T::ValueType& defaultValue, typename T::AllocatorType& a) { return GenericPointer(source, N - 1).GetWithDefault(root, defaultValue, a); } template typename T::ValueType& GetValueByPointerWithDefault(T& root, const CharType(&source)[N], const typename T::Ch* defaultValue, typename T::AllocatorType& a) { return GenericPointer(source, N - 1).GetWithDefault(root, defaultValue, a); } #if RAPIDJSON_HAS_STDSTRING template typename T::ValueType& GetValueByPointerWithDefault(T& root, const CharType(&source)[N], const std::basic_string& defaultValue, typename T::AllocatorType& a) { return GenericPointer(source, N - 1).GetWithDefault(root, defaultValue, a); } #endif template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename T::ValueType&)) GetValueByPointerWithDefault(T& root, const CharType(&source)[N], T2 defaultValue, typename T::AllocatorType& a) { return GenericPointer(source, N - 1).GetWithDefault(root, defaultValue, a); } // No allocator parameter template typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const GenericPointer& pointer, const typename DocumentType::ValueType& defaultValue) { return pointer.GetWithDefault(document, defaultValue); } template typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const GenericPointer& pointer, const typename DocumentType::Ch* defaultValue) { return pointer.GetWithDefault(document, defaultValue); } #if RAPIDJSON_HAS_STDSTRING template typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const GenericPointer& pointer, const std::basic_string& defaultValue) { return pointer.GetWithDefault(document, defaultValue); } #endif template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename DocumentType::ValueType&)) GetValueByPointerWithDefault(DocumentType& document, const GenericPointer& pointer, T2 defaultValue) { return pointer.GetWithDefault(document, defaultValue); } template typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], const typename DocumentType::ValueType& defaultValue) { return GenericPointer(source, N - 1).GetWithDefault(document, defaultValue); } template typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], const typename DocumentType::Ch* defaultValue) { return GenericPointer(source, N - 1).GetWithDefault(document, defaultValue); } #if RAPIDJSON_HAS_STDSTRING template typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], const std::basic_string& defaultValue) { return GenericPointer(source, N - 1).GetWithDefault(document, defaultValue); } #endif template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename DocumentType::ValueType&)) GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], T2 defaultValue) { return GenericPointer(source, N - 1).GetWithDefault(document, defaultValue); } ////////////////////////////////////////////////////////////////////////////// template typename T::ValueType& SetValueByPointer(T& root, const GenericPointer& pointer, typename T::ValueType& value, typename T::AllocatorType& a) { return pointer.Set(root, value, a); } template typename T::ValueType& SetValueByPointer(T& root, const GenericPointer& pointer, const typename T::ValueType& value, typename T::AllocatorType& a) { return pointer.Set(root, value, a); } template typename T::ValueType& SetValueByPointer(T& root, const GenericPointer& pointer, const typename T::Ch* value, typename T::AllocatorType& a) { return pointer.Set(root, value, a); } #if RAPIDJSON_HAS_STDSTRING template typename T::ValueType& SetValueByPointer(T& root, const GenericPointer& pointer, const std::basic_string& value, typename T::AllocatorType& a) { return pointer.Set(root, value, a); } #endif template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename T::ValueType&)) SetValueByPointer(T& root, const GenericPointer& pointer, T2 value, typename T::AllocatorType& a) { return pointer.Set(root, value, a); } template typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], typename T::ValueType& value, typename T::AllocatorType& a) { return GenericPointer(source, N - 1).Set(root, value, a); } template typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], const typename T::ValueType& value, typename T::AllocatorType& a) { return GenericPointer(source, N - 1).Set(root, value, a); } template typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], const typename T::Ch* value, typename T::AllocatorType& a) { return GenericPointer(source, N - 1).Set(root, value, a); } #if RAPIDJSON_HAS_STDSTRING template typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], const std::basic_string& value, typename T::AllocatorType& a) { return GenericPointer(source, N - 1).Set(root, value, a); } #endif template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename T::ValueType&)) SetValueByPointer(T& root, const CharType(&source)[N], T2 value, typename T::AllocatorType& a) { return GenericPointer(source, N - 1).Set(root, value, a); } // No allocator parameter template typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer& pointer, typename DocumentType::ValueType& value) { return pointer.Set(document, value); } template typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer& pointer, const typename DocumentType::ValueType& value) { return pointer.Set(document, value); } template typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer& pointer, const typename DocumentType::Ch* value) { return pointer.Set(document, value); } #if RAPIDJSON_HAS_STDSTRING template typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer& pointer, const std::basic_string& value) { return pointer.Set(document, value); } #endif template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename DocumentType::ValueType&)) SetValueByPointer(DocumentType& document, const GenericPointer& pointer, T2 value) { return pointer.Set(document, value); } template typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], typename DocumentType::ValueType& value) { return GenericPointer(source, N - 1).Set(document, value); } template typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], const typename DocumentType::ValueType& value) { return GenericPointer(source, N - 1).Set(document, value); } template typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], const typename DocumentType::Ch* value) { return GenericPointer(source, N - 1).Set(document, value); } #if RAPIDJSON_HAS_STDSTRING template typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], const std::basic_string& value) { return GenericPointer(source, N - 1).Set(document, value); } #endif template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename DocumentType::ValueType&)) SetValueByPointer(DocumentType& document, const CharType(&source)[N], T2 value) { return GenericPointer(source, N - 1).Set(document, value); } ////////////////////////////////////////////////////////////////////////////// template typename T::ValueType& SwapValueByPointer(T& root, const GenericPointer& pointer, typename T::ValueType& value, typename T::AllocatorType& a) { return pointer.Swap(root, value, a); } template typename T::ValueType& SwapValueByPointer(T& root, const CharType(&source)[N], typename T::ValueType& value, typename T::AllocatorType& a) { return GenericPointer(source, N - 1).Swap(root, value, a); } template typename DocumentType::ValueType& SwapValueByPointer(DocumentType& document, const GenericPointer& pointer, typename DocumentType::ValueType& value) { return pointer.Swap(document, value); } template typename DocumentType::ValueType& SwapValueByPointer(DocumentType& document, const CharType(&source)[N], typename DocumentType::ValueType& value) { return GenericPointer(source, N - 1).Swap(document, value); } ////////////////////////////////////////////////////////////////////////////// template bool EraseValueByPointer(T& root, const GenericPointer& pointer) { return pointer.Erase(root); } template bool EraseValueByPointer(T& root, const CharType(&source)[N]) { return GenericPointer(source, N - 1).Erase(root); } //@} RAPIDJSON_NAMESPACE_END #if defined(__clang__) || defined(_MSC_VER) RAPIDJSON_DIAG_POP #endif #endif // RAPIDJSON_POINTER_H_ ================================================ FILE: src/json/rapidjson/prettywriter.h ================================================ // Tencent is pleased to support the open source community by making RapidJSON available. // // Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at // // http://opensource.org/licenses/MIT // // Unless required by applicable law or agreed to in writing, software distributed // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. #ifndef RAPIDJSON_PRETTYWRITER_H_ #define RAPIDJSON_PRETTYWRITER_H_ #include "writer.h" #ifdef __GNUC__ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(effc++) #endif #if defined(__clang__) RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(c++98-compat) #endif RAPIDJSON_NAMESPACE_BEGIN //! Combination of PrettyWriter format flags. /*! \see PrettyWriter::SetFormatOptions */ enum PrettyFormatOptions { kFormatDefault = 0, //!< Default pretty formatting. kFormatSingleLineArray = 1 //!< Format arrays on a single line. }; //! Writer with indentation and spacing. /*! \tparam OutputStream Type of output os. \tparam SourceEncoding Encoding of source string. \tparam TargetEncoding Encoding of output stream. \tparam StackAllocator Type of allocator for allocating memory of stack. */ template, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator, unsigned writeFlags = kWriteDefaultFlags> class PrettyWriter : public Writer { public: typedef Writer Base; typedef typename Base::Ch Ch; //! Constructor /*! \param os Output stream. \param allocator User supplied allocator. If it is null, it will create a private one. \param levelDepth Initial capacity of stack. */ explicit PrettyWriter(OutputStream& os, StackAllocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) : Base(os, allocator, levelDepth), indentChar_(' '), indentCharCount_(4), formatOptions_(kFormatDefault) {} explicit PrettyWriter(StackAllocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) : Base(allocator, levelDepth), indentChar_(' '), indentCharCount_(4) {} #if RAPIDJSON_HAS_CXX11_RVALUE_REFS PrettyWriter(PrettyWriter&& rhs) : Base(std::forward(rhs)), indentChar_(rhs.indentChar_), indentCharCount_(rhs.indentCharCount_), formatOptions_(rhs.formatOptions_) {} #endif //! Set custom indentation. /*! \param indentChar Character for indentation. Must be whitespace character (' ', '\\t', '\\n', '\\r'). \param indentCharCount Number of indent characters for each indentation level. \note The default indentation is 4 spaces. */ PrettyWriter& SetIndent(Ch indentChar, unsigned indentCharCount) { RAPIDJSON_ASSERT(indentChar == ' ' || indentChar == '\t' || indentChar == '\n' || indentChar == '\r'); indentChar_ = indentChar; indentCharCount_ = indentCharCount; return *this; } //! Set pretty writer formatting options. /*! \param options Formatting options. */ PrettyWriter& SetFormatOptions(PrettyFormatOptions options) { formatOptions_ = options; return *this; } /*! @name Implementation of Handler \see Handler */ //@{ bool Null() { PrettyPrefix(kNullType); return Base::EndValue(Base::WriteNull()); } bool Bool(bool b) { PrettyPrefix(b ? kTrueType : kFalseType); return Base::EndValue(Base::WriteBool(b)); } bool Int(int i) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteInt(i)); } bool Uint(unsigned u) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteUint(u)); } bool Int64(int64_t i64) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteInt64(i64)); } bool Uint64(uint64_t u64) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteUint64(u64)); } bool Double(double d) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteDouble(d)); } bool RawNumber(const Ch* str, SizeType length, bool copy = false) { RAPIDJSON_ASSERT(str != 0); (void)copy; PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteString(str, length)); } bool String(const Ch* str, SizeType length, bool copy = false) { RAPIDJSON_ASSERT(str != 0); (void)copy; PrettyPrefix(kStringType); return Base::EndValue(Base::WriteString(str, length)); } #if RAPIDJSON_HAS_STDSTRING bool String(const std::basic_string& str) { return String(str.data(), SizeType(str.size())); } #endif bool StartObject() { PrettyPrefix(kObjectType); new (Base::level_stack_.template Push()) typename Base::Level(false); return Base::WriteStartObject(); } bool Key(const Ch* str, SizeType length, bool copy = false) { return String(str, length, copy); } #if RAPIDJSON_HAS_STDSTRING bool Key(const std::basic_string& str) { return Key(str.data(), SizeType(str.size())); } #endif bool EndObject(SizeType memberCount = 0) { (void)memberCount; RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level)); // not inside an Object RAPIDJSON_ASSERT(!Base::level_stack_.template Top()->inArray); // currently inside an Array, not Object RAPIDJSON_ASSERT(0 == Base::level_stack_.template Top()->valueCount % 2); // Object has a Key without a Value bool empty = Base::level_stack_.template Pop(1)->valueCount == 0; if (!empty) { Base::os_->Put('\n'); WriteIndent(); } bool ret = Base::EndValue(Base::WriteEndObject()); (void)ret; RAPIDJSON_ASSERT(ret == true); if (Base::level_stack_.Empty()) // end of json text Base::Flush(); return true; } bool StartArray() { PrettyPrefix(kArrayType); new (Base::level_stack_.template Push()) typename Base::Level(true); return Base::WriteStartArray(); } bool EndArray(SizeType memberCount = 0) { (void)memberCount; RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level)); RAPIDJSON_ASSERT(Base::level_stack_.template Top()->inArray); bool empty = Base::level_stack_.template Pop(1)->valueCount == 0; if (!empty && !(formatOptions_ & kFormatSingleLineArray)) { Base::os_->Put('\n'); WriteIndent(); } bool ret = Base::EndValue(Base::WriteEndArray()); (void)ret; RAPIDJSON_ASSERT(ret == true); if (Base::level_stack_.Empty()) // end of json text Base::Flush(); return true; } //@} /*! @name Convenience extensions */ //@{ //! Simpler but slower overload. bool String(const Ch* str) { return String(str, internal::StrLen(str)); } bool Key(const Ch* str) { return Key(str, internal::StrLen(str)); } //@} //! Write a raw JSON value. /*! For user to write a stringified JSON as a value. \param json A well-formed JSON value. It should not contain null character within [0, length - 1] range. \param length Length of the json. \param type Type of the root of json. \note When using PrettyWriter::RawValue(), the result json may not be indented correctly. */ bool RawValue(const Ch* json, size_t length, Type type) { RAPIDJSON_ASSERT(json != 0); PrettyPrefix(type); return Base::EndValue(Base::WriteRawValue(json, length)); } protected: void PrettyPrefix(Type type) { (void)type; if (Base::level_stack_.GetSize() != 0) { // this value is not at root typename Base::Level* level = Base::level_stack_.template Top(); if (level->inArray) { if (level->valueCount > 0) { Base::os_->Put(','); // add comma if it is not the first element in array if (formatOptions_ & kFormatSingleLineArray) Base::os_->Put(' '); } if (!(formatOptions_ & kFormatSingleLineArray)) { Base::os_->Put('\n'); WriteIndent(); } } else { // in object if (level->valueCount > 0) { if (level->valueCount % 2 == 0) { Base::os_->Put(','); Base::os_->Put('\n'); } else { Base::os_->Put(':'); Base::os_->Put(' '); } } else Base::os_->Put('\n'); if (level->valueCount % 2 == 0) WriteIndent(); } if (!level->inArray && level->valueCount % 2 == 0) RAPIDJSON_ASSERT(type == kStringType); // if it's in object, then even number should be a name level->valueCount++; } else { RAPIDJSON_ASSERT(!Base::hasRoot_); // Should only has one and only one root. Base::hasRoot_ = true; } } void WriteIndent() { size_t count = (Base::level_stack_.GetSize() / sizeof(typename Base::Level)) * indentCharCount_; PutN(*Base::os_, static_cast(indentChar_), count); } Ch indentChar_; unsigned indentCharCount_; PrettyFormatOptions formatOptions_; private: // Prohibit copy constructor & assignment operator. PrettyWriter(const PrettyWriter&); PrettyWriter& operator=(const PrettyWriter&); }; RAPIDJSON_NAMESPACE_END #if defined(__clang__) RAPIDJSON_DIAG_POP #endif #ifdef __GNUC__ RAPIDJSON_DIAG_POP #endif #endif // RAPIDJSON_RAPIDJSON_H_ ================================================ FILE: src/json/rapidjson/rapidjson.h ================================================ // Tencent is pleased to support the open source community by making RapidJSON available. // // Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at // // http://opensource.org/licenses/MIT // // Unless required by applicable law or agreed to in writing, software distributed // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. #ifndef RAPIDJSON_RAPIDJSON_H_ #define RAPIDJSON_RAPIDJSON_H_ /*!\file rapidjson.h \brief common definitions and configuration \see RAPIDJSON_CONFIG */ /*! \defgroup RAPIDJSON_CONFIG RapidJSON configuration \brief Configuration macros for library features Some RapidJSON features are configurable to adapt the library to a wide variety of platforms, environments and usage scenarios. Most of the features can be configured in terms of overridden or predefined preprocessor macros at compile-time. Some additional customization is available in the \ref RAPIDJSON_ERRORS APIs. \note These macros should be given on the compiler command-line (where applicable) to avoid inconsistent values when compiling different translation units of a single application. */ #include // malloc(), realloc(), free(), size_t #include // memset(), memcpy(), memmove(), memcmp() /////////////////////////////////////////////////////////////////////////////// // RAPIDJSON_VERSION_STRING // // ALWAYS synchronize the following 3 macros with corresponding variables in /CMakeLists.txt. // //!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN // token stringification #define RAPIDJSON_STRINGIFY(x) RAPIDJSON_DO_STRINGIFY(x) #define RAPIDJSON_DO_STRINGIFY(x) #x // token concatenation #define RAPIDJSON_JOIN(X, Y) RAPIDJSON_DO_JOIN(X, Y) #define RAPIDJSON_DO_JOIN(X, Y) RAPIDJSON_DO_JOIN2(X, Y) #define RAPIDJSON_DO_JOIN2(X, Y) X##Y //!@endcond /*! \def RAPIDJSON_MAJOR_VERSION \ingroup RAPIDJSON_CONFIG \brief Major version of RapidJSON in integer. */ /*! \def RAPIDJSON_MINOR_VERSION \ingroup RAPIDJSON_CONFIG \brief Minor version of RapidJSON in integer. */ /*! \def RAPIDJSON_PATCH_VERSION \ingroup RAPIDJSON_CONFIG \brief Patch version of RapidJSON in integer. */ /*! \def RAPIDJSON_VERSION_STRING \ingroup RAPIDJSON_CONFIG \brief Version of RapidJSON in ".." string format. */ #define RAPIDJSON_MAJOR_VERSION 1 #define RAPIDJSON_MINOR_VERSION 1 #define RAPIDJSON_PATCH_VERSION 0 #define RAPIDJSON_VERSION_STRING \ RAPIDJSON_STRINGIFY(RAPIDJSON_MAJOR_VERSION.RAPIDJSON_MINOR_VERSION.RAPIDJSON_PATCH_VERSION) /////////////////////////////////////////////////////////////////////////////// // RAPIDJSON_NAMESPACE_(BEGIN|END) /*! \def RAPIDJSON_NAMESPACE \ingroup RAPIDJSON_CONFIG \brief provide custom rapidjson namespace In order to avoid symbol clashes and/or "One Definition Rule" errors between multiple inclusions of (different versions of) RapidJSON in a single binary, users can customize the name of the main RapidJSON namespace. In case of a single nesting level, defining \c RAPIDJSON_NAMESPACE to a custom name (e.g. \c MyRapidJSON) is sufficient. If multiple levels are needed, both \ref RAPIDJSON_NAMESPACE_BEGIN and \ref RAPIDJSON_NAMESPACE_END need to be defined as well: \code // in some .cpp file #define RAPIDJSON_NAMESPACE my::rapidjson #define RAPIDJSON_NAMESPACE_BEGIN namespace my { namespace rapidjson { #define RAPIDJSON_NAMESPACE_END } } #include "rapidjson/..." \endcode \see rapidjson */ /*! \def RAPIDJSON_NAMESPACE_BEGIN \ingroup RAPIDJSON_CONFIG \brief provide custom rapidjson namespace (opening expression) \see RAPIDJSON_NAMESPACE */ /*! \def RAPIDJSON_NAMESPACE_END \ingroup RAPIDJSON_CONFIG \brief provide custom rapidjson namespace (closing expression) \see RAPIDJSON_NAMESPACE */ #ifndef RAPIDJSON_NAMESPACE #define RAPIDJSON_NAMESPACE rapidjson #endif #ifndef RAPIDJSON_NAMESPACE_BEGIN #define RAPIDJSON_NAMESPACE_BEGIN namespace RAPIDJSON_NAMESPACE { #endif #ifndef RAPIDJSON_NAMESPACE_END #define RAPIDJSON_NAMESPACE_END } #endif /////////////////////////////////////////////////////////////////////////////// // RAPIDJSON_HAS_STDSTRING #ifndef RAPIDJSON_HAS_STDSTRING #ifdef RAPIDJSON_DOXYGEN_RUNNING #define RAPIDJSON_HAS_STDSTRING 1 // force generation of documentation #else #define RAPIDJSON_HAS_STDSTRING 0 // no std::string support by default #endif /*! \def RAPIDJSON_HAS_STDSTRING \ingroup RAPIDJSON_CONFIG \brief Enable RapidJSON support for \c std::string By defining this preprocessor symbol to \c 1, several convenience functions for using \ref rapidjson::GenericValue with \c std::string are enabled, especially for construction and comparison. \hideinitializer */ #endif // !defined(RAPIDJSON_HAS_STDSTRING) #if RAPIDJSON_HAS_STDSTRING #include #endif // RAPIDJSON_HAS_STDSTRING /////////////////////////////////////////////////////////////////////////////// // RAPIDJSON_NO_INT64DEFINE /*! \def RAPIDJSON_NO_INT64DEFINE \ingroup RAPIDJSON_CONFIG \brief Use external 64-bit integer types. RapidJSON requires the 64-bit integer types \c int64_t and \c uint64_t types to be available at global scope. If users have their own definition, define RAPIDJSON_NO_INT64DEFINE to prevent RapidJSON from defining its own types. */ #ifndef RAPIDJSON_NO_INT64DEFINE //!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN #if defined(_MSC_VER) && (_MSC_VER < 1800) // Visual Studio 2013 #include "msinttypes/stdint.h" #include "msinttypes/inttypes.h" #else // Other compilers should have this. #include #include #endif //!@endcond #ifdef RAPIDJSON_DOXYGEN_RUNNING #define RAPIDJSON_NO_INT64DEFINE #endif #endif // RAPIDJSON_NO_INT64TYPEDEF /////////////////////////////////////////////////////////////////////////////// // RAPIDJSON_FORCEINLINE #ifndef RAPIDJSON_FORCEINLINE //!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN #if defined(_MSC_VER) && defined(NDEBUG) #define RAPIDJSON_FORCEINLINE __forceinline #elif defined(__GNUC__) && __GNUC__ >= 4 && defined(NDEBUG) #define RAPIDJSON_FORCEINLINE __attribute__((always_inline)) #else #define RAPIDJSON_FORCEINLINE #endif //!@endcond #endif // RAPIDJSON_FORCEINLINE /////////////////////////////////////////////////////////////////////////////// // RAPIDJSON_ENDIAN #define RAPIDJSON_LITTLEENDIAN 0 //!< Little endian machine #define RAPIDJSON_BIGENDIAN 1 //!< Big endian machine //! Endianness of the machine. /*! \def RAPIDJSON_ENDIAN \ingroup RAPIDJSON_CONFIG GCC 4.6 provided macro for detecting endianness of the target machine. But other compilers may not have this. User can define RAPIDJSON_ENDIAN to either \ref RAPIDJSON_LITTLEENDIAN or \ref RAPIDJSON_BIGENDIAN. Default detection implemented with reference to \li https://gcc.gnu.org/onlinedocs/gcc-4.6.0/cpp/Common-Predefined-Macros.html \li http://www.boost.org/doc/libs/1_42_0/boost/detail/endian.hpp */ #ifndef RAPIDJSON_ENDIAN // Detect with GCC 4.6's macro # ifdef __BYTE_ORDER__ # if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ # define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN # elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ # define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN # else # error Unknown machine endianness detected. User needs to define RAPIDJSON_ENDIAN. # endif // __BYTE_ORDER__ // Detect with GLIBC's endian.h # elif defined(__GLIBC__) # include # if (__BYTE_ORDER == __LITTLE_ENDIAN) # define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN # elif (__BYTE_ORDER == __BIG_ENDIAN) # define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN # else # error Unknown machine endianness detected. User needs to define RAPIDJSON_ENDIAN. # endif // __GLIBC__ // Detect with _LITTLE_ENDIAN and _BIG_ENDIAN macro # elif defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN) # define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN # elif defined(_BIG_ENDIAN) && !defined(_LITTLE_ENDIAN) # define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN // Detect with architecture macros # elif defined(__sparc) || defined(__sparc__) || defined(_POWER) || defined(__powerpc__) || defined(__ppc__) || defined(__hpux) || defined(__hppa) || defined(_MIPSEB) || defined(_POWER) || defined(__s390__) # define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN # elif defined(__i386__) || defined(__alpha__) || defined(__ia64) || defined(__ia64__) || defined(_M_IX86) || defined(_M_IA64) || defined(_M_ALPHA) || defined(__amd64) || defined(__amd64__) || defined(_M_AMD64) || defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || defined(__bfin__) # define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN # elif defined(_MSC_VER) && (defined(_M_ARM) || defined(_M_ARM64)) # define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN # elif defined(RAPIDJSON_DOXYGEN_RUNNING) # define RAPIDJSON_ENDIAN # else # error Unknown machine endianness detected. User needs to define RAPIDJSON_ENDIAN. # endif #endif // RAPIDJSON_ENDIAN /////////////////////////////////////////////////////////////////////////////// // RAPIDJSON_64BIT //! Whether using 64-bit architecture #ifndef RAPIDJSON_64BIT #if defined(__LP64__) || (defined(__x86_64__) && defined(__ILP32__)) || defined(_WIN64) || defined(__EMSCRIPTEN__) #define RAPIDJSON_64BIT 1 #else #define RAPIDJSON_64BIT 0 #endif #endif // RAPIDJSON_64BIT /////////////////////////////////////////////////////////////////////////////// // RAPIDJSON_ALIGN //! Data alignment of the machine. /*! \ingroup RAPIDJSON_CONFIG \param x pointer to align Some machines require strict data alignment. The default is 8 bytes. User can customize by defining the RAPIDJSON_ALIGN function macro. */ #ifndef RAPIDJSON_ALIGN #define RAPIDJSON_ALIGN(x) (((x) + static_cast(7u)) & ~static_cast(7u)) #endif /////////////////////////////////////////////////////////////////////////////// // RAPIDJSON_UINT64_C2 //! Construct a 64-bit literal by a pair of 32-bit integer. /*! 64-bit literal with or without ULL suffix is prone to compiler warnings. UINT64_C() is C macro which cause compilation problems. Use this macro to define 64-bit constants by a pair of 32-bit integer. */ #ifndef RAPIDJSON_UINT64_C2 #define RAPIDJSON_UINT64_C2(high32, low32) ((static_cast(high32) << 32) | static_cast(low32)) #endif /////////////////////////////////////////////////////////////////////////////// // RAPIDJSON_48BITPOINTER_OPTIMIZATION //! Use only lower 48-bit address for some pointers. /*! \ingroup RAPIDJSON_CONFIG This optimization uses the fact that current X86-64 architecture only implement lower 48-bit virtual address. The higher 16-bit can be used for storing other data. \c GenericValue uses this optimization to reduce its size form 24 bytes to 16 bytes in 64-bit architecture. */ #ifndef RAPIDJSON_48BITPOINTER_OPTIMIZATION #if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_X64) || defined(_M_AMD64) #define RAPIDJSON_48BITPOINTER_OPTIMIZATION 1 #else #define RAPIDJSON_48BITPOINTER_OPTIMIZATION 0 #endif #endif // RAPIDJSON_48BITPOINTER_OPTIMIZATION #if RAPIDJSON_48BITPOINTER_OPTIMIZATION == 1 #if RAPIDJSON_64BIT != 1 #error RAPIDJSON_48BITPOINTER_OPTIMIZATION can only be set to 1 when RAPIDJSON_64BIT=1 #endif #define RAPIDJSON_SETPOINTER(type, p, x) (p = reinterpret_cast((reinterpret_cast(p) & static_cast(RAPIDJSON_UINT64_C2(0xFFFF0000, 0x00000000))) | reinterpret_cast(reinterpret_cast(x)))) #define RAPIDJSON_GETPOINTER(type, p) (reinterpret_cast(reinterpret_cast(p) & static_cast(RAPIDJSON_UINT64_C2(0x0000FFFF, 0xFFFFFFFF)))) #else #define RAPIDJSON_SETPOINTER(type, p, x) (p = (x)) #define RAPIDJSON_GETPOINTER(type, p) (p) #endif /////////////////////////////////////////////////////////////////////////////// // RAPIDJSON_SSE2/RAPIDJSON_SSE42/RAPIDJSON_NEON/RAPIDJSON_SIMD /*! \def RAPIDJSON_SIMD \ingroup RAPIDJSON_CONFIG \brief Enable SSE2/SSE4.2/Neon optimization. RapidJSON supports optimized implementations for some parsing operations based on the SSE2, SSE4.2 or NEon SIMD extensions on modern Intel or ARM compatible processors. To enable these optimizations, three different symbols can be defined; \code // Enable SSE2 optimization. #define RAPIDJSON_SSE2 // Enable SSE4.2 optimization. #define RAPIDJSON_SSE42 \endcode // Enable ARM Neon optimization. #define RAPIDJSON_NEON \endcode \c RAPIDJSON_SSE42 takes precedence over SSE2, if both are defined. If any of these symbols is defined, RapidJSON defines the macro \c RAPIDJSON_SIMD to indicate the availability of the optimized code. */ #if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42) \ || defined(RAPIDJSON_NEON) || defined(RAPIDJSON_DOXYGEN_RUNNING) #define RAPIDJSON_SIMD #endif /////////////////////////////////////////////////////////////////////////////// // RAPIDJSON_NO_SIZETYPEDEFINE #ifndef RAPIDJSON_NO_SIZETYPEDEFINE /*! \def RAPIDJSON_NO_SIZETYPEDEFINE \ingroup RAPIDJSON_CONFIG \brief User-provided \c SizeType definition. In order to avoid using 32-bit size types for indexing strings and arrays, define this preprocessor symbol and provide the type rapidjson::SizeType before including RapidJSON: \code #define RAPIDJSON_NO_SIZETYPEDEFINE namespace rapidjson { typedef ::std::size_t SizeType; } #include "rapidjson/..." \endcode \see rapidjson::SizeType */ #ifdef RAPIDJSON_DOXYGEN_RUNNING #define RAPIDJSON_NO_SIZETYPEDEFINE #endif RAPIDJSON_NAMESPACE_BEGIN //! Size type (for string lengths, array sizes, etc.) /*! RapidJSON uses 32-bit array/string indices even on 64-bit platforms, instead of using \c size_t. Users may override the SizeType by defining \ref RAPIDJSON_NO_SIZETYPEDEFINE. */ typedef unsigned SizeType; RAPIDJSON_NAMESPACE_END #endif // always import std::size_t to rapidjson namespace RAPIDJSON_NAMESPACE_BEGIN using std::size_t; RAPIDJSON_NAMESPACE_END /////////////////////////////////////////////////////////////////////////////// // RAPIDJSON_ASSERT //! Assertion. /*! \ingroup RAPIDJSON_CONFIG By default, rapidjson uses C \c assert() for internal assertions. User can override it by defining RAPIDJSON_ASSERT(x) macro. \note Parsing errors are handled and can be customized by the \ref RAPIDJSON_ERRORS APIs. */ #ifndef RAPIDJSON_ASSERT #include #define RAPIDJSON_ASSERT(x) assert(x) #endif // RAPIDJSON_ASSERT /////////////////////////////////////////////////////////////////////////////// // RAPIDJSON_STATIC_ASSERT // Prefer C++11 static_assert, if available #ifndef RAPIDJSON_STATIC_ASSERT #if __cplusplus >= 201103L || ( defined(_MSC_VER) && _MSC_VER >= 1800 ) #define RAPIDJSON_STATIC_ASSERT(x) \ static_assert(x, RAPIDJSON_STRINGIFY(x)) #endif // C++11 #endif // RAPIDJSON_STATIC_ASSERT // Adopt C++03 implementation from boost #ifndef RAPIDJSON_STATIC_ASSERT #ifndef __clang__ //!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN #endif RAPIDJSON_NAMESPACE_BEGIN template struct STATIC_ASSERTION_FAILURE; template <> struct STATIC_ASSERTION_FAILURE { enum { value = 1 }; }; template struct StaticAssertTest {}; RAPIDJSON_NAMESPACE_END #if defined(__GNUC__) || defined(__clang__) #define RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE __attribute__((unused)) #else #define RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE #endif #ifndef __clang__ //!@endcond #endif /*! \def RAPIDJSON_STATIC_ASSERT \brief (Internal) macro to check for conditions at compile-time \param x compile-time condition \hideinitializer */ #define RAPIDJSON_STATIC_ASSERT(x) \ typedef ::RAPIDJSON_NAMESPACE::StaticAssertTest< \ sizeof(::RAPIDJSON_NAMESPACE::STATIC_ASSERTION_FAILURE)> \ RAPIDJSON_JOIN(StaticAssertTypedef, __LINE__) RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE #endif // RAPIDJSON_STATIC_ASSERT /////////////////////////////////////////////////////////////////////////////// // RAPIDJSON_LIKELY, RAPIDJSON_UNLIKELY //! Compiler branching hint for expression with high probability to be true. /*! \ingroup RAPIDJSON_CONFIG \param x Boolean expression likely to be true. */ #ifndef RAPIDJSON_LIKELY #if defined(__GNUC__) || defined(__clang__) #define RAPIDJSON_LIKELY(x) __builtin_expect(!!(x), 1) #else #define RAPIDJSON_LIKELY(x) (x) #endif #endif //! Compiler branching hint for expression with low probability to be true. /*! \ingroup RAPIDJSON_CONFIG \param x Boolean expression unlikely to be true. */ #ifndef RAPIDJSON_UNLIKELY #if defined(__GNUC__) || defined(__clang__) #define RAPIDJSON_UNLIKELY(x) __builtin_expect(!!(x), 0) #else #define RAPIDJSON_UNLIKELY(x) (x) #endif #endif /////////////////////////////////////////////////////////////////////////////// // Helpers //!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN #define RAPIDJSON_MULTILINEMACRO_BEGIN do { #define RAPIDJSON_MULTILINEMACRO_END \ } while((void)0, 0) // adopted from Boost #define RAPIDJSON_VERSION_CODE(x,y,z) \ (((x)*100000) + ((y)*100) + (z)) /////////////////////////////////////////////////////////////////////////////// // RAPIDJSON_DIAG_PUSH/POP, RAPIDJSON_DIAG_OFF #if defined(__GNUC__) #define RAPIDJSON_GNUC \ RAPIDJSON_VERSION_CODE(__GNUC__,__GNUC_MINOR__,__GNUC_PATCHLEVEL__) #endif #if defined(__clang__) || (defined(RAPIDJSON_GNUC) && RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,2,0)) #define RAPIDJSON_PRAGMA(x) _Pragma(RAPIDJSON_STRINGIFY(x)) #define RAPIDJSON_DIAG_PRAGMA(x) RAPIDJSON_PRAGMA(GCC diagnostic x) #define RAPIDJSON_DIAG_OFF(x) \ RAPIDJSON_DIAG_PRAGMA(ignored RAPIDJSON_STRINGIFY(RAPIDJSON_JOIN(-W,x))) // push/pop support in Clang and GCC>=4.6 #if defined(__clang__) || (defined(RAPIDJSON_GNUC) && RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) #define RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_PRAGMA(push) #define RAPIDJSON_DIAG_POP RAPIDJSON_DIAG_PRAGMA(pop) #else // GCC >= 4.2, < 4.6 #define RAPIDJSON_DIAG_PUSH /* ignored */ #define RAPIDJSON_DIAG_POP /* ignored */ #endif #elif defined(_MSC_VER) // pragma (MSVC specific) #define RAPIDJSON_PRAGMA(x) __pragma(x) #define RAPIDJSON_DIAG_PRAGMA(x) RAPIDJSON_PRAGMA(warning(x)) #define RAPIDJSON_DIAG_OFF(x) RAPIDJSON_DIAG_PRAGMA(disable: x) #define RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_PRAGMA(push) #define RAPIDJSON_DIAG_POP RAPIDJSON_DIAG_PRAGMA(pop) #else #define RAPIDJSON_DIAG_OFF(x) /* ignored */ #define RAPIDJSON_DIAG_PUSH /* ignored */ #define RAPIDJSON_DIAG_POP /* ignored */ #endif // RAPIDJSON_DIAG_* /////////////////////////////////////////////////////////////////////////////// // C++11 features #ifndef RAPIDJSON_HAS_CXX11_RVALUE_REFS #if defined(__clang__) #if __has_feature(cxx_rvalue_references) && \ (defined(_MSC_VER) || defined(_LIBCPP_VERSION) || defined(__GLIBCXX__) && __GLIBCXX__ >= 20080306) #define RAPIDJSON_HAS_CXX11_RVALUE_REFS 1 #else #define RAPIDJSON_HAS_CXX11_RVALUE_REFS 0 #endif #elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,3,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \ (defined(_MSC_VER) && _MSC_VER >= 1600) || \ (defined(__SUNPRO_CC) && __SUNPRO_CC >= 0x5140 && defined(__GXX_EXPERIMENTAL_CXX0X__)) #define RAPIDJSON_HAS_CXX11_RVALUE_REFS 1 #else #define RAPIDJSON_HAS_CXX11_RVALUE_REFS 0 #endif #endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS #ifndef RAPIDJSON_HAS_CXX11_NOEXCEPT #if defined(__clang__) #define RAPIDJSON_HAS_CXX11_NOEXCEPT __has_feature(cxx_noexcept) #elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \ (defined(_MSC_VER) && _MSC_VER >= 1900) || \ (defined(__SUNPRO_CC) && __SUNPRO_CC >= 0x5140 && defined(__GXX_EXPERIMENTAL_CXX0X__)) #define RAPIDJSON_HAS_CXX11_NOEXCEPT 1 #else #define RAPIDJSON_HAS_CXX11_NOEXCEPT 0 #endif #endif #if RAPIDJSON_HAS_CXX11_NOEXCEPT #define RAPIDJSON_NOEXCEPT noexcept #else #define RAPIDJSON_NOEXCEPT /* noexcept */ #endif // RAPIDJSON_HAS_CXX11_NOEXCEPT // no automatic detection, yet #ifndef RAPIDJSON_HAS_CXX11_TYPETRAITS #if (defined(_MSC_VER) && _MSC_VER >= 1700) #define RAPIDJSON_HAS_CXX11_TYPETRAITS 1 #else #define RAPIDJSON_HAS_CXX11_TYPETRAITS 0 #endif #endif #ifndef RAPIDJSON_HAS_CXX11_RANGE_FOR #if defined(__clang__) #define RAPIDJSON_HAS_CXX11_RANGE_FOR __has_feature(cxx_range_for) #elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \ (defined(_MSC_VER) && _MSC_VER >= 1700) || \ (defined(__SUNPRO_CC) && __SUNPRO_CC >= 0x5140 && defined(__GXX_EXPERIMENTAL_CXX0X__)) #define RAPIDJSON_HAS_CXX11_RANGE_FOR 1 #else #define RAPIDJSON_HAS_CXX11_RANGE_FOR 0 #endif #endif // RAPIDJSON_HAS_CXX11_RANGE_FOR //!@endcond //! Assertion (in non-throwing contexts). /*! \ingroup RAPIDJSON_CONFIG Some functions provide a \c noexcept guarantee, if the compiler supports it. In these cases, the \ref RAPIDJSON_ASSERT macro cannot be overridden to throw an exception. This macro adds a separate customization point for such cases. Defaults to C \c assert() (as \ref RAPIDJSON_ASSERT), if \c noexcept is supported, and to \ref RAPIDJSON_ASSERT otherwise. */ /////////////////////////////////////////////////////////////////////////////// // RAPIDJSON_NOEXCEPT_ASSERT #ifndef RAPIDJSON_NOEXCEPT_ASSERT #ifdef RAPIDJSON_ASSERT_THROWS #if RAPIDJSON_HAS_CXX11_NOEXCEPT #define RAPIDJSON_NOEXCEPT_ASSERT(x) #else #define RAPIDJSON_NOEXCEPT_ASSERT(x) RAPIDJSON_ASSERT(x) #endif // RAPIDJSON_HAS_CXX11_NOEXCEPT #else #define RAPIDJSON_NOEXCEPT_ASSERT(x) RAPIDJSON_ASSERT(x) #endif // RAPIDJSON_ASSERT_THROWS #endif // RAPIDJSON_NOEXCEPT_ASSERT /////////////////////////////////////////////////////////////////////////////// // new/delete #ifndef RAPIDJSON_NEW ///! customization point for global \c new #define RAPIDJSON_NEW(TypeName) new TypeName #endif #ifndef RAPIDJSON_DELETE ///! customization point for global \c delete #define RAPIDJSON_DELETE(x) delete x #endif /////////////////////////////////////////////////////////////////////////////// // Type /*! \namespace rapidjson \brief main RapidJSON namespace \see RAPIDJSON_NAMESPACE */ RAPIDJSON_NAMESPACE_BEGIN //! Type of JSON value enum Type { kNullType = 0, //!< null kFalseType = 1, //!< false kTrueType = 2, //!< true kObjectType = 3, //!< object kArrayType = 4, //!< array kStringType = 5, //!< string kNumberType = 6 //!< number }; RAPIDJSON_NAMESPACE_END #endif // RAPIDJSON_RAPIDJSON_H_ ================================================ FILE: src/json/rapidjson/reader.h ================================================ // Tencent is pleased to support the open source community by making RapidJSON available. // // Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at // // http://opensource.org/licenses/MIT // // Unless required by applicable law or agreed to in writing, software distributed // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. #ifndef RAPIDJSON_READER_H_ #define RAPIDJSON_READER_H_ /*! \file reader.h */ #include "allocators.h" #include "stream.h" #include "encodedstream.h" #include "src/json/rapidjson/internal/meta.h" #include "src/json/rapidjson/internal/stack.h" #include "src/json/rapidjson/internal/strtod.h" #include #if defined(RAPIDJSON_SIMD) && defined(_MSC_VER) #include #pragma intrinsic(_BitScanForward) #endif #ifdef RAPIDJSON_SSE42 #include #elif defined(RAPIDJSON_SSE2) #include #elif defined(RAPIDJSON_NEON) #include #endif #ifdef __clang__ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(old-style-cast) RAPIDJSON_DIAG_OFF(padded) RAPIDJSON_DIAG_OFF(switch-enum) #elif defined(_MSC_VER) RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant RAPIDJSON_DIAG_OFF(4702) // unreachable code #endif #ifdef __GNUC__ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(effc++) #endif //!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN #define RAPIDJSON_NOTHING /* deliberately empty */ #ifndef RAPIDJSON_PARSE_ERROR_EARLY_RETURN #define RAPIDJSON_PARSE_ERROR_EARLY_RETURN(value) \ RAPIDJSON_MULTILINEMACRO_BEGIN \ if (RAPIDJSON_UNLIKELY(HasParseError())) { return value; } \ RAPIDJSON_MULTILINEMACRO_END #endif #define RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID \ RAPIDJSON_PARSE_ERROR_EARLY_RETURN(RAPIDJSON_NOTHING) //!@endcond /*! \def RAPIDJSON_PARSE_ERROR_NORETURN \ingroup RAPIDJSON_ERRORS \brief Macro to indicate a parse error. \param parseErrorCode \ref rapidjson::ParseErrorCode of the error \param offset position of the error in JSON input (\c size_t) This macros can be used as a customization point for the internal error handling mechanism of RapidJSON. A common usage model is to throw an exception instead of requiring the caller to explicitly check the \ref rapidjson::GenericReader::Parse's return value: \code #define RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode,offset) \ throw ParseException(parseErrorCode, #parseErrorCode, offset) #include // std::runtime_error #include "rapidjson/error/error.h" // rapidjson::ParseResult struct ParseException : std::runtime_error, rapidjson::ParseResult { ParseException(rapidjson::ParseErrorCode code, const char* msg, size_t offset) : std::runtime_error(msg), ParseResult(code, offset) {} }; #include "rapidjson/reader.h" \endcode \see RAPIDJSON_PARSE_ERROR, rapidjson::GenericReader::Parse */ #ifndef RAPIDJSON_PARSE_ERROR_NORETURN #define RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode, offset) \ RAPIDJSON_MULTILINEMACRO_BEGIN \ RAPIDJSON_ASSERT(!HasParseError()); /* Error can only be assigned once */ \ SetParseError(parseErrorCode, offset); \ RAPIDJSON_MULTILINEMACRO_END #endif /*! \def RAPIDJSON_PARSE_ERROR \ingroup RAPIDJSON_ERRORS \brief (Internal) macro to indicate and handle a parse error. \param parseErrorCode \ref rapidjson::ParseErrorCode of the error \param offset position of the error in JSON input (\c size_t) Invokes RAPIDJSON_PARSE_ERROR_NORETURN and stops the parsing. \see RAPIDJSON_PARSE_ERROR_NORETURN \hideinitializer */ #ifndef RAPIDJSON_PARSE_ERROR #define RAPIDJSON_PARSE_ERROR(parseErrorCode, offset) \ RAPIDJSON_MULTILINEMACRO_BEGIN \ RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode, offset); \ RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; \ RAPIDJSON_MULTILINEMACRO_END #endif #include "src/json/rapidjson/error/error.h" // ParseErrorCode, ParseResult RAPIDJSON_NAMESPACE_BEGIN /////////////////////////////////////////////////////////////////////////////// // ParseFlag /*! \def RAPIDJSON_PARSE_DEFAULT_FLAGS \ingroup RAPIDJSON_CONFIG \brief User-defined kParseDefaultFlags definition. User can define this as any \c ParseFlag combinations. */ #ifndef RAPIDJSON_PARSE_DEFAULT_FLAGS #define RAPIDJSON_PARSE_DEFAULT_FLAGS kParseNoFlags #endif //! Combination of parseFlags /*! \see Reader::Parse, Document::Parse, Document::ParseInsitu, Document::ParseStream */ enum ParseFlag { kParseNoFlags = 0, //!< No flags are set. kParseInsituFlag = 1, //!< In-situ(destructive) parsing. kParseValidateEncodingFlag = 2, //!< Validate encoding of JSON strings. kParseIterativeFlag = 4, //!< Iterative(constant complexity in terms of function call stack size) parsing. kParseStopWhenDoneFlag = 8, //!< After parsing a complete JSON root from stream, stop further processing the rest of stream. When this flag is used, parser will not generate kParseErrorDocumentRootNotSingular error. kParseFullPrecisionFlag = 16, //!< Parse number in full precision (but slower). kParseCommentsFlag = 32, //!< Allow one-line (//) and multi-line (/**/) comments. kParseNumbersAsStringsFlag = 64, //!< Parse all numbers (ints/doubles) as strings. kParseTrailingCommasFlag = 128, //!< Allow trailing commas at the end of objects and arrays. kParseNanAndInfFlag = 256, //!< Allow parsing NaN, Inf, Infinity, -Inf and -Infinity as doubles. kParseDefaultFlags = RAPIDJSON_PARSE_DEFAULT_FLAGS //!< Default parse flags. Can be customized by defining RAPIDJSON_PARSE_DEFAULT_FLAGS }; /////////////////////////////////////////////////////////////////////////////// // Handler /*! \class rapidjson::Handler \brief Concept for receiving events from GenericReader upon parsing. The functions return true if no error occurs. If they return false, the event publisher should terminate the process. \code concept Handler { typename Ch; bool Null(); bool Bool(bool b); bool Int(int i); bool Uint(unsigned i); bool Int64(int64_t i); bool Uint64(uint64_t i); bool Double(double d); /// enabled via kParseNumbersAsStringsFlag, string is not null-terminated (use length) bool RawNumber(const Ch* str, SizeType length, bool copy); bool String(const Ch* str, SizeType length, bool copy); bool StartObject(); bool Key(const Ch* str, SizeType length, bool copy); bool EndObject(SizeType memberCount); bool StartArray(); bool EndArray(SizeType elementCount); }; \endcode */ /////////////////////////////////////////////////////////////////////////////// // BaseReaderHandler //! Default implementation of Handler. /*! This can be used as base class of any reader handler. \note implements Handler concept */ template, typename Derived = void> struct BaseReaderHandler { typedef typename Encoding::Ch Ch; typedef typename internal::SelectIf, BaseReaderHandler, Derived>::Type Override; bool Default() { return true; } bool Null() { return static_cast(*this).Default(); } bool Bool(bool) { return static_cast(*this).Default(); } bool Int(int) { return static_cast(*this).Default(); } bool Uint(unsigned) { return static_cast(*this).Default(); } bool Int64(int64_t) { return static_cast(*this).Default(); } bool Uint64(uint64_t) { return static_cast(*this).Default(); } bool Double(double) { return static_cast(*this).Default(); } /// enabled via kParseNumbersAsStringsFlag, string is not null-terminated (use length) bool RawNumber(const Ch* str, SizeType len, bool copy) { return static_cast(*this).String(str, len, copy); } bool String(const Ch*, SizeType, bool) { return static_cast(*this).Default(); } bool StartObject() { return static_cast(*this).Default(); } bool Key(const Ch* str, SizeType len, bool copy) { return static_cast(*this).String(str, len, copy); } bool EndObject(SizeType) { return static_cast(*this).Default(); } bool StartArray() { return static_cast(*this).Default(); } bool EndArray(SizeType) { return static_cast(*this).Default(); } }; /////////////////////////////////////////////////////////////////////////////// // StreamLocalCopy namespace internal { template::copyOptimization> class StreamLocalCopy; //! Do copy optimization. template class StreamLocalCopy { public: StreamLocalCopy(Stream& original) : s(original), original_(original) {} ~StreamLocalCopy() { original_ = s; } Stream s; private: StreamLocalCopy& operator=(const StreamLocalCopy&) /* = delete */; Stream& original_; }; //! Keep reference. template class StreamLocalCopy { public: StreamLocalCopy(Stream& original) : s(original) {} Stream& s; private: StreamLocalCopy& operator=(const StreamLocalCopy&) /* = delete */; }; } // namespace internal /////////////////////////////////////////////////////////////////////////////// // SkipWhitespace //! Skip the JSON white spaces in a stream. /*! \param is A input stream for skipping white spaces. \note This function has SSE2/SSE4.2 specialization. */ template void SkipWhitespace(InputStream& is) { internal::StreamLocalCopy copy(is); InputStream& s(copy.s); typename InputStream::Ch c; while ((c = s.Peek()) == ' ' || c == '\n' || c == '\r' || c == '\t') s.Take(); } inline const char* SkipWhitespace(const char* p, const char* end) { while (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')) ++p; return p; } #ifdef RAPIDJSON_SSE42 //! Skip whitespace with SSE 4.2 pcmpistrm instruction, testing 16 8-byte characters at once. inline const char *SkipWhitespace_SIMD(const char* p) { // Fast return for single non-whitespace if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') ++p; else return p; // 16-byte align to the next boundary const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); while (p != nextAligned) if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') ++p; else return p; // The rest of string using SIMD static const char whitespace[16] = " \n\r\t"; const __m128i w = _mm_loadu_si128(reinterpret_cast(&whitespace[0])); for (;; p += 16) { const __m128i s = _mm_load_si128(reinterpret_cast(p)); const int r = _mm_cmpistri(w, s, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_LEAST_SIGNIFICANT | _SIDD_NEGATIVE_POLARITY); if (r != 16) // some of characters is non-whitespace return p + r; } } inline const char *SkipWhitespace_SIMD(const char* p, const char* end) { // Fast return for single non-whitespace if (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')) ++p; else return p; // The middle of string using SIMD static const char whitespace[16] = " \n\r\t"; const __m128i w = _mm_loadu_si128(reinterpret_cast(&whitespace[0])); for (; p <= end - 16; p += 16) { const __m128i s = _mm_loadu_si128(reinterpret_cast(p)); const int r = _mm_cmpistri(w, s, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_LEAST_SIGNIFICANT | _SIDD_NEGATIVE_POLARITY); if (r != 16) // some of characters is non-whitespace return p + r; } return SkipWhitespace(p, end); } #elif defined(RAPIDJSON_SSE2) //! Skip whitespace with SSE2 instructions, testing 16 8-byte characters at once. inline const char *SkipWhitespace_SIMD(const char* p) { // Fast return for single non-whitespace if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') ++p; else return p; // 16-byte align to the next boundary const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); while (p != nextAligned) if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') ++p; else return p; // The rest of string #define C16(c) { c, c, c, c, c, c, c, c, c, c, c, c, c, c, c, c } static const char whitespaces[4][16] = { C16(' '), C16('\n'), C16('\r'), C16('\t') }; #undef C16 const __m128i w0 = _mm_loadu_si128(reinterpret_cast(&whitespaces[0][0])); const __m128i w1 = _mm_loadu_si128(reinterpret_cast(&whitespaces[1][0])); const __m128i w2 = _mm_loadu_si128(reinterpret_cast(&whitespaces[2][0])); const __m128i w3 = _mm_loadu_si128(reinterpret_cast(&whitespaces[3][0])); for (;; p += 16) { const __m128i s = _mm_load_si128(reinterpret_cast(p)); __m128i x = _mm_cmpeq_epi8(s, w0); x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w1)); x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w2)); x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w3)); unsigned short r = static_cast(~_mm_movemask_epi8(x)); if (r != 0) { // some of characters may be non-whitespace #ifdef _MSC_VER // Find the index of first non-whitespace unsigned long offset; _BitScanForward(&offset, r); return p + offset; #else return p + __builtin_ffs(r) - 1; #endif } } } inline const char *SkipWhitespace_SIMD(const char* p, const char* end) { // Fast return for single non-whitespace if (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')) ++p; else return p; // The rest of string #define C16(c) { c, c, c, c, c, c, c, c, c, c, c, c, c, c, c, c } static const char whitespaces[4][16] = { C16(' '), C16('\n'), C16('\r'), C16('\t') }; #undef C16 const __m128i w0 = _mm_loadu_si128(reinterpret_cast(&whitespaces[0][0])); const __m128i w1 = _mm_loadu_si128(reinterpret_cast(&whitespaces[1][0])); const __m128i w2 = _mm_loadu_si128(reinterpret_cast(&whitespaces[2][0])); const __m128i w3 = _mm_loadu_si128(reinterpret_cast(&whitespaces[3][0])); for (; p <= end - 16; p += 16) { const __m128i s = _mm_loadu_si128(reinterpret_cast(p)); __m128i x = _mm_cmpeq_epi8(s, w0); x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w1)); x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w2)); x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w3)); unsigned short r = static_cast(~_mm_movemask_epi8(x)); if (r != 0) { // some of characters may be non-whitespace #ifdef _MSC_VER // Find the index of first non-whitespace unsigned long offset; _BitScanForward(&offset, r); return p + offset; #else return p + __builtin_ffs(r) - 1; #endif } } return SkipWhitespace(p, end); } #elif defined(RAPIDJSON_NEON) //! Skip whitespace with ARM Neon instructions, testing 16 8-byte characters at once. inline const char *SkipWhitespace_SIMD(const char* p) { // Fast return for single non-whitespace if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') ++p; else return p; // 16-byte align to the next boundary const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); while (p != nextAligned) if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') ++p; else return p; const uint8x16_t w0 = vmovq_n_u8(' '); const uint8x16_t w1 = vmovq_n_u8('\n'); const uint8x16_t w2 = vmovq_n_u8('\r'); const uint8x16_t w3 = vmovq_n_u8('\t'); for (;; p += 16) { const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); uint8x16_t x = vceqq_u8(s, w0); x = vorrq_u8(x, vceqq_u8(s, w1)); x = vorrq_u8(x, vceqq_u8(s, w2)); x = vorrq_u8(x, vceqq_u8(s, w3)); x = vmvnq_u8(x); // Negate x = vrev64q_u8(x); // Rev in 64 uint64_t low = vgetq_lane_u64(reinterpret_cast(x), 0); // extract uint64_t high = vgetq_lane_u64(reinterpret_cast(x), 1); // extract if (low == 0) { if (high != 0) { int lz =__builtin_clzll(high);; return p + 8 + (lz >> 3); } } else { int lz = __builtin_clzll(low);; return p + (lz >> 3); } } } inline const char *SkipWhitespace_SIMD(const char* p, const char* end) { // Fast return for single non-whitespace if (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')) ++p; else return p; const uint8x16_t w0 = vmovq_n_u8(' '); const uint8x16_t w1 = vmovq_n_u8('\n'); const uint8x16_t w2 = vmovq_n_u8('\r'); const uint8x16_t w3 = vmovq_n_u8('\t'); for (; p <= end - 16; p += 16) { const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); uint8x16_t x = vceqq_u8(s, w0); x = vorrq_u8(x, vceqq_u8(s, w1)); x = vorrq_u8(x, vceqq_u8(s, w2)); x = vorrq_u8(x, vceqq_u8(s, w3)); x = vmvnq_u8(x); // Negate x = vrev64q_u8(x); // Rev in 64 uint64_t low = vgetq_lane_u64(reinterpret_cast(x), 0); // extract uint64_t high = vgetq_lane_u64(reinterpret_cast(x), 1); // extract if (low == 0) { if (high != 0) { int lz = __builtin_clzll(high); return p + 8 + (lz >> 3); } } else { int lz = __builtin_clzll(low); return p + (lz >> 3); } } return SkipWhitespace(p, end); } #endif // RAPIDJSON_NEON #ifdef RAPIDJSON_SIMD //! Template function specialization for InsituStringStream template<> inline void SkipWhitespace(InsituStringStream& is) { is.src_ = const_cast(SkipWhitespace_SIMD(is.src_)); } //! Template function specialization for StringStream template<> inline void SkipWhitespace(StringStream& is) { is.src_ = SkipWhitespace_SIMD(is.src_); } template<> inline void SkipWhitespace(EncodedInputStream, MemoryStream>& is) { is.is_.src_ = SkipWhitespace_SIMD(is.is_.src_, is.is_.end_); } #endif // RAPIDJSON_SIMD /////////////////////////////////////////////////////////////////////////////// // GenericReader //! SAX-style JSON parser. Use \ref Reader for UTF8 encoding and default allocator. /*! GenericReader parses JSON text from a stream, and send events synchronously to an object implementing Handler concept. It needs to allocate a stack for storing a single decoded string during non-destructive parsing. For in-situ parsing, the decoded string is directly written to the source text string, no temporary buffer is required. A GenericReader object can be reused for parsing multiple JSON text. \tparam SourceEncoding Encoding of the input stream. \tparam TargetEncoding Encoding of the parse output. \tparam StackAllocator Allocator type for stack. */ template class GenericReader { public: typedef typename SourceEncoding::Ch Ch; //!< SourceEncoding character type //! Constructor. /*! \param stackAllocator Optional allocator for allocating stack memory. (Only use for non-destructive parsing) \param stackCapacity stack capacity in bytes for storing a single decoded string. (Only use for non-destructive parsing) */ GenericReader(StackAllocator* stackAllocator = 0, size_t stackCapacity = kDefaultStackCapacity) : stack_(stackAllocator, stackCapacity), parseResult_(), state_(IterativeParsingStartState) {} //! Parse JSON text. /*! \tparam parseFlags Combination of \ref ParseFlag. \tparam InputStream Type of input stream, implementing Stream concept. \tparam Handler Type of handler, implementing Handler concept. \param is Input stream to be parsed. \param handler The handler to receive events. \return Whether the parsing is successful. */ template ParseResult Parse(InputStream& is, Handler& handler) { if (parseFlags & kParseIterativeFlag) return IterativeParse(is, handler); parseResult_.Clear(); ClearStackOnExit scope(*this); SkipWhitespaceAndComments(is); RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); if (RAPIDJSON_UNLIKELY(is.Peek() == '\0')) { RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentEmpty, is.Tell()); RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); } else { ParseValue(is, handler); RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); if (!(parseFlags & kParseStopWhenDoneFlag)) { SkipWhitespaceAndComments(is); RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); if (RAPIDJSON_UNLIKELY(is.Peek() != '\0')) { RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentRootNotSingular, is.Tell()); RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); } } } return parseResult_; } //! Parse JSON text (with \ref kParseDefaultFlags) /*! \tparam InputStream Type of input stream, implementing Stream concept \tparam Handler Type of handler, implementing Handler concept. \param is Input stream to be parsed. \param handler The handler to receive events. \return Whether the parsing is successful. */ template ParseResult Parse(InputStream& is, Handler& handler) { return Parse(is, handler); } //! Initialize JSON text token-by-token parsing /*! */ void IterativeParseInit() { parseResult_.Clear(); state_ = IterativeParsingStartState; } //! Parse one token from JSON text /*! \tparam InputStream Type of input stream, implementing Stream concept \tparam Handler Type of handler, implementing Handler concept. \param is Input stream to be parsed. \param handler The handler to receive events. \return Whether the parsing is successful. */ template bool IterativeParseNext(InputStream& is, Handler& handler) { while (RAPIDJSON_LIKELY(is.Peek() != '\0')) { SkipWhitespaceAndComments(is); Token t = Tokenize(is.Peek()); IterativeParsingState n = Predict(state_, t); IterativeParsingState d = Transit(state_, t, n, is, handler); // If we've finished or hit an error... if (RAPIDJSON_UNLIKELY(IsIterativeParsingCompleteState(d))) { // Report errors. if (d == IterativeParsingErrorState) { HandleError(state_, is); return false; } // Transition to the finish state. RAPIDJSON_ASSERT(d == IterativeParsingFinishState); state_ = d; // If StopWhenDone is not set... if (!(parseFlags & kParseStopWhenDoneFlag)) { // ... and extra non-whitespace data is found... SkipWhitespaceAndComments(is); if (is.Peek() != '\0') { // ... this is considered an error. HandleError(state_, is); return false; } } // Success! We are done! return true; } // Transition to the new state. state_ = d; // If we parsed anything other than a delimiter, we invoked the handler, so we can return true now. if (!IsIterativeParsingDelimiterState(n)) return true; } // We reached the end of file. stack_.Clear(); if (state_ != IterativeParsingFinishState) { HandleError(state_, is); return false; } return true; } //! Check if token-by-token parsing JSON text is complete /*! \return Whether the JSON has been fully decoded. */ RAPIDJSON_FORCEINLINE bool IterativeParseComplete() const { return IsIterativeParsingCompleteState(state_); } //! Whether a parse error has occurred in the last parsing. bool HasParseError() const { return parseResult_.IsError(); } //! Get the \ref ParseErrorCode of last parsing. ParseErrorCode GetParseErrorCode() const { return parseResult_.Code(); } //! Get the position of last parsing error in input, 0 otherwise. size_t GetErrorOffset() const { return parseResult_.Offset(); } protected: void SetParseError(ParseErrorCode code, size_t offset) { parseResult_.Set(code, offset); } private: // Prohibit copy constructor & assignment operator. GenericReader(const GenericReader&); GenericReader& operator=(const GenericReader&); void ClearStack() { stack_.Clear(); } // clear stack on any exit from ParseStream, e.g. due to exception struct ClearStackOnExit { explicit ClearStackOnExit(GenericReader& r) : r_(r) {} ~ClearStackOnExit() { r_.ClearStack(); } private: GenericReader& r_; ClearStackOnExit(const ClearStackOnExit&); ClearStackOnExit& operator=(const ClearStackOnExit&); }; template void SkipWhitespaceAndComments(InputStream& is) { SkipWhitespace(is); if (parseFlags & kParseCommentsFlag) { while (RAPIDJSON_UNLIKELY(Consume(is, '/'))) { if (Consume(is, '*')) { while (true) { if (RAPIDJSON_UNLIKELY(is.Peek() == '\0')) RAPIDJSON_PARSE_ERROR(kParseErrorUnspecificSyntaxError, is.Tell()); else if (Consume(is, '*')) { if (Consume(is, '/')) break; } else is.Take(); } } else if (RAPIDJSON_LIKELY(Consume(is, '/'))) while (is.Peek() != '\0' && is.Take() != '\n') {} else RAPIDJSON_PARSE_ERROR(kParseErrorUnspecificSyntaxError, is.Tell()); SkipWhitespace(is); } } } // Parse object: { string : value, ... } template void ParseObject(InputStream& is, Handler& handler) { RAPIDJSON_ASSERT(is.Peek() == '{'); is.Take(); // Skip '{' if (RAPIDJSON_UNLIKELY(!handler.StartObject())) RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); SkipWhitespaceAndComments(is); RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; if (Consume(is, '}')) { if (RAPIDJSON_UNLIKELY(!handler.EndObject(0))) // empty object RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); return; } for (SizeType memberCount = 0;;) { if (RAPIDJSON_UNLIKELY(is.Peek() != '"')) RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell()); ParseString(is, handler, true); RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; SkipWhitespaceAndComments(is); RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; if (RAPIDJSON_UNLIKELY(!Consume(is, ':'))) RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissColon, is.Tell()); SkipWhitespaceAndComments(is); RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; ParseValue(is, handler); RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; SkipWhitespaceAndComments(is); RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; ++memberCount; switch (is.Peek()) { case ',': is.Take(); SkipWhitespaceAndComments(is); RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; break; case '}': is.Take(); if (RAPIDJSON_UNLIKELY(!handler.EndObject(memberCount))) RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); return; default: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, is.Tell()); break; // This useless break is only for making warning and coverage happy } if (parseFlags & kParseTrailingCommasFlag) { if (is.Peek() == '}') { if (RAPIDJSON_UNLIKELY(!handler.EndObject(memberCount))) RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); is.Take(); return; } } } } // Parse array: [ value, ... ] template void ParseArray(InputStream& is, Handler& handler) { RAPIDJSON_ASSERT(is.Peek() == '['); is.Take(); // Skip '[' if (RAPIDJSON_UNLIKELY(!handler.StartArray())) RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); SkipWhitespaceAndComments(is); RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; if (Consume(is, ']')) { if (RAPIDJSON_UNLIKELY(!handler.EndArray(0))) // empty array RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); return; } for (SizeType elementCount = 0;;) { ParseValue(is, handler); RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; ++elementCount; SkipWhitespaceAndComments(is); RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; if (Consume(is, ',')) { SkipWhitespaceAndComments(is); RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; } else if (Consume(is, ']')) { if (RAPIDJSON_UNLIKELY(!handler.EndArray(elementCount))) RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); return; } else RAPIDJSON_PARSE_ERROR(kParseErrorArrayMissCommaOrSquareBracket, is.Tell()); if (parseFlags & kParseTrailingCommasFlag) { if (is.Peek() == ']') { if (RAPIDJSON_UNLIKELY(!handler.EndArray(elementCount))) RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); is.Take(); return; } } } } template void ParseNull(InputStream& is, Handler& handler) { RAPIDJSON_ASSERT(is.Peek() == 'n'); is.Take(); if (RAPIDJSON_LIKELY(Consume(is, 'u') && Consume(is, 'l') && Consume(is, 'l'))) { if (RAPIDJSON_UNLIKELY(!handler.Null())) RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); } else RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); } template void ParseTrue(InputStream& is, Handler& handler) { RAPIDJSON_ASSERT(is.Peek() == 't'); is.Take(); if (RAPIDJSON_LIKELY(Consume(is, 'r') && Consume(is, 'u') && Consume(is, 'e'))) { if (RAPIDJSON_UNLIKELY(!handler.Bool(true))) RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); } else RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); } template void ParseFalse(InputStream& is, Handler& handler) { RAPIDJSON_ASSERT(is.Peek() == 'f'); is.Take(); if (RAPIDJSON_LIKELY(Consume(is, 'a') && Consume(is, 'l') && Consume(is, 's') && Consume(is, 'e'))) { if (RAPIDJSON_UNLIKELY(!handler.Bool(false))) RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); } else RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); } template RAPIDJSON_FORCEINLINE static bool Consume(InputStream& is, typename InputStream::Ch expect) { if (RAPIDJSON_LIKELY(is.Peek() == expect)) { is.Take(); return true; } else return false; } // Helper function to parse four hexadecimal digits in \uXXXX in ParseString(). template unsigned ParseHex4(InputStream& is, size_t escapeOffset) { unsigned codepoint = 0; for (int i = 0; i < 4; i++) { Ch c = is.Peek(); codepoint <<= 4; codepoint += static_cast(c); if (c >= '0' && c <= '9') codepoint -= '0'; else if (c >= 'A' && c <= 'F') codepoint -= 'A' - 10; else if (c >= 'a' && c <= 'f') codepoint -= 'a' - 10; else { RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorStringUnicodeEscapeInvalidHex, escapeOffset); RAPIDJSON_PARSE_ERROR_EARLY_RETURN(0); } is.Take(); } return codepoint; } template class StackStream { public: typedef CharType Ch; StackStream(internal::Stack& stack) : stack_(stack), length_(0) {} RAPIDJSON_FORCEINLINE void Put(Ch c) { *stack_.template Push() = c; ++length_; } RAPIDJSON_FORCEINLINE void* Push(SizeType count) { length_ += count; return stack_.template Push(count); } size_t Length() const { return length_; } Ch* Pop() { return stack_.template Pop(length_); } private: StackStream(const StackStream&); StackStream& operator=(const StackStream&); internal::Stack& stack_; SizeType length_; }; // Parse string and generate String event. Different code paths for kParseInsituFlag. template void ParseString(InputStream& is, Handler& handler, bool isKey = false) { internal::StreamLocalCopy copy(is); InputStream& s(copy.s); RAPIDJSON_ASSERT(s.Peek() == '\"'); s.Take(); // Skip '\"' bool success = false; if (parseFlags & kParseInsituFlag) { typename InputStream::Ch *head = s.PutBegin(); ParseStringToStream(s, s); RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; size_t length = s.PutEnd(head) - 1; RAPIDJSON_ASSERT(length <= 0xFFFFFFFF); const typename TargetEncoding::Ch* const str = reinterpret_cast(head); success = (isKey ? handler.Key(str, SizeType(length), false) : handler.String(str, SizeType(length), false)); } else { StackStream stackStream(stack_); ParseStringToStream(s, stackStream); RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; SizeType length = static_cast(stackStream.Length()) - 1; const typename TargetEncoding::Ch* const str = stackStream.Pop(); success = (isKey ? handler.Key(str, length, true) : handler.String(str, length, true)); } if (RAPIDJSON_UNLIKELY(!success)) RAPIDJSON_PARSE_ERROR(kParseErrorTermination, s.Tell()); } // Parse string to an output is // This function handles the prefix/suffix double quotes, escaping, and optional encoding validation. template RAPIDJSON_FORCEINLINE void ParseStringToStream(InputStream& is, OutputStream& os) { //!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN #define Z16 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 static const char escape[256] = { Z16, Z16, 0, 0,'\"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'/', Z16, Z16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'\\', 0, 0, 0, 0, 0,'\b', 0, 0, 0,'\f', 0, 0, 0, 0, 0, 0, 0,'\n', 0, 0, 0,'\r', 0,'\t', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16 }; #undef Z16 //!@endcond for (;;) { // Scan and copy string before "\\\"" or < 0x20. This is an optional optimzation. if (!(parseFlags & kParseValidateEncodingFlag)) ScanCopyUnescapedString(is, os); Ch c = is.Peek(); if (RAPIDJSON_UNLIKELY(c == '\\')) { // Escape size_t escapeOffset = is.Tell(); // For invalid escaping, report the initial '\\' as error offset is.Take(); Ch e = is.Peek(); if ((sizeof(Ch) == 1 || unsigned(e) < 256) && RAPIDJSON_LIKELY(escape[static_cast(e)])) { is.Take(); os.Put(static_cast(escape[static_cast(e)])); } else if (RAPIDJSON_LIKELY(e == 'u')) { // Unicode is.Take(); unsigned codepoint = ParseHex4(is, escapeOffset); RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; if (RAPIDJSON_UNLIKELY(codepoint >= 0xD800 && codepoint <= 0xDBFF)) { // Handle UTF-16 surrogate pair if (RAPIDJSON_UNLIKELY(!Consume(is, '\\') || !Consume(is, 'u'))) RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, escapeOffset); unsigned codepoint2 = ParseHex4(is, escapeOffset); RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; if (RAPIDJSON_UNLIKELY(codepoint2 < 0xDC00 || codepoint2 > 0xDFFF)) RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, escapeOffset); codepoint = (((codepoint - 0xD800) << 10) | (codepoint2 - 0xDC00)) + 0x10000; } TEncoding::Encode(os, codepoint); } else RAPIDJSON_PARSE_ERROR(kParseErrorStringEscapeInvalid, escapeOffset); } else if (RAPIDJSON_UNLIKELY(c == '"')) { // Closing double quote is.Take(); os.Put('\0'); // null-terminate the string return; } else if (RAPIDJSON_UNLIKELY(static_cast(c) < 0x20)) { // RFC 4627: unescaped = %x20-21 / %x23-5B / %x5D-10FFFF if (c == '\0') RAPIDJSON_PARSE_ERROR(kParseErrorStringMissQuotationMark, is.Tell()); else RAPIDJSON_PARSE_ERROR(kParseErrorStringInvalidEncoding, is.Tell()); } else { size_t offset = is.Tell(); if (RAPIDJSON_UNLIKELY((parseFlags & kParseValidateEncodingFlag ? !Transcoder::Validate(is, os) : !Transcoder::Transcode(is, os)))) RAPIDJSON_PARSE_ERROR(kParseErrorStringInvalidEncoding, offset); } } } template static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(InputStream&, OutputStream&) { // Do nothing for generic version } #if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42) // StringStream -> StackStream static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(StringStream& is, StackStream& os) { const char* p = is.src_; // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); while (p != nextAligned) if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { is.src_ = p; return; } else os.Put(*p++); // The rest of string using SIMD static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; static const char space[16] = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F }; const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); for (;; p += 16) { const __m128i s = _mm_load_si128(reinterpret_cast(p)); const __m128i t1 = _mm_cmpeq_epi8(s, dq); const __m128i t2 = _mm_cmpeq_epi8(s, bs); const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x1F) == 0x1F const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); unsigned short r = static_cast(_mm_movemask_epi8(x)); if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped SizeType length; #ifdef _MSC_VER // Find the index of first escaped unsigned long offset; _BitScanForward(&offset, r); length = offset; #else length = static_cast(__builtin_ffs(r) - 1); #endif if (length != 0) { char* q = reinterpret_cast(os.Push(length)); for (size_t i = 0; i < length; i++) q[i] = p[i]; p += length; } break; } _mm_storeu_si128(reinterpret_cast<__m128i *>(os.Push(16)), s); } is.src_ = p; } // InsituStringStream -> InsituStringStream static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(InsituStringStream& is, InsituStringStream& os) { RAPIDJSON_ASSERT(&is == &os); (void)os; if (is.src_ == is.dst_) { SkipUnescapedString(is); return; } char* p = is.src_; char *q = is.dst_; // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); while (p != nextAligned) if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { is.src_ = p; is.dst_ = q; return; } else *q++ = *p++; // The rest of string using SIMD static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; static const char space[16] = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F }; const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); for (;; p += 16, q += 16) { const __m128i s = _mm_load_si128(reinterpret_cast(p)); const __m128i t1 = _mm_cmpeq_epi8(s, dq); const __m128i t2 = _mm_cmpeq_epi8(s, bs); const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x1F) == 0x1F const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); unsigned short r = static_cast(_mm_movemask_epi8(x)); if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped size_t length; #ifdef _MSC_VER // Find the index of first escaped unsigned long offset; _BitScanForward(&offset, r); length = offset; #else length = static_cast(__builtin_ffs(r) - 1); #endif for (const char* pend = p + length; p != pend; ) *q++ = *p++; break; } _mm_storeu_si128(reinterpret_cast<__m128i *>(q), s); } is.src_ = p; is.dst_ = q; } // When read/write pointers are the same for insitu stream, just skip unescaped characters static RAPIDJSON_FORCEINLINE void SkipUnescapedString(InsituStringStream& is) { RAPIDJSON_ASSERT(is.src_ == is.dst_); char* p = is.src_; // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); for (; p != nextAligned; p++) if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { is.src_ = is.dst_ = p; return; } // The rest of string using SIMD static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; static const char space[16] = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F }; const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); for (;; p += 16) { const __m128i s = _mm_load_si128(reinterpret_cast(p)); const __m128i t1 = _mm_cmpeq_epi8(s, dq); const __m128i t2 = _mm_cmpeq_epi8(s, bs); const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x1F) == 0x1F const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); unsigned short r = static_cast(_mm_movemask_epi8(x)); if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped size_t length; #ifdef _MSC_VER // Find the index of first escaped unsigned long offset; _BitScanForward(&offset, r); length = offset; #else length = static_cast(__builtin_ffs(r) - 1); #endif p += length; break; } } is.src_ = is.dst_ = p; } #elif defined(RAPIDJSON_NEON) // StringStream -> StackStream static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(StringStream& is, StackStream& os) { const char* p = is.src_; // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); while (p != nextAligned) if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { is.src_ = p; return; } else os.Put(*p++); // The rest of string using SIMD const uint8x16_t s0 = vmovq_n_u8('"'); const uint8x16_t s1 = vmovq_n_u8('\\'); const uint8x16_t s2 = vmovq_n_u8('\b'); const uint8x16_t s3 = vmovq_n_u8(32); for (;; p += 16) { const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); uint8x16_t x = vceqq_u8(s, s0); x = vorrq_u8(x, vceqq_u8(s, s1)); x = vorrq_u8(x, vceqq_u8(s, s2)); x = vorrq_u8(x, vcltq_u8(s, s3)); x = vrev64q_u8(x); // Rev in 64 uint64_t low = vgetq_lane_u64(reinterpret_cast(x), 0); // extract uint64_t high = vgetq_lane_u64(reinterpret_cast(x), 1); // extract SizeType length = 0; bool escaped = false; if (low == 0) { if (high != 0) { unsigned lz = (unsigned)__builtin_clzll(high);; length = 8 + (lz >> 3); escaped = true; } } else { unsigned lz = (unsigned)__builtin_clzll(low);; length = lz >> 3; escaped = true; } if (RAPIDJSON_UNLIKELY(escaped)) { // some of characters is escaped if (length != 0) { char* q = reinterpret_cast(os.Push(length)); for (size_t i = 0; i < length; i++) q[i] = p[i]; p += length; } break; } vst1q_u8(reinterpret_cast(os.Push(16)), s); } is.src_ = p; } // InsituStringStream -> InsituStringStream static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(InsituStringStream& is, InsituStringStream& os) { RAPIDJSON_ASSERT(&is == &os); (void)os; if (is.src_ == is.dst_) { SkipUnescapedString(is); return; } char* p = is.src_; char *q = is.dst_; // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); while (p != nextAligned) if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { is.src_ = p; is.dst_ = q; return; } else *q++ = *p++; // The rest of string using SIMD const uint8x16_t s0 = vmovq_n_u8('"'); const uint8x16_t s1 = vmovq_n_u8('\\'); const uint8x16_t s2 = vmovq_n_u8('\b'); const uint8x16_t s3 = vmovq_n_u8(32); for (;; p += 16, q += 16) { const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); uint8x16_t x = vceqq_u8(s, s0); x = vorrq_u8(x, vceqq_u8(s, s1)); x = vorrq_u8(x, vceqq_u8(s, s2)); x = vorrq_u8(x, vcltq_u8(s, s3)); x = vrev64q_u8(x); // Rev in 64 uint64_t low = vgetq_lane_u64(reinterpret_cast(x), 0); // extract uint64_t high = vgetq_lane_u64(reinterpret_cast(x), 1); // extract SizeType length = 0; bool escaped = false; if (low == 0) { if (high != 0) { unsigned lz = (unsigned)__builtin_clzll(high); length = 8 + (lz >> 3); escaped = true; } } else { unsigned lz = (unsigned)__builtin_clzll(low); length = lz >> 3; escaped = true; } if (RAPIDJSON_UNLIKELY(escaped)) { // some of characters is escaped for (const char* pend = p + length; p != pend; ) { *q++ = *p++; } break; } vst1q_u8(reinterpret_cast(q), s); } is.src_ = p; is.dst_ = q; } // When read/write pointers are the same for insitu stream, just skip unescaped characters static RAPIDJSON_FORCEINLINE void SkipUnescapedString(InsituStringStream& is) { RAPIDJSON_ASSERT(is.src_ == is.dst_); char* p = is.src_; // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); for (; p != nextAligned; p++) if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { is.src_ = is.dst_ = p; return; } // The rest of string using SIMD const uint8x16_t s0 = vmovq_n_u8('"'); const uint8x16_t s1 = vmovq_n_u8('\\'); const uint8x16_t s2 = vmovq_n_u8('\b'); const uint8x16_t s3 = vmovq_n_u8(32); for (;; p += 16) { const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); uint8x16_t x = vceqq_u8(s, s0); x = vorrq_u8(x, vceqq_u8(s, s1)); x = vorrq_u8(x, vceqq_u8(s, s2)); x = vorrq_u8(x, vcltq_u8(s, s3)); x = vrev64q_u8(x); // Rev in 64 uint64_t low = vgetq_lane_u64(reinterpret_cast(x), 0); // extract uint64_t high = vgetq_lane_u64(reinterpret_cast(x), 1); // extract if (low == 0) { if (high != 0) { int lz = __builtin_clzll(high); p += 8 + (lz >> 3); break; } } else { int lz = __builtin_clzll(low); p += lz >> 3; break; } } is.src_ = is.dst_ = p; } #endif // RAPIDJSON_NEON template class NumberStream; template class NumberStream { public: typedef typename InputStream::Ch Ch; NumberStream(GenericReader& reader, InputStream& s) : is(s) { (void)reader; } RAPIDJSON_FORCEINLINE Ch Peek() const { return is.Peek(); } RAPIDJSON_FORCEINLINE Ch TakePush() { return is.Take(); } RAPIDJSON_FORCEINLINE Ch Take() { return is.Take(); } RAPIDJSON_FORCEINLINE void Push(char) {} size_t Tell() { return is.Tell(); } size_t Length() { return 0; } const char* Pop() { return 0; } protected: NumberStream& operator=(const NumberStream&); InputStream& is; }; template class NumberStream : public NumberStream { typedef NumberStream Base; public: NumberStream(GenericReader& reader, InputStream& is) : Base(reader, is), stackStream(reader.stack_) {} RAPIDJSON_FORCEINLINE Ch TakePush() { stackStream.Put(static_cast(Base::is.Peek())); return Base::is.Take(); } RAPIDJSON_FORCEINLINE void Push(char c) { stackStream.Put(c); } size_t Length() { return stackStream.Length(); } const char* Pop() { stackStream.Put('\0'); return stackStream.Pop(); } private: StackStream stackStream; }; template class NumberStream : public NumberStream { typedef NumberStream Base; public: NumberStream(GenericReader& reader, InputStream& is) : Base(reader, is) {} RAPIDJSON_FORCEINLINE Ch Take() { return Base::TakePush(); } }; template void ParseNumber(InputStream& is, Handler& handler) { internal::StreamLocalCopy copy(is); NumberStream s(*this, copy.s); size_t startOffset = s.Tell(); double d = 0.0; bool useNanOrInf = false; // Parse minus bool minus = Consume(s, '-'); // Parse int: zero / ( digit1-9 *DIGIT ) unsigned i = 0; uint64_t i64 = 0; bool use64bit = false; int significandDigit = 0; if (RAPIDJSON_UNLIKELY(s.Peek() == '0')) { i = 0; s.TakePush(); } else if (RAPIDJSON_LIKELY(s.Peek() >= '1' && s.Peek() <= '9')) { i = static_cast(s.TakePush() - '0'); if (minus) while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { if (RAPIDJSON_UNLIKELY(i >= 214748364)) { // 2^31 = 2147483648 if (RAPIDJSON_LIKELY(i != 214748364 || s.Peek() > '8')) { i64 = i; use64bit = true; break; } } i = i * 10 + static_cast(s.TakePush() - '0'); significandDigit++; } else while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { if (RAPIDJSON_UNLIKELY(i >= 429496729)) { // 2^32 - 1 = 4294967295 if (RAPIDJSON_LIKELY(i != 429496729 || s.Peek() > '5')) { i64 = i; use64bit = true; break; } } i = i * 10 + static_cast(s.TakePush() - '0'); significandDigit++; } } // Parse NaN or Infinity here else if ((parseFlags & kParseNanAndInfFlag) && RAPIDJSON_LIKELY((s.Peek() == 'I' || s.Peek() == 'N'))) { if (Consume(s, 'N')) { if (Consume(s, 'a') && Consume(s, 'N')) { d = std::numeric_limits::quiet_NaN(); useNanOrInf = true; } } else if (RAPIDJSON_LIKELY(Consume(s, 'I'))) { if (Consume(s, 'n') && Consume(s, 'f')) { d = (minus ? -std::numeric_limits::infinity() : std::numeric_limits::infinity()); useNanOrInf = true; if (RAPIDJSON_UNLIKELY(s.Peek() == 'i' && !(Consume(s, 'i') && Consume(s, 'n') && Consume(s, 'i') && Consume(s, 't') && Consume(s, 'y')))) { RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell()); } } } if (RAPIDJSON_UNLIKELY(!useNanOrInf)) { RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell()); } } else RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell()); // Parse 64bit int bool useDouble = false; if (use64bit) { if (minus) while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { if (RAPIDJSON_UNLIKELY(i64 >= RAPIDJSON_UINT64_C2(0x0CCCCCCC, 0xCCCCCCCC))) // 2^63 = 9223372036854775808 if (RAPIDJSON_LIKELY(i64 != RAPIDJSON_UINT64_C2(0x0CCCCCCC, 0xCCCCCCCC) || s.Peek() > '8')) { d = static_cast(i64); useDouble = true; break; } i64 = i64 * 10 + static_cast(s.TakePush() - '0'); significandDigit++; } else while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { if (RAPIDJSON_UNLIKELY(i64 >= RAPIDJSON_UINT64_C2(0x19999999, 0x99999999))) // 2^64 - 1 = 18446744073709551615 if (RAPIDJSON_LIKELY(i64 != RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) || s.Peek() > '5')) { d = static_cast(i64); useDouble = true; break; } i64 = i64 * 10 + static_cast(s.TakePush() - '0'); significandDigit++; } } // Force double for big integer if (useDouble) { while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { d = d * 10 + (s.TakePush() - '0'); } } // Parse frac = decimal-point 1*DIGIT int expFrac = 0; size_t decimalPosition; if (Consume(s, '.')) { decimalPosition = s.Length(); if (RAPIDJSON_UNLIKELY(!(s.Peek() >= '0' && s.Peek() <= '9'))) RAPIDJSON_PARSE_ERROR(kParseErrorNumberMissFraction, s.Tell()); if (!useDouble) { #if RAPIDJSON_64BIT // Use i64 to store significand in 64-bit architecture if (!use64bit) i64 = i; while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { if (i64 > RAPIDJSON_UINT64_C2(0x1FFFFF, 0xFFFFFFFF)) // 2^53 - 1 for fast path break; else { i64 = i64 * 10 + static_cast(s.TakePush() - '0'); --expFrac; if (i64 != 0) significandDigit++; } } d = static_cast(i64); #else // Use double to store significand in 32-bit architecture d = static_cast(use64bit ? i64 : i); #endif useDouble = true; } while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { if (significandDigit < 17) { d = d * 10.0 + (s.TakePush() - '0'); --expFrac; if (RAPIDJSON_LIKELY(d > 0.0)) significandDigit++; } else s.TakePush(); } } else decimalPosition = s.Length(); // decimal position at the end of integer. // Parse exp = e [ minus / plus ] 1*DIGIT int exp = 0; if (Consume(s, 'e') || Consume(s, 'E')) { if (!useDouble) { d = static_cast(use64bit ? i64 : i); useDouble = true; } bool expMinus = false; if (Consume(s, '+')) ; else if (Consume(s, '-')) expMinus = true; if (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { exp = static_cast(s.Take() - '0'); if (expMinus) { // (exp + expFrac) must not underflow int => we're detecting when -exp gets // dangerously close to INT_MIN (a pessimistic next digit 9 would push it into // underflow territory): // // -(exp * 10 + 9) + expFrac >= INT_MIN // <=> exp <= (expFrac - INT_MIN - 9) / 10 RAPIDJSON_ASSERT(expFrac <= 0); int maxExp = (expFrac + 2147483639) / 10; while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { exp = exp * 10 + static_cast(s.Take() - '0'); if (RAPIDJSON_UNLIKELY(exp > maxExp)) { while (RAPIDJSON_UNLIKELY(s.Peek() >= '0' && s.Peek() <= '9')) // Consume the rest of exponent s.Take(); } } } else { // positive exp int maxExp = 308 - expFrac; while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { exp = exp * 10 + static_cast(s.Take() - '0'); if (RAPIDJSON_UNLIKELY(exp > maxExp)) RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, startOffset); } } } else RAPIDJSON_PARSE_ERROR(kParseErrorNumberMissExponent, s.Tell()); if (expMinus) exp = -exp; } // Finish parsing, call event according to the type of number. bool cont = true; if (parseFlags & kParseNumbersAsStringsFlag) { if (parseFlags & kParseInsituFlag) { s.Pop(); // Pop stack no matter if it will be used or not. typename InputStream::Ch* head = is.PutBegin(); const size_t length = s.Tell() - startOffset; RAPIDJSON_ASSERT(length <= 0xFFFFFFFF); // unable to insert the \0 character here, it will erase the comma after this number const typename TargetEncoding::Ch* const str = reinterpret_cast(head); cont = handler.RawNumber(str, SizeType(length), false); } else { SizeType numCharsToCopy = static_cast(s.Length()); StringStream srcStream(s.Pop()); StackStream dstStream(stack_); while (numCharsToCopy--) { Transcoder, TargetEncoding>::Transcode(srcStream, dstStream); } dstStream.Put('\0'); const typename TargetEncoding::Ch* str = dstStream.Pop(); const SizeType length = static_cast(dstStream.Length()) - 1; cont = handler.RawNumber(str, SizeType(length), true); } } else { size_t length = s.Length(); const char* decimal = s.Pop(); // Pop stack no matter if it will be used or not. if (useDouble) { int p = exp + expFrac; if (parseFlags & kParseFullPrecisionFlag) d = internal::StrtodFullPrecision(d, p, decimal, length, decimalPosition, exp); else d = internal::StrtodNormalPrecision(d, p); // Use > max, instead of == inf, to fix bogus warning -Wfloat-equal if (d > (std::numeric_limits::max)()) { // Overflow // TODO: internal::StrtodX should report overflow (or underflow) RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, startOffset); } cont = handler.Double(minus ? -d : d); } else if (useNanOrInf) { cont = handler.Double(d); } else { if (use64bit) { if (minus) cont = handler.Int64(static_cast(~i64 + 1)); else cont = handler.Uint64(i64); } else { if (minus) cont = handler.Int(static_cast(~i + 1)); else cont = handler.Uint(i); } } } if (RAPIDJSON_UNLIKELY(!cont)) RAPIDJSON_PARSE_ERROR(kParseErrorTermination, startOffset); } // Parse any JSON value template void ParseValue(InputStream& is, Handler& handler) { switch (is.Peek()) { case 'n': ParseNull (is, handler); break; case 't': ParseTrue (is, handler); break; case 'f': ParseFalse (is, handler); break; case '"': ParseString(is, handler); break; case '{': ParseObject(is, handler); break; case '[': ParseArray (is, handler); break; default : ParseNumber(is, handler); break; } } // Iterative Parsing // States enum IterativeParsingState { IterativeParsingFinishState = 0, // sink states at top IterativeParsingErrorState, // sink states at top IterativeParsingStartState, // Object states IterativeParsingObjectInitialState, IterativeParsingMemberKeyState, IterativeParsingMemberValueState, IterativeParsingObjectFinishState, // Array states IterativeParsingArrayInitialState, IterativeParsingElementState, IterativeParsingArrayFinishState, // Single value state IterativeParsingValueState, // Delimiter states (at bottom) IterativeParsingElementDelimiterState, IterativeParsingMemberDelimiterState, IterativeParsingKeyValueDelimiterState, cIterativeParsingStateCount }; // Tokens enum Token { LeftBracketToken = 0, RightBracketToken, LeftCurlyBracketToken, RightCurlyBracketToken, CommaToken, ColonToken, StringToken, FalseToken, TrueToken, NullToken, NumberToken, kTokenCount }; RAPIDJSON_FORCEINLINE Token Tokenize(Ch c) const { //!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN #define N NumberToken #define N16 N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N // Maps from ASCII to Token static const unsigned char tokenMap[256] = { N16, // 00~0F N16, // 10~1F N, N, StringToken, N, N, N, N, N, N, N, N, N, CommaToken, N, N, N, // 20~2F N, N, N, N, N, N, N, N, N, N, ColonToken, N, N, N, N, N, // 30~3F N16, // 40~4F N, N, N, N, N, N, N, N, N, N, N, LeftBracketToken, N, RightBracketToken, N, N, // 50~5F N, N, N, N, N, N, FalseToken, N, N, N, N, N, N, N, NullToken, N, // 60~6F N, N, N, N, TrueToken, N, N, N, N, N, N, LeftCurlyBracketToken, N, RightCurlyBracketToken, N, N, // 70~7F N16, N16, N16, N16, N16, N16, N16, N16 // 80~FF }; #undef N #undef N16 //!@endcond if (sizeof(Ch) == 1 || static_cast(c) < 256) return static_cast(tokenMap[static_cast(c)]); else return NumberToken; } RAPIDJSON_FORCEINLINE IterativeParsingState Predict(IterativeParsingState state, Token token) const { // current state x one lookahead token -> new state static const char G[cIterativeParsingStateCount][kTokenCount] = { // Finish(sink state) { IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState }, // Error(sink state) { IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState }, // Start { IterativeParsingArrayInitialState, // Left bracket IterativeParsingErrorState, // Right bracket IterativeParsingObjectInitialState, // Left curly bracket IterativeParsingErrorState, // Right curly bracket IterativeParsingErrorState, // Comma IterativeParsingErrorState, // Colon IterativeParsingValueState, // String IterativeParsingValueState, // False IterativeParsingValueState, // True IterativeParsingValueState, // Null IterativeParsingValueState // Number }, // ObjectInitial { IterativeParsingErrorState, // Left bracket IterativeParsingErrorState, // Right bracket IterativeParsingErrorState, // Left curly bracket IterativeParsingObjectFinishState, // Right curly bracket IterativeParsingErrorState, // Comma IterativeParsingErrorState, // Colon IterativeParsingMemberKeyState, // String IterativeParsingErrorState, // False IterativeParsingErrorState, // True IterativeParsingErrorState, // Null IterativeParsingErrorState // Number }, // MemberKey { IterativeParsingErrorState, // Left bracket IterativeParsingErrorState, // Right bracket IterativeParsingErrorState, // Left curly bracket IterativeParsingErrorState, // Right curly bracket IterativeParsingErrorState, // Comma IterativeParsingKeyValueDelimiterState, // Colon IterativeParsingErrorState, // String IterativeParsingErrorState, // False IterativeParsingErrorState, // True IterativeParsingErrorState, // Null IterativeParsingErrorState // Number }, // MemberValue { IterativeParsingErrorState, // Left bracket IterativeParsingErrorState, // Right bracket IterativeParsingErrorState, // Left curly bracket IterativeParsingObjectFinishState, // Right curly bracket IterativeParsingMemberDelimiterState, // Comma IterativeParsingErrorState, // Colon IterativeParsingErrorState, // String IterativeParsingErrorState, // False IterativeParsingErrorState, // True IterativeParsingErrorState, // Null IterativeParsingErrorState // Number }, // ObjectFinish(sink state) { IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState }, // ArrayInitial { IterativeParsingArrayInitialState, // Left bracket(push Element state) IterativeParsingArrayFinishState, // Right bracket IterativeParsingObjectInitialState, // Left curly bracket(push Element state) IterativeParsingErrorState, // Right curly bracket IterativeParsingErrorState, // Comma IterativeParsingErrorState, // Colon IterativeParsingElementState, // String IterativeParsingElementState, // False IterativeParsingElementState, // True IterativeParsingElementState, // Null IterativeParsingElementState // Number }, // Element { IterativeParsingErrorState, // Left bracket IterativeParsingArrayFinishState, // Right bracket IterativeParsingErrorState, // Left curly bracket IterativeParsingErrorState, // Right curly bracket IterativeParsingElementDelimiterState, // Comma IterativeParsingErrorState, // Colon IterativeParsingErrorState, // String IterativeParsingErrorState, // False IterativeParsingErrorState, // True IterativeParsingErrorState, // Null IterativeParsingErrorState // Number }, // ArrayFinish(sink state) { IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState }, // Single Value (sink state) { IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState }, // ElementDelimiter { IterativeParsingArrayInitialState, // Left bracket(push Element state) IterativeParsingArrayFinishState, // Right bracket IterativeParsingObjectInitialState, // Left curly bracket(push Element state) IterativeParsingErrorState, // Right curly bracket IterativeParsingErrorState, // Comma IterativeParsingErrorState, // Colon IterativeParsingElementState, // String IterativeParsingElementState, // False IterativeParsingElementState, // True IterativeParsingElementState, // Null IterativeParsingElementState // Number }, // MemberDelimiter { IterativeParsingErrorState, // Left bracket IterativeParsingErrorState, // Right bracket IterativeParsingErrorState, // Left curly bracket IterativeParsingObjectFinishState, // Right curly bracket IterativeParsingErrorState, // Comma IterativeParsingErrorState, // Colon IterativeParsingMemberKeyState, // String IterativeParsingErrorState, // False IterativeParsingErrorState, // True IterativeParsingErrorState, // Null IterativeParsingErrorState // Number }, // KeyValueDelimiter { IterativeParsingArrayInitialState, // Left bracket(push MemberValue state) IterativeParsingErrorState, // Right bracket IterativeParsingObjectInitialState, // Left curly bracket(push MemberValue state) IterativeParsingErrorState, // Right curly bracket IterativeParsingErrorState, // Comma IterativeParsingErrorState, // Colon IterativeParsingMemberValueState, // String IterativeParsingMemberValueState, // False IterativeParsingMemberValueState, // True IterativeParsingMemberValueState, // Null IterativeParsingMemberValueState // Number }, }; // End of G return static_cast(G[state][token]); } // Make an advance in the token stream and state based on the candidate destination state which was returned by Transit(). // May return a new state on state pop. template RAPIDJSON_FORCEINLINE IterativeParsingState Transit(IterativeParsingState src, Token token, IterativeParsingState dst, InputStream& is, Handler& handler) { (void)token; switch (dst) { case IterativeParsingErrorState: return dst; case IterativeParsingObjectInitialState: case IterativeParsingArrayInitialState: { // Push the state(Element or MemeberValue) if we are nested in another array or value of member. // In this way we can get the correct state on ObjectFinish or ArrayFinish by frame pop. IterativeParsingState n = src; if (src == IterativeParsingArrayInitialState || src == IterativeParsingElementDelimiterState) n = IterativeParsingElementState; else if (src == IterativeParsingKeyValueDelimiterState) n = IterativeParsingMemberValueState; // Push current state. *stack_.template Push(1) = n; // Initialize and push the member/element count. *stack_.template Push(1) = 0; // Call handler bool hr = (dst == IterativeParsingObjectInitialState) ? handler.StartObject() : handler.StartArray(); // On handler short circuits the parsing. if (!hr) { RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell()); return IterativeParsingErrorState; } else { is.Take(); return dst; } } case IterativeParsingMemberKeyState: ParseString(is, handler, true); if (HasParseError()) return IterativeParsingErrorState; else return dst; case IterativeParsingKeyValueDelimiterState: RAPIDJSON_ASSERT(token == ColonToken); is.Take(); return dst; case IterativeParsingMemberValueState: // Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state. ParseValue(is, handler); if (HasParseError()) { return IterativeParsingErrorState; } return dst; case IterativeParsingElementState: // Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state. ParseValue(is, handler); if (HasParseError()) { return IterativeParsingErrorState; } return dst; case IterativeParsingMemberDelimiterState: case IterativeParsingElementDelimiterState: is.Take(); // Update member/element count. *stack_.template Top() = *stack_.template Top() + 1; return dst; case IterativeParsingObjectFinishState: { // Transit from delimiter is only allowed when trailing commas are enabled if (!(parseFlags & kParseTrailingCommasFlag) && src == IterativeParsingMemberDelimiterState) { RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorObjectMissName, is.Tell()); return IterativeParsingErrorState; } // Get member count. SizeType c = *stack_.template Pop(1); // If the object is not empty, count the last member. if (src == IterativeParsingMemberValueState) ++c; // Restore the state. IterativeParsingState n = static_cast(*stack_.template Pop(1)); // Transit to Finish state if this is the topmost scope. if (n == IterativeParsingStartState) n = IterativeParsingFinishState; // Call handler bool hr = handler.EndObject(c); // On handler short circuits the parsing. if (!hr) { RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell()); return IterativeParsingErrorState; } else { is.Take(); return n; } } case IterativeParsingArrayFinishState: { // Transit from delimiter is only allowed when trailing commas are enabled if (!(parseFlags & kParseTrailingCommasFlag) && src == IterativeParsingElementDelimiterState) { RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorValueInvalid, is.Tell()); return IterativeParsingErrorState; } // Get element count. SizeType c = *stack_.template Pop(1); // If the array is not empty, count the last element. if (src == IterativeParsingElementState) ++c; // Restore the state. IterativeParsingState n = static_cast(*stack_.template Pop(1)); // Transit to Finish state if this is the topmost scope. if (n == IterativeParsingStartState) n = IterativeParsingFinishState; // Call handler bool hr = handler.EndArray(c); // On handler short circuits the parsing. if (!hr) { RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell()); return IterativeParsingErrorState; } else { is.Take(); return n; } } default: // This branch is for IterativeParsingValueState actually. // Use `default:` rather than // `case IterativeParsingValueState:` is for code coverage. // The IterativeParsingStartState is not enumerated in this switch-case. // It is impossible for that case. And it can be caught by following assertion. // The IterativeParsingFinishState is not enumerated in this switch-case either. // It is a "derivative" state which cannot triggered from Predict() directly. // Therefore it cannot happen here. And it can be caught by following assertion. RAPIDJSON_ASSERT(dst == IterativeParsingValueState); // Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state. ParseValue(is, handler); if (HasParseError()) { return IterativeParsingErrorState; } return IterativeParsingFinishState; } } template void HandleError(IterativeParsingState src, InputStream& is) { if (HasParseError()) { // Error flag has been set. return; } switch (src) { case IterativeParsingStartState: RAPIDJSON_PARSE_ERROR(kParseErrorDocumentEmpty, is.Tell()); return; case IterativeParsingFinishState: RAPIDJSON_PARSE_ERROR(kParseErrorDocumentRootNotSingular, is.Tell()); return; case IterativeParsingObjectInitialState: case IterativeParsingMemberDelimiterState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell()); return; case IterativeParsingMemberKeyState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissColon, is.Tell()); return; case IterativeParsingMemberValueState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, is.Tell()); return; case IterativeParsingKeyValueDelimiterState: case IterativeParsingArrayInitialState: case IterativeParsingElementDelimiterState: RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); return; default: RAPIDJSON_ASSERT(src == IterativeParsingElementState); RAPIDJSON_PARSE_ERROR(kParseErrorArrayMissCommaOrSquareBracket, is.Tell()); return; } } RAPIDJSON_FORCEINLINE bool IsIterativeParsingDelimiterState(IterativeParsingState s) const { return s >= IterativeParsingElementDelimiterState; } RAPIDJSON_FORCEINLINE bool IsIterativeParsingCompleteState(IterativeParsingState s) const { return s <= IterativeParsingErrorState; } template ParseResult IterativeParse(InputStream& is, Handler& handler) { parseResult_.Clear(); ClearStackOnExit scope(*this); IterativeParsingState state = IterativeParsingStartState; SkipWhitespaceAndComments(is); RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); while (is.Peek() != '\0') { Token t = Tokenize(is.Peek()); IterativeParsingState n = Predict(state, t); IterativeParsingState d = Transit(state, t, n, is, handler); if (d == IterativeParsingErrorState) { HandleError(state, is); break; } state = d; // Do not further consume streams if a root JSON has been parsed. if ((parseFlags & kParseStopWhenDoneFlag) && state == IterativeParsingFinishState) break; SkipWhitespaceAndComments(is); RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); } // Handle the end of file. if (state != IterativeParsingFinishState) HandleError(state, is); return parseResult_; } static const size_t kDefaultStackCapacity = 256; //!< Default stack capacity in bytes for storing a single decoded string. internal::Stack stack_; //!< A stack for storing decoded string temporarily during non-destructive parsing. ParseResult parseResult_; IterativeParsingState state_; }; // class GenericReader //! Reader with UTF8 encoding and default allocator. typedef GenericReader, UTF8<> > Reader; RAPIDJSON_NAMESPACE_END #if defined(__clang__) || defined(_MSC_VER) RAPIDJSON_DIAG_POP #endif #ifdef __GNUC__ RAPIDJSON_DIAG_POP #endif #endif // RAPIDJSON_READER_H_ ================================================ FILE: src/json/rapidjson/schema.h ================================================ // Tencent is pleased to support the open source community by making RapidJSON available-> // // Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip-> All rights reserved-> // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License-> You may obtain a copy of the License at // // http://opensource->org/licenses/MIT // // Unless required by applicable law or agreed to in writing, software distributed // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR // CONDITIONS OF ANY KIND, either express or implied-> See the License for the // specific language governing permissions and limitations under the License-> #ifndef RAPIDJSON_SCHEMA_H_ #define RAPIDJSON_SCHEMA_H_ #include "document.h" #include "pointer.h" #include "stringbuffer.h" #include // abs, floor #if !defined(RAPIDJSON_SCHEMA_USE_INTERNALREGEX) #define RAPIDJSON_SCHEMA_USE_INTERNALREGEX 1 #else #define RAPIDJSON_SCHEMA_USE_INTERNALREGEX 0 #endif #if !RAPIDJSON_SCHEMA_USE_INTERNALREGEX && defined(RAPIDJSON_SCHEMA_USE_STDREGEX) && (__cplusplus >=201103L || (defined(_MSC_VER) && _MSC_VER >= 1800)) #define RAPIDJSON_SCHEMA_USE_STDREGEX 1 #else #define RAPIDJSON_SCHEMA_USE_STDREGEX 0 #endif #if RAPIDJSON_SCHEMA_USE_INTERNALREGEX #include "src/json/rapidjson/internal/regex.h" #elif RAPIDJSON_SCHEMA_USE_STDREGEX #include #endif #if RAPIDJSON_SCHEMA_USE_INTERNALREGEX || RAPIDJSON_SCHEMA_USE_STDREGEX #define RAPIDJSON_SCHEMA_HAS_REGEX 1 #else #define RAPIDJSON_SCHEMA_HAS_REGEX 0 #endif #ifndef RAPIDJSON_SCHEMA_VERBOSE #define RAPIDJSON_SCHEMA_VERBOSE 0 #endif #if RAPIDJSON_SCHEMA_VERBOSE #include "stringbuffer.h" #endif RAPIDJSON_DIAG_PUSH #if defined(__GNUC__) RAPIDJSON_DIAG_OFF(effc++) #endif #ifdef __clang__ RAPIDJSON_DIAG_OFF(weak-vtables) RAPIDJSON_DIAG_OFF(exit-time-destructors) RAPIDJSON_DIAG_OFF(c++98-compat-pedantic) RAPIDJSON_DIAG_OFF(variadic-macros) #elif defined(_MSC_VER) RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated #endif RAPIDJSON_NAMESPACE_BEGIN /////////////////////////////////////////////////////////////////////////////// // Verbose Utilities #if RAPIDJSON_SCHEMA_VERBOSE namespace internal { inline void PrintInvalidKeyword(const char* keyword) { printf("Fail keyword: %s\n", keyword); } inline void PrintInvalidKeyword(const wchar_t* keyword) { wprintf(L"Fail keyword: %ls\n", keyword); } inline void PrintInvalidDocument(const char* document) { printf("Fail document: %s\n\n", document); } inline void PrintInvalidDocument(const wchar_t* document) { wprintf(L"Fail document: %ls\n\n", document); } inline void PrintValidatorPointers(unsigned depth, const char* s, const char* d) { printf("S: %*s%s\nD: %*s%s\n\n", depth * 4, " ", s, depth * 4, " ", d); } inline void PrintValidatorPointers(unsigned depth, const wchar_t* s, const wchar_t* d) { wprintf(L"S: %*ls%ls\nD: %*ls%ls\n\n", depth * 4, L" ", s, depth * 4, L" ", d); } } // namespace internal #endif // RAPIDJSON_SCHEMA_VERBOSE /////////////////////////////////////////////////////////////////////////////// // RAPIDJSON_INVALID_KEYWORD_RETURN #if RAPIDJSON_SCHEMA_VERBOSE #define RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword) internal::PrintInvalidKeyword(keyword) #else #define RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword) #endif #define RAPIDJSON_INVALID_KEYWORD_RETURN(keyword)\ RAPIDJSON_MULTILINEMACRO_BEGIN\ context.invalidKeyword = keyword.GetString();\ RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword.GetString());\ return false;\ RAPIDJSON_MULTILINEMACRO_END /////////////////////////////////////////////////////////////////////////////// // Forward declarations template class GenericSchemaDocument; namespace internal { template class Schema; /////////////////////////////////////////////////////////////////////////////// // ISchemaValidator class ISchemaValidator { public: virtual ~ISchemaValidator() {} virtual bool IsValid() const = 0; }; /////////////////////////////////////////////////////////////////////////////// // ISchemaStateFactory template class ISchemaStateFactory { public: virtual ~ISchemaStateFactory() {} virtual ISchemaValidator* CreateSchemaValidator(const SchemaType&) = 0; virtual void DestroySchemaValidator(ISchemaValidator* validator) = 0; virtual void* CreateHasher() = 0; virtual uint64_t GetHashCode(void* hasher) = 0; virtual void DestroryHasher(void* hasher) = 0; virtual void* MallocState(size_t size) = 0; virtual void FreeState(void* p) = 0; }; /////////////////////////////////////////////////////////////////////////////// // IValidationErrorHandler template class IValidationErrorHandler { public: typedef typename SchemaType::Ch Ch; typedef typename SchemaType::SValue SValue; virtual ~IValidationErrorHandler() {} virtual void NotMultipleOf(int64_t actual, const SValue& expected) = 0; virtual void NotMultipleOf(uint64_t actual, const SValue& expected) = 0; virtual void NotMultipleOf(double actual, const SValue& expected) = 0; virtual void AboveMaximum(int64_t actual, const SValue& expected, bool exclusive) = 0; virtual void AboveMaximum(uint64_t actual, const SValue& expected, bool exclusive) = 0; virtual void AboveMaximum(double actual, const SValue& expected, bool exclusive) = 0; virtual void BelowMinimum(int64_t actual, const SValue& expected, bool exclusive) = 0; virtual void BelowMinimum(uint64_t actual, const SValue& expected, bool exclusive) = 0; virtual void BelowMinimum(double actual, const SValue& expected, bool exclusive) = 0; virtual void TooLong(const Ch* str, SizeType length, SizeType expected) = 0; virtual void TooShort(const Ch* str, SizeType length, SizeType expected) = 0; virtual void DoesNotMatch(const Ch* str, SizeType length) = 0; virtual void DisallowedItem(SizeType index) = 0; virtual void TooFewItems(SizeType actualCount, SizeType expectedCount) = 0; virtual void TooManyItems(SizeType actualCount, SizeType expectedCount) = 0; virtual void DuplicateItems(SizeType index1, SizeType index2) = 0; virtual void TooManyProperties(SizeType actualCount, SizeType expectedCount) = 0; virtual void TooFewProperties(SizeType actualCount, SizeType expectedCount) = 0; virtual void StartMissingProperties() = 0; virtual void AddMissingProperty(const SValue& name) = 0; virtual bool EndMissingProperties() = 0; virtual void PropertyViolations(ISchemaValidator** subvalidators, SizeType count) = 0; virtual void DisallowedProperty(const Ch* name, SizeType length) = 0; virtual void StartDependencyErrors() = 0; virtual void StartMissingDependentProperties() = 0; virtual void AddMissingDependentProperty(const SValue& targetName) = 0; virtual void EndMissingDependentProperties(const SValue& sourceName) = 0; virtual void AddDependencySchemaError(const SValue& souceName, ISchemaValidator* subvalidator) = 0; virtual bool EndDependencyErrors() = 0; virtual void DisallowedValue() = 0; virtual void StartDisallowedType() = 0; virtual void AddExpectedType(const typename SchemaType::ValueType& expectedType) = 0; virtual void EndDisallowedType(const typename SchemaType::ValueType& actualType) = 0; virtual void NotAllOf(ISchemaValidator** subvalidators, SizeType count) = 0; virtual void NoneOf(ISchemaValidator** subvalidators, SizeType count) = 0; virtual void NotOneOf(ISchemaValidator** subvalidators, SizeType count) = 0; virtual void Disallowed() = 0; }; /////////////////////////////////////////////////////////////////////////////// // Hasher // For comparison of compound value template class Hasher { public: typedef typename Encoding::Ch Ch; Hasher(Allocator* allocator = 0, size_t stackCapacity = kDefaultSize) : stack_(allocator, stackCapacity) {} bool Null() { return WriteType(kNullType); } bool Bool(bool b) { return WriteType(b ? kTrueType : kFalseType); } bool Int(int i) { Number n; n.u.i = i; n.d = static_cast(i); return WriteNumber(n); } bool Uint(unsigned u) { Number n; n.u.u = u; n.d = static_cast(u); return WriteNumber(n); } bool Int64(int64_t i) { Number n; n.u.i = i; n.d = static_cast(i); return WriteNumber(n); } bool Uint64(uint64_t u) { Number n; n.u.u = u; n.d = static_cast(u); return WriteNumber(n); } bool Double(double d) { Number n; if (d < 0) n.u.i = static_cast(d); else n.u.u = static_cast(d); n.d = d; return WriteNumber(n); } bool RawNumber(const Ch* str, SizeType len, bool) { WriteBuffer(kNumberType, str, len * sizeof(Ch)); return true; } bool String(const Ch* str, SizeType len, bool) { WriteBuffer(kStringType, str, len * sizeof(Ch)); return true; } bool StartObject() { return true; } bool Key(const Ch* str, SizeType len, bool copy) { return String(str, len, copy); } bool EndObject(SizeType memberCount) { uint64_t h = Hash(0, kObjectType); uint64_t* kv = stack_.template Pop(memberCount * 2); for (SizeType i = 0; i < memberCount; i++) h ^= Hash(kv[i * 2], kv[i * 2 + 1]); // Use xor to achieve member order insensitive *stack_.template Push() = h; return true; } bool StartArray() { return true; } bool EndArray(SizeType elementCount) { uint64_t h = Hash(0, kArrayType); uint64_t* e = stack_.template Pop(elementCount); for (SizeType i = 0; i < elementCount; i++) h = Hash(h, e[i]); // Use hash to achieve element order sensitive *stack_.template Push() = h; return true; } bool IsValid() const { return stack_.GetSize() == sizeof(uint64_t); } uint64_t GetHashCode() const { RAPIDJSON_ASSERT(IsValid()); return *stack_.template Top(); } private: static const size_t kDefaultSize = 256; struct Number { union U { uint64_t u; int64_t i; }u; double d; }; bool WriteType(Type type) { return WriteBuffer(type, 0, 0); } bool WriteNumber(const Number& n) { return WriteBuffer(kNumberType, &n, sizeof(n)); } bool WriteBuffer(Type type, const void* data, size_t len) { // FNV-1a from http://isthe.com/chongo/tech/comp/fnv/ uint64_t h = Hash(RAPIDJSON_UINT64_C2(0x84222325, 0xcbf29ce4), type); const unsigned char* d = static_cast(data); for (size_t i = 0; i < len; i++) h = Hash(h, d[i]); *stack_.template Push() = h; return true; } static uint64_t Hash(uint64_t h, uint64_t d) { static const uint64_t kPrime = RAPIDJSON_UINT64_C2(0x00000100, 0x000001b3); h ^= d; h *= kPrime; return h; } Stack stack_; }; /////////////////////////////////////////////////////////////////////////////// // SchemaValidationContext template struct SchemaValidationContext { typedef Schema SchemaType; typedef ISchemaStateFactory SchemaValidatorFactoryType; typedef IValidationErrorHandler ErrorHandlerType; typedef typename SchemaType::ValueType ValueType; typedef typename ValueType::Ch Ch; enum PatternValidatorType { kPatternValidatorOnly, kPatternValidatorWithProperty, kPatternValidatorWithAdditionalProperty }; SchemaValidationContext(SchemaValidatorFactoryType& f, ErrorHandlerType& eh, const SchemaType* s) : factory(f), error_handler(eh), schema(s), valueSchema(), invalidKeyword(), hasher(), arrayElementHashCodes(), validators(), validatorCount(), patternPropertiesValidators(), patternPropertiesValidatorCount(), patternPropertiesSchemas(), patternPropertiesSchemaCount(), valuePatternValidatorType(kPatternValidatorOnly), propertyExist(), inArray(false), valueUniqueness(false), arrayUniqueness(false) { } ~SchemaValidationContext() { if (hasher) factory.DestroryHasher(hasher); if (validators) { for (SizeType i = 0; i < validatorCount; i++) factory.DestroySchemaValidator(validators[i]); factory.FreeState(validators); } if (patternPropertiesValidators) { for (SizeType i = 0; i < patternPropertiesValidatorCount; i++) factory.DestroySchemaValidator(patternPropertiesValidators[i]); factory.FreeState(patternPropertiesValidators); } if (patternPropertiesSchemas) factory.FreeState(patternPropertiesSchemas); if (propertyExist) factory.FreeState(propertyExist); } SchemaValidatorFactoryType& factory; ErrorHandlerType& error_handler; const SchemaType* schema; const SchemaType* valueSchema; const Ch* invalidKeyword; void* hasher; // Only validator access void* arrayElementHashCodes; // Only validator access this ISchemaValidator** validators; SizeType validatorCount; ISchemaValidator** patternPropertiesValidators; SizeType patternPropertiesValidatorCount; const SchemaType** patternPropertiesSchemas; SizeType patternPropertiesSchemaCount; PatternValidatorType valuePatternValidatorType; PatternValidatorType objectPatternValidatorType; SizeType arrayElementIndex; bool* propertyExist; bool inArray; bool valueUniqueness; bool arrayUniqueness; }; /////////////////////////////////////////////////////////////////////////////// // Schema template class Schema { public: typedef typename SchemaDocumentType::ValueType ValueType; typedef typename SchemaDocumentType::AllocatorType AllocatorType; typedef typename SchemaDocumentType::PointerType PointerType; typedef typename ValueType::EncodingType EncodingType; typedef typename EncodingType::Ch Ch; typedef SchemaValidationContext Context; typedef Schema SchemaType; typedef GenericValue SValue; typedef IValidationErrorHandler ErrorHandler; friend class GenericSchemaDocument; Schema(SchemaDocumentType* schemaDocument, const PointerType& p, const ValueType& value, const ValueType& document, AllocatorType* allocator) : allocator_(allocator), uri_(schemaDocument->GetURI(), *allocator), pointer_(p, allocator), typeless_(schemaDocument->GetTypeless()), enum_(), enumCount_(), not_(), type_((1 << kTotalSchemaType) - 1), // typeless validatorCount_(), notValidatorIndex_(), properties_(), additionalPropertiesSchema_(), patternProperties_(), patternPropertyCount_(), propertyCount_(), minProperties_(), maxProperties_(SizeType(~0)), additionalProperties_(true), hasDependencies_(), hasRequired_(), hasSchemaDependencies_(), additionalItemsSchema_(), itemsList_(), itemsTuple_(), itemsTupleCount_(), minItems_(), maxItems_(SizeType(~0)), additionalItems_(true), uniqueItems_(false), pattern_(), minLength_(0), maxLength_(~SizeType(0)), exclusiveMinimum_(false), exclusiveMaximum_(false), defaultValueLength_(0) { typedef typename SchemaDocumentType::ValueType ValueType; typedef typename ValueType::ConstValueIterator ConstValueIterator; typedef typename ValueType::ConstMemberIterator ConstMemberIterator; if (!value.IsObject()) return; if (const ValueType* v = GetMember(value, GetTypeString())) { type_ = 0; if (v->IsString()) AddType(*v); else if (v->IsArray()) for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr) AddType(*itr); } if (const ValueType* v = GetMember(value, GetEnumString())) if (v->IsArray() && v->Size() > 0) { enum_ = static_cast(allocator_->Malloc(sizeof(uint64_t) * v->Size())); for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr) { typedef Hasher > EnumHasherType; char buffer[256u + 24]; MemoryPoolAllocator<> hasherAllocator(buffer, sizeof(buffer)); EnumHasherType h(&hasherAllocator, 256); itr->Accept(h); enum_[enumCount_++] = h.GetHashCode(); } } if (schemaDocument) { AssignIfExist(allOf_, *schemaDocument, p, value, GetAllOfString(), document); AssignIfExist(anyOf_, *schemaDocument, p, value, GetAnyOfString(), document); AssignIfExist(oneOf_, *schemaDocument, p, value, GetOneOfString(), document); } if (const ValueType* v = GetMember(value, GetNotString())) { schemaDocument->CreateSchema(¬_, p.Append(GetNotString(), allocator_), *v, document); notValidatorIndex_ = validatorCount_; validatorCount_++; } // Object const ValueType* properties = GetMember(value, GetPropertiesString()); const ValueType* required = GetMember(value, GetRequiredString()); const ValueType* dependencies = GetMember(value, GetDependenciesString()); { // Gather properties from properties/required/dependencies SValue allProperties(kArrayType); if (properties && properties->IsObject()) for (ConstMemberIterator itr = properties->MemberBegin(); itr != properties->MemberEnd(); ++itr) AddUniqueElement(allProperties, itr->name); if (required && required->IsArray()) for (ConstValueIterator itr = required->Begin(); itr != required->End(); ++itr) if (itr->IsString()) AddUniqueElement(allProperties, *itr); if (dependencies && dependencies->IsObject()) for (ConstMemberIterator itr = dependencies->MemberBegin(); itr != dependencies->MemberEnd(); ++itr) { AddUniqueElement(allProperties, itr->name); if (itr->value.IsArray()) for (ConstValueIterator i = itr->value.Begin(); i != itr->value.End(); ++i) if (i->IsString()) AddUniqueElement(allProperties, *i); } if (allProperties.Size() > 0) { propertyCount_ = allProperties.Size(); properties_ = static_cast(allocator_->Malloc(sizeof(Property) * propertyCount_)); for (SizeType i = 0; i < propertyCount_; i++) { new (&properties_[i]) Property(); properties_[i].name = allProperties[i]; properties_[i].schema = typeless_; } } } if (properties && properties->IsObject()) { PointerType q = p.Append(GetPropertiesString(), allocator_); for (ConstMemberIterator itr = properties->MemberBegin(); itr != properties->MemberEnd(); ++itr) { SizeType index; if (FindPropertyIndex(itr->name, &index)) schemaDocument->CreateSchema(&properties_[index].schema, q.Append(itr->name, allocator_), itr->value, document); } } if (const ValueType* v = GetMember(value, GetPatternPropertiesString())) { PointerType q = p.Append(GetPatternPropertiesString(), allocator_); patternProperties_ = static_cast(allocator_->Malloc(sizeof(PatternProperty) * v->MemberCount())); patternPropertyCount_ = 0; for (ConstMemberIterator itr = v->MemberBegin(); itr != v->MemberEnd(); ++itr) { new (&patternProperties_[patternPropertyCount_]) PatternProperty(); patternProperties_[patternPropertyCount_].pattern = CreatePattern(itr->name); schemaDocument->CreateSchema(&patternProperties_[patternPropertyCount_].schema, q.Append(itr->name, allocator_), itr->value, document); patternPropertyCount_++; } } if (required && required->IsArray()) for (ConstValueIterator itr = required->Begin(); itr != required->End(); ++itr) if (itr->IsString()) { SizeType index; if (FindPropertyIndex(*itr, &index)) { properties_[index].required = true; hasRequired_ = true; } } if (dependencies && dependencies->IsObject()) { PointerType q = p.Append(GetDependenciesString(), allocator_); hasDependencies_ = true; for (ConstMemberIterator itr = dependencies->MemberBegin(); itr != dependencies->MemberEnd(); ++itr) { SizeType sourceIndex; if (FindPropertyIndex(itr->name, &sourceIndex)) { if (itr->value.IsArray()) { properties_[sourceIndex].dependencies = static_cast(allocator_->Malloc(sizeof(bool) * propertyCount_)); std::memset(properties_[sourceIndex].dependencies, 0, sizeof(bool)* propertyCount_); for (ConstValueIterator targetItr = itr->value.Begin(); targetItr != itr->value.End(); ++targetItr) { SizeType targetIndex; if (FindPropertyIndex(*targetItr, &targetIndex)) properties_[sourceIndex].dependencies[targetIndex] = true; } } else if (itr->value.IsObject()) { hasSchemaDependencies_ = true; schemaDocument->CreateSchema(&properties_[sourceIndex].dependenciesSchema, q.Append(itr->name, allocator_), itr->value, document); properties_[sourceIndex].dependenciesValidatorIndex = validatorCount_; validatorCount_++; } } } } if (const ValueType* v = GetMember(value, GetAdditionalPropertiesString())) { if (v->IsBool()) additionalProperties_ = v->GetBool(); else if (v->IsObject()) schemaDocument->CreateSchema(&additionalPropertiesSchema_, p.Append(GetAdditionalPropertiesString(), allocator_), *v, document); } AssignIfExist(minProperties_, value, GetMinPropertiesString()); AssignIfExist(maxProperties_, value, GetMaxPropertiesString()); // Array if (const ValueType* v = GetMember(value, GetItemsString())) { PointerType q = p.Append(GetItemsString(), allocator_); if (v->IsObject()) // List validation schemaDocument->CreateSchema(&itemsList_, q, *v, document); else if (v->IsArray()) { // Tuple validation itemsTuple_ = static_cast(allocator_->Malloc(sizeof(const Schema*) * v->Size())); SizeType index = 0; for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr, index++) schemaDocument->CreateSchema(&itemsTuple_[itemsTupleCount_++], q.Append(index, allocator_), *itr, document); } } AssignIfExist(minItems_, value, GetMinItemsString()); AssignIfExist(maxItems_, value, GetMaxItemsString()); if (const ValueType* v = GetMember(value, GetAdditionalItemsString())) { if (v->IsBool()) additionalItems_ = v->GetBool(); else if (v->IsObject()) schemaDocument->CreateSchema(&additionalItemsSchema_, p.Append(GetAdditionalItemsString(), allocator_), *v, document); } AssignIfExist(uniqueItems_, value, GetUniqueItemsString()); // String AssignIfExist(minLength_, value, GetMinLengthString()); AssignIfExist(maxLength_, value, GetMaxLengthString()); if (const ValueType* v = GetMember(value, GetPatternString())) pattern_ = CreatePattern(*v); // Number if (const ValueType* v = GetMember(value, GetMinimumString())) if (v->IsNumber()) minimum_.CopyFrom(*v, *allocator_); if (const ValueType* v = GetMember(value, GetMaximumString())) if (v->IsNumber()) maximum_.CopyFrom(*v, *allocator_); AssignIfExist(exclusiveMinimum_, value, GetExclusiveMinimumString()); AssignIfExist(exclusiveMaximum_, value, GetExclusiveMaximumString()); if (const ValueType* v = GetMember(value, GetMultipleOfString())) if (v->IsNumber() && v->GetDouble() > 0.0) multipleOf_.CopyFrom(*v, *allocator_); // Default if (const ValueType* v = GetMember(value, GetDefaultValueString())) if (v->IsString()) defaultValueLength_ = v->GetStringLength(); } ~Schema() { AllocatorType::Free(enum_); if (properties_) { for (SizeType i = 0; i < propertyCount_; i++) properties_[i].~Property(); AllocatorType::Free(properties_); } if (patternProperties_) { for (SizeType i = 0; i < patternPropertyCount_; i++) patternProperties_[i].~PatternProperty(); AllocatorType::Free(patternProperties_); } AllocatorType::Free(itemsTuple_); #if RAPIDJSON_SCHEMA_HAS_REGEX if (pattern_) { pattern_->~RegexType(); AllocatorType::Free(pattern_); } #endif } const SValue& GetURI() const { return uri_; } const PointerType& GetPointer() const { return pointer_; } bool BeginValue(Context& context) const { if (context.inArray) { if (uniqueItems_) context.valueUniqueness = true; if (itemsList_) context.valueSchema = itemsList_; else if (itemsTuple_) { if (context.arrayElementIndex < itemsTupleCount_) context.valueSchema = itemsTuple_[context.arrayElementIndex]; else if (additionalItemsSchema_) context.valueSchema = additionalItemsSchema_; else if (additionalItems_) context.valueSchema = typeless_; else { context.error_handler.DisallowedItem(context.arrayElementIndex); RAPIDJSON_INVALID_KEYWORD_RETURN(GetItemsString()); } } else context.valueSchema = typeless_; context.arrayElementIndex++; } return true; } RAPIDJSON_FORCEINLINE bool EndValue(Context& context) const { if (context.patternPropertiesValidatorCount > 0) { bool otherValid = false; SizeType count = context.patternPropertiesValidatorCount; if (context.objectPatternValidatorType != Context::kPatternValidatorOnly) otherValid = context.patternPropertiesValidators[--count]->IsValid(); bool patternValid = true; for (SizeType i = 0; i < count; i++) if (!context.patternPropertiesValidators[i]->IsValid()) { patternValid = false; break; } if (context.objectPatternValidatorType == Context::kPatternValidatorOnly) { if (!patternValid) { context.error_handler.PropertyViolations(context.patternPropertiesValidators, count); RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString()); } } else if (context.objectPatternValidatorType == Context::kPatternValidatorWithProperty) { if (!patternValid || !otherValid) { context.error_handler.PropertyViolations(context.patternPropertiesValidators, count + 1); RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString()); } } else if (!patternValid && !otherValid) { // kPatternValidatorWithAdditionalProperty) context.error_handler.PropertyViolations(context.patternPropertiesValidators, count + 1); RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString()); } } if (enum_) { const uint64_t h = context.factory.GetHashCode(context.hasher); for (SizeType i = 0; i < enumCount_; i++) if (enum_[i] == h) goto foundEnum; context.error_handler.DisallowedValue(); RAPIDJSON_INVALID_KEYWORD_RETURN(GetEnumString()); foundEnum:; } if (allOf_.schemas) for (SizeType i = allOf_.begin; i < allOf_.begin + allOf_.count; i++) if (!context.validators[i]->IsValid()) { context.error_handler.NotAllOf(&context.validators[allOf_.begin], allOf_.count); RAPIDJSON_INVALID_KEYWORD_RETURN(GetAllOfString()); } if (anyOf_.schemas) { for (SizeType i = anyOf_.begin; i < anyOf_.begin + anyOf_.count; i++) if (context.validators[i]->IsValid()) goto foundAny; context.error_handler.NoneOf(&context.validators[anyOf_.begin], anyOf_.count); RAPIDJSON_INVALID_KEYWORD_RETURN(GetAnyOfString()); foundAny:; } if (oneOf_.schemas) { bool oneValid = false; for (SizeType i = oneOf_.begin; i < oneOf_.begin + oneOf_.count; i++) if (context.validators[i]->IsValid()) { if (oneValid) { context.error_handler.NotOneOf(&context.validators[oneOf_.begin], oneOf_.count); RAPIDJSON_INVALID_KEYWORD_RETURN(GetOneOfString()); } else oneValid = true; } if (!oneValid) { context.error_handler.NotOneOf(&context.validators[oneOf_.begin], oneOf_.count); RAPIDJSON_INVALID_KEYWORD_RETURN(GetOneOfString()); } } if (not_ && context.validators[notValidatorIndex_]->IsValid()) { context.error_handler.Disallowed(); RAPIDJSON_INVALID_KEYWORD_RETURN(GetNotString()); } return true; } bool Null(Context& context) const { if (!(type_ & (1 << kNullSchemaType))) { DisallowedType(context, GetNullString()); RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); } return CreateParallelValidator(context); } bool Bool(Context& context, bool) const { if (!(type_ & (1 << kBooleanSchemaType))) { DisallowedType(context, GetBooleanString()); RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); } return CreateParallelValidator(context); } bool Int(Context& context, int i) const { if (!CheckInt(context, i)) return false; return CreateParallelValidator(context); } bool Uint(Context& context, unsigned u) const { if (!CheckUint(context, u)) return false; return CreateParallelValidator(context); } bool Int64(Context& context, int64_t i) const { if (!CheckInt(context, i)) return false; return CreateParallelValidator(context); } bool Uint64(Context& context, uint64_t u) const { if (!CheckUint(context, u)) return false; return CreateParallelValidator(context); } bool Double(Context& context, double d) const { if (!(type_ & (1 << kNumberSchemaType))) { DisallowedType(context, GetNumberString()); RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); } if (!minimum_.IsNull() && !CheckDoubleMinimum(context, d)) return false; if (!maximum_.IsNull() && !CheckDoubleMaximum(context, d)) return false; if (!multipleOf_.IsNull() && !CheckDoubleMultipleOf(context, d)) return false; return CreateParallelValidator(context); } bool String(Context& context, const Ch* str, SizeType length, bool) const { if (!(type_ & (1 << kStringSchemaType))) { DisallowedType(context, GetStringString()); RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); } if (minLength_ != 0 || maxLength_ != SizeType(~0)) { SizeType count; if (internal::CountStringCodePoint(str, length, &count)) { if (count < minLength_) { context.error_handler.TooShort(str, length, minLength_); RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinLengthString()); } if (count > maxLength_) { context.error_handler.TooLong(str, length, maxLength_); RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxLengthString()); } } } if (pattern_ && !IsPatternMatch(pattern_, str, length)) { context.error_handler.DoesNotMatch(str, length); RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternString()); } return CreateParallelValidator(context); } bool StartObject(Context& context) const { if (!(type_ & (1 << kObjectSchemaType))) { DisallowedType(context, GetObjectString()); RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); } if (hasDependencies_ || hasRequired_) { context.propertyExist = static_cast(context.factory.MallocState(sizeof(bool) * propertyCount_)); std::memset(context.propertyExist, 0, sizeof(bool) * propertyCount_); } if (patternProperties_) { // pre-allocate schema array SizeType count = patternPropertyCount_ + 1; // extra for valuePatternValidatorType context.patternPropertiesSchemas = static_cast(context.factory.MallocState(sizeof(const SchemaType*) * count)); context.patternPropertiesSchemaCount = 0; std::memset(context.patternPropertiesSchemas, 0, sizeof(SchemaType*) * count); } return CreateParallelValidator(context); } bool Key(Context& context, const Ch* str, SizeType len, bool) const { if (patternProperties_) { context.patternPropertiesSchemaCount = 0; for (SizeType i = 0; i < patternPropertyCount_; i++) if (patternProperties_[i].pattern && IsPatternMatch(patternProperties_[i].pattern, str, len)) { context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = patternProperties_[i].schema; context.valueSchema = typeless_; } } SizeType index; if (FindPropertyIndex(ValueType(str, len).Move(), &index)) { if (context.patternPropertiesSchemaCount > 0) { context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = properties_[index].schema; context.valueSchema = typeless_; context.valuePatternValidatorType = Context::kPatternValidatorWithProperty; } else context.valueSchema = properties_[index].schema; if (context.propertyExist) context.propertyExist[index] = true; return true; } if (additionalPropertiesSchema_) { if (additionalPropertiesSchema_ && context.patternPropertiesSchemaCount > 0) { context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = additionalPropertiesSchema_; context.valueSchema = typeless_; context.valuePatternValidatorType = Context::kPatternValidatorWithAdditionalProperty; } else context.valueSchema = additionalPropertiesSchema_; return true; } else if (additionalProperties_) { context.valueSchema = typeless_; return true; } if (context.patternPropertiesSchemaCount == 0) { // patternProperties are not additional properties context.error_handler.DisallowedProperty(str, len); RAPIDJSON_INVALID_KEYWORD_RETURN(GetAdditionalPropertiesString()); } return true; } bool EndObject(Context& context, SizeType memberCount) const { if (hasRequired_) { context.error_handler.StartMissingProperties(); for (SizeType index = 0; index < propertyCount_; index++) if (properties_[index].required && !context.propertyExist[index]) if (properties_[index].schema->defaultValueLength_ == 0 ) context.error_handler.AddMissingProperty(properties_[index].name); if (context.error_handler.EndMissingProperties()) RAPIDJSON_INVALID_KEYWORD_RETURN(GetRequiredString()); } if (memberCount < minProperties_) { context.error_handler.TooFewProperties(memberCount, minProperties_); RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinPropertiesString()); } if (memberCount > maxProperties_) { context.error_handler.TooManyProperties(memberCount, maxProperties_); RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxPropertiesString()); } if (hasDependencies_) { context.error_handler.StartDependencyErrors(); for (SizeType sourceIndex = 0; sourceIndex < propertyCount_; sourceIndex++) { const Property& source = properties_[sourceIndex]; if (context.propertyExist[sourceIndex]) { if (source.dependencies) { context.error_handler.StartMissingDependentProperties(); for (SizeType targetIndex = 0; targetIndex < propertyCount_; targetIndex++) if (source.dependencies[targetIndex] && !context.propertyExist[targetIndex]) context.error_handler.AddMissingDependentProperty(properties_[targetIndex].name); context.error_handler.EndMissingDependentProperties(source.name); } else if (source.dependenciesSchema) { ISchemaValidator* dependenciesValidator = context.validators[source.dependenciesValidatorIndex]; if (!dependenciesValidator->IsValid()) context.error_handler.AddDependencySchemaError(source.name, dependenciesValidator); } } } if (context.error_handler.EndDependencyErrors()) RAPIDJSON_INVALID_KEYWORD_RETURN(GetDependenciesString()); } return true; } bool StartArray(Context& context) const { if (!(type_ & (1 << kArraySchemaType))) { DisallowedType(context, GetArrayString()); RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); } context.arrayElementIndex = 0; context.inArray = true; return CreateParallelValidator(context); } bool EndArray(Context& context, SizeType elementCount) const { context.inArray = false; if (elementCount < minItems_) { context.error_handler.TooFewItems(elementCount, minItems_); RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinItemsString()); } if (elementCount > maxItems_) { context.error_handler.TooManyItems(elementCount, maxItems_); RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxItemsString()); } return true; } // Generate functions for string literal according to Ch #define RAPIDJSON_STRING_(name, ...) \ static const ValueType& Get##name##String() {\ static const Ch s[] = { __VA_ARGS__, '\0' };\ static const ValueType v(s, static_cast(sizeof(s) / sizeof(Ch) - 1));\ return v;\ } RAPIDJSON_STRING_(Null, 'n', 'u', 'l', 'l') RAPIDJSON_STRING_(Boolean, 'b', 'o', 'o', 'l', 'e', 'a', 'n') RAPIDJSON_STRING_(Object, 'o', 'b', 'j', 'e', 'c', 't') RAPIDJSON_STRING_(Array, 'a', 'r', 'r', 'a', 'y') RAPIDJSON_STRING_(String, 's', 't', 'r', 'i', 'n', 'g') RAPIDJSON_STRING_(Number, 'n', 'u', 'm', 'b', 'e', 'r') RAPIDJSON_STRING_(Integer, 'i', 'n', 't', 'e', 'g', 'e', 'r') RAPIDJSON_STRING_(Type, 't', 'y', 'p', 'e') RAPIDJSON_STRING_(Enum, 'e', 'n', 'u', 'm') RAPIDJSON_STRING_(AllOf, 'a', 'l', 'l', 'O', 'f') RAPIDJSON_STRING_(AnyOf, 'a', 'n', 'y', 'O', 'f') RAPIDJSON_STRING_(OneOf, 'o', 'n', 'e', 'O', 'f') RAPIDJSON_STRING_(Not, 'n', 'o', 't') RAPIDJSON_STRING_(Properties, 'p', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') RAPIDJSON_STRING_(Required, 'r', 'e', 'q', 'u', 'i', 'r', 'e', 'd') RAPIDJSON_STRING_(Dependencies, 'd', 'e', 'p', 'e', 'n', 'd', 'e', 'n', 'c', 'i', 'e', 's') RAPIDJSON_STRING_(PatternProperties, 'p', 'a', 't', 't', 'e', 'r', 'n', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') RAPIDJSON_STRING_(AdditionalProperties, 'a', 'd', 'd', 'i', 't', 'i', 'o', 'n', 'a', 'l', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') RAPIDJSON_STRING_(MinProperties, 'm', 'i', 'n', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') RAPIDJSON_STRING_(MaxProperties, 'm', 'a', 'x', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') RAPIDJSON_STRING_(Items, 'i', 't', 'e', 'm', 's') RAPIDJSON_STRING_(MinItems, 'm', 'i', 'n', 'I', 't', 'e', 'm', 's') RAPIDJSON_STRING_(MaxItems, 'm', 'a', 'x', 'I', 't', 'e', 'm', 's') RAPIDJSON_STRING_(AdditionalItems, 'a', 'd', 'd', 'i', 't', 'i', 'o', 'n', 'a', 'l', 'I', 't', 'e', 'm', 's') RAPIDJSON_STRING_(UniqueItems, 'u', 'n', 'i', 'q', 'u', 'e', 'I', 't', 'e', 'm', 's') RAPIDJSON_STRING_(MinLength, 'm', 'i', 'n', 'L', 'e', 'n', 'g', 't', 'h') RAPIDJSON_STRING_(MaxLength, 'm', 'a', 'x', 'L', 'e', 'n', 'g', 't', 'h') RAPIDJSON_STRING_(Pattern, 'p', 'a', 't', 't', 'e', 'r', 'n') RAPIDJSON_STRING_(Minimum, 'm', 'i', 'n', 'i', 'm', 'u', 'm') RAPIDJSON_STRING_(Maximum, 'm', 'a', 'x', 'i', 'm', 'u', 'm') RAPIDJSON_STRING_(ExclusiveMinimum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'i', 'n', 'i', 'm', 'u', 'm') RAPIDJSON_STRING_(ExclusiveMaximum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'a', 'x', 'i', 'm', 'u', 'm') RAPIDJSON_STRING_(MultipleOf, 'm', 'u', 'l', 't', 'i', 'p', 'l', 'e', 'O', 'f') RAPIDJSON_STRING_(DefaultValue, 'd', 'e', 'f', 'a', 'u', 'l', 't') #undef RAPIDJSON_STRING_ private: enum SchemaValueType { kNullSchemaType, kBooleanSchemaType, kObjectSchemaType, kArraySchemaType, kStringSchemaType, kNumberSchemaType, kIntegerSchemaType, kTotalSchemaType }; #if RAPIDJSON_SCHEMA_USE_INTERNALREGEX typedef internal::GenericRegex RegexType; #elif RAPIDJSON_SCHEMA_USE_STDREGEX typedef std::basic_regex RegexType; #else typedef char RegexType; #endif struct SchemaArray { SchemaArray() : schemas(), count() {} ~SchemaArray() { AllocatorType::Free(schemas); } const SchemaType** schemas; SizeType begin; // begin index of context.validators SizeType count; }; template void AddUniqueElement(V1& a, const V2& v) { for (typename V1::ConstValueIterator itr = a.Begin(); itr != a.End(); ++itr) if (*itr == v) return; V1 c(v, *allocator_); a.PushBack(c, *allocator_); } static const ValueType* GetMember(const ValueType& value, const ValueType& name) { typename ValueType::ConstMemberIterator itr = value.FindMember(name); return itr != value.MemberEnd() ? &(itr->value) : 0; } static void AssignIfExist(bool& out, const ValueType& value, const ValueType& name) { if (const ValueType* v = GetMember(value, name)) if (v->IsBool()) out = v->GetBool(); } static void AssignIfExist(SizeType& out, const ValueType& value, const ValueType& name) { if (const ValueType* v = GetMember(value, name)) if (v->IsUint64() && v->GetUint64() <= SizeType(~0)) out = static_cast(v->GetUint64()); } void AssignIfExist(SchemaArray& out, SchemaDocumentType& schemaDocument, const PointerType& p, const ValueType& value, const ValueType& name, const ValueType& document) { if (const ValueType* v = GetMember(value, name)) { if (v->IsArray() && v->Size() > 0) { PointerType q = p.Append(name, allocator_); out.count = v->Size(); out.schemas = static_cast(allocator_->Malloc(out.count * sizeof(const Schema*))); memset(out.schemas, 0, sizeof(Schema*)* out.count); for (SizeType i = 0; i < out.count; i++) schemaDocument.CreateSchema(&out.schemas[i], q.Append(i, allocator_), (*v)[i], document); out.begin = validatorCount_; validatorCount_ += out.count; } } } #if RAPIDJSON_SCHEMA_USE_INTERNALREGEX template RegexType* CreatePattern(const ValueType& value) { if (value.IsString()) { RegexType* r = new (allocator_->Malloc(sizeof(RegexType))) RegexType(value.GetString(), allocator_); if (!r->IsValid()) { r->~RegexType(); AllocatorType::Free(r); r = 0; } return r; } return 0; } static bool IsPatternMatch(const RegexType* pattern, const Ch *str, SizeType) { GenericRegexSearch rs(*pattern); return rs.Search(str); } #elif RAPIDJSON_SCHEMA_USE_STDREGEX template RegexType* CreatePattern(const ValueType& value) { if (value.IsString()) { RegexType *r = static_cast(allocator_->Malloc(sizeof(RegexType))); try { return new (r) RegexType(value.GetString(), std::size_t(value.GetStringLength()), std::regex_constants::ECMAScript); } catch (const std::regex_error&) { AllocatorType::Free(r); } } return 0; } static bool IsPatternMatch(const RegexType* pattern, const Ch *str, SizeType length) { std::match_results r; return std::regex_search(str, str + length, r, *pattern); } #else template RegexType* CreatePattern(const ValueType&) { return 0; } static bool IsPatternMatch(const RegexType*, const Ch *, SizeType) { return true; } #endif // RAPIDJSON_SCHEMA_USE_STDREGEX void AddType(const ValueType& type) { if (type == GetNullString() ) type_ |= 1 << kNullSchemaType; else if (type == GetBooleanString()) type_ |= 1 << kBooleanSchemaType; else if (type == GetObjectString() ) type_ |= 1 << kObjectSchemaType; else if (type == GetArrayString() ) type_ |= 1 << kArraySchemaType; else if (type == GetStringString() ) type_ |= 1 << kStringSchemaType; else if (type == GetIntegerString()) type_ |= 1 << kIntegerSchemaType; else if (type == GetNumberString() ) type_ |= (1 << kNumberSchemaType) | (1 << kIntegerSchemaType); } bool CreateParallelValidator(Context& context) const { if (enum_ || context.arrayUniqueness) context.hasher = context.factory.CreateHasher(); if (validatorCount_) { RAPIDJSON_ASSERT(context.validators == 0); context.validators = static_cast(context.factory.MallocState(sizeof(ISchemaValidator*) * validatorCount_)); context.validatorCount = validatorCount_; if (allOf_.schemas) CreateSchemaValidators(context, allOf_); if (anyOf_.schemas) CreateSchemaValidators(context, anyOf_); if (oneOf_.schemas) CreateSchemaValidators(context, oneOf_); if (not_) context.validators[notValidatorIndex_] = context.factory.CreateSchemaValidator(*not_); if (hasSchemaDependencies_) { for (SizeType i = 0; i < propertyCount_; i++) if (properties_[i].dependenciesSchema) context.validators[properties_[i].dependenciesValidatorIndex] = context.factory.CreateSchemaValidator(*properties_[i].dependenciesSchema); } } return true; } void CreateSchemaValidators(Context& context, const SchemaArray& schemas) const { for (SizeType i = 0; i < schemas.count; i++) context.validators[schemas.begin + i] = context.factory.CreateSchemaValidator(*schemas.schemas[i]); } // O(n) bool FindPropertyIndex(const ValueType& name, SizeType* outIndex) const { SizeType len = name.GetStringLength(); const Ch* str = name.GetString(); for (SizeType index = 0; index < propertyCount_; index++) if (properties_[index].name.GetStringLength() == len && (std::memcmp(properties_[index].name.GetString(), str, sizeof(Ch) * len) == 0)) { *outIndex = index; return true; } return false; } bool CheckInt(Context& context, int64_t i) const { if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) { DisallowedType(context, GetIntegerString()); RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); } if (!minimum_.IsNull()) { if (minimum_.IsInt64()) { if (exclusiveMinimum_ ? i <= minimum_.GetInt64() : i < minimum_.GetInt64()) { context.error_handler.BelowMinimum(i, minimum_, exclusiveMinimum_); RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); } } else if (minimum_.IsUint64()) { context.error_handler.BelowMinimum(i, minimum_, exclusiveMinimum_); RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); // i <= max(int64_t) < minimum.GetUint64() } else if (!CheckDoubleMinimum(context, static_cast(i))) return false; } if (!maximum_.IsNull()) { if (maximum_.IsInt64()) { if (exclusiveMaximum_ ? i >= maximum_.GetInt64() : i > maximum_.GetInt64()) { context.error_handler.AboveMaximum(i, maximum_, exclusiveMaximum_); RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); } } else if (maximum_.IsUint64()) { } /* do nothing */ // i <= max(int64_t) < maximum_.GetUint64() else if (!CheckDoubleMaximum(context, static_cast(i))) return false; } if (!multipleOf_.IsNull()) { if (multipleOf_.IsUint64()) { if (static_cast(i >= 0 ? i : -i) % multipleOf_.GetUint64() != 0) { context.error_handler.NotMultipleOf(i, multipleOf_); RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString()); } } else if (!CheckDoubleMultipleOf(context, static_cast(i))) return false; } return true; } bool CheckUint(Context& context, uint64_t i) const { if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) { DisallowedType(context, GetIntegerString()); RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); } if (!minimum_.IsNull()) { if (minimum_.IsUint64()) { if (exclusiveMinimum_ ? i <= minimum_.GetUint64() : i < minimum_.GetUint64()) { context.error_handler.BelowMinimum(i, minimum_, exclusiveMinimum_); RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); } } else if (minimum_.IsInt64()) /* do nothing */; // i >= 0 > minimum.Getint64() else if (!CheckDoubleMinimum(context, static_cast(i))) return false; } if (!maximum_.IsNull()) { if (maximum_.IsUint64()) { if (exclusiveMaximum_ ? i >= maximum_.GetUint64() : i > maximum_.GetUint64()) { context.error_handler.AboveMaximum(i, maximum_, exclusiveMaximum_); RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); } } else if (maximum_.IsInt64()) { context.error_handler.AboveMaximum(i, maximum_, exclusiveMaximum_); RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); // i >= 0 > maximum_ } else if (!CheckDoubleMaximum(context, static_cast(i))) return false; } if (!multipleOf_.IsNull()) { if (multipleOf_.IsUint64()) { if (i % multipleOf_.GetUint64() != 0) { context.error_handler.NotMultipleOf(i, multipleOf_); RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString()); } } else if (!CheckDoubleMultipleOf(context, static_cast(i))) return false; } return true; } bool CheckDoubleMinimum(Context& context, double d) const { if (exclusiveMinimum_ ? d <= minimum_.GetDouble() : d < minimum_.GetDouble()) { context.error_handler.BelowMinimum(d, minimum_, exclusiveMinimum_); RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); } return true; } bool CheckDoubleMaximum(Context& context, double d) const { if (exclusiveMaximum_ ? d >= maximum_.GetDouble() : d > maximum_.GetDouble()) { context.error_handler.AboveMaximum(d, maximum_, exclusiveMaximum_); RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); } return true; } bool CheckDoubleMultipleOf(Context& context, double d) const { double a = std::abs(d), b = std::abs(multipleOf_.GetDouble()); double q = std::floor(a / b); double r = a - q * b; if (r > 0.0) { context.error_handler.NotMultipleOf(d, multipleOf_); RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString()); } return true; } void DisallowedType(Context& context, const ValueType& actualType) const { ErrorHandler& eh = context.error_handler; eh.StartDisallowedType(); if (type_ & (1 << kNullSchemaType)) eh.AddExpectedType(GetNullString()); if (type_ & (1 << kBooleanSchemaType)) eh.AddExpectedType(GetBooleanString()); if (type_ & (1 << kObjectSchemaType)) eh.AddExpectedType(GetObjectString()); if (type_ & (1 << kArraySchemaType)) eh.AddExpectedType(GetArrayString()); if (type_ & (1 << kStringSchemaType)) eh.AddExpectedType(GetStringString()); if (type_ & (1 << kNumberSchemaType)) eh.AddExpectedType(GetNumberString()); else if (type_ & (1 << kIntegerSchemaType)) eh.AddExpectedType(GetIntegerString()); eh.EndDisallowedType(actualType); } struct Property { Property() : schema(), dependenciesSchema(), dependenciesValidatorIndex(), dependencies(), required(false) {} ~Property() { AllocatorType::Free(dependencies); } SValue name; const SchemaType* schema; const SchemaType* dependenciesSchema; SizeType dependenciesValidatorIndex; bool* dependencies; bool required; }; struct PatternProperty { PatternProperty() : schema(), pattern() {} ~PatternProperty() { if (pattern) { pattern->~RegexType(); AllocatorType::Free(pattern); } } const SchemaType* schema; RegexType* pattern; }; AllocatorType* allocator_; SValue uri_; PointerType pointer_; const SchemaType* typeless_; uint64_t* enum_; SizeType enumCount_; SchemaArray allOf_; SchemaArray anyOf_; SchemaArray oneOf_; const SchemaType* not_; unsigned type_; // bitmask of kSchemaType SizeType validatorCount_; SizeType notValidatorIndex_; Property* properties_; const SchemaType* additionalPropertiesSchema_; PatternProperty* patternProperties_; SizeType patternPropertyCount_; SizeType propertyCount_; SizeType minProperties_; SizeType maxProperties_; bool additionalProperties_; bool hasDependencies_; bool hasRequired_; bool hasSchemaDependencies_; const SchemaType* additionalItemsSchema_; const SchemaType* itemsList_; const SchemaType** itemsTuple_; SizeType itemsTupleCount_; SizeType minItems_; SizeType maxItems_; bool additionalItems_; bool uniqueItems_; RegexType* pattern_; SizeType minLength_; SizeType maxLength_; SValue minimum_; SValue maximum_; SValue multipleOf_; bool exclusiveMinimum_; bool exclusiveMaximum_; SizeType defaultValueLength_; }; template struct TokenHelper { RAPIDJSON_FORCEINLINE static void AppendIndexToken(Stack& documentStack, SizeType index) { *documentStack.template Push() = '/'; char buffer[21]; size_t length = static_cast((sizeof(SizeType) == 4 ? u32toa(index, buffer) : u64toa(index, buffer)) - buffer); for (size_t i = 0; i < length; i++) *documentStack.template Push() = static_cast(buffer[i]); } }; // Partial specialized version for char to prevent buffer copying. template struct TokenHelper { RAPIDJSON_FORCEINLINE static void AppendIndexToken(Stack& documentStack, SizeType index) { if (sizeof(SizeType) == 4) { char *buffer = documentStack.template Push(1 + 10); // '/' + uint *buffer++ = '/'; const char* end = internal::u32toa(index, buffer); documentStack.template Pop(static_cast(10 - (end - buffer))); } else { char *buffer = documentStack.template Push(1 + 20); // '/' + uint64 *buffer++ = '/'; const char* end = internal::u64toa(index, buffer); documentStack.template Pop(static_cast(20 - (end - buffer))); } } }; } // namespace internal /////////////////////////////////////////////////////////////////////////////// // IGenericRemoteSchemaDocumentProvider template class IGenericRemoteSchemaDocumentProvider { public: typedef typename SchemaDocumentType::Ch Ch; virtual ~IGenericRemoteSchemaDocumentProvider() {} virtual const SchemaDocumentType* GetRemoteDocument(const Ch* uri, SizeType length) = 0; }; /////////////////////////////////////////////////////////////////////////////// // GenericSchemaDocument //! JSON schema document. /*! A JSON schema document is a compiled version of a JSON schema. It is basically a tree of internal::Schema. \note This is an immutable class (i.e. its instance cannot be modified after construction). \tparam ValueT Type of JSON value (e.g. \c Value ), which also determine the encoding. \tparam Allocator Allocator type for allocating memory of this document. */ template class GenericSchemaDocument { public: typedef ValueT ValueType; typedef IGenericRemoteSchemaDocumentProvider IRemoteSchemaDocumentProviderType; typedef Allocator AllocatorType; typedef typename ValueType::EncodingType EncodingType; typedef typename EncodingType::Ch Ch; typedef internal::Schema SchemaType; typedef GenericPointer PointerType; typedef GenericValue URIType; friend class internal::Schema; template friend class GenericSchemaValidator; //! Constructor. /*! Compile a JSON document into schema document. \param document A JSON document as source. \param uri The base URI of this schema document for purposes of violation reporting. \param uriLength Length of \c name, in code points. \param remoteProvider An optional remote schema document provider for resolving remote reference. Can be null. \param allocator An optional allocator instance for allocating memory. Can be null. */ explicit GenericSchemaDocument(const ValueType& document, const Ch* uri = 0, SizeType uriLength = 0, IRemoteSchemaDocumentProviderType* remoteProvider = 0, Allocator* allocator = 0) : remoteProvider_(remoteProvider), allocator_(allocator), ownAllocator_(), root_(), typeless_(), schemaMap_(allocator, kInitialSchemaMapSize), schemaRef_(allocator, kInitialSchemaRefSize) { if (!allocator_) ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); Ch noUri[1] = {0}; uri_.SetString(uri ? uri : noUri, uriLength, *allocator_); typeless_ = static_cast(allocator_->Malloc(sizeof(SchemaType))); new (typeless_) SchemaType(this, PointerType(), ValueType(kObjectType).Move(), ValueType(kObjectType).Move(), allocator_); // Generate root schema, it will call CreateSchema() to create sub-schemas, // And call AddRefSchema() if there are $ref. CreateSchemaRecursive(&root_, PointerType(), document, document); // Resolve $ref while (!schemaRef_.Empty()) { SchemaRefEntry* refEntry = schemaRef_.template Pop(1); if (const SchemaType* s = GetSchema(refEntry->target)) { if (refEntry->schema) *refEntry->schema = s; // Create entry in map if not exist if (!GetSchema(refEntry->source)) { new (schemaMap_.template Push()) SchemaEntry(refEntry->source, const_cast(s), false, allocator_); } } else if (refEntry->schema) *refEntry->schema = typeless_; refEntry->~SchemaRefEntry(); } RAPIDJSON_ASSERT(root_ != 0); schemaRef_.ShrinkToFit(); // Deallocate all memory for ref } #if RAPIDJSON_HAS_CXX11_RVALUE_REFS //! Move constructor in C++11 GenericSchemaDocument(GenericSchemaDocument&& rhs) RAPIDJSON_NOEXCEPT : remoteProvider_(rhs.remoteProvider_), allocator_(rhs.allocator_), ownAllocator_(rhs.ownAllocator_), root_(rhs.root_), typeless_(rhs.typeless_), schemaMap_(std::move(rhs.schemaMap_)), schemaRef_(std::move(rhs.schemaRef_)), uri_(std::move(rhs.uri_)) { rhs.remoteProvider_ = 0; rhs.allocator_ = 0; rhs.ownAllocator_ = 0; rhs.typeless_ = 0; } #endif //! Destructor ~GenericSchemaDocument() { while (!schemaMap_.Empty()) schemaMap_.template Pop(1)->~SchemaEntry(); if (typeless_) { typeless_->~SchemaType(); Allocator::Free(typeless_); } RAPIDJSON_DELETE(ownAllocator_); } const URIType& GetURI() const { return uri_; } //! Get the root schema. const SchemaType& GetRoot() const { return *root_; } private: //! Prohibit copying GenericSchemaDocument(const GenericSchemaDocument&); //! Prohibit assignment GenericSchemaDocument& operator=(const GenericSchemaDocument&); struct SchemaRefEntry { SchemaRefEntry(const PointerType& s, const PointerType& t, const SchemaType** outSchema, Allocator *allocator) : source(s, allocator), target(t, allocator), schema(outSchema) {} PointerType source; PointerType target; const SchemaType** schema; }; struct SchemaEntry { SchemaEntry(const PointerType& p, SchemaType* s, bool o, Allocator* allocator) : pointer(p, allocator), schema(s), owned(o) {} ~SchemaEntry() { if (owned) { schema->~SchemaType(); Allocator::Free(schema); } } PointerType pointer; SchemaType* schema; bool owned; }; void CreateSchemaRecursive(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document) { if (schema) *schema = typeless_; if (v.GetType() == kObjectType) { const SchemaType* s = GetSchema(pointer); if (!s) CreateSchema(schema, pointer, v, document); for (typename ValueType::ConstMemberIterator itr = v.MemberBegin(); itr != v.MemberEnd(); ++itr) CreateSchemaRecursive(0, pointer.Append(itr->name, allocator_), itr->value, document); } else if (v.GetType() == kArrayType) for (SizeType i = 0; i < v.Size(); i++) CreateSchemaRecursive(0, pointer.Append(i, allocator_), v[i], document); } void CreateSchema(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document) { RAPIDJSON_ASSERT(pointer.IsValid()); if (v.IsObject()) { if (!HandleRefSchema(pointer, schema, v, document)) { SchemaType* s = new (allocator_->Malloc(sizeof(SchemaType))) SchemaType(this, pointer, v, document, allocator_); new (schemaMap_.template Push()) SchemaEntry(pointer, s, true, allocator_); if (schema) *schema = s; } } } bool HandleRefSchema(const PointerType& source, const SchemaType** schema, const ValueType& v, const ValueType& document) { static const Ch kRefString[] = { '$', 'r', 'e', 'f', '\0' }; static const ValueType kRefValue(kRefString, 4); typename ValueType::ConstMemberIterator itr = v.FindMember(kRefValue); if (itr == v.MemberEnd()) return false; if (itr->value.IsString()) { SizeType len = itr->value.GetStringLength(); if (len > 0) { const Ch* s = itr->value.GetString(); SizeType i = 0; while (i < len && s[i] != '#') // Find the first # i++; if (i > 0) { // Remote reference, resolve immediately if (remoteProvider_) { if (const GenericSchemaDocument* remoteDocument = remoteProvider_->GetRemoteDocument(s, i)) { PointerType pointer(&s[i], len - i, allocator_); if (pointer.IsValid()) { if (const SchemaType* sc = remoteDocument->GetSchema(pointer)) { if (schema) *schema = sc; new (schemaMap_.template Push()) SchemaEntry(source, const_cast(sc), false, allocator_); return true; } } } } } else if (s[i] == '#') { // Local reference, defer resolution PointerType pointer(&s[i], len - i, allocator_); if (pointer.IsValid()) { if (const ValueType* nv = pointer.Get(document)) if (HandleRefSchema(source, schema, *nv, document)) return true; new (schemaRef_.template Push()) SchemaRefEntry(source, pointer, schema, allocator_); return true; } } } } return false; } const SchemaType* GetSchema(const PointerType& pointer) const { for (const SchemaEntry* target = schemaMap_.template Bottom(); target != schemaMap_.template End(); ++target) if (pointer == target->pointer) return target->schema; return 0; } PointerType GetPointer(const SchemaType* schema) const { for (const SchemaEntry* target = schemaMap_.template Bottom(); target != schemaMap_.template End(); ++target) if (schema == target->schema) return target->pointer; return PointerType(); } const SchemaType* GetTypeless() const { return typeless_; } static const size_t kInitialSchemaMapSize = 64; static const size_t kInitialSchemaRefSize = 64; IRemoteSchemaDocumentProviderType* remoteProvider_; Allocator *allocator_; Allocator *ownAllocator_; const SchemaType* root_; //!< Root schema. SchemaType* typeless_; internal::Stack schemaMap_; // Stores created Pointer -> Schemas internal::Stack schemaRef_; // Stores Pointer from $ref and schema which holds the $ref URIType uri_; }; //! GenericSchemaDocument using Value type. typedef GenericSchemaDocument SchemaDocument; //! IGenericRemoteSchemaDocumentProvider using SchemaDocument. typedef IGenericRemoteSchemaDocumentProvider IRemoteSchemaDocumentProvider; /////////////////////////////////////////////////////////////////////////////// // GenericSchemaValidator //! JSON Schema Validator. /*! A SAX style JSON schema validator. It uses a \c GenericSchemaDocument to validate SAX events. It delegates the incoming SAX events to an output handler. The default output handler does nothing. It can be reused multiple times by calling \c Reset(). \tparam SchemaDocumentType Type of schema document. \tparam OutputHandler Type of output handler. Default handler does nothing. \tparam StateAllocator Allocator for storing the internal validation states. */ template < typename SchemaDocumentType, typename OutputHandler = BaseReaderHandler, typename StateAllocator = CrtAllocator> class GenericSchemaValidator : public internal::ISchemaStateFactory, public internal::ISchemaValidator, public internal::IValidationErrorHandler { public: typedef typename SchemaDocumentType::SchemaType SchemaType; typedef typename SchemaDocumentType::PointerType PointerType; typedef typename SchemaType::EncodingType EncodingType; typedef typename SchemaType::SValue SValue; typedef typename EncodingType::Ch Ch; typedef GenericStringRef StringRefType; typedef GenericValue ValueType; //! Constructor without output handler. /*! \param schemaDocument The schema document to conform to. \param allocator Optional allocator for storing internal validation states. \param schemaStackCapacity Optional initial capacity of schema path stack. \param documentStackCapacity Optional initial capacity of document path stack. */ GenericSchemaValidator( const SchemaDocumentType& schemaDocument, StateAllocator* allocator = 0, size_t schemaStackCapacity = kDefaultSchemaStackCapacity, size_t documentStackCapacity = kDefaultDocumentStackCapacity) : schemaDocument_(&schemaDocument), root_(schemaDocument.GetRoot()), stateAllocator_(allocator), ownStateAllocator_(0), schemaStack_(allocator, schemaStackCapacity), documentStack_(allocator, documentStackCapacity), outputHandler_(0), error_(kObjectType), currentError_(), missingDependents_(), valid_(true) #if RAPIDJSON_SCHEMA_VERBOSE , depth_(0) #endif { } //! Constructor with output handler. /*! \param schemaDocument The schema document to conform to. \param allocator Optional allocator for storing internal validation states. \param schemaStackCapacity Optional initial capacity of schema path stack. \param documentStackCapacity Optional initial capacity of document path stack. */ GenericSchemaValidator( const SchemaDocumentType& schemaDocument, OutputHandler& outputHandler, StateAllocator* allocator = 0, size_t schemaStackCapacity = kDefaultSchemaStackCapacity, size_t documentStackCapacity = kDefaultDocumentStackCapacity) : schemaDocument_(&schemaDocument), root_(schemaDocument.GetRoot()), stateAllocator_(allocator), ownStateAllocator_(0), schemaStack_(allocator, schemaStackCapacity), documentStack_(allocator, documentStackCapacity), outputHandler_(&outputHandler), error_(kObjectType), currentError_(), missingDependents_(), valid_(true) #if RAPIDJSON_SCHEMA_VERBOSE , depth_(0) #endif { } //! Destructor. ~GenericSchemaValidator() { Reset(); RAPIDJSON_DELETE(ownStateAllocator_); } //! Reset the internal states. void Reset() { while (!schemaStack_.Empty()) PopSchema(); documentStack_.Clear(); error_.SetObject(); currentError_.SetNull(); missingDependents_.SetNull(); valid_ = true; } //! Checks whether the current state is valid. // Implementation of ISchemaValidator virtual bool IsValid() const { return valid_; } //! Gets the error object. ValueType& GetError() { return error_; } const ValueType& GetError() const { return error_; } //! Gets the JSON pointer pointed to the invalid schema. PointerType GetInvalidSchemaPointer() const { return schemaStack_.Empty() ? PointerType() : CurrentSchema().GetPointer(); } //! Gets the keyword of invalid schema. const Ch* GetInvalidSchemaKeyword() const { return schemaStack_.Empty() ? 0 : CurrentContext().invalidKeyword; } //! Gets the JSON pointer pointed to the invalid value. PointerType GetInvalidDocumentPointer() const { if (documentStack_.Empty()) { return PointerType(); } else { return PointerType(documentStack_.template Bottom(), documentStack_.GetSize() / sizeof(Ch)); } } void NotMultipleOf(int64_t actual, const SValue& expected) { AddNumberError(SchemaType::GetMultipleOfString(), ValueType(actual).Move(), expected); } void NotMultipleOf(uint64_t actual, const SValue& expected) { AddNumberError(SchemaType::GetMultipleOfString(), ValueType(actual).Move(), expected); } void NotMultipleOf(double actual, const SValue& expected) { AddNumberError(SchemaType::GetMultipleOfString(), ValueType(actual).Move(), expected); } void AboveMaximum(int64_t actual, const SValue& expected, bool exclusive) { AddNumberError(SchemaType::GetMaximumString(), ValueType(actual).Move(), expected, exclusive ? &SchemaType::GetExclusiveMaximumString : 0); } void AboveMaximum(uint64_t actual, const SValue& expected, bool exclusive) { AddNumberError(SchemaType::GetMaximumString(), ValueType(actual).Move(), expected, exclusive ? &SchemaType::GetExclusiveMaximumString : 0); } void AboveMaximum(double actual, const SValue& expected, bool exclusive) { AddNumberError(SchemaType::GetMaximumString(), ValueType(actual).Move(), expected, exclusive ? &SchemaType::GetExclusiveMaximumString : 0); } void BelowMinimum(int64_t actual, const SValue& expected, bool exclusive) { AddNumberError(SchemaType::GetMinimumString(), ValueType(actual).Move(), expected, exclusive ? &SchemaType::GetExclusiveMinimumString : 0); } void BelowMinimum(uint64_t actual, const SValue& expected, bool exclusive) { AddNumberError(SchemaType::GetMinimumString(), ValueType(actual).Move(), expected, exclusive ? &SchemaType::GetExclusiveMinimumString : 0); } void BelowMinimum(double actual, const SValue& expected, bool exclusive) { AddNumberError(SchemaType::GetMinimumString(), ValueType(actual).Move(), expected, exclusive ? &SchemaType::GetExclusiveMinimumString : 0); } void TooLong(const Ch* str, SizeType length, SizeType expected) { AddNumberError(SchemaType::GetMaxLengthString(), ValueType(str, length, GetStateAllocator()).Move(), SValue(expected).Move()); } void TooShort(const Ch* str, SizeType length, SizeType expected) { AddNumberError(SchemaType::GetMinLengthString(), ValueType(str, length, GetStateAllocator()).Move(), SValue(expected).Move()); } void DoesNotMatch(const Ch* str, SizeType length) { currentError_.SetObject(); currentError_.AddMember(GetActualString(), ValueType(str, length, GetStateAllocator()).Move(), GetStateAllocator()); AddCurrentError(SchemaType::GetPatternString()); } void DisallowedItem(SizeType index) { currentError_.SetObject(); currentError_.AddMember(GetDisallowedString(), ValueType(index).Move(), GetStateAllocator()); AddCurrentError(SchemaType::GetAdditionalItemsString(), true); } void TooFewItems(SizeType actualCount, SizeType expectedCount) { AddNumberError(SchemaType::GetMinItemsString(), ValueType(actualCount).Move(), SValue(expectedCount).Move()); } void TooManyItems(SizeType actualCount, SizeType expectedCount) { AddNumberError(SchemaType::GetMaxItemsString(), ValueType(actualCount).Move(), SValue(expectedCount).Move()); } void DuplicateItems(SizeType index1, SizeType index2) { ValueType duplicates(kArrayType); duplicates.PushBack(index1, GetStateAllocator()); duplicates.PushBack(index2, GetStateAllocator()); currentError_.SetObject(); currentError_.AddMember(GetDuplicatesString(), duplicates, GetStateAllocator()); AddCurrentError(SchemaType::GetUniqueItemsString(), true); } void TooManyProperties(SizeType actualCount, SizeType expectedCount) { AddNumberError(SchemaType::GetMaxPropertiesString(), ValueType(actualCount).Move(), SValue(expectedCount).Move()); } void TooFewProperties(SizeType actualCount, SizeType expectedCount) { AddNumberError(SchemaType::GetMinPropertiesString(), ValueType(actualCount).Move(), SValue(expectedCount).Move()); } void StartMissingProperties() { currentError_.SetArray(); } void AddMissingProperty(const SValue& name) { currentError_.PushBack(ValueType(name, GetStateAllocator()).Move(), GetStateAllocator()); } bool EndMissingProperties() { if (currentError_.Empty()) return false; ValueType error(kObjectType); error.AddMember(GetMissingString(), currentError_, GetStateAllocator()); currentError_ = error; AddCurrentError(SchemaType::GetRequiredString()); return true; } void PropertyViolations(ISchemaValidator** subvalidators, SizeType count) { for (SizeType i = 0; i < count; ++i) MergeError(static_cast(subvalidators[i])->GetError()); } void DisallowedProperty(const Ch* name, SizeType length) { currentError_.SetObject(); currentError_.AddMember(GetDisallowedString(), ValueType(name, length, GetStateAllocator()).Move(), GetStateAllocator()); AddCurrentError(SchemaType::GetAdditionalPropertiesString(), true); } void StartDependencyErrors() { currentError_.SetObject(); } void StartMissingDependentProperties() { missingDependents_.SetArray(); } void AddMissingDependentProperty(const SValue& targetName) { missingDependents_.PushBack(ValueType(targetName, GetStateAllocator()).Move(), GetStateAllocator()); } void EndMissingDependentProperties(const SValue& sourceName) { if (!missingDependents_.Empty()) currentError_.AddMember(ValueType(sourceName, GetStateAllocator()).Move(), missingDependents_, GetStateAllocator()); } void AddDependencySchemaError(const SValue& sourceName, ISchemaValidator* subvalidator) { currentError_.AddMember(ValueType(sourceName, GetStateAllocator()).Move(), static_cast(subvalidator)->GetError(), GetStateAllocator()); } bool EndDependencyErrors() { if (currentError_.ObjectEmpty()) return false; ValueType error(kObjectType); error.AddMember(GetErrorsString(), currentError_, GetStateAllocator()); currentError_ = error; AddCurrentError(SchemaType::GetDependenciesString()); return true; } void DisallowedValue() { currentError_.SetObject(); AddCurrentError(SchemaType::GetEnumString()); } void StartDisallowedType() { currentError_.SetArray(); } void AddExpectedType(const typename SchemaType::ValueType& expectedType) { currentError_.PushBack(ValueType(expectedType, GetStateAllocator()).Move(), GetStateAllocator()); } void EndDisallowedType(const typename SchemaType::ValueType& actualType) { ValueType error(kObjectType); error.AddMember(GetExpectedString(), currentError_, GetStateAllocator()); error.AddMember(GetActualString(), ValueType(actualType, GetStateAllocator()).Move(), GetStateAllocator()); currentError_ = error; AddCurrentError(SchemaType::GetTypeString()); } void NotAllOf(ISchemaValidator** subvalidators, SizeType count) { for (SizeType i = 0; i < count; ++i) { MergeError(static_cast(subvalidators[i])->GetError()); } } void NoneOf(ISchemaValidator** subvalidators, SizeType count) { AddErrorArray(SchemaType::GetAnyOfString(), subvalidators, count); } void NotOneOf(ISchemaValidator** subvalidators, SizeType count) { AddErrorArray(SchemaType::GetOneOfString(), subvalidators, count); } void Disallowed() { currentError_.SetObject(); AddCurrentError(SchemaType::GetNotString()); } #define RAPIDJSON_STRING_(name, ...) \ static const StringRefType& Get##name##String() {\ static const Ch s[] = { __VA_ARGS__, '\0' };\ static const StringRefType v(s, static_cast(sizeof(s) / sizeof(Ch) - 1)); \ return v;\ } RAPIDJSON_STRING_(InstanceRef, 'i', 'n', 's', 't', 'a', 'n', 'c', 'e', 'R', 'e', 'f') RAPIDJSON_STRING_(SchemaRef, 's', 'c', 'h', 'e', 'm', 'a', 'R', 'e', 'f') RAPIDJSON_STRING_(Expected, 'e', 'x', 'p', 'e', 'c', 't', 'e', 'd') RAPIDJSON_STRING_(Actual, 'a', 'c', 't', 'u', 'a', 'l') RAPIDJSON_STRING_(Disallowed, 'd', 'i', 's', 'a', 'l', 'l', 'o', 'w', 'e', 'd') RAPIDJSON_STRING_(Missing, 'm', 'i', 's', 's', 'i', 'n', 'g') RAPIDJSON_STRING_(Errors, 'e', 'r', 'r', 'o', 'r', 's') RAPIDJSON_STRING_(Duplicates, 'd', 'u', 'p', 'l', 'i', 'c', 'a', 't', 'e', 's') #undef RAPIDJSON_STRING_ #if RAPIDJSON_SCHEMA_VERBOSE #define RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_() \ RAPIDJSON_MULTILINEMACRO_BEGIN\ *documentStack_.template Push() = '\0';\ documentStack_.template Pop(1);\ internal::PrintInvalidDocument(documentStack_.template Bottom());\ RAPIDJSON_MULTILINEMACRO_END #else #define RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_() #endif #define RAPIDJSON_SCHEMA_HANDLE_BEGIN_(method, arg1)\ if (!valid_) return false; \ if (!BeginValue() || !CurrentSchema().method arg1) {\ RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_();\ return valid_ = false;\ } #define RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2)\ for (Context* context = schemaStack_.template Bottom(); context != schemaStack_.template End(); context++) {\ if (context->hasher)\ static_cast(context->hasher)->method arg2;\ if (context->validators)\ for (SizeType i_ = 0; i_ < context->validatorCount; i_++)\ static_cast(context->validators[i_])->method arg2;\ if (context->patternPropertiesValidators)\ for (SizeType i_ = 0; i_ < context->patternPropertiesValidatorCount; i_++)\ static_cast(context->patternPropertiesValidators[i_])->method arg2;\ } #define RAPIDJSON_SCHEMA_HANDLE_END_(method, arg2)\ return valid_ = EndValue() && (!outputHandler_ || outputHandler_->method arg2) #define RAPIDJSON_SCHEMA_HANDLE_VALUE_(method, arg1, arg2) \ RAPIDJSON_SCHEMA_HANDLE_BEGIN_ (method, arg1);\ RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2);\ RAPIDJSON_SCHEMA_HANDLE_END_ (method, arg2) bool Null() { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Null, (CurrentContext()), ( )); } bool Bool(bool b) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Bool, (CurrentContext(), b), (b)); } bool Int(int i) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int, (CurrentContext(), i), (i)); } bool Uint(unsigned u) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint, (CurrentContext(), u), (u)); } bool Int64(int64_t i) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int64, (CurrentContext(), i), (i)); } bool Uint64(uint64_t u) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint64, (CurrentContext(), u), (u)); } bool Double(double d) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Double, (CurrentContext(), d), (d)); } bool RawNumber(const Ch* str, SizeType length, bool copy) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); } bool String(const Ch* str, SizeType length, bool copy) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); } bool StartObject() { RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartObject, (CurrentContext())); RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartObject, ()); return valid_ = !outputHandler_ || outputHandler_->StartObject(); } bool Key(const Ch* str, SizeType len, bool copy) { if (!valid_) return false; AppendToken(str, len); if (!CurrentSchema().Key(CurrentContext(), str, len, copy)) return valid_ = false; RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(Key, (str, len, copy)); return valid_ = !outputHandler_ || outputHandler_->Key(str, len, copy); } bool EndObject(SizeType memberCount) { if (!valid_) return false; RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndObject, (memberCount)); if (!CurrentSchema().EndObject(CurrentContext(), memberCount)) return valid_ = false; RAPIDJSON_SCHEMA_HANDLE_END_(EndObject, (memberCount)); } bool StartArray() { RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartArray, (CurrentContext())); RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartArray, ()); return valid_ = !outputHandler_ || outputHandler_->StartArray(); } bool EndArray(SizeType elementCount) { if (!valid_) return false; RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndArray, (elementCount)); if (!CurrentSchema().EndArray(CurrentContext(), elementCount)) return valid_ = false; RAPIDJSON_SCHEMA_HANDLE_END_(EndArray, (elementCount)); } #undef RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_ #undef RAPIDJSON_SCHEMA_HANDLE_BEGIN_ #undef RAPIDJSON_SCHEMA_HANDLE_PARALLEL_ #undef RAPIDJSON_SCHEMA_HANDLE_VALUE_ // Implementation of ISchemaStateFactory virtual ISchemaValidator* CreateSchemaValidator(const SchemaType& root) { return new (GetStateAllocator().Malloc(sizeof(GenericSchemaValidator))) GenericSchemaValidator(*schemaDocument_, root, documentStack_.template Bottom(), documentStack_.GetSize(), #if RAPIDJSON_SCHEMA_VERBOSE depth_ + 1, #endif &GetStateAllocator()); } virtual void DestroySchemaValidator(ISchemaValidator* validator) { GenericSchemaValidator* v = static_cast(validator); v->~GenericSchemaValidator(); StateAllocator::Free(v); } virtual void* CreateHasher() { return new (GetStateAllocator().Malloc(sizeof(HasherType))) HasherType(&GetStateAllocator()); } virtual uint64_t GetHashCode(void* hasher) { return static_cast(hasher)->GetHashCode(); } virtual void DestroryHasher(void* hasher) { HasherType* h = static_cast(hasher); h->~HasherType(); StateAllocator::Free(h); } virtual void* MallocState(size_t size) { return GetStateAllocator().Malloc(size); } virtual void FreeState(void* p) { StateAllocator::Free(p); } private: typedef typename SchemaType::Context Context; typedef GenericValue, StateAllocator> HashCodeArray; typedef internal::Hasher HasherType; GenericSchemaValidator( const SchemaDocumentType& schemaDocument, const SchemaType& root, const char* basePath, size_t basePathSize, #if RAPIDJSON_SCHEMA_VERBOSE unsigned depth, #endif StateAllocator* allocator = 0, size_t schemaStackCapacity = kDefaultSchemaStackCapacity, size_t documentStackCapacity = kDefaultDocumentStackCapacity) : schemaDocument_(&schemaDocument), root_(root), stateAllocator_(allocator), ownStateAllocator_(0), schemaStack_(allocator, schemaStackCapacity), documentStack_(allocator, documentStackCapacity), outputHandler_(0), error_(kObjectType), currentError_(), missingDependents_(), valid_(true) #if RAPIDJSON_SCHEMA_VERBOSE , depth_(depth) #endif { if (basePath && basePathSize) memcpy(documentStack_.template Push(basePathSize), basePath, basePathSize); } StateAllocator& GetStateAllocator() { if (!stateAllocator_) stateAllocator_ = ownStateAllocator_ = RAPIDJSON_NEW(StateAllocator)(); return *stateAllocator_; } bool BeginValue() { if (schemaStack_.Empty()) PushSchema(root_); else { if (CurrentContext().inArray) internal::TokenHelper, Ch>::AppendIndexToken(documentStack_, CurrentContext().arrayElementIndex); if (!CurrentSchema().BeginValue(CurrentContext())) return false; SizeType count = CurrentContext().patternPropertiesSchemaCount; const SchemaType** sa = CurrentContext().patternPropertiesSchemas; typename Context::PatternValidatorType patternValidatorType = CurrentContext().valuePatternValidatorType; bool valueUniqueness = CurrentContext().valueUniqueness; RAPIDJSON_ASSERT(CurrentContext().valueSchema); PushSchema(*CurrentContext().valueSchema); if (count > 0) { CurrentContext().objectPatternValidatorType = patternValidatorType; ISchemaValidator**& va = CurrentContext().patternPropertiesValidators; SizeType& validatorCount = CurrentContext().patternPropertiesValidatorCount; va = static_cast(MallocState(sizeof(ISchemaValidator*) * count)); for (SizeType i = 0; i < count; i++) va[validatorCount++] = CreateSchemaValidator(*sa[i]); } CurrentContext().arrayUniqueness = valueUniqueness; } return true; } bool EndValue() { if (!CurrentSchema().EndValue(CurrentContext())) return false; #if RAPIDJSON_SCHEMA_VERBOSE GenericStringBuffer sb; schemaDocument_->GetPointer(&CurrentSchema()).Stringify(sb); *documentStack_.template Push() = '\0'; documentStack_.template Pop(1); internal::PrintValidatorPointers(depth_, sb.GetString(), documentStack_.template Bottom()); #endif uint64_t h = CurrentContext().arrayUniqueness ? static_cast(CurrentContext().hasher)->GetHashCode() : 0; PopSchema(); if (!schemaStack_.Empty()) { Context& context = CurrentContext(); if (context.valueUniqueness) { HashCodeArray* a = static_cast(context.arrayElementHashCodes); if (!a) CurrentContext().arrayElementHashCodes = a = new (GetStateAllocator().Malloc(sizeof(HashCodeArray))) HashCodeArray(kArrayType); for (typename HashCodeArray::ConstValueIterator itr = a->Begin(); itr != a->End(); ++itr) if (itr->GetUint64() == h) { DuplicateItems(static_cast(itr - a->Begin()), a->Size()); RAPIDJSON_INVALID_KEYWORD_RETURN(SchemaType::GetUniqueItemsString()); } a->PushBack(h, GetStateAllocator()); } } // Remove the last token of document pointer while (!documentStack_.Empty() && *documentStack_.template Pop(1) != '/') ; return true; } void AppendToken(const Ch* str, SizeType len) { documentStack_.template Reserve(1 + len * 2); // worst case all characters are escaped as two characters *documentStack_.template PushUnsafe() = '/'; for (SizeType i = 0; i < len; i++) { if (str[i] == '~') { *documentStack_.template PushUnsafe() = '~'; *documentStack_.template PushUnsafe() = '0'; } else if (str[i] == '/') { *documentStack_.template PushUnsafe() = '~'; *documentStack_.template PushUnsafe() = '1'; } else *documentStack_.template PushUnsafe() = str[i]; } } RAPIDJSON_FORCEINLINE void PushSchema(const SchemaType& schema) { new (schemaStack_.template Push()) Context(*this, *this, &schema); } RAPIDJSON_FORCEINLINE void PopSchema() { Context* c = schemaStack_.template Pop(1); if (HashCodeArray* a = static_cast(c->arrayElementHashCodes)) { a->~HashCodeArray(); StateAllocator::Free(a); } c->~Context(); } void AddErrorLocation(ValueType& result, bool parent) { GenericStringBuffer sb; PointerType instancePointer = GetInvalidDocumentPointer(); ((parent && instancePointer.GetTokenCount() > 0) ? PointerType(instancePointer.GetTokens(), instancePointer.GetTokenCount() - 1) : instancePointer).StringifyUriFragment(sb); ValueType instanceRef(sb.GetString(), static_cast(sb.GetSize() / sizeof(Ch)), GetStateAllocator()); result.AddMember(GetInstanceRefString(), instanceRef, GetStateAllocator()); sb.Clear(); memcpy(sb.Push(CurrentSchema().GetURI().GetStringLength()), CurrentSchema().GetURI().GetString(), CurrentSchema().GetURI().GetStringLength() * sizeof(Ch)); GetInvalidSchemaPointer().StringifyUriFragment(sb); ValueType schemaRef(sb.GetString(), static_cast(sb.GetSize() / sizeof(Ch)), GetStateAllocator()); result.AddMember(GetSchemaRefString(), schemaRef, GetStateAllocator()); } void AddError(ValueType& keyword, ValueType& error) { typename ValueType::MemberIterator member = error_.FindMember(keyword); if (member == error_.MemberEnd()) error_.AddMember(keyword, error, GetStateAllocator()); else { if (member->value.IsObject()) { ValueType errors(kArrayType); errors.PushBack(member->value, GetStateAllocator()); member->value = errors; } member->value.PushBack(error, GetStateAllocator()); } } void AddCurrentError(const typename SchemaType::ValueType& keyword, bool parent = false) { AddErrorLocation(currentError_, parent); AddError(ValueType(keyword, GetStateAllocator(), false).Move(), currentError_); } void MergeError(ValueType& other) { for (typename ValueType::MemberIterator it = other.MemberBegin(), end = other.MemberEnd(); it != end; ++it) { AddError(it->name, it->value); } } void AddNumberError(const typename SchemaType::ValueType& keyword, ValueType& actual, const SValue& expected, const typename SchemaType::ValueType& (*exclusive)() = 0) { currentError_.SetObject(); currentError_.AddMember(GetActualString(), actual, GetStateAllocator()); currentError_.AddMember(GetExpectedString(), ValueType(expected, GetStateAllocator()).Move(), GetStateAllocator()); if (exclusive) currentError_.AddMember(ValueType(exclusive(), GetStateAllocator()).Move(), true, GetStateAllocator()); AddCurrentError(keyword); } void AddErrorArray(const typename SchemaType::ValueType& keyword, ISchemaValidator** subvalidators, SizeType count) { ValueType errors(kArrayType); for (SizeType i = 0; i < count; ++i) errors.PushBack(static_cast(subvalidators[i])->GetError(), GetStateAllocator()); currentError_.SetObject(); currentError_.AddMember(GetErrorsString(), errors, GetStateAllocator()); AddCurrentError(keyword); } const SchemaType& CurrentSchema() const { return *schemaStack_.template Top()->schema; } Context& CurrentContext() { return *schemaStack_.template Top(); } const Context& CurrentContext() const { return *schemaStack_.template Top(); } static const size_t kDefaultSchemaStackCapacity = 1024; static const size_t kDefaultDocumentStackCapacity = 256; const SchemaDocumentType* schemaDocument_; const SchemaType& root_; StateAllocator* stateAllocator_; StateAllocator* ownStateAllocator_; internal::Stack schemaStack_; //!< stack to store the current path of schema (BaseSchemaType *) internal::Stack documentStack_; //!< stack to store the current path of validating document (Ch) OutputHandler* outputHandler_; ValueType error_; ValueType currentError_; ValueType missingDependents_; bool valid_; #if RAPIDJSON_SCHEMA_VERBOSE unsigned depth_; #endif }; typedef GenericSchemaValidator SchemaValidator; /////////////////////////////////////////////////////////////////////////////// // SchemaValidatingReader //! A helper class for parsing with validation. /*! This helper class is a functor, designed as a parameter of \ref GenericDocument::Populate(). \tparam parseFlags Combination of \ref ParseFlag. \tparam InputStream Type of input stream, implementing Stream concept. \tparam SourceEncoding Encoding of the input stream. \tparam SchemaDocumentType Type of schema document. \tparam StackAllocator Allocator type for stack. */ template < unsigned parseFlags, typename InputStream, typename SourceEncoding, typename SchemaDocumentType = SchemaDocument, typename StackAllocator = CrtAllocator> class SchemaValidatingReader { public: typedef typename SchemaDocumentType::PointerType PointerType; typedef typename InputStream::Ch Ch; typedef GenericValue ValueType; //! Constructor /*! \param is Input stream. \param sd Schema document. */ SchemaValidatingReader(InputStream& is, const SchemaDocumentType& sd) : is_(is), sd_(sd), invalidSchemaKeyword_(), error_(kObjectType), isValid_(true) {} template bool operator()(Handler& handler) { GenericReader reader; GenericSchemaValidator validator(sd_, handler); parseResult_ = reader.template Parse(is_, validator); isValid_ = validator.IsValid(); if (isValid_) { invalidSchemaPointer_ = PointerType(); invalidSchemaKeyword_ = 0; invalidDocumentPointer_ = PointerType(); error_.SetObject(); } else { invalidSchemaPointer_ = validator.GetInvalidSchemaPointer(); invalidSchemaKeyword_ = validator.GetInvalidSchemaKeyword(); invalidDocumentPointer_ = validator.GetInvalidDocumentPointer(); error_.CopyFrom(validator.GetError(), allocator_); } return parseResult_; } const ParseResult& GetParseResult() const { return parseResult_; } bool IsValid() const { return isValid_; } const PointerType& GetInvalidSchemaPointer() const { return invalidSchemaPointer_; } const Ch* GetInvalidSchemaKeyword() const { return invalidSchemaKeyword_; } const PointerType& GetInvalidDocumentPointer() const { return invalidDocumentPointer_; } const ValueType& GetError() const { return error_; } private: InputStream& is_; const SchemaDocumentType& sd_; ParseResult parseResult_; PointerType invalidSchemaPointer_; const Ch* invalidSchemaKeyword_; PointerType invalidDocumentPointer_; StackAllocator allocator_; ValueType error_; bool isValid_; }; RAPIDJSON_NAMESPACE_END RAPIDJSON_DIAG_POP #endif // RAPIDJSON_SCHEMA_H_ ================================================ FILE: src/json/rapidjson/stream.h ================================================ // Tencent is pleased to support the open source community by making RapidJSON available. // // Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at // // http://opensource.org/licenses/MIT // // Unless required by applicable law or agreed to in writing, software distributed // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. #include "rapidjson.h" #ifndef RAPIDJSON_STREAM_H_ #define RAPIDJSON_STREAM_H_ #include "encodings.h" RAPIDJSON_NAMESPACE_BEGIN /////////////////////////////////////////////////////////////////////////////// // Stream /*! \class rapidjson::Stream \brief Concept for reading and writing characters. For read-only stream, no need to implement PutBegin(), Put(), Flush() and PutEnd(). For write-only stream, only need to implement Put() and Flush(). \code concept Stream { typename Ch; //!< Character type of the stream. //! Read the current character from stream without moving the read cursor. Ch Peek() const; //! Read the current character from stream and moving the read cursor to next character. Ch Take(); //! Get the current read cursor. //! \return Number of characters read from start. size_t Tell(); //! Begin writing operation at the current read pointer. //! \return The begin writer pointer. Ch* PutBegin(); //! Write a character. void Put(Ch c); //! Flush the buffer. void Flush(); //! End the writing operation. //! \param begin The begin write pointer returned by PutBegin(). //! \return Number of characters written. size_t PutEnd(Ch* begin); } \endcode */ //! Provides additional information for stream. /*! By using traits pattern, this type provides a default configuration for stream. For custom stream, this type can be specialized for other configuration. See TEST(Reader, CustomStringStream) in readertest.cpp for example. */ template struct StreamTraits { //! Whether to make local copy of stream for optimization during parsing. /*! By default, for safety, streams do not use local copy optimization. Stream that can be copied fast should specialize this, like StreamTraits. */ enum { copyOptimization = 0 }; }; //! Reserve n characters for writing to a stream. template inline void PutReserve(Stream& stream, size_t count) { (void)stream; (void)count; } //! Write character to a stream, presuming buffer is reserved. template inline void PutUnsafe(Stream& stream, typename Stream::Ch c) { stream.Put(c); } //! Put N copies of a character to a stream. template inline void PutN(Stream& stream, Ch c, size_t n) { PutReserve(stream, n); for (size_t i = 0; i < n; i++) PutUnsafe(stream, c); } /////////////////////////////////////////////////////////////////////////////// // GenericStreamWrapper //! A Stream Wrapper /*! \tThis string stream is a wrapper for any stream by just forwarding any \treceived message to the origin stream. \note implements Stream concept */ #if defined(_MSC_VER) && _MSC_VER <= 1800 RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(4702) // unreachable code RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated #endif template > class GenericStreamWrapper { public: typedef typename Encoding::Ch Ch; GenericStreamWrapper(InputStream& is): is_(is) {} Ch Peek() const { return is_.Peek(); } Ch Take() { return is_.Take(); } size_t Tell() { return is_.Tell(); } Ch* PutBegin() { return is_.PutBegin(); } void Put(Ch ch) { is_.Put(ch); } void Flush() { is_.Flush(); } size_t PutEnd(Ch* ch) { return is_.PutEnd(ch); } // wrapper for MemoryStream const Ch* Peek4() const { return is_.Peek4(); } // wrapper for AutoUTFInputStream UTFType GetType() const { return is_.GetType(); } bool HasBOM() const { return is_.HasBOM(); } protected: InputStream& is_; }; #if defined(_MSC_VER) && _MSC_VER <= 1800 RAPIDJSON_DIAG_POP #endif /////////////////////////////////////////////////////////////////////////////// // StringStream //! Read-only string stream. /*! \note implements Stream concept */ template struct GenericStringStream { typedef typename Encoding::Ch Ch; GenericStringStream(const Ch *src) : src_(src), head_(src) {} Ch Peek() const { return *src_; } Ch Take() { return *src_++; } size_t Tell() const { return static_cast(src_ - head_); } Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } void Put(Ch) { RAPIDJSON_ASSERT(false); } void Flush() { RAPIDJSON_ASSERT(false); } size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } const Ch* src_; //!< Current read position. const Ch* head_; //!< Original head of the string. }; template struct StreamTraits > { enum { copyOptimization = 1 }; }; //! String stream with UTF8 encoding. typedef GenericStringStream > StringStream; /////////////////////////////////////////////////////////////////////////////// // InsituStringStream //! A read-write string stream. /*! This string stream is particularly designed for in-situ parsing. \note implements Stream concept */ template struct GenericInsituStringStream { typedef typename Encoding::Ch Ch; GenericInsituStringStream(Ch *src) : src_(src), dst_(0), head_(src) {} // Read Ch Peek() { return *src_; } Ch Take() { return *src_++; } size_t Tell() { return static_cast(src_ - head_); } // Write void Put(Ch c) { RAPIDJSON_ASSERT(dst_ != 0); *dst_++ = c; } Ch* PutBegin() { return dst_ = src_; } size_t PutEnd(Ch* begin) { return static_cast(dst_ - begin); } void Flush() {} Ch* Push(size_t count) { Ch* begin = dst_; dst_ += count; return begin; } void Pop(size_t count) { dst_ -= count; } Ch* src_; Ch* dst_; Ch* head_; }; template struct StreamTraits > { enum { copyOptimization = 1 }; }; //! Insitu string stream with UTF8 encoding. typedef GenericInsituStringStream > InsituStringStream; RAPIDJSON_NAMESPACE_END #endif // RAPIDJSON_STREAM_H_ ================================================ FILE: src/json/rapidjson/stringbuffer.h ================================================ // Tencent is pleased to support the open source community by making RapidJSON available. // // Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at // // http://opensource.org/licenses/MIT // // Unless required by applicable law or agreed to in writing, software distributed // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. #ifndef RAPIDJSON_STRINGBUFFER_H_ #define RAPIDJSON_STRINGBUFFER_H_ #include "stream.h" #include "src/json/rapidjson/internal/stack.h" #if RAPIDJSON_HAS_CXX11_RVALUE_REFS #include // std::move #endif #include "src/json/rapidjson/internal/stack.h" #if defined(__clang__) RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(c++98-compat) #endif RAPIDJSON_NAMESPACE_BEGIN //! Represents an in-memory output stream. /*! \tparam Encoding Encoding of the stream. \tparam Allocator type for allocating memory buffer. \note implements Stream concept */ template class GenericStringBuffer { public: typedef typename Encoding::Ch Ch; GenericStringBuffer(Allocator* allocator = 0, size_t capacity = kDefaultCapacity) : stack_(allocator, capacity) {} #if RAPIDJSON_HAS_CXX11_RVALUE_REFS GenericStringBuffer(GenericStringBuffer&& rhs) : stack_(std::move(rhs.stack_)) {} GenericStringBuffer& operator=(GenericStringBuffer&& rhs) { if (&rhs != this) stack_ = std::move(rhs.stack_); return *this; } #endif void Put(Ch c) { *stack_.template Push() = c; } void PutUnsafe(Ch c) { *stack_.template PushUnsafe() = c; } void Flush() {} void Clear() { stack_.Clear(); } void ShrinkToFit() { // Push and pop a null terminator. This is safe. *stack_.template Push() = '\0'; stack_.ShrinkToFit(); stack_.template Pop(1); } void Reserve(size_t count) { stack_.template Reserve(count); } Ch* Push(size_t count) { return stack_.template Push(count); } Ch* PushUnsafe(size_t count) { return stack_.template PushUnsafe(count); } void Pop(size_t count) { stack_.template Pop(count); } const Ch* GetString() const { // Push and pop a null terminator. This is safe. *stack_.template Push() = '\0'; stack_.template Pop(1); return stack_.template Bottom(); } //! Get the size of string in bytes in the string buffer. size_t GetSize() const { return stack_.GetSize(); } //! Get the length of string in Ch in the string buffer. size_t GetLength() const { return stack_.GetSize() / sizeof(Ch); } static const size_t kDefaultCapacity = 256; mutable internal::Stack stack_; private: // Prohibit copy constructor & assignment operator. GenericStringBuffer(const GenericStringBuffer&); GenericStringBuffer& operator=(const GenericStringBuffer&); }; //! String buffer with UTF8 encoding typedef GenericStringBuffer > StringBuffer; template inline void PutReserve(GenericStringBuffer& stream, size_t count) { stream.Reserve(count); } template inline void PutUnsafe(GenericStringBuffer& stream, typename Encoding::Ch c) { stream.PutUnsafe(c); } //! Implement specialized version of PutN() with memset() for better performance. template<> inline void PutN(GenericStringBuffer >& stream, char c, size_t n) { std::memset(stream.stack_.Push(n), c, n * sizeof(c)); } RAPIDJSON_NAMESPACE_END #if defined(__clang__) RAPIDJSON_DIAG_POP #endif #endif // RAPIDJSON_STRINGBUFFER_H_ ================================================ FILE: src/json/rapidjson/writer.h ================================================ // Tencent is pleased to support the open source community by making RapidJSON available. // // Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at // // http://opensource.org/licenses/MIT // // Unless required by applicable law or agreed to in writing, software distributed // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. #ifndef RAPIDJSON_WRITER_H_ #define RAPIDJSON_WRITER_H_ #include "stream.h" #include "src/json/rapidjson/internal/meta.h" #include "src/json/rapidjson/internal/stack.h" #include "src/json/rapidjson/internal/strfunc.h" #include "src/json/rapidjson/internal/dtoa.h" #include "src/json/rapidjson/internal/itoa.h" #include "stringbuffer.h" #include // placement new #if defined(RAPIDJSON_SIMD) && defined(_MSC_VER) #include #pragma intrinsic(_BitScanForward) #endif #ifdef RAPIDJSON_SSE42 #include #elif defined(RAPIDJSON_SSE2) #include #elif defined(RAPIDJSON_NEON) #include #endif #ifdef __clang__ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(padded) RAPIDJSON_DIAG_OFF(unreachable-code) RAPIDJSON_DIAG_OFF(c++98-compat) #elif defined(_MSC_VER) RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant #endif RAPIDJSON_NAMESPACE_BEGIN /////////////////////////////////////////////////////////////////////////////// // WriteFlag /*! \def RAPIDJSON_WRITE_DEFAULT_FLAGS \ingroup RAPIDJSON_CONFIG \brief User-defined kWriteDefaultFlags definition. User can define this as any \c WriteFlag combinations. */ #ifndef RAPIDJSON_WRITE_DEFAULT_FLAGS #define RAPIDJSON_WRITE_DEFAULT_FLAGS kWriteNoFlags #endif //! Combination of writeFlags enum WriteFlag { kWriteNoFlags = 0, //!< No flags are set. kWriteValidateEncodingFlag = 1, //!< Validate encoding of JSON strings. kWriteNanAndInfFlag = 2, //!< Allow writing of Infinity, -Infinity and NaN. kWriteDefaultFlags = RAPIDJSON_WRITE_DEFAULT_FLAGS //!< Default write flags. Can be customized by defining RAPIDJSON_WRITE_DEFAULT_FLAGS }; //! JSON writer /*! Writer implements the concept Handler. It generates JSON text by events to an output os. User may programmatically calls the functions of a writer to generate JSON text. On the other side, a writer can also be passed to objects that generates events, for example Reader::Parse() and Document::Accept(). \tparam OutputStream Type of output stream. \tparam SourceEncoding Encoding of source string. \tparam TargetEncoding Encoding of output stream. \tparam StackAllocator Type of allocator for allocating memory of stack. \note implements Handler concept */ template, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator, unsigned writeFlags = kWriteDefaultFlags> class Writer { public: typedef typename SourceEncoding::Ch Ch; static const int kDefaultMaxDecimalPlaces = 324; //! Constructor /*! \param os Output stream. \param stackAllocator User supplied allocator. If it is null, it will create a private one. \param levelDepth Initial capacity of stack. */ explicit Writer(OutputStream& os, StackAllocator* stackAllocator = 0, size_t levelDepth = kDefaultLevelDepth) : os_(&os), level_stack_(stackAllocator, levelDepth * sizeof(Level)), maxDecimalPlaces_(kDefaultMaxDecimalPlaces), hasRoot_(false) {} explicit Writer(StackAllocator* allocator = 0, size_t levelDepth = kDefaultLevelDepth) : os_(0), level_stack_(allocator, levelDepth * sizeof(Level)), maxDecimalPlaces_(kDefaultMaxDecimalPlaces), hasRoot_(false) {} #if RAPIDJSON_HAS_CXX11_RVALUE_REFS Writer(Writer&& rhs) : os_(rhs.os_), level_stack_(std::move(rhs.level_stack_)), maxDecimalPlaces_(rhs.maxDecimalPlaces_), hasRoot_(rhs.hasRoot_) { rhs.os_ = 0; } #endif //! Reset the writer with a new stream. /*! This function reset the writer with a new stream and default settings, in order to make a Writer object reusable for output multiple JSONs. \param os New output stream. \code Writer writer(os1); writer.StartObject(); // ... writer.EndObject(); writer.Reset(os2); writer.StartObject(); // ... writer.EndObject(); \endcode */ void Reset(OutputStream& os) { os_ = &os; hasRoot_ = false; level_stack_.Clear(); } //! Checks whether the output is a complete JSON. /*! A complete JSON has a complete root object or array. */ bool IsComplete() const { return hasRoot_ && level_stack_.Empty(); } int GetMaxDecimalPlaces() const { return maxDecimalPlaces_; } //! Sets the maximum number of decimal places for double output. /*! This setting truncates the output with specified number of decimal places. For example, \code writer.SetMaxDecimalPlaces(3); writer.StartArray(); writer.Double(0.12345); // "0.123" writer.Double(0.0001); // "0.0" writer.Double(1.234567890123456e30); // "1.234567890123456e30" (do not truncate significand for positive exponent) writer.Double(1.23e-4); // "0.0" (do truncate significand for negative exponent) writer.EndArray(); \endcode The default setting does not truncate any decimal places. You can restore to this setting by calling \code writer.SetMaxDecimalPlaces(Writer::kDefaultMaxDecimalPlaces); \endcode */ void SetMaxDecimalPlaces(int maxDecimalPlaces) { maxDecimalPlaces_ = maxDecimalPlaces; } /*!@name Implementation of Handler \see Handler */ //@{ bool Null() { Prefix(kNullType); return EndValue(WriteNull()); } bool Bool(bool b) { Prefix(b ? kTrueType : kFalseType); return EndValue(WriteBool(b)); } bool Int(int i) { Prefix(kNumberType); return EndValue(WriteInt(i)); } bool Uint(unsigned u) { Prefix(kNumberType); return EndValue(WriteUint(u)); } bool Int64(int64_t i64) { Prefix(kNumberType); return EndValue(WriteInt64(i64)); } bool Uint64(uint64_t u64) { Prefix(kNumberType); return EndValue(WriteUint64(u64)); } //! Writes the given \c double value to the stream /*! \param d The value to be written. \return Whether it is succeed. */ bool Double(double d) { Prefix(kNumberType); return EndValue(WriteDouble(d)); } bool RawNumber(const Ch* str, SizeType length, bool copy = false) { RAPIDJSON_ASSERT(str != 0); (void)copy; Prefix(kNumberType); return EndValue(WriteString(str, length)); } bool String(const Ch* str, SizeType length, bool copy = false) { RAPIDJSON_ASSERT(str != 0); (void)copy; Prefix(kStringType); return EndValue(WriteString(str, length)); } #if RAPIDJSON_HAS_STDSTRING bool String(const std::basic_string& str) { return String(str.data(), SizeType(str.size())); } #endif bool StartObject() { Prefix(kObjectType); new (level_stack_.template Push()) Level(false); return WriteStartObject(); } bool Key(const Ch* str, SizeType length, bool copy = false) { return String(str, length, copy); } #if RAPIDJSON_HAS_STDSTRING bool Key(const std::basic_string& str) { return Key(str.data(), SizeType(str.size())); } #endif bool EndObject(SizeType memberCount = 0) { (void)memberCount; RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level)); // not inside an Object RAPIDJSON_ASSERT(!level_stack_.template Top()->inArray); // currently inside an Array, not Object RAPIDJSON_ASSERT(0 == level_stack_.template Top()->valueCount % 2); // Object has a Key without a Value level_stack_.template Pop(1); return EndValue(WriteEndObject()); } bool StartArray() { Prefix(kArrayType); new (level_stack_.template Push()) Level(true); return WriteStartArray(); } bool EndArray(SizeType elementCount = 0) { (void)elementCount; RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level)); RAPIDJSON_ASSERT(level_stack_.template Top()->inArray); level_stack_.template Pop(1); return EndValue(WriteEndArray()); } //@} /*! @name Convenience extensions */ //@{ //! Simpler but slower overload. bool String(const Ch* const& str) { return String(str, internal::StrLen(str)); } bool Key(const Ch* const& str) { return Key(str, internal::StrLen(str)); } //@} //! Write a raw JSON value. /*! For user to write a stringified JSON as a value. \param json A well-formed JSON value. It should not contain null character within [0, length - 1] range. \param length Length of the json. \param type Type of the root of json. */ bool RawValue(const Ch* json, size_t length, Type type) { RAPIDJSON_ASSERT(json != 0); Prefix(type); return EndValue(WriteRawValue(json, length)); } //! Flush the output stream. /*! Allows the user to flush the output stream immediately. */ void Flush() { os_->Flush(); } protected: //! Information for each nested level struct Level { Level(bool inArray_) : valueCount(0), inArray(inArray_) {} size_t valueCount; //!< number of values in this level bool inArray; //!< true if in array, otherwise in object }; static const size_t kDefaultLevelDepth = 32; bool WriteNull() { PutReserve(*os_, 4); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'u'); PutUnsafe(*os_, 'l'); PutUnsafe(*os_, 'l'); return true; } bool WriteBool(bool b) { if (b) { PutReserve(*os_, 4); PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'r'); PutUnsafe(*os_, 'u'); PutUnsafe(*os_, 'e'); } else { PutReserve(*os_, 5); PutUnsafe(*os_, 'f'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'l'); PutUnsafe(*os_, 's'); PutUnsafe(*os_, 'e'); } return true; } bool WriteInt(int i) { char buffer[11]; const char* end = internal::i32toa(i, buffer); PutReserve(*os_, static_cast(end - buffer)); for (const char* p = buffer; p != end; ++p) PutUnsafe(*os_, static_cast(*p)); return true; } bool WriteUint(unsigned u) { char buffer[10]; const char* end = internal::u32toa(u, buffer); PutReserve(*os_, static_cast(end - buffer)); for (const char* p = buffer; p != end; ++p) PutUnsafe(*os_, static_cast(*p)); return true; } bool WriteInt64(int64_t i64) { char buffer[21]; const char* end = internal::i64toa(i64, buffer); PutReserve(*os_, static_cast(end - buffer)); for (const char* p = buffer; p != end; ++p) PutUnsafe(*os_, static_cast(*p)); return true; } bool WriteUint64(uint64_t u64) { char buffer[20]; char* end = internal::u64toa(u64, buffer); PutReserve(*os_, static_cast(end - buffer)); for (char* p = buffer; p != end; ++p) PutUnsafe(*os_, static_cast(*p)); return true; } bool WriteDouble(double d) { if (internal::Double(d).IsNanOrInf()) { if (!(writeFlags & kWriteNanAndInfFlag)) return false; if (internal::Double(d).IsNan()) { PutReserve(*os_, 3); PutUnsafe(*os_, 'N'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'N'); return true; } if (internal::Double(d).Sign()) { PutReserve(*os_, 9); PutUnsafe(*os_, '-'); } else PutReserve(*os_, 8); PutUnsafe(*os_, 'I'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'f'); PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'y'); return true; } char buffer[25]; char* end = internal::dtoa(d, buffer, maxDecimalPlaces_); PutReserve(*os_, static_cast(end - buffer)); for (char* p = buffer; p != end; ++p) PutUnsafe(*os_, static_cast(*p)); return true; } bool WriteString(const Ch* str, SizeType length) { static const typename OutputStream::Ch hexDigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; static const char escape[256] = { #define Z16 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 //0 1 2 3 4 5 6 7 8 9 A B C D E F 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'b', 't', 'n', 'u', 'f', 'r', 'u', 'u', // 00 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', // 10 0, 0, '"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20 Z16, Z16, // 30~4F 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'\\', 0, 0, 0, // 50 Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16 // 60~FF #undef Z16 }; if (TargetEncoding::supportUnicode) PutReserve(*os_, 2 + length * 6); // "\uxxxx..." else PutReserve(*os_, 2 + length * 12); // "\uxxxx\uyyyy..." PutUnsafe(*os_, '\"'); GenericStringStream is(str); while (ScanWriteUnescapedString(is, length)) { const Ch c = is.Peek(); if (!TargetEncoding::supportUnicode && static_cast(c) >= 0x80) { // Unicode escaping unsigned codepoint; if (RAPIDJSON_UNLIKELY(!SourceEncoding::Decode(is, &codepoint))) return false; PutUnsafe(*os_, '\\'); PutUnsafe(*os_, 'u'); if (codepoint <= 0xD7FF || (codepoint >= 0xE000 && codepoint <= 0xFFFF)) { PutUnsafe(*os_, hexDigits[(codepoint >> 12) & 15]); PutUnsafe(*os_, hexDigits[(codepoint >> 8) & 15]); PutUnsafe(*os_, hexDigits[(codepoint >> 4) & 15]); PutUnsafe(*os_, hexDigits[(codepoint ) & 15]); } else { RAPIDJSON_ASSERT(codepoint >= 0x010000 && codepoint <= 0x10FFFF); // Surrogate pair unsigned s = codepoint - 0x010000; unsigned lead = (s >> 10) + 0xD800; unsigned trail = (s & 0x3FF) + 0xDC00; PutUnsafe(*os_, hexDigits[(lead >> 12) & 15]); PutUnsafe(*os_, hexDigits[(lead >> 8) & 15]); PutUnsafe(*os_, hexDigits[(lead >> 4) & 15]); PutUnsafe(*os_, hexDigits[(lead ) & 15]); PutUnsafe(*os_, '\\'); PutUnsafe(*os_, 'u'); PutUnsafe(*os_, hexDigits[(trail >> 12) & 15]); PutUnsafe(*os_, hexDigits[(trail >> 8) & 15]); PutUnsafe(*os_, hexDigits[(trail >> 4) & 15]); PutUnsafe(*os_, hexDigits[(trail ) & 15]); } } else if ((sizeof(Ch) == 1 || static_cast(c) < 256) && RAPIDJSON_UNLIKELY(escape[static_cast(c)])) { is.Take(); PutUnsafe(*os_, '\\'); PutUnsafe(*os_, static_cast(escape[static_cast(c)])); if (escape[static_cast(c)] == 'u') { PutUnsafe(*os_, '0'); PutUnsafe(*os_, '0'); PutUnsafe(*os_, hexDigits[static_cast(c) >> 4]); PutUnsafe(*os_, hexDigits[static_cast(c) & 0xF]); } } else if (RAPIDJSON_UNLIKELY(!(writeFlags & kWriteValidateEncodingFlag ? Transcoder::Validate(is, *os_) : Transcoder::TranscodeUnsafe(is, *os_)))) return false; } PutUnsafe(*os_, '\"'); return true; } bool ScanWriteUnescapedString(GenericStringStream& is, size_t length) { return RAPIDJSON_LIKELY(is.Tell() < length); } bool WriteStartObject() { os_->Put('{'); return true; } bool WriteEndObject() { os_->Put('}'); return true; } bool WriteStartArray() { os_->Put('['); return true; } bool WriteEndArray() { os_->Put(']'); return true; } bool WriteRawValue(const Ch* json, size_t length) { PutReserve(*os_, length); GenericStringStream is(json); while (RAPIDJSON_LIKELY(is.Tell() < length)) { RAPIDJSON_ASSERT(is.Peek() != '\0'); if (RAPIDJSON_UNLIKELY(!(writeFlags & kWriteValidateEncodingFlag ? Transcoder::Validate(is, *os_) : Transcoder::TranscodeUnsafe(is, *os_)))) return false; } return true; } void Prefix(Type type) { (void)type; if (RAPIDJSON_LIKELY(level_stack_.GetSize() != 0)) { // this value is not at root Level* level = level_stack_.template Top(); if (level->valueCount > 0) { if (level->inArray) os_->Put(','); // add comma if it is not the first element in array else // in object os_->Put((level->valueCount % 2 == 0) ? ',' : ':'); } if (!level->inArray && level->valueCount % 2 == 0) RAPIDJSON_ASSERT(type == kStringType); // if it's in object, then even number should be a name level->valueCount++; } else { RAPIDJSON_ASSERT(!hasRoot_); // Should only has one and only one root. hasRoot_ = true; } } // Flush the value if it is the top level one. bool EndValue(bool ret) { if (RAPIDJSON_UNLIKELY(level_stack_.Empty())) // end of json text Flush(); return ret; } OutputStream* os_; internal::Stack level_stack_; int maxDecimalPlaces_; bool hasRoot_; private: // Prohibit copy constructor & assignment operator. Writer(const Writer&); Writer& operator=(const Writer&); }; // Full specialization for StringStream to prevent memory copying template<> inline bool Writer::WriteInt(int i) { char *buffer = os_->Push(11); const char* end = internal::i32toa(i, buffer); os_->Pop(static_cast(11 - (end - buffer))); return true; } template<> inline bool Writer::WriteUint(unsigned u) { char *buffer = os_->Push(10); const char* end = internal::u32toa(u, buffer); os_->Pop(static_cast(10 - (end - buffer))); return true; } template<> inline bool Writer::WriteInt64(int64_t i64) { char *buffer = os_->Push(21); const char* end = internal::i64toa(i64, buffer); os_->Pop(static_cast(21 - (end - buffer))); return true; } template<> inline bool Writer::WriteUint64(uint64_t u) { char *buffer = os_->Push(20); const char* end = internal::u64toa(u, buffer); os_->Pop(static_cast(20 - (end - buffer))); return true; } template<> inline bool Writer::WriteDouble(double d) { if (internal::Double(d).IsNanOrInf()) { // Note: This code path can only be reached if (RAPIDJSON_WRITE_DEFAULT_FLAGS & kWriteNanAndInfFlag). if (!(kWriteDefaultFlags & kWriteNanAndInfFlag)) return false; if (internal::Double(d).IsNan()) { PutReserve(*os_, 3); PutUnsafe(*os_, 'N'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'N'); return true; } if (internal::Double(d).Sign()) { PutReserve(*os_, 9); PutUnsafe(*os_, '-'); } else PutReserve(*os_, 8); PutUnsafe(*os_, 'I'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'f'); PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'y'); return true; } char *buffer = os_->Push(25); char* end = internal::dtoa(d, buffer, maxDecimalPlaces_); os_->Pop(static_cast(25 - (end - buffer))); return true; } #if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42) template<> inline bool Writer::ScanWriteUnescapedString(StringStream& is, size_t length) { if (length < 16) return RAPIDJSON_LIKELY(is.Tell() < length); if (!RAPIDJSON_LIKELY(is.Tell() < length)) return false; const char* p = is.src_; const char* end = is.head_ + length; const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); const char* endAligned = reinterpret_cast(reinterpret_cast(end) & static_cast(~15)); if (nextAligned > end) return true; while (p != nextAligned) if (*p < 0x20 || *p == '\"' || *p == '\\') { is.src_ = p; return RAPIDJSON_LIKELY(is.Tell() < length); } else os_->PutUnsafe(*p++); // The rest of string using SIMD static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; static const char space[16] = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F }; const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); for (; p != endAligned; p += 16) { const __m128i s = _mm_load_si128(reinterpret_cast(p)); const __m128i t1 = _mm_cmpeq_epi8(s, dq); const __m128i t2 = _mm_cmpeq_epi8(s, bs); const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x1F) == 0x1F const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); unsigned short r = static_cast(_mm_movemask_epi8(x)); if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped SizeType len; #ifdef _MSC_VER // Find the index of first escaped unsigned long offset; _BitScanForward(&offset, r); len = offset; #else len = static_cast(__builtin_ffs(r) - 1); #endif char* q = reinterpret_cast(os_->PushUnsafe(len)); for (size_t i = 0; i < len; i++) q[i] = p[i]; p += len; break; } _mm_storeu_si128(reinterpret_cast<__m128i *>(os_->PushUnsafe(16)), s); } is.src_ = p; return RAPIDJSON_LIKELY(is.Tell() < length); } #elif defined(RAPIDJSON_NEON) template<> inline bool Writer::ScanWriteUnescapedString(StringStream& is, size_t length) { if (length < 16) return RAPIDJSON_LIKELY(is.Tell() < length); if (!RAPIDJSON_LIKELY(is.Tell() < length)) return false; const char* p = is.src_; const char* end = is.head_ + length; const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); const char* endAligned = reinterpret_cast(reinterpret_cast(end) & static_cast(~15)); if (nextAligned > end) return true; while (p != nextAligned) if (*p < 0x20 || *p == '\"' || *p == '\\') { is.src_ = p; return RAPIDJSON_LIKELY(is.Tell() < length); } else os_->PutUnsafe(*p++); // The rest of string using SIMD const uint8x16_t s0 = vmovq_n_u8('"'); const uint8x16_t s1 = vmovq_n_u8('\\'); const uint8x16_t s2 = vmovq_n_u8('\b'); const uint8x16_t s3 = vmovq_n_u8(32); for (; p != endAligned; p += 16) { const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); uint8x16_t x = vceqq_u8(s, s0); x = vorrq_u8(x, vceqq_u8(s, s1)); x = vorrq_u8(x, vceqq_u8(s, s2)); x = vorrq_u8(x, vcltq_u8(s, s3)); x = vrev64q_u8(x); // Rev in 64 uint64_t low = vgetq_lane_u64(reinterpret_cast(x), 0); // extract uint64_t high = vgetq_lane_u64(reinterpret_cast(x), 1); // extract SizeType len = 0; bool escaped = false; if (low == 0) { if (high != 0) { unsigned lz = (unsigned)__builtin_clzll(high); len = 8 + (lz >> 3); escaped = true; } } else { unsigned lz = (unsigned)__builtin_clzll(low); len = lz >> 3; escaped = true; } if (RAPIDJSON_UNLIKELY(escaped)) { // some of characters is escaped char* q = reinterpret_cast(os_->PushUnsafe(len)); for (size_t i = 0; i < len; i++) q[i] = p[i]; p += len; break; } vst1q_u8(reinterpret_cast(os_->PushUnsafe(16)), s); } is.src_ = p; return RAPIDJSON_LIKELY(is.Tell() < length); } #endif // RAPIDJSON_NEON RAPIDJSON_NAMESPACE_END #if defined(_MSC_VER) || defined(__clang__) RAPIDJSON_DIAG_POP #endif #endif // RAPIDJSON_RAPIDJSON_H_ ================================================ FILE: src/listen/ClientWorker.cpp ================================================ #include #include "ClientWorker.h" #include "listen/Listener.h" #include "src/utils/url.h" #include "src/utils/GroupKey.h" #include "src/crypto/md5/md5.h" #include "src/utils/ParamUtils.h" #include "src/utils/TimeUtils.h" #include "src/log/Logger.h" #include "src/debug/DebugAssertion.h" #include "constant/ConfigConstant.h" #include "constant/PropertyKeyConst.h" #include "src/http/HttpStatus.h" #include "src/config/ConfigProxy.h" #define MAX(a,b) ((a) > (b) ? (a) : (b)) using namespace std; namespace nacos{ ClientWorker::ClientWorker(ObjectConfigData *objectConfigData) { threadId = 0; stopThread = true; pthread_mutex_init(&watchListMutex, NULL); pthread_mutex_init(&stopThreadMutex, NULL); _objectConfigData = objectConfigData; _longPullingTimeoutStr = _objectConfigData->_appConfigManager->get(PropertyKeyConst::CONFIG_LONGPULLLING_TIMEOUT); _longPullingTimeout = atoi(_longPullingTimeoutStr.c_str()); } ClientWorker::~ClientWorker() { log_debug("[ClientWorker]:~ClientWorker()\n"); stopListening(); cleanUp(); } NacosString ClientWorker::getServerConfig ( const NacosString &tenant, const NacosString &dataId, const NacosString &group, long timeoutMs ) NACOS_THROW(NacosException) { HttpResult res = getServerConfigHelper(tenant, dataId, group, timeoutMs); AppConfigManager *_appConfigManager = _objectConfigData->_appConfigManager; LocalSnapshotManager *localSnapshotManager = _objectConfigData->_localSnapshotManager; switch (res.code) { case HttpStatus::HTTP_OK: localSnapshotManager->saveSnapshot(_appConfigManager->get(PropertyKeyConst::CLIENT_NAME), dataId, group, tenant, res.content); return res.content; case HttpStatus::HTTP_NOT_FOUND: //Update snapshot localSnapshotManager->saveSnapshot(_appConfigManager->get(PropertyKeyConst::CLIENT_NAME), dataId, group, tenant, NULLSTR); throw NacosException(NacosException::HTTP_NOT_FOUND, "getServerConfig could not get content for Key " + group + ":" + dataId); case HttpStatus::HTTP_FORBIDDEN: //Update snapshot localSnapshotManager->saveSnapshot(_appConfigManager->get(PropertyKeyConst::CLIENT_NAME), dataId, group, tenant, NULLSTR); throw NacosException(NacosException::NO_RIGHT, "permission denied for Key " + group + ":" + dataId); default: localSnapshotManager->saveSnapshot(_appConfigManager->get(PropertyKeyConst::CLIENT_NAME), dataId, group, tenant, NULLSTR); throw NacosException(NacosException::SERVER_ERROR, "getServerConfig failed with code:" + NacosStringOps::valueOf(res.code)); } return NULLSTR; } HttpResult ClientWorker::getServerConfigHelper ( const NacosString &tenant, const NacosString &dataId, const NacosString &group, long timeoutMs ) NACOS_THROW(NacosException) { std::list headers; std::list paramValues; ParamUtils::addKV(paramValues, "dataId", dataId); if (!isNull(group)) { ParamUtils::addKV(paramValues, "group", group); } else { ParamUtils::addKV(paramValues, "group", ConfigConstant::DEFAULT_GROUP); } if (!isNull(tenant)) { ParamUtils::addKV(paramValues, "tenant", tenant); } //Get the request url NacosString path = _objectConfigData->_appConfigManager->getContextPath() + ConfigConstant::CONFIG_CONTROLLER_PATH; NacosString serverAddr = _objectConfigData->_serverListManager->getCurrentServerAddr(); NacosString url = serverAddr + "/" + path; log_debug("[ClientWorker]-getServerConfigHelper:httpGet Assembled URL:%s\n", url.c_str()); HttpResult res; ConfigProxy *_configProxy = _objectConfigData->_configProxy; try { res = _configProxy->reqAPI(IHttpCli::GET, url, headers, paramValues, _objectConfigData->encoding, timeoutMs); } catch (NetworkException &e) { throw NacosException(NacosException::SERVER_ERROR, e.what()); } return res; } void *ClientWorker::listenerThread(void *parm) { log_debug("[ClientWorker]-listenerThread:Entered watch thread...\n"); ClientWorker *thelistener = (ClientWorker *) parm; while (!thelistener->stopThread) { int64_t start_time = TimeUtils::getCurrentTimeInMs(); log_debug("[ClientWorker]-listenerThread:Start watching at %u...\n", start_time); thelistener->performWatch(); log_debug("[ClientWorker]-listenerThread:Watch function exit at %u...\n", TimeUtils::getCurrentTimeInMs()); } return 0; } vector ClientWorker::parseListenedKeys(const NacosString &ReturnedKeys) { NacosString changedKeyList = urldecode(ReturnedKeys); vector explodedList; ParamUtils::Explode(explodedList, changedKeyList, ConfigConstant::LINE_SEPARATOR); //If the server returns a string with a trailing \x01, actually there is no data after that //but ParamUtils::Explode will return an extra item with empty string, we need to remove that //from the list so it won't disrupt subsequent operations log_debug("[ClientWorker]-parseListenedKeys:extra data:%s\n", explodedList[explodedList.size() - 1].c_str()); if (explodedList.size() >= 1 && ParamUtils::isBlank(explodedList[explodedList.size() - 1])) { explodedList.pop_back(); } return explodedList; } void ClientWorker::startListening() { //Already started, skip this if (!stopThread) { log_debug("[ClientWorker]-startListening:The thread is already started or the starting is in progress...\n"); return; } pthread_mutex_lock(&stopThreadMutex); if (!stopThread) { pthread_mutex_unlock(&stopThreadMutex); log_debug("[ClientWorker]-startListening:The thread is already started or the starting is in progress...\n"); return; } stopThread = false; pthread_mutex_unlock(&stopThreadMutex); log_debug("[ClientWorker]-startListening:Starting the thread...\n"); pthread_create(&threadId, NULL, listenerThread, (void *) this); log_debug("[ClientWorker]-startListening:Started thread with id:%d...\n", threadId); } void ClientWorker::stopListening() { log_debug("[ClientWorker]-stopListening: entered\n"); if (stopThread)//Stop in progress { log_debug("[ClientWorker]-stopListening:The thread is already stopped or the stop is in progress...\n"); return; } pthread_mutex_lock(&stopThreadMutex); if (stopThread)//Stop in progress { pthread_mutex_unlock(&stopThreadMutex); log_debug("[ClientWorker]-stopListening:The thread is already stopped or the stop is in progress...\n"); return; } stopThread = true; pthread_mutex_unlock(&stopThreadMutex); pthread_join(threadId, NULL); log_info("[ClientWorker]-stopListening:The thread is stopped successfully...\n"); } void ClientWorker::addListener ( const NacosString &dataId, const NacosString &group, const NacosString &tenant, const NacosString &initialContent, Listener *listener ) { NacosString key = GroupKey::getKeyTenant(dataId, group, tenant); log_debug("[ClientWorker]-addListener:Adding listener with key: %s\n", key.c_str()); pthread_mutex_lock(&watchListMutex); //Check whether the listener being added to the list already exists if (listeningKeys.find(key) != listeningKeys.end()) { ListeningData *curListeningData = listeningKeys[key]; if (!curListeningData->addListener(listener)) { log_warn("[ClientWorker]-addListener:Key %s is already in the watch list, leaving...\n", key.c_str()); } listener->incRef(); pthread_mutex_unlock(&watchListMutex); return; } //if the listener does not exist, just create it ListeningData *listeningData = new ListeningData(tenant, dataId, group, initialContent); //If no, copy one listeningKeys[key] = listeningData; listeningData->addListener(listener); listener->incRef(); pthread_mutex_unlock(&watchListMutex); log_debug("[ClientWorker]-addListener:Key %s is added successfully!\n", key.c_str()); } /** * Removes a listener from the listened list actively, but slow * @param listener the listener to be removed * @author Liu, Hanyu */ void ClientWorker::removeListenerActively ( const NacosString &dataId, const NacosString &group, const NacosString &tenant, Listener *listener ) { NacosString key = GroupKey::getKeyTenant(dataId, group, tenant); pthread_mutex_lock(&watchListMutex); map::iterator it = listeningKeys.find(key); //Check whether the cachedata being removed exists if (it == listeningKeys.end()) { log_warn("[ClientWorker]-removeListenerActively:Removing a non-existing listener %s, leaving...\n", key.c_str()); pthread_mutex_unlock(&watchListMutex); return; } //If so, remove it and free the resources ListeningData *curListeningData = it->second; bool succRemoved = curListeningData->removeListener(listener); if (!succRemoved) { log_warn("[ClientWorker]-removeListenerActively:Removing a non-existing listener %s...\n", key.c_str()); } else { //remove the listener, it is created by the client but freed by nacos-sdk-cpp log_debug("[ClientWorker]-removeListenerActively:Removing a listener %s...\n", key.c_str()); int refcount = listener->decRef(); if (refcount == 0) { log_debug("[ClientWorker]-removeListenerActively:" "Refcount of the listener(Name = %s) is 0 so delete it.\n", listener->getListenerName().c_str()); delete listener; listener = NULL; } } //no listener on the list, remove the entry if (curListeningData->isEmpty()) { listeningKeys.erase(key); //free the space for this slot delete curListeningData; } pthread_mutex_unlock(&watchListMutex); } /** * Removes a listener from the listened list(very fast compared to removeListenerActively) * This function will return fast, * since it just mark the remove flag of the listener and wait for the watcher to actually remove it * @param listener the listener to be removed * @author Liu, Hanyu */ void ClientWorker::removeListener ( const NacosString &dataId, const NacosString &group, const NacosString &tenant, Listener *listener ) { //set the remove flag and return quickly //the background process will remove the listener //add this listener to the remove list since it is marked to be removed OperateItem operateItem(dataId, tenant, group, listener); addDeleteItem(operateItem); } NacosString ClientWorker::checkListenedKeys() NACOS_THROW(NetworkException,NacosException) { NacosString postData; pthread_mutex_lock(&watchListMutex); for (map::iterator it = listeningKeys.begin(); it != listeningKeys.end(); it++) { ListeningData *curListenedKey = it->second; postData += curListenedKey->getDataId(); postData += ConfigConstant::WORD_SEPARATOR; postData += curListenedKey->getGroup(); postData += ConfigConstant::WORD_SEPARATOR; if (!isNull(curListenedKey->getTenant())) { postData += curListenedKey->getMD5(); postData += ConfigConstant::WORD_SEPARATOR; postData += curListenedKey->getTenant(); postData += ConfigConstant::LINE_SEPARATOR; } else { postData += curListenedKey->getMD5(); postData += ConfigConstant::LINE_SEPARATOR; } } pthread_mutex_unlock(&watchListMutex); list headers; list paramValues; headers.push_back("Long-Pulling-Timeout"); headers.push_back(_longPullingTimeoutStr); paramValues.push_back(ConfigConstant::PROBE_MODIFY_REQUEST); paramValues.push_back(postData); log_debug("[ClientWorker]-checkListenedKeys:Assembled postData:%s\n", postData.c_str()); //Get the request url //TODO:move /listener to constant NacosString path = _objectConfigData->_appConfigManager->getContextPath() + ConfigConstant::CONFIG_CONTROLLER_PATH + "/listener"; HttpResult res; NacosString serverAddr = _objectConfigData->_serverListManager->getCurrentServerAddr(); NacosString url = serverAddr + "/" + path; log_debug("[ClientWorker]-checkListenedKeys:httpPost Assembled URL:%s\n", url.c_str()); ConfigProxy *_configProxy = _objectConfigData->_configProxy; res = _configProxy->reqAPI(IHttpCli::POST, url, headers, paramValues, _objectConfigData->encoding, _longPullingTimeout); log_debug("[ClientWorker]-checkListenedKeys:Received the message below from server:\n%s\n", res.content.c_str()); log_debug("[ClientWorker]-checkListenedKeys:return status:httpcode=%d curlcode=%d\n", res.code, res.curlcode); if (res.code != HttpStatus::HTTP_OK) { //bugfix #52 please check github throw NacosException(res.code, "Error while watching keys"); } return res.content; } void ClientWorker::performWatch() { MD5 md5; NacosString changedData; try { changedData = checkListenedKeys(); } catch (NetworkException &e) { log_warn("[ClientWorker]-performWatch:sleep and retry, reason: NetworkException with: %s\n", e.what()); //wait for at lease 3 secs to avoid too many wake-ups when the network is down sleep(MAX(_longPullingTimeout / 10000, 3)); return; } catch (NacosException &e) { log_warn("[ClientWorker]-performWatch:sleep and retry, reason: NacosException with: %s\n", e.what()); //same reason as above sleep(MAX(_longPullingTimeout / 10000, 3)); return; } vector changedList = ClientWorker::parseListenedKeys(changedData); pthread_mutex_lock(&watchListMutex); for (std::vector::iterator it = changedList.begin(); it != changedList.end(); it++) { NacosString dataId, group, tenant; ParamUtils::parseString2KeyGroupTenant(*it, dataId, group, tenant); log_debug("[ClientWorker]-performWatch:Processing item:%s, dataId = %s, group = %s, tenant = %s\n", it->c_str(), dataId.c_str(), group.c_str(), tenant.c_str()); NacosString key = GroupKey::getKeyTenant(dataId, group, tenant); map::iterator listenedDataIter = listeningKeys.find(key); HttpResult res; //check whether the data being watched still exists if (listenedDataIter != listeningKeys.end()) { log_debug("[ClientWorker]-performWatch:Found entry for:%s\n", key.c_str()); ListeningData *listenedList = listenedDataIter->second; NacosString updatedcontent = ""; try { res = getServerConfigHelper(listenedList->getTenant(), listenedList->getDataId(), listenedList->getGroup(), _objectConfigData->_appConfigManager->getServeReqTimeout()); updatedcontent = res.content; } catch (NacosException &e) { //Same design as SubscriptionPoller log_warn("[ClientWorker]-performWatch:Encountered exception when getting config from server:%s:%s:%s\n", listenedList->getTenant().c_str(), listenedList->getGroup().c_str(), listenedList->getDataId().c_str()); //wait for at lease 3 secs to avoid too many wake-ups when the network is down sleep(MAX(_longPullingTimeout / 10000, 3)); break; } log_debug("[ClientWorker]-performWatch:Data fetched from the server: %s\n", updatedcontent.c_str()); //Bugfix #42, please check github if (res.code == HttpStatus::HTTP_OK) { md5.reset(); md5.update(updatedcontent.c_str()); listenedList->setMD5(md5.toString()); log_debug("[ClientWorker]-performWatch:MD5 got for that data: %s\n", listenedList->getMD5().c_str()); } else { listenedList->setMD5(""); updatedcontent = ""; } std::map < Listener * , char > const *listenerList = listenedList->getListenerList(); for (std::map::const_iterator listenerIt = listenerList->begin(); listenerIt != listenerList->end(); listenerIt++) { Listener *curListener = listenerIt->first; NACOS_ASSERT(curListener->refCnt() > 0); curListener->receiveConfigInfo(updatedcontent); } } } clearDeleteList(20);//TODO:constant pthread_mutex_unlock(&watchListMutex); } /** * Removes listeners in deleteList * * *NOT THREAD SAFE*, must be called within a watchListMutex guarded area or after the watcher thread is stopped * @param maxRemoves if < 0, clear all items in the deleteList * @author Liu, Hanyu */ void ClientWorker::clearDeleteList(int maxRemoves) { int removeCount = 0; while (!deleteList.empty()) { //remove limited items every time if (maxRemoves > 0 && removeCount >= maxRemoves) { break; } std::list::iterator it = deleteList.begin(); OperateItem itm = *it; NacosString key = GroupKey::getKeyTenant(itm.getDataId(), itm.getGroup(), itm.getTenant()); if (listeningKeys.find(key) == listeningKeys.end()) { log_warn("[ClientWorker]-clearDeleteList:Trying to remove non-existent key: %s\n", key.c_str()); deleteList.erase(it); continue; } ListeningData *slotOfListener = listeningKeys[key]; Listener *theListener = itm.getListener(); slotOfListener->removeListener(theListener); int refcount = theListener->decRef(); log_debug("[ClientWorker]-clearDeleteList:The listener (Name = %s) on deleteList is removed, key = %s.\n", theListener->getListenerName().c_str(), key.c_str()); if (refcount == 0) { delete theListener; } itm.setListener(NULL); if (slotOfListener->isEmpty()) { log_debug("[ClientWorker]-clearDeleteList:The slot (Name = %s) is empty and removed\n", key.c_str()); delete slotOfListener; slotOfListener = NULL; listeningKeys.erase(key); } deleteList.erase(it); removeCount++; } } void ClientWorker::cleanUp() { log_debug("[ClientWorker]-cleanUp:entered\n"); clearDeleteList(0); for (map::iterator it = listeningKeys.begin(); it != listeningKeys.end(); it++) { ListeningData *listeningData = it->second; log_debug("[ClientWorker]-cleanUp:Cleaning %s\n", listeningData->toString().c_str()); listeningData->clearListeners(); delete listeningData; listeningData = NULL; } } void ClientWorker::addDeleteItem(const OperateItem &operateItem) { log_debug("[ClientWorker]-addDeleteItem:Adding delete item: %s\n", operateItem.toString().c_str()); pthread_mutex_lock(&watchListMutex); deleteList.push_back(operateItem); pthread_mutex_unlock(&watchListMutex); } }//namespace nacos ================================================ FILE: src/listen/ClientWorker.h ================================================ #ifndef __CLIENT_WORKER_H_ #define __CLIENT_WORKER_H_ #include #include #include #include "NacosString.h" #include "listen/Listener.h" #include "ListeningData.h" #include "OperateItem.h" #include "src/config/AppConfigManager.h" #include "src/server/ServerListManager.h" #include "src/config/LocalSnapshotManager.h" #include "NacosExceptions.h" #include "src/factory/ObjectConfigData.h" #include "Compatibility.h" /** * ClientWorker * * @author Liu, Hanyu * Directly migrated from Java, but with significant work of rewrite/redesign */ namespace nacos{ class ClientWorker { private: //This list holds the listeners to be removed after a performWatch() event //And after all the items in this list is removed, an extra check must be performed on listeningKeys //to make sure if the entry of that key is empty, remove the entry std::list deleteList; //dataID||group||tenant -> Cachedata* Mapping std::map listeningKeys; pthread_mutex_t watchListMutex;//TODO:refactor to Mutex ObjectConfigData *_objectConfigData; //Listener thread related info pthread_t threadId; volatile bool stopThread; pthread_mutex_t stopThreadMutex; int _longPullingTimeout; NacosString _longPullingTimeoutStr; static void *listenerThread(void *watcher); //You just can't construct a ClientWorker object without any parameter ClientWorker(); std::vector parseListenedKeys(const NacosString &ReturnedKeys); NacosString checkListenedKeys() NACOS_THROW(NetworkException,NacosException); void clearDeleteList(int maxRemoves); void cleanUp(); void addDeleteItem(const OperateItem &item); public: ClientWorker(ObjectConfigData *objectConfigData); ~ClientWorker(); void startListening(); void stopListening(); void addListener(const NacosString &dataId, const NacosString &group, const NacosString &tenant, const NacosString &initialContent, Listener *listener); void removeListenerActively(const NacosString &dataId, const NacosString &group, const NacosString &tenant, Listener *listener); void removeListener(const NacosString &dataId, const NacosString &group, const NacosString &tenant, Listener *listener); void performWatch(); NacosString getServerConfig(const NacosString &tenant, const NacosString &dataId, const NacosString &group, long timeoutMs) NACOS_THROW(NacosException); HttpResult getServerConfigHelper(const NacosString &tenant, const NacosString &dataId, const NacosString &group, long timeoutMs) NACOS_THROW(NacosException); }; }//namespace nacos #endif ================================================ FILE: src/listen/ListeningData.h ================================================ #ifndef __LISTENINGDATA_H_ #define __LISTENINGDATA_H_ #include #include #include "NacosString.h" #include "listen/Listener.h" #include "src/crypto//md5/md5.h" #include "src/log/Logger.h" namespace nacos{ class ListeningData { private: NacosString tenant; NacosString dataId; NacosString group; NacosString dataMD5; std::map listenerList; public: NacosString toString() const { return tenant + ":" + dataId + ":" + group + ":" + dataMD5 + ", listenerList size=" + NacosStringOps::valueOf(listenerList.size()); } NacosString getDataId() const { return dataId; } NacosString getTenant() const { return tenant; } NacosString getGroup() const { return group; } NacosString getMD5() const { return dataMD5; } void setMD5(const NacosString &md5) { dataMD5 = md5; } /** * Adds a listener to this object being listened for further notification * @param listener the listener to be added * @return true if added successfully * false if the listener is already in the list */ bool addListener(Listener *listener) { if (listenerList.find(listener) != listenerList.end()) { return false; } listenerList[listener] = 1; return true; } /** * Removes a listener from this object being listened * @param listener the listener to be removed * @return true if removed successfully * false if the listener does not exist in the list */ bool removeListener(Listener *listener) { if (listenerList.count(listener) <= 0) { return false; } listenerList.erase(listener); return true; } void clearListeners() { while (!listenerList.empty()) { std::map::iterator it = listenerList.begin(); Listener *theListener = it->first; int ref = theListener->decRef(); log_debug("ListeningData::clearListeners():Removing %s, refcnt = %d\n", theListener->getListenerName().c_str(), theListener->refCnt()); if (ref == 0) { delete theListener; } listenerList.erase(it); } } bool isEmpty() const { return listenerList.size() == 0; } ListeningData(const NacosString &tenant, const NacosString &dataId, const NacosString &group, const NacosString &cfgcontent) { setContent(tenant, dataId, group, cfgcontent); } void setContent(const NacosString &tenant, const NacosString &dataId, const NacosString &group, const NacosString &cfgcontent) { this->tenant = tenant; this->dataId = dataId; this->group = group; if (!isNull(cfgcontent)) { MD5 md5;//TODO:optimize this to a static object to avoid repeatedly ctor&dtor of object md5.update(cfgcontent); this->dataMD5 = md5.toString(); } else { this->dataMD5 = ""; } } bool operator==(const ListeningData &rhs) const { return tenant == rhs.tenant && dataId == rhs.dataId && group == rhs.group; }; const std::map *getListenerList() const { return &this->listenerList; } }; }//namespace nacos #endif ================================================ FILE: src/listen/OperateItem.h ================================================ #ifndef __OPERATE_ITM_H_ #define __OPERATE_ITM_H_ #include "NacosString.h" #include "listen/Listener.h" /** * ListeningData * * @author Liu, Hanyu * Keeps track of the items to be operated on */ namespace nacos{ class OperateItem { private: NacosString tenant; NacosString dataId; NacosString group; Listener *listener; public: NacosString getDataId() const { return dataId; } NacosString getTenant() const { return tenant; } NacosString getGroup() const { return group; } Listener *getListener() const { return listener; } void setListener(Listener *listener) { this->listener = listener; } OperateItem(const NacosString &dataId, const NacosString &tenant, const NacosString &group, Listener *listener) { this->dataId = dataId; this->tenant = tenant; this->group = group; this->listener = listener; } NacosString toString() const { return tenant + ":" + group + ":" + dataId; } }; }//namespace nacos #endif ================================================ FILE: src/log/Logger.cpp ================================================ #include #include #include #include #include "Logger.h" #include "src/utils/TimeUtils.h" #include "NacosExceptions.h" #include "src/utils/ConfigParserUtils.h" #include "Properties.h" #include "src/utils/DirUtils.h" #include "src/utils/ParamUtils.h" #include "src/config/IOUtils.h" #include "constant/ConfigConstant.h" #include "constant/PropertyKeyConst.h" #include #include namespace nacos{ LOG_LEVEL Logger::_CUR_SYS_LOG_LEVEL = ERROR; NacosString Logger::_log_base_dir = ""; NacosString Logger::_log_file = ""; int64_t Logger::_rotate_size; int64_t Logger::_last_rotate_time; FILE *Logger::_output_file; Mutex Logger::setFileLock; //rotate time (in Ms) void Logger::setRotateSize(int64_t rotateSize) { _rotate_size = rotateSize; } void Logger::setBaseDir(const NacosString &baseDir) { LockGuard _setFile(setFileLock); _log_base_dir = baseDir; if (_output_file != NULL) { fclose(_output_file); _output_file = NULL; } _log_file = _log_base_dir + ConfigConstant::FILE_SEPARATOR + "nacos-sdk-cpp.log"; IOUtils::recursivelyCreate(_log_base_dir.c_str()); _output_file = fopen(_log_file.c_str(), "a"); if (_output_file == NULL) { NacosString errMsg = "Unable to open file "; errMsg += _log_file; throw NacosException(NacosException::UNABLE_TO_OPEN_FILE, errMsg); } } void Logger::setLogLevel(LOG_LEVEL level) { _CUR_SYS_LOG_LEVEL = level; }; int64_t Logger::getRotateSize() { return _rotate_size; } const NacosString &Logger::getBaseDir() { return _log_base_dir; } LOG_LEVEL Logger::getLogLevel() { return _CUR_SYS_LOG_LEVEL; } int Logger::debug_helper(LOG_LEVEL level, const char *format, va_list args) { //Since the current system debug level is greater than this message //Supress it if (Logger::_CUR_SYS_LOG_LEVEL > level) { return 0; } //va_list argList; //va_start(argList, format); int64_t now = TimeUtils::getCurrentTimeInMs(); struct stat stat_buf; stat(_log_file.c_str(), &stat_buf); if (stat_buf.st_size >= _rotate_size) { truncate(_log_file.c_str(), 0); _last_rotate_time = now; } const char *log_level; switch (level) { case DEBUG: log_level = "[DEBUG]"; break; case INFO: log_level = "[INFO]"; break; case WARN: log_level = "[WARN]"; break; case ERROR: log_level = "[ERROR]"; break; case NONE: log_level = "[NONE]"; break; default: log_level = "[UNKNOWN]"; break; } time_t t = time(0); struct tm current_time; localtime_r(&t, ¤t_time); //length of [9999-12-31 99:99:99] = 19 char time_buf[22]; strftime(time_buf, sizeof(time_buf), "[%Y-%m-%d %H:%M:%S]", ¤t_time); int retval = fprintf(_output_file, "%s%s", time_buf, log_level); retval += vfprintf(_output_file, format, args); fflush(_output_file); //va_end(argList); return retval; } //Output string in self-defined log_level int Logger::debug_print(LOG_LEVEL level, const char *format, ...) { va_list argList; va_start(argList, format); int retval = debug_helper(level, format, argList); va_end(argList); return retval; } int Logger::debug_debug(const char *format, ...) { va_list argList; va_start(argList, format); int retval = debug_helper(DEBUG, format, argList); va_end(argList); return retval; } int Logger::debug_info(const char *format, ...) { va_list argList; va_start(argList, format); int retval = debug_helper(INFO, format, argList); va_end(argList); return retval; } int Logger::debug_warn(const char *format, ...) { va_list argList; va_start(argList, format); int retval = debug_helper(WARN, format, argList); va_end(argList); return retval; } int Logger::debug_error(const char *format, ...) { va_list argList; va_start(argList, format); int retval = debug_helper(ERROR, format, argList); va_end(argList); return retval; } void Logger::deInit() { if (_output_file != NULL) { fclose(_output_file); _output_file = NULL; } } void Logger::initializeLogSystem() { Properties props; try { props = ConfigParserUtils::parseConfigFile(DirUtils::getCwd() + ConfigConstant::FILE_SEPARATOR + ConfigConstant::DEFAULT_CONFIG_FILE); } catch (IOException &e) { //if we failed to read log settings //use default settings as backup } applyLogSettings(props); } void Logger::applyLogSettings(Properties &props) { if (!props.contains(PropertyKeyConst::LOG_PATH)) { NacosString homedir = DirUtils::getHome(); Logger::setBaseDir(homedir + ConfigConstant::FILE_SEPARATOR + "nacos" + ConfigConstant::FILE_SEPARATOR + "logs"); } else { Logger::setBaseDir(props[PropertyKeyConst::LOG_PATH]); } if (props.contains(PropertyKeyConst::LOG_LEVEL)) { //default log level is error, if user specifies it within configuration file, update it NacosString &logLevelStr = props[PropertyKeyConst::LOG_LEVEL]; if (logLevelStr == "DEBUG") { Logger::setLogLevel(DEBUG); } else if (logLevelStr == "INFO") { Logger::setLogLevel(INFO); } else if (logLevelStr == "WARN") { Logger::setLogLevel(WARN); } else if (logLevelStr == "ERROR") { Logger::setLogLevel(ERROR); } else if (logLevelStr == "NONE") { Logger::setLogLevel(NONE); } else { throw NacosException(NacosException::INVALID_CONFIG_PARAM, "Invalid option " + logLevelStr + " for " + PropertyKeyConst::LOG_LEVEL); } } if (!props.contains(PropertyKeyConst::LOG_ROTATE_SIZE)) { Logger::setRotateSize(10 * 1024 *1024);//10M by default } else { const NacosString &logRotateSizeStr = props[PropertyKeyConst::LOG_ROTATE_SIZE]; if (ParamUtils::isBlank(logRotateSizeStr)) { throw NacosException(NacosException::INVALID_CONFIG_PARAM, "Invalid option '" + logRotateSizeStr + "' for " + PropertyKeyConst::LOG_ROTATE_SIZE); } size_t logrotate_lastch = logRotateSizeStr.length() - 1; int mulplier = 1; unsigned long logRotateSize = 0; switch (logRotateSizeStr[logrotate_lastch]) { case 'g': case 'G': mulplier *= 1024; case 'm': case 'M': mulplier *= 1024; case 'k': case 'K': mulplier *= 1024; logRotateSize = atol(logRotateSizeStr.substr(0, logrotate_lastch).c_str());//logrotate_lastch = exclude the unit logRotateSize *= mulplier; break; default: if (!isdigit(logRotateSizeStr[logrotate_lastch])) { throw NacosException(NacosException::INVALID_CONFIG_PARAM, "Invalid option '" + logRotateSizeStr + "' for " + PropertyKeyConst::LOG_ROTATE_SIZE + ", the unit of size must be G/g M/m K/k or decimal numbers."); } mulplier = 1; logRotateSize = atol(logRotateSizeStr.substr(0, logRotateSizeStr.length()).c_str()); break; } if (logRotateSize <= 0) { throw NacosException(NacosException::INVALID_CONFIG_PARAM, PropertyKeyConst::LOG_ROTATE_SIZE + " should be greater than 0"); } Logger::setRotateSize(logRotateSize); } log_info("Current log path:%s\n", Logger::getBaseDir().c_str()); } void Logger::Init() { initializeLogSystem(); } }//namespace nacos ================================================ FILE: src/log/Logger.h ================================================ #ifndef __LOGGER_H_ #define __LOGGER_H_ #include #include #include "NacosString.h" #include "src/thread/Mutex.h" #include "Properties.h" #define DETAILED_DEBUG_INFO //TODO:Line info #ifndef DETAILED_DEBUG_INFO #define log_print(level, format, args...) Logger::debug_print(level, format, ##args) #define log_debug(format, args...) Logger::debug_debug(format, ##args) #define log_info(format, args...) Logger::debug_info(format, ##args) #define log_warn(format, args...) Logger::debug_warn(format, ##args) #define log_error(format, args...) Logger::debug_error(format, ##args) #else #define STR(X) #X #define log_print(level, format, args...) Logger::debug_print(level, format, ##args) #define log_debug(format, args...) Logger::debug_debug(format, ##args) #define log_info(format, args...) Logger::debug_info(format, ##args) #define log_warn(format, args...) Logger::debug_warn(format, ##args) #define log_error(format, args...) Logger::debug_error(format, ##args) #endif namespace nacos{ enum LOG_LEVEL { DEBUG = 0, INFO, WARN, ERROR, NONE }; /** * Logger * Author: Liu, Hanyu * This piece of code is a little bit weird, I have to admit * Actually there are 2 stages which need logging: * 1. The nacos-client is up but no client service is created, need to log issues when creating client service object * 2. client service is created, now need to log issue for the client service * * stage #1 is called the bootstrap stage * stage #2 is called the runtime stage * * On stage #1, the log will be written to: * homedir(~)/nacos/logs/nacos-sdk-cpp.log * * On stage #2, the path can be configured AT MOST ONCE * The path will be implicitly configured when the first client service object is created, so if you don't specify it, the default setting(same as #1) will be used */ class Logger { private: static LOG_LEVEL _CUR_SYS_LOG_LEVEL; static NacosString _log_base_dir; static NacosString _log_file; static int64_t _rotate_size; static int64_t _last_rotate_time; static FILE *_output_file; static Mutex setFileLock; static int debug_helper(LOG_LEVEL level, const char *format, va_list args); static void initializeLogSystem(); public: static void applyLogSettings(Properties &props); static void setRotateSize(int64_t rotateSize); static void setBaseDir(const NacosString &baseDir); static void setLogLevel(LOG_LEVEL level); static int64_t getRotateSize(); static const NacosString &getBaseDir(); static LOG_LEVEL getLogLevel(); //Output string in self-defined log_level static int debug_print(LOG_LEVEL level, const char *format, ...); static int debug_debug(const char *format, ...); static int debug_info(const char *format, ...); static int debug_warn(const char *format, ...); static int debug_error(const char *format, ...); static void Init(); static void deInit(); }; }//namespace nacos #endif ================================================ FILE: src/naming/Cluster.cpp ================================================ #include "naming/Cluster.h" namespace nacos{ NacosString Cluster::getName() const { return name; } void Cluster::setName(const NacosString &name) { Cluster::name = name; } HealthChecker Cluster::getHealthChecker() const { return healthChecker; } void Cluster::setHealthChecker(const HealthChecker &healthChecker) { Cluster::healthChecker = healthChecker; } std::map Cluster::getMetadata() const { return metadata; } void Cluster::setMetadata(const std::map &metadata) { Cluster::metadata = metadata; } }//namespace nacos ================================================ FILE: src/naming/Instance.cpp ================================================ #include "naming/Instance.h" #include "src/utils/ParamUtils.h" #include "src/log/Logger.h" namespace nacos{ Instance::Instance() { weight = (double)1; healthy = true; enabled = true; ephemeral = true; instanceId = ""; ip = ""; clusterName = ""; serviceName = ""; port = 0; } NacosString Instance::toString() const{ return "instanceId:" + instanceId + " ip:" + ip + " port:" + NacosStringOps::valueOf(port) + " weight:" + NacosStringOps::valueOf(weight) + " healthy:" + NacosStringOps::valueOf(healthy) + " enabled:" + NacosStringOps::valueOf(enabled) + " ephemeral:" + NacosStringOps::valueOf(ephemeral) + " clusterName:" + clusterName + " serviceName:" + serviceName + " metadata:{" + ParamUtils::Implode(metadata) + "}"; } NacosString Instance::toInetAddr() { return ip + ":" + NacosStringOps::valueOf(port); } Instance & Instance::operator = (const Instance &rhs) { this->instanceId = rhs.instanceId; this->ip = rhs.ip; this->port = rhs.port; this->weight = rhs.weight; this->healthy = rhs.healthy; this->enabled = rhs.enabled; this->ephemeral = rhs.ephemeral; this->clusterName = rhs.clusterName; this->metadata = rhs.metadata; return *this; } bool Instance::operator != (const Instance &rhs) const { return !operator==(rhs); } bool Instance::operator == (const Instance &rhs) const { return this->instanceId == rhs.instanceId && this->ip == rhs.ip && this->port == rhs.port && this->weight == rhs.weight && this->healthy == rhs.healthy && this->enabled == rhs.enabled && this->ephemeral == rhs.ephemeral && this->clusterName == rhs.clusterName && this->metadata == rhs.metadata; } }//namespace nacos ================================================ FILE: src/naming/NacosNamingMaintainService.cpp ================================================ #include "src/naming/NacosNamingMaintainService.h" #include "src/security/SecurityManager.h" #include "constant/ConfigConstant.h" using namespace std; namespace nacos{ NacosNamingMaintainService::NacosNamingMaintainService(ObjectConfigData *objectConfigData) { _objectConfigData = objectConfigData; if (_objectConfigData->_appConfigManager->nacosAuthEnabled()) { _objectConfigData->_securityManager->login(); _objectConfigData->_securityManager->start(); } } bool NacosNamingMaintainService::updateInstance ( const NacosString &serviceName, const NacosString & groupName, const Instance &instance ) NACOS_THROW(NacosException) { Instance paramInstance = instance; paramInstance.serviceName = serviceName; paramInstance.groupName = groupName; return _objectConfigData->_serverProxy->updateServiceInstance(paramInstance); } ServiceInfo2 NacosNamingMaintainService::queryService ( const NacosString &serviceName, const NacosString &groupName ) NACOS_THROW(NacosException) { return _objectConfigData->_serverProxy->getServiceInfo(serviceName, groupName); } bool NacosNamingMaintainService::createService(const ServiceInfo2 &service, naming::Selector *selector) NACOS_THROW(NacosException) { ServiceInfo2 parmServiceInfo = service; if (!parmServiceInfo.isGroupNameSet()) { parmServiceInfo.setGroupName(ConfigConstant::DEFAULT_GROUP); } return _objectConfigData->_serverProxy->createServiceInfo(parmServiceInfo, selector); } bool NacosNamingMaintainService::deleteService(const NacosString &serviceName, const NacosString &groupName) NACOS_THROW(NacosException) { return _objectConfigData->_serverProxy->deleteServiceInfo(serviceName, groupName); } bool NacosNamingMaintainService::updateService(const ServiceInfo2 &service, naming::Selector *selector) NACOS_THROW(NacosException) { return _objectConfigData->_serverProxy->updateServiceInfo(service, selector); } NacosNamingMaintainService::~NacosNamingMaintainService() { delete _objectConfigData; } }//namespace nacos ================================================ FILE: src/naming/NacosNamingMaintainService.h ================================================ #ifndef __NACOS_NAM_MTN_SVC_H_ #define __NACOS_NAM_MTN_SVC_H_ #include "naming/NamingMaintainService.h" #include "naming/Instance.h" #include "src/naming/NamingProxy.h" #include "src/http/IHttpCli.h" #include "NacosString.h" #include "Properties.h" #include "src/factory/ObjectConfigData.h" #include "Compatibility.h" namespace nacos{ /** * NacosNamingMaintainService * Maintain functions for Nacos * * @author liaochuntao * @author Liu, Hanyu * Maintain service * Special thanks to @liaochuntao */ class NacosNamingMaintainService : public NamingMaintainService { private: NacosNamingMaintainService(); ObjectConfigData *_objectConfigData; public: NacosNamingMaintainService(ObjectConfigData *objectConfigData); bool updateInstance(const NacosString &serviceName, const NacosString & groupName, const Instance &instance) NACOS_THROW(NacosException); ServiceInfo2 queryService(const NacosString &serviceName, const NacosString &groupName) NACOS_THROW(NacosException); bool createService(const ServiceInfo2 &service, naming::Selector *selector) NACOS_THROW(NacosException); bool deleteService(const NacosString &serviceName, const NacosString &groupName) NACOS_THROW(NacosException); bool updateService(const ServiceInfo2 &service, naming::Selector *selector) NACOS_THROW(NacosException); virtual ~NacosNamingMaintainService(); }; }//namespace nacos #endif ================================================ FILE: src/naming/NacosNamingService.cpp ================================================ #include "src/naming/NacosNamingService.h" #include "src/naming/subscribe/SubscriptionPoller.h" #include "src/naming/subscribe/UdpNamingServiceListener.h" #include "src/naming/beat/BeatReactor.h" #include "src/utils/SequenceProvider.h" #include "src/utils/NamingUtils.h" #include "constant/UtilAndComs.h" #include "src/utils/ParamUtils.h" #include "constant/PropertyKeyConst.h" #include "src/json/JSON.h" using namespace std; using nacos::naming::selectors::Selector; namespace nacos{ NacosNamingService::NacosNamingService(ObjectConfigData *objectConfigData) { _objectConfigData = objectConfigData; _objectConfigData->_beatReactor->start(); _objectConfigData->_subscriptionPoller->start(); _objectConfigData->_udpNamingServiceListener->start(); if (_objectConfigData->_appConfigManager->nacosAuthEnabled()) { _objectConfigData->_securityManager->login(); _objectConfigData->_securityManager->start(); } } NacosNamingService::~NacosNamingService() { delete _objectConfigData; } void NacosNamingService::registerInstance ( const NacosString &serviceName, const NacosString &ip, int port ) NACOS_THROW(NacosException) { registerInstance(serviceName, ip, port, ConfigConstant::DEFAULT_CLUSTER_NAME); } void NacosNamingService::registerInstance ( const NacosString &serviceName, const NacosString &groupName, const NacosString &ip, int port ) NACOS_THROW(NacosException) { registerInstance(serviceName, groupName, ip, port, ConfigConstant::DEFAULT_CLUSTER_NAME); } void NacosNamingService::registerInstance ( const NacosString &serviceName, const NacosString &ip, int port, const NacosString &clusterName ) NACOS_THROW(NacosException) { registerInstance(serviceName, ConfigConstant::DEFAULT_GROUP, ip, port, clusterName); } void NacosNamingService::registerInstance ( const NacosString &serviceName, const NacosString &groupName, const NacosString &ip, int port, const NacosString &clusterName ) NACOS_THROW(NacosException) { Instance instance; instance.ip = ip; instance.port = port; instance.weight = (double)1; instance.clusterName = clusterName; registerInstance(serviceName, groupName, instance); } void NacosNamingService::registerInstance ( const NacosString &serviceName, Instance &instance ) NACOS_THROW(NacosException) { registerInstance(serviceName, ConfigConstant::DEFAULT_GROUP, instance); } void NacosNamingService::registerInstance ( const NacosString &serviceName, const NacosString &groupName, Instance &instance ) NACOS_THROW(NacosException) { const NacosString &instanceIdPrefix = _objectConfigData->_appConfigManager->get(PropertyKeyConst::INSTANCE_ID_PREFIX); instance.instanceId = instanceIdPrefix + NacosStringOps::valueOf(_objectConfigData->_sequenceProvider->next()); if (instance.ephemeral) { BeatInfo beatInfo; beatInfo.serviceName = NamingUtils::getGroupedName(serviceName, groupName); beatInfo.ip = instance.ip; beatInfo.port = instance.port; beatInfo.cluster = instance.clusterName; beatInfo.weight = instance.weight; beatInfo.metadata = instance.metadata; beatInfo.scheduled = false; _objectConfigData->_beatReactor->addBeatInfo(NamingUtils::getGroupedName(serviceName, groupName), beatInfo); } _objectConfigData->_serverProxy->registerService(NamingUtils::getGroupedName(serviceName, groupName), groupName, instance); } void NacosNamingService::deregisterInstance ( const NacosString &serviceName, const NacosString &ip, int port ) NACOS_THROW(NacosException) { deregisterInstance(serviceName, ip, port, ConfigConstant::DEFAULT_CLUSTER_NAME); } void NacosNamingService::deregisterInstance ( const NacosString &serviceName, const NacosString &groupName, const NacosString &ip, int port ) NACOS_THROW(NacosException) { deregisterInstance(serviceName, groupName, ip, port, ConfigConstant::DEFAULT_CLUSTER_NAME); } void NacosNamingService::deregisterInstance ( const NacosString &serviceName, const NacosString &ip, int port, const NacosString &clusterName ) NACOS_THROW(NacosException) { deregisterInstance(serviceName, ConfigConstant::DEFAULT_GROUP, ip, port, clusterName); } void NacosNamingService::deregisterInstance ( const NacosString &serviceName, const NacosString &groupName, const NacosString &ip, int port, const NacosString &clusterName ) NACOS_THROW(NacosException) { Instance instance; instance.ip = ip; instance.port = port; instance.clusterName = clusterName; deregisterInstance(serviceName, groupName, instance); } void NacosNamingService::deregisterInstance ( const NacosString &serviceName, const NacosString &groupName, Instance &instance ) NACOS_THROW(NacosException) { _objectConfigData->_beatReactor->removeBeatInfo(NamingUtils::getGroupedName(serviceName, groupName), instance.ip, instance.port); _objectConfigData->_serverProxy->deregisterService(NamingUtils::getGroupedName(serviceName, groupName), instance); } list NacosNamingService::getAllInstances ( const NacosString &serviceName ) NACOS_THROW(NacosException) { list clusters; return getAllInstances(serviceName, clusters); } list NacosNamingService::getAllInstances ( const NacosString &serviceName, const NacosString &groupName ) NACOS_THROW(NacosException) { list clusters; return getAllInstances(serviceName, groupName, clusters); } list NacosNamingService::getAllInstances ( const NacosString &serviceName, const list &clusters ) NACOS_THROW(NacosException) { return getAllInstances(serviceName, ConfigConstant::DEFAULT_GROUP, clusters); } list NacosNamingService::getAllInstances ( const NacosString &serviceName, const NacosString &groupName, const list &clusters ) NACOS_THROW(NacosException) { ServiceInfo serviceInfo; //TODO:cache and failover NacosString clusterString = ParamUtils::Implode(clusters); NacosString result = _objectConfigData->_serverProxy->queryList( serviceName, groupName, clusterString, 0/*non-zero value to receive subscription push from the server, set to 0 since we don't need it here*/, false); serviceInfo = JSON::JsonStr2ServiceInfo(result); list hostlist = serviceInfo.getHosts(); return hostlist; } void NacosNamingService::subscribe(const NacosString &serviceName, EventListener *listener) NACOS_THROW (NacosException) { list clusters;//empty cluster subscribe(serviceName, ConfigConstant::DEFAULT_GROUP, clusters, listener); } void NacosNamingService::subscribe ( const NacosString &serviceName, const NacosString &groupName, EventListener *listener ) NACOS_THROW (NacosException) { list clusters;//empty cluster subscribe(serviceName, groupName, clusters, listener); } void NacosNamingService::subscribe( const NacosString &serviceName, const std::list &clusters, EventListener *listener ) NACOS_THROW (NacosException) { subscribe(serviceName, ConfigConstant::DEFAULT_GROUP, clusters, listener); } void NacosNamingService::subscribe ( const NacosString &serviceName, const NacosString &groupName, const std::list &clusters, EventListener *listener ) NACOS_THROW (NacosException) { NacosString clusterName = ParamUtils::Implode(clusters); NacosString groupedName = NamingUtils::getGroupedName(serviceName, groupName); if (!_objectConfigData->_eventDispatcher->addListener(groupedName, clusterName, listener)){ return;//The listener is already listening to the service specified, no need to add to the polling list } _objectConfigData->_subscriptionPoller->addPollItem(serviceName, groupName, clusterName); } void NacosNamingService::unsubscribe( const NacosString &serviceName, const NacosString &groupName, const std::list &clusters, EventListener *listener ) NACOS_THROW (NacosException) { NacosString clusterName = ParamUtils::Implode(clusters); NacosString groupedName = NamingUtils::getGroupedName(serviceName, groupName); int remainingListener; if (!_objectConfigData->_eventDispatcher->removeListener(groupedName, clusterName, listener, remainingListener)) { return;//The listener is not in the list or it is already removed } if (remainingListener == 0) { //Since there's no more listeners listening to this service, remove it from the polling list _objectConfigData->_subscriptionPoller->removePollItem(serviceName, groupName, clusterName); } } void NacosNamingService::unsubscribe ( const NacosString &serviceName, const std::list &clusters, EventListener *listener ) NACOS_THROW (NacosException) { unsubscribe(serviceName, ConfigConstant::DEFAULT_GROUP, clusters, listener); } void NacosNamingService::unsubscribe ( const NacosString &serviceName, const NacosString &groupName, EventListener *listener ) NACOS_THROW (NacosException) { list clusters; unsubscribe(serviceName, groupName, clusters, listener); } void NacosNamingService::unsubscribe(const NacosString &serviceName, EventListener *listener) NACOS_THROW (NacosException) { list clusters; unsubscribe(serviceName, ConfigConstant::DEFAULT_GROUP, clusters, listener); } ListView NacosNamingService::getServiceList(int pageNo, int pageSize) NACOS_THROW (NacosException) { return _objectConfigData->_serverProxy->getServiceList(pageNo, pageSize, ConfigConstant::DEFAULT_GROUP); } ListView NacosNamingService::getServiceList(int pageNo, int pageSize, const NacosString &groupName) NACOS_THROW (NacosException){ return _objectConfigData->_serverProxy->getServiceList(pageNo, pageSize, groupName); } list NacosNamingService::getInstanceWithPredicate ( const NacosString &serviceName, const NacosString &groupName, const std::list &clusters, Selector *predicate ) NACOS_THROW(NacosException) { list allInstances = getAllInstances(serviceName, groupName, clusters); if (predicate) { return predicate->select(allInstances); } else { return allInstances; } } list NacosNamingService::getInstanceWithPredicate ( const NacosString &serviceName, const std::list &clusters, Selector *predicate ) NACOS_THROW(NacosException) { list allInstances = getAllInstances(serviceName, ConfigConstant::DEFAULT_GROUP, clusters); if (predicate) { return predicate->select(allInstances); } else { return allInstances; } } list NacosNamingService::getInstanceWithPredicate ( const NacosString &serviceName, const NacosString &groupName, Selector *predicate ) NACOS_THROW(NacosException) { list clusters; list allInstances = getAllInstances(serviceName, groupName, clusters); if (predicate) { return predicate->select(allInstances); } else { return allInstances; } } list NacosNamingService::getInstanceWithPredicate ( const NacosString &serviceName, Selector *predicate ) NACOS_THROW(NacosException) { list clusters; list allInstances = getAllInstances(serviceName, ConfigConstant::DEFAULT_GROUP, clusters); if (predicate) { return predicate->select(allInstances); } else { return allInstances; } } }//namespace nacos ================================================ FILE: src/naming/NacosNamingService.h ================================================ #ifndef __NACOS_NAM_SVC_H_ #define __NACOS_NAM_SVC_H_ #include "naming/NamingService.h" #include "naming/Instance.h" #include "src/naming/NamingProxy.h" #include "src/naming/subscribe/EventDispatcher.h" #include "src/naming/beat/BeatReactor.h" #include "src/http/IHttpCli.h" #include "NacosString.h" #include "Properties.h" #include "src/factory/ObjectConfigData.h" #include "Compatibility.h" namespace nacos{ class NacosNamingService : public NamingService { private: ObjectConfigData *_objectConfigData; NacosNamingService(); NacosString cacheDir; NacosString logName; public: NacosNamingService(ObjectConfigData *objectConfigData); ~NacosNamingService(); void registerInstance(const NacosString &serviceName, const NacosString &ip, int port) NACOS_THROW(NacosException); void registerInstance(const NacosString &serviceName, const NacosString &groupName, const NacosString &ip, int port) NACOS_THROW(NacosException); void registerInstance(const NacosString &serviceName, const NacosString &ip, int port, const NacosString &clusterName) NACOS_THROW(NacosException); void registerInstance(const NacosString &serviceName, const NacosString &groupName, const NacosString &ip, int port, const NacosString &clusterName) NACOS_THROW(NacosException); void registerInstance(const NacosString &serviceName, Instance &instance) NACOS_THROW(NacosException); void registerInstance(const NacosString &serviceName, const NacosString &groupName, Instance &instance) NACOS_THROW(NacosException); void deregisterInstance(const NacosString &serviceName, const NacosString &ip, int port) NACOS_THROW(NacosException); void deregisterInstance(const NacosString &serviceName, const NacosString &groupName, const NacosString &ip, int port) NACOS_THROW(NacosException); void deregisterInstance(const NacosString &serviceName, const NacosString &ip, int port, const NacosString &clusterName) NACOS_THROW(NacosException); void deregisterInstance(const NacosString &serviceName, const NacosString &groupName, const NacosString &ip, int port, const NacosString &clusterName) NACOS_THROW(NacosException); void deregisterInstance(const NacosString &serviceName, const NacosString &groupName, Instance &instance) NACOS_THROW(NacosException); std::list getAllInstances(const NacosString &serviceName) NACOS_THROW(NacosException); std::list getAllInstances(const NacosString &serviceName, const NacosString &groupName) NACOS_THROW(NacosException); std::list getAllInstances(const NacosString &serviceName, const std::list &clusters) NACOS_THROW(NacosException); std::list getAllInstances(const NacosString &serviceName, const NacosString &groupName, const std::list &clusters) NACOS_THROW(NacosException); void subscribe(const NacosString &serviceName, EventListener *listener) NACOS_THROW (NacosException); void subscribe(const NacosString &serviceName, const NacosString &groupName, const std::list &clusters, EventListener *listener) NACOS_THROW (NacosException); void subscribe(const NacosString &serviceName, const NacosString &groupName, EventListener *listener) NACOS_THROW (NacosException); void subscribe(const NacosString &serviceName, const std::list &clusters, EventListener *listener) NACOS_THROW (NacosException); void unsubscribe(const NacosString &serviceName, EventListener *listener) NACOS_THROW (NacosException); void unsubscribe(const NacosString &serviceName, const NacosString &groupName, EventListener *listener) NACOS_THROW (NacosException); void unsubscribe(const NacosString &serviceName, const std::list &clusters, EventListener *listener) NACOS_THROW (NacosException); void unsubscribe(const NacosString &serviceName, const NacosString &groupName, const std::list &clusters, EventListener *listener) NACOS_THROW (NacosException); ListView getServiceList(int pageNo, int pageSize) NACOS_THROW (NacosException); ListView getServiceList(int pageNo, int pageSize, const NacosString &groupName) NACOS_THROW (NacosException); std::list getInstanceWithPredicate(const NacosString &serviceName, const NacosString &groupName, const std::list &clusters, nacos::naming::selectors::Selector *predicate) NACOS_THROW(NacosException); std::list getInstanceWithPredicate(const NacosString &serviceName, const std::list &clusters, nacos::naming::selectors::Selector *predicate) NACOS_THROW(NacosException); std::list getInstanceWithPredicate(const NacosString &serviceName, const NacosString &groupName, nacos::naming::selectors::Selector *predicate) NACOS_THROW(NacosException); std::list getInstanceWithPredicate(const NacosString &serviceName, nacos::naming::selectors::Selector *predicate) NACOS_THROW(NacosException); IHttpCli *getHttpCli() const { return _objectConfigData->_httpCli; }; NamingProxy *getServerProxy() const { return _objectConfigData->_serverProxy; }; BeatReactor *getBeatReactor() const { return _objectConfigData->_beatReactor; }; void setHttpCli(IHttpCli *httpCli) { this->_objectConfigData->_httpCli = httpCli; }; void setServerProxy(NamingProxy *_namingProxy) { this->_objectConfigData->_serverProxy = _namingProxy; }; void setBeatReactor(BeatReactor *_beatReactor) { this->_objectConfigData->_beatReactor = _beatReactor; }; }; }//namespace nacos #endif ================================================ FILE: src/naming/NamingProxy.cpp ================================================ #include #include "NamingProxy.h" #include "constant/NamingConstant.h" #include "constant/UtilAndComs.h" #include "src/utils/UuidUtils.h" #include "src/utils/ParamUtils.h" #include "src/utils/url.h" #include "src/utils/RandomUtils.h" #include "src/json/JSON.h" #include "src/http/HttpStatus.h" #include "NacosExceptions.h" #include "src/crypto/SignatureTool.h" using namespace std; namespace nacos{ ListView NamingProxy::nullResult; NamingProxy::NamingProxy(ObjectConfigData *objectConfigData) { _objectConfigData = objectConfigData; log_debug("NamingProxy Constructor:\n" "namespace:%s, endpoint:%s, Servers:%s\n", objectConfigData->_serverListManager->getNamespace().c_str(), objectConfigData->_serverListManager->getEndpoint().c_str(), objectConfigData->_serverListManager->toString().c_str()); serverPort = "8848"; if (objectConfigData->_serverListManager->getServerCount() == 1) { nacosDomain = objectConfigData->_serverListManager->getServerList().begin()->getCompleteAddress(); } log_debug("The serverlist:%s\n", objectConfigData->_serverListManager->toString().c_str()); nullResult.setCount(0); _hb_fail_wait = atoi(objectConfigData->_appConfigManager->get(PropertyKeyConst::HB_FAIL_WAIT_TIME).c_str()); } NamingProxy::~NamingProxy() { } void NamingProxy::registerService(const NacosString &serviceName, const NacosString &groupName, Instance &instance) NACOS_THROW(NacosException) { log_info("[REGISTER-SERVICE] %s registering service %s with instance: %s\n", getNamespaceId().c_str(), serviceName.c_str(), instance.toString().c_str()); list params; ParamUtils::addKV(params, NamingConstant::NAMESPACE_ID, getNamespaceId()); ParamUtils::addKV(params, NamingConstant::SERVICE_NAME, serviceName); ParamUtils::addKV(params, NamingConstant::GROUP_NAME, groupName); ParamUtils::addKV(params, NamingConstant::CLUSTER_NAME, instance.clusterName); ParamUtils::addKV(params, "ip", instance.ip); ParamUtils::addKV(params, "port", NacosStringOps::valueOf(instance.port)); ParamUtils::addKV(params, "weight", NacosStringOps::valueOf(instance.weight)); ParamUtils::addKV(params, "enable", NacosStringOps::valueOf(instance.enabled)); ParamUtils::addKV(params, NamingConstant::HEALTHY, NacosStringOps::valueOf(instance.healthy)); ParamUtils::addKV(params, "ephemeral", NacosStringOps::valueOf(instance.ephemeral)); ParamUtils::addKV(params, "metadata", JSON::toJSONString(instance.metadata)); reqAPI("/" + _objectConfigData->_appConfigManager->getContextPath() + UtilAndComs::NACOS_URL_INSTANCE, params, IHttpCli::POST); } void NamingProxy::deregisterService(const NacosString &serviceName, Instance &instance) NACOS_THROW(NacosException) { log_info("[DEREGISTER-SERVICE] %s deregistering service %s with instance: %s\n", getNamespaceId().c_str(), serviceName.c_str(), instance.toString().c_str()); list params; ParamUtils::addKV(params, NamingConstant::NAMESPACE_ID, getNamespaceId()); ParamUtils::addKV(params, NamingConstant::SERVICE_NAME, serviceName); ParamUtils::addKV(params, NamingConstant::CLUSTER_NAME, instance.clusterName); ParamUtils::addKV(params, "ip", instance.ip); ParamUtils::addKV(params, "port", NacosStringOps::valueOf(instance.port)); ParamUtils::addKV(params, "ephemeral", NacosStringOps::valueOf(instance.ephemeral)); reqAPI("/" + _objectConfigData->_appConfigManager->getContextPath() + UtilAndComs::NACOS_URL_INSTANCE, params, IHttpCli::DELETE); } NacosString NamingProxy::queryList(const NacosString &serviceName, const NacosString &groupName, const NacosString &clusters, int udpPort, bool healthyOnly) NACOS_THROW(NacosException) { list params; const NacosString &localIp = _objectConfigData->_appConfigManager->get(PropertyKeyConst::LOCAL_IP); ParamUtils::addKV(params, NamingConstant::NAMESPACE_ID, getNamespaceId()); ParamUtils::addKV(params, NamingConstant::SERVICE_NAME, serviceName); ParamUtils::addKV(params, NamingConstant::GROUP_NAME, groupName); ParamUtils::addKV(params, NamingConstant::CLUSTERS, clusters); ParamUtils::addKV(params, NamingConstant::UDP_PORT, NacosStringOps::valueOf(udpPort)); ParamUtils::addKV(params, NamingConstant::CLIENT_IP, localIp); ParamUtils::addKV(params, NamingConstant::HEALTHY_ONLY, NacosStringOps::valueOf(healthyOnly)); return reqAPI("/" + _objectConfigData->_appConfigManager->getContextPath() + UtilAndComs::NACOS_URL_BASE + "/instance/list", params, IHttpCli::GET); } NacosString NamingProxy::reqAPI(const NacosString &api, list ¶ms, int method) NACOS_THROW(NacosException) { ServerListManager *_serverListManager = _objectConfigData->_serverListManager; list servers = _serverListManager->getServerList(); if (_serverListManager->getServerCount() == 0) { throw NacosException(NacosException::NO_SERVER_AVAILABLE, "no server available"); } NacosString errmsg; if (!servers.empty()) { size_t maxSvrSlot = servers.size(); log_debug("nr_servers:%d\n", maxSvrSlot); size_t selectedServer = RandomUtils::random(0, maxSvrSlot) % maxSvrSlot; log_debug("selected_server:%d\n", selectedServer); for (size_t i = 0; i < servers.size(); i++) { const NacosServerInfo &server = ParamUtils::getNthElem(servers, selectedServer); log_debug("Trying to access server:%s\n", server.toString().c_str()); try { return callServer(api, params, server.getCompleteAddress(), method); } catch (NacosException &e) { errmsg = e.what(); log_error("request %s failed.\n", server.toString().c_str()); } catch (exception &e) { errmsg = e.what(); log_error("request %s failed.\n", server.toString().c_str()); } selectedServer = (selectedServer + 1) % servers.size(); } throw NacosException(NacosException::ALL_SERVERS_TRIED_AND_FAILED, "failed to req API:" + api + " after all servers(" + _serverListManager->toString() + ") tried: " + errmsg); } for (int i = 0; i < UtilAndComs::REQUEST_DOMAIN_RETRY_COUNT; i++) { try { return callServer(api, params, nacosDomain); } catch (exception &e) { errmsg = e.what(); log_error("[NA] req api:%s failed, server(%s), e = %s\n", api.c_str(), nacosDomain.c_str(), e.what()); } } throw NacosException(NacosException::ALL_SERVERS_TRIED_AND_FAILED, "failed to req API:/api/" + api + " after all servers(" + _serverListManager->toString() + ") tried: " + errmsg); } NacosString NamingProxy::callServer ( const NacosString &api, list ¶ms, const NacosString &curServer ) NACOS_THROW(NacosException) { return callServer(api, params, nacosDomain, IHttpCli::GET); } NacosString NamingProxy::getDataToSign(const std::list ¶mValues, NacosString &nowTimeMs) { const NacosString &serviceName = ParamUtils::findByKey(paramValues, NamingConstant::SERVICE_NAME); const NacosString &groupName = ParamUtils::findByKey(paramValues, NamingConstant::GROUP_NAME); NacosString dataToSign = ""; if ((!ParamUtils::isBlank(serviceName) && serviceName.find(NamingConstant::SPLITER) != std::string::npos) || ParamUtils::isBlank(groupName)) { dataToSign = nowTimeMs + NamingConstant::SPLITER + serviceName; } else { dataToSign = nowTimeMs + NamingConstant::SPLITER + groupName + NamingConstant::SPLITER + serviceName; } return dataToSign; } NacosString NamingProxy::callServer ( const NacosString &api, list ¶ms, const NacosString &curServer, int method ) NACOS_THROW(NacosException) { NacosString requestUrl; //Current server address doesn't have SERVER_ADDR_IP_SPLITER, which means if (!ParamUtils::contains(curServer, UtilAndComs::SERVER_ADDR_IP_SPLITER)) { requestUrl = curServer + UtilAndComs::SERVER_ADDR_IP_SPLITER + serverPort; } else { requestUrl = curServer; } //TODO:http/https implementation requestUrl = requestUrl + api; HttpResult requestRes; list headers; headers = builderHeaders(); //SPAS security NacosString secretKey = _objectConfigData->_appConfigManager->get(PropertyKeyConst::SECRET_KEY); NacosString accessKey = _objectConfigData->_appConfigManager->get(PropertyKeyConst::ACCESS_KEY); //If SPAS security credentials are set, SPAS is enabled if (!ParamUtils::isBlank(secretKey) && !ParamUtils::isBlank(accessKey)) { NacosString nowTimeMs = NacosStringOps::valueOf(TimeUtils::getCurrentTimeInMs()); NacosString dataToSign = getDataToSign(params, nowTimeMs); NacosString signature = SignatureTool::SignWithHMAC_SHA1(dataToSign, secretKey); //inject security credentials if (method == IHttpCli::GET || method == IHttpCli::DELETE) { ParamUtils::addKV(params, "signature", signature); ParamUtils::addKV(params, "data", dataToSign); ParamUtils::addKV(params, "ak", accessKey); } else { requestUrl = requestUrl + "?signature=" + urlencode(signature) + "&data=" + dataToSign + "&ak=" + accessKey; } } HttpDelegate *_httpDelegate = _objectConfigData->_httpDelegate; try { long _http_req_timeout = _objectConfigData->_appConfigManager->getServeReqTimeout(); switch (method) { case IHttpCli::GET: requestRes = _httpDelegate->httpGet(requestUrl, headers, params, UtilAndComs::ENCODING, _http_req_timeout); break; case IHttpCli::PUT: requestRes = _httpDelegate->httpPut(requestUrl, headers, params, UtilAndComs::ENCODING, _http_req_timeout); break; case IHttpCli::POST: requestRes = _httpDelegate->httpPost(requestUrl, headers, params, UtilAndComs::ENCODING, _http_req_timeout); break; case IHttpCli::DELETE: requestRes = _httpDelegate->httpDelete(requestUrl, headers, params, UtilAndComs::ENCODING, _http_req_timeout); break; } } catch (NetworkException &e) { NacosString errMsg = "Failed to request server, "; errMsg += e.what(); throw NacosException(NacosException::SERVER_ERROR, errMsg); } if (requestRes.code == HttpStatus::HTTP_OK) { return requestRes.content; } if (requestRes.code == HttpStatus::HTTP_NOT_MODIFIED) { return NULLSTR; } //TODO:Metrics & Monitoring throw NacosException(requestRes.code, "failed to req API:" + requestUrl + " code:" + NacosStringOps::valueOf(requestRes.code) + " errormsg:" + requestRes.content); } inline NacosString NamingProxy::getNamespaceId() { return _objectConfigData->_appConfigManager->get(PropertyKeyConst::NAMESPACE); } list NamingProxy::builderHeaders() { list headers; headers.push_back("Client-Version"); headers.push_back(UtilAndComs::VERSION); headers.push_back("Accept-Encoding"); headers.push_back("gzip,deflate,sdch"); headers.push_back("Connection"); headers.push_back("Keep-Alive"); headers.push_back("RequestId"); headers.push_back(UuidUtils::generateUuid()); headers.push_back("Request-Module"); headers.push_back("Naming"); return headers; } long NamingProxy::sendBeat(BeatInfo &beatInfo) { try { NacosString beatInfoStr = beatInfo.toString(); log_info("[BEAT] %s sending beat to server: %s\n", getNamespaceId().c_str(), beatInfoStr.c_str()); list params; ParamUtils::addKV(params, NamingConstant::BEAT, JSON::toJSONString(beatInfo)); ParamUtils::addKV(params, NamingConstant::NAMESPACE_ID, getNamespaceId()); ParamUtils::addKV(params, NamingConstant::SERVICE_NAME, beatInfo.serviceName); NacosString result = reqAPI("/" + _objectConfigData->_appConfigManager->getContextPath() + UtilAndComs::NACOS_URL_BASE + "/instance/beat", params, IHttpCli::PUT); //JSONObject jsonObject = JSON.parseObject(result); if (!isNull(result)) { return JSON::getLong(result, "clientBeatInterval"); } } catch (NacosException &e) { NacosString jsonBeatInfo = JSON::toJSONString(beatInfo); log_error("[CLIENT-BEAT] failed to send beat: %s e:%s\n", jsonBeatInfo.c_str(), e.what()); return _hb_fail_wait; } return 0L; } ListView NamingProxy::getServiceList(int page, int pageSize, const NacosString &groupName) NACOS_THROW(NacosException) { log_debug("[NAMEPRXY] request:group=%s page=%d pageSize=%d\n", groupName.c_str(), page, pageSize); list params; ParamUtils::addKV(params, NamingConstant::PAGE_NO, NacosStringOps::valueOf(page)); ParamUtils::addKV(params, NamingConstant::PAGE_SIZE, NacosStringOps::valueOf(pageSize)); ParamUtils::addKV(params, NamingConstant::GROUP_NAME, groupName); ParamUtils::addKV(params, NamingConstant::NAMESPACE_ID, getNamespaceId()); NacosString result = reqAPI("/" + _objectConfigData->_appConfigManager->getContextPath() + UtilAndComs::NACOS_URL_BASE + "/service/list", params, IHttpCli::GET); if (!isNull(result)) { return JSON::Json2ServiceList(result); } return nullResult; } ServiceInfo2 NamingProxy::getServiceInfo(const NacosString &serviceName, const NacosString &groupName) NACOS_THROW(NacosException) { log_debug("[NAMEPRXY] getServiceInfo request:serviceName=%s groupName=%s\n", serviceName.c_str(), groupName.c_str()); list params; ParamUtils::addKV(params, NamingConstant::SERVICE_NAME, serviceName); if (!NacosStringOps::isNullStr(groupName)) { ParamUtils::addKV(params, NamingConstant::GROUP_NAME, groupName); } else { ParamUtils::addKV(params, NamingConstant::GROUP_NAME, ConfigConstant::DEFAULT_GROUP); } ParamUtils::addKV(params, NamingConstant::NAMESPACE_ID, getNamespaceId()); NacosString result = reqAPI("/" + _objectConfigData->_appConfigManager->getContextPath() + UtilAndComs::NACOS_URL_BASE + "/service", params, IHttpCli::GET); log_debug("NamingProxy::getServiceInfo: service info from server:%s\n", result.c_str()); if (!isNull(result)) { return JSON::Json2ServiceInfo2(result); } return ServiceInfo2::nullServiceInfo2; } bool areYouOk(const NacosString &imVeryOk) { if (imVeryOk.compare("ok") == 0) { return true; } else { return false; } } /** * removes a service info from nacos server * please note that the operation will succeed ONLY WHEN there's NO instance of the service specified * * @param serviceName service name * @param groupName group name * @return true if operation succeeds * otherwise return false * @throws IOException IOException */ bool NamingProxy::deleteServiceInfo(const NacosString &serviceName, const NacosString &groupName) NACOS_THROW(NacosException) { list params; ParamUtils::addKV(params, NamingConstant::SERVICE_NAME, serviceName); ParamUtils::addKV(params, NamingConstant::GROUP_NAME, groupName); ParamUtils::addKV(params, NamingConstant::NAMESPACE_ID, getNamespaceId()); NacosString result = reqAPI("/" + _objectConfigData->_appConfigManager->getContextPath() + UtilAndComs::NACOS_URL_BASE + "/service", params, IHttpCli::DELETE); return areYouOk(result); } void assembleServiceInfoRequest(list &target, const ServiceInfo2 &serviceInfo2) { if (serviceInfo2.isNameSet()) { ParamUtils::addKV(target, NamingConstant::SERVICE_NAME, serviceInfo2.getName()); } if (serviceInfo2.isGroupNameSet()) { ParamUtils::addKV(target, NamingConstant::GROUP_NAME, serviceInfo2.getGroupName()); } if (serviceInfo2.isNamespaceIdSet()) { ParamUtils::addKV(target, NamingConstant::NAMESPACE_ID, serviceInfo2.getNamespaceId()); } if (serviceInfo2.isProtectThresholdSet()) { ParamUtils::addKV(target, "protectThreshold", NacosStringOps::valueOf(serviceInfo2.getProtectThreshold())); } if (serviceInfo2.isMetadataSet()) { ParamUtils::addKV(target, "metadata", JSON::toJSONString(serviceInfo2.getMetadata())); } } bool NamingProxy::createServiceInfo(const ServiceInfo2 &serviceInfo2, naming::Selector *selector) NACOS_THROW(NacosException) { list params; assembleServiceInfoRequest(params, serviceInfo2); if (selector) { ParamUtils::addKV(params, "selector", selector->getSelectorString()); } NacosString result = reqAPI("/" + _objectConfigData->_appConfigManager->getContextPath() + UtilAndComs::NACOS_URL_BASE + "/service", params, IHttpCli::POST); return areYouOk(result); } bool NamingProxy::updateServiceInfo(const ServiceInfo2 &serviceInfo2, naming::Selector *selector) NACOS_THROW(NacosException) { list params; assembleServiceInfoRequest(params, serviceInfo2); if (selector) { ParamUtils::addKV(params, "selector", selector->getSelectorString()); } NacosString result = reqAPI("/" + _objectConfigData->_appConfigManager->getContextPath() + UtilAndComs::NACOS_URL_BASE + "/service", params, IHttpCli::PUT); return areYouOk(result); } bool NamingProxy::serverHealthy() { list params; NacosString result = reqAPI("/" + _objectConfigData->_appConfigManager->getContextPath() + UtilAndComs::NACOS_URL_BASE + "/operator/metrics", params, IHttpCli::GET); NacosString healthyTag = "status\":\""; size_t pos = result.find(healthyTag); if (pos == std::string::npos) { return false; } NacosString healthy = result.substr(pos + healthyTag.length(), 2); return healthy == "UP"; } /** * gets one service instance info from nacos server * * @param serviceName service name * @param ip ip address * @param port port * @param params optional parameters, options are: * groupName * namespaceId * cluster * healthyOnly * ephemeral * @return Instance info if succeed, nullObj if fails * @throws NacosException NacosException */ Instance NamingProxy::getServiceInstance ( const NacosString &serviceName, const NacosString &ip, int port, const std::map ¶ms ) NACOS_THROW(NacosException) { list paramsList; ParamUtils::addKV(paramsList, NamingConstant::SERVICE_NAME, serviceName); ParamUtils::addKV(paramsList, "ip", ip); ParamUtils::addKV(paramsList, "port", NacosStringOps::valueOf(port)); for (map::const_iterator it = params.begin(); it != params.end(); it++) { ParamUtils::addKV(paramsList, it->first, it->second); } if (params.count(NamingConstant::NAMESPACE_ID) == 0) { ParamUtils::addKV(paramsList, NamingConstant::NAMESPACE_ID, getNamespaceId()); } if (params.count(NamingConstant::GROUP_NAME) == 0) { ParamUtils::addKV(paramsList, NamingConstant::GROUP_NAME, ConfigConstant::DEFAULT_GROUP); } NacosString result = reqAPI("/" + _objectConfigData->_appConfigManager->getContextPath() + UtilAndComs::NACOS_URL_BASE + "/instance", paramsList, IHttpCli::GET); return JSON::Json2Instance(result); } bool NamingProxy::updateServiceInstance(const Instance &instance) NACOS_THROW(NacosException) { list params; ParamUtils::addKV(params, NamingConstant::SERVICE_NAME, instance.serviceName); ParamUtils::addKV(params, "ip", instance.ip); ParamUtils::addKV(params, "port", NacosStringOps::valueOf(instance.port)); if (NacosStringOps::isNullStr(instance.groupName)) { ParamUtils::addKV(params, NamingConstant::GROUP_NAME, ConfigConstant::DEFAULT_GROUP); } else { ParamUtils::addKV(params, NamingConstant::GROUP_NAME, instance.groupName); } if (NacosStringOps::isNullStr(instance.namespaceId)) { ParamUtils::addKV(params, NamingConstant::NAMESPACE_ID, getNamespaceId()); } else { ParamUtils::addKV(params, NamingConstant::NAMESPACE_ID, instance.namespaceId); } if (!NacosStringOps::isNullStr(instance.clusterName)) { ParamUtils::addKV(params, NamingConstant::CLUSTER_NAME, instance.clusterName); } if (instance.metadata.size() > 0) { ParamUtils::addKV(params, "metadata", JSON::toJSONString(instance.metadata)); } ParamUtils::addKV(params, "ephemeral", NacosStringOps::valueOf(instance.ephemeral)); //TODO:weight is optional ParamUtils::addKV(params, "weight", NacosStringOps::valueOf(instance.weight)); NacosString result = reqAPI("/" + _objectConfigData->_appConfigManager->getContextPath() + UtilAndComs::NACOS_URL_BASE + "/instance", params, IHttpCli::PUT); return areYouOk(result); } }//namespace nacos ================================================ FILE: src/naming/NamingProxy.h ================================================ #ifndef __NAMING_PROXY_H_ #define __NAMING_PROXY_H_ #include #include #include "NacosString.h" #include "NacosExceptions.h" #include "src/http/HttpDelegate.h" #include "naming/Instance.h" #include "src/naming/beat/BeatInfo.h" #include "src/server/ServerListManager.h" #include "naming/ListView.h" #include "naming/ServiceInfo2.h" #include "src/factory/ObjectConfigData.h" #include "Compatibility.h" namespace nacos{ class NamingProxy { private: NacosString serverPort; ObjectConfigData *_objectConfigData; NacosString nacosDomain; NamingProxy(); NacosString reqAPI(const NacosString &api, std::list ¶ms, int method) NACOS_THROW(NacosException); NacosString callServer(const NacosString &api, std::list ¶ms, const NacosString &curServer, int method) NACOS_THROW(NacosException); NacosString callServer(const NacosString &api, std::list ¶ms, const NacosString &curServer) NACOS_THROW(NacosException); std::list builderHeaders(); long _hb_fail_wait;//Time to wait when a heartbeat request fails (in ms) static ListView nullResult; NacosString getDataToSign(const std::list ¶mValues, NacosString &nowTimeMs); public: NamingProxy(ObjectConfigData *objectConfigData); ~NamingProxy(); //instance CRUD void registerService(const NacosString &serviceName, const NacosString &groupName, Instance &instance) NACOS_THROW(NacosException); Instance getServiceInstance(const NacosString &serviceName, const NacosString &ip, int port, const std::map ¶ms) NACOS_THROW(NacosException); bool updateServiceInstance(const Instance &instance) NACOS_THROW(NacosException); void deregisterService(const NacosString &serviceName, Instance &instance) NACOS_THROW(NacosException); NacosString queryList(const NacosString &serviceName, const NacosString &groupName, const NacosString &clusters, int udpPort, bool healthyOnly) NACOS_THROW(NacosException); //service CRUD ListView getServiceList(int page, int pageSize, const NacosString &groupName) NACOS_THROW(NacosException); ServiceInfo2 getServiceInfo(const NacosString &serviceName, const NacosString &groupName) NACOS_THROW(NacosException); bool deleteServiceInfo(const NacosString &serviceName, const NacosString &groupName) NACOS_THROW(NacosException); bool createServiceInfo(const ServiceInfo2 &serviceInfo2, naming::Selector *selector) NACOS_THROW(NacosException); bool updateServiceInfo(const ServiceInfo2 &serviceInfo2, naming::Selector *selector) NACOS_THROW(NacosException); inline NacosString getNamespaceId(); long sendBeat(BeatInfo &beatInfo); bool serverHealthy(); }; }//namespace nacos #endif ================================================ FILE: src/naming/ServiceInfo.cpp ================================================ #include "naming/ServiceInfo.h" #include #include #include #include "NacosString.h" #include "constant/ConfigConstant.h" #include "src/utils/url.h" #include "src/utils/ParamUtils.h" #include "naming/Instance.h" namespace nacos{ ServiceInfo::ServiceInfo() : _jsonFromServer(""), _cacheMillis(1000L), _lastRefTime(0L), _checksum(""), _allIPs(false) { } bool ServiceInfo::isAllIPs() const{ return _allIPs; } void ServiceInfo::setAllIPs(bool allIPs) { _allIPs = allIPs; } ServiceInfo::ServiceInfo(const NacosString &key) : _jsonFromServer(""), _cacheMillis(1000L), _lastRefTime(0L), _checksum(""), _allIPs(false) { std::vector segs; ParamUtils::Explode(segs, key, ConfigConstant::SERVICE_INFO_SPLITER); if (segs.size() == 2) { setGroupName(segs[0]); setName(segs[1]); } else if (segs.size() == 3) { setGroupName(segs[0]); setName(segs[1]); setClusters(segs[2]); } } ServiceInfo::ServiceInfo(const NacosString &name, const NacosString &clusters) { _name = name; _clusters = clusters; } int ServiceInfo::ipCount() { return _hosts.size(); } bool ServiceInfo::expired() const{ //TODO:extract this snippet to a common util struct timeval tp; gettimeofday(&tp, NULL); long int ms = tp.tv_sec * 1000 + tp.tv_usec / 1000; return ms - _lastRefTime > _cacheMillis; } void ServiceInfo::setHosts(std::list &hosts) { _hosts = hosts; } bool ServiceInfo::isValid() { return _hosts.size() > 0; } NacosString ServiceInfo::getName() { return _name; } void ServiceInfo::setName(const NacosString &name) { _name = name; } NacosString ServiceInfo::getGroupName() { return _groupName; } void ServiceInfo::setGroupName(const NacosString &groupName) { _groupName = groupName; } void ServiceInfo::setLastRefTime(long lastRefTime) { _lastRefTime = lastRefTime; } long ServiceInfo::getLastRefTime() { return _lastRefTime; } NacosString ServiceInfo::getClusters() { return _clusters; } void ServiceInfo::setClusters(const NacosString &clusters) { _clusters = clusters; } long ServiceInfo::getCacheMillis() { return _cacheMillis; } void ServiceInfo::setCacheMillis(long cacheMillis) { _cacheMillis = cacheMillis; } std::list ServiceInfo::getHosts() { return _hosts; } std::list *ServiceInfo::getHostsNocopy() { return &_hosts; } bool ServiceInfo::validate() const{ if (isAllIPs()) { return true; } //TODO: Idk what does this mean in Java, ignore in C++ /*std::list validHosts; for (std::list::iterator it = _hosts.begin() it != _hosts.end(); it++) { if (it->isHealthy()) { continue; } for (int i = 0; i < it->getWeight(); i++) { validHosts.push_back(*it); } }*/ return true; } //@JSONField(serialize = false) NacosString ServiceInfo::getJsonFromServer() const{ return _jsonFromServer; } void ServiceInfo::setJsonFromServer(const NacosString &jsonFromServer) { _jsonFromServer = jsonFromServer; } //@JSONField(serialize = false) NacosString ServiceInfo::getKey() const{ return getKey(_name, _clusters); } //@JSONField(serialize = false) NacosString ServiceInfo::getKeyEncoded() const{ return getKey(urlencode(_name), _clusters); } //@JSONField(serialize = false) void ServiceInfo::fromKey(ServiceInfo &serviceInfo, const NacosString &key) { std::vector segs; ParamUtils::Explode(segs, key, ConfigConstant::SERVICE_INFO_SPLITER); if (segs.size() == 2) { serviceInfo.setGroupName(segs[0]); serviceInfo.setName(segs[1]); } else if (segs.size() == 3) { serviceInfo.setGroupName(segs[0]); serviceInfo.setName(segs[1]); serviceInfo.setClusters(segs[2]); } } //@JSONField(serialize = false) NacosString ServiceInfo::getKey(const NacosString &name, const NacosString &clusters) { if (!ParamUtils::isBlank(clusters)) { return name + ConfigConstant::SERVICE_INFO_SPLITER + clusters; } return name; } //@Override NacosString ServiceInfo::toString() const{ return getKey(); } //!!BE CAREFUL!! //This function is very expensive!! call it with care! NacosString ServiceInfo::toInstanceString() const{ NacosString res = "{\"lastRefTime\":" + NacosStringOps::valueOf(_lastRefTime) + " [\n"; for (std::list::const_iterator it = _hosts.begin(); it != _hosts.end(); it++) { res += it->toString() + "\n"; } res += "\n]}"; return res; } NacosString ServiceInfo::getChecksum() const{ return _checksum; } void ServiceInfo::setChecksum(const NacosString &checksum) { _checksum = checksum; } } ================================================ FILE: src/naming/beat/BeatInfo.cpp ================================================ #include "src/json/JSON.h" #include "BeatInfo.h" #include "NacosString.h" namespace nacos{ NacosString BeatInfo::toString() { return JSON::toJSONString(*this); } }//namespace nacos ================================================ FILE: src/naming/beat/BeatInfo.h ================================================ #ifndef __BEAT_INFO_H_ #define __BEAT_INFO_H_ #include #include "NacosString.h" /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * @author nkorange */ namespace nacos{ class BeatInfo { public: int port; NacosString ip; double weight; NacosString serviceName; NacosString cluster; std::map metadata; volatile bool scheduled; volatile long nextHbTime; NacosString toString(); }; }//namespace nacos #endif ================================================ FILE: src/naming/beat/BeatReactor.cpp ================================================ #include #include "BeatReactor.h" #include "BeatTask.h" #include "NacosString.h" #include "src/debug/DebugAssertion.h" using namespace std; namespace nacos{ void BeatReactor::start() { _stop = false; _delayedThreadPool->start(); } void BeatReactor::stop() { if (_stop) { log_warn("Calling BeatReactor::stop() on an already-stopped BeatReactor\n"); return; } _stop = true; _delayedThreadPool->stop(); } void BeatReactor::addBeatInfo(const NacosString &serviceName, BeatInfo &beatInfo) { NacosString beatInfoStr = beatInfo.toString(); log_info("[BEAT] adding beat: %s to beat map.\n", beatInfoStr.c_str()); NacosString beatKey = buildKey(serviceName, beatInfo.ip, beatInfo.port); BeatTask *newBeatTask; { WriteGuard _lockguard(_beatInfoLock); //The specified beatInfo is already in the list if (_beatInfoList.count(beatKey) != 0) { log_warn("Adding already-exist key:%s\n", beatKey.c_str()); return; } newBeatTask = new BeatTask(beatInfo, _objectConfigData); newBeatTask->setScheduled(true); newBeatTask->setTaskName(beatKey); newBeatTask->setInterval(_clientBeatInterval); _beatInfoList[beatKey] = newBeatTask; } _delayedThreadPool->schedule(newBeatTask, TimeUtils::getCurrentTimeInMs() + _clientBeatInterval); //TODO:MetricsMonitor.getDom2BeatSizeMonitor().set(dom2Beat.size()); } /** * Looks up a beatInfo and return it via beatInfo * @param serviceName the service name * @param ip the service's ip * @param port the service's port * @param beatInfo the beatInfo, ip and port must be set and cannot be changed via this function * @return false if beatInfo can't be found with designated serviceName, ip and port * true if beatInfo is found * the BeatInfo is returned via beatInfo parameter */ bool BeatReactor::getBeatInfo(const NacosString &serviceName, const NacosString &ip, int port, BeatInfo &beatInfo) { NacosString beatKey = buildKey(serviceName, ip, port); { ReadGuard _lockguard(_beatInfoLock); if (_beatInfoList.count(beatKey) == 0) { return false; } beatInfo = _beatInfoList[beatKey]->getBeatInfo(); return true; } } /** * Modifies a beatInfo * @param serviceName the service name * @param beatInfo the beatInfo, ip and port must be set and cannot be changed via this function * @return false if nothing is modified (e.g.: the beatInfo doesn't exist in BeatReactor) * true if modification is performed */ bool BeatReactor::modifyBeatInfo(const NacosString &serviceName, BeatInfo &beatInfo) { NacosString beatInfoStr = beatInfo.toString(); log_info("[BEAT] modify beat: %s to beat map.\n", beatInfoStr.c_str()); NacosString beatKey = buildKey(serviceName, beatInfo.ip, beatInfo.port); { WriteGuard _lockguard(_beatInfoLock); if (_beatInfoList.count(beatKey) != 0) { log_warn("Modifying non-existent key:%s\n", beatKey.c_str()); return false; } BeatInfo originalBeatInfo = _beatInfoList[beatKey]->getBeatInfo(); originalBeatInfo.weight = beatInfo.weight; originalBeatInfo.cluster = beatInfo.cluster; originalBeatInfo.metadata = beatInfo.metadata; _beatInfoList[beatKey]->setBeatInfo(originalBeatInfo);//modified } return true; } /** * Removes a beatInfo * @param serviceName the service name * @param ip the service's ip * @param port the service's port * @return false if nothing is removed (e.g.: the beatInfo doesn't exist in BeatReactor) * true if a beatInfo is removed */ bool BeatReactor::removeBeatInfo(const NacosString &serviceName, const NacosString &ip, int port) { log_info("[BEAT] removing beat: %s:%s:%d from beat map.", serviceName.c_str(), ip.c_str(), port); NacosString beatKey = buildKey(serviceName, ip, port); BeatTask *beatTaskToRemove = NULL; { WriteGuard _lockguard(_beatInfoLock); //If we can't find the beatInfo in the list, just return if (_beatInfoList.count(beatKey) != 1) { log_warn("Removing non-existent key:%s\n", beatKey.c_str()); return false; } beatTaskToRemove = _beatInfoList[beatKey]; beatTaskToRemove->setScheduled(false); _beatInfoList.erase(beatKey); } //TODO:MetricsMonitor.getDom2BeatSizeMonitor().set(dom2Beat.size()); return true; } void BeatReactor::removeAllBeatInfo() { log_debug("BeatReactor::removeAllBeatInfo() start to remove\n"); WriteGuard _lockguard(_beatInfoLock); for (map::iterator it = _beatInfoList.begin(); it != _beatInfoList.end(); it++) { BeatTask *curTask = it->second; delete curTask; curTask = NULL; } _beatInfoList.clear(); } NacosString BeatReactor::buildKey(const NacosString &serviceName, const NacosString &ip, int port) { return serviceName + ConfigConstant::NAMING_INSTANCE_ID_SPLITTER + ip + ConfigConstant::NAMING_INSTANCE_ID_SPLITTER + NacosStringOps::valueOf(port); } }//namespace nacos ================================================ FILE: src/naming/beat/BeatReactor.h ================================================ #ifndef __BEAT_REACTOR_H_ #define __BEAT_REACTOR_H_ #include #include "src/naming/NamingProxy.h" #include "naming/Instance.h" #include "NacosString.h" #include "NacosExceptions.h" #include "src/thread/DelayedThreadPool.h" #include "src/thread/Thread.h" #include "src/thread/RWLock.h" #include "BeatTask.h" #include "constant/ConfigConstant.h" #include "constant/UtilAndComs.h" #include "src/factory/ObjectConfigData.h" namespace nacos{ class BeatReactor { private: ObjectConfigData *_objectConfigData; int _threadCount; RWLock _beatInfoLock; std::map _beatInfoList; volatile uint64_t _clientBeatInterval; protected: friend class BeatTask; volatile bool _stop; DelayedThreadPool *_delayedThreadPool; public: void setClientBeatInterval(uint64_t interval) { _clientBeatInterval = interval; }; uint64_t getClientBeatInterval() const { return _clientBeatInterval; }; BeatReactor(ObjectConfigData *objectConfigData, int threadCount) : _objectConfigData(objectConfigData), _beatInfoLock() { _threadCount = threadCount; _stop = true; _clientBeatInterval = 5 * 1000; _delayedThreadPool = new DelayedThreadPool("BeatReactor-Daemon", threadCount); }; BeatReactor(ObjectConfigData *objectConfigData) : _objectConfigData(objectConfigData), _beatInfoLock() { _threadCount = UtilAndComs::DEFAULT_CLIENT_BEAT_THREAD_COUNT; _stop = true; _clientBeatInterval = 5 * 1000; _delayedThreadPool = new DelayedThreadPool("BeatReactor-Daemon", _threadCount); }; ~BeatReactor() { log_debug("BeatReactor::~BeatReactor() calling stop\n"); stop(); log_debug("BeatReactor::~BeatReactor() delete threadpool\n"); delete _delayedThreadPool; _delayedThreadPool = NULL; log_debug("BeatReactor::~BeatReactor() removeAllBeatInfo()\n"); removeAllBeatInfo(); }; void start(); void stop(); void addBeatInfo(const NacosString &serviceName, BeatInfo &beatInfo); bool modifyBeatInfo(const NacosString &serviceName, BeatInfo &beatInfo); bool getBeatInfo(const NacosString &serviceName, const NacosString &ip, int port, BeatInfo &beatInfo); bool removeBeatInfo(const NacosString &serviceName, const NacosString &ip, int port); //NOTICE:Should be invoked ONLY when the working threads are ALL STOPPED void removeAllBeatInfo(); NacosString buildKey(const NacosString &serviceName, const NacosString &ip, int port); }; }//namespace nacos #endif ================================================ FILE: src/naming/beat/BeatTask.cpp ================================================ #include #include "BeatReactor.h" #include "NacosString.h" using namespace std; namespace nacos{ BeatTask::BeatTask(BeatInfo &beatInfo, ObjectConfigData *objectConfigData) : _beatInfo(beatInfo), _objectConfigData(objectConfigData), _scheduled(false) { }; BeatInfo BeatTask::getBeatInfo() const { return _beatInfo; } void BeatTask::setBeatInfo(const BeatInfo &beatInfo) { _beatInfo = beatInfo; } void BeatTask::run() { if (!_scheduled) { delete this; return; } uint64_t now_ms = TimeUtils::getCurrentTimeInMs(); _objectConfigData->_beatReactor->_delayedThreadPool->schedule(this, now_ms + _interval); _interval = _objectConfigData->_serverProxy->sendBeat(_beatInfo); } BeatTask::~BeatTask() { NacosString taskName = getTaskName(); log_debug("[BeatTask]Removing taskObject:%s\n", taskName.c_str()); } }//namespace nacos ================================================ FILE: src/naming/beat/BeatTask.h ================================================ #ifndef __BEAT_TASK_H_ #define __BEAT_TASK_H_ #include "BeatInfo.h" #include "src/naming/NamingProxy.h" #include "BeatReactor.h" #include "src/thread/ThreadPool.h" #include "src/thread/Task.h" #include "thread/AtomicInt.h" #include "src/log/Logger.h" #include "src/factory/ObjectConfigData.h" namespace nacos{ class BeatReactor; class BeatTask : public Task { private: BeatInfo _beatInfo; ObjectConfigData *_objectConfigData; bool _scheduled; uint64_t _interval;//interval for heartbeat got from the server public: BeatTask(BeatInfo &beatInfo, ObjectConfigData *objectConfigData); ~BeatTask(); void setBeatInfo(const BeatInfo &beatInfo); BeatInfo getBeatInfo() const; void run(); void setScheduled(bool scheduled) { _scheduled = scheduled; }; bool getScheduled() { return _scheduled; }; void setInterval(uint64_t interval) { _interval = interval; }; uint64_t getInterval() const { return _interval; }; }; }//namespace nacos #endif ================================================ FILE: src/naming/cache/ChangeAdvice.cpp ================================================ #include "naming/ChangeAdvice.h" using namespace std; namespace nacos{ ChangeAdvice::ChangeAdvice() { added = false; modified = false; removed = false; //addedInstances.clear(); //removedInstances.clear(); //modifiedInstances.clear(); } ChangeAdvice::~ChangeAdvice() { } void ChangeAdvice::compareChange ( ServiceInfo &oldInfo, ServiceInfo &newInfo, ChangeAdvice &changeAdvice ) { map oldInstanceList; map newInstanceList; for (list::iterator it = oldInfo.getHostsNocopy()->begin(); it != oldInfo.getHostsNocopy()->end(); it++) { oldInstanceList[it->toInetAddr()] = *it; } for (list::iterator it = newInfo.getHostsNocopy()->begin(); it != newInfo.getHostsNocopy()->end(); it++) { newInstanceList[it->toInetAddr()] = *it; } //find removed instances for (map::iterator it = oldInstanceList.begin(); it != oldInstanceList.end(); it++) { if (newInstanceList.count(it->first) == 0) { changeAdvice.removed = true; //changeAdvice.removedInstances.push_back(it->second); } else//find modified instances { //the item exists in both Lists, compare the content between these 2 if (it->second != newInstanceList[it->first]) { changeAdvice.modified = true; //changeAdvice.modifiedInstances.push_back(newInstanceList[it->first]); } } } //find added instances for (map::iterator it = newInstanceList.begin(); it != newInstanceList.end(); it++) { if (oldInstanceList.count(it->first) == 0) { changeAdvice.added = true; //changeAdvice.addedInstances.push_back(it->second); } } } NacosString ChangeAdvice::toString() { return "Unimplemented";//trivial function } }//namespace nacos ================================================ FILE: src/naming/cache/NamingCache.cpp ================================================ #include "NamingCache.h" namespace nacos{ ServiceInfo NamingCache::getServiceInfo(const NacosString &key) NACOS_THROW(NacosException) { ReadGuard __readGuard(_rwLock); if (!contains(key)) { throw NacosException(0, "Key" + key + " doesn't exist"); } return namingList[key]; } bool NamingCache::contains(const NacosString &key) { ReadGuard __readGuard(_rwLock); return namingList.count(key) > 0; } void NamingCache::setServiceInfo(const NacosString &key, const ServiceInfo &info) { WriteGuard __writeGuard(_rwLock); if (_eventDispatcher != NULL) { ChangeAdvice changeAdvice; if (namingList.count(key) > 0) { //changeAdvice.oldServiceInfo = namingList[key]; //changeAdvice.newServiceInfo = info; } else { //changeAdvice.newServiceInfo = info; } //_eventDispatcher->notify(changeAdvice); } namingList[key] = info; } void NamingCache::removeServiceInfo(const NacosString &key) { WriteGuard __writeGuard(_rwLock); if (namingList.count(key) == 0) { return; } if (_eventDispatcher != NULL) { ChangeAdvice changeAdvice; //changeAdvice.oldServiceInfo = namingList[key]; //_eventDispatcher->notify(changeAdvice); } namingList.erase(key); } }//namespace nacos ================================================ FILE: src/naming/cache/NamingCache.h ================================================ #ifndef __NAMING_CACHE_H_ #define __NAMING_CACHE_H_ #include #include "NacosString.h" #include "src/naming/subscribe/EventDispatcher.h" #include "naming/ServiceInfo.h" #include "src/thread/RWLock.h" #include "naming/ChangeAdvice.h" #include "Compatibility.h" #include "NacosExceptions.h" namespace nacos{ class NamingCache { private: std::map namingList; RWLock _rwLock; EventDispatcher *_eventDispatcher; public: NamingCache(); NamingCache(EventDispatcher *eventDispatcher) { _eventDispatcher = eventDispatcher; }; ServiceInfo getServiceInfo(const NacosString &key) NACOS_THROW(NacosException); bool contains(const NacosString &key); void setServiceInfo(const NacosString &key, const ServiceInfo &info); void removeServiceInfo(const NacosString &key); }; }//namespace nacos #endif ================================================ FILE: src/naming/selectors/HealthInstanceSelector.cpp ================================================ #include #include "naming/selectors/HealthInstanceSelector.h" namespace nacos { namespace naming { namespace selectors { std::list HealthInstanceSelector::select(const std::list &instancesToSelect){ std::list result; for (std::list::const_iterator it = instancesToSelect.begin(); it != instancesToSelect.end(); it++) { if (it->healthy) { result.push_back(*it); } } return result; } } /*selectors*/ } /*naming*/ }/*nacos*/ ================================================ FILE: src/naming/selectors/RandomByWeightSelector.cpp ================================================ #include #include #include "naming/selectors/RandomByWeightSelector.h" #include "src/log/Logger.h" #include "src/utils/ParamUtils.h" #include "src/utils/RandomUtils.h" #define BASIC_WEIGHT 65536 namespace nacos { namespace naming { namespace selectors { std::list RandomByWeightSelector::select(const std::list &instancesToSelect){ std::vector::const_iterator > > weightedList; std::list result; int total_weight = 0; for (std::list::const_iterator it = instancesToSelect.begin(); it != instancesToSelect.end(); it++) { if (it->weight < 1e-10) { //we consider a very small weight as 0 continue; } total_weight += it->weight * BASIC_WEIGHT; log_debug("RandomByWeightSelector::select:weight for current instance:%f\n", it->weight); weightedList.push_back(std::make_pair(it->weight * BASIC_WEIGHT, it)); } if (total_weight == 0) { //no server instance is chosen return result; } log_debug("RandomByWeightSelector::select:total_weight:%d\n", total_weight); size_t selectedWeight = RandomUtils::random(0, total_weight - 1); log_debug("RandomByWeightSelector::select selected weight:%d\n", selectedWeight); std::vector::const_iterator> >::const_iterator it = weightedList.begin(); while (selectedWeight > it->second->weight * BASIC_WEIGHT) { selectedWeight -= it->second->weight * BASIC_WEIGHT; it++; } result.push_back(*it->second); return result; } } /*selectors*/ } /*naming*/ }/*nacos*/ ================================================ FILE: src/naming/selectors/RandomSelector.cpp ================================================ #include "naming/selectors/RandomSelector.h" #include "src/log/Logger.h" #include "src/utils/RandomUtils.h" #include "src/utils/ParamUtils.h" namespace nacos { namespace naming { namespace selectors { std::list RandomSelector::select(const std::list &instancesToSelect){ size_t maxSvrSlot = instancesToSelect.size(); log_debug("RandomSelector::select:nr_servers%d\n", maxSvrSlot); size_t selectedServer; if (maxSvrSlot == 1) { selectedServer = 0; } else { selectedServer = RandomUtils::random(0, maxSvrSlot - 1); } log_debug("RandomSelector::select:%d\n", selectedServer); std::list result; result.push_back(ParamUtils::getNthElem(instancesToSelect, selectedServer)); return result; } } /*selectors*/ } /*naming*/ }/*nacos*/ ================================================ FILE: src/naming/subscribe/EventDispatcher.cpp ================================================ // // Created by liuhanyu on 2020/9/25. // #include "EventDispatcher.h" #include "src/debug/DebugAssertion.h" using namespace std; namespace nacos{ bool EventDispatcher::addListener(const NacosString &serviceName, const NacosString &clusters, EventListener *eventListener) { NACOS_ASSERT(eventListener != NULL); WriteGuard __writeGuard(rwLock); NacosString key = ServiceInfo::getKey(serviceName, clusters); if (observerMap.count(key) > 0) { list &listenerList = observerMap[key]; for (list::iterator it = listenerList.begin(); it != listenerList.end(); it++) { if ((*it) == eventListener) { return false; } } eventListener->incRef(); listenerList.push_back(eventListener); return true; } else { list listenerList; eventListener->incRef(); listenerList.push_back(eventListener); observerMap[key] = listenerList; return true; } } bool EventDispatcher::removeListenerHelper(const NacosString &key, EventListener *eventListener, int &remainingListeners) { list &listenerList = observerMap[key]; for (list::iterator it = listenerList.begin(); it != listenerList.end(); it++) { if (*it == eventListener) { EventListener *removedItem = *it; log_debug("[EventDispatcher]-remove:erased item: %s\n", removedItem->getListenerName().c_str()); listenerList.erase(it); int refCount = removedItem->decRef(); if (refCount == 0) { log_debug("[EventDispatcher]-remove:The refcount of listener '%s' is 0, deleting the item.\n", removedItem->getListenerName().c_str()); delete removedItem; } if (listenerList.empty()) { observerMap.erase(key); } remainingListeners = listenerList.size(); return true; } } return false; } bool EventDispatcher::removeListener(const NacosString &serviceName, const NacosString &clusters, EventListener *eventListener, int &remainingListeners) { NACOS_ASSERT(eventListener != NULL); WriteGuard __writeGuard(rwLock); NacosString key = ServiceInfo::getKey(serviceName, clusters); if (observerMap.count(key) == 0) { remainingListeners = 0; return false; } else { return removeListenerHelper(key, eventListener, remainingListeners); } } void EventDispatcher::start() { if (_started) { return; } _started = true; eventNotifier->start(); } void EventDispatcher::stop() { if (!_started) { log_debug("[EventDispatcher]:Calling stop() on a already-stopped EventDispatcher\n"); return; } _started = false; NotifyData notifyData; notifyData.exit = true; notifyDataList.enqueue(notifyData); } void EventDispatcher::notify(const ChangeAdvice &changeAdvice) { NacosString key = changeAdvice.key; NotifyData notifyData; { ReadGuard __readGuard(rwLock); if (observerMap.count(key) == 0) { log_debug("[EventDispatcher]:Notifying non-existent key:%s\n", key.c_str()); return; } list &listeners = observerMap[key]; for (list::iterator it = listeners.begin(); it != listeners.end(); it++) { (*it)->incRef(); } notifyData.listeners = listeners; } notifyData.serviceInfo = changeAdvice.newServiceInfo; notifyDataList.enqueue(notifyData); } void EventDispatcher::notifyDirectly(const ChangeAdvice &changeAdvice) { NacosString key = changeAdvice.key; list listeners; { ReadGuard __readGuard(rwLock); if (observerMap.count(key) == 0) { log_debug("[EventDispatcher]:Notifying non-existent key:%s\n", key.c_str()); return; } listeners = observerMap[key]; for (list::iterator it = listeners.begin(); it != listeners.end(); it++) { (*it)->incRef(); } } for (list::iterator curListener = listeners.begin(); curListener != listeners.end(); curListener++) { //asm volatile("int $3"); (*curListener)->receiveNamingInfo(changeAdvice.newServiceInfo); int refCnt = (*curListener)->decRef(); if (refCnt == 0) { log_debug("[EventDispatcher]:Destroying listener whose refcount = 0\n"); delete *curListener; } } } void *EventDispatcher::eventDispatcherThread(void *parm) { EventDispatcher *thisObj = (EventDispatcher*)parm; while (true) { NotifyData notifyData = thisObj->notifyDataList.dequeue(); if (notifyData.exit) { while (!thisObj->notifyDataList.empty()) { NotifyData notifyDataToRemove = thisObj->notifyDataList.dequeue(); for (list::iterator curListener = notifyDataToRemove.listeners.begin(); curListener != notifyDataToRemove.listeners.end(); curListener++) { (*curListener)->decRef(); int refCount = (*curListener)->decRef(); if (refCount == 0) { delete (*curListener); } } } return 0; } for (list::iterator curListener = notifyData.listeners.begin(); curListener != notifyData.listeners.end(); curListener++) { (*curListener)->receiveNamingInfo(notifyData.serviceInfo); (*curListener)->decRef(); int refCount = (*curListener)->decRef(); if (refCount == 0) { delete (*curListener); } } } return 0; } //NOTE:This function can be called ONLY when the dispatcher thread is stopped! //This designed to be called when the an EventDispatcher Object is dtor'd void EventDispatcher::purgeAllListeners() { WriteGuard __writeGuard(rwLock); for (map >::iterator it = observerMap.begin(); it != observerMap.end(); it++) { for (list::iterator listenerItem = it->second.begin(); listenerItem != it->second.end(); listenerItem++) { int refCount = (*listenerItem)->decRef(); if (refCount == 0) { log_debug("[EventDispatcher]:Deleting the object whose refcount = 0:%s\n", (*listenerItem)->getListenerName().c_str()); delete *listenerItem; *listenerItem = NULL; } } } for (map >::iterator it = observerMap.begin(); it != observerMap.end(); it++) { for (list::iterator curListener = it->second.begin(); curListener != it->second.end(); curListener++) { NACOS_ASSERT(*curListener == NULL); } } observerMap.clear(); } EventDispatcher::EventDispatcher() { _started = false; eventNotifier = new Thread("Nacos-NamingEvent-Dispatcher", eventDispatcherThread, (void*)this); } EventDispatcher::~EventDispatcher() { stop(); eventNotifier->join(); delete eventNotifier; eventNotifier = NULL; purgeAllListeners(); } }//namespace nacos ================================================ FILE: src/naming/subscribe/EventDispatcher.h ================================================ #ifndef __EVT_DISPTCH_H_ #define __EVT_DISPTCH_H_ #include "src/thread/Thread.h" #include "src/thread/RWLock.h" #include "src/thread/BlockingQueue.h" #include "naming/subscribe/EventListener.h" #include namespace nacos{ struct NotifyData { NotifyData() { exit = false; }; bool exit; ServiceInfo serviceInfo; std::list listeners; }; //TODO:refactor to 2 types of eventDispatcher: //1. blocking mode, notify in notifier's thread //2. non-blocking mode(async), dispatcher has a blocking queue and send notify in that thread asynchronously class EventDispatcher { private: volatile bool _started; RWLock rwLock;//for observerMap std::map > observerMap; BlockingQueue notifyDataList; Thread *eventNotifier; static void *eventDispatcherThread(void *parm); bool removeListenerHelper(const NacosString &key, EventListener *eventListener, int &remainingListeners); void purgeAllListeners(); public: bool addListener(const NacosString &serviceName, const NacosString &clusters, EventListener *eventListener); bool removeListener(const NacosString &serviceName, const NacosString &clusters, EventListener *eventListener, int &remainingListeners); void notify(const ChangeAdvice &changeAdvice); void notifyDirectly(const ChangeAdvice &changeAdvice); void stop(); void start(); EventDispatcher(); ~EventDispatcher(); }; }//namespace nacos #endif ================================================ FILE: src/naming/subscribe/EventListener.cpp ================================================ #include "naming/subscribe/EventListener.h" #include "src/debug/DebugAssertion.h" namespace nacos { EventListener::~EventListener() { NACOS_ASSERT(refCnt() == 0) } }//namespace nacos ================================================ FILE: src/naming/subscribe/HostReactor.cpp ================================================ // // Created by liuhanyu on 2021/1/7. // #include "HostReactor.h" #include "src/json/JSON.h" #include "src/utils/NamingUtils.h" #include "src/naming/subscribe/EventDispatcher.h" namespace nacos { HostReactor::HostReactor(ObjectConfigData *objectConfigData) { _objectConfigData = objectConfigData; } void HostReactor::processServiceJson(const NacosString &json) { ServiceInfo serviceInfo = JSON::JsonStr2ServiceInfo(json); NacosString name = NamingUtils::getGroupedName(serviceInfo.getName(), serviceInfo.getGroupName()); NacosString key = ServiceInfo::getKey(name, serviceInfo.getClusters()); ServiceInfo oldServiceInfo; bool newServiceInfo = false; { WriteGuard _writeGuard(rwLock); if (serviceInfoMap.count(key) == 0) { if (serviceInfo.ipCount()==0) { log_warn("hosts got from server is empty.\n"); return; } serviceInfoMap[key] = serviceInfo; newServiceInfo = true; } else { oldServiceInfo = serviceInfoMap[key]; if (oldServiceInfo.getLastRefTime() >= serviceInfo.getLastRefTime()) { log_warn("ServiceInfo got from server is older than the one in client.\n"); return; } serviceInfoMap[key] = serviceInfo;//update local service info to the new one } } ChangeAdvice changeAdvice; changeAdvice.key = key; if (newServiceInfo) { changeAdvice.added = true; changeAdvice.newServiceInfo = serviceInfo; _objectConfigData->_eventDispatcher->notifyDirectly(changeAdvice); } else {//service info is updated ChangeAdvice::compareChange(oldServiceInfo, serviceInfo, changeAdvice); log_debug("Change status:modified:%d added:%d removed:%d\n", changeAdvice.modified, changeAdvice.added, changeAdvice.removed); if (changeAdvice.modified || changeAdvice.added || changeAdvice.removed) { //asm volatile("int $3"); changeAdvice.newServiceInfo = serviceInfo; _objectConfigData->_eventDispatcher->notifyDirectly(changeAdvice); } } } } ================================================ FILE: src/naming/subscribe/HostReactor.h ================================================ // // Created by liuhanyu on 2021/1/7. // #ifndef NACOS_SDK_CPP_HOSTREACTOR_H #define NACOS_SDK_CPP_HOSTREACTOR_H #include #include "NacosString.h" #include "naming/ServiceInfo.h" #include "src/factory/ObjectConfigData.h" #include "src/thread/RWLock.h" namespace nacos { class HostReactor { private: ObjectConfigData *_objectConfigData; //memory cache std::map serviceInfoMap; //rw lock for serviceInfoMap RWLock rwLock; public: HostReactor(ObjectConfigData *objectConfigData); void processServiceJson(const NacosString &json); }; } #endif //NACOS_SDK_CPP_HOSTREACTOR_H ================================================ FILE: src/naming/subscribe/SubscriptionPoller.cpp ================================================ #include "SubscriptionPoller.h" #include "constant/ConfigConstant.h" #include "src/utils/NamingUtils.h" #include "src/json/JSON.h" #include "HostReactor.h" using namespace std; namespace nacos{ SubscriptionPoller::SubscriptionPoller(ObjectConfigData *objectConfigData) { _objectConfigData = objectConfigData; _pollingThread = new Thread("NamingServicePoller", pollingThreadFunc, (void*)this); _pollingInterval = atoi(_objectConfigData->_appConfigManager->get(PropertyKeyConst::SUBSCRIPTION_POLL_INTERVAL).c_str()); _udpPort = atoi(_objectConfigData->_appConfigManager->get(PropertyKeyConst::UDP_RECEIVER_PORT).c_str()); _started = false; } SubscriptionPoller::~SubscriptionPoller() { if (_started) { stop(); } if (_pollingThread != NULL) { delete _pollingThread; _pollingThread = NULL; } } bool SubscriptionPoller::addPollItem(const NacosString &serviceName, const NacosString &groupName, const NacosString &clusters) { struct PollingData pd; pd.clusters = clusters; pd.serviceName = serviceName; pd.groupName = groupName; NacosString name = NamingUtils::getGroupedName(serviceName, groupName); NacosString key = ServiceInfo::getKey(name, clusters); { WriteGuard __writeGuard(rwLock); if (pollingList.count(key) > 0) { return false; } pollingList[key] = pd; return true; } } bool SubscriptionPoller::removePollItem(const NacosString &serviceName, const NacosString &groupName, const NacosString &clusters) { NacosString name = NamingUtils::getGroupedName(serviceName, groupName); NacosString key = ServiceInfo::getKey(name, clusters); { WriteGuard __writeGuard(rwLock); if (pollingList.count(key) == 0) { return false; } pollingList.erase(key); } return true; } void SubscriptionPoller::start() { if (_started) { log_warn("Calling start on already-started SubscriptionPoller\n"); return; } _started = true; _pollingThread->start(); } void SubscriptionPoller::stop() { if (!_started) { log_warn("Calling stop on already-stopped SubscriptionPoller\n"); return; } _started = false; _pollingThread->kill(); _pollingThread->join(); } void *SubscriptionPoller::pollingThreadFunc(void *parm) { SubscriptionPoller *thisObj = (SubscriptionPoller*)parm; while (thisObj->_started) { log_debug("SubscriptionPoller::pollingThreadFunc start polling, interval = %d\n", thisObj->_pollingInterval); map copiedList; { ReadGuard __readGuard(thisObj->rwLock); copiedList = thisObj->pollingList; log_debug("Copied polling list, size = %d\n", copiedList.size()); } if (copiedList.empty()) { log_debug("PollingList is empty, hibernating...\n", copiedList.size()); sleep(thisObj->_pollingInterval / 1000); continue; } for (map::iterator it = copiedList.begin(); it != copiedList.end(); it++) { NacosString name = NamingUtils::getGroupedName(it->second.serviceName, it->second.groupName); NacosString key = ServiceInfo::getKey(name, it->second.clusters); log_debug("Polling data: name=%s, key=%s\n", name.c_str(), key.c_str()); NacosString result; try { result = thisObj->_objectConfigData->_serverProxy->queryList( it->second.serviceName, it->second.groupName, it->second.clusters, thisObj->_udpPort,false); } catch (NacosException &e) { //no server available or all servers tried but failed //just ignore and wait the network restore or manual restore //the reason why we choose to ignore the exception is that: //1. the network is down, we don't know the current status of the server, so we don't know whether it changed or not //for that reason we can't make any notifications //2. when the network restored, we can get the latest status of the server and send notifications //3. if the network is down for a rather long time, manual restore is needed, //and the server will be restarted, this listener thread will be restarted, too //4. when the server returns a 404 error, we still need to poll the service's information, since it may become available sometime later continue; } log_debug("Server info got from server:%s\n=======>\n", result.c_str()); thisObj->_objectConfigData->_hostReactor->processServiceJson(result); } log_debug("Polling process finished, hibernating...\n"); sleep(thisObj->_pollingInterval / 1000); } log_debug("Polling thread for NamingService exited normally.\n"); return NULL; } }//namespace nacos ================================================ FILE: src/naming/subscribe/SubscriptionPoller.h ================================================ // // Created by liuhanyu on 2020/9/26. // #ifndef NACOS_SDK_CPP_SUBSCRIPTIONPOLLER_H #define NACOS_SDK_CPP_SUBSCRIPTIONPOLLER_H #include #include "src/thread/Thread.h" #include "src/naming/NamingProxy.h" #include "EventDispatcher.h" #include "src/factory/ObjectConfigData.h" namespace nacos{ struct PollingData { NacosString serviceName; NacosString groupName; NacosString clusters; volatile long nextPollTime; }; class SubscriptionPoller { private: Thread *_pollingThread; int _pollingInterval;//In ms int _udpPort;//udp receiver port volatile bool _started; ObjectConfigData *_objectConfigData; SubscriptionPoller(); static void *pollingThreadFunc(void *parm); //for polling list RWLock rwLock; std::map pollingList; public: SubscriptionPoller(ObjectConfigData *objectConfigData); bool addPollItem(const NacosString &serviceName, const NacosString &groupName, const NacosString &clusters); bool removePollItem(const NacosString &serviceName, const NacosString &groupName, const NacosString &clusters); void start(); void stop(); ~SubscriptionPoller(); }; }//namespace nacos #endif //NACOS_SDK_CPP_SUBSCRIPTIONPOLLER_H ================================================ FILE: src/naming/subscribe/UdpNamingServiceListener.cpp ================================================ #include #include #include #include "UdpNamingServiceListener.h" #include "src/config/AppConfigManager.h" #include "constant/PropertyKeyConst.h" #include "src/json/JSON.h" #include "HostReactor.h" #include "zlib.h" #include "src/debug/DebugAssertion.h" #include using namespace std; namespace nacos { void UdpNamingServiceListener::initializeUdpListener() NACOS_THROW(NacosException) { log_debug("in thread UdpNamingServiceListener::initializeUdpListener()\n"); // Creating socket file descriptor if ( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ) { throw NacosException(NacosException::UNABLE_TO_CREATE_SOCKET, "Unable to create socket"); } memset(&cliaddr, 0, sizeof(cliaddr)); // Filling client information cliaddr.sin_family = AF_INET; // IPv4 cliaddr.sin_addr.s_addr = INADDR_ANY; cliaddr.sin_port = htons(udpReceiverPort); log_debug("udp receiver port = %d\n", cliaddr.sin_port); // Bind the socket with the server address if ( ::bind(sockfd, (const struct sockaddr *)&cliaddr, sizeof(cliaddr)) < 0 ) { throw NacosException(NacosException::UNABLE_TO_CREATE_SOCKET, "Unable to bind"); } log_debug("socket bound\n"); } bool UdpNamingServiceListener::unGzip(char *inBuffer, size_t inSize) { //reference:https://zlib.net/zlib_how.html /* allocate inflate state */ z_stream strm; strm.zalloc = Z_NULL; strm.zfree = Z_NULL; strm.opaque = Z_NULL; strm.avail_in = 0; strm.next_in = Z_NULL; int ret = inflateInit2(&strm, MAX_WBITS + 16); if (ret != Z_OK) { log_error("failed to perform inflateInit()\n"); return false; } strm.avail_in = inSize; strm.next_in = (unsigned char*)inBuffer; strm.avail_out = sizeof(this->uncompressedData); strm.next_out = (unsigned char*)this->uncompressedData; ret = inflate(&strm, Z_NO_FLUSH); NACOS_ASSERT(ret != Z_STREAM_ERROR); switch (ret) { case Z_NEED_DICT: ret = Z_DATA_ERROR; /* and fall through */ case Z_DATA_ERROR: case Z_MEM_ERROR: (void)inflateEnd(&strm); log_error("in switch block, inflate failed with code = %d\n", ret); return false; } if (strm.avail_out == 0) { log_error("uncompressed data exceeds the size limit, please consider a larger uncompressedData\n"); return false; } (void)inflateEnd(&strm); this->uncompressedData[sizeof(this->uncompressedData) - strm.avail_out] = '\0'; return true; } void *UdpNamingServiceListener::listenerThreadFunc(void *param) { UdpNamingServiceListener *thisObj = (UdpNamingServiceListener*)param; log_debug("in thread UdpNamingServiceListener::listenerThreadFunc()\n"); thisObj->initializeUdpListener(); while (thisObj->_started) { int ret;//also data_len log_debug("before recvfrom() socketfd:%d\n", thisObj->sockfd); struct sockaddr src_addr = {0}; socklen_t src_addr_len = sizeof(struct sockaddr_in); ret = recvfrom(thisObj->sockfd, (char *)thisObj->receiveBuffer, UDP_MSS, MSG_WAITALL, &src_addr, &src_addr_len); log_debug("ret got from recvfrom():%d\n", ret); if (ret == -1) { if (errno == EINTR) { log_debug("got sigint from main thread, exiting...\n"); //got kill() signal from main thread, free resources & exit } //other kinds of error break; } //parse the package thisObj->receiveBuffer[ret] = 0; log_debug("content got from UDP server is %s\n", thisObj->receiveBuffer); PushPacket pushPacket; try { if (ret <= 2) { //the server returns a packet shorter than 2 bytes, which could not be parsed by the listener log_warn("got an invalid packet, len = %d, content = %s", ret, thisObj->receiveBuffer); continue; } char *packetToParse = thisObj->receiveBuffer; if ((unsigned char)thisObj->receiveBuffer[0] == 0x1f && (unsigned char)thisObj->receiveBuffer[1] == 0x8b) { if (!thisObj->unGzip(thisObj->receiveBuffer, ret)) { continue; } packetToParse = thisObj->uncompressedData; } pushPacket = JSON::Json2PushPacket(packetToParse); } catch (NacosException &e) { log_error("Invalid json string got from server:%s\n", thisObj->receiveBuffer); continue; } NacosString ack; if (pushPacket.type == "dom" || pushPacket.type == "service") { thisObj->_objectConfigData->_hostReactor->processServiceJson(pushPacket.data); // send ack to server ack = "{\"type\": \"push-ack\", \"lastRefTime\":\"" + NacosStringOps::valueOf(pushPacket.lastRefTime) + "\", \"data\":\"\"}"; } else if (pushPacket.type == "dump") { // dump data to server //TODO:Unimplemented ack = "{\"type\": \"dump-ack\", \"lastRefTime\": \"" + NacosStringOps::valueOf(pushPacket.lastRefTime) + "\", \"data\":\"\"}"; } else { // do nothing & send ack only ack = "{\"type\": \"unknown-ack\", \"lastRefTime\":\"" + NacosStringOps::valueOf(pushPacket.lastRefTime) + "\", \"data\":\"\"}"; } ssize_t recv_ret = sendto(thisObj->sockfd, ack.c_str(), ack.length(), 0, &src_addr, src_addr_len); if (recv_ret < 0) { log_error("error while sending data...%d\n", errno); } } close(thisObj->sockfd); return NULL; } UdpNamingServiceListener::UdpNamingServiceListener(ObjectConfigData *objectConfigData) { _listenerThread = NULL; _started = false; _objectConfigData = objectConfigData; udpReceiverPort = atoi(_objectConfigData->_appConfigManager->get(PropertyKeyConst::UDP_RECEIVER_PORT).c_str()); log_debug("udpReceiverPort is %d\n", udpReceiverPort); _listenerThread = new Thread(objectConfigData->name + "UDPListener", listenerThreadFunc, (void*)this); } void UdpNamingServiceListener::start() { if (_started) { return; } _started = true; _listenerThread->start(); } void UdpNamingServiceListener::stop() { if (!_started) { return; } _started = false; _listenerThread->kill(); _listenerThread->join(); } UdpNamingServiceListener::~UdpNamingServiceListener() { if (_started) { stop(); } if (_listenerThread != NULL) { delete _listenerThread; } } } ================================================ FILE: src/naming/subscribe/UdpNamingServiceListener.h ================================================ // // Created by liuhanyu on 2020/9/26. // #ifndef NACOS_SDK_CPP_UDPLSNR_H_ #define NACOS_SDK_CPP_UDPLSNR_H_ #include #include #include #include #include "src/factory/ObjectConfigData.h" #include "src/thread/Thread.h" #include "Compatibility.h" #define UDP_MSS 64 * 1024 namespace nacos{ typedef struct { NacosString type; long lastRefTime; NacosString data; } PushPacket; class UdpNamingServiceListener { private: ObjectConfigData *_objectConfigData; volatile bool _started; int sockfd; int udpReceiverPort; struct sockaddr_in cliaddr; char receiveBuffer[UDP_MSS]; //assume the max compress ratio = 90% char uncompressedData[UDP_MSS * 10]; Thread *_listenerThread; void initializeUdpListener() NACOS_THROW(NacosException); static void *listenerThreadFunc(void *param); bool unGzip(char *inBuffer, size_t inSize); public: UdpNamingServiceListener(ObjectConfigData *objectConfigData); ~UdpNamingServiceListener(); void start(); void stop(); }; }//namespace nacos #endif //NACOS_SDK_CPP_UDPLSNR_H_ ================================================ FILE: src/security/SecurityManager.cpp ================================================ // // Created by liuhanyu on 2020/11/28. // #include "SecurityManager.h" #include "src/json/JSON.h" #include "src/utils/RandomUtils.h" #include "src/utils/TimeUtils.h" #include "src/utils/ParamUtils.h" #include "constant/ConfigConstant.h" using namespace std; namespace nacos { SecurityManager::SecurityManager(ObjectConfigData *objectConfigData) { _objectConfigData = objectConfigData; _started = false; _tokenRefreshThread = new Thread("TokenRefreshThread", tokenRefreshThreadFunc, (void*)this); } void SecurityManager::doLogin(const NacosString &serverAddr) NACOS_THROW(NacosException, NetworkException) { //TODO:refactor string constants NacosString url = serverAddr + "/" + _objectConfigData->_appConfigManager->getContextPath() + "/v1/auth/users/login"; list headers; list paramValues; const NacosString &username = _objectConfigData->_appConfigManager->get(PropertyKeyConst::AUTH_USERNAME); const NacosString &password = _objectConfigData->_appConfigManager->get(PropertyKeyConst::AUTH_PASSWORD); paramValues.push_back("username"); paramValues.push_back(username); paramValues.push_back("password"); paramValues.push_back(password); HttpResult result = _objectConfigData->_httpCli->httpPost(url, headers, paramValues, NULLSTR, 3000); _accessToken = JSON::Json2AccessToken(result.content); } void SecurityManager::login() NACOS_THROW (NacosException) { WriteGuard writeGuard(_rwLock); list serversToTry = _objectConfigData->_serverListManager->getServerList(); size_t nr_servers = serversToTry.size(); if (nr_servers == 0) { throw NacosException(NacosException::NO_SERVER_AVAILABLE, "No available server when getting access token"); } size_t start = 0; if (nr_servers > 1) { start = RandomUtils::random(0, nr_servers - 1); } for (size_t nr_tries = 0; nr_tries < nr_servers; nr_tries++) { const NacosServerInfo &curServer = ParamUtils::getNthElem(serversToTry, (nr_tries + start) % nr_servers); NacosString serverAddr = curServer.getCompleteAddress(); try { //the method will throw if there's something wrong(e.g.: network problem) doLogin(serverAddr); } catch (NetworkException &e) { //continue to try next node continue; } catch (NacosException &e) { //for some cases, e.g.:invalid username/password, //we should throw exception directly since retry on another node will not correct this problem if (e.errorcode() == NacosException::INVALID_LOGIN_CREDENTIAL) { /** * Here we don't need to keep log for it, because there are 3 situations where we will call this login() routine: * 1. Initialization stage of NamingService * 2. Initialization stage of ConfigService * 3. tokenRefreshThreadFunc's invocation of this routine to refresh the credentials * In case 1 and case 2, the program will crash, because these situations are considered as a config error of the program, so let it crash * In case 3, the log is printed by tokenRefreshThreadFunc to remind the dev-ops to correct the config */ throw e; } log_error("Unknown error while login to server, e:%d = %s\n", e.errorcode(), e.what()); continue; } //login succeeded return; } //this is (usually) a network problem, the caller (thread) should handle this throw NacosException(NacosException::ALL_SERVERS_TRIED_AND_FAILED, "Login failed after all servers are tried"); } NacosString &SecurityManager::getAccessToken() { ReadGuard _readGuard(_rwLock); return _accessToken.accessToken; } void SecurityManager::addAccessToken2Req(std::list ¶meter){ ReadGuard _readGuard(_rwLock); parameter.push_back("accessToken"); parameter.push_back(_accessToken.accessToken); } SecurityManager::~SecurityManager() { stop(); delete _tokenRefreshThread; _tokenRefreshThread = NULL; } void SecurityManager::sleepWithRunStatusCheck(long _milliSecsToSleep) { if (_milliSecsToSleep == 0) { return; } long granularity = 10; long sleep_start_time = TimeUtils::getCurrentTimeInMs(); long sleep_end_time = sleep_start_time + _milliSecsToSleep; while (_started) { if (TimeUtils::getCurrentTimeInMs() >= sleep_end_time) { break; } sleep(granularity); } } void *SecurityManager::tokenRefreshThreadFunc(void *param) { SecurityManager *thisObj = (SecurityManager*)param; log_debug("In thread SecurityManager::tokenRefreshThreadFunc\n"); while (thisObj->_started) { try { log_debug("Ttl got from nacos server:%ld\n", thisObj->_accessToken.tokenTtl); thisObj->sleepWithRunStatusCheck(thisObj->_accessToken.tokenTtl * 1000); log_debug("Trying to login...\n"); thisObj->login(); } catch (NacosException &e) { if (e.errorcode() == NacosException::INVALID_LOGIN_CREDENTIAL) { /** * invalid credential while the application is running, wait for a moment and retry * since the existing data in the nacos client is still usable for the application * we should keep the application alive for the Availability, but the Consistency, in this case, is NOT guaranteed * the error log reminds the dev-ops to check the config */ log_error("Invalid credential, please check your server settings!\n"); sleep(30); } else if (e.errorcode() == NacosException::ALL_SERVERS_TRIED_AND_FAILED) { log_warn("Network down, sleep for 30 secs and retry\n"); sleep(30);//network down, wait for a moment continue; } else { //unknown error, there should be a better way to handle this situation log_error("Unknown error happend, code: %d, reason: %s\n", e.errorcode(), e.what()); sleep(30);//unknown error, wait for 30 sec and continue continue; } } } return NULL; } void SecurityManager::start() { if (_started) { return; } _started = true; _tokenRefreshThread->start(); } void SecurityManager::stop() { if (!_started) { return; } _started = false; _tokenRefreshThread->kill(); _tokenRefreshThread->join(); } }//nacos ================================================ FILE: src/security/SecurityManager.h ================================================ // // Created by liuhanyu on 2020/11/28. // #ifndef NACOS_SDK_CPP_SECURITYMANAGER_H #define NACOS_SDK_CPP_SECURITYMANAGER_H #include "src/config/AppConfigManager.h" #include "src/http/IHttpCli.h" #include "src/server/ServerListManager.h" #include "src/factory/ObjectConfigData.h" #include "Compatibility.h" namespace nacos { struct AccessToken { NacosString accessToken; long tokenTtl; bool globalAdmin; long lastRefTime; AccessToken() { accessToken = ""; tokenTtl = 0; globalAdmin = false; lastRefTime = 0; } }; class SecurityManager { private: ObjectConfigData *_objectConfigData; AccessToken _accessToken; void doLogin(const NacosString &serverAddr) NACOS_THROW(NacosException, NetworkException); RWLock _rwLock; volatile bool _started; static void * tokenRefreshThreadFunc(void *param); Thread *_tokenRefreshThread; void sleepWithRunStatusCheck(long _milliSecsToSleep); public: SecurityManager(ObjectConfigData *objectConfigData); ~SecurityManager(); void login() NACOS_THROW (NacosException); NacosString &getAccessToken(); void addAccessToken2Req(std::list ¶meter); void start(); void stop(); }; } #endif //NACOS_SDK_CPP_SECURITYMANAGER_H ================================================ FILE: src/server/NacosServerInfo.h ================================================ // // Created by Liu, Hanyu on 2020/9/4. // #ifndef NACOS_SDK_CPP_NACOSSERVERINFO_H #define NACOS_SDK_CPP_NACOSSERVERINFO_H #include #include "NacosString.h" namespace nacos{ //a instance of nacos naming service class NacosServerInfo { private: int mode;//1 - http/2 - https NacosString ip; int port; NacosString site; float weight; float adWeight; bool alive; long lastRefTime; NacosString lastRefTimeStr; NacosString key; public: enum MODE { mode_http = 1, mode_http_safe }; int getMode() const { return mode; } void setMode(int mode) { NacosServerInfo::mode = mode; } const NacosString getIp() const { return ip; } void setIp(const NacosString &ip) { NacosServerInfo::ip = ip; } int getPort() const { return port; } void setPort(int port) { NacosServerInfo::port = port; } const NacosString getSite() const { return site; } void setSite(const NacosString &site) { NacosServerInfo::site = site; } float getWeight() const { return weight; } void setWeight(float weight) { NacosServerInfo::weight = weight; } float getAdWeight() const { return adWeight; } void setAdWeight(float adWeight) { NacosServerInfo::adWeight = adWeight; } bool isAlive() const { return alive; } void setAlive(bool alive) { NacosServerInfo::alive = alive; } long getLastRefTime() const { return lastRefTime; } void setLastRefTime(long lastRefTime) { NacosServerInfo::lastRefTime = lastRefTime; } const NacosString getLastRefTimeStr() const { return lastRefTimeStr; } void setLastRefTimeStr(const NacosString &lastRefTimeStr) { NacosServerInfo::lastRefTimeStr = lastRefTimeStr; } const NacosString getKey() const { return key; } void setKey(const NacosString &key) { NacosServerInfo::key = key; } NacosString getCompleteAddress() const { if (port != 0) { return ip + ":" + NacosStringOps::valueOf(port); } else { return ip; } } NacosString toString() const { NacosString res; res = ip + ":" + NacosStringOps::valueOf(port); return res; } bool operator<(const NacosServerInfo &other) const { return getCompleteAddress().compare(other.getCompleteAddress()); } bool operator==(const NacosServerInfo &other) const { return getCompleteAddress().compare(other.getCompleteAddress()) == 0; } }; }//namespace nacos #endif //NACOS_SDK_CPP_NACOSSERVERINFO_H ================================================ FILE: src/server/ServerListManager.cpp ================================================ #include #include #include "ServerListManager.h" #include "constant/PropertyKeyConst.h" #include "src/utils/ParamUtils.h" #include "src/log/Logger.h" #include "src/json/JSON.h" using namespace std; namespace nacos{ void ServerListManager::addToSrvList(NacosString &address) { address = ParamUtils::trim(address); NacosString address_lc = ParamUtils::toLower(address); if (address_lc.find("http://") == 0 || address_lc.find("https://") == 0) { size_t startPos = address.find(':');//4=http,5=https //use http://someaddress[:port] as server address NacosString ip = address; int port = PropertyKeyConst::NACOS_DEFAULT_PORT; size_t pos = address.find_last_of(':'); if (pos != 4 && pos != 5) { NacosString portStr = address.substr(pos + 1); port = atoi(portStr.c_str()); ip = address.substr(0, pos); } NacosServerInfo curServer; curServer.setKey(address); curServer.setAlive(true); curServer.setIp(ip); curServer.setPort(port); curServer.setWeight(1.0); curServer.setAdWeight(1.0); curServer.setMode(startPos == 4 ? NacosServerInfo::mode_http : NacosServerInfo::mode_http_safe); serverList.push_back(curServer); } else if (address.find(':') == std::string::npos) { //If the address doesn't contain port, add 8848 as the default port for it NacosServerInfo curServer; curServer.setKey("http://" + address + ":" + NacosStringOps::valueOf(PropertyKeyConst::NACOS_DEFAULT_PORT)); curServer.setAlive(true); curServer.setIp("http://" + address); curServer.setPort(PropertyKeyConst::NACOS_DEFAULT_PORT); curServer.setWeight(1.0); curServer.setAdWeight(1.0); curServer.setMode(NacosServerInfo::mode_http); serverList.push_back(curServer); } else { //user specified address & port vector explodedAddress; ParamUtils::Explode(explodedAddress, address, ':'); NacosServerInfo curServer; curServer.setKey("http://" + address); curServer.setAlive(true); curServer.setIp("http://" + explodedAddress[0]); curServer.setPort(atoi(explodedAddress[1].c_str())); curServer.setWeight(1.0); curServer.setAdWeight(1.0); curServer.setMode(NacosServerInfo::mode_http); serverList.push_back(curServer); } } ServerListManager::ServerListManager(list &fixed) { started = false; isFixed = true; _pullThread = NULL; refreshInterval = 30000; for (list::iterator it = fixed.begin(); it != fixed.end(); it++) { addToSrvList(*it); } } NacosString ServerListManager::getCurrentServerAddr() { //By default, this function returns a server in the serverList randomly //later we should sort it according to the java client and use cache ReadGuard _readGuard(rwLock); size_t max_serv_slot = serverList.size(); srand(time(NULL)); int to_skip = rand() % max_serv_slot; std::list::iterator it = serverList.begin(); for (int skipper = 0; skipper < to_skip; skipper++) { it++; } return it->getCompleteAddress(); } void ServerListManager::initAll() NACOS_THROW(NacosException) { serverList.clear(); Properties props = _objectConfigData->_appConfigManager->getAllConfig(); if (props.contains(PropertyKeyConst::SERVER_ADDR)) {//Server address is configured isFixed = true; NacosString server_addr = props[PropertyKeyConst::SERVER_ADDR]; vector explodedServers; ParamUtils::Explode(explodedServers, server_addr, ','); for (vector::iterator it = explodedServers.begin(); it != explodedServers.end(); it++) { addToSrvList(*it); } serverList.sort(); } else {//use endpoint mode to pull nacos server info from server if (!props.contains(PropertyKeyConst::ENDPOINT)) { throw NacosException(NacosException::CLIENT_INVALID_PARAM, "no server address specified and the endpoint is blank"); } isFixed = false; const NacosString& endpoint = getEndpoint(); NacosString endpoint_lc = ParamUtils::toLower(endpoint); //endpoint doesn't start with http or https prefix, consider it as http if (endpoint_lc.find("http://") == NacosString::npos && endpoint_lc.find("https://") == NacosString::npos) { endpoint_lc = "http://" + endpoint_lc; } addressServerUrl = endpoint_lc + ":" + NacosStringOps::valueOf(getEndpointPort()); const NacosString& endpointContextPath = getEndpointContextPath(); const NacosString& contextPath = endpointContextPath.empty() ? getContextPath() : endpointContextPath; if (!contextPath.empty()) { if (contextPath[0] != '/') { // not start by ’/‘ addressServerUrl += ("/" + contextPath); } else { addressServerUrl += contextPath; } } const NacosString& clusterName = getClusterName(); if (!clusterName.empty()) { if (clusterName[0] != '/') { addressServerUrl += ("/" + clusterName); } else { addressServerUrl += clusterName; } } const NacosString& namespaceInfo = getNamespace(); if (!namespaceInfo.empty()) { addressServerUrl += ("?namespace=" + namespaceInfo); } const NacosString& endpointQueryParams = getEndpointQueryParams(); if (!endpointQueryParams.empty()) { addressServerUrl += ((namespaceInfo.empty() ? "?" : "&") + endpointQueryParams); } log_debug("Assembled addressServerUrl:%s\n", addressServerUrl.c_str()); serverList = pullServerList(); start(); } } ServerListManager::ServerListManager(ObjectConfigData *objectConfigData) NACOS_THROW(NacosException) { started = false; _pullThread = NULL; _objectConfigData = objectConfigData; refreshInterval = atoi(_objectConfigData->_appConfigManager->get(PropertyKeyConst::SRVLISTMGR_REFRESH_INTERVAL).c_str()); initAll(); } list ServerListManager::tryPullServerListFromNacosServer() NACOS_THROW(NacosException) { std::list headers; std::list paramValues; size_t maxSvrSlot = serverList.size(); if (maxSvrSlot == 0) { throw NacosException(0, "failed to get nacos servers, raison: no server(s) available"); } log_debug("nr_servers:%d\n", maxSvrSlot); srand(time(NULL)); long _read_timeout = _objectConfigData->_appConfigManager->getServeReqTimeout(); NacosString errmsg; for (size_t i = 0; i < serverList.size(); i++) { size_t selectedServer = rand() % maxSvrSlot; const NacosServerInfo &server = ParamUtils::getNthElem(serverList, selectedServer); log_debug("selected_server:%d\n", selectedServer); log_debug("Trying to access server:%s\n", server.getCompleteAddress().c_str()); try { HttpResult serverRes = _objectConfigData->_httpDelegate->httpGet( server.getCompleteAddress() + "/" + _objectConfigData->_appConfigManager->getContextPath() + "/" + ConfigConstant::PROTOCOL_VERSION + "/" + ConfigConstant::GET_SERVERS_PATH, headers, paramValues, NULLSTR, _read_timeout); return JSON::Json2NacosServerInfo(serverRes.content); } catch (NacosException &e) { errmsg = e.what(); log_error("request %s failed.\n", server.getCompleteAddress().c_str()); } catch (exception &e) { errmsg = e.what(); log_error("request %s failed.\n", server.getCompleteAddress().c_str()); } selectedServer = (selectedServer + 1) % serverList.size(); } throw NacosException(0, "failed to get nacos servers after all servers(" + toString() + ") tried: " + errmsg); } list ServerListManager::pullServerList() NACOS_THROW(NacosException) { std::list headers; std::list paramValues; long _read_timeout = _objectConfigData->_appConfigManager->getServeReqTimeout(); if (!NacosStringOps::isNullStr(addressServerUrl)) { HttpResult serverRes = _objectConfigData->_httpDelegate->httpGet(addressServerUrl, headers, paramValues, NULLSTR, _read_timeout); list explodedServerList; ParamUtils::Explode(explodedServerList, serverRes.content, '\n'); list serversPulled; for (list::const_iterator it = explodedServerList.begin(); it != explodedServerList.end(); it++) { NacosServerInfo curServer; size_t pos = it->find(":"); if (pos == std::string::npos) { curServer.setIp(*it); curServer.setPort(8848); } else { NacosString ip = it->substr(0, pos); NacosString port = it->substr(pos + 1); curServer.setIp(ip); curServer.setPort(atoi(port.c_str())); } curServer.setAlive(true); serversPulled.push_back(curServer); } serversPulled.sort(); log_debug("pullServerList: servers list: %s\n", serverListToString(serversPulled).c_str()); return serversPulled; } //usually this should not be happening throw NacosException(0, "addressServerUrl is not set, please config it on properties"); } std::list ServerListManager::__debug() { return tryPullServerListFromNacosServer(); } NacosString ServerListManager::serverListToString(const std::list &serverList) { NacosString res; bool first = true; for (list::const_iterator it = serverList.begin(); it != serverList.end(); it++) { if (first) { first = false; } else { res += ","; } res += it->toString(); } return res; } NacosString ServerListManager::toString() const { return serverListToString(serverList); } void *ServerListManager::pullWorkerThread(void *param) { ServerListManager *thisMgr = (ServerListManager *) param; while (thisMgr->started) { try { bool changed = false; list serverList = thisMgr->pullServerList(); { ReadGuard _readGuard(thisMgr->rwLock); if (serverList != thisMgr->serverList) { log_debug("Servers got from nacos differs from local one, updating...\n"); changed = true; } } if (changed) { WriteGuard _writeGuard(thisMgr->rwLock); log_debug("updated!\n"); thisMgr->serverList = serverList; } } catch (NacosException &e) { //Error occured during the invocation, sleep for a longer time sleep(thisMgr->refreshInterval / 1000); } sleep(thisMgr->refreshInterval / 1000); } return NULL; } void ServerListManager::start() { if (started && !isFixed) { return; } started = true; if (_pullThread == NULL) { NacosString threadName = getClusterName() + "," + getEndpoint() + ":" + NacosStringOps::valueOf(getEndpointPort()) + "-" + getNamespace(); _pullThread = new Thread(threadName, pullWorkerThread, (void *) this); } _pullThread->start(); } void ServerListManager::stop() { if (!started) { return; } started = false; if (_pullThread != NULL) { _pullThread->join(); _pullThread = NULL; } } const NacosString &ServerListManager::getContextPath() const { return _objectConfigData->_appConfigManager->getContextPath(); } ServerListManager::~ServerListManager() { stop(); if (_pullThread != NULL) { delete _pullThread; _pullThread = NULL; } } int ServerListManager::getServerCount() { int count = 0; { ReadGuard _readGuard(rwLock); count = serverList.size(); } return count; }; list ServerListManager::getServerList() { //further optimization could be implemented here if the server list cannot be changed during runtime std::list res; { ReadGuard _readGuard(rwLock); res = serverList; } return res; }; }//namespace nacos ================================================ FILE: src/server/ServerListManager.h ================================================ #ifndef __SERVER_LIST_MGR_H_ #define __SERVER_LIST_MGR_H_ #include #include "NacosString.h" #include "Properties.h" #include "NacosExceptions.h" #include "src/server/NacosServerInfo.h" #include "src/http/HttpDelegate.h" #include "src/thread/Thread.h" #include "src/config/AppConfigManager.h" #include "constant/PropertyKeyConst.h" #include "src/thread/RWLock.h" #include "src/factory/ObjectConfigData.h" #include "Compatibility.h" namespace nacos{ class ServerListManager { private: //status info bool started; bool isFixed; //Nacos server info std::list serverList; //url to pull nacos cluster nodes info from NacosString addressServerUrl; //Worker thread Thread *_pullThread; static void *pullWorkerThread(void *param); long refreshInterval;//in Millis RWLock rwLock;//to lock the serverList void initAll() NACOS_THROW(NacosException); void addToSrvList(NacosString &address); std::list tryPullServerListFromNacosServer() NACOS_THROW(NacosException); std::list pullServerList() NACOS_THROW(NacosException); ObjectConfigData *_objectConfigData; static NacosString serverListToString(const std::list &serverList); public: //Cluster info inline const NacosString& getClusterName() { return _objectConfigData->_appConfigManager->get(PropertyKeyConst::CLUSTER_NAME); }; inline const NacosString& getEndpoint() { return _objectConfigData->_appConfigManager->get(PropertyKeyConst::ENDPOINT); }; inline int getEndpointPort() { return atoi(_objectConfigData->_appConfigManager->get(PropertyKeyConst::ENDPOINT_PORT).c_str()); }; inline const NacosString &getContextPath() const; inline const NacosString& getEndpointContextPath() { return _objectConfigData->_appConfigManager->get(PropertyKeyConst::ENDPOINT_CONTEXT_PATH); } inline const NacosString& getNamespace() { return _objectConfigData->_appConfigManager->get(PropertyKeyConst::NAMESPACE); }; inline const NacosString& getEndpointQueryParams() { return _objectConfigData->_appConfigManager->get(PropertyKeyConst::ENDPOINT_QUERY_PARAMS); } std::list __debug();//DO NOT use, may be changed without prior notification HttpDelegate *getHttpDelegate() const { return _objectConfigData->_httpDelegate; }; void setHttpDelegate(HttpDelegate *httpDelegate) { _objectConfigData->_httpDelegate = httpDelegate; }; AppConfigManager *getAppConfigManager() const { return _objectConfigData->_appConfigManager; }; void setAppConfigManager(AppConfigManager *_appConfigManager) { _objectConfigData->_appConfigManager = _appConfigManager; }; ServerListManager(std::list &fixed); ServerListManager(ObjectConfigData *objectConfigData) NACOS_THROW(NacosException); NacosString getCurrentServerAddr(); int getServerCount(); std::list getServerList(); NacosString toString() const; void start(); void stop(); ~ServerListManager(); }; }//namespace nacos #endif ================================================ FILE: src/thread/BlockingQueue.h ================================================ #ifndef __BLOCKING_Q_H_ #define __BLOCKING_Q_H_ #include #include "src/thread/Mutex.h" /* * BlockingQueue.h * Thanks to Shuo, Chen's muduo: https://github.com/chenshuo/muduo/blob/master/muduo/base/BlockingQueue.h */ namespace nacos{ template class BlockingQueue { private: Mutex _mutex; Condition _notEmpty; Condition _notFull; std::deque _queue; size_t _maxSize; volatile bool _full; volatile bool _empty; public: bool full() { LockGuard lockguard(_mutex); return _full; }; bool empty() { LockGuard lockguard(_mutex); return _empty; }; BlockingQueue() : _mutex(), _notEmpty(_mutex), _notFull(_mutex), _maxSize(64), _full(false), _empty(true) {}; BlockingQueue(size_t queueSize) : _mutex(), _notEmpty(_mutex), _notFull(_mutex), _maxSize(queueSize), _full(false), _empty(true) {}; void enqueue(const T &data) { LockGuard lockguard(_mutex); _empty = false; while (_queue.size() == _maxSize) { _full = true; _notFull.wait(); } _queue.push_back(data); _notEmpty.notify(); } T dequeue() { LockGuard lockguard(_mutex); _full = false; while (_queue.empty()) { _empty = true; _notEmpty.wait(); } T front = _queue.front(); _queue.pop_front(); _notFull.notify(); return front; } }; }//namespace nacos #endif ================================================ FILE: src/thread/DelayedThreadPool.cpp ================================================ #include #include "src/thread/DelayedThreadPool.h" #include "NacosExceptions.h" namespace nacos { /** * A thread pool that can run tasks with specified delay */ class DelayedWorker : public Task { private: DelayedThreadPool &_container; public: volatile bool _start; DelayedWorker(DelayedThreadPool &container) : _container(container) { _start = false; }; void run() { log_debug("DelayedWorker::run()\n"); while (!_container._stop_delayed_tp) { _container._lockForScheduleTasks.lock(); //no data, wait until data is available log_debug("[DelayedWorker] start to wait for task stop=%d\n", _container._stop); if (_container._scheduledTasks.empty()) { log_debug("[DelayedWorker] empty, wait for task\n"); if (_container._stop_delayed_tp) { _container._lockForScheduleTasks.unlock(); return; } _container._delayTaskNotEmpty.wait(); log_debug("[DelayedWorker] wake up due to incoming event\n"); } /* * Here actually we have 2 different design patterns: * 1. wait both on the _delayTaskNotEmpty condition and the nearest task to be executed * 2. wait on the nearest task to be executed * * The advantages/disadvantages for pattern 1 (the pattern applied here). * The advantages are that: * 1. This design pattern is more responsive * 2. For tasks that take a short time, this pattern is more effective since one worker can handle more tasks * (compared with the pattern that wait() for exact one task in else {} block) * * The disadvantages are that: * 1. Will incur more wake-ups and context-switches(I guess) * 2. Not as precise as the pattern 2 * */ log_debug("[DelayedWorker] iterating on _scheduledTasks\n"); std::vector< std::pair >::iterator it; while ((it = _container._scheduledTasks.begin()) != _container._scheduledTasks.end()) { int64_t now_time = TimeUtils::getCurrentTimeInMs(); log_debug("[DelayedWorker] now = %ld wakeup time = %ld\n", now_time, it->first); if (it->first <= now_time) { Task *task = it->second; _container._scheduledTasks.erase(it); _container._lockForScheduleTasks.unlock(); //the task can also attempt to retrieve the lock if (_container._stop_delayed_tp) { return; } task->run(); _container._lockForScheduleTasks.lock(); log_debug("[DelayedWorker] continue 2 next task\n"); } else { //awake from sleep when a stop signal is sent if (_container._stop_delayed_tp) { _container._lockForScheduleTasks.unlock(); return; } _container._delayTaskNotEmpty.wait(it->first - now_time); } } _container._lockForScheduleTasks.unlock(); } _start = false; } }; DelayedThreadPool::DelayedThreadPool(const NacosString &poolName, size_t poolSize) :ThreadPool(poolName, poolSize),_delayTaskNotEmpty(_lockForScheduleTasks), _stop_delayed_tp(true) { log_debug("DelayedThreadPool::DelayedThreadPool() name = %s size = %d\n", poolName.c_str(), poolSize); if (poolSize <= 0) { throw NacosException(NacosException::INVALID_PARAM, "Poll size cannot be lesser than 0"); } delayTasks = new DelayedWorker*[poolSize]; log_debug("DelayedThreadPool::DelayedThreadPool initializing tasks\n"); for (size_t i = 0; i < poolSize; i++) { delayTasks[i] = new DelayedWorker(*this); } } DelayedThreadPool::~DelayedThreadPool() { log_debug("DelayedThreadPool::~DelayedThreadPool\n"); if (delayTasks != NULL) { for (size_t i = 0; i < _poolSize; i++) { delete delayTasks[i]; delayTasks[i] = NULL; } delete [] delayTasks; delayTasks = NULL; } } struct tagAscOrdFunctor{ bool operator ()(const std::pair &lhs, const std::pair &rhs) { return lhs.first < rhs.first; }; } ascOrdFunctor = {}; #include //futureTimeToRun: time in MS void DelayedThreadPool::schedule(Task *t, long futureTimeToRun) { if (_stop) { return; } if (futureTimeToRun < 0) { throw NacosException(NacosException::INVALID_PARAM, "futureTimeToRun must not be negative"); } log_debug("DelayedThreadPool::schedule() name=%s future = %ld\n", t->getTaskName().c_str(), futureTimeToRun); std::pair scheduledTask = std::make_pair (futureTimeToRun, t); { LockGuard __lockSchedTasks(_lockForScheduleTasks); _scheduledTasks.push_back(scheduledTask); std::sort(_scheduledTasks.begin(), _scheduledTasks.end(), ascOrdFunctor); _delayTaskNotEmpty.notifyAll(); } } void DelayedThreadPool::start() { _stop_delayed_tp = false; ThreadPool::start(); log_debug("DelayedThreadPool::start()\n"); for (size_t i = 0; i < _poolSize; i++) { delayTasks[i]->_start = true; put((Task*)delayTasks[i]); } } void DelayedThreadPool::stop() { if (_stop_delayed_tp) { return; } _stop_delayed_tp = true; _delayTaskNotEmpty.notifyAll(); for (std::list::iterator it = _threads.begin(); it != _threads.end(); it++) { (*it)->kill(); } ThreadPool::stop(); } } ================================================ FILE: src/thread/DelayedThreadPool.h ================================================ #ifndef __DELAYED_THREAD_POOL_H_ #define __DELAYED_THREAD_POOL_H_ #include #include #include "src/thread/ThreadPool.h" #include "src/thread/Task.h" #include "src/thread/Mutex.h" namespace nacos { class DelayedWorker; class DelayedThreadPool : public ThreadPool { private: Condition _delayTaskNotEmpty; Mutex _lockForScheduleTasks;//for _scheduledTasks std::vector< std::pair > _scheduledTasks; DelayedThreadPool(); DelayedWorker **delayTasks; volatile bool _stop_delayed_tp; public: DelayedThreadPool(const NacosString &poolName, size_t poolSize) ; ~DelayedThreadPool(); /** * schedule the execution for a task * @param t the task to run * @param futureTimeToRun the time (in ms) for the task to run */ void schedule(Task *t, long futureTimeToRun); friend class DelayedWorker; virtual void start(); virtual void stop(); }; } #endif ================================================ FILE: src/thread/Mutex.h ================================================ #ifndef __MUTEX_H_ #define __MUTEX_H_ #include #include #include "Tid.h" #include "src/utils/TimeUtils.h" /* * Mutex.h * Author: Liu, Hanyu * Thanks to Shuo, Chen's muduo: * https://github.com/chenshuo/muduo/blob/master/muduo/base/Mutex.h */ namespace nacos{ class Mutex { friend class Condition; private: TID_T _holder; pthread_mutex_t _mutex; public: Mutex() { pthread_mutex_init(&_mutex, NULL); }; ~Mutex() { pthread_mutex_destroy(&_mutex); }; void lock() { pthread_mutex_lock(&_mutex); assignHolder(); }; void unlock() { unassignHolder(); pthread_mutex_unlock(&_mutex); }; pthread_mutex_t *getPthreadMutex() { return &_mutex; }; void assignHolder() { _holder = gettidv1(); }; void unassignHolder() { _holder = 0; }; }; class Condition { private: Mutex &_mutex; pthread_cond_t _cond; public: Condition(Mutex &mutex) : _mutex(mutex) { pthread_cond_init(&_cond, NULL); }; ~Condition() { pthread_cond_destroy(&_cond); }; int wait() { return pthread_cond_wait(&_cond, _mutex.getPthreadMutex()); } int wait(long millis) { struct timeval now; struct timespec wakeup_time; TimeUtils::getCurrentTimeInStruct(now); now.tv_usec = now.tv_usec + millis * 1000; now.tv_sec = now.tv_sec + now.tv_usec / 1000000; now.tv_usec = now.tv_usec % 1000000; wakeup_time.tv_nsec = now.tv_usec * 1000; wakeup_time.tv_sec = now.tv_sec; //std::cout << " millis:" << millis //<< " wakeup time:sec:" << wakeup_time.tv_sec << " nsec:" << wakeup_time.tv_nsec << std::endl; return pthread_cond_timedwait(&_cond, _mutex.getPthreadMutex(), &wakeup_time); } void notify() { pthread_cond_signal(&_cond); } void notifyAll() { pthread_cond_broadcast(&_cond); } }; class LockGuard { private: Mutex &_mutex; public: LockGuard(Mutex &mutex) : _mutex(mutex) { _mutex.lock(); }; ~LockGuard() { _mutex.unlock(); }; }; }//namespace nacos #endif ================================================ FILE: src/thread/RWLock.h ================================================ #ifndef __RWLOCK_H_ #define __RWLOCK_H_ #include #include /* * Mutex.h * Author: Liu, Hanyu * An encapsulation of pthread_rwlock */ namespace nacos{ class RWLock { private: pthread_rwlock_t _pthread_rwlock; public: RWLock() { pthread_rwlock_init(&_pthread_rwlock, NULL); }; ~RWLock() { pthread_rwlock_destroy(&_pthread_rwlock); }; int unlock() { return pthread_rwlock_unlock(&_pthread_rwlock); }; int readLock() { return pthread_rwlock_rdlock(&_pthread_rwlock); }; int writeLock() { return pthread_rwlock_wrlock(&_pthread_rwlock); }; }; class ReadGuard { private: RWLock &_rwLock; public: ReadGuard(RWLock &rwLock) : _rwLock(rwLock) { _rwLock.readLock(); } ~ReadGuard() { _rwLock.unlock(); } }; class WriteGuard { private: RWLock &_rwLock; public: WriteGuard(RWLock &rwLock) : _rwLock(rwLock) { _rwLock.writeLock(); } ~WriteGuard() { _rwLock.unlock(); } }; }//namespace nacos #endif ================================================ FILE: src/thread/Task.h ================================================ #ifndef __TASK_H_ #define __TASK_H_ #include "NacosString.h" namespace nacos{ class Task { private: NacosString _taskName; public: virtual void run() = 0; virtual ~Task() {}; void setTaskName(const NacosString &taskName) { _taskName = taskName; }; const NacosString &getTaskName() const { return _taskName; }; }; }//namespace nacos #endif ================================================ FILE: src/thread/Thread.cpp ================================================ #include "Thread.h" using namespace nacos; struct sigaction Thread::old_action; void Thread::Init() { struct sigaction action; action.sa_flags = 0; action.sa_handler = empty_signal_handler; sigemptyset(&action.sa_mask); sigaction(THREAD_STOP_SIGNAL, &action, &Thread::old_action); }; void Thread::DeInit() { sigaction(THREAD_STOP_SIGNAL, &Thread::old_action, NULL); }; void *Thread::threadFunc(void *param) { Thread *currentThread = (Thread *) param; currentThread->_tid = gettidv1(); try { return currentThread->_function(currentThread->_threadData); } catch (std::exception &e) { currentThread->_function = NULL; log_error("Exception happens when executing:\n"); log_error("Thread Name:%s Thread Id:%d\n", currentThread->_threadName.c_str(), currentThread->_tid); log_error("Raison:%s", e.what()); abort(); } catch (...) { currentThread->_function = NULL; log_error("Unknown exception happens when executing:\n"); log_error("Thread Name:%s Thread Id:%d\n", currentThread->_threadName.c_str(), currentThread->_tid); throw; } } void Thread::start() { _start = true; pthread_create(&_thread, NULL, threadFunc, (void *) this); } void Thread::join() { log_debug("Calling Thread::join() on %s\n", _threadName.c_str()); if (!_start) { log_debug("Thread::join() called on stopped thread for %s\n", _threadName.c_str()); return; } pthread_join(_thread, NULL); } void Thread::kill() { pthread_kill(_thread, THREAD_STOP_SIGNAL); } ================================================ FILE: src/thread/Thread.h ================================================ #ifndef __THREAD_H_ #define __THREAD_H_ #include #include #include #include #include "NacosString.h" #include "src/log/Logger.h" #include "src/thread/Tid.h" #define THREAD_STOP_SIGNAL SIGUSR1 namespace nacos{ typedef void *(*ThreadFn)(void *); /* * Thread.h * Author: Liu, Hanyu * This is NOT like the thread class in Java! * It's just a simple encapsulation of pthread_create() and pthread_join * It doesn't have a virtual run() function, * a function pointer(ThreadFn) should be passed to the constructor so it will be used as the function pointer parameter for pthread_create */ class Thread { private: NacosString _threadName; pthread_t _thread; ThreadFn _function; //TODO:thread id TID_T _tid; bool _start; void *_threadData; Thread() {}; static void empty_signal_handler(int signum) {}; static struct sigaction old_action; public: static void Init(); static void DeInit(); void setThreadName(const NacosString &threadName) { _threadName = threadName; }; NacosString getThreadName() { return _threadName; }; static void *threadFunc(void *param); Thread(const NacosString &threadName, ThreadFn fn) : _threadName(threadName), _function(fn), _threadData(NULL) { _start = false; }; Thread(const NacosString &threadName, ThreadFn fn, void *threadData) : _threadName(threadName), _function(fn), _threadData(threadData) { _start = false; }; ~Thread() { _start = false; } void start(); void join(); void kill(); }; }//namespace nacos #endif ================================================ FILE: src/thread/ThreadLocal.h ================================================ #ifndef __THREAD_LOCAL_H_ #define __THREAD_LOCAL_H_ #include namespace nacos{ template class ThreadLocal; template struct ObjectWrapper { T wrappedObject; ThreadLocal *threadLocalObj; }; template class ThreadLocal{ private: pthread_key_t pthreadKey; T _defaultValue; static void destroyer(void *param) { ObjectWrapper *wrapper = reinterpret_cast< ObjectWrapper *>(param); wrapper->threadLocalObj->onDestroy(&wrapper->wrappedObject); delete wrapper; } ObjectWrapper *getWrapper() const { ObjectWrapper *wrapper = reinterpret_cast< ObjectWrapper *>(pthread_getspecific(pthreadKey)); return wrapper; } ObjectWrapper *createWrapper() { ObjectWrapper *wrapper = new ObjectWrapper; wrapper->threadLocalObj = this; onCreate(&wrapper->wrappedObject); pthread_setspecific(pthreadKey, reinterpret_cast(wrapper)); return wrapper; } public: ThreadLocal() { /* init the curl session */ pthread_key_create(&pthreadKey, destroyer); } ThreadLocal(T defaultValue) { _defaultValue = defaultValue; /* init the curl session */ pthread_key_create(&pthreadKey, destroyer); } void set(T value) { ObjectWrapper *wrapper = getWrapper(); if (wrapper == NULL) { wrapper = createWrapper(); } wrapper->wrappedObject = value; } T get() { ObjectWrapper *wrapper = getWrapper(); if (wrapper == NULL) { wrapper = createWrapper(); } return wrapper->wrappedObject; } virtual ~ThreadLocal() { ObjectWrapper *wrapper = getWrapper(); if (wrapper != NULL) { wrapper->threadLocalObj->onDestroy(&wrapper->wrappedObject); delete wrapper; } pthread_key_delete(pthreadKey); } virtual void onCreate(T *value) { //do nothing by default; //!!!!!shall NOT access anything other than VALUE!!!!! *value = _defaultValue; } virtual void onDestroy(T *value) { //do nothing by default; //!!!!!shall NOT access anything other than VALUE!!!!! } }; }//namespace nacos #endif ================================================ FILE: src/thread/ThreadPool.cpp ================================================ #include #include "ThreadPool.h" #include "Task.h" using namespace std; namespace nacos{ DummyTask ThreadPool::_dummyTask; void *ThreadPool::runInThread(void *param) { ThreadPool *thisobj = (ThreadPool *) param; log_debug("ThreadPool::runInThread()\n"); while (!thisobj->_stop) { Task *t = thisobj->take(); NacosString taskName = t->getTaskName(); log_debug("Thread got task:%s\n", taskName.c_str()); try { t->run(); } catch (exception &e) { log_error("Exception happens when executing:\n"); log_error("Thread pool Name:%s Task name:%s\n", thisobj->_poolName.c_str(), taskName.c_str()); log_error("Raison:%s", e.what()); } catch (...) { log_error("Unknown exception happens when executing:\n"); log_error("Thread pool Name:%s Task name:%s\n", thisobj->_poolName.c_str(), taskName.c_str()); throw; } log_debug("Thread finished task:%s without problem\n", taskName.c_str()); } return NULL; } Task *ThreadPool::take() { LockGuard _lockGuard(_lock); while (_taskList.empty() && !_stop) { _NotEmpty.wait(); } if (!_taskList.empty()) { Task *curTask = _taskList.front(); _taskList.pop_front(); _NotFull.notify(); return curTask; } return &_dummyTask; }; void ThreadPool::put(Task *t) { { LockGuard _lockGuard(_lock); log_debug("ThreadPool:::::taskList:%d poolSize:%d stop:%d\n", _taskList.size(), _poolSize, _stop); while (!(_taskList.size() < _poolSize) && !_stop) { _NotFull.wait(); } if (!_stop) { _taskList.push_back(t); _NotEmpty.notify(); return; } } //The thread pool is stopped, we need to run it locally log_debug("Running locally since the threadpool is stopped\n"); t->run(); }; void ThreadPool::start() { log_warn("ThreadPool::start() start\n"); if (!_stop) { log_warn("Thread pool named '%s' is started multiple times\n", _poolName.c_str()); return; } _stop = false; for (size_t i = 0; i < _poolSize; i++) { Thread *currentThread = new Thread(_poolName + "-poolthread-" + NacosStringOps::valueOf(i), runInThread, this); _threads.push_back(currentThread); currentThread->start(); } }; void ThreadPool::stop() { if (_stop) { return; } _stop = true; { LockGuard _lockGuard(_lock); _NotEmpty.notifyAll(); _NotFull.notifyAll(); } for (std::list::iterator it = _threads.begin(); it != _threads.end(); it++) { (*it)->join(); delete *it; } _threads.clear(); }; }//namespace nacos ================================================ FILE: src/thread/ThreadPool.h ================================================ #ifndef __THREAD_POOL_H_ #define __THREAD_POOL_H_ #include #include #include "Thread.h" #include "Task.h" #include "NacosString.h" #include "src/thread/Mutex.h" namespace nacos{ class DummyTask : public Task { public: DummyTask() { setTaskName("DummyTask"); }; void run() {}; }; class ThreadPool { private: NacosString _poolName; std::deque _taskList; Mutex _lock; Condition _NotEmpty; Condition _NotFull; static DummyTask _dummyTask; static void *runInThread(void *param); ThreadPool() : _poolName("CannotBeCreated"), _NotEmpty(_lock), _NotFull(_lock), _stop(true), _poolSize(0) {}; protected: std::list _threads; volatile bool _stop; size_t _poolSize; public: ThreadPool(const NacosString &poolName, size_t poolSize) : _poolName(poolName), _NotEmpty(_lock), _NotFull(_lock), _stop(true), _poolSize(poolSize) { }; ThreadPool(size_t poolSize) : _poolName("NacosCliWorkerThread"), _NotEmpty(_lock), _NotFull(_lock), _stop(true), _poolSize(poolSize) { }; virtual ~ThreadPool() {}; Task *take(); void put(Task *t); virtual void start(); virtual void stop(); }; }//namespace nacos #endif ================================================ FILE: src/thread/Tid.cpp ================================================ #include "src/thread/Tid.h" #if defined(__CYGWIN__) || defined(MS_WINDOWS) //TODO:for windows/cygwin #elif defined(linux) //for linux //solved in header file #elif defined(__APPLE__) && defined(__MACH__) //Mac OS code goes here #include TID_T gettidv1() { TID_T tid; pthread_threadid_np(NULL, &tid); return tid; } #else //regard the system as an unix-like system //for linux solved in header file #endif//OS-specific code ================================================ FILE: src/thread/Tid.h ================================================ #ifndef __TID_HELPER_H_ #define __TID_HELPER_H_ #if defined(__CYGWIN__) || defined(MS_WINDOWS) //TODO:for windows/cygwin #include #include #define TID_T pid_t #define gettidv1() syscall(__NR_gettid) #elif defined(linux) || defined(LINUX) //for linux #include #include #define TID_T pid_t #define gettidv1() syscall(__NR_gettid) //#define gettidv2() syscall(SYS_gettid) #elif defined(__APPLE__) && defined(__MACH__) //Mac OS code goes here #define TID_T unsigned long long TID_T gettidv1(); #else //regard the system as an unix-like system #include #include #define TID_T pid_t #define gettidv1() syscall(__NR_gettid) #endif//OS-specific code #endif//HEADER_GUARD ================================================ FILE: src/utils/ConfigParserUtils.cpp ================================================ // // Created by liuhanyu on 2021/1/9. // #include #include "ConfigParserUtils.h" #include "src/config/IOUtils.h" #include "src/utils/ParamUtils.h" #include "constant/ConfigConstant.h" using namespace std; namespace nacos { Properties ConfigParserUtils::parseConfigFile(const NacosString &file) NACOS_THROW(NacosException) { Properties parsedConfig; NacosString confContent = IOUtils::readStringFromFile(file, NULLSTR);//TODO: add encoding support vector configList; ParamUtils::Explode(configList, confContent, ConfigConstant::CONFIG_NEXT_LINE); int line = 0; for (vector::iterator it = configList.begin(); it != configList.end(); it++) { line++; NacosString trimmedLine = ParamUtils::trim(*it); if (ParamUtils::isBlank(trimmedLine)) { continue; } if (trimmedLine[0] == '#') { //skip comment continue; } if (it->find(ConfigConstant::CONFIG_KV_SEPARATOR) == std::string::npos) { throw MalformedConfigException(file, " no '=' found at line " + NacosStringOps::valueOf(line)); } vector configKV; ParamUtils::Explode(configKV, *it, ConfigConstant::CONFIG_KV_SEPARATOR); //k = v NacosString key = ParamUtils::trim(configKV[0]); if (ParamUtils::isBlank(key)) { throw MalformedConfigException(file, " key is blank at " + NacosStringOps::valueOf(line)); } if (configKV.size() == 1) { parsedConfig[key] = NULLSTR; } else { parsedConfig[key] = configKV[1]; } } return parsedConfig; } } ================================================ FILE: src/utils/ConfigParserUtils.h ================================================ // // Created by liuhanyu on 2021/1/9. // #ifndef NACOS_SDK_CPP_CONFIGPARSERUTILS_H #define NACOS_SDK_CPP_CONFIGPARSERUTILS_H #include "Properties.h" #include "NacosExceptions.h" #include "Compatibility.h" namespace nacos { class ConfigParserUtils { public: static Properties parseConfigFile(const NacosString &file) NACOS_THROW(NacosException); }; } #endif //NACOS_SDK_CPP_CONFIGPARSERUTILS_H ================================================ FILE: src/utils/DirUtils.cpp ================================================ #include #include #include #include "src/utils/DirUtils.h" #if defined(__CYGWIN__) || defined(MS_WINDOWS) #define PATH_MAX 260 #elif defined(linux) #include #elif defined(FreeBSD) #include #else //we don't know how to handle this situation, check if it's defined //the is not necessarily an issue, actually in most cases it will just work #warning "Unknown system or arch, trying fallback strategy. Please check if the compilation is correct" #ifndef PATH_MAX #define PATH_MAX 260 #endif #endif namespace nacos{ NacosString DirUtils::getHome() { struct passwd *pw = getpwuid(getuid()); NacosString homedir = pw->pw_dir; return homedir; } NacosString DirUtils::getCwd() { char cwd[PATH_MAX]; NacosString cwds; if (getcwd(cwd, sizeof(cwd)) != NULL) { cwds = cwd; return cwds; } return NULLSTR; } }//namespace nacos ================================================ FILE: src/utils/DirUtils.h ================================================ #ifndef __DIR_UTILS_H_ #define __DIR_UTILS_H_ #include "NacosString.h" namespace nacos{ class DirUtils { public: static NacosString getHome(); static NacosString getCwd(); }; }//namespace nacos #endif ================================================ FILE: src/utils/Env.h ================================================ #include const char* getEnv(const char* env) { if (env == NULL) { return NULL; } char* var = getenv(env); return var; } ================================================ FILE: src/utils/GroupKey.h ================================================ #ifndef __GROUP_KEY_H_ #define __GROUP_KEY_H_ #include "NacosString.h" #include "src/utils/url.h" /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * Synthesize the form of dataId+groupId. Escapes reserved characters in dataId and groupId. * * @author Nacos */ namespace nacos{ class GroupKey { public: static NacosString getKey(const NacosString &dataId, const NacosString &group) { //DataID+GroupId return urlencode(dataId) + "+" + urlencode(group); } static NacosString getKeyTenant(const NacosString &dataId, const NacosString &group, const NacosString &tenant) { //DataID+GroupId NacosString key = getKey(dataId, group); if (!isNull(tenant)) { key = key + "+" + urlencode(tenant); } return key; } /*static NacosString getKey(const NacosString &dataId, const NacosString &group, const NacosString &datumStr) { //DataID+GroupId+datumStr return urlencode(dataId) + "+" + urlencode(group) + "+" + urlencode(datumStr); }*/ /*static public NacosString[] parseKey(NacosString groupKey) { StringBuilder sb = new StringBuilder(); NacosString dataId = null; NacosString group = null; NacosString tenant = null; for (int i = 0; i < groupKey.length(); ++i) { char c = groupKey.charAt(i); if ('+' == c) { if (null == dataId) { dataId = sb.toString(); sb.setLength(0); } else if (null == group) { group = sb.toString(); sb.setLength(0); } else { throw new IllegalArgumentException("invalid groupkey:" + groupKey); } } else if ('%' == c) { char next = groupKey.charAt(++i); char nextnext = groupKey.charAt(++i); if ('2' == next && 'B' == nextnext) { sb.append('+'); } else if ('2' == next && '5' == nextnext) { sb.append('%'); } else { throw new IllegalArgumentException("invalid groupkey:" + groupKey); } } else { sb.append(c); } } if (StringUtils.isBlank(group)) { group = sb.toString(); if (group.length() == 0) { throw new IllegalArgumentException("invalid groupkey:" + groupKey); } } else { tenant = sb.toString(); if (group.length() == 0) { throw new IllegalArgumentException("invalid groupkey:" + groupKey); } } return new NacosString[] {dataId, group, tenant}; }*/ /** * + -> %2B % -> %25 */ /*static void urlEncode(NacosString str, StringBuilder sb) { for (int idx = 0; idx < str.length(); ++idx) { char c = str.charAt(idx); if ('+' == c) { sb.append("%2B"); } else if ('%' == c) { sb.append("%25"); } else { sb.append(c); } } }*/ }; }//namespace nacos #endif ================================================ FILE: src/utils/NamingUtils.h ================================================ #ifndef __NAMING_UTILS_H_ #define __NAMING_UTILS_H_ #include #include "NacosString.h" #include "constant/ConfigConstant.h" #include "src/utils/ParamUtils.h" namespace nacos{ class NamingUtils { public: static NacosString getGroupedName(const NacosString &serviceName, const NacosString &groupName) { return groupName + ConfigConstant::SERVICE_INFO_SPLITER + serviceName; } static NacosString getServiceName(const NacosString &serviceNameWithGroup) { if (!ParamUtils::contains(serviceNameWithGroup, ConfigConstant::SERVICE_INFO_SPLITER)) { return serviceNameWithGroup; } std::vector splittedNameNGroup; ParamUtils::Explode(splittedNameNGroup, serviceNameWithGroup, ConfigConstant::SERVICE_INFO_SPLITER); return splittedNameNGroup[1]; } static NacosString getGroupName(const NacosString &serviceNameWithGroup) { if (!ParamUtils::contains(serviceNameWithGroup, ConfigConstant::SERVICE_INFO_SPLITER)) { return ConfigConstant::DEFAULT_GROUP; } std::vector splittedNameNGroup; ParamUtils::Explode(splittedNameNGroup, serviceNameWithGroup, ConfigConstant::SERVICE_INFO_SPLITER); return splittedNameNGroup[0]; } }; }//namespace nacos #endif ================================================ FILE: src/utils/NetUtils.cpp ================================================ #include "NetUtils.h" #include #include #include #include #include #include #include #include #define HOST_AND_LEN 250 namespace nacos{ NacosString NetUtils::getHostIp() NACOS_THROW(NacosException){ struct ifaddrs *ifaddr, *ifa; int s; char host[NI_MAXHOST]; if (getifaddrs(&ifaddr) == -1) { throw NacosException(NacosException::UNABLE_TO_GET_HOST_IP, "Failed to get IF address"); } for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { log_debug("iterating on iface=%s\n", ifa->ifa_name); if (ifa->ifa_addr == NULL || !(ifa->ifa_addr->sa_family==AF_INET)) { continue; } if((strcmp(ifa->ifa_name,"lo")==0)) { continue; } s = getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in),host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST); if (s != 0) { freeifaddrs(ifaddr); throw NacosException(NacosException::UNABLE_TO_GET_HOST_IP, "Failed to get IF address"); } log_debug("selected iface=%s ip=%s\n", ifa->ifa_name, host); freeifaddrs(ifaddr); return host; } //Usually the program will not run to here throw NacosException(NacosException::UNABLE_TO_GET_HOST_IP, "Failed to get IF address"); } NacosString NetUtils::getHostName() NACOS_THROW(NacosException) { char hostname[HOST_AND_LEN]; int res = gethostname(hostname, HOST_AND_LEN); if (res == 0) { return NacosString(hostname); } throw NacosException(NacosException::UNABLE_TO_GET_HOST_NAME, "Failed to get hostname, errno = " + NacosStringOps::valueOf(errno)); } }//namespace nacos ================================================ FILE: src/utils/NetUtils.h ================================================ #ifndef __NET_UTILS_H_ #define __NET_UTILS_H_ #include "NacosString.h" #include "NacosExceptions.h" #include "Compatibility.h" namespace nacos{ class NetUtils { public: //Get IP address (best guess) static NacosString getHostIp() NACOS_THROW(NacosException); //Get hostname static NacosString getHostName() NACOS_THROW(NacosException); }; }//namespace nacos #endif ================================================ FILE: src/utils/ParamUtils.h ================================================ #ifndef __PARMUTILS_H_ #define __PARMUTILS_H_ #include #include #include #include #include "NacosString.h" #include "NacosExceptions.h" #include "constant/ConfigConstant.h" namespace nacos{ class ParamUtils { public: template static const T &getNthElem(const std::list &parm, size_t i) { assert(parm.size() > i); typename std::list::const_iterator it = parm.begin(); for (size_t skipper = 0; skipper < i; skipper++) { it++; } return *it; } static NacosString trim(const NacosString &content) { int start = 0; int end = content.size() - 1; while (start < end && isBlank(content[start])) { start++; } while (start < end && isBlank(content[end])) { end--; } return NacosString(content.substr(start, end - start + 1)); } static NacosString null2defaultGroup(const NacosString &group) { return (isNull(group)) ? ConfigConstant::DEFAULT_GROUP : ParamUtils::trim(group); } static void parseString2KeyGroupTenant(const NacosString &stringToParse, NacosString &dataId, NacosString &group, NacosString &tenant) { std::vector KGT;//KeyGroupTenant Explode(KGT, stringToParse, ConfigConstant::WORD_SEPARATOR); dataId = KGT[0]; group = KGT[1]; if (KGT.size() == 3)//with tenant { tenant = KGT[2]; } else { tenant = NULLSTR; } } static bool isBlank(char character) { switch (character) { case ' ': case '\t': case '\r': case '\n': return true; default: return false; } } static bool isBlank(const NacosString &content) { //TODO:Apply ParamUtils.Java's logic to here, support whitespaces in other countries/zones if (content.size() == 0) { return true; } for (size_t i = 0; i < content.size(); i++) { if (!isBlank(content[i])) return false; } return true; }; static bool isValid(const NacosString &content) { return false; }; static void checkParam(const NacosString &dataId, const NacosString &group, const NacosString &content) NACOS_THROW(NacosException) { if (isBlank(content)) { throw NacosException(NacosException::CLIENT_INVALID_PARAM, "content invalid"); } }; //A little trick here for NacosString constants static void Explode(std::vector &explodedList, const NacosString &stringToExplode, const NacosString &separator) { size_t start_pos = 0; size_t separator_len = separator.length(); size_t cur_pos = 0; cur_pos = stringToExplode.find(separator, start_pos); //break the string with separator while (cur_pos != std::string::npos) { NacosString cur_addr = stringToExplode.substr(start_pos, cur_pos - start_pos); explodedList.push_back(cur_addr); start_pos = cur_pos + separator_len; cur_pos = stringToExplode.find(separator, start_pos); } //deal with the last string NacosString last_addr = stringToExplode.substr(start_pos); explodedList.push_back(last_addr); } static void Explode(std::vector &explodedList, const NacosString &stringToExplode, char separator) { size_t start_pos = 0; size_t cur_pos = 0; cur_pos = stringToExplode.find(separator, start_pos); //break the string with separator while (cur_pos != std::string::npos) { NacosString cur_addr = stringToExplode.substr(start_pos, cur_pos - start_pos); explodedList.push_back(cur_addr); start_pos = cur_pos + 1; cur_pos = stringToExplode.find(separator, start_pos); } //deal with the last string NacosString last_addr = stringToExplode.substr(start_pos); explodedList.push_back(last_addr); } static void Explode(std::list &explodedList, const NacosString &stringToExplode, char separator) { size_t start_pos = 0; size_t cur_pos = 0; cur_pos = stringToExplode.find(separator, start_pos); //break the string with separator while (cur_pos != std::string::npos) { NacosString cur_addr = stringToExplode.substr(start_pos, cur_pos - start_pos); explodedList.push_back(cur_addr); start_pos = cur_pos + 1; cur_pos = stringToExplode.find(separator, start_pos); } //deal with the last string NacosString last_addr = stringToExplode.substr(start_pos); explodedList.push_back(last_addr); } //use ',' as separator by default static NacosString Implode(const std::list &toImplode) { return Implode(toImplode, ','); } static NacosString Implode(const std::list &toImplode, char separator) { NacosString implodedString; for (std::list::const_iterator it = toImplode.begin(); it != toImplode.end(); /*it++ is within the for ... loop*/) { implodedString += *it; it++; if (it != toImplode.end()) { implodedString += ","; } } return implodedString; } //use ',' as default separator to serialize a map static NacosString Implode(const std::map &toImplode) { return Implode(toImplode, ','); } static NacosString Implode(const std::map &toImplode, char separator) { NacosString implodedString; for (std::map::const_iterator it = toImplode.begin(); it != toImplode.end(); it++) { implodedString += it->first + "=" + it->second; if (it != toImplode.end()) { implodedString += ","; } } return implodedString; } static bool contains(const NacosString &haystack, char needle) { if (haystack.find(needle) != std::string::npos) { return true; } return false; } static bool contains(const NacosString &haystack, const NacosString &needle) { if (haystack.find(needle) != std::string::npos) { return true; } return false; } static void addKV(std::list &list, const NacosString &key, const NacosString &value) { list.push_back(key); list.push_back(value); } static NacosString toLower(const NacosString &str) { NacosString lowerCaseString; for (NacosString::const_iterator it = str.begin(); it != str.end(); it++) { lowerCaseString.push_back(tolower(*it)); } return lowerCaseString; } static bool equals_ic(const NacosString &str1, const NacosString &str2) { NacosString lcase_str1 = toLower(str1); NacosString lcase_str2 = toLower(str2); return lcase_str1 == lcase_str2; } static const NacosString &findByKey(const std::list &hayStack, const NacosString &needleKey) { for (std::list::const_iterator it = hayStack.begin(); it != hayStack.end(); it++) { if (*it == needleKey) { it++; return *it; } } return NacosStringOps::nullstr; } }; }//namespace nacos #endif ================================================ FILE: src/utils/RandomUtils.cpp ================================================ #include #include #include #include #include #include #include "src/utils/RandomUtils.h" namespace nacos{ int RandomUtils::fd; ThreadLocal RandomUtils::initedForThisThread(false); void RandomUtils::Init() { fd = open("/dev/urandom", O_RDONLY); } void RandomUtils::DeInit() { if (fd != 0) { close(fd); fd = 0; } } size_t RandomUtils::getRandomBuffer(void *dest, size_t size) { size_t bytes_read = 0; while (bytes_read < size) { bytes_read += read(fd, (char*)dest + bytes_read, size - bytes_read); } return bytes_read; } int RandomUtils::random_inner(){ if (!initedForThisThread.get()){ srand(time(NULL)); initedForThisThread.set(true); } return rand(); } int RandomUtils::random(int begin, int end) NACOS_THROW (NacosException) { //sanity check if (begin == end || begin > end) { throw NacosException(NacosException::INVALID_PARAM, "end must be greater than begin"); } long offset = random_inner() % (end - begin + 1); return begin + offset; } }//namespace nacos ================================================ FILE: src/utils/RandomUtils.h ================================================ #ifndef __RND_UTILS_H_ #define __RND_UTILS_H_ #include "NacosExceptions.h" #include "src/thread/ThreadLocal.h" #include "Compatibility.h" /** * RandomUtils * * @author Liu, Hanyu * get random buffer from /dev/urandom * and helper functions to get random random number */ namespace nacos{ class RandomUtils { private: static int fd; static int random_inner(); static ThreadLocal initedForThisThread; public: static void Init(); static void DeInit(); static size_t getRandomBuffer(void *dest, size_t size); /** * generates a random number ranges from begin to end (including the begin and the end) * * @param begin begin of the range (inclusive) * @param end end of the range (inclusive) * @return a long random number * @throw (NacosException) if begin >= end */ static int random(int begin, int end) NACOS_THROW(NacosException); }; }//namespace nacos #endif ================================================ FILE: src/utils/SequenceProvider.h ================================================ #include #include #include #include #include "NacosString.h" #include "NacosExceptions.h" #include "thread/AtomicInt.h" #include "src/thread/Mutex.h" #include "src/config/IOUtils.h" namespace nacos { template class SequenceProvider { private: NacosString _fileName; AtomicInt _current; Mutex _acquireMutex; T _nr_to_preserve; T _initSequence; volatile T _hwm;//high water mark void ensureWrite(int fd, T data) { size_t bytes_written = 0; while (bytes_written < sizeof(T)) { bytes_written += write(fd, (char*)&data + bytes_written, sizeof(T) - bytes_written); } } T preserve() { T current; int fd; bool newFile = false; if (IOUtils::checkNotExistOrNotFile(_fileName)) { newFile = true; } mode_t mode = S_IRUSR | S_IWUSR | S_IRWXG | S_IWGRP; fd = open(_fileName.c_str(), O_RDWR | O_CREAT, mode); if (fd <= 0) { throw new NacosException(NacosException::UNABLE_TO_OPEN_FILE, _fileName); } if (newFile) { ensureWrite(fd, _initSequence); lseek(fd, 0, SEEK_SET);//read from the beginning } size_t bytes_read = 0; while (bytes_read < sizeof(T)) { bytes_read += read(fd, (char*)¤t + bytes_read, sizeof(T) - bytes_read); } lseek(fd, 0, SEEK_SET);//write from the beginning ensureWrite(fd, current + _nr_to_preserve); close(fd); _hwm = current + _nr_to_preserve; return current; }; public: SequenceProvider(const NacosString &fileName, T initSequence, T nr_to_preserve) { _fileName = fileName; _initSequence = initSequence; _nr_to_preserve = nr_to_preserve; _current.set(preserve()); }; T next(int step = 1) { T res = _current.getAndInc(step); while (res >= _hwm) { _acquireMutex.lock(); if (res >= _hwm) { preserve(); } _acquireMutex.unlock(); } return res; }; }; } // namespace nacos ================================================ FILE: src/utils/TimeUtils.cpp ================================================ #include #include "src/utils/TimeUtils.h" namespace nacos{ int64_t TimeUtils::getCurrentTimeInMs() { struct timeval tv; gettimeofday(&tv, NULL); return tv.tv_sec * 1000 + tv.tv_usec / 1000; } void TimeUtils::getCurrentTimeInStruct(struct timeval &tv) { gettimeofday(&tv, NULL); } }//namespace nacos ================================================ FILE: src/utils/TimeUtils.h ================================================ #ifndef __TIME_UTILS_H_ #define __TIME_UTILS_H_ #include #include namespace nacos{ class TimeUtils { public: static int64_t getCurrentTimeInMs(); static void getCurrentTimeInStruct(struct timeval &tv); }; }//namespace nacos #endif ================================================ FILE: src/utils/UuidUtils.cpp ================================================ #include #include #include #include #include "src/utils/UuidUtils.h" #include "src/utils/RandomUtils.h" namespace nacos{ void UuidUtils::Init() { } void UuidUtils::DeInit() { } NacosString UuidUtils::generateUuid() { char random_num[UUID_LEN_BYTES]; char str_buffer[33]; //an UUID has 128 bits(16 bytes), so we need to get 16 bytes from the urandom RandomUtils::getRandomBuffer(random_num, UUID_LEN_BYTES); int32_t *val_ptr = (int32_t *) random_num; snprintf(str_buffer, sizeof(str_buffer), "%08X%08X%08X%08X", val_ptr[0], val_ptr[1], val_ptr[2], val_ptr[3]); NacosString uuid = str_buffer; return uuid; } }//namespace nacos ================================================ FILE: src/utils/UuidUtils.h ================================================ #ifndef __UUID_UTILS_H_ #define __UUID_UTILS_H_ #include "NacosString.h" #define UUID_LEN_BYTES 16 /** * UuidUtils * * @author yzz-ihep * Generates UUID from /dev/urandom */ namespace nacos{ class UuidUtils { private: public: static NacosString generateUuid(); static void Init(); static void DeInit(); }; }//namespace nacos #endif ================================================ FILE: src/utils/url.cpp ================================================ #include "src/utils/url.h" #include namespace nacos{ NacosString urlencode(const NacosString &content) { NacosString result; CURL *curl = curl_easy_init(); char *output = NULL; if (curl) { output = curl_easy_escape(curl, content.c_str(), content.length()); } if (output) { result = output; curl_free(output); } curl_easy_cleanup(curl); return result; } NacosString urldecode(const NacosString &content) { NacosString result; CURL *curl = curl_easy_init(); char *output = NULL; if (curl) { output = curl_easy_unescape(curl, content.c_str(), content.length(), NULL); } if (output) { result = output; curl_free(output); } curl_easy_cleanup(curl); return result; } }//namespace nacos ================================================ FILE: src/utils/url.h ================================================ #ifndef __URL_H_ #define __URL_H_ #include "NacosString.h" namespace nacos{ NacosString urlencode(const NacosString &content); NacosString urldecode(const NacosString &content); }//namespace nacos #endif ================================================ FILE: test/allinone.cpp ================================================ #include #include #include "src/debug/DebugAssertion.h" #include "src/init/Init.h" #include "src/log/Logger.h" #include using namespace std; using namespace nacos; bool testNormalHttpRequest(); bool testNoServerRequest(); bool testGetConfig(); bool testGetConfigwithDefaultPort(); bool testInvalidConfig(); bool testDebug(); bool testVaArgs(); bool testVaArgs2(); bool testlogPrint(); bool testPublishConfig(); bool testStringEqual(); bool testAddListener(); bool testReadWriteFile(); bool testGetFileSize(); bool testFileExists(); bool testCreateAndRemove(); bool testCleanDirectory(); bool testSaveSnapshot(); bool testCleanTestenvCacheAndGetTestenv(); bool testCleanPrdCacheAndGetPrdenv(); bool testCleanAllCache(); bool testMD5(); bool testURLEncodeAndDecode(); bool testStringExplode(); bool testStringExplode2(); bool testNamingProxySmokeTest(); bool testNamingServiceRegister(); bool testRapidJsonIntroduce(); bool testSerialize(); bool testThreadSmoke(); bool testThreadPoolSmoke(); bool testString2ServiceInfo(); bool testMalformedJson2ServiceInfo(); bool testMalformedDouble2ServiceInfo(); bool testLackcacheMillisServiceInfo(); bool testGetAllInstances(); bool testListeningKeys(); bool testAppConfigManager(); bool testServerListManager(); bool testDeleteConfig(); bool testEndpointWithNamingProxy(); bool testUUID(); bool testUUIDMT(); bool testListenService(); bool testGetServiceNames(); bool testInstanceSelectors(); bool testRandomByWeightSelector(); bool testThreadLocal(); bool testThreadLocalPtr(); bool testThreadLocalPtrWithInitializer(); bool testMaintainGetService(); bool testMaintainUpdateService(); bool testMaintainCreateService(); bool testMaintainUpdateInstance(); bool testPublishConfigWithHttpPrefix(); bool testRemoveKeyBeingWatched(); bool testGetHostIp(); bool testDelayedThread(); bool testDelayedThread2(); bool testNamingServiceAndDeRegisterActively(); bool testThreadPoolConcurrentWithAtomicCounter(); bool testSequenceProvider(); bool testHMACSHA1(); bool testSubscribeAlotOfServices(); TestData disabledTestList[] = TEST_ITEM_START TEST_ITEM_END TestData testList[] = TEST_ITEM_START TEST_ITEM("Normal http test", testNormalHttpRequest) TEST_ITEM("No server request, should fail", testNoServerRequest) TEST_ITEM("Publish config to server", testPublishConfig) TEST_ITEM("Get config from server", testGetConfig) TEST_ITEM("Get config from server (with default port)", testGetConfigwithDefaultPort) TEST_ITEM("Connect the server with invalid config, should throw an exception", testInvalidConfig) TEST_ITEM("Test printing logs", testDebug) TEST_ITEM("Delete config from server", testDeleteConfig) TEST_ITEM("Test printing logs with va_args", testVaArgs) TEST_ITEM("Test printing logs with va_args2", testVaArgs2) TEST_ITEM("Test printing logs", testlogPrint) TEST_ITEM("Test for string characteristics", testStringEqual) TEST_ITEM("Read&Write file test", testReadWriteFile) TEST_ITEM("GetFileSize, should work well", testGetFileSize) TEST_ITEM("Test get instances with predicate(testRandomByWeightSelector)", testRandomByWeightSelector) TEST_ITEM("Check whether file exists or not", testFileExists) TEST_ITEM("Create&Remove file", testCreateAndRemove) TEST_ITEM("Create a directory with subdirectories, and clean it", testCleanDirectory) TEST_ITEM("Save snapshot", testSaveSnapshot) TEST_ITEM("Save cache in test and prod env, then clean test env, should only get config from prod env", testCleanTestenvCacheAndGetTestenv) TEST_ITEM("Save cache in test and prod env, then clean prod env, should only get config from test env", testCleanPrdCacheAndGetPrdenv) TEST_ITEM("Save cache in test and prod env, then clean all, should not get any data", testCleanAllCache) TEST_ITEM("Test MD5", testMD5) TEST_ITEM("Endpoint function test, get available nacos server from endpoint", testEndpointWithNamingProxy) TEST_ITEM("Test urlencode/urldecode of libcurl", testURLEncodeAndDecode) TEST_ITEM("Test Config Listener function for nacos", testAddListener) TEST_ITEM("Test basic function of NamingProxy's registerService", testNamingProxySmokeTest) TEST_ITEM("Check whether rapidjson is introduced into the project successfully", testRapidJsonIntroduce) TEST_ITEM("Check if the serialization succeeds", testSerialize) TEST_ITEM("Smoke test for Thread", testThreadSmoke) TEST_ITEM("Smoke test for ThreadPool", testThreadPoolSmoke) TEST_ITEM("Test basic function of NacosNamingService's registerService", testNamingServiceRegister) TEST_ITEM("Test serialization/deserialization of Business Object", testString2ServiceInfo) TEST_ITEM("Test get instances with predicate(Randomly)", testInstanceSelectors) TEST_ITEM("Test serialization/deserialization of malformed Business Object", testMalformedJson2ServiceInfo) TEST_ITEM("Test serialization/deserialization of malformed Business Object (Double)", testMalformedDouble2ServiceInfo) TEST_ITEM("Test serialization/deserialization of malformed Business Object (no cacheMillis)", testLackcacheMillisServiceInfo) TEST_ITEM("Register many services and get one", testGetAllInstances) TEST_ITEM("Listen to key and remove it from listening list", testListeningKeys) TEST_ITEM("Test explode function", testStringExplode) TEST_ITEM("Test explode function version 2 enhanced", testStringExplode2) TEST_ITEM("AppConfigManager smoke test", testAppConfigManager) TEST_ITEM("ServerListManager smoke test", testServerListManager) TEST_ITEM("Test UUID generation", testUUID) TEST_ITEM("Test UUID generation(Multi-thread)", testUUIDMT) TEST_ITEM("Register many services and get one", testGetAllInstances) TEST_ITEM("Subscribe & unsubscribe services", testListenService) TEST_ITEM("Test get all service names", testGetServiceNames) TEST_ITEM("Smoking test of ThreadLocal", testThreadLocal) TEST_ITEM("Smoking test of ThreadLocal(pointer)", testThreadLocalPtr) TEST_ITEM("Smoking test of ThreadLocal(pointer with initializer)", testThreadLocalPtrWithInitializer) TEST_ITEM("MaintainService: testMaintainGetService", testMaintainGetService) TEST_ITEM("MaintainService: testMaintainUpdateService", testMaintainUpdateService) TEST_ITEM("MaintainService: testMaintainCreateService", testMaintainCreateService) TEST_ITEM("MaintainService: testMaintainUpdateInstance", testMaintainUpdateInstance) TEST_ITEM("Test with address config containing http prefix", testPublishConfigWithHttpPrefix) TEST_ITEM("Test with address config containing http prefix", testRemoveKeyBeingWatched) TEST_ITEM("Get local machine's IP", testGetHostIp) TEST_ITEM("Test delayed task pool", testDelayedThread) TEST_ITEM("Test delayed task pool - multiple tasks triggered at the same time", testDelayedThread2) TEST_ITEM("Register a service instance and remove it actively", testNamingServiceAndDeRegisterActively) TEST_ITEM("thread pool with concurrent add & atomic operation", testThreadPoolConcurrentWithAtomicCounter) TEST_ITEM("Test sequence provider", testSequenceProvider) TEST_ITEM("Test Message digest algorithm - HMACSHA1", testHMACSHA1) TEST_ITEM("Test subscribe a lot of services, bugfix #101", testSubscribeAlotOfServices) TEST_ITEM_END int main() { Init::doInit(); list failed_list; cout << "Please start a nacos server listening on port 8848 in this machine first." << endl; cout << "And when the server is ready, press any key to start the test." << endl; getchar(); int nr_succ = 0, nr_fail = 0; Logger::setLogLevel(DEBUG); cout << "BEGIN OF TESTS" << endl; cout << "===========================" << endl; for (size_t i = 0; i < sizeof(testList) / sizeof(TestData); i++) { TestData * curtest = &testList[i]; TESTFN testfunction = curtest->testFn; cout << "Testing " << curtest->testName << " ..." << endl; bool pass = testfunction(); if (!pass) { cout << "FAILED" << endl; failed_list.push_back(curtest); nr_fail++; } else { cout << "PASSED!" << endl; nr_succ++; } cout << "===========================" << endl; } if (!failed_list.empty()) { cout << "List of failed cases:" << endl; for (list::iterator it = failed_list.begin(); it != failed_list.end(); it++) { cout << (*it)->testName << endl; } cout << "===========================" << endl; } cout << "SUMMARY" << endl; cout << "Total:" << nr_succ + nr_fail << endl; cout << "Succ:" << nr_succ << endl; cout << "Fail:" << nr_fail << endl; cout << "===========================" << endl; return 0; } ================================================ FILE: test/testcase/AssertString.cpp ================================================ #include #include "NacosString.h" #include "src/debug/DebugAssertion.h" using namespace std; using namespace nacos; //Use a special variable NULLSTR as null in Java to pass null parameter //But "" (zero-length string) will still be regarded as null bool testStringEqual() { cout << "In testStringEqual" << endl; NacosString s2 = ""; NacosString s3 = "Nacos"; SHOULD_BE_TRUE(isNull(NULLSTR), "NULLSTR should be NULL"); SHOULD_BE_TRUE(isNull(s2), "\"\" should be NULL"); SHOULD_BE_FALSE(isNull(s3), "\"Nacos\" should not be NULL"); return true; } ================================================ FILE: test/testcase/DebugTest.cpp ================================================ #include "src/log/Logger.h" using namespace std; using namespace nacos; bool testDebug() { log_print(DEBUG, "print\n"); log_debug("debug\n"); log_info("info\n"); log_warn("warn\n"); log_error("error\n"); return true; } bool testVaArgs() { //log_print(DEBUG, "print\n","print","print"); log_debug("debug\n", "debug", "debug"); log_info("info\n", "info", "info"); log_warn("warn\n", "warn", "warn"); log_error("error\n", "error", "error"); return true; } bool testlogPrint() { log_print(DEBUG, "===>print %s %s %d\n", "print", "print", 9); log_print(DEBUG, "===>print %s %s %d\n", "print", "print", 9); log_print(DEBUG, "===>print %s %s %d\n", "print", "print", 9); return true; } bool testVaArgs2() { log_info("info %s %s\n", "info", "info"); log_print(DEBUG, "print %s %s %d\n", "print", "print", 9); log_debug("debug %s %s\n", "debug", "debug"); log_info("info %s %s\n", "info", "info"); log_warn("warn %s %s\n", "warn", "warn"); log_error("error %s %s %d %f\n", "error", "error", 999, 3.14); return true; } ================================================ FILE: test/testcase/testAppConfigManager.cpp ================================================ #include #include "src/config/AppConfigManager.h" #include "src/utils/DirUtils.h" #include "constant/ConfigConstant.h" using namespace std; using namespace nacos; bool testAppConfigManager() { cout << "in function testAppConfigManager" << endl; NacosString configFile = DirUtils::getCwd() + ConfigConstant::FILE_SEPARATOR + ConfigConstant::DEFAULT_CONFIG_FILE; AppConfigManager appConfigManager(configFile); appConfigManager.loadConfig(configFile); Properties configs = appConfigManager.getAllConfig(); return true; } ================================================ FILE: test/testcase/testCache.cpp ================================================ #include #include "src/config/LocalSnapshotManager.h" #include "src/debug/DebugAssertion.h" #include "NacosString.h" #include "ResourceGuard.h" using namespace std; using namespace nacos; bool testSaveSnapshot() { cout << "in function testSaveSnapshot" << endl; Properties props; AppConfigManager *appConfigManager = new AppConfigManager(props); LocalSnapshotManager *localSnapshotManager = new LocalSnapshotManager(appConfigManager); ResourceGuard __guardCfg(appConfigManager); ResourceGuard __guardCfgProcessor(localSnapshotManager); localSnapshotManager->cleanAllSnapshot(); localSnapshotManager->saveSnapshot("Testenv", "DummyData", "BusinessGrp1", "FrontTenant", "ConfigName=Value for FrontTenant"); NacosString content = localSnapshotManager->getSnapshot("Testenv", "DummyData", "BusinessGrp1", "FrontTenant"); SHOULD_BE_TRUE(content == "ConfigName=Value for FrontTenant", "Saved snapshot, read it again, should be the same"); localSnapshotManager->saveSnapshot("Testenv", "DummyData", "BusinessGrp1", NULLSTR, "ConfigName=Value"); content = localSnapshotManager->getSnapshot("Testenv", "DummyData", "BusinessGrp1", NULLSTR); SHOULD_BE_TRUE(content == "ConfigName=Value", "Saved snapshot(No tenant), read it again, should be the same"); return true; } bool testCleanTestenvCacheAndGetTestenv() { cout << "in function testCleanTestenvCacheAndGetTestenv" << endl; Properties props; AppConfigManager *appConfigManager = new AppConfigManager(props); LocalSnapshotManager *localSnapshotManager = new LocalSnapshotManager(appConfigManager); ResourceGuard __guardCfg(appConfigManager); ResourceGuard __guardCfgProcessor(localSnapshotManager); localSnapshotManager->cleanAllSnapshot(); localSnapshotManager->saveSnapshot("Testenv", "DummyData", "BusinessGrp1", "FrontTenant", "Value for FrontTenant&Testenv"); localSnapshotManager->saveSnapshot("Prodenv", "DummyData", "BusinessGrp1", "FrontTenant", "Value for FrontTenant&Prodenv"); localSnapshotManager->saveSnapshot("Testenv", "DummyData", "BusinessGrp1", NULLSTR, "Value for Testenv"); localSnapshotManager->saveSnapshot("Prodenv", "DummyData", "BusinessGrp1", NULLSTR, "Value for Prodenv"); NacosString cntfrontEndTest = localSnapshotManager->getSnapshot("Testenv", "DummyData", "BusinessGrp1", "FrontTenant"); NacosString cntPrdFrontEnd = localSnapshotManager->getSnapshot("Prodenv", "DummyData", "BusinessGrp1", "FrontTenant"); NacosString cntTest = localSnapshotManager->getSnapshot("Testenv", "DummyData", "BusinessGrp1", NULLSTR); NacosString cntPrd = localSnapshotManager->getSnapshot("Prodenv", "DummyData", "BusinessGrp1", NULLSTR); SHOULD_BE_TRUE(cntfrontEndTest == "Value for FrontTenant&Testenv", "Check settings for FrontTenant&Testenv"); SHOULD_BE_TRUE(cntPrdFrontEnd == "Value for FrontTenant&Prodenv", "Check settings for FrontTenant&Prodenv"); SHOULD_BE_TRUE(cntTest == "Value for Testenv", "Check settings for Testenv"); SHOULD_BE_TRUE(cntPrd == "Value for Prodenv", "Check settings for Prodenv"); localSnapshotManager->cleanEnvSnapshot("Testenv"); cntfrontEndTest = localSnapshotManager->getSnapshot("Testenv", "DummyData", "BusinessGrp1", "FrontTenant"); cntPrdFrontEnd = localSnapshotManager->getSnapshot("Prodenv", "DummyData", "BusinessGrp1", "FrontTenant"); cntTest = localSnapshotManager->getSnapshot("Testenv", "DummyData", "BusinessGrp1", NULLSTR); cntPrd = localSnapshotManager->getSnapshot("Prodenv", "DummyData", "BusinessGrp1", NULLSTR); SHOULD_BE_TRUE(cntfrontEndTest == "", "Testenv removed, Check settings for FrontTenant&Testenv"); SHOULD_BE_TRUE(cntPrdFrontEnd == "Value for FrontTenant&Prodenv", "Testenv removed, Check settings for FrontTenant&Prodenv"); SHOULD_BE_TRUE(cntTest == "", "Testenv removed, Check settings for Testenv"); SHOULD_BE_TRUE(cntPrd == "Value for Prodenv", "Testenv removed, Check settings for Prodenv"); return true; } bool testCleanPrdCacheAndGetPrdenv() { cout << "in function testCleanPrdCacheAndGetPrdenv" << endl; Properties props; AppConfigManager *appConfigManager = new AppConfigManager(props); LocalSnapshotManager *localSnapshotManager = new LocalSnapshotManager(appConfigManager); ResourceGuard __guardCfg(appConfigManager); ResourceGuard __guardCfgProcessor(localSnapshotManager); localSnapshotManager->cleanAllSnapshot(); localSnapshotManager->saveSnapshot("Testenv", "DummyData", "BusinessGrp1", "FrontTenant", "Value for FrontTenant&Testenv"); localSnapshotManager->saveSnapshot("Prodenv", "DummyData", "BusinessGrp1", "FrontTenant", "Value for FrontTenant&Prodenv"); localSnapshotManager->saveSnapshot("Testenv", "DummyData", "BusinessGrp1", NULLSTR, "Value for Testenv"); localSnapshotManager->saveSnapshot("Prodenv", "DummyData", "BusinessGrp1", NULLSTR, "Value for Prodenv"); NacosString cntfrontEndTest = localSnapshotManager->getSnapshot("Testenv", "DummyData", "BusinessGrp1", "FrontTenant"); NacosString cntPrdFrontEnd = localSnapshotManager->getSnapshot("Prodenv", "DummyData", "BusinessGrp1", "FrontTenant"); NacosString cntTest = localSnapshotManager->getSnapshot("Testenv", "DummyData", "BusinessGrp1", NULLSTR); NacosString cntPrd = localSnapshotManager->getSnapshot("Prodenv", "DummyData", "BusinessGrp1", NULLSTR); SHOULD_BE_TRUE(cntfrontEndTest == "Value for FrontTenant&Testenv", "Check settings for FrontTenant&Testenv"); SHOULD_BE_TRUE(cntPrdFrontEnd == "Value for FrontTenant&Prodenv", "Check settings for FrontTenant&Prodenv"); SHOULD_BE_TRUE(cntTest == "Value for Testenv", "Check settings for Testenv"); SHOULD_BE_TRUE(cntPrd == "Value for Prodenv", "Check settings for Prodenv"); localSnapshotManager->cleanEnvSnapshot("Prodenv"); cntfrontEndTest = localSnapshotManager->getSnapshot("Testenv", "DummyData", "BusinessGrp1", "FrontTenant"); cntPrdFrontEnd = localSnapshotManager->getSnapshot("Prodenv", "DummyData", "BusinessGrp1", "FrontTenant"); cntTest = localSnapshotManager->getSnapshot("Testenv", "DummyData", "BusinessGrp1", NULLSTR); cntPrd = localSnapshotManager->getSnapshot("Prodenv", "DummyData", "BusinessGrp1", NULLSTR); SHOULD_BE_TRUE(cntfrontEndTest == "Value for FrontTenant&Testenv", "Prodenv removed, Check settings for FrontTenant&Testenv"); SHOULD_BE_TRUE(cntPrdFrontEnd == "", "Prodenv removed, Check settings for FrontTenant&Prodenv"); SHOULD_BE_TRUE(cntTest == "Value for Testenv", "Prodenv removed, Check settings for Testenv"); SHOULD_BE_TRUE(cntPrd == "", "Prodenv removed, Check settings for Prodenv"); return true; } bool testCleanAllCache() { cout << "in function testCleanAllCache" << endl; Properties props; AppConfigManager *appConfigManager = new AppConfigManager(props); LocalSnapshotManager *localSnapshotManager = new LocalSnapshotManager(appConfigManager); ResourceGuard __guardCfg(appConfigManager); ResourceGuard __guardCfgProcessor(localSnapshotManager); localSnapshotManager->cleanAllSnapshot(); localSnapshotManager->saveSnapshot("Testenv", "DummyData", "BusinessGrp1", "FrontTenant", "Value for FrontTenant&Testenv"); localSnapshotManager->saveSnapshot("Prodenv", "DummyData", "BusinessGrp1", "FrontTenant", "Value for FrontTenant&Prodenv"); localSnapshotManager->saveSnapshot("Testenv", "DummyData", "BusinessGrp1", NULLSTR, "Value for Testenv"); localSnapshotManager->saveSnapshot("Prodenv", "DummyData", "BusinessGrp1", NULLSTR, "Value for Prodenv"); NacosString cntfrontEndTest = localSnapshotManager->getSnapshot("Testenv", "DummyData", "BusinessGrp1", "FrontTenant"); NacosString cntPrdFrontEnd = localSnapshotManager->getSnapshot("Prodenv", "DummyData", "BusinessGrp1", "FrontTenant"); NacosString cntTest = localSnapshotManager->getSnapshot("Testenv", "DummyData", "BusinessGrp1", NULLSTR); NacosString cntPrd = localSnapshotManager->getSnapshot("Prodenv", "DummyData", "BusinessGrp1", NULLSTR); SHOULD_BE_TRUE(cntfrontEndTest == "Value for FrontTenant&Testenv", "Check settings for FrontTenant&Testenv"); SHOULD_BE_TRUE(cntPrdFrontEnd == "Value for FrontTenant&Prodenv", "Check settings for FrontTenant&Prodenv"); SHOULD_BE_TRUE(cntTest == "Value for Testenv", "Check settings for Testenv"); SHOULD_BE_TRUE(cntPrd == "Value for Prodenv", "Check settings for Prodenv"); localSnapshotManager->cleanAllSnapshot(); cntfrontEndTest = localSnapshotManager->getSnapshot("Testenv", "DummyData", "BusinessGrp1", "FrontTenant"); cntPrdFrontEnd = localSnapshotManager->getSnapshot("Prodenv", "DummyData", "BusinessGrp1", "FrontTenant"); cntTest = localSnapshotManager->getSnapshot("Testenv", "DummyData", "BusinessGrp1", NULLSTR); cntPrd = localSnapshotManager->getSnapshot("Prodenv", "DummyData", "BusinessGrp1", NULLSTR); SHOULD_BE_TRUE(cntfrontEndTest == "", "cleanAllSnapshot(), Check settings for FrontTenant&Testenv"); SHOULD_BE_TRUE(cntPrdFrontEnd == "", "cleanAllSnapshot(), Check settings for FrontTenant&Prodenv"); SHOULD_BE_TRUE(cntTest == "", "cleanAllSnapshot(), Check settings for Testenv"); SHOULD_BE_TRUE(cntPrd == "", "cleanAllSnapshot(), Check settings for Prodenv"); return true; } ================================================ FILE: test/testcase/testDelayedThreadPool.cpp ================================================ #include #include #include #include #include "src/thread/DelayedThreadPool.h" #include "src/debug/DebugAssertion.h" #include "NacosExceptions.h" using namespace std; using namespace nacos; class DelayedTask : public Task { public: DelayedThreadPool *executor; uint64_t interval;// in MS uint64_t last_exec_time; DelayedTask() { last_exec_time = 0; } void run() { uint64_t now_ms = TimeUtils::getCurrentTimeInMs(); uint64_t interval_calc = 0; if (last_exec_time != 0) { interval_calc = now_ms - last_exec_time; } last_exec_time = now_ms; executor->schedule(this,now_ms + interval);//interval/1000 secs later if (executor == NULL) { throw NacosException(NacosException::INVALID_CONFIG_PARAM, "no executor"); } printf(">>>>>>>>>>>>>>>>>>Task %s triggered, time =%llu (%llu), interval = %llu\n", getTaskName().c_str(), now_ms/1000, now_ms, interval_calc); sleep(1); } }; bool testDelayedThread() { cout << "in function testDelayedThread" << endl; DelayedThreadPool dtp("testDPool", 11); dtp.start(); cout << "create tasks" << endl; DelayedTask delayedTasks[10]; uint64_t now_ms = TimeUtils::getCurrentTimeInMs(); for (size_t i = 0; i < sizeof(delayedTasks) / sizeof(DelayedTask); i++) { delayedTasks[i].executor = &dtp; delayedTasks[i].interval = (i + 1) * 1000; delayedTasks[i].setTaskName("DelayedTask-" + NacosStringOps::valueOf(i)); dtp.schedule(&delayedTasks[i], now_ms); } sleep(20); cout << "call stop()" << endl; dtp.stop(); cout << "end of test" << endl; return true; } bool testDelayedThread2() { cout << "in function testDelayedThread2 - multiple tasks triggered at the same time" << endl; DelayedThreadPool dtp("testDPool", 11); dtp.start(); cout << "create tasks" << endl; DelayedTask delayedTasks[10]; uint64_t now_ms = TimeUtils::getCurrentTimeInMs(); for (size_t i = 0; i < sizeof(delayedTasks) / sizeof(DelayedTask); i++) { delayedTasks[i].executor = &dtp; delayedTasks[i].interval = 1000; delayedTasks[i].setTaskName("DelayedTask-" + NacosStringOps::valueOf(i)); dtp.schedule(&delayedTasks[i], now_ms); } sleep(20); cout << "call stop()" << endl; dtp.stop(); cout << "end of test" << endl; return true; } ================================================ FILE: test/testcase/testDeleteConfig.cpp ================================================ #include #include #include "factory/NacosFactoryFactory.h" #include "constant/PropertyKeyConst.h" #include "src/debug/DebugAssertion.h" #include "ResourceGuard.h" using namespace std; using namespace nacos; bool testDeleteConfig() { cout << "in function testDeleteConfig" << endl; Properties props; props[PropertyKeyConst::SERVER_ADDR] = "127.0.0.1:8848"; ADD_AUTH_INFO(props); ADD_SPAS_INFO(props); INacosServiceFactory *factory = NacosFactoryFactory::getNacosFactory(props); ResourceGuard _guardFactory(factory); ConfigService *n = factory->CreateConfigService(); ResourceGuard _serviceFactory(n); bool bSucc; for (int i = 5; i < 50; i++) { char key_s[200]; char val_s[200]; snprintf(key_s, sizeof(key_s), "Key%d", i); snprintf(val_s, sizeof(val_s), "v__%d", i); try { bSucc = n->removeConfig(key_s, NULLSTR); } catch (NacosException &e) { cout << "Request failed with curl code:" << e.errorcode() << endl << "Reason:" << e.what() << endl; return false; } cout << "Delete Key:" << key_s << " with value:" << val_s << " result:" << bSucc << endl; } return true; } ================================================ FILE: test/testcase/testDeleteListenedKeys.cpp ================================================ #include #include #include #include #include "factory/NacosFactoryFactory.h" #include "ResourceGuard.h" #include "listen/Listener.h" #include "constant/PropertyKeyConst.h" #include "src/debug/DebugAssertion.h" #include "src/log/Logger.h" using namespace std; using namespace nacos; class MyListener : public Listener { private: int num; public: MyListener(int num) { this->num = num; } void receiveConfigInfo(const NacosString &configInfo) { cout << "===================================" << endl; cout << "Watcher" << num << endl; cout << "Watched Key UPDATED:" << configInfo << endl; cout << "===================================" << endl; } }; bool testRemoveKeyBeingWatched() { cout << "in function testRemoveKeyBeingWatched" << endl; Properties props; props[PropertyKeyConst::SERVER_ADDR] = "127.0.0.1:8848"; ADD_AUTH_INFO(props); ADD_SPAS_INFO(props); INacosServiceFactory *factory = NacosFactoryFactory::getNacosFactory(props); ResourceGuard _guardFactory(factory); ConfigService *n = factory->CreateConfigService(); ResourceGuard _serviceFactory(n); n->publishConfig("RemovedWhileWatching", NULLSTR, "dummyContent"); MyListener *theListener = new MyListener(1); n->addListener("RemovedWhileWatching", NULLSTR, theListener); sleep(2); cout << "remove key" << endl; n->removeConfig("RemovedWhileWatching", NULLSTR); sleep(2); cout << "set key" << endl; n->publishConfig("RemovedWhileWatching", NULLSTR, "dummyContent1"); sleep(2); cout << "remove key" << endl; n->removeConfig("RemovedWhileWatching", NULLSTR); cout << "Hold for 30 secs" << endl; sleep(30); n->removeListener("RemovedWhileWatching", NULLSTR, theListener); cout << "remove listener2" << endl; cout << "test successful" << endl; return true; } ================================================ FILE: test/testcase/testEndpointWithNamingSvc.cpp ================================================ #include #include #include #include "src/naming/NamingProxy.h" #include "src/naming/NacosNamingService.h" #include "factory/NacosFactoryFactory.h" #include "ResourceGuard.h" #include "naming/Instance.h" #include "constant/ConfigConstant.h" #include "constant/UtilAndComs.h" #include "src/http/HTTPCli.h" #include "src/debug/DebugAssertion.h" #include "src/log/Logger.h" #include "NacosString.h" #include "Properties.h" #include "constant/PropertyKeyConst.h" using namespace std; using namespace nacos; bool testEndpointWithNamingProxy() { cout << "in function testEndpointWithNamingProxy" << endl; cout << "For this test, please create an endpoint on your 80 port with a file in the following path:" << endl; cout << "yourip:80/nacos/endpoint0" << endl; cout << "And the content should be a list of ip:port separated with \\n the ip:port group points at a nacos server" << endl; Properties configProps; ADD_AUTH_INFO(configProps); ADD_SPAS_INFO(configProps); configProps[PropertyKeyConst::ENDPOINT] = "127.0.0.1"; configProps[PropertyKeyConst::ENDPOINT_PORT] = "80"; configProps[PropertyKeyConst::CONTEXT_PATH] = "nacos"; configProps[PropertyKeyConst::CLUSTER_NAME] = "endpoint0"; INacosServiceFactory *factory = NacosFactoryFactory::getNacosFactory(configProps); ResourceGuard _guardFactory(factory); NamingService *namingSvc = factory->CreateNamingService(); ResourceGuard _serviceFactory(namingSvc); Instance instance; instance.clusterName = "DefaultCluster"; instance.ip = "127.0.0.1"; instance.port = 2333; instance.instanceId = "1"; instance.ephemeral = true; try { for (int i = 0; i < 5; i++) { NacosString serviceName = "TestNamingService" + NacosStringOps::valueOf(i); instance.port = 2000 + i; namingSvc->registerInstance(serviceName, instance); } } catch (NacosException &e) { cout << "encounter exception while registering service instance, raison:" << e.what() << endl; return false; } cout << "Keep the services for 30 secs..." << endl; sleep(30); cout << "Deregister the services" << endl; try { for (int i = 0; i < 5; i++) { NacosString serviceName = "TestNamingService" + NacosStringOps::valueOf(i); namingSvc->deregisterInstance(serviceName, "127.0.0.1", 2000 + i); sleep(1); } } catch (NacosException &e) { cout << "encounter exception while registering service instance, raison:" << e.what() << endl; return false; } cout << "testNamingServiceRegister finished" << endl; return true; } ================================================ FILE: test/testcase/testGetAllInstances.cpp ================================================ #include #include #include #include #include "src/naming/NamingProxy.h" #include "factory/NacosFactoryFactory.h" #include "naming/Instance.h" #include "constant/ConfigConstant.h" #include "constant/UtilAndComs.h" #include "src/http/HTTPCli.h" #include "src/debug/DebugAssertion.h" #include "src/log/Logger.h" #include "NacosString.h" #include "Properties.h" #include "constant/PropertyKeyConst.h" #include "ResourceGuard.h" using namespace std; using namespace nacos; bool testGetAllInstances() { cout << "in function testGetAllInstances" << endl; Properties configProps; ADD_AUTH_INFO(configProps); ADD_SPAS_INFO(configProps); configProps[PropertyKeyConst::SERVER_ADDR] = "127.0.0.1"; configProps[PropertyKeyConst::NAMESPACE] = "238e832b-d103-44c6-b618-d74da8c38b06"; INacosServiceFactory *factory = NacosFactoryFactory::getNacosFactory(configProps); ResourceGuard _guardFactory(factory); NamingService *namingSvc = factory->CreateNamingService(); ResourceGuard _guardService(namingSvc); Instance instance; instance.clusterName = "DefaultCluster"; instance.ip = "127.0.0.1"; instance.port = 2333; instance.instanceId = "1"; instance.ephemeral = true; try { for (int i = 0; i < 10; i++) { NacosString serviceName = "TestNamingService" + NacosStringOps::valueOf(i); instance.port = 2000 + i; namingSvc->registerInstance(serviceName, instance); } } catch (NacosException &e) { cout << "encounter exception while registering service instance, raison:" << e.what() << endl; return false; } cout << "wait for 5 secs" << endl; sleep(5); list instances = namingSvc->getAllInstances("TestNamingService1"); cout << "getAllInstances from server:" << endl; for (list::iterator it = instances.begin(); it != instances.end(); it++) { cout << "Instance:" << it->toString() << endl; } if (instances.size() != 1) { cout << "There should be only 1 instance for TestNamingService1" << endl; return false; } if (instances.front().port != 2001) { cout << "TestNamingService1's port should be 2001" << endl; return false; } if (instances.front().ip != "127.0.0.1") { cout << "TestNamingService1's ip should be 127.0.0.1" << endl; return false; } sleep(1); try { for (int i = 0; i < 3; i++) { NacosString serviceName = "TestNamingService" + NacosStringOps::valueOf(i); namingSvc->deregisterInstance(serviceName, "127.0.0.1", 2000 + i); sleep(1); } } catch (NacosException &e) { cout << "encounter exception while registering service instance, raison:" << e.what() << endl; return false; } return true; } ================================================ FILE: test/testcase/testGetConfig.cpp ================================================ #include #include "factory/NacosFactoryFactory.h" #include "constant/PropertyKeyConst.h" #include "src/debug/DebugAssertion.h" #include "ResourceGuard.h" #include "src/log/Logger.h" using namespace std; using namespace nacos; bool testGetConfig() { cout << "in function testGetConfig" << endl; Properties props; props[PropertyKeyConst::SERVER_ADDR] = "127.0.0.1:8848"; ADD_AUTH_INFO(props); ADD_SPAS_INFO(props); INacosServiceFactory *factory = NacosFactoryFactory::getNacosFactory(props); ResourceGuard _guardFactory(factory); ConfigService *n = factory->CreateConfigService(); ResourceGuard _serviceFactory(n); NacosString ss = ""; try { ss = n->getConfig("k", NULLSTR, 1000); } catch (NacosException &e) { cout << "Request failed with curl code:" << e.errorcode() << endl << "Reason:" << e.what() << endl; return false; } cout << ss << endl; return true; } bool testGetConfigwithDefaultPort() { cout << "in function testGetConfigwithDefaultPort" << endl; Properties props; props[PropertyKeyConst::SERVER_ADDR] = "127.0.0.1"; ADD_AUTH_INFO(props); ADD_SPAS_INFO(props); INacosServiceFactory *factory = NacosFactoryFactory::getNacosFactory(props); ResourceGuard _guardFactory(factory); ConfigService *n = factory->CreateConfigService(); ResourceGuard _serviceFactory(n); NacosString ss = n->getConfig("k", NULLSTR, 1000); cout << ss << endl; return true; } bool testInvalidConfig() { cout << "in function testInvalidConfig" << endl; Properties props; ADD_AUTH_INFO(props); ADD_SPAS_INFO(props); NacosString ss; try { INacosServiceFactory *factory = NacosFactoryFactory::getNacosFactory(props); ResourceGuard _guardFactory(factory); ConfigService *n = factory->CreateConfigService(); ResourceGuard _serviceFactory(n); ss = n->getConfig("k", NULLSTR, 1000); } catch (NacosException &e) { NacosString errmsgShouldBe = "no server address specified and the endpoint is blank"; if (errmsgShouldBe == e.what()) { return true; } else { return false; } } cout << ss << endl; return false; } ================================================ FILE: test/testcase/testGetServiceNames.cpp ================================================ #include #include #include #include #include "src/naming/NamingProxy.h" #include "factory/NacosFactoryFactory.h" #include "naming/Instance.h" #include "constant/ConfigConstant.h" #include "constant/UtilAndComs.h" #include "src/http/HTTPCli.h" #include "src/debug/DebugAssertion.h" #include "src/log/Logger.h" #include "NacosString.h" #include "Properties.h" #include "constant/PropertyKeyConst.h" #include "ResourceGuard.h" using namespace std; using namespace nacos; bool testGetServiceNames() { cout << "in function testGetServiceNames" << endl; Properties configProps; ADD_AUTH_INFO(configProps); ADD_SPAS_INFO(configProps); configProps[PropertyKeyConst::SERVER_ADDR] = "127.0.0.1"; INacosServiceFactory *factory = NacosFactoryFactory::getNacosFactory(configProps); ResourceGuard _guardFactory(factory); NamingService *namingSvc = factory->CreateNamingService(); ResourceGuard _guardService(namingSvc); ListView res; try { res = namingSvc->getServiceList(1, 10); } catch (NacosException &e) { cout << "encounter exception while getting service names, raison:" << e.what() << endl; return false; } list nameList = res.getData(); for (list::const_iterator it = nameList.begin(); it != nameList.end(); it++) { cout << "serviceName:" << *it << endl; } return true; } ================================================ FILE: test/testcase/testHttpRequest.cpp ================================================ #include "src/http/HTTPCli.h" #include "src/log/Logger.h" #include "src/debug/DebugAssertion.h" #include using namespace std; using namespace nacos; #define DEFAULT_ENCODING "UTF-8" bool testNormalHttpRequest() { cout << "in function testNormalHttpRequest" << endl; NacosString path = "http://127.0.0.1:8848/nacos/v1/ns/operator/servers"; NacosString ENCODING = DEFAULT_ENCODING; std::list headers; std::list paramValues; HTTPCli httpcli; HttpResult callres; try { callres = httpcli.httpGet(path, headers, paramValues, ENCODING, 1000); } catch (NetworkException &e) { cout << "Request failed with curl code:" << e.errorcode() << endl << "Reason:" << e.what() << endl; return false; } cout << "Http Request returned with code:" << callres.code << endl; cout << "Headers:" << endl; for (std::map::iterator it = callres.headers.begin(); it != callres.headers.end(); ++it) { cout << it->first << ":" << it->second << endl; } cout << "Content:" << callres.content << endl; return true; } bool testNoServerRequest() { cout << "in function testNoServerRequest" << endl; NacosString path = "http://127.0.0.1:9999/nacos/v1/ns/operator/servers"; NacosString ENCODING = DEFAULT_ENCODING; std::list headers; std::list paramValues; HTTPCli httpcli; HttpResult callres; try { callres = httpcli.httpGet(path, headers, paramValues, ENCODING, 1000); } catch (NetworkException &e) { //should throw a exception cout << "Request failed with curl code:" << e.errorcode() << endl << "Reason:" << e.what() << endl; return true; } cout << "Http Request returned with code:" << callres.code << endl; cout << "Headers:" << endl; for (std::map::iterator it = callres.headers.begin(); it != callres.headers.end(); ++it) { cout << it->first << ":" << it->second << endl; } cout << "Content:" << callres.content << endl; return false; } ================================================ FILE: test/testcase/testIOUtils.cpp ================================================ #include #include "src/debug/DebugAssertion.h" #include "src/config/IOUtils.h" #include "src/utils/DirUtils.h" #include "src/log/Logger.h" #define ENCODING "UTF-8" using namespace std; using namespace nacos; bool testReadWriteFile() { cout << "in function testReadWriteFile" << endl; NacosString cwd = DirUtils::getCwd(); NacosString tmpPath = cwd + "/tmp"; IOUtils::recursivelyCreate(tmpPath); NacosString writeFile = tmpPath + "/testfile"; IOUtils::writeStringToFile(writeFile, "nacos-cpp-cli-test", ENCODING); NacosString content = IOUtils::readStringFromFile(writeFile, ENCODING); SHOULD_BE_TRUE(content == "nacos-cpp-cli-test", "NacosString \"nacos-cpp-cli-test\" is written to file, should be the same when reading"); return true; } bool testGetFileSize() { cout << "in function testGetFileSize" << endl; NacosString cwd = DirUtils::getCwd(); NacosString tmpPath = cwd + "/tmp"; IOUtils::recursivelyCreate(tmpPath); NacosString writeFile = tmpPath + "/testfile"; IOUtils::writeStringToFile(writeFile, "nacos-cpp-cli-test", ENCODING); size_t sz = IOUtils::getFileSize(writeFile); SHOULD_BE_TRUE(sz == 18, "NacosString \"nacos-cpp-cli-test\" is written to file, size should be 18"); return true; } bool testFileExists() { cout << "in function testFileExists" << endl; NacosString cwd = DirUtils::getCwd(); NacosString tmpPath = cwd + "/tmp"; NacosString writeFile = tmpPath + "/testfile"; IOUtils::recursivelyRemove(tmpPath); cout << "Cwd:" << cwd << endl; SHOULD_BE_TRUE(IOUtils::checkNotExistOrNotFile(cwd), "The source folder should be a folder, not a file"); SHOULD_BE_FALSE(IOUtils::checkNotExistOrNotDir(cwd), "The source folder should be a folder"); IOUtils::recursivelyCreate(tmpPath); IOUtils::writeStringToFile(writeFile, "nacos-cpp-cli-test", ENCODING); SHOULD_BE_FALSE(IOUtils::checkNotExistOrNotFile(writeFile), "Created a file named \"testfile\" with content \"nacos-cpp-cli-test\""); return true; } bool testCreateAndRemove() { cout << "in function testFileExists" << endl; NacosString cwd = DirUtils::getCwd(); NacosString tmpPath = cwd + "/tmp/123/456/789/2312/afda/4__dsa/dd_"; NacosString rmpath = cwd + "/tmp/123"; IOUtils::recursivelyCreate(tmpPath); SHOULD_BE_FALSE(IOUtils::checkNotExistOrNotDir(tmpPath), "We have created a dir, it should exist"); IOUtils::recursivelyRemove(rmpath); SHOULD_BE_TRUE(IOUtils::checkNotExistOrNotFile(cwd + "/tmp"), "Removed everything except the tmp folder"); NacosString easterEgg = cwd + "/tmp/Liao/Sijia"; IOUtils::recursivelyCreate(easterEgg); return true; } bool testCleanDirectory() { cout << "in function testCleanDirectory" << endl; NacosString cwd = DirUtils::getCwd(); NacosString tmpPath = cwd + "/tmp/testcleandir/456/789/2312/afda/4__dsa/dd_"; NacosString cleanPath = cwd + "/tmp/testcleandir"; IOUtils::recursivelyCreate(tmpPath); SHOULD_BE_FALSE(IOUtils::checkNotExistOrNotDir(tmpPath), "We have created a dir, it should exist"); IOUtils::cleanDirectory(cleanPath); SHOULD_BE_TRUE(IOUtils::checkNotExistOrNotDir(tmpPath), "Subdirectories of the dir is cleaned, it should not exist"); SHOULD_BE_FALSE(IOUtils::checkNotExistOrNotDir(cleanPath), "The dir is cleaned, but itself should exist"); return true; } ================================================ FILE: test/testcase/testInstanceSelector.cpp ================================================ #include #include #include #include #include "factory/NacosFactoryFactory.h" #include "naming/Instance.h" #include "naming/selectors/RandomByWeightSelector.h" #include "naming/selectors/HealthInstanceSelector.h" #include "naming/selectors/RandomSelector.h" #include "constant/ConfigConstant.h" #include "constant/UtilAndComs.h" #include "src/http/HTTPCli.h" #include "src/debug/DebugAssertion.h" #include "src/log/Logger.h" #include "NacosString.h" #include "Properties.h" #include "constant/PropertyKeyConst.h" #include "ResourceGuard.h" using namespace std; using namespace nacos; using namespace nacos::naming::selectors; bool testInstanceSelectors() { cout << "in function testInstanceSelectors" << endl; Properties configProps; ADD_AUTH_INFO(configProps); ADD_SPAS_INFO(configProps); configProps[PropertyKeyConst::SERVER_ADDR] = "127.0.0.1"; configProps[PropertyKeyConst::SUBSCRIPTION_POLL_INTERVAL] = "3000"; INacosServiceFactory *factory = NacosFactoryFactory::getNacosFactory(configProps); ResourceGuard _guardFactory(factory); NamingService *namingSvc = factory->CreateNamingService(); ResourceGuard _guardService(namingSvc); list res; HealthInstanceSelector healthInstanceSelector; RandomSelector randomSelector; Instance instance; instance.clusterName = "DefaultCluster"; instance.ip = "127.0.0.1"; instance.port = 2333; instance.instanceId = "1"; instance.ephemeral = true; try { for (int i = 0; i < 10; i++) { NacosString serviceName = "TestNamingService0"; instance.port = 2000 + i; namingSvc->registerInstance(serviceName, instance); } res = namingSvc->getInstanceWithPredicate("TestNamingService0", &randomSelector); } catch (NacosException &e) { cout << "encounter exception while getting service names, raison:" << e.what() << endl; return false; } cout << "Selected instance(s):" << endl; for (list::const_iterator it = res.begin(); it != res.end(); it++) { cout << "service:" << it->toString() << endl; } return true; } bool testRandomByWeightSelector() { cout << "in function testRandomByWeightSelector" << endl; Properties configProps; ADD_AUTH_INFO(configProps); ADD_SPAS_INFO(configProps); configProps[PropertyKeyConst::SERVER_ADDR] = "127.0.0.1"; configProps[PropertyKeyConst::SUBSCRIPTION_POLL_INTERVAL] = "3000"; INacosServiceFactory *factory = NacosFactoryFactory::getNacosFactory(configProps); ResourceGuard _guardFactory(factory); NamingService *namingSvc = factory->CreateNamingService(); ResourceGuard _guardService(namingSvc); Instance instance; instance.clusterName = "DefaultCluster"; instance.ip = "127.0.0.1"; instance.port = 2333; instance.instanceId = "1"; instance.ephemeral = true; RandomByWeightSelector randomByWeightSelector; try { for (int i = 0; i < 10; i++) { NacosString serviceName = "TestNamingService0"; instance.port = 2000 + i; instance.weight = i + 1; namingSvc->registerInstance(serviceName, instance); } } catch (NacosException &e) { cout << "encounter exception while registering service instance, raison:" << e.what() << endl; return false; } cout << "start to select instance with selectors " << endl; for (int i = 0; i < 20; i++) { list res = namingSvc->getInstanceWithPredicate("TestNamingService0", &randomByWeightSelector); for (list::const_iterator it = res.begin(); it != res.end(); it++) { cout << "service:" << it->toString() << endl; } } return true; } ================================================ FILE: test/testcase/testJson2BizObjects.cpp ================================================ #include #include "src/debug/DebugAssertion.h" #include "src/json/rapidjson/document.h" #include "src/json/rapidjson/writer.h" #include "src/json/rapidjson/stringbuffer.h" #include "src/naming/beat/BeatInfo.h" #include "src/json/JSON.h" using namespace std; using namespace nacos; bool testString2ServiceInfo() { cout << "in function testString2ServiceInfo" << endl; const char *json = "{" " \"dom\": \"nacos.test.1\"," " \"cacheMillis\": 1000," " \"useSpecifiedURL\": false," " \"hosts\": [{" " \"valid\": true," " \"marked\": false," " \"instanceId\": \"10.10.10.10-8888-DEFAULT-nacos.test.1\"," " \"port\": 8888," " \"ip\": \"10.10.10.10\"," " \"weight\": 1.0," " \"healthy\": true," " \"enabled\": true," " \"metadata\": {}" " }]," " \"checksum\": \"3bbcf6dd1175203a8afdade0e77a27cd1528787794594\"," " \"lastRefTime\": 1528787794594," " \"env\": \"\"," " \"clusters\": \"\"," " \"name\": \"test\"" "}"; cout << "Deserializing the following string into an object:" << endl << json << endl; ServiceInfo si = JSON::JsonStr2ServiceInfo(json); SHOULD_BE_TRUE(si.getHosts().size() == 1, "There is 1 host in the json object"); SHOULD_BE_TRUE(si.getClusters() == "", "obj.cluster == \"\""); Instance i = si.getHosts().front(); SHOULD_BE_TRUE(i.instanceId == "10.10.10.10-8888-DEFAULT-nacos.test.1", "The first instance's ID is 10.10.10.10-8888-DEFAULT-nacos.test.1"); return true; } bool testMalformedJson2ServiceInfo() { cout << "in function testMalformedJson2ServiceInfo" << endl; const char *json = "{" " \"dom\": \"nacos.test.1\"," " \"cacheMillis\": 1000," " \"useSpecifiedURL\": false," " \"hosts\": [{" " \"valid\": true," " \"marked\": false," " \"instanceId\": \"10.10.10.10-8888-DEFAULT-nacos.test.1\"," " \"port\": 8888," " \"ip\": \"10.10.10.10\"," " \"weight\": 1.0," " \"healthy\": true," " \"enabled\": true," " \"metadata\": {}" " }]," " \"checksum\": \"3bbcf6dd1175203a8afdade0e77a27cd1528787794594\"," " \"lastRefTime\": 152878779459499999999999999999999999999999999999999," " \"env\": \"\"," " \"clusters\": \"\"," " \"name\": \"test\"" "}"; cout << "Deserializing the following string into an object:" << endl << json << endl; ServiceInfo si; bool invalidJsonFormatException = false; try { si = JSON::JsonStr2ServiceInfo(json); } catch (NacosException &e) { invalidJsonFormatException = true; cout << "Caught exception:" << e.what() << endl; SHOULD_BE_TRUE(e.errorcode() == NacosException::INVALID_JSON_FORMAT, "Exception should be NacosException::INVALID_JSON_FORMAT"); } SHOULD_BE_TRUE(invalidJsonFormatException, "This json string can't be parsed"); return true; } bool testMalformedDouble2ServiceInfo() { cout << "in function testMalformedJson2ServiceInfo" << endl; const char *json = "{" " \"dom\": \"nacos.test.1\"," " \"cacheMillis\": 1000," " \"useSpecifiedURL\": false," " \"hosts\": [{" " \"valid\": true," " \"marked\": false," " \"instanceId\": \"10.10.10.10-8888-DEFAULT-nacos.test.1\"," " \"port\": 8888," " \"ip\": \"10.10.10.10\"," " \"weight\": 1999999999999999999999999999999999999999..0," " \"healthy\": true," " \"enabled\": true," " \"metadata\": {}" " }]," " \"checksum\": \"3bbcf6dd1175203a8afdade0e77a27cd1528787794594\"," " \"lastRefTime\": 1528787794594," " \"env\": \"\"," " \"clusters\": \"\"," " \"name\": \"test\"" "}"; cout << "Deserializing the following string into an object:" << endl << json << endl; ServiceInfo si; bool invalidJsonFormatException = false; try { si = JSON::JsonStr2ServiceInfo(json); } catch (NacosException &e) { if (e.errorcode() == NacosException::INVALID_JSON_FORMAT) { invalidJsonFormatException = true; } cout << "Caught exception:" << e.what() << endl; SHOULD_BE_TRUE(e.errorcode() == NacosException::INVALID_JSON_FORMAT, "Exception should be NacosException::INVALID_JSON_FORMAT"); } SHOULD_BE_TRUE(invalidJsonFormatException, "This json string can't be parsed"); return true; } bool testLackcacheMillisServiceInfo() { cout << "in function testMalformedJson2ServiceInfo" << endl; const char *json = "{" " \"dom\": \"nacos.test.1\"," /*" \"cacheMillis\": 1000,"*///<- no cacheMillis " \"useSpecifiedURL\": false," " \"hosts\": [{" " \"valid\": true," " \"marked\": false," " \"instanceId\": \"10.10.10.10-8888-DEFAULT-nacos.test.1\"," " \"port\": 8888," " \"ip\": \"10.10.10.10\"," " \"weight\": 1.0," " \"healthy\": true," " \"enabled\": true," " \"metadata\": {}" " }]," " \"checksum\": \"3bbcf6dd1175203a8afdade0e77a27cd1528787794594\"," " \"lastRefTime\": 1528787794594," " \"env\": \"\"," " \"clusters\": \"\"," " \"name\": \"test\"" "}"; cout << "Deserializing the following string into an object:" << endl << json << endl; ServiceInfo si; bool invalidJsonFormatException = false; try { si = JSON::JsonStr2ServiceInfo(json); } catch (NacosException &e) { if (e.errorcode() == NacosException::INVALID_JSON_FORMAT) { invalidJsonFormatException = true; } cout << "Caught exception:" << e.what() << endl; SHOULD_BE_TRUE(e.errorcode() == NacosException::LACK_JSON_FIELD, "Exception should be NacosException::LACK_JSON_FIELD"); } SHOULD_BE_FALSE(invalidJsonFormatException, "This JSON is parsable, but cacheMillis is missing"); return true; } ================================================ FILE: test/testcase/testListenWorker.cpp ================================================ #include #include #include #include #include "src/log/Logger.h" #include "src/debug/DebugAssertion.h" #include "listen/Listener.h" #include "src/http/HttpDelegate.h" #include "factory/NacosFactoryFactory.h" #include "ResourceGuard.h" #include "constant/PropertyKeyConst.h" using namespace std; using namespace nacos; bool key_change_called = false; class KeyChangeListener : public Listener { private: NacosString key; public: void setKey(const NacosString &_key) { key = _key; }; NacosString getKey() const { return key; }; void receiveConfigInfo(const NacosString &configInfo) { key_change_called = true; cout << "in receiveConfigInfo: " << key << " changed to " << configInfo << endl; } }; bool testAddListener() { cout << "in function testAddListener" << endl; Properties props; props[PropertyKeyConst::SERVER_ADDR] = "127.0.0.1:8848"; ADD_AUTH_INFO(props); ADD_SPAS_INFO(props); KeyChangeListener *thelistener = new KeyChangeListener(); thelistener->setKey("k"); bool bSucc; try { INacosServiceFactory *factory = NacosFactoryFactory::getNacosFactory(props); ResourceGuard _guardFactory(factory); ConfigService *n = factory->CreateConfigService(); ResourceGuard _serviceFactory(n); n->removeConfig("k4", NULLSTR); n->removeConfig("k2", NULLSTR);//Known issue: this key will be monitored in next cycle(LONG_PULLING_TIME) n->removeConfig("k", NULLSTR);//so will this sleep(1); n->addListener("k4", NULLSTR, thelistener); n->addListener("k2", NULLSTR, thelistener); n->addListener("k", NULLSTR, thelistener); sleep(1); cout << "publish k" << endl; bSucc = n->publishConfig("k4", NULLSTR, "hahaha"); } catch (NacosException &e) { cout << "Failed to add listener" << endl << "Reason:" << e.what() << endl; return false; } SHOULD_BE_TRUE(key_change_called, "Notification function should be called"); SHOULD_BE_TRUE(bSucc, "Publish should succeed"); cout << "test successful" << endl; return true; } ================================================ FILE: test/testcase/testListeningKeys.cpp ================================================ #include #include #include #include #include "factory/NacosFactoryFactory.h" #include "ResourceGuard.h" #include "listen/Listener.h" #include "constant/PropertyKeyConst.h" #include "src/debug/DebugAssertion.h" #include "src/log/Logger.h" using namespace std; using namespace nacos; class MyListener : public Listener { private: int num; public: MyListener(int num) { this->num = num; } void receiveConfigInfo(const NacosString &configInfo) { cout << "===================================" << endl; cout << "Watcher" << num << endl; cout << "Watched Key UPDATED:" << configInfo << endl; cout << "===================================" << endl; } }; bool testListeningKeys() { cout << "in function testListeningKeys" << endl; Properties props; ADD_AUTH_INFO(props); ADD_SPAS_INFO(props); props[PropertyKeyConst::SERVER_ADDR] = "127.0.0.1:8848"; INacosServiceFactory *factory = NacosFactoryFactory::getNacosFactory(props); ResourceGuard _guardFactory(factory); ConfigService *n = factory->CreateConfigService(); ResourceGuard _serviceFactory(n); MyListener *theListener = new MyListener(1); MyListener *theListener2 = new MyListener(2); MyListener *theListener3 = new MyListener(3); n->addListener("dqid", NULLSTR, theListener); n->addListener("dqid", NULLSTR, theListener2); n->addListener("dqid", NULLSTR, theListener3); n->addListener("dqid1", NULLSTR, theListener3); n->addListener("dqid2", NULLSTR, theListener3); n->addListener("dqid3", NULLSTR, theListener3); for (int i = 10; i < 60; i++) { NacosString strKey = "dqid" + NacosStringOps::valueOf(i); n->addListener(strKey, NULLSTR, theListener3); } cout << "Change key and hold for 15 secs" << endl; n->publishConfig("dqid", NULLSTR, "Hello"); n->publishConfig("dqid", NULLSTR, "World"); sleep(15); cout << "remove listener" << endl; n->removeListener("dqid", NULLSTR, theListener); cout << "Hold for 15 secs" << endl; n->publishConfig("dqid3", NULLSTR, "Hello-3"); n->publishConfig("dqid3", NULLSTR, "World-3"); n->publishConfig("dqid1", NULLSTR, "World-3"); n->publishConfig("dqid2", NULLSTR, "World-3"); n->publishConfig("dqid3", NULLSTR, "World-3"); n->publishConfig("dqid1", NULLSTR, "World-3"); n->publishConfig("dqid2", NULLSTR, "World-3"); n->publishConfig("dqid3", NULLSTR, "World-3"); sleep(15); cout << "remove listener2" << endl; n->publishConfig("dqid", NULLSTR, "Data change before remove"); n->removeListener("dqid", NULLSTR, theListener2); n->removeListener("dqid", NULLSTR, theListener3); n->publishConfig("dqid", NULLSTR, "Data change after remove"); cout << "test successful" << endl; return true; } ================================================ FILE: test/testcase/testListeningKeysWithHttpPrefix.cpp ================================================ #include #include #include #include #include "factory/NacosFactoryFactory.h" #include "ResourceGuard.h" #include "listen/Listener.h" #include "constant/PropertyKeyConst.h" #include "src/debug/DebugAssertion.h" #include "src/log/Logger.h" using namespace std; using namespace nacos; class MyListenerHttpPrefix : public Listener { private: int num; public: MyListenerHttpPrefix(int num) { this->num = num; } void receiveConfigInfo(const NacosString &configInfo) { cout << "===================================" << endl; cout << "Watcher" << num << endl; cout << "Watched Key UPDATED:" << configInfo << endl; cout << "===================================" << endl; } }; bool testListeningKeysWithHttpPrefix() { cout << "in function testListeningKeysWithHttpPrefix" << endl; Properties props; ADD_AUTH_INFO(props); ADD_SPAS_INFO(props); props[PropertyKeyConst::SERVER_ADDR] = "HttP://127.0.0.1:8848,HtTP://localhost"; INacosServiceFactory *factory = NacosFactoryFactory::getNacosFactory(props); ResourceGuard _guardFactory(factory); ConfigService *n = factory->CreateConfigService(); ResourceGuard _serviceFactory(n); MyListenerHttpPrefix *theListener = new MyListenerHttpPrefix(1); MyListenerHttpPrefix *theListener2 = new MyListenerHttpPrefix(2); MyListenerHttpPrefix *theListener3 = new MyListenerHttpPrefix(3); n->addListener("dqid", NULLSTR, theListener); n->addListener("dqid", NULLSTR, theListener2); n->addListener("dqid", NULLSTR, theListener3); n->addListener("dqid1", NULLSTR, theListener3); n->addListener("dqid2", NULLSTR, theListener3); n->addListener("dqid3", NULLSTR, theListener3); for (int i = 10; i < 60; i++) { NacosString strKey = "dqid" + NacosStringOps::valueOf(i); n->addListener(strKey, NULLSTR, theListener3); } cout << "Hold for 20 secs" << endl; sleep(20); cout << "remove listener" << endl; n->removeListener("dqid", NULLSTR, theListener); cout << "Hold for 20 secs" << endl; sleep(20); cout << "remove listener2" << endl; n->removeListener("dqid", NULLSTR, theListener2); n->removeListener("dqid", NULLSTR, theListener3); cout << "test successful" << endl; return true; } ================================================ FILE: test/testcase/testMAC.cpp ================================================ #include #include #include "src/debug/DebugAssertion.h" #include "src/crypto/MACProvider.h" using namespace std; using namespace nacos; typedef struct _HMACSHA1TestCase { const char *key; size_t key_len; const char *data; size_t data_len; const char *digest; } HMACSHA1TestCase; HMACSHA1TestCase HMACSHA1TestCases[] = { {"\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b", 20, "Hi There", 8, "\xb6\x17\x31\x86\x55\x05\x72\x64\xe2\x8b\xc0\xb6\xfb\x37\x8c\x8e\xf1\x46\xbe\x00"}, {"Jefe", 4, "what do ya want for nothing?", 28, "\xef\xfc\xdf\x6a\xe5\xeb\x2f\xa2\xd2\x74\x16\xd5\xf1\x84\xdf\x9c\x25\x9a\x7c\x79"}, {"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa", 20, "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd", 50, "\x12\x5d\x73\x42\xb9\xac\x11\xcd\x91\xa3\x9a\xf4\x8a\xa1\x7b\x4f\x63\xf1\x75\xd3"}, {"\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19", 25, "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd", 50, "\x4c\x90\x07\xf4\x02\x62\x50\xc6\xbc\x84\x14\xf9\xbf\x50\xc8\x6c\x2d\x72\x35\xda"}, {"\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c", 20, "Test With Truncation", 20, "\x4c\x1a\x03\x42\x4b\x55\xe0\x7f\xe7\xf2\x7b\xe1\xd5\x8b\xb9\x32\x4a\x9a\x5a\x04"}, {"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa", 80, "Test Using Larger Than Block-Size Key - Hash Key First", 54, "\xaa\x4a\xe5\xe1\x52\x72\xd0\x0e\x95\x70\x56\x37\xce\x8a\x3b\x55\xed\x40\x21\x12"}, {"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa", 80, "Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data", 73, "\xe8\xe9\x9d\x0f\x45\x23\x7d\x78\x6d\x6b\xba\xa7\x96\x5c\x78\x08\xbb\xff\x1a\x91"}, }; bool testHMACSHA1() { //Actually, I've no idea why Alibaba is still using this old-school MAC algorithm :( cout << "In function testHMACSHA1()" << endl; cout << "Getting MAC algorithm" << endl; cout << "Test case acquired at https://datatracker.ietf.org/doc/html/rfc2202" << endl; IMAC *digester = MACProvider::getMAC(MACProvider::HMAC_SHA1); SHOULD_BE_TRUE(digester != NULL, "Should be able to get a IMAC* from MACProvider"); for (size_t i = 0; i < sizeof(HMACSHA1TestCases) / sizeof(HMACSHA1TestCase); i++) { const void *k = (const void*)HMACSHA1TestCases[i].key; size_t kl = HMACSHA1TestCases[i].key_len; const void *data = (const void*)HMACSHA1TestCases[i].data; size_t dl = HMACSHA1TestCases[i].data_len; char out[256] = {0}; size_t out_l = sizeof(out); digester->getMac(k, kl, data, dl, out, &out_l); SHOULD_BE_TRUE(out_l == 20, "Digested data should be 20-bytes long"); SHOULD_BE_TRUE(memcmp(out, HMACSHA1TestCases[i].digest, 20) == 0, "Checking digested data with test vector"); cout << "Test case " << i + 1 << ": out_l=" << out_l << " passed." < #include #include "src/crypto/md5/md5.h" #include "src/debug/DebugAssertion.h" using namespace std; using namespace nacos; bool testMD5() { cout << "in function testMD5" << endl; MD5 md5; cout << "Checking md5 for \"a\"" << endl; md5.reset(); md5.update("a"); cout << "md5:" << md5.toString() << endl; SHOULD_BE_TRUE(md5.toString() == "0cc175b9c0f1b6a831c399e269772661", "NacosString \"a\" md5 should be 0cc175b9c0f1b6a831c399e269772661"); cout << "Checking md5 for \"aaaaaaa\"" << endl; md5.reset(); md5.update("aaaaaaa"); cout << "md5:" << md5.toString() << endl; SHOULD_BE_TRUE(md5.toString() == "5d793fc5b00a2348c3fb9ab59e5ca98a", "NacosString \"aaaaaaa\" md5 should be 5d793fc5b00a2348c3fb9ab59e5ca98a"); cout << "Checking md5 for \"2652451346435432\"" << endl; md5.reset(); md5.update("2652451346435432"); cout << "md5:" << md5.toString() << endl; SHOULD_BE_TRUE(md5.toString() == "69a8b79497dc611dcf9326a0b232ae55", "NacosString \"2652451346435432\" md5 should be 69a8b79497dc611dcf9326a0b232ae55"); return true; } ================================================ FILE: test/testcase/testMaintainInstances.cpp ================================================ #include #include #include #include #include "factory/NacosFactoryFactory.h" #include "naming/NamingMaintainService.h" #include "src/debug/DebugAssertion.h" #include "src/log/Logger.h" #include "NacosString.h" #include "Properties.h" #include "constant/PropertyKeyConst.h" #include "ResourceGuard.h" #include "constant/ConfigConstant.h" using namespace std; using namespace nacos; bool testMaintainUpdateInstance() { cout << "in function testMaintainUpdateInstance" << endl; Properties configProps; ADD_AUTH_INFO(configProps); ADD_SPAS_INFO(configProps); configProps[PropertyKeyConst::SERVER_ADDR] = "127.0.0.1"; INacosServiceFactory *factory = NacosFactoryFactory::getNacosFactory(configProps); ResourceGuard _guardFactory(factory); NamingMaintainService *maintainService = factory->CreateNamingMaintainService(); ResourceGuard _guardService(maintainService); NamingService *namingService = factory->CreateNamingService(); ResourceGuard _guardService2(namingService); ListView res; try { Instance instance; instance.serviceName = "MaintainTestUpdateInstance"; instance.ip = "127.0.0.1"; instance.port = 2333; instance.ephemeral = false; try { maintainService->deleteService("MaintainTestUpdateInstance", ConfigConstant::DEFAULT_GROUP); } catch (exception &ignore) { /*We may come across service not exist exception, just ignore*/ } namingService->registerInstance("MaintainTestUpdateInstance", instance); map metadata; metadata.insert(make_pair("hello", "world")); instance.metadata = metadata; bool res = maintainService->updateInstance("MaintainTestUpdateInstance", NULLSTR, instance); SHOULD_BE_TRUE(res, "updateInstance should succeed"); list instanceList = namingService->getInstanceWithPredicate("MaintainTestUpdateInstance", NULL); SHOULD_BE_TRUE(instanceList.size() == 1, "there should be one and only one instance of MaintainTestUpdateInstance"); map &metadataGet = instanceList.begin()->metadata; SHOULD_BE_TRUE(metadataGet.count("hello") == 1, "metadata should contain hello"); SHOULD_BE_TRUE(metadataGet["hello"].compare("world") == 0, "metadata should be {hello: world}"); try { //restore to the initial status maintainService->deleteService("MaintainTestUpdateInstance", ConfigConstant::DEFAULT_GROUP); } catch (exception &ignore) { /*We may come across service not exist exception, just ignore*/ } return true; } catch (NacosException &e) { cout << "encounter exception while testing, raison:" << e.what() << endl; return false; } } ================================================ FILE: test/testcase/testMaintainServices.cpp ================================================ #include #include #include #include #include "factory/NacosFactoryFactory.h" #include "naming/NamingMaintainService.h" #include "src/debug/DebugAssertion.h" #include "src/log/Logger.h" #include "NacosString.h" #include "Properties.h" #include "constant/PropertyKeyConst.h" #include "ResourceGuard.h" using namespace std; using namespace nacos; bool testMaintainGetService() { cout << "in function testMaintainGetService" << endl; Properties configProps; ADD_AUTH_INFO(configProps); ADD_SPAS_INFO(configProps); configProps[PropertyKeyConst::SERVER_ADDR] = "127.0.0.1"; INacosServiceFactory *factory = NacosFactoryFactory::getNacosFactory(configProps); ResourceGuard _guardFactory(factory); NamingMaintainService *maintainService = factory->CreateNamingMaintainService(); ResourceGuard _guardService(maintainService); NamingService *namingService = factory->CreateNamingService(); ResourceGuard _guardService2(namingService); ListView res; try { namingService->registerInstance("MaintainTestService", "127.0.0.1", 2333); ServiceInfo2 res = maintainService->queryService("MaintainTestService", NULLSTR); cout << "service name got from server:" << res.getName() << endl; SHOULD_BE_TRUE(res.getName().compare("MaintainTestService") == 0, "Service name should be MaintainTestService"); } catch (NacosException &e) { cout << "encounter exception while getting service, raison:" << e.what() << endl; return false; } return true; } bool testMaintainUpdateService() { cout << "in function testMaintainUpdateService" << endl; Properties configProps; ADD_AUTH_INFO(configProps); ADD_SPAS_INFO(configProps); configProps[PropertyKeyConst::SERVER_ADDR] = "127.0.0.1"; INacosServiceFactory *factory = NacosFactoryFactory::getNacosFactory(configProps); ResourceGuard _guardFactory(factory); NamingMaintainService *maintainService = factory->CreateNamingMaintainService(); ResourceGuard _guardService(maintainService); NamingService *namingService = factory->CreateNamingService(); ResourceGuard _guardService2(namingService); ListView res; try { namingService->registerInstance("MaintainTestUpdateService", "127.0.0.1", 2333); ServiceInfo2 serviceInfo2; serviceInfo2.setName("MaintainTestUpdateService"); serviceInfo2.setProtectThreshold((double)2); bool updateRes = maintainService->updateService(serviceInfo2, NULL); cout << "update protect threshold:" << NacosStringOps::valueOf(updateRes) << endl; SHOULD_BE_TRUE(updateRes, "update of protect threshold should be successful"); ServiceInfo2 serviceInfoQuery = maintainService->queryService("MaintainTestService", NULLSTR); SHOULD_BE_TRUE(serviceInfoQuery.getProtectThreshold() - 2.0 < 1e-9, "protect threshold should be 2.0D"); } catch (NacosException &e) { cout << "encounter exception while updating service, raison:" << e.what() << endl; return false; } return true; } //actually this will test delete as well bool testMaintainCreateService() { cout << "in function testMaintainCreateService" << endl; Properties configProps; ADD_AUTH_INFO(configProps); ADD_SPAS_INFO(configProps); configProps[PropertyKeyConst::SERVER_ADDR] = "127.0.0.1"; INacosServiceFactory *factory = NacosFactoryFactory::getNacosFactory(configProps); ResourceGuard _guardFactory(factory); NamingMaintainService *maintainService = factory->CreateNamingMaintainService(); ResourceGuard _guardService(maintainService); ListView res; try { ServiceInfo2 serviceInfo2; serviceInfo2.setName("MaintainTestCreateService"); serviceInfo2.setProtectThreshold((double)2); map metadata; metadata.insert(make_pair("hello", "world")); metadata.insert(make_pair("girlfriend", "Liao, Sijia")); serviceInfo2.setMetadata(metadata); try { maintainService->deleteService("MaintainTestCreateService", NULLSTR); } catch (exception &ignore) { log_debug("designated service does not exist\n"); } bool createResult = maintainService->createService(serviceInfo2, NULL); SHOULD_BE_TRUE(createResult, "MaintainTestCreateService should be created"); ServiceInfo2 serviceInfoQuery = maintainService->queryService("MaintainTestCreateService", NULLSTR); SHOULD_BE_TRUE(serviceInfoQuery.getProtectThreshold() - 2.0 < 1e-9, "protect threshold should be 2.0D"); SHOULD_BE_TRUE(serviceInfoQuery.getName().compare("MaintainTestCreateService") == 0, "name should be MaintainTestCreateService"); map returnedMetadata = serviceInfoQuery.getMetadata(); SHOULD_BE_TRUE(returnedMetadata.count("hello") == 1, "metadata should contain hello"); SHOULD_BE_TRUE(returnedMetadata.count("girlfriend") == 1, "metadata should contain girlfriend"); NacosString hello = returnedMetadata["hello"]; SHOULD_BE_TRUE(hello.compare("world") == 0 , "hello should be world"); NacosString girlfriend = returnedMetadata["girlfriend"]; SHOULD_BE_TRUE(girlfriend.compare("Liao, Sijia") == 0, "girlfriend should be correct"); bool deleteFunctionality = maintainService->deleteService("MaintainTestCreateService", NULLSTR); SHOULD_BE_TRUE(deleteFunctionality, "delete should be successful"); } catch (NacosException &e) { cout << "encounter exception while updating service, raison:" << e.what() << endl; return false; } return true; } ================================================ FILE: test/testcase/testNamingService.cpp ================================================ #include #include #include #include "src/naming/NamingProxy.h" #include "src/naming/NacosNamingService.h" #include "factory/NacosFactoryFactory.h" #include "ResourceGuard.h" #include "naming/Instance.h" #include "constant/ConfigConstant.h" #include "constant/UtilAndComs.h" #include "src/http/HTTPCli.h" #include "src/debug/DebugAssertion.h" #include "src/log/Logger.h" #include "NacosString.h" #include "Properties.h" #include "constant/PropertyKeyConst.h" #define NR_SERVICE_INSTS 3 #define NR_SERVICES 3 using namespace std; using namespace nacos; bool testNamingProxySmokeTest() { cout << "in function testNamingProxySmokeTest" << endl; NacosString servers = "127.0.0.1:8848"; Properties props; ADD_AUTH_INFO(props); ADD_SPAS_INFO(props); props[PropertyKeyConst::SERVER_ADDR] = servers; INacosServiceFactory *factory = NacosFactoryFactory::getNacosFactory(props); ResourceGuard _guardFactory(factory); NamingService *n = factory->CreateNamingService(); ResourceGuard _serviceFactory(n); NacosNamingService *nn = (NacosNamingService *) n; NamingProxy *namingProxy = nn->getServerProxy(); Instance theinstance; theinstance.instanceId = "TestInstance"; theinstance.ip = "127.0.0.1"; theinstance.port = 3333; theinstance.clusterName = "TestCluster"; //Create a clean environment try { for (int i = 0; i < 10; i++) { NacosString serviceName = "TestServiceName" + NacosStringOps::valueOf(i); theinstance.serviceName = serviceName; namingProxy->deregisterService(serviceName, theinstance); } } catch (NacosException &e) { cout << "Exception caught during deregistering service, raison:" << e.what() << endl; return false; } //Register 100 services try { for (int i = 0; i < 10; i++) { NacosString serviceName = "TestServiceName" + NacosStringOps::valueOf(i); theinstance.serviceName = serviceName; namingProxy->registerService(serviceName, ConfigConstant::DEFAULT_GROUP, theinstance); sleep(1); } } catch (NacosException &e) { cout << "Exception caught during registering service, raison:" << e.what() << endl; return false; } //check whether the data are correct for (int i = 0; i < 10; i++) { NacosString serviceName = "TestServiceName" + NacosStringOps::valueOf(i); NacosString serverlist = namingProxy->queryList(serviceName, ConfigConstant::DEFAULT_GROUP, "TestCluster", 0, false); cout << serverlist << endl; if (serverlist.find("\"serviceName\":\"" + serviceName + "\"") == string::npos && //nacos 2.x compatibility serverlist.find("\"serviceName\":\"DEFAULT_GROUP@@" + serviceName + "\"") == string::npos) { cout << "Failed to get data for:" << serviceName << endl; return false; } cout << "Servers from nacos:" + serverlist << endl; } //Clear-ups try { for (int i = 0; i < 10; i++) { NacosString serviceName = "TestServiceName" + NacosStringOps::valueOf(i); theinstance.serviceName = serviceName; namingProxy->deregisterService(serviceName, theinstance); } } catch (NacosException &e) { cout << "Exception caught during cleaning the test environment, raison:" << e.what() << endl; return false; } return true; } bool testNamingProxyServerHealthy() { cout << "in function testNamingProxyServerHealthy" << endl; NacosString servers = "127.0.0.1:8848"; Properties props; ADD_AUTH_INFO(props); ADD_SPAS_INFO(props); props[PropertyKeyConst::SERVER_ADDR] = servers; INacosServiceFactory *factory = NacosFactoryFactory::getNacosFactory(props); ResourceGuard _guardFactory(factory); NamingService *n = factory->CreateNamingService(); ResourceGuard _serviceFactory(n); NacosNamingService *nn = (NacosNamingService *) n; NamingProxy *namingProxy = nn->getServerProxy(); //Create a clean environment bool healthy; try { healthy = namingProxy->serverHealthy(); } catch (NacosException &e) { cout << "Exception caught during deregistering service, raison:" << e.what() << endl; return false; } if (healthy) { cout << "server healthy" << endl; } else { cout << "server down" << endl; } return true; } bool testNamingProxyFailOver() { cout << "in function testNamingProxyFailOver" << endl; return true; } class NamingServiceSelector : public naming::selectors::Selector { private: int _port; public: void setSelectPort(int port) { _port = port; }; list select(const std::list &itemsToSelect) { list res; for (list::const_iterator it = itemsToSelect.begin(); it != itemsToSelect.end(); it++) { if ((*it).port == _port) { res.push_back(*it); } } return res; } }; bool testNamingServiceRegister() { cout << "in function testNamingServiceRegister" << endl; Properties configProps; configProps[PropertyKeyConst::SERVER_ADDR] = "127.0.0.1"; ADD_AUTH_INFO(configProps); ADD_SPAS_INFO(configProps); INacosServiceFactory *factory = NacosFactoryFactory::getNacosFactory(configProps); ResourceGuard _guardFactory(factory); NamingService *namingSvc = factory->CreateNamingService(); ResourceGuard _serviceFactory(namingSvc); Instance instance; instance.clusterName = "DefaultCluster"; instance.ip = "127.0.0.1"; instance.port = 2333; instance.instanceId = "1"; instance.ephemeral = true; try { for (int i = 0; i < NR_SERVICES; i++) { NacosString serviceName = "TestNamingService" + NacosStringOps::valueOf(i); for (int j = 0; j < NR_SERVICE_INSTS; j++) { instance.clusterName = "DefaultCluster"; instance.ip = "127.0.0.1"; instance.port = 20000 + i*NR_SERVICE_INSTS+j; instance.instanceId = NacosStringOps::valueOf(i * NR_SERVICE_INSTS + j); instance.ephemeral = true; namingSvc->registerInstance(serviceName, instance); } } } catch (NacosException &e) { cout << "encounter exception while registering service instance, raison:" << e.what() << endl; return false; } sleep(5); try { for (int i = 0; i < NR_SERVICES; i++) { NacosString serviceName = "TestNamingService" + NacosStringOps::valueOf(i); for (int j = 0; j < NR_SERVICE_INSTS; j++) { NamingServiceSelector selector; selector.setSelectPort(20000 + i*NR_SERVICE_INSTS+j); list instanceList = namingSvc->getInstanceWithPredicate(serviceName, &selector); if (!instanceList.empty()) { cout << (i*NR_SERVICE_INSTS+j) << ":Instance info got from server:" << instanceList.begin()->toString() << endl; } else { cout << "Got empty instance list from server!" << endl; return false; } SHOULD_BE_TRUE(instanceList.size() == 1, "There should be only 1 instance for each port"); SHOULD_BE_TRUE(instanceList.begin()->clusterName == "DefaultCluster", "Cluster name should be DefaultCluster"); SHOULD_BE_TRUE(instanceList.begin()->ephemeral == true, "Should be ephemeral node"); } } } catch (NacosException &e) { cout << "encounter exception while getting service instance, raison:" << e.what() << endl; return false; } cout << "testNamingServiceRegister successful" << endl; return true; } bool testNamingServiceAndDeRegisterActively() { cout << "in function testNamingServiceAndDeRegisterActively" << endl; Properties configProps; configProps[PropertyKeyConst::SERVER_ADDR] = "127.0.0.1"; ADD_AUTH_INFO(configProps); ADD_SPAS_INFO(configProps); INacosServiceFactory *factory = NacosFactoryFactory::getNacosFactory(configProps); ResourceGuard _guardFactory(factory); NamingService *namingSvc = factory->CreateNamingService(); ResourceGuard _serviceFactory(namingSvc); Instance instance; instance.clusterName = "DefaultCluster"; instance.ip = "127.0.0.1"; instance.port = 2333; instance.instanceId = "1"; instance.ephemeral = true; try { for (int i = 0; i < NR_SERVICES; i++) { NacosString serviceName = "TestNamingService" + NacosStringOps::valueOf(i); for (int j = 0; j < NR_SERVICE_INSTS; j++) { instance.clusterName = "DefaultCluster"; instance.ip = "127.0.0.1"; instance.port = 20000 + i*NR_SERVICE_INSTS+j; instance.instanceId = NacosStringOps::valueOf(i * NR_SERVICE_INSTS + j); instance.ephemeral = true; namingSvc->registerInstance(serviceName, instance); } } } catch (NacosException &e) { cout << "encounter exception while registering service instance, raison:" << e.what() << endl; return false; } sleep(5); try { for (int i = 0; i < NR_SERVICES; i++) { NacosString serviceName = "TestNamingService" + NacosStringOps::valueOf(i); for (int j = 0; j < NR_SERVICE_INSTS; j++) { NamingServiceSelector selector; selector.setSelectPort(20000 + i*NR_SERVICE_INSTS+j); list instanceList = namingSvc->getInstanceWithPredicate(serviceName, &selector); if (!instanceList.empty()) { cout << (i*NR_SERVICE_INSTS+j) << ":Instance info got from server:" << instanceList.begin()->toString() << endl; } else { cout << "Got empty instance list from server!" << endl; return false; } SHOULD_BE_TRUE(instanceList.size() == 1, "There should be only 1 instance for each port"); SHOULD_BE_TRUE(instanceList.begin()->clusterName == "DefaultCluster", "Cluster name should be DefaultCluster"); SHOULD_BE_TRUE(instanceList.begin()->ephemeral == true, "Should be ephemeral node"); } } } catch (NacosException &e) { cout << "encounter exception while getting service instance, raison:" << e.what() << endl; return false; } try { for (int i = 0; i < NR_SERVICES; i++) { NacosString serviceName = "TestNamingService" + NacosStringOps::valueOf(i); for (int j = 0; j < NR_SERVICE_INSTS; j++) { NamingServiceSelector selector; selector.setSelectPort(20000 + i*NR_SERVICE_INSTS+j); namingSvc->deregisterInstance(serviceName, "127.0.0.1", 20000 + i*NR_SERVICE_INSTS+j); } } } catch (NacosException &e) { cout << "encounter exception while getting service instance, raison:" << e.what() << endl; return false; } cout << "wait for 40 secs to make sure all instances are removed from nacos server" << endl; sleep(40); try { for (int i = 0; i < NR_SERVICES; i++) { NacosString serviceName = "TestNamingService" + NacosStringOps::valueOf(i); for (int j = 0; j < NR_SERVICE_INSTS; j++) { NamingServiceSelector selector; selector.setSelectPort(20000 + i*NR_SERVICE_INSTS+j); list instanceList = namingSvc->getInstanceWithPredicate(serviceName, &selector); SHOULD_BE_TRUE(instanceList.empty(), "There shouldn't be any instance since removed"); } } } catch (NacosException &e) { cout << "encounter exception while getting service instance, raison:" << e.what() << endl; return false; } cout << "testNamingServiceAndDeRegisterActively successful" << endl; return true; } ================================================ FILE: test/testcase/testNamingSubscribe.cpp ================================================ #include #include #include #include #include "factory/NacosFactoryFactory.h" #include "ResourceGuard.h" #include "naming/subscribe/EventListener.h" #include "constant/PropertyKeyConst.h" #include "src/debug/DebugAssertion.h" #include "src/log/Logger.h" using namespace std; using namespace nacos; class MyServiceListener : public EventListener { private: int num; public: MyServiceListener(int num) { this->num = num; } void receiveNamingInfo(const ServiceInfo &serviceInfo){ cout << "===================================" << endl; cout << "Watcher: " << num << endl; cout << "Watched service UPDATED: " << serviceInfo.toInstanceString() << endl; cout << "===================================" << endl; } }; bool testListenService() { cout << "in function testListenService" << endl; Properties props; props[PropertyKeyConst::SERVER_ADDR] = "127.0.0.1:8848"; props[PropertyKeyConst::LOCAL_IP] = "127.0.0.1"; ADD_AUTH_INFO(props); ADD_SPAS_INFO(props); INacosServiceFactory *factory = NacosFactoryFactory::getNacosFactory(props); ResourceGuard _guardFactory(factory); NamingService *n = factory->CreateNamingService(); ResourceGuard _serviceFactory(n); n->subscribe("ss", new MyServiceListener(1)); n->registerInstance("ss", "127.0.0.1", 33); n->registerInstance("ss", "127.0.0.1", 34); n->deregisterInstance("ss", "127.0.0.1", 33); n->deregisterInstance("ss", "127.0.0.1", 34); n->registerInstance("ss", "127.0.0.1", 33); n->registerInstance("ss", "127.0.0.1", 34); n->deregisterInstance("ss", "127.0.0.1", 33); n->deregisterInstance("ss", "127.0.0.1", 34); cout << "All instances Unregistered" << endl; return true; } //testcase for bugfix#101, if the problem persists, this function will cause a coredump bool testSubscribeAlotOfServices() { cout << "in function testSubscribeAlotOfServices" << endl; Properties props; props[PropertyKeyConst::SERVER_ADDR] = "127.0.0.1:8848"; props[PropertyKeyConst::LOCAL_IP] = "127.0.0.1"; props[PropertyKeyConst::LOG_LEVEL] = "WARN"; ADD_AUTH_INFO(props); ADD_SPAS_INFO(props); INacosServiceFactory *factory = NacosFactoryFactory::getNacosFactory(props); ResourceGuard _guardFactory(factory); NamingService *n = factory->CreateNamingService(); ResourceGuard _serviceFactory(n); n->subscribe("ss", new MyServiceListener(1)); for (int i = 0; i < 1000; i++) { n->registerInstance("ss", "127.0.0.1", 2000+i); } sleep(20); for (int i = 0; i < 1000; i++) { n->deregisterInstance("ss", "127.0.0.1", 2000+i); } return true; } ================================================ FILE: test/testcase/testNetUtils.cpp ================================================ #include #include "src/utils//NetUtils.h" #include "src/debug/DebugAssertion.h" using namespace std; using namespace nacos; bool testGetHostIp() { cout << "in function testGetHostIp" << endl; NacosString ip = NetUtils::getHostIp(); cout << "ip:" << ip << endl; SHOULD_BE_TRUE(!ip.empty(), "Ip got for local machine should not be empty"); cout << "test end..." << endl; return true; } ================================================ FILE: test/testcase/testPublishConfig.cpp ================================================ #include #include #include #include #include "factory/NacosFactoryFactory.h" #include "ResourceGuard.h" #include "constant/PropertyKeyConst.h" #include "src/debug/DebugAssertion.h" #include "src/log/Logger.h" using namespace std; using namespace nacos; bool testPublishConfig() { cout << "in function testPublishConfig" << endl; Properties props; props[PropertyKeyConst::SERVER_ADDR] = "127.0.0.1:8848"; ADD_AUTH_INFO(props); ADD_SPAS_INFO(props); INacosServiceFactory *factory = NacosFactoryFactory::getNacosFactory(props); ResourceGuard _guardFactory(factory); ConfigService *n = factory->CreateConfigService(); ResourceGuard _serviceFactory(n); bool bSucc; for (int i = 0; i < 50; i++) { char key_s[200]; char val_s[200]; snprintf(key_s, sizeof(key_s), "Key%d", i); snprintf(val_s, sizeof(val_s), "v__%d", i); NacosString ss = ""; try { bSucc = n->publishConfig(key_s, NULLSTR, val_s); int retry = 0; while (!(ss == val_s) && retry++ < 10) { sleep(1); try { ss = n->getConfig(key_s, NULLSTR, 1000); } catch (NacosException & ignore) { //getConfig may throw 404, but that doesn't matter } } if (!(ss == val_s)) { throw NacosException(0, "getConfig() failed."); } } catch (NacosException &e) { cout << "Request failed with curl code:" << e.errorcode() << endl << "Reason:" << e.what() << endl; return false; } cout << "Publishing Key:" << key_s << " with value:" << val_s << " result:" << bSucc << endl; } return true; } ================================================ FILE: test/testcase/testPublishConfigWithHttpPrefix.cpp ================================================ #include #include #include #include #include "factory/NacosFactoryFactory.h" #include "ResourceGuard.h" #include "constant/PropertyKeyConst.h" #include "src/debug/DebugAssertion.h" #include "src/log/Logger.h" using namespace std; using namespace nacos; bool testPublishConfigWithHttpPrefix() { cout << "in function testPublishConfigWithHttpPrefix" << endl; Properties props; props[PropertyKeyConst::SERVER_ADDR] = "htTp://localhost:8848,HtTP://127.0.0.1:8848"; ADD_AUTH_INFO(props); ADD_SPAS_INFO(props); INacosServiceFactory *factory = NacosFactoryFactory::getNacosFactory(props); ResourceGuard _guardFactory(factory); ConfigService *n = factory->CreateConfigService(); ResourceGuard _serviceFactory(n); bool bSucc; for (int i = 0; i < 50; i++) { char key_s[200]; char val_s[200]; snprintf(key_s, sizeof(key_s), "Key%d", i); snprintf(val_s, sizeof(val_s), "v__%d", i); NacosString ss = ""; try { bSucc = n->publishConfig(key_s, NULLSTR, val_s); int retry = 0; while (!(ss == val_s) && retry++ < 10) { sleep(1); try { ss = n->getConfig(key_s, NULLSTR, 1000); } catch (NacosException &e) { } } n->removeConfig(key_s, NULLSTR); if (!(ss == val_s)) { throw NacosException(0, "getConfig() failed."); } } catch (NacosException &e) { cout << "Request failed with curl code:" << e.errorcode() << endl << "Reason:" << e.what() << endl; return false; } cout << "Publishing Key:" << key_s << " with value:" << val_s << " result:" << bSucc << endl; } return true; } ================================================ FILE: test/testcase/testRapidJson.cpp ================================================ #include #include "src/debug/DebugAssertion.h" #include "src/json/rapidjson/document.h" #include "src/json/rapidjson/writer.h" #include "src/json/rapidjson/stringbuffer.h" #include "src/naming/beat/BeatInfo.h" #include "src/json/JSON.h" using namespace std; using namespace rapidjson; using namespace nacos; bool testRapidJsonIntroduce() { // 1. 把 JSON 解析至 DOM。 const char *json = "{\"project\":\"rapidjson\",\"stars\":10}"; Document d; d.Parse(json); // 2. 利用 DOM 作出修改。 Value &s = d["stars"]; s.SetInt(s.GetInt() + 1); // 3. 把 DOM 转换(stringify)成 JSON。 StringBuffer buffer; Writer writer(buffer); d.Accept(writer); // Output {"project":"rapidjson","stars":11} std::cout << buffer.GetString() << std::endl; Document parsedAgain; parsedAgain.Parse(buffer.GetString()); Value &s2 = parsedAgain["stars"]; int expectedtoBe11 = s2.GetInt(); SHOULD_BE_TRUE(expectedtoBe11 == 11, "There should be 11 stars"); return true; } bool testSerialize() { BeatInfo bi; bi.port = 10; cout << JSON::toJSONString(bi); return true; } ================================================ FILE: test/testcase/testSequenceProvider.cpp ================================================ #include #include "src/thread/Thread.h" #include "src/utils/SequenceProvider.h" #include "src/utils/DirUtils.h" using namespace std; using namespace nacos; #define NR_THREADS 200 #define GENERATION_PER_THREAD 1000 int64_t sequences[GENERATION_PER_THREAD * NR_THREADS]; int tid[NR_THREADS]; SequenceProvider *sequenceProvider; void *SeqThreadFunc(void *param) { int *thread_no = (int*)param; for (int i = 0; i < GENERATION_PER_THREAD; i++) { int64_t res = sequenceProvider->next(); sequences[(*thread_no) * GENERATION_PER_THREAD + i] = res; } return NULL; } bool testSequenceProvider() { cout << "in function testSequenceProvider" << endl; cout << "Generating SEQ..." << endl; sequenceProvider = new SequenceProvider (DirUtils::getCwd() + "/test_seq.dat", 20000, 100); Thread *threads[NR_THREADS] = {NULL}; for (int i = 0; i < NR_THREADS; i++) { NacosString threadName = "SEQThread-" + NacosStringOps::valueOf(i); tid[i] = i; threads[i] = new Thread(threadName, SeqThreadFunc, (void *) &tid[i]); threads[i]->start(); } for (int i = 0; i < NR_THREADS; i++) { threads[i]->join(); delete threads[i]; } cout << "Generated." << endl; for (int i = 0; i < NR_THREADS; i++) { for (int j = 0; j < GENERATION_PER_THREAD; j++) { cout << "Thread " << i << ": sequence =\t" << sequences[i * GENERATION_PER_THREAD + j] << endl; } } cout << "test end..." << endl; return true; } ================================================ FILE: test/testcase/testServerListManager.cpp ================================================ #include #include #include "src/server/ServerListManager.h" #include "src/config/NacosConfigService.h" #include "factory/NacosFactoryFactory.h" #include "ResourceGuard.h" #include "src/http/HTTPCli.h" #include "constant/PropertyKeyConst.h" #include "src/json/JSON.h" using namespace std; using namespace nacos; bool testServerListManager() { cout << "in function testServerListManager" << endl; Properties props; props[PropertyKeyConst::SERVER_ADDR] = "127.0.0.1:8848"; INacosServiceFactory *factory = NacosFactoryFactory::getNacosFactory(props); ResourceGuard _guardFactory(factory); ConfigService *n = factory->CreateConfigService(); ResourceGuard _serviceFactory(n); //NacosConfigService *nn = (NacosConfigService *) n; //ServerListManager *serverListManager = nn->getServerListManager(); list res;// = serverListManager->__debug(); for (list::iterator it = res.begin(); it != res.end(); it++) { NacosString key = it->getKey(); NacosString val = it->getCompleteAddress(); cout << key << ":" << endl << val << endl; } cout << "=====================cornercase========================" << endl; NacosString fakeSvr = "{\"servers\":[]}"; list result = JSON::Json2NacosServerInfo(fakeSvr); cout << "result.size == " << result.size() << endl; for (list::iterator it = result.begin(); it != result.end(); it++) { NacosString key = it->getCompleteAddress(); NacosString val = it->toString(); cout << key << ":" << endl << val << endl; } return true; } ================================================ FILE: test/testcase/testStringExplode.cpp ================================================ #include #include #include #include "src/utils/ParamUtils.h" #include "src/debug/DebugAssertion.h" #include "src/log/Logger.h" using namespace std; using namespace nacos; bool testStringExplode() { cout << "in function testStringExplode" << endl; vector explodedList; NacosString originalContent = "Hello|World|My|Name|Is"; ParamUtils::Explode(explodedList, originalContent, '|'); SHOULD_BE_TRUE(explodedList.size() == 5, "Exploding Hello|World|My|Name|Is with separator | should get a list with size 5."); SHOULD_BE_TRUE(explodedList[0] == "Hello", "explodedList[0] should be Hello"); SHOULD_BE_TRUE(explodedList[1] == "World", "explodedList[1] should be World"); SHOULD_BE_TRUE(explodedList[2] == "My", "explodedList[2] should be My"); SHOULD_BE_TRUE(explodedList[3] == "Name", "explodedList[3] should be Name"); SHOULD_BE_TRUE(explodedList[4] == "Is", "explodedList[4] should be Is"); vector specialExplode; NacosString specialoriginalContent = "Hello\x1World\x1My\x1Name\x1Is"; ParamUtils::Explode(specialExplode, specialoriginalContent, '\x1'); SHOULD_BE_TRUE(specialExplode.size() == 5, "Exploding Hello\x1World\x1My\x1Name\x1Is with separator \x1 should get a list with size 5."); SHOULD_BE_TRUE(specialExplode[0] == "Hello", "specialExplode[0] should be Hello"); SHOULD_BE_TRUE(specialExplode[1] == "World", "specialExplode[1] should be World"); SHOULD_BE_TRUE(specialExplode[2] == "My", "specialExplode[2] should be My"); SHOULD_BE_TRUE(specialExplode[3] == "Name", "specialExplode[3] should be Name"); SHOULD_BE_TRUE(specialExplode[4] == "Is", "specialExplode[4] should be Is"); vector nullEnd; NacosString nullEndStr = "k="; ParamUtils::Explode(nullEnd, nullEndStr, '='); SHOULD_BE_TRUE(nullEnd.size() == 2, "exploding k= should get 2 items"); SHOULD_BE_TRUE(nullEnd[0] == "k", "nullEnd[0] should be k"); SHOULD_BE_TRUE(nullEnd[1] == "", "nullEnd[0] should be empty"); return true; } bool testStringExplode2() { cout << "in function testStringExplode2 - enhanced one which can handle string separator" << endl; vector explodedList; NacosString originalContent = "Hello||World||My||Name||Is"; ParamUtils::Explode(explodedList, originalContent, "||"); SHOULD_BE_TRUE(explodedList.size() == 5, "Exploding Hello||World||My||Name||Is with separator || should get a list with size 5."); SHOULD_BE_TRUE(explodedList[0] == "Hello", "explodedList[0] should be Hello"); SHOULD_BE_TRUE(explodedList[1] == "World", "explodedList[1] should be World"); SHOULD_BE_TRUE(explodedList[2] == "My", "explodedList[2] should be My"); SHOULD_BE_TRUE(explodedList[3] == "Name", "explodedList[3] should be Name"); SHOULD_BE_TRUE(explodedList[4] == "Is", "explodedList[4] should be Is"); vector specialExplode; NacosString specialoriginalContent = "Hello\x1\x1World\x1\x1My\x1\x1Name\x1\x1Is\x1\x1"; ParamUtils::Explode(specialExplode, specialoriginalContent, "\x1\x1"); SHOULD_BE_TRUE(specialExplode.size() == 6, "Exploding Hello\x1\x1World\x1\x1My\x1\x1Name\x1\x1Is\x1\x1 with separator \x1\x1 should get a list with size 5."); SHOULD_BE_TRUE(specialExplode[0] == "Hello", "specialExplode[0] should be Hello"); SHOULD_BE_TRUE(specialExplode[1] == "World", "specialExplode[1] should be World"); SHOULD_BE_TRUE(specialExplode[2] == "My", "specialExplode[2] should be My"); SHOULD_BE_TRUE(specialExplode[3] == "Name", "specialExplode[3] should be Name"); SHOULD_BE_TRUE(specialExplode[4] == "Is", "specialExplode[4] should be Is"); vector nullEnd; NacosString nullEndStr = "k=="; ParamUtils::Explode(nullEnd, nullEndStr, "=="); SHOULD_BE_TRUE(nullEnd.size() == 2, "exploding k== should get 2 items"); SHOULD_BE_TRUE(nullEnd[0] == "k", "nullEnd[0] should be k"); SHOULD_BE_TRUE(nullEnd[1] == "", "nullEnd[0] should be empty"); return true; } ================================================ FILE: test/testcase/testThreadLocal.cpp ================================================ #include #include "src/utils/UuidUtils.h" #include "src/thread/Thread.h" #include "src/thread/ThreadLocal.h" #include "src/debug/DebugAssertion.h" #include "src/utils/RandomUtils.h" using namespace std; using namespace nacos; ThreadLocal threadLocal; ThreadLocal threadLocalPtr(NULL); class ThreadLocalWithInit : public ThreadLocal { public: void onCreate(int **value){ *value = (int*)0xffff; log_debug("ThreadLocalWithInit::onCreate is called, ptr value: %p\n", *value); } void onDestroy(int **value) { log_debug("ThreadLocalWithInit::onDestroy is called, ptr value: %p\n", *value); } }; ThreadLocalWithInit threadLocalPtrWithInitializer; void *ThreadLocalFuncs4Ptr(void *param) { log_debug("threadLocalPtr.get() : %p, should be null\n", threadLocalPtr.get()); NACOS_ASSERT(threadLocalPtr.get() == NULL); for (int i = 0; i < 100; i++) { int* rndPtr = reinterpret_cast(RandomUtils::random(0, 1000)); threadLocalPtr.set(rndPtr); NACOS_ASSERT(rndPtr == threadLocalPtr.get()) } return NULL; } void *ThreadLocalFuncs4PtrWithInitializer(void *param) { log_debug("threadLocalPtr.get() : %p, should be 0xFFFF\n", threadLocalPtrWithInitializer.get()); NACOS_ASSERT(threadLocalPtrWithInitializer.get() == (int*)0xFFFF); for (int i = 0; i < 100; i++) { int* rndPtr = reinterpret_cast(RandomUtils::random(0, 1000)); threadLocalPtrWithInitializer.set(rndPtr); NACOS_ASSERT(rndPtr == threadLocalPtrWithInitializer.get()) } return NULL; } void *ThreadLocalFuncs(void *param) { Thread *thisThread = *((Thread **) param); for (int i = 0; i < 100; i++) { threadLocal.set(UuidUtils::generateUuid().c_str()); log_debug("Thread %s UUID: %s\n", thisThread->getThreadName().c_str(), threadLocal.get().c_str()); } return NULL; } bool testThreadLocal() { cout << "in function testThreadLocal" << endl; cout << "Generating threads..." << endl; Thread *threads[10] = {NULL}; for (int i = 0; i < 10; i++) { NacosString threadName = "Thread-" + NacosStringOps::valueOf(i); threads[i] = new Thread(threadName, ThreadLocalFuncs, (void *) &threads[i]); threads[i]->start(); } for (int i = 0; i < 10; i++) { threads[i]->join(); delete threads[i]; } cout << "test end..." << endl; return true; } bool testThreadLocalPtr() { cout << "in function testThreadLocalPtr" << endl; cout << "Generating threads..." << endl; Thread *threads[10] = {NULL}; for (int i = 0; i < 10; i++) { NacosString threadName = "ThreadPtr-" + NacosStringOps::valueOf(i); threads[i] = new Thread(threadName, ThreadLocalFuncs4Ptr, (void *) &threads[i]); threads[i]->start(); } for (int i = 0; i < 10; i++) { threads[i]->join(); delete threads[i]; } cout << "test end..." << endl; return true; } bool testThreadLocalPtrWithInitializer() { cout << "in function testThreadLocalPtrWithInitializer" << endl; cout << "Generating threads..." << endl; Thread *threads[10] = {NULL}; for (int i = 0; i < 10; i++) { NacosString threadName = "ThreadPtr-" + NacosStringOps::valueOf(i); threads[i] = new Thread(threadName, ThreadLocalFuncs4PtrWithInitializer, (void *) &threads[i]); threads[i]->start(); } for (int i = 0; i < 10; i++) { threads[i]->join(); delete threads[i]; } cout << "test end..." << endl; return true; } ================================================ FILE: test/testcase/testThreadSmoke.cpp ================================================ #include #include #include "src/debug/DebugAssertion.h" #include "src/log/Logger.h" #include "src/thread/Thread.h" #include "src/thread/ThreadPool.h" #include "NacosString.h" #include "thread/AtomicInt.h" using namespace std; using namespace nacos; class ThreadPoolLongRunTask : public Task { public: ThreadPoolLongRunTask(int taskId) { NacosString taskName = "ThreadPoolLongRunTask" + NacosStringOps::valueOf(taskId); setTaskName(taskName); }; void run() { NacosString taskName = getTaskName(); log_info("Hello world from %s\n", taskName.c_str()); log_info("Run for 10 secs\n"); sleep(5); log_info("ok\n"); }; }; void *threadFunc(void *param) { log_info("Hello from threadFunc() in another thread, stopping threadpool\n"); return NULL; } bool testThreadSmoke() { cout << "In testThreadSmoke" << endl; Thread t("testThread", threadFunc); t.start(); t.join(); cout << "If no coredump, should be successful" << endl; return true; } void *threadStopper(void *param) { ThreadPool *tp = (ThreadPool *) param; log_info("Hello from threadStopper() in another thread, stopping threadpool\n"); sleep(10); tp->stop(); log_info("threadStopper():ok\n"); return NULL; } bool testThreadPoolSmoke() { cout << "Starting testThreadPoolSmoke..." << endl; ThreadPool tp(10); tp.start(); cout << "ok, size = 32" << endl; Thread tpStopper("tpStopper", threadStopper, &tp); tpStopper.start(); Task *tasks[1000]; for (size_t i = 0; i < 40; i++) { cout << "Creating task " << i << "..." << endl; tasks[i] = new ThreadPoolLongRunTask(i); tp.put(tasks[i]); cout << "ok" << endl; } tp.stop(); for (size_t i = 0; i < 40; i++) { cout << "destroying task " << i << "..." << endl; delete tasks[i]; tasks[i] = NULL; cout << "ok" << endl; } cout << "If no coredump, should be successful" << endl; tpStopper.join(); return true; } AtomicInt totalFinishedThreads; class SmokingTestThreadTask : public Task { private: AtomicInt &_counter; public: SmokingTestThreadTask(const NacosString &taskName, AtomicInt &counter) : _counter(counter) { setTaskName(taskName); }; void run() { NacosString taskName = getTaskName(); int currentTotal = 0; for (int i = 0; i < 100000; i++) { currentTotal = _counter.inc(); } log_info("Hello world from %s, totalCount = %d\n", taskName.c_str(), currentTotal); totalFinishedThreads.inc(); }; }; bool testThreadPoolConcurrentWithAtomicCounter() { cout << "Starting testThreadPoolConcurrentWithAtomicCounter..." << endl; ThreadPool tp(10); tp.start(); cout << "ok, size = 10" << endl; AtomicInt totalCounter; Task *tasks[1000]; for (size_t i = 0; i < 40; i++) { cout << "Creating task " << i << "..." << endl; tasks[i] = new SmokingTestThreadTask("Counter-" + NacosStringOps::valueOf(i), totalCounter); tp.put(tasks[i]); cout << "ok" << endl; } while (totalFinishedThreads.get() != 40) { sleep(1); } tp.stop(); cout << "If no coredump, should be successful" << endl; cout << "totalCounter.get() = " << totalCounter.get() << endl; SHOULD_BE_TRUE(totalCounter.get() == 40 * 100000, "40 threads, each thread accumulates totalCounter by 100k, result should be 4000k"); for (size_t i = 0; i < 40; i++) { cout << "destroying task " << i << "..." << endl; delete tasks[i]; tasks[i] = NULL; cout << "ok" << endl; } return true; } ================================================ FILE: test/testcase/testURL.cpp ================================================ #include #include #include "src/utils/url.h" #include "src/debug/DebugAssertion.h" #include "src/log/Logger.h" using namespace std; using namespace nacos; bool testURLEncodeAndDecode() { cout << "in function testURLEncode" << endl; NacosString originalContent = "Hello W! orld \\/,.%$@^%#*43543"; NacosString encoded = urlencode(originalContent); cout << "Encoded:" << encoded << endl; NacosString decoded = urldecode(encoded); SHOULD_BE_TRUE(originalContent == decoded, "After encoding and decoding, the content should remain the same."); return true; } ================================================ FILE: test/testcase/testUUID.cpp ================================================ #include #include "src/utils//UuidUtils.h" #include "src/thread/Thread.h" using namespace std; using namespace nacos; bool testUUID() { cout << "in function testUUID" << endl; cout << "Generating UUID..." << endl; for (int i = 0; i < 100; i++) { cout << "UUID:" << UuidUtils::generateUuid() << endl; } cout << "test end..." << endl; return true; } void *UUIDThreadFunc(void *param) { Thread *thisThread = *((Thread **) param); for (int i = 0; i < 100; i++) { log_debug("Thread %s UUID: %s\n", thisThread->getThreadName().c_str(), UuidUtils::generateUuid().c_str()); } return NULL; } bool testUUIDMT() { cout << "in function testUUIDMT" << endl; cout << "Generating UUID..." << endl; Thread *threads[10] = {NULL}; for (int i = 0; i < 10; i++) { NacosString threadName = "UUIDThread-" + NacosStringOps::valueOf(i); threads[i] = new Thread(threadName, UUIDThreadFunc, (void *) &threads[i]); threads[i]->start(); } for (int i = 0; i < 10; i++) { threads[i]->join(); delete threads[i]; } cout << "test end..." << endl; return true; }