├── .gitignore ├── .gitmodules ├── LICENSE ├── README.md ├── examples ├── config.helloWorld.lua ├── config.testEtcd.lua ├── helloWorld.lua └── testEtcd.lua ├── luaclib-src └── lua-cjson-2.1.0 │ ├── CMakeLists.txt │ ├── LICENSE │ ├── Makefile │ ├── NEWS │ ├── THANKS │ ├── dtoa.c │ ├── dtoa_config.h │ ├── fpconv.c │ ├── fpconv.h │ ├── g_fmt.c │ ├── lua-cjson-2.1devel-1.rockspec │ ├── lua-cjson.spec │ ├── lua │ ├── cjson │ │ └── util.lua │ ├── json2lua.lua │ └── lua2json.lua │ ├── lua_cjson.c │ ├── manual.txt │ ├── performance.txt │ ├── rfc4627.txt │ ├── runtests.sh │ ├── strbuf.c │ ├── strbuf.h │ └── tests │ ├── README │ ├── bench.lua │ ├── example1.json │ ├── example2.json │ ├── example3.json │ ├── example4.json │ ├── example5.json │ ├── genutf8.pl │ ├── numbers.json │ ├── octets-escaped.dat │ ├── rfc-example1.json │ ├── rfc-example2.json │ ├── test.lua │ └── types.json ├── luaclib └── cjson.so ├── lualib ├── common.lua └── etcd │ ├── core │ ├── encode_args.lua │ ├── serializers │ │ ├── json.lua │ │ └── raw.lua │ ├── typeof.lua │ ├── utils.lua │ ├── v2.lua │ └── v3.lua │ └── etcd.lua ├── service └── etcdd.lua └── tools └── etcd-cluster ├── etcd1.conf ├── etcd2.conf ├── etcd3.conf └── instart.sh /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Lua sources 2 | luac.out 3 | 4 | # luarocks build files 5 | *.src.rock 6 | *.zip 7 | *.tar.gz 8 | 9 | # Object files 10 | *.o 11 | *.os 12 | *.ko 13 | *.obj 14 | *.elf 15 | 16 | # Precompiled Headers 17 | *.gch 18 | *.pch 19 | 20 | # Libraries 21 | *.lib 22 | *.a 23 | *.la 24 | *.lo 25 | *.def 26 | *.exp 27 | 28 | # Shared objects (inc. Windows DLLs) 29 | *.dll 30 | *.so 31 | *.so.* 32 | *.dylib 33 | 34 | # Executables 35 | *.exe 36 | *.out 37 | *.app 38 | *.i*86 39 | *.x86_64 40 | *.hex 41 | 42 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "skynet"] 2 | path = skynet 3 | url = https://github.com/cloudwu/skynet.git 4 | [submodule "3rd/lua-cjson"] 5 | path = 3rd/lua-cjson 6 | url = https://github.com/mpx/lua-cjson.git 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 1. Checkout代码并根据[skynet指引](https://github.com/cloudwu/skynet/blob/master/README.md)编译skynet: 2 | 3 | ``` 4 | git checkout git@github.com:JieTrancender/skynet-etcd.git 5 | ``` 6 | 7 | 2. 运行测试: 8 | 9 | ``` 10 | ./skynet/skynet examples/config.helloWorld.lua 11 | ``` 12 | -------------------------------------------------------------------------------- /examples/config.helloWorld.lua: -------------------------------------------------------------------------------- 1 | root = "./" 2 | 3 | DEBUG = true 4 | project_name = "examples" 5 | thread = 8 6 | logger = nil 7 | harbor = 0 8 | start = "helloWorld" 9 | bootstrap = "snlua bootstrap" 10 | luaservice = root.."service/?.lua;"..root.."skynet/service/?.lua;"..root..project_name.."/?.lua" 11 | lualoader = root.."skynet/lualib/loader.lua" 12 | cpath = root.."skynet/cservice/?.so;"..root.."cservice/?.so" 13 | lua_cpath = root.."luaclib/?.so;"..root.."skynet/luaclib/?.so" 14 | lua_path = root.."lualib/?.lua;"..root..project_name.."/?.lua;"..root.."skynet/lualib/?.lua;"..root.."skynet/lualib/skynet/?.lua" 15 | snax = root.."service/?.lua;"..root.."skynet/service/?.lua" 16 | -------------------------------------------------------------------------------- /examples/config.testEtcd.lua: -------------------------------------------------------------------------------- 1 | root = "./" 2 | 3 | DEBUG = true 4 | project_name = "examples" 5 | thread = 8 6 | logger = nil 7 | harbor = 0 8 | start = "testEtcd" 9 | bootstrap = "snlua bootstrap" 10 | luaservice = root.."service/?.lua;"..root.."skynet/service/?.lua;"..root..project_name.."/?.lua" 11 | lualoader = root.."skynet/lualib/loader.lua" 12 | cpath = root.."skynet/cservice/?.so;"..root.."cservice/?.so" 13 | lua_cpath = root.."luaclib/?.so;"..root.."skynet/luaclib/?.so" 14 | lua_path = root.."lualib/?.lua;"..root..project_name.."/?.lua;"..root.."skynet/lualib/?.lua;"..root.."skynet/lualib/skynet/?.lua" 15 | snax = root.."service/?.lua;"..root.."skynet/service/?.lua" 16 | 17 | etcd_hosts = "http://127.0.0.1:2379" 18 | etcd_user = "root" 19 | etcd_pass = "123456" 20 | etcd_protocol = "v3" 21 | etcd_base_path = "/config/dev/" 22 | -------------------------------------------------------------------------------- /examples/helloWorld.lua: -------------------------------------------------------------------------------- 1 | local skynet = require "skynet" 2 | 3 | skynet.start(function() 4 | print("Hello World!") 5 | end) 6 | -------------------------------------------------------------------------------- /examples/testEtcd.lua: -------------------------------------------------------------------------------- 1 | require "common" 2 | 3 | local skynet = require "skynet" 4 | local snax = require "skynet.snax" 5 | local crypt = require "skynet.crypt" 6 | local encode_base64 = crypt.base64encode 7 | 8 | local etcd_hosts = skynet.getenv "etcd_hosts" 9 | local etcd_user = skynet.getenv "etcd_user" 10 | local etcd_pass = skynet.getenv "etcd_pass" 11 | local etcd_protocol = skynet.getenv "etcd_protocol" 12 | local etcd_base_path = skynet.getenv "etcd_base_path" 13 | 14 | local function create_basicauth(user, password) 15 | local userPwd = user .. ':' .. password 16 | local base64Str = encode_base64(userPwd) 17 | return 'Authorization', 'Basic ' .. base64Str 18 | end 19 | 20 | -- test set and get 21 | local function testsetget(etcdd) 22 | print("------------testsetget begin") 23 | res, err = etcdd.req.set(etcd_base_path.."hello", {message = "world"}) 24 | if not res then 25 | print(string.format("etcd set %s fail, err: %s", etcd_base_path.."hello", err)) 26 | return 27 | end 28 | print(string.format("set key %s, revision: %s", etcd_base_path.."hello", table_dump_line(res.body.header.revision))) 29 | 30 | res, err = etcdd.req.get(etcd_base_path.."hello") 31 | if not res then 32 | print(string.format("etcd get %s fail, err: %s", etcd_base_path.."hello", err)) 33 | return 34 | end 35 | print(string.format("key %s is %s", etcd_base_path.."hello", table_dump_line(res.body.kvs[1].value))) 36 | 37 | res, err = etcdd.req.delete(etcd_base_path.."hello") 38 | if not res then 39 | print(string.format("delete %s fail, err: %s", etcd_base_path.."hello", err)) 40 | return 41 | end 42 | print(string.format("delete key %s, deleted: %s", etcd_base_path.."hello", table_dump_line(res.body.deleted))) 43 | 44 | res, err = etcdd.req.get(etcd_base_path.."hello") 45 | print(string.format("key %s is %s", etcd_base_path.."hello", table_dump_line(res.body.kvs))) 46 | 47 | print("------------testsetget finished") 48 | end 49 | 50 | -- test setx 51 | local function testsetx(etcdd) 52 | print("------------testsetx begin") 53 | res, err = etcdd.req.set(etcd_base_path.."hello", {message = "world"}) 54 | print(string.format("set key %s, revision: %s", etcd_base_path.."hello", table_dump_line(res.body.header.revision))) 55 | 56 | res, err = etcdd.req.setx(etcd_base_path.."hello", {message = "newWorld"}) 57 | print(string.format("etcd setx %s, res: %s", etcd_base_path.."hello", table_dump_line(res.body.header.revision))) 58 | 59 | res, err = etcdd.req.get(etcd_base_path.."hello") 60 | print(string.format("key %s is %s, create_revision: %s, mod_revision: %s", etcd_base_path.."hello", 61 | table_dump_line(res.body.kvs[1].value), res.body.kvs[1].create_revision, res.body.kvs[1].mod_revision)) 62 | 63 | res, err = etcdd.req.setx(etcd_base_path.."hello2", {message = "newhello"}) 64 | print(string.format("etcd setx %s, res: %s", etcd_base_path.."hello2", table_dump_line(res.body.responses))) 65 | 66 | res, err = etcdd.req.get(etcd_base_path.."hello2") 67 | print(string.format("key %s is %s", etcd_base_path.."hello2", table_dump_line(res.body.kvs))) 68 | 69 | res, err = etcdd.req.delete(etcd_base_path.."hello") 70 | res, err = etcdd.req.delete(etcd_base_path.."hello2") 71 | print("------------testsetx finished") 72 | end 73 | 74 | -- test setnx 75 | local function testsetnx(etcdd) 76 | print("------------testsetnx begin") 77 | res, err = etcdd.req.set(etcd_base_path.."hello", {message = "world"}) 78 | res, err = etcdd.req.setnx(etcd_base_path.."hello", {message = "newWorld"}) 79 | res, err = etcdd.req.get(etcd_base_path.."hello") 80 | print(string.format("key %s is %s", etcd_base_path.."hello", table_dump_line(res.body.kvs[1].value))) 81 | 82 | res, err = etcdd.req.delete(etcd_base_path.."hello") 83 | print("------------testsetnx finished") 84 | end 85 | 86 | -- test grant 87 | local function testgrant(etcdd) 88 | print("------------testgrant begin") 89 | local res, err = etcdd.req.grant(2) 90 | if not res then 91 | print("testgrant fail: ", err) 92 | return 93 | end 94 | print(string.format("grant res: %s %s", res.body.ID, res.body.TTL)) 95 | skynet.sleep(300) 96 | res, err = etcdd.req.grant(10, res.body.ID) 97 | print(string.format("grant %s res: %s %s", res.body.ID, res.body.ID, res.body.TTL)) 98 | 99 | print("------------testgrant finished") 100 | end 101 | 102 | -- test revoke 103 | local function testrevoke(etcdd) 104 | print("------------testrevoke begin") 105 | local res, err = etcdd.req.grant(10) 106 | local ID = res.body.ID 107 | res, err = etcdd.req.revoke(ID) 108 | print(string.format("revoke %s revision: %s", ID, table_dump_line(res.body.header.revision))) 109 | print("------------testrevoke finished") 110 | end 111 | 112 | -- test keepalive and timetolive 113 | local function testkeepalive(etcdd) 114 | print("------------testkeepalive begin") 115 | local res, err = etcdd.req.grant(10) 116 | local ID = res.body.ID 117 | res, err = etcdd.req.keepalive(ID) 118 | if not res then 119 | print("testkeepalive fail, err:", err) 120 | return 121 | end 122 | print(string.format("keepalive %s, res: %s", ID, table_dump_line(res.body.result.TTL))) 123 | 124 | res, err = etcdd.req.timetolive(ID) 125 | print(string.format("timetolive %s, grantedTTL: %s, TTL: %s", ID, res.body.grantedTTL, res.body.TTL)) 126 | skynet.sleep(1300) 127 | res, err = etcdd.req.timetolive(ID) 128 | print(table_dump_line(res.body)) 129 | print(string.format("timetolive %s, grantedTTL: %s, TTL: %s", ID, res.body.grantedTTL, res.body.TTL)) 130 | print("------------testkeepalive finished") 131 | end 132 | 133 | -- test leases 134 | local function testleases(etcdd) 135 | local res, err = etcdd.req.grant(10) 136 | print("grant lease", res.body.ID) 137 | res, err = etcdd.req.leases() 138 | if not res then 139 | print("testleases fail, err:", err) 140 | return 141 | end 142 | print(string.format("leases res: %s", table_dump_line(res.body.leases))) 143 | end 144 | 145 | -- test etcdd 146 | local function testreaddir(etcdd) 147 | local res, err = etcdd.req.set(etcd_base_path.."hello", {message = "world"}) 148 | res, err = etcdd.req.set(etcd_base_path.."hello2", {message = "world2"}) 149 | res, err = etcdd.req.set(etcd_base_path.."hello3", {message = "world3"}) 150 | res, err = etcdd.req.exec("readdir", etcd_base_path) 151 | if not res then 152 | print("testreaddir fail, err: ", err) 153 | return 154 | end 155 | print(string.format("readdir res: %s", table_dump_line(res.body.kvs))) 156 | etcdd.req.delete(etcd_base_path.."hello") 157 | etcdd.req.delete(etcd_base_path.."hello2") 158 | etcdd.req.delete(etcd_base_path.."hello3") 159 | end 160 | 161 | -- test rmdir 162 | local function testrmdir(etcdd) 163 | local res, err = etcdd.req.set(etcd_base_path.."hello", {message = "world"}) 164 | res, err = etcdd.req.set(etcd_base_path.."hello2", {message = "world2"}) 165 | res, err = etcdd.req.set(etcd_base_path.."hello3", {message = "world3"}) 166 | res, err = etcdd.req.exec("readdir", etcd_base_path) 167 | print(string.format("rmdir#readdir res: %s", table_dump_line(res.body.kvs))) 168 | res, err = etcdd.req.exec("rmdir", etcd_base_path) 169 | if not res then 170 | print("testrmdir fail, err: ", err) 171 | return 172 | end 173 | 174 | res, err = etcdd.req.exec("readdir", etcd_base_path) 175 | print(string.format("rmdir#readdir res: %s", table_dump_line(res.body.kvs))) 176 | end 177 | 178 | skynet.start(function() 179 | print("token:", create_basicauth(etcd_user, etcd_pass)) 180 | local etcdd = snax.uniqueservice("etcdd", etcd_hosts, etcd_user, etcd_pass, etcd_protocol) 181 | local res, err = etcdd.req.version() 182 | if not res then 183 | print("etcd version fail, err: ", err) 184 | return 185 | end 186 | print("etcd version: ", table_dump_line(res.body)) 187 | 188 | testsetget(etcdd) 189 | 190 | testsetx(etcdd) 191 | 192 | testsetnx(etcdd) 193 | 194 | testgrant(etcdd) 195 | 196 | testrevoke(etcdd) 197 | 198 | testkeepalive(etcdd) 199 | 200 | testleases(etcdd) 201 | 202 | testreaddir(etcdd) 203 | 204 | testrmdir(etcdd) 205 | end) 206 | -------------------------------------------------------------------------------- /luaclib-src/lua-cjson-2.1.0/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # If Lua is installed in a non-standard location, please set the LUA_DIR 2 | # environment variable to point to prefix for the install. Eg: 3 | # Unix: export LUA_DIR=/home/user/pkg 4 | # Windows: set LUA_DIR=c:\lua51 5 | 6 | project(lua-cjson C) 7 | cmake_minimum_required(VERSION 2.6) 8 | 9 | option(USE_INTERNAL_FPCONV "Use internal strtod() / g_fmt() code for performance") 10 | option(MULTIPLE_THREADS "Support multi-threaded apps with internal fpconv - recommended" ON) 11 | 12 | if(NOT CMAKE_BUILD_TYPE) 13 | set(CMAKE_BUILD_TYPE Release CACHE STRING 14 | "Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel." 15 | FORCE) 16 | endif() 17 | 18 | find_package(Lua51 REQUIRED) 19 | include_directories(${LUA_INCLUDE_DIR}) 20 | 21 | if(NOT USE_INTERNAL_FPCONV) 22 | # Use libc number conversion routines (strtod(), sprintf()) 23 | set(FPCONV_SOURCES fpconv.c) 24 | else() 25 | # Use internal number conversion routines 26 | add_definitions(-DUSE_INTERNAL_FPCONV) 27 | set(FPCONV_SOURCES g_fmt.c dtoa.c) 28 | 29 | include(TestBigEndian) 30 | TEST_BIG_ENDIAN(IEEE_BIG_ENDIAN) 31 | if(IEEE_BIG_ENDIAN) 32 | add_definitions(-DIEEE_BIG_ENDIAN) 33 | endif() 34 | 35 | if(MULTIPLE_THREADS) 36 | set(CMAKE_THREAD_PREFER_PTHREAD TRUE) 37 | find_package(Threads REQUIRED) 38 | if(NOT CMAKE_USE_PTHREADS_INIT) 39 | message(FATAL_ERROR 40 | "Pthreads not found - required by MULTIPLE_THREADS option") 41 | endif() 42 | add_definitions(-DMULTIPLE_THREADS) 43 | endif() 44 | endif() 45 | 46 | # Handle platforms missing isinf() macro (Eg, some Solaris systems). 47 | include(CheckSymbolExists) 48 | CHECK_SYMBOL_EXISTS(isinf math.h HAVE_ISINF) 49 | if(NOT HAVE_ISINF) 50 | add_definitions(-DUSE_INTERNAL_ISINF) 51 | endif() 52 | 53 | set(_MODULE_LINK "${CMAKE_THREAD_LIBS_INIT}") 54 | get_filename_component(_lua_lib_dir ${LUA_LIBRARY} PATH) 55 | 56 | if(APPLE) 57 | set(CMAKE_SHARED_MODULE_CREATE_C_FLAGS 58 | "${CMAKE_SHARED_MODULE_CREATE_C_FLAGS} -undefined dynamic_lookup") 59 | endif() 60 | 61 | if(WIN32) 62 | # Win32 modules need to be linked to the Lua library. 63 | set(_MODULE_LINK ${LUA_LIBRARY} ${_MODULE_LINK}) 64 | set(_lua_module_dir "${_lua_lib_dir}") 65 | # Windows sprintf()/strtod() handle NaN/inf differently. Not supported. 66 | add_definitions(-DDISABLE_INVALID_NUMBERS) 67 | else() 68 | set(_lua_module_dir "${_lua_lib_dir}/lua/5.1") 69 | endif() 70 | 71 | add_library(cjson MODULE lua_cjson.c strbuf.c ${FPCONV_SOURCES}) 72 | set_target_properties(cjson PROPERTIES PREFIX "") 73 | target_link_libraries(cjson ${_MODULE_LINK}) 74 | install(TARGETS cjson DESTINATION "${_lua_module_dir}") 75 | 76 | # vi:ai et sw=4 ts=4: 77 | -------------------------------------------------------------------------------- /luaclib-src/lua-cjson-2.1.0/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010-2012 Mark Pulford 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 18 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 19 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 20 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /luaclib-src/lua-cjson-2.1.0/Makefile: -------------------------------------------------------------------------------- 1 | ##### Available defines for CJSON_CFLAGS ##### 2 | ## 3 | ## USE_INTERNAL_ISINF: Workaround for Solaris platforms missing isinf(). 4 | ## DISABLE_INVALID_NUMBERS: Permanently disable invalid JSON numbers: 5 | ## NaN, Infinity, hex. 6 | ## 7 | ## Optional built-in number conversion uses the following defines: 8 | ## USE_INTERNAL_FPCONV: Use builtin strtod/dtoa for numeric conversions. 9 | ## IEEE_BIG_ENDIAN: Required on big endian architectures. 10 | ## MULTIPLE_THREADS: Must be set when Lua CJSON may be used in a 11 | ## multi-threaded application. Requries _pthreads_. 12 | 13 | ##### Build defaults ##### 14 | LUA_VERSION = 5.1 15 | TARGET = cjson.so 16 | PREFIX = /usr/local 17 | #CFLAGS = -g -Wall -pedantic -fno-inline 18 | CFLAGS = -O3 -Wall -pedantic -DNDEBUG 19 | CJSON_CFLAGS = -fpic 20 | CJSON_LDFLAGS = -shared 21 | LUA_INCLUDE_DIR = $(PREFIX)/include 22 | LUA_CMODULE_DIR = $(PREFIX)/lib/lua/$(LUA_VERSION) 23 | LUA_MODULE_DIR = $(PREFIX)/share/lua/$(LUA_VERSION) 24 | LUA_BIN_DIR = $(PREFIX)/bin 25 | 26 | ##### Platform overrides ##### 27 | ## 28 | ## Tweak one of the platform sections below to suit your situation. 29 | ## 30 | ## See http://lua-users.org/wiki/BuildingModules for further platform 31 | ## specific details. 32 | 33 | ## Linux 34 | 35 | ## FreeBSD 36 | #LUA_INCLUDE_DIR = $(PREFIX)/include/lua51 37 | 38 | ## MacOSX (Macports) 39 | #PREFIX = /opt/local 40 | #CJSON_LDFLAGS = -bundle -undefined dynamic_lookup 41 | 42 | ## Solaris 43 | #CC = gcc 44 | #CJSON_CFLAGS = -fpic -DUSE_INTERNAL_ISINF 45 | 46 | ## Windows (MinGW) 47 | #TARGET = cjson.dll 48 | #PREFIX = /home/user/opt 49 | #CJSON_CFLAGS = -DDISABLE_INVALID_NUMBERS 50 | #CJSON_LDFLAGS = -shared -L$(PREFIX)/lib -llua51 51 | #LUA_BIN_SUFFIX = .lua 52 | 53 | ##### Number conversion configuration ##### 54 | 55 | ## Use Libc support for number conversion (default) 56 | FPCONV_OBJS = fpconv.o 57 | 58 | ## Use built in number conversion 59 | #FPCONV_OBJS = g_fmt.o dtoa.o 60 | #CJSON_CFLAGS += -DUSE_INTERNAL_FPCONV 61 | 62 | ## Compile built in number conversion for big endian architectures 63 | #CJSON_CFLAGS += -DIEEE_BIG_ENDIAN 64 | 65 | ## Compile built in number conversion to support multi-threaded 66 | ## applications (recommended) 67 | #CJSON_CFLAGS += -pthread -DMULTIPLE_THREADS 68 | #CJSON_LDFLAGS += -pthread 69 | 70 | ##### End customisable sections ##### 71 | 72 | TEST_FILES = README bench.lua genutf8.pl test.lua octets-escaped.dat \ 73 | example1.json example2.json example3.json example4.json \ 74 | example5.json numbers.json rfc-example1.json \ 75 | rfc-example2.json types.json 76 | DATAPERM = 644 77 | EXECPERM = 755 78 | 79 | ASCIIDOC = asciidoc 80 | 81 | BUILD_CFLAGS = -I$(LUA_INCLUDE_DIR) $(CJSON_CFLAGS) 82 | OBJS = lua_cjson.o strbuf.o $(FPCONV_OBJS) 83 | 84 | .PHONY: all clean install install-extra doc 85 | 86 | .SUFFIXES: .html .txt 87 | 88 | .c.o: 89 | $(CC) -c $(CFLAGS) $(CPPFLAGS) $(BUILD_CFLAGS) -o $@ $< 90 | 91 | .txt.html: 92 | $(ASCIIDOC) -n -a toc $< 93 | 94 | all: $(TARGET) 95 | 96 | doc: manual.html performance.html 97 | 98 | $(TARGET): $(OBJS) 99 | $(CC) $(LDFLAGS) $(CJSON_LDFLAGS) -o $@ $(OBJS) 100 | 101 | install: $(TARGET) 102 | mkdir -p $(DESTDIR)/$(LUA_CMODULE_DIR) 103 | cp $(TARGET) $(DESTDIR)/$(LUA_CMODULE_DIR) 104 | chmod $(EXECPERM) $(DESTDIR)/$(LUA_CMODULE_DIR)/$(TARGET) 105 | 106 | install-extra: 107 | mkdir -p $(DESTDIR)/$(LUA_MODULE_DIR)/cjson/tests \ 108 | $(DESTDIR)/$(LUA_BIN_DIR) 109 | cp lua/cjson/util.lua $(DESTDIR)/$(LUA_MODULE_DIR)/cjson 110 | chmod $(DATAPERM) $(DESTDIR)/$(LUA_MODULE_DIR)/cjson/util.lua 111 | cp lua/lua2json.lua $(DESTDIR)/$(LUA_BIN_DIR)/lua2json$(LUA_BIN_SUFFIX) 112 | chmod $(EXECPERM) $(DESTDIR)/$(LUA_BIN_DIR)/lua2json$(LUA_BIN_SUFFIX) 113 | cp lua/json2lua.lua $(DESTDIR)/$(LUA_BIN_DIR)/json2lua$(LUA_BIN_SUFFIX) 114 | chmod $(EXECPERM) $(DESTDIR)/$(LUA_BIN_DIR)/json2lua$(LUA_BIN_SUFFIX) 115 | cd tests; cp $(TEST_FILES) $(DESTDIR)/$(LUA_MODULE_DIR)/cjson/tests 116 | cd tests; chmod $(DATAPERM) $(TEST_FILES); chmod $(EXECPERM) *.lua *.pl 117 | 118 | clean: 119 | rm -f *.o $(TARGET) 120 | -------------------------------------------------------------------------------- /luaclib-src/lua-cjson-2.1.0/NEWS: -------------------------------------------------------------------------------- 1 | Version 2.1.0 (Mar 1 2012) 2 | * Added cjson.safe module interface which returns nil after an error 3 | * Improved Makefile compatibility with Solaris make 4 | 5 | Version 2.0.0 (Jan 22 2012) 6 | * Improved platform compatibility for strtod/sprintf locale workaround 7 | * Added option to build with David Gay's dtoa.c for improved performance 8 | * Added support for Lua 5.2 9 | * Added option to encode infinity/NaN as JSON null 10 | * Fixed encode bug with a raised default limit and deeply nested tables 11 | * Updated Makefile for compatibility with non-GNU make implementations 12 | * Added CMake build support 13 | * Added HTML manual 14 | * Increased default nesting limit to 1000 15 | * Added support for re-entrant use of encode and decode 16 | * Added support for installing lua2json and json2lua utilities 17 | * Added encode_invalid_numbers() and decode_invalid_numbers() 18 | * Added decode_max_depth() 19 | * Removed registration of global cjson module table 20 | * Removed refuse_invalid_numbers() 21 | 22 | Version 1.0.4 (Nov 30 2011) 23 | * Fixed numeric conversion under locales with a comma decimal separator 24 | 25 | Version 1.0.3 (Sep 15 2011) 26 | * Fixed detection of objects with numeric string keys 27 | * Provided work around for missing isinf() on Solaris 28 | 29 | Version 1.0.2 (May 30 2011) 30 | * Portability improvements for Windows 31 | - No longer links with -lm 32 | - Use "socket" instead of "posix" for sub-second timing 33 | * Removed UTF-8 test dependency on Perl Text::Iconv 34 | * Added simple CLI commands for testing Lua <-> JSON conversions 35 | * Added cjson.encode_number_precision() 36 | 37 | Version 1.0.1 (May 10 2011) 38 | * Added build support for OSX 39 | * Removed unnecessary whitespace from JSON output 40 | * Added cjson.encode_keep_buffer() 41 | * Fixed memory leak on Lua stack overflow exception 42 | 43 | Version 1.0 (May 9 2011) 44 | * Initial release 45 | -------------------------------------------------------------------------------- /luaclib-src/lua-cjson-2.1.0/THANKS: -------------------------------------------------------------------------------- 1 | The following people have helped with bug reports, testing and/or 2 | suggestions: 3 | 4 | - Louis-Philippe Perron (@loopole) 5 | - Ondřej Jirman 6 | - Steve Donovan 7 | - Zhang "agentzh" Yichun 8 | 9 | Thanks! 10 | -------------------------------------------------------------------------------- /luaclib-src/lua-cjson-2.1.0/dtoa_config.h: -------------------------------------------------------------------------------- 1 | #ifndef _DTOA_CONFIG_H 2 | #define _DTOA_CONFIG_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | /* Ensure dtoa.c does not USE_LOCALE. Lua CJSON must not use locale 9 | * aware conversion routines. */ 10 | #undef USE_LOCALE 11 | 12 | /* dtoa.c should not touch errno, Lua CJSON does not use it, and it 13 | * may not be threadsafe */ 14 | #define NO_ERRNO 15 | 16 | #define Long int32_t 17 | #define ULong uint32_t 18 | #define Llong int64_t 19 | #define ULLong uint64_t 20 | 21 | #ifdef IEEE_BIG_ENDIAN 22 | #define IEEE_MC68k 23 | #else 24 | #define IEEE_8087 25 | #endif 26 | 27 | #define MALLOC(n) xmalloc(n) 28 | 29 | static void *xmalloc(size_t size) 30 | { 31 | void *p; 32 | 33 | p = malloc(size); 34 | if (!p) { 35 | fprintf(stderr, "Out of memory"); 36 | abort(); 37 | } 38 | 39 | return p; 40 | } 41 | 42 | #ifdef MULTIPLE_THREADS 43 | 44 | /* Enable locking to support multi-threaded applications */ 45 | 46 | #include 47 | 48 | static pthread_mutex_t private_dtoa_lock[2] = { 49 | PTHREAD_MUTEX_INITIALIZER, 50 | PTHREAD_MUTEX_INITIALIZER 51 | }; 52 | 53 | #define ACQUIRE_DTOA_LOCK(n) do { \ 54 | int r = pthread_mutex_lock(&private_dtoa_lock[n]); \ 55 | if (r) { \ 56 | fprintf(stderr, "pthread_mutex_lock failed with %d\n", r); \ 57 | abort(); \ 58 | } \ 59 | } while (0) 60 | 61 | #define FREE_DTOA_LOCK(n) do { \ 62 | int r = pthread_mutex_unlock(&private_dtoa_lock[n]); \ 63 | if (r) { \ 64 | fprintf(stderr, "pthread_mutex_unlock failed with %d\n", r);\ 65 | abort(); \ 66 | } \ 67 | } while (0) 68 | 69 | #endif /* MULTIPLE_THREADS */ 70 | 71 | #endif /* _DTOA_CONFIG_H */ 72 | 73 | /* vi:ai et sw=4 ts=4: 74 | */ 75 | -------------------------------------------------------------------------------- /luaclib-src/lua-cjson-2.1.0/fpconv.c: -------------------------------------------------------------------------------- 1 | /* fpconv - Floating point conversion routines 2 | * 3 | * Copyright (c) 2011-2012 Mark Pulford 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining 6 | * a copy of this software and associated documentation files (the 7 | * "Software"), to deal in the Software without restriction, including 8 | * without limitation the rights to use, copy, modify, merge, publish, 9 | * distribute, sublicense, and/or sell copies of the Software, and to 10 | * permit persons to whom the Software is furnished to do so, subject to 11 | * the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be 14 | * included in all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | */ 24 | 25 | /* JSON uses a '.' decimal separator. strtod() / sprintf() under C libraries 26 | * with locale support will break when the decimal separator is a comma. 27 | * 28 | * fpconv_* will around these issues with a translation buffer if required. 29 | */ 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | #include "fpconv.h" 37 | 38 | /* Lua CJSON assumes the locale is the same for all threads within a 39 | * process and doesn't change after initialisation. 40 | * 41 | * This avoids the need for per thread storage or expensive checks 42 | * for call. */ 43 | static char locale_decimal_point = '.'; 44 | 45 | /* In theory multibyte decimal_points are possible, but 46 | * Lua CJSON only supports UTF-8 and known locales only have 47 | * single byte decimal points ([.,]). 48 | * 49 | * localconv() may not be thread safe (=>crash), and nl_langinfo() is 50 | * not supported on some platforms. Use sprintf() instead - if the 51 | * locale does change, at least Lua CJSON won't crash. */ 52 | static void fpconv_update_locale() 53 | { 54 | char buf[8]; 55 | 56 | snprintf(buf, sizeof(buf), "%g", 0.5); 57 | 58 | /* Failing this test might imply the platform has a buggy dtoa 59 | * implementation or wide characters */ 60 | if (buf[0] != '0' || buf[2] != '5' || buf[3] != 0) { 61 | fprintf(stderr, "Error: wide characters found or printf() bug."); 62 | abort(); 63 | } 64 | 65 | locale_decimal_point = buf[1]; 66 | } 67 | 68 | /* Check for a valid number character: [-+0-9a-yA-Y.] 69 | * Eg: -0.6e+5, infinity, 0xF0.F0pF0 70 | * 71 | * Used to find the probable end of a number. It doesn't matter if 72 | * invalid characters are counted - strtod() will find the valid 73 | * number if it exists. The risk is that slightly more memory might 74 | * be allocated before a parse error occurs. */ 75 | static inline int valid_number_character(char ch) 76 | { 77 | char lower_ch; 78 | 79 | if ('0' <= ch && ch <= '9') 80 | return 1; 81 | if (ch == '-' || ch == '+' || ch == '.') 82 | return 1; 83 | 84 | /* Hex digits, exponent (e), base (p), "infinity",.. */ 85 | lower_ch = ch | 0x20; 86 | if ('a' <= lower_ch && lower_ch <= 'y') 87 | return 1; 88 | 89 | return 0; 90 | } 91 | 92 | /* Calculate the size of the buffer required for a strtod locale 93 | * conversion. */ 94 | static int strtod_buffer_size(const char *s) 95 | { 96 | const char *p = s; 97 | 98 | while (valid_number_character(*p)) 99 | p++; 100 | 101 | return p - s; 102 | } 103 | 104 | /* Similar to strtod(), but must be passed the current locale's decimal point 105 | * character. Guaranteed to be called at the start of any valid number in a string */ 106 | double fpconv_strtod(const char *nptr, char **endptr) 107 | { 108 | char localbuf[FPCONV_G_FMT_BUFSIZE]; 109 | char *buf, *endbuf, *dp; 110 | int buflen; 111 | double value; 112 | 113 | /* System strtod() is fine when decimal point is '.' */ 114 | if (locale_decimal_point == '.') 115 | return strtod(nptr, endptr); 116 | 117 | buflen = strtod_buffer_size(nptr); 118 | if (!buflen) { 119 | /* No valid characters found, standard strtod() return */ 120 | *endptr = (char *)nptr; 121 | return 0; 122 | } 123 | 124 | /* Duplicate number into buffer */ 125 | if (buflen >= FPCONV_G_FMT_BUFSIZE) { 126 | /* Handle unusually large numbers */ 127 | buf = malloc(buflen + 1); 128 | if (!buf) { 129 | fprintf(stderr, "Out of memory"); 130 | abort(); 131 | } 132 | } else { 133 | /* This is the common case.. */ 134 | buf = localbuf; 135 | } 136 | memcpy(buf, nptr, buflen); 137 | buf[buflen] = 0; 138 | 139 | /* Update decimal point character if found */ 140 | dp = strchr(buf, '.'); 141 | if (dp) 142 | *dp = locale_decimal_point; 143 | 144 | value = strtod(buf, &endbuf); 145 | *endptr = (char *)&nptr[endbuf - buf]; 146 | if (buflen >= FPCONV_G_FMT_BUFSIZE) 147 | free(buf); 148 | 149 | return value; 150 | } 151 | 152 | /* "fmt" must point to a buffer of at least 6 characters */ 153 | static void set_number_format(char *fmt, int precision) 154 | { 155 | int d1, d2, i; 156 | 157 | assert(1 <= precision && precision <= 14); 158 | 159 | /* Create printf format (%.14g) from precision */ 160 | d1 = precision / 10; 161 | d2 = precision % 10; 162 | fmt[0] = '%'; 163 | fmt[1] = '.'; 164 | i = 2; 165 | if (d1) { 166 | fmt[i++] = '0' + d1; 167 | } 168 | fmt[i++] = '0' + d2; 169 | fmt[i++] = 'g'; 170 | fmt[i] = 0; 171 | } 172 | 173 | /* Assumes there is always at least 32 characters available in the target buffer */ 174 | int fpconv_g_fmt(char *str, double num, int precision) 175 | { 176 | char buf[FPCONV_G_FMT_BUFSIZE]; 177 | char fmt[6]; 178 | int len; 179 | char *b; 180 | 181 | set_number_format(fmt, precision); 182 | 183 | /* Pass through when decimal point character is dot. */ 184 | if (locale_decimal_point == '.') 185 | return snprintf(str, FPCONV_G_FMT_BUFSIZE, fmt, num); 186 | 187 | /* snprintf() to a buffer then translate for other decimal point characters */ 188 | len = snprintf(buf, FPCONV_G_FMT_BUFSIZE, fmt, num); 189 | 190 | /* Copy into target location. Translate decimal point if required */ 191 | b = buf; 192 | do { 193 | *str++ = (*b == locale_decimal_point ? '.' : *b); 194 | } while(*b++); 195 | 196 | return len; 197 | } 198 | 199 | void fpconv_init() 200 | { 201 | fpconv_update_locale(); 202 | } 203 | 204 | /* vi:ai et sw=4 ts=4: 205 | */ 206 | -------------------------------------------------------------------------------- /luaclib-src/lua-cjson-2.1.0/fpconv.h: -------------------------------------------------------------------------------- 1 | /* Lua CJSON floating point conversion routines */ 2 | 3 | /* Buffer required to store the largest string representation of a double. 4 | * 5 | * Longest double printed with %.14g is 21 characters long: 6 | * -1.7976931348623e+308 */ 7 | # define FPCONV_G_FMT_BUFSIZE 32 8 | 9 | #ifdef USE_INTERNAL_FPCONV 10 | static inline void fpconv_init() 11 | { 12 | /* Do nothing - not required */ 13 | } 14 | #else 15 | extern inline void fpconv_init(); 16 | #endif 17 | 18 | extern int fpconv_g_fmt(char*, double, int); 19 | extern double fpconv_strtod(const char*, char**); 20 | 21 | /* vi:ai et sw=4 ts=4: 22 | */ 23 | -------------------------------------------------------------------------------- /luaclib-src/lua-cjson-2.1.0/g_fmt.c: -------------------------------------------------------------------------------- 1 | /**************************************************************** 2 | * 3 | * The author of this software is David M. Gay. 4 | * 5 | * Copyright (c) 1991, 1996 by Lucent Technologies. 6 | * 7 | * Permission to use, copy, modify, and distribute this software for any 8 | * purpose without fee is hereby granted, provided that this entire notice 9 | * is included in all copies of any software which is or includes a copy 10 | * or modification of this software and in all copies of the supporting 11 | * documentation for such software. 12 | * 13 | * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED 14 | * WARRANTY. IN PARTICULAR, NEITHER THE AUTHOR NOR LUCENT MAKES ANY 15 | * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY 16 | * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. 17 | * 18 | ***************************************************************/ 19 | 20 | /* g_fmt(buf,x) stores the closest decimal approximation to x in buf; 21 | * it suffices to declare buf 22 | * char buf[32]; 23 | */ 24 | 25 | #ifdef __cplusplus 26 | extern "C" { 27 | #endif 28 | extern char *dtoa(double, int, int, int *, int *, char **); 29 | extern int g_fmt(char *, double, int); 30 | extern void freedtoa(char*); 31 | #ifdef __cplusplus 32 | } 33 | #endif 34 | 35 | int 36 | fpconv_g_fmt(char *b, double x, int precision) 37 | { 38 | register int i, k; 39 | register char *s; 40 | int decpt, j, sign; 41 | char *b0, *s0, *se; 42 | 43 | b0 = b; 44 | #ifdef IGNORE_ZERO_SIGN 45 | if (!x) { 46 | *b++ = '0'; 47 | *b = 0; 48 | goto done; 49 | } 50 | #endif 51 | s = s0 = dtoa(x, 2, precision, &decpt, &sign, &se); 52 | if (sign) 53 | *b++ = '-'; 54 | if (decpt == 9999) /* Infinity or Nan */ { 55 | while((*b++ = *s++)); 56 | /* "b" is used to calculate the return length. Decrement to exclude the 57 | * Null terminator from the length */ 58 | b--; 59 | goto done0; 60 | } 61 | if (decpt <= -4 || decpt > precision) { 62 | *b++ = *s++; 63 | if (*s) { 64 | *b++ = '.'; 65 | while((*b = *s++)) 66 | b++; 67 | } 68 | *b++ = 'e'; 69 | /* sprintf(b, "%+.2d", decpt - 1); */ 70 | if (--decpt < 0) { 71 | *b++ = '-'; 72 | decpt = -decpt; 73 | } 74 | else 75 | *b++ = '+'; 76 | for(j = 2, k = 10; 10*k <= decpt; j++, k *= 10); 77 | for(;;) { 78 | i = decpt / k; 79 | *b++ = i + '0'; 80 | if (--j <= 0) 81 | break; 82 | decpt -= i*k; 83 | decpt *= 10; 84 | } 85 | *b = 0; 86 | } 87 | else if (decpt <= 0) { 88 | *b++ = '0'; 89 | *b++ = '.'; 90 | for(; decpt < 0; decpt++) 91 | *b++ = '0'; 92 | while((*b++ = *s++)); 93 | b--; 94 | } 95 | else { 96 | while((*b = *s++)) { 97 | b++; 98 | if (--decpt == 0 && *s) 99 | *b++ = '.'; 100 | } 101 | for(; decpt > 0; decpt--) 102 | *b++ = '0'; 103 | *b = 0; 104 | } 105 | done0: 106 | freedtoa(s0); 107 | #ifdef IGNORE_ZERO_SIGN 108 | done: 109 | #endif 110 | return b - b0; 111 | } 112 | -------------------------------------------------------------------------------- /luaclib-src/lua-cjson-2.1.0/lua-cjson-2.1devel-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "lua-cjson" 2 | version = "2.1devel-1" 3 | 4 | source = { 5 | url = "http://www.kyne.com.au/~mark/software/download/lua-cjson-2.1devel.zip", 6 | } 7 | 8 | description = { 9 | summary = "A fast JSON encoding/parsing module", 10 | detailed = [[ 11 | The Lua CJSON module provides JSON support for Lua. It features: 12 | - Fast, standards compliant encoding/parsing routines 13 | - Full support for JSON with UTF-8, including decoding surrogate pairs 14 | - Optional run-time support for common exceptions to the JSON specification 15 | (infinity, NaN,..) 16 | - No dependencies on other libraries 17 | ]], 18 | homepage = "http://www.kyne.com.au/~mark/software/lua-cjson.php", 19 | license = "MIT" 20 | } 21 | 22 | dependencies = { 23 | "lua >= 5.1" 24 | } 25 | 26 | build = { 27 | type = "builtin", 28 | modules = { 29 | cjson = { 30 | sources = { "lua_cjson.c", "strbuf.c", "fpconv.c" }, 31 | defines = { 32 | -- LuaRocks does not support platform specific configuration for Solaris. 33 | -- Uncomment the line below on Solaris platforms if required. 34 | -- "USE_INTERNAL_ISINF" 35 | } 36 | } 37 | }, 38 | install = { 39 | lua = { 40 | ["cjson.util"] = "lua/cjson/util.lua" 41 | }, 42 | bin = { 43 | json2lua = "lua/json2lua.lua", 44 | lua2json = "lua/lua2json.lua" 45 | } 46 | }, 47 | -- Override default build options (per platform) 48 | platforms = { 49 | win32 = { modules = { cjson = { defines = { 50 | "DISABLE_INVALID_NUMBERS" 51 | } } } } 52 | }, 53 | copy_directories = { "tests" } 54 | } 55 | 56 | -- vi:ai et sw=4 ts=4: 57 | -------------------------------------------------------------------------------- /luaclib-src/lua-cjson-2.1.0/lua-cjson.spec: -------------------------------------------------------------------------------- 1 | %define luaver 5.1 2 | %define lualibdir %{_libdir}/lua/%{luaver} 3 | %define luadatadir %{_datadir}/lua/%{luaver} 4 | 5 | Name: lua-cjson 6 | Version: 2.1devel 7 | Release: 1%{?dist} 8 | Summary: A fast JSON encoding/parsing module for Lua 9 | 10 | Group: Development/Libraries 11 | License: MIT 12 | URL: http://www.kyne.com.au/~mark/software/lua-cjson/ 13 | Source0: http://www.kyne.com.au/~mark/software/lua-cjson/download/lua-cjson-%{version}.tar.gz 14 | BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX) 15 | 16 | BuildRequires: lua >= %{luaver}, lua-devel >= %{luaver} 17 | Requires: lua >= %{luaver} 18 | 19 | %description 20 | The Lua CJSON module provides JSON support for Lua. It features: 21 | - Fast, standards compliant encoding/parsing routines 22 | - Full support for JSON with UTF-8, including decoding surrogate pairs 23 | - Optional run-time support for common exceptions to the JSON specification 24 | (infinity, NaN,..) 25 | - No dependencies on other libraries 26 | 27 | 28 | %prep 29 | %setup -q 30 | 31 | 32 | %build 33 | make %{?_smp_mflags} CFLAGS="%{optflags}" LUA_INCLUDE_DIR="%{_includedir}" 34 | 35 | 36 | %install 37 | rm -rf "$RPM_BUILD_ROOT" 38 | make install DESTDIR="$RPM_BUILD_ROOT" LUA_CMODULE_DIR="%{lualibdir}" 39 | make install-extra DESTDIR="$RPM_BUILD_ROOT" LUA_MODULE_DIR="%{luadatadir}" \ 40 | LUA_BIN_DIR="%{_bindir}" 41 | 42 | 43 | %clean 44 | rm -rf "$RPM_BUILD_ROOT" 45 | 46 | 47 | %preun 48 | /bin/rm -f "%{luadatadir}/cjson/tests/utf8.dat" 49 | 50 | 51 | %files 52 | %defattr(-,root,root,-) 53 | %doc LICENSE NEWS performance.html performance.txt manual.html manual.txt rfc4627.txt THANKS 54 | %{lualibdir}/* 55 | %{luadatadir}/* 56 | %{_bindir}/* 57 | 58 | 59 | %changelog 60 | * Thu Mar 1 2012 Mark Pulford - 2.1.0-1 61 | - Update for 2.1.0 62 | 63 | * Sun Jan 22 2012 Mark Pulford - 2.0.0-1 64 | - Update for 2.0.0 65 | - Install lua2json / json2lua utilities 66 | 67 | * Wed Nov 27 2011 Mark Pulford - 1.0.4-1 68 | - Update for 1.0.4 69 | 70 | * Wed Sep 15 2011 Mark Pulford - 1.0.3-1 71 | - Update for 1.0.3 72 | 73 | * Sun May 29 2011 Mark Pulford - 1.0.2-1 74 | - Update for 1.0.2 75 | 76 | * Sun May 10 2011 Mark Pulford - 1.0.1-1 77 | - Update for 1.0.1 78 | 79 | * Sun May 1 2011 Mark Pulford - 1.0-1 80 | - Initial package 81 | -------------------------------------------------------------------------------- /luaclib-src/lua-cjson-2.1.0/lua/cjson/util.lua: -------------------------------------------------------------------------------- 1 | local json = require "cjson" 2 | 3 | -- Various common routines used by the Lua CJSON package 4 | -- 5 | -- Mark Pulford 6 | 7 | -- Determine with a Lua table can be treated as an array. 8 | -- Explicitly returns "not an array" for very sparse arrays. 9 | -- Returns: 10 | -- -1 Not an array 11 | -- 0 Empty table 12 | -- >0 Highest index in the array 13 | local function is_array(table) 14 | local max = 0 15 | local count = 0 16 | for k, v in pairs(table) do 17 | if type(k) == "number" then 18 | if k > max then max = k end 19 | count = count + 1 20 | else 21 | return -1 22 | end 23 | end 24 | if max > count * 2 then 25 | return -1 26 | end 27 | 28 | return max 29 | end 30 | 31 | local serialise_value 32 | 33 | local function serialise_table(value, indent, depth) 34 | local spacing, spacing2, indent2 35 | if indent then 36 | spacing = "\n" .. indent 37 | spacing2 = spacing .. " " 38 | indent2 = indent .. " " 39 | else 40 | spacing, spacing2, indent2 = " ", " ", false 41 | end 42 | depth = depth + 1 43 | if depth > 50 then 44 | return "Cannot serialise any further: too many nested tables" 45 | end 46 | 47 | local max = is_array(value) 48 | 49 | local comma = false 50 | local fragment = { "{" .. spacing2 } 51 | if max > 0 then 52 | -- Serialise array 53 | for i = 1, max do 54 | if comma then 55 | table.insert(fragment, "," .. spacing2) 56 | end 57 | table.insert(fragment, serialise_value(value[i], indent2, depth)) 58 | comma = true 59 | end 60 | elseif max < 0 then 61 | -- Serialise table 62 | for k, v in pairs(value) do 63 | if comma then 64 | table.insert(fragment, "," .. spacing2) 65 | end 66 | table.insert(fragment, 67 | ("[%s] = %s"):format(serialise_value(k, indent2, depth), 68 | serialise_value(v, indent2, depth))) 69 | comma = true 70 | end 71 | end 72 | table.insert(fragment, spacing .. "}") 73 | 74 | return table.concat(fragment) 75 | end 76 | 77 | function serialise_value(value, indent, depth) 78 | if indent == nil then indent = "" end 79 | if depth == nil then depth = 0 end 80 | 81 | if value == json.null then 82 | return "json.null" 83 | elseif type(value) == "string" then 84 | return ("%q"):format(value) 85 | elseif type(value) == "nil" or type(value) == "number" or 86 | type(value) == "boolean" then 87 | return tostring(value) 88 | elseif type(value) == "table" then 89 | return serialise_table(value, indent, depth) 90 | else 91 | return "\"<" .. type(value) .. ">\"" 92 | end 93 | end 94 | 95 | local function file_load(filename) 96 | local file 97 | if filename == nil then 98 | file = io.stdin 99 | else 100 | local err 101 | file, err = io.open(filename, "rb") 102 | if file == nil then 103 | error(("Unable to read '%s': %s"):format(filename, err)) 104 | end 105 | end 106 | local data = file:read("*a") 107 | 108 | if filename ~= nil then 109 | file:close() 110 | end 111 | 112 | if data == nil then 113 | error("Failed to read " .. filename) 114 | end 115 | 116 | return data 117 | end 118 | 119 | local function file_save(filename, data) 120 | local file 121 | if filename == nil then 122 | file = io.stdout 123 | else 124 | local err 125 | file, err = io.open(filename, "wb") 126 | if file == nil then 127 | error(("Unable to write '%s': %s"):format(filename, err)) 128 | end 129 | end 130 | file:write(data) 131 | if filename ~= nil then 132 | file:close() 133 | end 134 | end 135 | 136 | local function compare_values(val1, val2) 137 | local type1 = type(val1) 138 | local type2 = type(val2) 139 | if type1 ~= type2 then 140 | return false 141 | end 142 | 143 | -- Check for NaN 144 | if type1 == "number" and val1 ~= val1 and val2 ~= val2 then 145 | return true 146 | end 147 | 148 | if type1 ~= "table" then 149 | return val1 == val2 150 | end 151 | 152 | -- check_keys stores all the keys that must be checked in val2 153 | local check_keys = {} 154 | for k, _ in pairs(val1) do 155 | check_keys[k] = true 156 | end 157 | 158 | for k, v in pairs(val2) do 159 | if not check_keys[k] then 160 | return false 161 | end 162 | 163 | if not compare_values(val1[k], val2[k]) then 164 | return false 165 | end 166 | 167 | check_keys[k] = nil 168 | end 169 | for k, _ in pairs(check_keys) do 170 | -- Not the same if any keys from val1 were not found in val2 171 | return false 172 | end 173 | return true 174 | end 175 | 176 | local test_count_pass = 0 177 | local test_count_total = 0 178 | 179 | local function run_test_summary() 180 | return test_count_pass, test_count_total 181 | end 182 | 183 | local function run_test(testname, func, input, should_work, output) 184 | local function status_line(name, status, value) 185 | local statusmap = { [true] = ":success", [false] = ":error" } 186 | if status ~= nil then 187 | name = name .. statusmap[status] 188 | end 189 | print(("[%s] %s"):format(name, serialise_value(value, false))) 190 | end 191 | 192 | local result = { pcall(func, unpack(input)) } 193 | local success = table.remove(result, 1) 194 | 195 | local correct = false 196 | if success == should_work and compare_values(result, output) then 197 | correct = true 198 | test_count_pass = test_count_pass + 1 199 | end 200 | test_count_total = test_count_total + 1 201 | 202 | local teststatus = { [true] = "PASS", [false] = "FAIL" } 203 | print(("==> Test [%d] %s: %s"):format(test_count_total, testname, 204 | teststatus[correct])) 205 | 206 | status_line("Input", nil, input) 207 | if not correct then 208 | status_line("Expected", should_work, output) 209 | end 210 | status_line("Received", success, result) 211 | print() 212 | 213 | return correct, result 214 | end 215 | 216 | local function run_test_group(tests) 217 | local function run_helper(name, func, input) 218 | if type(name) == "string" and #name > 0 then 219 | print("==> " .. name) 220 | end 221 | -- Not a protected call, these functions should never generate errors. 222 | func(unpack(input or {})) 223 | print() 224 | end 225 | 226 | for _, v in ipairs(tests) do 227 | -- Run the helper if "should_work" is missing 228 | if v[4] == nil then 229 | run_helper(unpack(v)) 230 | else 231 | run_test(unpack(v)) 232 | end 233 | end 234 | end 235 | 236 | -- Run a Lua script in a separate environment 237 | local function run_script(script, env) 238 | local env = env or {} 239 | local func 240 | 241 | -- Use setfenv() if it exists, otherwise assume Lua 5.2 load() exists 242 | if _G.setfenv then 243 | func = loadstring(script) 244 | if func then 245 | setfenv(func, env) 246 | end 247 | else 248 | func = load(script, nil, nil, env) 249 | end 250 | 251 | if func == nil then 252 | error("Invalid syntax.") 253 | end 254 | func() 255 | 256 | return env 257 | end 258 | 259 | -- Export functions 260 | return { 261 | serialise_value = serialise_value, 262 | file_load = file_load, 263 | file_save = file_save, 264 | compare_values = compare_values, 265 | run_test_summary = run_test_summary, 266 | run_test = run_test, 267 | run_test_group = run_test_group, 268 | run_script = run_script 269 | } 270 | 271 | -- vi:ai et sw=4 ts=4: 272 | -------------------------------------------------------------------------------- /luaclib-src/lua-cjson-2.1.0/lua/json2lua.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | 3 | -- usage: json2lua.lua [json_file] 4 | -- 5 | -- Eg: 6 | -- echo '[ "testing" ]' | ./json2lua.lua 7 | -- ./json2lua.lua test.json 8 | 9 | local json = require "cjson" 10 | local util = require "cjson.util" 11 | 12 | local json_text = util.file_load(arg[1]) 13 | local t = json.decode(json_text) 14 | print(util.serialise_value(t)) 15 | -------------------------------------------------------------------------------- /luaclib-src/lua-cjson-2.1.0/lua/lua2json.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | 3 | -- usage: lua2json.lua [lua_file] 4 | -- 5 | -- Eg: 6 | -- echo '{ "testing" }' | ./lua2json.lua 7 | -- ./lua2json.lua test.lua 8 | 9 | local json = require "cjson" 10 | local util = require "cjson.util" 11 | 12 | local env = { 13 | json = { null = json.null }, 14 | null = json.null 15 | } 16 | 17 | local t = util.run_script("data = " .. util.file_load(arg[1]), env) 18 | print(json.encode(t.data)) 19 | 20 | -- vi:ai et sw=4 ts=4: 21 | -------------------------------------------------------------------------------- /luaclib-src/lua-cjson-2.1.0/manual.txt: -------------------------------------------------------------------------------- 1 | = Lua CJSON 2.1devel Manual = 2 | Mark Pulford 3 | :revdate: 1st March 2012 4 | 5 | Overview 6 | -------- 7 | 8 | The Lua CJSON module provides JSON support for Lua. 9 | 10 | *Features*:: 11 | - Fast, standards compliant encoding/parsing routines 12 | - Full support for JSON with UTF-8, including decoding surrogate pairs 13 | - Optional run-time support for common exceptions to the JSON 14 | specification (infinity, NaN,..) 15 | - No dependencies on other libraries 16 | 17 | *Caveats*:: 18 | - UTF-16 and UTF-32 are not supported 19 | 20 | Lua CJSON is covered by the MIT license. Review the file +LICENSE+ for 21 | details. 22 | 23 | The latest version of this software is available from the 24 | http://www.kyne.com.au/%7Emark/software/lua-cjson.php[Lua CJSON website]. 25 | 26 | Feel free to email me if you have any patches, suggestions, or comments. 27 | 28 | 29 | Installation 30 | ------------ 31 | 32 | Lua CJSON requires either http://www.lua.org[Lua] 5.1, Lua 5.2, or 33 | http://www.luajit.org[LuaJIT] to build. 34 | 35 | The build method can be selected from 4 options: 36 | 37 | Make:: Unix (including Linux, BSD, Mac OSX & Solaris), Windows 38 | CMake:: Unix, Windows 39 | RPM:: Linux 40 | LuaRocks:: Unix, Windows 41 | 42 | 43 | Make 44 | ~~~~ 45 | 46 | The included +Makefile+ has generic settings. 47 | 48 | First, review and update the included makefile to suit your platform (if 49 | required). 50 | 51 | Next, build and install the module: 52 | 53 | [source,sh] 54 | make install 55 | 56 | Or install manually into your Lua module directory: 57 | 58 | [source,sh] 59 | make 60 | cp cjson.so $LUA_MODULE_DIRECTORY 61 | 62 | 63 | CMake 64 | ~~~~~ 65 | 66 | http://www.cmake.org[CMake] can generate build configuration for many 67 | different platforms (including Unix and Windows). 68 | 69 | First, generate the makefile for your platform using CMake. If CMake is 70 | unable to find Lua, manually set the +LUA_DIR+ environment variable to 71 | the base prefix of your Lua 5.1 installation. 72 | 73 | While +cmake+ is used in the example below, +ccmake+ or +cmake-gui+ may 74 | be used to present an interface for changing the default build options. 75 | 76 | [source,sh] 77 | mkdir build 78 | cd build 79 | # Optional: export LUA_DIR=$LUA51_PREFIX 80 | cmake .. 81 | 82 | Next, build and install the module: 83 | 84 | [source,sh] 85 | make install 86 | # Or: 87 | make 88 | cp cjson.so $LUA_MODULE_DIRECTORY 89 | 90 | Review the 91 | http://www.cmake.org/cmake/help/documentation.html[CMake documentation] 92 | for further details. 93 | 94 | 95 | RPM 96 | ~~~ 97 | 98 | Linux distributions using http://rpm.org[RPM] can create a package via 99 | the included RPM spec file. Ensure the +rpm-build+ package (or similar) 100 | has been installed. 101 | 102 | Build and install the module via RPM: 103 | 104 | [source,sh] 105 | rpmbuild -tb lua-cjson-2.1devel.tar.gz 106 | rpm -Uvh $LUA_CJSON_RPM 107 | 108 | 109 | LuaRocks 110 | ~~~~~~~~ 111 | 112 | http://luarocks.org[LuaRocks] can be used to install and manage Lua 113 | modules on a wide range of platforms (including Windows). 114 | 115 | First, extract the Lua CJSON source package. 116 | 117 | Next, install the module: 118 | 119 | [source,sh] 120 | cd lua-cjson-2.1devel 121 | luarocks make 122 | 123 | [NOTE] 124 | LuaRocks does not support platform specific configuration for Solaris. 125 | On Solaris, you may need to manually uncomment +USE_INTERNAL_ISINF+ in 126 | the rockspec before building this module. 127 | 128 | Review the http://luarocks.org/en/Documentation[LuaRocks documentation] 129 | for further details. 130 | 131 | 132 | [[build_options]] 133 | Build Options (#define) 134 | ~~~~~~~~~~~~~~~~~~~~~~~ 135 | 136 | Lua CJSON offers several +#define+ build options to address portability 137 | issues, and enable non-default features. Some build methods may 138 | automatically set platform specific options if required. Other features 139 | should be enabled manually. 140 | 141 | USE_INTERNAL_ISINF:: Workaround for Solaris platforms missing +isinf+. 142 | DISABLE_INVALID_NUMBERS:: Recommended on platforms where +strtod+ / 143 | +sprintf+ are not POSIX compliant (eg, Windows MinGW). Prevents 144 | +cjson.encode_invalid_numbers+ and +cjson.decode_invalid_numbers+ from 145 | being enabled. However, +cjson.encode_invalid_numbers+ may still be 146 | set to +"null"+. When using the Lua CJSON built-in floating point 147 | conversion this option is unnecessary and is ignored. 148 | 149 | 150 | Built-in floating point conversion 151 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 152 | 153 | Lua CJSON may be built with David Gay's 154 | http://www.netlib.org/fp/[floating point conversion routines]. This can 155 | increase overall performance by up to 50% on some platforms when 156 | converting a large amount of numeric data. However, this option reduces 157 | portability and is disabled by default. 158 | 159 | USE_INTERNAL_FPCONV:: Enable internal number conversion routines. 160 | IEEE_BIG_ENDIAN:: Must be set on big endian architectures. 161 | MULTIPLE_THREADS:: Must be set if Lua CJSON may be used in a 162 | multi-threaded application. Requires the _pthreads_ library. 163 | 164 | 165 | API (Functions) 166 | --------------- 167 | 168 | Synopsis 169 | ~~~~~~~~ 170 | 171 | [source,lua] 172 | ------------ 173 | -- Module instantiation 174 | local cjson = require "cjson" 175 | local cjson2 = cjson.new() 176 | local cjson_safe = require "cjson.safe" 177 | 178 | -- Translate Lua value to/from JSON 179 | text = cjson.encode(value) 180 | value = cjson.decode(text) 181 | 182 | -- Get and/or set Lua CJSON configuration 183 | setting = cjson.decode_invalid_numbers([setting]) 184 | setting = cjson.encode_invalid_numbers([setting]) 185 | keep = cjson.encode_keep_buffer([keep]) 186 | depth = cjson.encode_max_depth([depth]) 187 | depth = cjson.decode_max_depth([depth]) 188 | convert, ratio, safe = cjson.encode_sparse_array([convert[, ratio[, safe]]]) 189 | ------------ 190 | 191 | 192 | Module Instantiation 193 | ~~~~~~~~~~~~~~~~~~~~ 194 | 195 | [source,lua] 196 | ------------ 197 | local cjson = require "cjson" 198 | local cjson2 = cjson.new() 199 | local cjson_safe = require "cjson.safe" 200 | ------------ 201 | 202 | Import Lua CJSON via the Lua +require+ function. Lua CJSON does not 203 | register a global module table. 204 | 205 | The +cjson+ module will throw an error during JSON conversion if any 206 | invalid data is encountered. Refer to <> 207 | and <> for details. 208 | 209 | The +cjson.safe+ module behaves identically to the +cjson+ module, 210 | except when errors are encountered during JSON conversion. On error, the 211 | +cjson_safe.encode+ and +cjson_safe.decode+ functions will return 212 | +nil+ followed by the error message. 213 | 214 | +cjson.new+ can be used to instantiate an independent copy of the Lua 215 | CJSON module. The new module has a separate persistent encoding buffer, 216 | and default settings. 217 | 218 | Lua CJSON can support Lua implementations using multiple preemptive 219 | threads within a single Lua state provided the persistent encoding 220 | buffer is not shared. This can be achieved by one of the following 221 | methods: 222 | 223 | - Disabling the persistent encoding buffer with 224 | <> 225 | - Ensuring each thread calls <> separately (ie, 226 | treat +cjson.encode+ as non-reentrant). 227 | - Using a separate +cjson+ module table per preemptive thread 228 | (+cjson.new+) 229 | 230 | [NOTE] 231 | Lua CJSON uses +strtod+ and +snprintf+ to perform numeric conversion as 232 | they are usually well supported, fast and bug free. However, these 233 | functions require a workaround for JSON encoding/parsing under locales 234 | using a comma decimal separator. Lua CJSON detects the current locale 235 | during instantiation to determine and automatically implement the 236 | workaround if required. Lua CJSON should be reinitialised via 237 | +cjson.new+ if the locale of the current process changes. Using a 238 | different locale per thread is not supported. 239 | 240 | 241 | decode 242 | ~~~~~~ 243 | 244 | [source,lua] 245 | ------------ 246 | value = cjson.decode(json_text) 247 | ------------ 248 | 249 | +cjson.decode+ will deserialise any UTF-8 JSON string into a Lua value 250 | or table. 251 | 252 | UTF-16 and UTF-32 JSON strings are not supported. 253 | 254 | +cjson.decode+ requires that any NULL (ASCII 0) and double quote (ASCII 255 | 34) characters are escaped within strings. All escape codes will be 256 | decoded and other bytes will be passed transparently. UTF-8 characters 257 | are not validated during decoding and should be checked elsewhere if 258 | required. 259 | 260 | JSON +null+ will be converted to a NULL +lightuserdata+ value. This can 261 | be compared with +cjson.null+ for convenience. 262 | 263 | By default, numbers incompatible with the JSON specification (infinity, 264 | NaN, hexadecimal) can be decoded. This default can be changed with 265 | <>. 266 | 267 | .Example: Decoding 268 | [source,lua] 269 | json_text = '[ true, { "foo": "bar" } ]' 270 | value = cjson.decode(json_text) 271 | -- Returns: { true, { foo = "bar" } } 272 | 273 | [CAUTION] 274 | Care must be taken after decoding JSON objects with numeric keys. Each 275 | numeric key will be stored as a Lua +string+. Any subsequent code 276 | assuming type +number+ may break. 277 | 278 | 279 | [[decode_invalid_numbers]] 280 | decode_invalid_numbers 281 | ~~~~~~~~~~~~~~~~~~~~~~ 282 | 283 | [source,lua] 284 | ------------ 285 | setting = cjson.decode_invalid_numbers([setting]) 286 | -- "setting" must be a boolean. Default: true. 287 | ------------ 288 | 289 | Lua CJSON may generate an error when trying to decode numbers not 290 | supported by the JSON specification. _Invalid numbers_ are defined as: 291 | 292 | - infinity 293 | - not-a-number (NaN) 294 | - hexadecimal 295 | 296 | Available settings: 297 | 298 | +true+:: Accept and decode _invalid numbers_. This is the default 299 | setting. 300 | +false+:: Throw an error when _invalid numbers_ are encountered. 301 | 302 | The current setting is always returned, and is only updated when an 303 | argument is provided. 304 | 305 | 306 | [[decode_max_depth]] 307 | decode_max_depth 308 | ~~~~~~~~~~~~~~~~ 309 | 310 | [source,lua] 311 | ------------ 312 | depth = cjson.decode_max_depth([depth]) 313 | -- "depth" must be a positive integer. Default: 1000. 314 | ------------ 315 | 316 | Lua CJSON will generate an error when parsing deeply nested JSON once 317 | the maximum array/object depth has been exceeded. This check prevents 318 | unnecessarily complicated JSON from slowing down the application, or 319 | crashing the application due to lack of process stack space. 320 | 321 | An error may be generated before the depth limit is hit if Lua is unable 322 | to allocate more objects on the Lua stack. 323 | 324 | By default, Lua CJSON will reject JSON with arrays and/or objects nested 325 | more than 1000 levels deep. 326 | 327 | The current setting is always returned, and is only updated when an 328 | argument is provided. 329 | 330 | 331 | [[encode]] 332 | encode 333 | ~~~~~~ 334 | 335 | [source,lua] 336 | ------------ 337 | json_text = cjson.encode(value) 338 | ------------ 339 | 340 | +cjson.encode+ will serialise a Lua value into a string containing the 341 | JSON representation. 342 | 343 | +cjson.encode+ supports the following types: 344 | 345 | - +boolean+ 346 | - +lightuserdata+ (NULL value only) 347 | - +nil+ 348 | - +number+ 349 | - +string+ 350 | - +table+ 351 | 352 | The remaining Lua types will generate an error: 353 | 354 | - +function+ 355 | - +lightuserdata+ (non-NULL values) 356 | - +thread+ 357 | - +userdata+ 358 | 359 | By default, numbers are encoded with 14 significant digits. Refer to 360 | <> for details. 361 | 362 | Lua CJSON will escape the following characters within each UTF-8 string: 363 | 364 | - Control characters (ASCII 0 - 31) 365 | - Double quote (ASCII 34) 366 | - Forward slash (ASCII 47) 367 | - Blackslash (ASCII 92) 368 | - Delete (ASCII 127) 369 | 370 | All other bytes are passed transparently. 371 | 372 | [CAUTION] 373 | ========= 374 | Lua CJSON will successfully encode/decode binary strings, but this is 375 | technically not supported by JSON and may not be compatible with other 376 | JSON libraries. To ensure the output is valid JSON, applications should 377 | ensure all Lua strings passed to +cjson.encode+ are UTF-8. 378 | 379 | Base64 is commonly used to encode binary data as the most efficient 380 | encoding under UTF-8 can only reduce the encoded size by a further 381 | ~8%. Lua Base64 routines can be found in the 382 | http://w3.impa.br/%7Ediego/software/luasocket/[LuaSocket] and 383 | http://www.tecgraf.puc-rio.br/%7Elhf/ftp/lua/#lbase64[lbase64] packages. 384 | ========= 385 | 386 | Lua CJSON uses a heuristic to determine whether to encode a Lua table as 387 | a JSON array or an object. A Lua table with only positive integer keys 388 | of type +number+ will be encoded as a JSON array. All other tables will 389 | be encoded as a JSON object. 390 | 391 | Lua CJSON does not use metamethods when serialising tables. 392 | 393 | - +rawget+ is used to iterate over Lua arrays 394 | - +next+ is used to iterate over Lua objects 395 | 396 | Lua arrays with missing entries (_sparse arrays_) may optionally be 397 | encoded in several different ways. Refer to 398 | <> for details. 399 | 400 | JSON object keys are always strings. Hence +cjson.encode+ only supports 401 | table keys which are type +number+ or +string+. All other types will 402 | generate an error. 403 | 404 | [NOTE] 405 | Standards compliant JSON must be encapsulated in either an object (+{}+) 406 | or an array (+[]+). If strictly standards compliant JSON is desired, a 407 | table must be passed to +cjson.encode+. 408 | 409 | By default, encoding the following Lua values will generate errors: 410 | 411 | - Numbers incompatible with the JSON specification (infinity, NaN) 412 | - Tables nested more than 1000 levels deep 413 | - Excessively sparse Lua arrays 414 | 415 | These defaults can be changed with: 416 | 417 | - <> 418 | - <> 419 | - <> 420 | 421 | .Example: Encoding 422 | [source,lua] 423 | value = { true, { foo = "bar" } } 424 | json_text = cjson.encode(value) 425 | -- Returns: '[true,{"foo":"bar"}]' 426 | 427 | 428 | [[encode_invalid_numbers]] 429 | encode_invalid_numbers 430 | ~~~~~~~~~~~~~~~~~~~~~~ 431 | [source,lua] 432 | ------------ 433 | setting = cjson.encode_invalid_numbers([setting]) 434 | -- "setting" must a boolean or "null". Default: false. 435 | ------------ 436 | 437 | Lua CJSON may generate an error when encoding floating point numbers not 438 | supported by the JSON specification (_invalid numbers_): 439 | 440 | - infinity 441 | - not-a-number (NaN) 442 | 443 | Available settings: 444 | 445 | +true+:: Allow _invalid numbers_ to be encoded. This will generate 446 | non-standard JSON, but this output is supported by some libraries. 447 | +"null"+:: Encode _invalid numbers_ as a JSON +null+ value. This allows 448 | infinity and NaN to be encoded into valid JSON. 449 | +false+:: Throw an error when attempting to encode _invalid numbers_. 450 | This is the default setting. 451 | 452 | The current setting is always returned, and is only updated when an 453 | argument is provided. 454 | 455 | 456 | [[encode_keep_buffer]] 457 | encode_keep_buffer 458 | ~~~~~~~~~~~~~~~~~~ 459 | 460 | [source,lua] 461 | ------------ 462 | keep = cjson.encode_keep_buffer([keep]) 463 | -- "keep" must be a boolean. Default: true. 464 | ------------ 465 | 466 | Lua CJSON can reuse the JSON encoding buffer to improve performance. 467 | 468 | Available settings: 469 | 470 | +true+:: The buffer will grow to the largest size required and is not 471 | freed until the Lua CJSON module is garbage collected. This is the 472 | default setting. 473 | +false+:: Free the encode buffer after each call to +cjson.encode+. 474 | 475 | The current setting is always returned, and is only updated when an 476 | argument is provided. 477 | 478 | 479 | [[encode_max_depth]] 480 | encode_max_depth 481 | ~~~~~~~~~~~~~~~~ 482 | 483 | [source,lua] 484 | ------------ 485 | depth = cjson.encode_max_depth([depth]) 486 | -- "depth" must be a positive integer. Default: 1000. 487 | ------------ 488 | 489 | Once the maximum table depth has been exceeded Lua CJSON will generate 490 | an error. This prevents a deeply nested or recursive data structure from 491 | crashing the application. 492 | 493 | By default, Lua CJSON will generate an error when trying to encode data 494 | structures with more than 1000 nested tables. 495 | 496 | The current setting is always returned, and is only updated when an 497 | argument is provided. 498 | 499 | .Example: Recursive Lua table 500 | [source,lua] 501 | a = {}; a[1] = a 502 | 503 | 504 | [[encode_number_precision]] 505 | encode_number_precision 506 | ~~~~~~~~~~~~~~~~~~~~~~~ 507 | 508 | [source,lua] 509 | ------------ 510 | precision = cjson.encode_number_precision([precision]) 511 | -- "precision" must be an integer between 1 and 14. Default: 14. 512 | ------------ 513 | 514 | The amount of significant digits returned by Lua CJSON when encoding 515 | numbers can be changed to balance accuracy versus performance. For data 516 | structures containing many numbers, setting 517 | +cjson.encode_number_precision+ to a smaller integer, for example +3+, 518 | can improve encoding performance by up to 50%. 519 | 520 | By default, Lua CJSON will output 14 significant digits when converting 521 | a number to text. 522 | 523 | The current setting is always returned, and is only updated when an 524 | argument is provided. 525 | 526 | 527 | [[encode_sparse_array]] 528 | encode_sparse_array 529 | ~~~~~~~~~~~~~~~~~~~ 530 | 531 | [source,lua] 532 | ------------ 533 | convert, ratio, safe = cjson.encode_sparse_array([convert[, ratio[, safe]]]) 534 | -- "convert" must be a boolean. Default: false. 535 | -- "ratio" must be a positive integer. Default: 2. 536 | -- "safe" must be a positive integer. Default: 10. 537 | ------------ 538 | 539 | Lua CJSON classifies a Lua table into one of three kinds when encoding a 540 | JSON array. This is determined by the number of values missing from the 541 | Lua array as follows: 542 | 543 | Normal:: All values are available. 544 | Sparse:: At least 1 value is missing. 545 | Excessively sparse:: The number of values missing exceeds the configured 546 | ratio. 547 | 548 | Lua CJSON encodes sparse Lua arrays as JSON arrays using JSON +null+ for 549 | the missing entries. 550 | 551 | An array is excessively sparse when all the following conditions are 552 | met: 553 | 554 | - +ratio+ > +0+ 555 | - _maximum_index_ > +safe+ 556 | - _maximum_index_ > _item_count_ * +ratio+ 557 | 558 | Lua CJSON will never consider an array to be _excessively sparse_ when 559 | +ratio+ = +0+. The +safe+ limit ensures that small Lua arrays are always 560 | encoded as sparse arrays. 561 | 562 | By default, attempting to encode an _excessively sparse_ array will 563 | generate an error. If +convert+ is set to +true+, _excessively sparse_ 564 | arrays will be converted to a JSON object. 565 | 566 | The current settings are always returned. A particular setting is only 567 | changed when the argument is provided (non-++nil++). 568 | 569 | .Example: Encoding a sparse array 570 | [source,lua] 571 | cjson.encode({ [3] = "data" }) 572 | -- Returns: '[null,null,"data"]' 573 | 574 | .Example: Enabling conversion to a JSON object 575 | [source,lua] 576 | cjson.encode_sparse_array(true) 577 | cjson.encode({ [1000] = "excessively sparse" }) 578 | -- Returns: '{"1000":"excessively sparse"}' 579 | 580 | 581 | API (Variables) 582 | --------------- 583 | 584 | _NAME 585 | ~~~~~ 586 | 587 | The name of the Lua CJSON module (+"cjson"+). 588 | 589 | 590 | _VERSION 591 | ~~~~~~~~ 592 | 593 | The version number of the Lua CJSON module (+"2.1devel"+). 594 | 595 | 596 | null 597 | ~~~~ 598 | 599 | Lua CJSON decodes JSON +null+ as a Lua +lightuserdata+ NULL pointer. 600 | +cjson.null+ is provided for comparison. 601 | 602 | 603 | [sect1] 604 | References 605 | ---------- 606 | 607 | - http://tools.ietf.org/html/rfc4627[RFC 4627] 608 | - http://www.json.org/[JSON website] 609 | 610 | 611 | // vi:ft=asciidoc tw=72: 612 | -------------------------------------------------------------------------------- /luaclib-src/lua-cjson-2.1.0/performance.txt: -------------------------------------------------------------------------------- 1 | JSON module performance comparison under Lua 2 | ============================================ 3 | Mark Pulford 4 | :revdate: January 22, 2012 5 | 6 | This performance comparison aims to provide a guide of relative 7 | performance between several fast and popular JSON modules. 8 | 9 | The examples used in this comparison were mostly sourced from the 10 | http://json.org[JSON website] and 11 | http://tools.ietf.org/html/rfc4627[RFC 4627]. 12 | 13 | Performance will vary widely between platforms and data sets. These 14 | results should only be used as an approximation. 15 | 16 | 17 | Modules 18 | ------- 19 | 20 | The following JSON modules for Lua were tested: 21 | 22 | http://chiselapp.com/user/dhkolf/repository/dkjson/[DKJSON 2.1]:: 23 | - Lua implementation with no dependencies on other libraries 24 | - Supports LPeg to improve decode performance 25 | 26 | https://github.com/brimworks/lua-yajl[Lua YAJL 2.0]:: 27 | - C wrapper for the YAJL library 28 | 29 | http://www.kyne.com.au/%7Emark/software/lua-cjson.php[Lua CSJON 2.0.0]:: 30 | - C implementation with no dependencies on other libraries 31 | 32 | 33 | Summary 34 | ------- 35 | 36 | All modules were built and tested as follows: 37 | 38 | DKJSON:: Tested with/without LPeg 10.2. 39 | Lua YAJL:: Tested with YAJL 2.0.4. 40 | Lua CJSON:: Tested with Libc and internal floating point conversion 41 | routines. 42 | 43 | The following Lua implementations were used for this comparison: 44 | 45 | - http://www.lua.org[Lua 5.1.4] (_Lua_) 46 | - http://www.luajit.org[LuaJIT 2.0.0-beta9] (_JIT_) 47 | 48 | These results show the number of JSON operations per second sustained by 49 | each module. All results have been normalised against the pure Lua 50 | DKJSON implementation. 51 | 52 | .Decoding performance 53 | ............................................................................ 54 | | DKJSON | Lua YAJL | Lua CJSON 55 | | No LPeg With LPeg | | Libc Internal 56 | | Lua JIT Lua JIT | Lua JIT | Lua JIT Lua JIT 57 | example1 | 1x 2x 2.6x 3.4x | 7.1x 10x | 14x 20x 14x 20x 58 | example2 | 1x 2.2x 2.9x 4.4x | 6.7x 9.9x | 14x 22x 14x 22x 59 | example3 | 1x 2.1x 3x 4.3x | 6.9x 9.3x | 14x 21x 15x 22x 60 | example4 | 1x 2x 2.5x 3.7x | 7.3x 10x | 12x 19x 12x 20x 61 | example5 | 1x 2.2x 3x 4.5x | 7.8x 11x | 16x 24x 16x 24x 62 | numbers | 1x 2.2x 2.3x 4x | 4.6x 5.5x | 8.9x 10x 13x 17x 63 | rfc-example1 | 1x 2.1x 2.8x 4.3x | 6.1x 8.1x | 13x 19x 14x 21x 64 | rfc-example2 | 1x 2.1x 3.1x 4.2x | 7.1x 9.2x | 15x 21x 17x 24x 65 | types | 1x 2.2x 2.6x 4.3x | 5.3x 7.4x | 12x 20x 13x 21x 66 | -------------|-------------------------|------------|----------------------- 67 | = Average => | 1x 2.1x 2.7x 4.1x | 6.5x 9x | 13x 20x 14x 21x 68 | ............................................................................ 69 | 70 | .Encoding performance 71 | ............................................................................. 72 | | DKJSON | Lua YAJL | Lua CJSON 73 | | No LPeg With LPeg | | Libc Internal 74 | | Lua JIT Lua JIT | Lua JIT | Lua JIT Lua JIT 75 | example1 | 1x 1.8x 0.97x 1.6x | 3.1x 5.2x | 23x 29x 23x 29x 76 | example2 | 1x 2x 0.97x 1.7x | 2.6x 4.3x | 22x 28x 22x 28x 77 | example3 | 1x 1.9x 0.98x 1.6x | 2.8x 4.3x | 13x 15x 16x 18x 78 | example4 | 1x 1.7x 0.96x 1.3x | 3.9x 6.1x | 15x 19x 17x 21x 79 | example5 | 1x 2x 0.98x 1.7x | 2.7x 4.5x | 20x 23x 20x 23x 80 | numbers | 1x 2.3x 1x 2.2x | 1.3x 1.9x | 3.8x 4.1x 4.2x 4.6x 81 | rfc-example1 | 1x 1.9x 0.97x 1.6x | 2.2x 3.2x | 8.5x 9.3x 11x 12x 82 | rfc-example2 | 1x 1.9x 0.98x 1.6x | 2.6x 3.9x | 10x 11x 17x 19x 83 | types | 1x 2.2x 0.97x 2x | 1.2x 1.9x | 11x 13x 12x 14x 84 | -------------|-------------------------|------------|----------------------- 85 | = Average => | 1x 1.9x 0.98x 1.7x | 2.5x 3.9x | 14x 17x 16x 19x 86 | ............................................................................. 87 | 88 | 89 | // vi:ft=asciidoc tw=72: 90 | -------------------------------------------------------------------------------- /luaclib-src/lua-cjson-2.1.0/rfc4627.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Network Working Group D. Crockford 8 | Request for Comments: 4627 JSON.org 9 | Category: Informational July 2006 10 | 11 | 12 | The application/json Media Type for JavaScript Object Notation (JSON) 13 | 14 | Status of This Memo 15 | 16 | This memo provides information for the Internet community. It does 17 | not specify an Internet standard of any kind. Distribution of this 18 | memo is unlimited. 19 | 20 | Copyright Notice 21 | 22 | Copyright (C) The Internet Society (2006). 23 | 24 | Abstract 25 | 26 | JavaScript Object Notation (JSON) is a lightweight, text-based, 27 | language-independent data interchange format. It was derived from 28 | the ECMAScript Programming Language Standard. JSON defines a small 29 | set of formatting rules for the portable representation of structured 30 | data. 31 | 32 | 1. Introduction 33 | 34 | JavaScript Object Notation (JSON) is a text format for the 35 | serialization of structured data. It is derived from the object 36 | literals of JavaScript, as defined in the ECMAScript Programming 37 | Language Standard, Third Edition [ECMA]. 38 | 39 | JSON can represent four primitive types (strings, numbers, booleans, 40 | and null) and two structured types (objects and arrays). 41 | 42 | A string is a sequence of zero or more Unicode characters [UNICODE]. 43 | 44 | An object is an unordered collection of zero or more name/value 45 | pairs, where a name is a string and a value is a string, number, 46 | boolean, null, object, or array. 47 | 48 | An array is an ordered sequence of zero or more values. 49 | 50 | The terms "object" and "array" come from the conventions of 51 | JavaScript. 52 | 53 | JSON's design goals were for it to be minimal, portable, textual, and 54 | a subset of JavaScript. 55 | 56 | 57 | 58 | Crockford Informational [Page 1] 59 | 60 | RFC 4627 JSON July 2006 61 | 62 | 63 | 1.1. Conventions Used in This Document 64 | 65 | The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", 66 | "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this 67 | document are to be interpreted as described in [RFC2119]. 68 | 69 | The grammatical rules in this document are to be interpreted as 70 | described in [RFC4234]. 71 | 72 | 2. JSON Grammar 73 | 74 | A JSON text is a sequence of tokens. The set of tokens includes six 75 | structural characters, strings, numbers, and three literal names. 76 | 77 | A JSON text is a serialized object or array. 78 | 79 | JSON-text = object / array 80 | 81 | These are the six structural characters: 82 | 83 | begin-array = ws %x5B ws ; [ left square bracket 84 | 85 | begin-object = ws %x7B ws ; { left curly bracket 86 | 87 | end-array = ws %x5D ws ; ] right square bracket 88 | 89 | end-object = ws %x7D ws ; } right curly bracket 90 | 91 | name-separator = ws %x3A ws ; : colon 92 | 93 | value-separator = ws %x2C ws ; , comma 94 | 95 | Insignificant whitespace is allowed before or after any of the six 96 | structural characters. 97 | 98 | ws = *( 99 | %x20 / ; Space 100 | %x09 / ; Horizontal tab 101 | %x0A / ; Line feed or New line 102 | %x0D ; Carriage return 103 | ) 104 | 105 | 2.1. Values 106 | 107 | A JSON value MUST be an object, array, number, or string, or one of 108 | the following three literal names: 109 | 110 | false null true 111 | 112 | 113 | 114 | Crockford Informational [Page 2] 115 | 116 | RFC 4627 JSON July 2006 117 | 118 | 119 | The literal names MUST be lowercase. No other literal names are 120 | allowed. 121 | 122 | value = false / null / true / object / array / number / string 123 | 124 | false = %x66.61.6c.73.65 ; false 125 | 126 | null = %x6e.75.6c.6c ; null 127 | 128 | true = %x74.72.75.65 ; true 129 | 130 | 2.2. Objects 131 | 132 | An object structure is represented as a pair of curly brackets 133 | surrounding zero or more name/value pairs (or members). A name is a 134 | string. A single colon comes after each name, separating the name 135 | from the value. A single comma separates a value from a following 136 | name. The names within an object SHOULD be unique. 137 | 138 | object = begin-object [ member *( value-separator member ) ] 139 | end-object 140 | 141 | member = string name-separator value 142 | 143 | 2.3. Arrays 144 | 145 | An array structure is represented as square brackets surrounding zero 146 | or more values (or elements). Elements are separated by commas. 147 | 148 | array = begin-array [ value *( value-separator value ) ] end-array 149 | 150 | 2.4. Numbers 151 | 152 | The representation of numbers is similar to that used in most 153 | programming languages. A number contains an integer component that 154 | may be prefixed with an optional minus sign, which may be followed by 155 | a fraction part and/or an exponent part. 156 | 157 | Octal and hex forms are not allowed. Leading zeros are not allowed. 158 | 159 | A fraction part is a decimal point followed by one or more digits. 160 | 161 | An exponent part begins with the letter E in upper or lowercase, 162 | which may be followed by a plus or minus sign. The E and optional 163 | sign are followed by one or more digits. 164 | 165 | Numeric values that cannot be represented as sequences of digits 166 | (such as Infinity and NaN) are not permitted. 167 | 168 | 169 | 170 | Crockford Informational [Page 3] 171 | 172 | RFC 4627 JSON July 2006 173 | 174 | 175 | number = [ minus ] int [ frac ] [ exp ] 176 | 177 | decimal-point = %x2E ; . 178 | 179 | digit1-9 = %x31-39 ; 1-9 180 | 181 | e = %x65 / %x45 ; e E 182 | 183 | exp = e [ minus / plus ] 1*DIGIT 184 | 185 | frac = decimal-point 1*DIGIT 186 | 187 | int = zero / ( digit1-9 *DIGIT ) 188 | 189 | minus = %x2D ; - 190 | 191 | plus = %x2B ; + 192 | 193 | zero = %x30 ; 0 194 | 195 | 2.5. Strings 196 | 197 | The representation of strings is similar to conventions used in the C 198 | family of programming languages. A string begins and ends with 199 | quotation marks. All Unicode characters may be placed within the 200 | quotation marks except for the characters that must be escaped: 201 | quotation mark, reverse solidus, and the control characters (U+0000 202 | through U+001F). 203 | 204 | Any character may be escaped. If the character is in the Basic 205 | Multilingual Plane (U+0000 through U+FFFF), then it may be 206 | represented as a six-character sequence: a reverse solidus, followed 207 | by the lowercase letter u, followed by four hexadecimal digits that 208 | encode the character's code point. The hexadecimal letters A though 209 | F can be upper or lowercase. So, for example, a string containing 210 | only a single reverse solidus character may be represented as 211 | "\u005C". 212 | 213 | Alternatively, there are two-character sequence escape 214 | representations of some popular characters. So, for example, a 215 | string containing only a single reverse solidus character may be 216 | represented more compactly as "\\". 217 | 218 | To escape an extended character that is not in the Basic Multilingual 219 | Plane, the character is represented as a twelve-character sequence, 220 | encoding the UTF-16 surrogate pair. So, for example, a string 221 | containing only the G clef character (U+1D11E) may be represented as 222 | "\uD834\uDD1E". 223 | 224 | 225 | 226 | Crockford Informational [Page 4] 227 | 228 | RFC 4627 JSON July 2006 229 | 230 | 231 | string = quotation-mark *char quotation-mark 232 | 233 | char = unescaped / 234 | escape ( 235 | %x22 / ; " quotation mark U+0022 236 | %x5C / ; \ reverse solidus U+005C 237 | %x2F / ; / solidus U+002F 238 | %x62 / ; b backspace U+0008 239 | %x66 / ; f form feed U+000C 240 | %x6E / ; n line feed U+000A 241 | %x72 / ; r carriage return U+000D 242 | %x74 / ; t tab U+0009 243 | %x75 4HEXDIG ) ; uXXXX U+XXXX 244 | 245 | escape = %x5C ; \ 246 | 247 | quotation-mark = %x22 ; " 248 | 249 | unescaped = %x20-21 / %x23-5B / %x5D-10FFFF 250 | 251 | 3. Encoding 252 | 253 | JSON text SHALL be encoded in Unicode. The default encoding is 254 | UTF-8. 255 | 256 | Since the first two characters of a JSON text will always be ASCII 257 | characters [RFC0020], it is possible to determine whether an octet 258 | stream is UTF-8, UTF-16 (BE or LE), or UTF-32 (BE or LE) by looking 259 | at the pattern of nulls in the first four octets. 260 | 261 | 00 00 00 xx UTF-32BE 262 | 00 xx 00 xx UTF-16BE 263 | xx 00 00 00 UTF-32LE 264 | xx 00 xx 00 UTF-16LE 265 | xx xx xx xx UTF-8 266 | 267 | 4. Parsers 268 | 269 | A JSON parser transforms a JSON text into another representation. A 270 | JSON parser MUST accept all texts that conform to the JSON grammar. 271 | A JSON parser MAY accept non-JSON forms or extensions. 272 | 273 | An implementation may set limits on the size of texts that it 274 | accepts. An implementation may set limits on the maximum depth of 275 | nesting. An implementation may set limits on the range of numbers. 276 | An implementation may set limits on the length and character contents 277 | of strings. 278 | 279 | 280 | 281 | 282 | Crockford Informational [Page 5] 283 | 284 | RFC 4627 JSON July 2006 285 | 286 | 287 | 5. Generators 288 | 289 | A JSON generator produces JSON text. The resulting text MUST 290 | strictly conform to the JSON grammar. 291 | 292 | 6. IANA Considerations 293 | 294 | The MIME media type for JSON text is application/json. 295 | 296 | Type name: application 297 | 298 | Subtype name: json 299 | 300 | Required parameters: n/a 301 | 302 | Optional parameters: n/a 303 | 304 | Encoding considerations: 8bit if UTF-8; binary if UTF-16 or UTF-32 305 | 306 | JSON may be represented using UTF-8, UTF-16, or UTF-32. When JSON 307 | is written in UTF-8, JSON is 8bit compatible. When JSON is 308 | written in UTF-16 or UTF-32, the binary content-transfer-encoding 309 | must be used. 310 | 311 | Security considerations: 312 | 313 | Generally there are security issues with scripting languages. JSON 314 | is a subset of JavaScript, but it is a safe subset that excludes 315 | assignment and invocation. 316 | 317 | A JSON text can be safely passed into JavaScript's eval() function 318 | (which compiles and executes a string) if all the characters not 319 | enclosed in strings are in the set of characters that form JSON 320 | tokens. This can be quickly determined in JavaScript with two 321 | regular expressions and calls to the test and replace methods. 322 | 323 | var my_JSON_object = !(/[^,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]/.test( 324 | text.replace(/"(\\.|[^"\\])*"/g, ''))) && 325 | eval('(' + text + ')'); 326 | 327 | Interoperability considerations: n/a 328 | 329 | Published specification: RFC 4627 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | Crockford Informational [Page 6] 339 | 340 | RFC 4627 JSON July 2006 341 | 342 | 343 | Applications that use this media type: 344 | 345 | JSON has been used to exchange data between applications written 346 | in all of these programming languages: ActionScript, C, C#, 347 | ColdFusion, Common Lisp, E, Erlang, Java, JavaScript, Lua, 348 | Objective CAML, Perl, PHP, Python, Rebol, Ruby, and Scheme. 349 | 350 | Additional information: 351 | 352 | Magic number(s): n/a 353 | File extension(s): .json 354 | Macintosh file type code(s): TEXT 355 | 356 | Person & email address to contact for further information: 357 | Douglas Crockford 358 | douglas@crockford.com 359 | 360 | Intended usage: COMMON 361 | 362 | Restrictions on usage: none 363 | 364 | Author: 365 | Douglas Crockford 366 | douglas@crockford.com 367 | 368 | Change controller: 369 | Douglas Crockford 370 | douglas@crockford.com 371 | 372 | 7. Security Considerations 373 | 374 | See Security Considerations in Section 6. 375 | 376 | 8. Examples 377 | 378 | This is a JSON object: 379 | 380 | { 381 | "Image": { 382 | "Width": 800, 383 | "Height": 600, 384 | "Title": "View from 15th Floor", 385 | "Thumbnail": { 386 | "Url": "http://www.example.com/image/481989943", 387 | "Height": 125, 388 | "Width": "100" 389 | }, 390 | "IDs": [116, 943, 234, 38793] 391 | 392 | 393 | 394 | Crockford Informational [Page 7] 395 | 396 | RFC 4627 JSON July 2006 397 | 398 | 399 | } 400 | } 401 | 402 | Its Image member is an object whose Thumbnail member is an object 403 | and whose IDs member is an array of numbers. 404 | 405 | This is a JSON array containing two objects: 406 | 407 | [ 408 | { 409 | "precision": "zip", 410 | "Latitude": 37.7668, 411 | "Longitude": -122.3959, 412 | "Address": "", 413 | "City": "SAN FRANCISCO", 414 | "State": "CA", 415 | "Zip": "94107", 416 | "Country": "US" 417 | }, 418 | { 419 | "precision": "zip", 420 | "Latitude": 37.371991, 421 | "Longitude": -122.026020, 422 | "Address": "", 423 | "City": "SUNNYVALE", 424 | "State": "CA", 425 | "Zip": "94085", 426 | "Country": "US" 427 | } 428 | ] 429 | 430 | 9. References 431 | 432 | 9.1. Normative References 433 | 434 | [ECMA] European Computer Manufacturers Association, "ECMAScript 435 | Language Specification 3rd Edition", December 1999, 436 | . 438 | 439 | [RFC0020] Cerf, V., "ASCII format for network interchange", RFC 20, 440 | October 1969. 441 | 442 | [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate 443 | Requirement Levels", BCP 14, RFC 2119, March 1997. 444 | 445 | [RFC4234] Crocker, D. and P. Overell, "Augmented BNF for Syntax 446 | Specifications: ABNF", RFC 4234, October 2005. 447 | 448 | 449 | 450 | Crockford Informational [Page 8] 451 | 452 | RFC 4627 JSON July 2006 453 | 454 | 455 | [UNICODE] The Unicode Consortium, "The Unicode Standard Version 4.0", 456 | 2003, . 457 | 458 | Author's Address 459 | 460 | Douglas Crockford 461 | JSON.org 462 | EMail: douglas@crockford.com 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | Crockford Informational [Page 9] 507 | 508 | RFC 4627 JSON July 2006 509 | 510 | 511 | Full Copyright Statement 512 | 513 | Copyright (C) The Internet Society (2006). 514 | 515 | This document is subject to the rights, licenses and restrictions 516 | contained in BCP 78, and except as set forth therein, the authors 517 | retain all their rights. 518 | 519 | This document and the information contained herein are provided on an 520 | "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS 521 | OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET 522 | ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, 523 | INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE 524 | INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED 525 | WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. 526 | 527 | Intellectual Property 528 | 529 | The IETF takes no position regarding the validity or scope of any 530 | Intellectual Property Rights or other rights that might be claimed to 531 | pertain to the implementation or use of the technology described in 532 | this document or the extent to which any license under such rights 533 | might or might not be available; nor does it represent that it has 534 | made any independent effort to identify any such rights. Information 535 | on the procedures with respect to rights in RFC documents can be 536 | found in BCP 78 and BCP 79. 537 | 538 | Copies of IPR disclosures made to the IETF Secretariat and any 539 | assurances of licenses to be made available, or the result of an 540 | attempt made to obtain a general license or permission for the use of 541 | such proprietary rights by implementers or users of this 542 | specification can be obtained from the IETF on-line IPR repository at 543 | http://www.ietf.org/ipr. 544 | 545 | The IETF invites any interested party to bring to its attention any 546 | copyrights, patents or patent applications, or other proprietary 547 | rights that may cover technology that may be required to implement 548 | this standard. Please address the information to the IETF at 549 | ietf-ipr@ietf.org. 550 | 551 | Acknowledgement 552 | 553 | Funding for the RFC Editor function is provided by the IETF 554 | Administrative Support Activity (IASA). 555 | 556 | 557 | 558 | 559 | 560 | 561 | 562 | Crockford Informational [Page 10] 563 | 564 | -------------------------------------------------------------------------------- /luaclib-src/lua-cjson-2.1.0/runtests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | PLATFORM="`uname -s`" 4 | [ "$1" ] && VERSION="$1" || VERSION="2.1devel" 5 | 6 | set -e 7 | 8 | # Portable "ggrep -A" replacement. 9 | # Work around Solaris awk record limit of 2559 bytes. 10 | # contextgrep PATTERN POST_MATCH_LINES 11 | contextgrep() { 12 | cut -c -2500 | awk "/$1/ { count = ($2 + 1) } count > 0 { count--; print }" 13 | } 14 | 15 | do_tests() { 16 | echo 17 | cd tests 18 | lua -e 'print("Testing Lua CJSON version " .. require("cjson")._VERSION)' 19 | ./test.lua | contextgrep 'FAIL|Summary' 3 | grep -v PASS | cut -c -150 20 | cd .. 21 | } 22 | 23 | echo "===== Setting LuaRocks PATH =====" 24 | eval "`luarocks path`" 25 | 26 | echo "===== Building UTF-8 test data =====" 27 | ( cd tests && ./genutf8.pl; ) 28 | 29 | echo "===== Cleaning old build data =====" 30 | make clean 31 | rm -f tests/cjson.so 32 | 33 | echo "===== Verifying cjson.so is not installed =====" 34 | 35 | cd tests 36 | if lua -e 'require "cjson"' 2>/dev/null 37 | then 38 | cat < "$LOG" 82 | RPM="`awk '/^Wrote: / && ! /debuginfo/ { print $2}' < "$LOG"`" 83 | sudo -- rpm -Uvh \"$RPM\" 84 | do_tests 85 | sudo -- rpm -e lua-cjson 86 | rm -f "$LOG" 87 | else 88 | echo "==> skipping, $TGZ not found" 89 | fi 90 | fi 91 | 92 | # vi:ai et sw=4 ts=4: 93 | -------------------------------------------------------------------------------- /luaclib-src/lua-cjson-2.1.0/strbuf.c: -------------------------------------------------------------------------------- 1 | /* strbuf - String buffer routines 2 | * 3 | * Copyright (c) 2010-2012 Mark Pulford 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining 6 | * a copy of this software and associated documentation files (the 7 | * "Software"), to deal in the Software without restriction, including 8 | * without limitation the rights to use, copy, modify, merge, publish, 9 | * distribute, sublicense, and/or sell copies of the Software, and to 10 | * permit persons to whom the Software is furnished to do so, subject to 11 | * the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be 14 | * included in all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | */ 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include "strbuf.h" 31 | 32 | static void die(const char *fmt, ...) 33 | { 34 | va_list arg; 35 | 36 | va_start(arg, fmt); 37 | vfprintf(stderr, fmt, arg); 38 | va_end(arg); 39 | fprintf(stderr, "\n"); 40 | 41 | exit(-1); 42 | } 43 | 44 | void strbuf_init(strbuf_t *s, int len) 45 | { 46 | int size; 47 | 48 | if (len <= 0) 49 | size = STRBUF_DEFAULT_SIZE; 50 | else 51 | size = len + 1; /* \0 terminator */ 52 | 53 | s->buf = NULL; 54 | s->size = size; 55 | s->length = 0; 56 | s->increment = STRBUF_DEFAULT_INCREMENT; 57 | s->dynamic = 0; 58 | s->reallocs = 0; 59 | s->debug = 0; 60 | 61 | s->buf = malloc(size); 62 | if (!s->buf) 63 | die("Out of memory"); 64 | 65 | strbuf_ensure_null(s); 66 | } 67 | 68 | strbuf_t *strbuf_new(int len) 69 | { 70 | strbuf_t *s; 71 | 72 | s = malloc(sizeof(strbuf_t)); 73 | if (!s) 74 | die("Out of memory"); 75 | 76 | strbuf_init(s, len); 77 | 78 | /* Dynamic strbuf allocation / deallocation */ 79 | s->dynamic = 1; 80 | 81 | return s; 82 | } 83 | 84 | void strbuf_set_increment(strbuf_t *s, int increment) 85 | { 86 | /* Increment > 0: Linear buffer growth rate 87 | * Increment < -1: Exponential buffer growth rate */ 88 | if (increment == 0 || increment == -1) 89 | die("BUG: Invalid string increment"); 90 | 91 | s->increment = increment; 92 | } 93 | 94 | static inline void debug_stats(strbuf_t *s) 95 | { 96 | if (s->debug) { 97 | fprintf(stderr, "strbuf(%lx) reallocs: %d, length: %d, size: %d\n", 98 | (long)s, s->reallocs, s->length, s->size); 99 | } 100 | } 101 | 102 | /* If strbuf_t has not been dynamically allocated, strbuf_free() can 103 | * be called any number of times strbuf_init() */ 104 | void strbuf_free(strbuf_t *s) 105 | { 106 | debug_stats(s); 107 | 108 | if (s->buf) { 109 | free(s->buf); 110 | s->buf = NULL; 111 | } 112 | if (s->dynamic) 113 | free(s); 114 | } 115 | 116 | char *strbuf_free_to_string(strbuf_t *s, int *len) 117 | { 118 | char *buf; 119 | 120 | debug_stats(s); 121 | 122 | strbuf_ensure_null(s); 123 | 124 | buf = s->buf; 125 | if (len) 126 | *len = s->length; 127 | 128 | if (s->dynamic) 129 | free(s); 130 | 131 | return buf; 132 | } 133 | 134 | static int calculate_new_size(strbuf_t *s, int len) 135 | { 136 | int reqsize, newsize; 137 | 138 | if (len <= 0) 139 | die("BUG: Invalid strbuf length requested"); 140 | 141 | /* Ensure there is room for optional NULL termination */ 142 | reqsize = len + 1; 143 | 144 | /* If the user has requested to shrink the buffer, do it exactly */ 145 | if (s->size > reqsize) 146 | return reqsize; 147 | 148 | newsize = s->size; 149 | if (s->increment < 0) { 150 | /* Exponential sizing */ 151 | while (newsize < reqsize) 152 | newsize *= -s->increment; 153 | } else { 154 | /* Linear sizing */ 155 | newsize = ((newsize + s->increment - 1) / s->increment) * s->increment; 156 | } 157 | 158 | return newsize; 159 | } 160 | 161 | 162 | /* Ensure strbuf can handle a string length bytes long (ignoring NULL 163 | * optional termination). */ 164 | void strbuf_resize(strbuf_t *s, int len) 165 | { 166 | int newsize; 167 | 168 | newsize = calculate_new_size(s, len); 169 | 170 | if (s->debug > 1) { 171 | fprintf(stderr, "strbuf(%lx) resize: %d => %d\n", 172 | (long)s, s->size, newsize); 173 | } 174 | 175 | s->size = newsize; 176 | s->buf = realloc(s->buf, s->size); 177 | if (!s->buf) 178 | die("Out of memory"); 179 | s->reallocs++; 180 | } 181 | 182 | void strbuf_append_string(strbuf_t *s, const char *str) 183 | { 184 | int space, i; 185 | 186 | space = strbuf_empty_length(s); 187 | 188 | for (i = 0; str[i]; i++) { 189 | if (space < 1) { 190 | strbuf_resize(s, s->length + 1); 191 | space = strbuf_empty_length(s); 192 | } 193 | 194 | s->buf[s->length] = str[i]; 195 | s->length++; 196 | space--; 197 | } 198 | } 199 | 200 | /* strbuf_append_fmt() should only be used when an upper bound 201 | * is known for the output string. */ 202 | void strbuf_append_fmt(strbuf_t *s, int len, const char *fmt, ...) 203 | { 204 | va_list arg; 205 | int fmt_len; 206 | 207 | strbuf_ensure_empty_length(s, len); 208 | 209 | va_start(arg, fmt); 210 | fmt_len = vsnprintf(s->buf + s->length, len, fmt, arg); 211 | va_end(arg); 212 | 213 | if (fmt_len < 0) 214 | die("BUG: Unable to convert number"); /* This should never happen.. */ 215 | 216 | s->length += fmt_len; 217 | } 218 | 219 | /* strbuf_append_fmt_retry() can be used when the there is no known 220 | * upper bound for the output string. */ 221 | void strbuf_append_fmt_retry(strbuf_t *s, const char *fmt, ...) 222 | { 223 | va_list arg; 224 | int fmt_len, try; 225 | int empty_len; 226 | 227 | /* If the first attempt to append fails, resize the buffer appropriately 228 | * and try again */ 229 | for (try = 0; ; try++) { 230 | va_start(arg, fmt); 231 | /* Append the new formatted string */ 232 | /* fmt_len is the length of the string required, excluding the 233 | * trailing NULL */ 234 | empty_len = strbuf_empty_length(s); 235 | /* Add 1 since there is also space to store the terminating NULL. */ 236 | fmt_len = vsnprintf(s->buf + s->length, empty_len + 1, fmt, arg); 237 | va_end(arg); 238 | 239 | if (fmt_len <= empty_len) 240 | break; /* SUCCESS */ 241 | if (try > 0) 242 | die("BUG: length of formatted string changed"); 243 | 244 | strbuf_resize(s, s->length + fmt_len); 245 | } 246 | 247 | s->length += fmt_len; 248 | } 249 | 250 | /* vi:ai et sw=4 ts=4: 251 | */ 252 | -------------------------------------------------------------------------------- /luaclib-src/lua-cjson-2.1.0/strbuf.h: -------------------------------------------------------------------------------- 1 | /* strbuf - String buffer routines 2 | * 3 | * Copyright (c) 2010-2012 Mark Pulford 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining 6 | * a copy of this software and associated documentation files (the 7 | * "Software"), to deal in the Software without restriction, including 8 | * without limitation the rights to use, copy, modify, merge, publish, 9 | * distribute, sublicense, and/or sell copies of the Software, and to 10 | * permit persons to whom the Software is furnished to do so, subject to 11 | * the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be 14 | * included in all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | */ 24 | 25 | #include 26 | #include 27 | 28 | /* Size: Total bytes allocated to *buf 29 | * Length: String length, excluding optional NULL terminator. 30 | * Increment: Allocation increments when resizing the string buffer. 31 | * Dynamic: True if created via strbuf_new() 32 | */ 33 | 34 | typedef struct { 35 | char *buf; 36 | int size; 37 | int length; 38 | int increment; 39 | int dynamic; 40 | int reallocs; 41 | int debug; 42 | } strbuf_t; 43 | 44 | #ifndef STRBUF_DEFAULT_SIZE 45 | #define STRBUF_DEFAULT_SIZE 1023 46 | #endif 47 | #ifndef STRBUF_DEFAULT_INCREMENT 48 | #define STRBUF_DEFAULT_INCREMENT -2 49 | #endif 50 | 51 | /* Initialise */ 52 | extern strbuf_t *strbuf_new(int len); 53 | extern void strbuf_init(strbuf_t *s, int len); 54 | extern void strbuf_set_increment(strbuf_t *s, int increment); 55 | 56 | /* Release */ 57 | extern void strbuf_free(strbuf_t *s); 58 | extern char *strbuf_free_to_string(strbuf_t *s, int *len); 59 | 60 | /* Management */ 61 | extern void strbuf_resize(strbuf_t *s, int len); 62 | static int strbuf_empty_length(strbuf_t *s); 63 | static int strbuf_length(strbuf_t *s); 64 | static char *strbuf_string(strbuf_t *s, int *len); 65 | static void strbuf_ensure_empty_length(strbuf_t *s, int len); 66 | static char *strbuf_empty_ptr(strbuf_t *s); 67 | static void strbuf_extend_length(strbuf_t *s, int len); 68 | 69 | /* Update */ 70 | extern void strbuf_append_fmt(strbuf_t *s, int len, const char *fmt, ...); 71 | extern void strbuf_append_fmt_retry(strbuf_t *s, const char *format, ...); 72 | static void strbuf_append_mem(strbuf_t *s, const char *c, int len); 73 | extern void strbuf_append_string(strbuf_t *s, const char *str); 74 | static void strbuf_append_char(strbuf_t *s, const char c); 75 | static void strbuf_ensure_null(strbuf_t *s); 76 | 77 | /* Reset string for before use */ 78 | static inline void strbuf_reset(strbuf_t *s) 79 | { 80 | s->length = 0; 81 | } 82 | 83 | static inline int strbuf_allocated(strbuf_t *s) 84 | { 85 | return s->buf != NULL; 86 | } 87 | 88 | /* Return bytes remaining in the string buffer 89 | * Ensure there is space for a NULL terminator. */ 90 | static inline int strbuf_empty_length(strbuf_t *s) 91 | { 92 | return s->size - s->length - 1; 93 | } 94 | 95 | static inline void strbuf_ensure_empty_length(strbuf_t *s, int len) 96 | { 97 | if (len > strbuf_empty_length(s)) 98 | strbuf_resize(s, s->length + len); 99 | } 100 | 101 | static inline char *strbuf_empty_ptr(strbuf_t *s) 102 | { 103 | return s->buf + s->length; 104 | } 105 | 106 | static inline void strbuf_extend_length(strbuf_t *s, int len) 107 | { 108 | s->length += len; 109 | } 110 | 111 | static inline int strbuf_length(strbuf_t *s) 112 | { 113 | return s->length; 114 | } 115 | 116 | static inline void strbuf_append_char(strbuf_t *s, const char c) 117 | { 118 | strbuf_ensure_empty_length(s, 1); 119 | s->buf[s->length++] = c; 120 | } 121 | 122 | static inline void strbuf_append_char_unsafe(strbuf_t *s, const char c) 123 | { 124 | s->buf[s->length++] = c; 125 | } 126 | 127 | static inline void strbuf_append_mem(strbuf_t *s, const char *c, int len) 128 | { 129 | strbuf_ensure_empty_length(s, len); 130 | memcpy(s->buf + s->length, c, len); 131 | s->length += len; 132 | } 133 | 134 | static inline void strbuf_append_mem_unsafe(strbuf_t *s, const char *c, int len) 135 | { 136 | memcpy(s->buf + s->length, c, len); 137 | s->length += len; 138 | } 139 | 140 | static inline void strbuf_ensure_null(strbuf_t *s) 141 | { 142 | s->buf[s->length] = 0; 143 | } 144 | 145 | static inline char *strbuf_string(strbuf_t *s, int *len) 146 | { 147 | if (len) 148 | *len = s->length; 149 | 150 | return s->buf; 151 | } 152 | 153 | /* vi:ai et sw=4 ts=4: 154 | */ 155 | -------------------------------------------------------------------------------- /luaclib-src/lua-cjson-2.1.0/tests/README: -------------------------------------------------------------------------------- 1 | These JSON examples were taken from the JSON website 2 | (http://json.org/example.html) and RFC 4627. 3 | 4 | Used with permission. 5 | -------------------------------------------------------------------------------- /luaclib-src/lua-cjson-2.1.0/tests/bench.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | 3 | -- This benchmark script measures wall clock time and should be 4 | -- run on an unloaded system. 5 | -- 6 | -- Your Mileage May Vary. 7 | -- 8 | -- Mark Pulford 9 | 10 | local json_module = os.getenv("JSON_MODULE") or "cjson" 11 | 12 | require "socket" 13 | local json = require(json_module) 14 | local util = require "cjson.util" 15 | 16 | local function find_func(mod, funcnames) 17 | for _, v in ipairs(funcnames) do 18 | if mod[v] then 19 | return mod[v] 20 | end 21 | end 22 | 23 | return nil 24 | end 25 | 26 | local json_encode = find_func(json, { "encode", "Encode", "to_string", "stringify", "json" }) 27 | local json_decode = find_func(json, { "decode", "Decode", "to_value", "parse" }) 28 | 29 | local function average(t) 30 | local total = 0 31 | for _, v in ipairs(t) do 32 | total = total + v 33 | end 34 | return total / #t 35 | end 36 | 37 | function benchmark(tests, seconds, rep) 38 | local function bench(func, iter) 39 | -- Use socket.gettime() to measure microsecond resolution 40 | -- wall clock time. 41 | local t = socket.gettime() 42 | for i = 1, iter do 43 | func(i) 44 | end 45 | t = socket.gettime() - t 46 | 47 | -- Don't trust any results when the run lasted for less than a 48 | -- millisecond - return nil. 49 | if t < 0.001 then 50 | return nil 51 | end 52 | 53 | return (iter / t) 54 | end 55 | 56 | -- Roughly calculate the number of interations required 57 | -- to obtain a particular time period. 58 | local function calc_iter(func, seconds) 59 | local iter = 1 60 | local rate 61 | -- Warm up the bench function first. 62 | func() 63 | while not rate do 64 | rate = bench(func, iter) 65 | iter = iter * 10 66 | end 67 | return math.ceil(seconds * rate) 68 | end 69 | 70 | local test_results = {} 71 | for name, func in pairs(tests) do 72 | -- k(number), v(string) 73 | -- k(string), v(function) 74 | -- k(number), v(function) 75 | if type(func) == "string" then 76 | name = func 77 | func = _G[name] 78 | end 79 | 80 | local iter = calc_iter(func, seconds) 81 | 82 | local result = {} 83 | for i = 1, rep do 84 | result[i] = bench(func, iter) 85 | end 86 | 87 | -- Remove the slowest half (round down) of the result set 88 | table.sort(result) 89 | for i = 1, math.floor(#result / 2) do 90 | table.remove(result, 1) 91 | end 92 | 93 | test_results[name] = average(result) 94 | end 95 | 96 | return test_results 97 | end 98 | 99 | function bench_file(filename) 100 | local data_json = util.file_load(filename) 101 | local data_obj = json_decode(data_json) 102 | 103 | local function test_encode() 104 | json_encode(data_obj) 105 | end 106 | local function test_decode() 107 | json_decode(data_json) 108 | end 109 | 110 | local tests = {} 111 | if json_encode then tests.encode = test_encode end 112 | if json_decode then tests.decode = test_decode end 113 | 114 | return benchmark(tests, 0.1, 5) 115 | end 116 | 117 | -- Optionally load any custom configuration required for this module 118 | local success, data = pcall(util.file_load, ("bench-%s.lua"):format(json_module)) 119 | if success then 120 | util.run_script(data, _G) 121 | configure(json) 122 | end 123 | 124 | for i = 1, #arg do 125 | local results = bench_file(arg[i]) 126 | for k, v in pairs(results) do 127 | print(("%s\t%s\t%d"):format(arg[i], k, v)) 128 | end 129 | end 130 | 131 | -- vi:ai et sw=4 ts=4: 132 | -------------------------------------------------------------------------------- /luaclib-src/lua-cjson-2.1.0/tests/example1.json: -------------------------------------------------------------------------------- 1 | { 2 | "glossary": { 3 | "title": "example glossary", 4 | "GlossDiv": { 5 | "title": "S", 6 | "GlossList": { 7 | "GlossEntry": { 8 | "ID": "SGML", 9 | "SortAs": "SGML", 10 | "GlossTerm": "Standard Generalized Mark up Language", 11 | "Acronym": "SGML", 12 | "Abbrev": "ISO 8879:1986", 13 | "GlossDef": { 14 | "para": "A meta-markup language, used to create markup languages such as DocBook.", 15 | "GlossSeeAlso": ["GML", "XML"] 16 | }, 17 | "GlossSee": "markup" 18 | } 19 | } 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /luaclib-src/lua-cjson-2.1.0/tests/example2.json: -------------------------------------------------------------------------------- 1 | {"menu": { 2 | "id": "file", 3 | "value": "File", 4 | "popup": { 5 | "menuitem": [ 6 | {"value": "New", "onclick": "CreateNewDoc()"}, 7 | {"value": "Open", "onclick": "OpenDoc()"}, 8 | {"value": "Close", "onclick": "CloseDoc()"} 9 | ] 10 | } 11 | }} 12 | -------------------------------------------------------------------------------- /luaclib-src/lua-cjson-2.1.0/tests/example3.json: -------------------------------------------------------------------------------- 1 | {"widget": { 2 | "debug": "on", 3 | "window": { 4 | "title": "Sample Konfabulator Widget", 5 | "name": "main_window", 6 | "width": 500, 7 | "height": 500 8 | }, 9 | "image": { 10 | "src": "Images/Sun.png", 11 | "name": "sun1", 12 | "hOffset": 250, 13 | "vOffset": 250, 14 | "alignment": "center" 15 | }, 16 | "text": { 17 | "data": "Click Here", 18 | "size": 36, 19 | "style": "bold", 20 | "name": "text1", 21 | "hOffset": 250, 22 | "vOffset": 100, 23 | "alignment": "center", 24 | "onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;" 25 | } 26 | }} 27 | -------------------------------------------------------------------------------- /luaclib-src/lua-cjson-2.1.0/tests/example4.json: -------------------------------------------------------------------------------- 1 | {"web-app": { 2 | "servlet": [ 3 | { 4 | "servlet-name": "cofaxCDS", 5 | "servlet-class": "org.cofax.cds.CDSServlet", 6 | "init-param": { 7 | "configGlossary:installationAt": "Philadelphia, PA", 8 | "configGlossary:adminEmail": "ksm@pobox.com", 9 | "configGlossary:poweredBy": "Cofax", 10 | "configGlossary:poweredByIcon": "/images/cofax.gif", 11 | "configGlossary:staticPath": "/content/static", 12 | "templateProcessorClass": "org.cofax.WysiwygTemplate", 13 | "templateLoaderClass": "org.cofax.FilesTemplateLoader", 14 | "templatePath": "templates", 15 | "templateOverridePath": "", 16 | "defaultListTemplate": "listTemplate.htm", 17 | "defaultFileTemplate": "articleTemplate.htm", 18 | "useJSP": false, 19 | "jspListTemplate": "listTemplate.jsp", 20 | "jspFileTemplate": "articleTemplate.jsp", 21 | "cachePackageTagsTrack": 200, 22 | "cachePackageTagsStore": 200, 23 | "cachePackageTagsRefresh": 60, 24 | "cacheTemplatesTrack": 100, 25 | "cacheTemplatesStore": 50, 26 | "cacheTemplatesRefresh": 15, 27 | "cachePagesTrack": 200, 28 | "cachePagesStore": 100, 29 | "cachePagesRefresh": 10, 30 | "cachePagesDirtyRead": 10, 31 | "searchEngineListTemplate": "forSearchEnginesList.htm", 32 | "searchEngineFileTemplate": "forSearchEngines.htm", 33 | "searchEngineRobotsDb": "WEB-INF/robots.db", 34 | "useDataStore": true, 35 | "dataStoreClass": "org.cofax.SqlDataStore", 36 | "redirectionClass": "org.cofax.SqlRedirection", 37 | "dataStoreName": "cofax", 38 | "dataStoreDriver": "com.microsoft.jdbc.sqlserver.SQLServerDriver", 39 | "dataStoreUrl": "jdbc:microsoft:sqlserver://LOCALHOST:1433;DatabaseName=goon", 40 | "dataStoreUser": "sa", 41 | "dataStorePassword": "dataStoreTestQuery", 42 | "dataStoreTestQuery": "SET NOCOUNT ON;select test='test';", 43 | "dataStoreLogFile": "/usr/local/tomcat/logs/datastore.log", 44 | "dataStoreInitConns": 10, 45 | "dataStoreMaxConns": 100, 46 | "dataStoreConnUsageLimit": 100, 47 | "dataStoreLogLevel": "debug", 48 | "maxUrlLength": 500}}, 49 | { 50 | "servlet-name": "cofaxEmail", 51 | "servlet-class": "org.cofax.cds.EmailServlet", 52 | "init-param": { 53 | "mailHost": "mail1", 54 | "mailHostOverride": "mail2"}}, 55 | { 56 | "servlet-name": "cofaxAdmin", 57 | "servlet-class": "org.cofax.cds.AdminServlet"}, 58 | 59 | { 60 | "servlet-name": "fileServlet", 61 | "servlet-class": "org.cofax.cds.FileServlet"}, 62 | { 63 | "servlet-name": "cofaxTools", 64 | "servlet-class": "org.cofax.cms.CofaxToolsServlet", 65 | "init-param": { 66 | "templatePath": "toolstemplates/", 67 | "log": 1, 68 | "logLocation": "/usr/local/tomcat/logs/CofaxTools.log", 69 | "logMaxSize": "", 70 | "dataLog": 1, 71 | "dataLogLocation": "/usr/local/tomcat/logs/dataLog.log", 72 | "dataLogMaxSize": "", 73 | "removePageCache": "/content/admin/remove?cache=pages&id=", 74 | "removeTemplateCache": "/content/admin/remove?cache=templates&id=", 75 | "fileTransferFolder": "/usr/local/tomcat/webapps/content/fileTransferFolder", 76 | "lookInContext": 1, 77 | "adminGroupID": 4, 78 | "betaServer": true}}], 79 | "servlet-mapping": { 80 | "cofaxCDS": "/", 81 | "cofaxEmail": "/cofaxutil/aemail/*", 82 | "cofaxAdmin": "/admin/*", 83 | "fileServlet": "/static/*", 84 | "cofaxTools": "/tools/*"}, 85 | 86 | "taglib": { 87 | "taglib-uri": "cofax.tld", 88 | "taglib-location": "/WEB-INF/tlds/cofax.tld"}}} 89 | -------------------------------------------------------------------------------- /luaclib-src/lua-cjson-2.1.0/tests/example5.json: -------------------------------------------------------------------------------- 1 | {"menu": { 2 | "header": "SVG Viewer", 3 | "items": [ 4 | {"id": "Open"}, 5 | {"id": "OpenNew", "label": "Open New"}, 6 | null, 7 | {"id": "ZoomIn", "label": "Zoom In"}, 8 | {"id": "ZoomOut", "label": "Zoom Out"}, 9 | {"id": "OriginalView", "label": "Original View"}, 10 | null, 11 | {"id": "Quality"}, 12 | {"id": "Pause"}, 13 | {"id": "Mute"}, 14 | null, 15 | {"id": "Find", "label": "Find..."}, 16 | {"id": "FindAgain", "label": "Find Again"}, 17 | {"id": "Copy"}, 18 | {"id": "CopyAgain", "label": "Copy Again"}, 19 | {"id": "CopySVG", "label": "Copy SVG"}, 20 | {"id": "ViewSVG", "label": "View SVG"}, 21 | {"id": "ViewSource", "label": "View Source"}, 22 | {"id": "SaveAs", "label": "Save As"}, 23 | null, 24 | {"id": "Help"}, 25 | {"id": "About", "label": "About Adobe CVG Viewer..."} 26 | ] 27 | }} 28 | -------------------------------------------------------------------------------- /luaclib-src/lua-cjson-2.1.0/tests/genutf8.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | # Create test comparison data using a different UTF-8 implementation. 4 | 5 | # The generated utf8.dat file must have the following MD5 sum: 6 | # cff03b039d850f370a7362f3313e5268 7 | 8 | use strict; 9 | 10 | # 0xD800 - 0xDFFF are used to encode supplementary codepoints 11 | # 0x10000 - 0x10FFFF are supplementary codepoints 12 | my (@codepoints) = (0 .. 0xD7FF, 0xE000 .. 0x10FFFF); 13 | 14 | my $utf8 = pack("U*", @codepoints); 15 | defined($utf8) or die "Unable create UTF-8 string\n"; 16 | 17 | open(FH, ">:utf8", "utf8.dat") 18 | or die "Unable to open utf8.dat: $!\n"; 19 | print FH $utf8 20 | or die "Unable to write utf8.dat\n"; 21 | close(FH); 22 | 23 | # vi:ai et sw=4 ts=4: 24 | -------------------------------------------------------------------------------- /luaclib-src/lua-cjson-2.1.0/tests/numbers.json: -------------------------------------------------------------------------------- 1 | [ 0.110001, 2 | 0.12345678910111, 3 | 0.412454033640, 4 | 2.6651441426902, 5 | 2.718281828459, 6 | 3.1415926535898, 7 | 2.1406926327793 ] 8 | -------------------------------------------------------------------------------- /luaclib-src/lua-cjson-2.1.0/tests/octets-escaped.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JieTrancender/skynet-etcd/c871fc2355c56eb6eeec0d2a6fcac697adabcd6c/luaclib-src/lua-cjson-2.1.0/tests/octets-escaped.dat -------------------------------------------------------------------------------- /luaclib-src/lua-cjson-2.1.0/tests/rfc-example1.json: -------------------------------------------------------------------------------- 1 | { 2 | "Image": { 3 | "Width": 800, 4 | "Height": 600, 5 | "Title": "View from 15th Floor", 6 | "Thumbnail": { 7 | "Url": "http://www.example.com/image/481989943", 8 | "Height": 125, 9 | "Width": "100" 10 | }, 11 | "IDs": [116, 943, 234, 38793] 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /luaclib-src/lua-cjson-2.1.0/tests/rfc-example2.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "precision": "zip", 4 | "Latitude": 37.7668, 5 | "Longitude": -122.3959, 6 | "Address": "", 7 | "City": "SAN FRANCISCO", 8 | "State": "CA", 9 | "Zip": "94107", 10 | "Country": "US" 11 | }, 12 | { 13 | "precision": "zip", 14 | "Latitude": 37.371991, 15 | "Longitude": -122.026020, 16 | "Address": "", 17 | "City": "SUNNYVALE", 18 | "State": "CA", 19 | "Zip": "94085", 20 | "Country": "US" 21 | } 22 | ] 23 | -------------------------------------------------------------------------------- /luaclib-src/lua-cjson-2.1.0/tests/test.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | 3 | -- Lua CJSON tests 4 | -- 5 | -- Mark Pulford 6 | -- 7 | -- Note: The output of this script is easier to read with "less -S" 8 | 9 | local json = require "cjson" 10 | local json_safe = require "cjson.safe" 11 | local util = require "cjson.util" 12 | 13 | local function gen_raw_octets() 14 | local chars = {} 15 | for i = 0, 255 do chars[i + 1] = string.char(i) end 16 | return table.concat(chars) 17 | end 18 | 19 | -- Generate every UTF-16 codepoint, including supplementary codes 20 | local function gen_utf16_escaped() 21 | -- Create raw table escapes 22 | local utf16_escaped = {} 23 | local count = 0 24 | 25 | local function append_escape(code) 26 | local esc = ('\\u%04X'):format(code) 27 | table.insert(utf16_escaped, esc) 28 | end 29 | 30 | table.insert(utf16_escaped, '"') 31 | for i = 0, 0xD7FF do 32 | append_escape(i) 33 | end 34 | -- Skip 0xD800 - 0xDFFF since they are used to encode supplementary 35 | -- codepoints 36 | for i = 0xE000, 0xFFFF do 37 | append_escape(i) 38 | end 39 | -- Append surrogate pair for each supplementary codepoint 40 | for high = 0xD800, 0xDBFF do 41 | for low = 0xDC00, 0xDFFF do 42 | append_escape(high) 43 | append_escape(low) 44 | end 45 | end 46 | table.insert(utf16_escaped, '"') 47 | 48 | return table.concat(utf16_escaped) 49 | end 50 | 51 | function load_testdata() 52 | local data = {} 53 | 54 | -- Data for 8bit raw <-> escaped octets tests 55 | data.octets_raw = gen_raw_octets() 56 | data.octets_escaped = util.file_load("octets-escaped.dat") 57 | 58 | -- Data for \uXXXX -> UTF-8 test 59 | data.utf16_escaped = gen_utf16_escaped() 60 | 61 | -- Load matching data for utf16_escaped 62 | local utf8_loaded 63 | utf8_loaded, data.utf8_raw = pcall(util.file_load, "utf8.dat") 64 | if not utf8_loaded then 65 | data.utf8_raw = "Failed to load utf8.dat - please run genutf8.pl" 66 | end 67 | 68 | data.table_cycle = {} 69 | data.table_cycle[1] = data.table_cycle 70 | 71 | local big = {} 72 | for i = 1, 1100 do 73 | big = { { 10, false, true, json.null }, "string", a = big } 74 | end 75 | data.deeply_nested_data = big 76 | 77 | return data 78 | end 79 | 80 | function test_decode_cycle(filename) 81 | local obj1 = json.decode(util.file_load(filename)) 82 | local obj2 = json.decode(json.encode(obj1)) 83 | return util.compare_values(obj1, obj2) 84 | end 85 | 86 | -- Set up data used in tests 87 | local Inf = math.huge; 88 | local NaN = math.huge * 0; 89 | 90 | local testdata = load_testdata() 91 | 92 | local cjson_tests = { 93 | -- Test API variables 94 | { "Check module name, version", 95 | function () return json._NAME, json._VERSION end, { }, 96 | true, { "cjson", "2.1devel" } }, 97 | 98 | -- Test decoding simple types 99 | { "Decode string", 100 | json.decode, { '"test string"' }, true, { "test string" } }, 101 | { "Decode numbers", 102 | json.decode, { '[ 0.0, -5e3, -1, 0.3e-3, 1023.2, 0e10 ]' }, 103 | true, { { 0.0, -5000, -1, 0.0003, 1023.2, 0 } } }, 104 | { "Decode null", 105 | json.decode, { 'null' }, true, { json.null } }, 106 | { "Decode true", 107 | json.decode, { 'true' }, true, { true } }, 108 | { "Decode false", 109 | json.decode, { 'false' }, true, { false } }, 110 | { "Decode object with numeric keys", 111 | json.decode, { '{ "1": "one", "3": "three" }' }, 112 | true, { { ["1"] = "one", ["3"] = "three" } } }, 113 | { "Decode object with string keys", 114 | json.decode, { '{ "a": "a", "b": "b" }' }, 115 | true, { { a = "a", b = "b" } } }, 116 | { "Decode array", 117 | json.decode, { '[ "one", null, "three" ]' }, 118 | true, { { "one", json.null, "three" } } }, 119 | 120 | -- Test decoding errors 121 | { "Decode UTF-16BE [throw error]", 122 | json.decode, { '\0"\0"' }, 123 | false, { "JSON parser does not support UTF-16 or UTF-32" } }, 124 | { "Decode UTF-16LE [throw error]", 125 | json.decode, { '"\0"\0' }, 126 | false, { "JSON parser does not support UTF-16 or UTF-32" } }, 127 | { "Decode UTF-32BE [throw error]", 128 | json.decode, { '\0\0\0"' }, 129 | false, { "JSON parser does not support UTF-16 or UTF-32" } }, 130 | { "Decode UTF-32LE [throw error]", 131 | json.decode, { '"\0\0\0' }, 132 | false, { "JSON parser does not support UTF-16 or UTF-32" } }, 133 | { "Decode partial JSON [throw error]", 134 | json.decode, { '{ "unexpected eof": ' }, 135 | false, { "Expected value but found T_END at character 21" } }, 136 | { "Decode with extra comma [throw error]", 137 | json.decode, { '{ "extra data": true }, false' }, 138 | false, { "Expected the end but found T_COMMA at character 23" } }, 139 | { "Decode invalid escape code [throw error]", 140 | json.decode, { [[ { "bad escape \q code" } ]] }, 141 | false, { "Expected object key string but found invalid escape code at character 16" } }, 142 | { "Decode invalid unicode escape [throw error]", 143 | json.decode, { [[ { "bad unicode \u0f6 escape" } ]] }, 144 | false, { "Expected object key string but found invalid unicode escape code at character 17" } }, 145 | { "Decode invalid keyword [throw error]", 146 | json.decode, { ' [ "bad barewood", test ] ' }, 147 | false, { "Expected value but found invalid token at character 20" } }, 148 | { "Decode invalid number #1 [throw error]", 149 | json.decode, { '[ -+12 ]' }, 150 | false, { "Expected value but found invalid number at character 3" } }, 151 | { "Decode invalid number #2 [throw error]", 152 | json.decode, { '-v' }, 153 | false, { "Expected value but found invalid number at character 1" } }, 154 | { "Decode invalid number exponent [throw error]", 155 | json.decode, { '[ 0.4eg10 ]' }, 156 | false, { "Expected comma or array end but found invalid token at character 6" } }, 157 | 158 | -- Test decoding nested arrays / objects 159 | { "Set decode_max_depth(5)", 160 | json.decode_max_depth, { 5 }, true, { 5 } }, 161 | { "Decode array at nested limit", 162 | json.decode, { '[[[[[ "nested" ]]]]]' }, 163 | true, { {{{{{ "nested" }}}}} } }, 164 | { "Decode array over nested limit [throw error]", 165 | json.decode, { '[[[[[[ "nested" ]]]]]]' }, 166 | false, { "Found too many nested data structures (6) at character 6" } }, 167 | { "Decode object at nested limit", 168 | json.decode, { '{"a":{"b":{"c":{"d":{"e":"nested"}}}}}' }, 169 | true, { {a={b={c={d={e="nested"}}}}} } }, 170 | { "Decode object over nested limit [throw error]", 171 | json.decode, { '{"a":{"b":{"c":{"d":{"e":{"f":"nested"}}}}}}' }, 172 | false, { "Found too many nested data structures (6) at character 26" } }, 173 | { "Set decode_max_depth(1000)", 174 | json.decode_max_depth, { 1000 }, true, { 1000 } }, 175 | { "Decode deeply nested array [throw error]", 176 | json.decode, { string.rep("[", 1100) .. '1100' .. string.rep("]", 1100)}, 177 | false, { "Found too many nested data structures (1001) at character 1001" } }, 178 | 179 | -- Test encoding nested tables 180 | { "Set encode_max_depth(5)", 181 | json.encode_max_depth, { 5 }, true, { 5 } }, 182 | { "Encode nested table as array at nested limit", 183 | json.encode, { {{{{{"nested"}}}}} }, true, { '[[[[["nested"]]]]]' } }, 184 | { "Encode nested table as array after nested limit [throw error]", 185 | json.encode, { { {{{{{"nested"}}}}} } }, 186 | false, { "Cannot serialise, excessive nesting (6)" } }, 187 | { "Encode nested table as object at nested limit", 188 | json.encode, { {a={b={c={d={e="nested"}}}}} }, 189 | true, { '{"a":{"b":{"c":{"d":{"e":"nested"}}}}}' } }, 190 | { "Encode nested table as object over nested limit [throw error]", 191 | json.encode, { {a={b={c={d={e={f="nested"}}}}}} }, 192 | false, { "Cannot serialise, excessive nesting (6)" } }, 193 | { "Encode table with cycle [throw error]", 194 | json.encode, { testdata.table_cycle }, 195 | false, { "Cannot serialise, excessive nesting (6)" } }, 196 | { "Set encode_max_depth(1000)", 197 | json.encode_max_depth, { 1000 }, true, { 1000 } }, 198 | { "Encode deeply nested data [throw error]", 199 | json.encode, { testdata.deeply_nested_data }, 200 | false, { "Cannot serialise, excessive nesting (1001)" } }, 201 | 202 | -- Test encoding simple types 203 | { "Encode null", 204 | json.encode, { json.null }, true, { 'null' } }, 205 | { "Encode true", 206 | json.encode, { true }, true, { 'true' } }, 207 | { "Encode false", 208 | json.encode, { false }, true, { 'false' } }, 209 | { "Encode empty object", 210 | json.encode, { { } }, true, { '{}' } }, 211 | { "Encode integer", 212 | json.encode, { 10 }, true, { '10' } }, 213 | { "Encode string", 214 | json.encode, { "hello" }, true, { '"hello"' } }, 215 | { "Encode Lua function [throw error]", 216 | json.encode, { function () end }, 217 | false, { "Cannot serialise function: type not supported" } }, 218 | 219 | -- Test decoding invalid numbers 220 | { "Set decode_invalid_numbers(true)", 221 | json.decode_invalid_numbers, { true }, true, { true } }, 222 | { "Decode hexadecimal", 223 | json.decode, { '0x6.ffp1' }, true, { 13.9921875 } }, 224 | { "Decode numbers with leading zero", 225 | json.decode, { '[ 0123, 00.33 ]' }, true, { { 123, 0.33 } } }, 226 | { "Decode +-Inf", 227 | json.decode, { '[ +Inf, Inf, -Inf ]' }, true, { { Inf, Inf, -Inf } } }, 228 | { "Decode +-Infinity", 229 | json.decode, { '[ +Infinity, Infinity, -Infinity ]' }, 230 | true, { { Inf, Inf, -Inf } } }, 231 | { "Decode +-NaN", 232 | json.decode, { '[ +NaN, NaN, -NaN ]' }, true, { { NaN, NaN, NaN } } }, 233 | { "Decode Infrared (not infinity) [throw error]", 234 | json.decode, { 'Infrared' }, 235 | false, { "Expected the end but found invalid token at character 4" } }, 236 | { "Decode Noodle (not NaN) [throw error]", 237 | json.decode, { 'Noodle' }, 238 | false, { "Expected value but found invalid token at character 1" } }, 239 | { "Set decode_invalid_numbers(false)", 240 | json.decode_invalid_numbers, { false }, true, { false } }, 241 | { "Decode hexadecimal [throw error]", 242 | json.decode, { '0x6' }, 243 | false, { "Expected value but found invalid number at character 1" } }, 244 | { "Decode numbers with leading zero [throw error]", 245 | json.decode, { '[ 0123, 00.33 ]' }, 246 | false, { "Expected value but found invalid number at character 3" } }, 247 | { "Decode +-Inf [throw error]", 248 | json.decode, { '[ +Inf, Inf, -Inf ]' }, 249 | false, { "Expected value but found invalid token at character 3" } }, 250 | { "Decode +-Infinity [throw error]", 251 | json.decode, { '[ +Infinity, Infinity, -Infinity ]' }, 252 | false, { "Expected value but found invalid token at character 3" } }, 253 | { "Decode +-NaN [throw error]", 254 | json.decode, { '[ +NaN, NaN, -NaN ]' }, 255 | false, { "Expected value but found invalid token at character 3" } }, 256 | { 'Set decode_invalid_numbers("on")', 257 | json.decode_invalid_numbers, { "on" }, true, { true } }, 258 | 259 | -- Test encoding invalid numbers 260 | { "Set encode_invalid_numbers(false)", 261 | json.encode_invalid_numbers, { false }, true, { false } }, 262 | { "Encode NaN [throw error]", 263 | json.encode, { NaN }, 264 | false, { "Cannot serialise number: must not be NaN or Inf" } }, 265 | { "Encode Infinity [throw error]", 266 | json.encode, { Inf }, 267 | false, { "Cannot serialise number: must not be NaN or Inf" } }, 268 | { "Set encode_invalid_numbers(\"null\")", 269 | json.encode_invalid_numbers, { "null" }, true, { "null" } }, 270 | { "Encode NaN as null", 271 | json.encode, { NaN }, true, { "null" } }, 272 | { "Encode Infinity as null", 273 | json.encode, { Inf }, true, { "null" } }, 274 | { "Set encode_invalid_numbers(true)", 275 | json.encode_invalid_numbers, { true }, true, { true } }, 276 | { "Encode NaN", 277 | json.encode, { NaN }, true, { "nan" } }, 278 | { "Encode Infinity", 279 | json.encode, { Inf }, true, { "inf" } }, 280 | { 'Set encode_invalid_numbers("off")', 281 | json.encode_invalid_numbers, { "off" }, true, { false } }, 282 | 283 | -- Test encoding tables 284 | { "Set encode_sparse_array(true, 2, 3)", 285 | json.encode_sparse_array, { true, 2, 3 }, true, { true, 2, 3 } }, 286 | { "Encode sparse table as array #1", 287 | json.encode, { { [3] = "sparse test" } }, 288 | true, { '[null,null,"sparse test"]' } }, 289 | { "Encode sparse table as array #2", 290 | json.encode, { { [1] = "one", [4] = "sparse test" } }, 291 | true, { '["one",null,null,"sparse test"]' } }, 292 | { "Encode sparse array as object", 293 | json.encode, { { [1] = "one", [5] = "sparse test" } }, 294 | true, { '{"1":"one","5":"sparse test"}' } }, 295 | { "Encode table with numeric string key as object", 296 | json.encode, { { ["2"] = "numeric string key test" } }, 297 | true, { '{"2":"numeric string key test"}' } }, 298 | { "Set encode_sparse_array(false)", 299 | json.encode_sparse_array, { false }, true, { false, 2, 3 } }, 300 | { "Encode table with incompatible key [throw error]", 301 | json.encode, { { [false] = "wrong" } }, 302 | false, { "Cannot serialise boolean: table key must be a number or string" } }, 303 | 304 | -- Test escaping 305 | { "Encode all octets (8-bit clean)", 306 | json.encode, { testdata.octets_raw }, true, { testdata.octets_escaped } }, 307 | { "Decode all escaped octets", 308 | json.decode, { testdata.octets_escaped }, true, { testdata.octets_raw } }, 309 | { "Decode single UTF-16 escape", 310 | json.decode, { [["\uF800"]] }, true, { "\239\160\128" } }, 311 | { "Decode all UTF-16 escapes (including surrogate combinations)", 312 | json.decode, { testdata.utf16_escaped }, true, { testdata.utf8_raw } }, 313 | { "Decode swapped surrogate pair [throw error]", 314 | json.decode, { [["\uDC00\uD800"]] }, 315 | false, { "Expected value but found invalid unicode escape code at character 2" } }, 316 | { "Decode duplicate high surrogate [throw error]", 317 | json.decode, { [["\uDB00\uDB00"]] }, 318 | false, { "Expected value but found invalid unicode escape code at character 2" } }, 319 | { "Decode duplicate low surrogate [throw error]", 320 | json.decode, { [["\uDB00\uDB00"]] }, 321 | false, { "Expected value but found invalid unicode escape code at character 2" } }, 322 | { "Decode missing low surrogate [throw error]", 323 | json.decode, { [["\uDB00"]] }, 324 | false, { "Expected value but found invalid unicode escape code at character 2" } }, 325 | { "Decode invalid low surrogate [throw error]", 326 | json.decode, { [["\uDB00\uD"]] }, 327 | false, { "Expected value but found invalid unicode escape code at character 2" } }, 328 | 329 | -- Test locale support 330 | -- 331 | -- The standard Lua interpreter is ANSI C online doesn't support locales 332 | -- by default. Force a known problematic locale to test strtod()/sprintf(). 333 | { "Set locale to cs_CZ (comma separator)", function () 334 | os.setlocale("cs_CZ") 335 | json.new() 336 | end }, 337 | { "Encode number under comma locale", 338 | json.encode, { 1.5 }, true, { '1.5' } }, 339 | { "Decode number in array under comma locale", 340 | json.decode, { '[ 10, "test" ]' }, true, { { 10, "test" } } }, 341 | { "Revert locale to POSIX", function () 342 | os.setlocale("C") 343 | json.new() 344 | end }, 345 | 346 | -- Test encode_keep_buffer() and enable_number_precision() 347 | { "Set encode_keep_buffer(false)", 348 | json.encode_keep_buffer, { false }, true, { false } }, 349 | { "Set encode_number_precision(3)", 350 | json.encode_number_precision, { 3 }, true, { 3 } }, 351 | { "Encode number with precision 3", 352 | json.encode, { 1/3 }, true, { "0.333" } }, 353 | { "Set encode_number_precision(14)", 354 | json.encode_number_precision, { 14 }, true, { 14 } }, 355 | { "Set encode_keep_buffer(true)", 356 | json.encode_keep_buffer, { true }, true, { true } }, 357 | 358 | -- Test config API errors 359 | -- Function is listed as '?' due to pcall 360 | { "Set encode_number_precision(0) [throw error]", 361 | json.encode_number_precision, { 0 }, 362 | false, { "bad argument #1 to '?' (expected integer between 1 and 14)" } }, 363 | { "Set encode_number_precision(\"five\") [throw error]", 364 | json.encode_number_precision, { "five" }, 365 | false, { "bad argument #1 to '?' (number expected, got string)" } }, 366 | { "Set encode_keep_buffer(nil, true) [throw error]", 367 | json.encode_keep_buffer, { nil, true }, 368 | false, { "bad argument #2 to '?' (found too many arguments)" } }, 369 | { "Set encode_max_depth(\"wrong\") [throw error]", 370 | json.encode_max_depth, { "wrong" }, 371 | false, { "bad argument #1 to '?' (number expected, got string)" } }, 372 | { "Set decode_max_depth(0) [throw error]", 373 | json.decode_max_depth, { "0" }, 374 | false, { "bad argument #1 to '?' (expected integer between 1 and 2147483647)" } }, 375 | { "Set encode_invalid_numbers(-2) [throw error]", 376 | json.encode_invalid_numbers, { -2 }, 377 | false, { "bad argument #1 to '?' (invalid option '-2')" } }, 378 | { "Set decode_invalid_numbers(true, false) [throw error]", 379 | json.decode_invalid_numbers, { true, false }, 380 | false, { "bad argument #2 to '?' (found too many arguments)" } }, 381 | { "Set encode_sparse_array(\"not quite on\") [throw error]", 382 | json.encode_sparse_array, { "not quite on" }, 383 | false, { "bad argument #1 to '?' (invalid option 'not quite on')" } }, 384 | 385 | { "Reset Lua CJSON configuration", function () json = json.new() end }, 386 | -- Wrap in a function to ensure the table returned by json.new() is used 387 | { "Check encode_sparse_array()", 388 | function (...) return json.encode_sparse_array(...) end, { }, 389 | true, { false, 2, 10 } }, 390 | 391 | { "Encode (safe) simple value", 392 | json_safe.encode, { true }, 393 | true, { "true" } }, 394 | { "Encode (safe) argument validation [throw error]", 395 | json_safe.encode, { "arg1", "arg2" }, 396 | false, { "bad argument #1 to '?' (expected 1 argument)" } }, 397 | { "Decode (safe) error generation", 398 | json_safe.decode, { "Oops" }, 399 | true, { nil, "Expected value but found invalid token at character 1" } }, 400 | { "Decode (safe) error generation after new()", 401 | function(...) return json_safe.new().decode(...) end, { "Oops" }, 402 | true, { nil, "Expected value but found invalid token at character 1" } }, 403 | } 404 | 405 | print(("==> Testing Lua CJSON version %s\n"):format(json._VERSION)) 406 | 407 | util.run_test_group(cjson_tests) 408 | 409 | for _, filename in ipairs(arg) do 410 | util.run_test("Decode cycle " .. filename, test_decode_cycle, { filename }, 411 | true, { true }) 412 | end 413 | 414 | local pass, total = util.run_test_summary() 415 | 416 | if pass == total then 417 | print("==> Summary: all tests succeeded") 418 | else 419 | print(("==> Summary: %d/%d tests failed"):format(total - pass, total)) 420 | os.exit(1) 421 | end 422 | 423 | -- vi:ai et sw=4 ts=4: 424 | -------------------------------------------------------------------------------- /luaclib-src/lua-cjson-2.1.0/tests/types.json: -------------------------------------------------------------------------------- 1 | { "array": [ 10, true, null ] } 2 | -------------------------------------------------------------------------------- /luaclib/cjson.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JieTrancender/skynet-etcd/c871fc2355c56eb6eeec0d2a6fcac697adabcd6c/luaclib/cjson.so -------------------------------------------------------------------------------- /lualib/common.lua: -------------------------------------------------------------------------------- 1 | local core = require "skynet.core" 2 | local table_concat = table.concat 3 | local table_insert = table.insert 4 | local string_sub = string.sub 5 | local string_len = string.len 6 | local string_find = string.find 7 | local type = type 8 | 9 | function table_dump_line(obj) 10 | local getIndent, quoteStr, wrapKey, wrapVal, dumpObj 11 | getIndent = function(level) 12 | return "" 13 | -- return string.rep("\t", level) 14 | end 15 | quoteStr = function(str) 16 | return '"' .. string.gsub(str, '"', '\\"') .. '"' 17 | end 18 | wrapKey = function(val) 19 | if type(val) == "number" then 20 | return "[" .. val .. "]" 21 | elseif type(val) == "string" then 22 | return "[" .. quoteStr(val) .. "]" 23 | else 24 | return "[" .. tostring(val) .. "]" 25 | end 26 | end 27 | wrapVal = function(val, level) 28 | if type(val) == "table" then 29 | return dumpObj(val, level) 30 | elseif type(val) == "number" then 31 | return val 32 | elseif type(val) == "string" then 33 | return quoteStr(val) 34 | else 35 | return tostring(val) 36 | end 37 | end 38 | dumpObj = function(obj, level) 39 | if type(obj) ~= "table" then 40 | return wrapVal(obj) 41 | end 42 | level = level + 1 43 | local tokens = {} 44 | tokens[#tokens + 1] = "{" 45 | for k, v in pairs(obj) do 46 | tokens[#tokens + 1] = getIndent(level) .. wrapKey(k) .. " = " .. wrapVal(v, level) .. "," 47 | end 48 | tokens[#tokens + 1] = getIndent(level - 1) .. "}" 49 | return table.concat(tokens, "") 50 | end 51 | return dumpObj(obj, 0) 52 | end 53 | 54 | -- log implement 55 | function logImp(...) 56 | local t = {...} 57 | for i = 1, #t do 58 | t[i] = tostring(t[i]) 59 | end 60 | 61 | return core.error(table_concat(t, " ")) 62 | end 63 | 64 | function logInfo(...) 65 | logImp("INFO", ...) 66 | end 67 | 68 | function logWarn(...) 69 | logImp("Warn", ...) 70 | end 71 | 72 | function logError(...) 73 | logImp("ERROR", ...) 74 | end 75 | 76 | function split(str, delim) 77 | if str == nil or str == "" then 78 | return {} 79 | end 80 | 81 | str = str .. "" 82 | local delim, fields = delim or ":", {} 83 | if not str then return fields end 84 | if delim == "" then 85 | fields = string.strToArr(str) 86 | return fields 87 | end 88 | 89 | if type(delim) ~= "string" or string_len(delim) <= 0 then 90 | return 91 | end 92 | 93 | local start = 1 94 | local t = {} 95 | while true do 96 | local pos = string_find(str, delim, start, true) -- plain find 97 | if not pos then 98 | break 99 | end 100 | 101 | table_insert(t, string_sub(str, start, pos - 1)) 102 | start = pos + string_len(delim) 103 | end 104 | table_insert(t, string_sub(str, start)) 105 | 106 | return t 107 | end 108 | 109 | function generateEtcdHosts(etcdHostStr) 110 | local hosts = split(etcdHostStr, ",") 111 | return hosts 112 | end 113 | -------------------------------------------------------------------------------- /lualib/etcd/core/encode_args.lua: -------------------------------------------------------------------------------- 1 | local string = require "string" 2 | local table = require "table" 3 | local hex_to_char = function(x) 4 | return string.char(tonumber(x, 16)) 5 | end 6 | 7 | local typeof = require "etcd.core.typeof" 8 | local function urlencode(url) 9 | if url == nil then 10 | return 11 | end 12 | s = string.gsub(url, "([^%w%.%- ])", function(c) return string.format("%%%02X", string.byte(c)) end) 13 | return string.gsub(url, " ", "+") 14 | end 15 | 16 | local urldecode = function(url) 17 | if url == nil then 18 | return 19 | end 20 | url = url:gsub("+", " ") 21 | url = url:gsub("%%(%x%x)", hex_to_char) 22 | return url 23 | end 24 | 25 | return function (params) 26 | local str = '' 27 | local is_not_first = false 28 | for k,v in pairs(params) do 29 | if typeof.table(v) then 30 | --TODO: 31 | assert(false) 32 | elseif is_not_first then 33 | str = str .. '&' .. k .. '=' .. v 34 | else 35 | str = str .. k .. '=' .. v 36 | is_not_first = true 37 | end 38 | end 39 | 40 | return urlencode(str) 41 | end 42 | -------------------------------------------------------------------------------- /lualib/etcd/core/serializers/json.lua: -------------------------------------------------------------------------------- 1 | local cjson = require("cjson.safe") 2 | 3 | return { 4 | serialize = cjson.encode, 5 | deserialize = cjson.decode, 6 | } 7 | -------------------------------------------------------------------------------- /lualib/etcd/core/serializers/raw.lua: -------------------------------------------------------------------------------- 1 | local type = type 2 | 3 | local function raw_encode(v) 4 | local t = type(v) 5 | if t ~= 'string' then 6 | return nil, 'unsupported type of ' .. t 7 | end 8 | 9 | return v 10 | end 11 | 12 | local function raw_decode(v) 13 | return v 14 | end 15 | 16 | return { 17 | serialize = raw_encode, 18 | deserialize = raw_decode, 19 | } 20 | -------------------------------------------------------------------------------- /lualib/etcd/core/typeof.lua: -------------------------------------------------------------------------------- 1 | local INFINITE_POS = math.huge 2 | local INFINITE_NEG = -INFINITE_POS 3 | local type = type 4 | local floor = math.floor 5 | local rawequal = rawequal 6 | 7 | local function typeof(cmp, arg) 8 | return cmp == type(arg) 9 | end 10 | 11 | local function typeof_nil(...) 12 | return typeof('nil', ...) 13 | end 14 | 15 | local function typeof_bool(...) 16 | return typeof('boolean', ...) 17 | end 18 | 19 | local function typeof_str(...) 20 | return typeof('string', ...) 21 | end 22 | 23 | local function typeof_num(...) 24 | return typeof('number', ...) 25 | end 26 | 27 | local function typeof_fun(...) 28 | return typeof('function', ...) 29 | end 30 | 31 | local function typeof_table(...) 32 | return typeof('table', ...) 33 | end 34 | 35 | local function typeof_thread(...) 36 | return typeof('thread', ...) 37 | end 38 | 39 | local function typeof_userdata(...) 40 | return typeof('userdata', ...) 41 | end 42 | 43 | local function typeof_finite(arg) 44 | return type(arg) == 'number' and (arg < INFINITE_POS and arg > INFINITE_NEG) 45 | end 46 | 47 | local function typeof_unsigned(arg) 48 | return type(arg) == 'number' and (arg < INFINITE_POS and arg >= 0) 49 | end 50 | 51 | local function typeof_int(arg) 52 | return typeof_finite(arg) and rawequal(floor(arg), arg) 53 | end 54 | 55 | local function typeof_int8(arg) 56 | return typeof_int(arg) and arg >= -128 and arg <= 127 57 | end 58 | 59 | local function typeof_int16(arg) 60 | return typeof_int(arg) and arg >= -32768 and arg <= 32767 61 | end 62 | 63 | local function typeof_int32(arg) 64 | return typeof_int(arg) and arg >= -2147483648 and arg <= 2147483647 65 | end 66 | 67 | local function typeof_uint(arg) 68 | return typeof_unsigned(arg) and rawequal(floor(arg), arg) 69 | end 70 | 71 | local function typeof_uint8(arg) 72 | return typeof_uint(arg) and arg <= 255 73 | end 74 | 75 | local function typeof_uint16(arg) 76 | return typeof_uint(arg) and arg <= 65535 77 | end 78 | 79 | local function typeof_uint32(arg) 80 | return typeof_uint(arg) and arg <= 4294967295 81 | end 82 | 83 | local function typeof_nan(arg) 84 | return arg ~= arg 85 | end 86 | 87 | local function typeof_non(arg) 88 | return arg == nil or arg == false or arg == 0 or arg == '' or arg ~= arg 89 | end 90 | 91 | local _M = { 92 | version = 0.1, 93 | ['nil'] = typeof_nil, 94 | ['boolean'] = typeof_bool, 95 | ['string'] = typeof_str, 96 | ['number'] = typeof_num, 97 | ['function'] = typeof_fun, 98 | ['table'] = typeof_table, 99 | ['thread'] = typeof_thread, 100 | ['userdata'] = typeof_userdata, 101 | ['finite'] = typeof_finite, 102 | ['unsigned'] = typeof_unsigned, 103 | ['int'] = typeof_int, 104 | ['int8'] = typeof_int8, 105 | ['int16'] = typeof_int16, 106 | ['int32'] = typeof_int32, 107 | ['uint'] = typeof_uint, 108 | ['uint8'] = typeof_uint8, 109 | ['uint16'] = typeof_uint16, 110 | ['uint32'] = typeof_uint32, 111 | ['nan'] = typeof_nan, 112 | ['non'] = typeof_non, 113 | -- alias 114 | ['Nil'] = typeof_nil, 115 | ['Function'] = typeof_fun 116 | } 117 | 118 | return _M 119 | -------------------------------------------------------------------------------- /lualib/etcd/core/utils.lua: -------------------------------------------------------------------------------- 1 | local c = require "skynet.core" 2 | local http = require "http.httpc" 3 | local table_concat = table.concat 4 | local tostring = tostring 5 | local select = select 6 | local ipairs = ipairs 7 | local pairs = pairs 8 | local type = type 9 | 10 | local _M = {} 11 | 12 | local function clear_tab(t) 13 | t = {} 14 | end 15 | _M.clear_tab = clear_tab 16 | 17 | function _M.split(s, delim) 18 | local sp = {} 19 | local pattern = "[^" .. delim .. ']+' 20 | string.gsub(s, pattern, function(v) table.insert(sp, v) end) 21 | return sp 22 | end 23 | 24 | local normalize 25 | do 26 | local items = {} 27 | local function concat(sep, ...) 28 | local argc = select('#', ...) 29 | clear_tab(items) 30 | local len, v = 0 31 | 32 | for i = 1, argc do 33 | v = select(i, ...) 34 | if v ~= nil then 35 | len = len + 1 36 | items[len] = tostring(v) 37 | end 38 | end 39 | 40 | return table_concat(items, sep) 41 | end 42 | 43 | local segs = {} 44 | function normalize(...) 45 | local path = concat('/', ...) 46 | local names = {} 47 | local err 48 | 49 | segs, err = split(path, [[/]]) 50 | if not segs then 51 | return nil, err 52 | end 53 | 54 | local len = 0 55 | for _, seg in ipairs(segs) do 56 | if seg == '..' then 57 | if len > 0 then 58 | len = len - 1 59 | end 60 | elseif seg == '' or seg == '/' and names[len] == '/' then 61 | -- do nothing 62 | elseif seg ~= '.' then 63 | len = len + 1 64 | names[len] = seg 65 | end 66 | end 67 | 68 | return '/' .. table_concat(names, '/', 1, len) 69 | end 70 | end 71 | _M.normalize = normalize 72 | 73 | function _M.get_real_key(prefix, key) 74 | return (type(prefix) == 'string' and prefix or "") .. key 75 | end 76 | 77 | function _M.has_value(arr, val) 78 | for key, value in pairs(arr) do 79 | if value == val then 80 | return key 81 | end 82 | end 83 | 84 | return false 85 | end 86 | 87 | local log_info 88 | local log_error 89 | 90 | -- todo: log level 91 | local log_level = "ERROR" 92 | do 93 | local function logImp(...) 94 | local t = {...} 95 | for i = 1, #t do 96 | t[i] = tostring(t[i]) 97 | end 98 | 99 | return c.error(table.concat(t, " ")) 100 | end 101 | 102 | function log_info(...) 103 | if log_level ~= "INFO" then 104 | return 105 | end 106 | 107 | logImp("INFO", ...) 108 | end 109 | 110 | function log_error(...) 111 | logImp("ERROR", ...) 112 | end 113 | 114 | function table_dump_line(obj) 115 | local getIndent, quoteStr, wrapKey, wrapVal, dumpObj 116 | getIndent = function(level) 117 | return "" 118 | -- return string.rep("\t", level) 119 | end 120 | quoteStr = function(str) 121 | return '"' .. string.gsub(str, '"', '\\"') .. '"' 122 | end 123 | wrapKey = function(val) 124 | if type(val) == "number" then 125 | return "[" .. val .. "]" 126 | elseif type(val) == "string" then 127 | return "[" .. quoteStr(val) .. "]" 128 | else 129 | return "[" .. tostring(val) .. "]" 130 | end 131 | end 132 | wrapVal = function(val, level) 133 | if type(val) == "table" then 134 | return dumpObj(val, level) 135 | elseif type(val) == "number" then 136 | return val 137 | elseif type(val) == "string" then 138 | return quoteStr(val) 139 | else 140 | return tostring(val) 141 | end 142 | end 143 | dumpObj = function(obj, level) 144 | if type(obj) ~= "table" then 145 | return wrapVal(obj) 146 | end 147 | level = level + 1 148 | local tokens = {} 149 | tokens[#tokens + 1] = "{" 150 | for k, v in pairs(obj) do 151 | tokens[#tokens + 1] = getIndent(level) .. wrapKey(k) .. " = " .. wrapVal(v, level) .. "," 152 | end 153 | tokens[#tokens + 1] = getIndent(level - 1) .. "}" 154 | return table.concat(tokens, "") 155 | end 156 | return dumpObj(obj, 0) 157 | end 158 | end 159 | _M.log_info = log_info 160 | _M.log_error = log_error 161 | _M.table_dump_line = table_dump_line 162 | 163 | local function verify_key(key) 164 | if not key or #key == 0 then 165 | return false, "key should not be empty" 166 | end 167 | 168 | return true, nil 169 | end 170 | _M.verify_key = verify_key 171 | 172 | return _M 173 | -------------------------------------------------------------------------------- /lualib/etcd/core/v2.lua: -------------------------------------------------------------------------------- 1 | -- https://github.com/ledgetech/lua-resty-http 2 | local httpc = require "http.httpc" 3 | local typeof = require("etcd.core.typeof") 4 | local utils = require("etcd.core.utils") 5 | local cjson = require("cjson.safe") 6 | local encode_args = require("etcd.core.encode_args") 7 | local setmetatable = setmetatable 8 | local tostring = tostring 9 | local ipairs = ipairs 10 | local type = type 11 | local crypt = require "skynet.crypt" 12 | local encode_base64 = crypt.base64encode 13 | local require = require 14 | local next = next 15 | local table = table 16 | local INIT_COUNT_RESIZE = 2e8 17 | 18 | 19 | local _M = { 20 | decode_json = cjson.decode, 21 | encode_json = cjson.encode, 22 | } 23 | 24 | 25 | local mt = { __index = _M } 26 | 27 | local clear_tab = function (t) 28 | for k in pairs(t) do 29 | t[k] = nil 30 | end 31 | end 32 | 33 | local table_exist_keys = function (t) 34 | return next(t) 35 | end 36 | 37 | 38 | local tab_nkeys = function (t) 39 | local num = 0 40 | for k, _ in pairs(t) do 41 | num = num + 1 42 | end 43 | return num 44 | end 45 | 46 | 47 | function _M.new(opts) 48 | local timeout = opts.timeout 49 | local ttl = opts.ttl 50 | local api_prefix = opts.api_prefix or "" 51 | local key_prefix = opts.key_prefix or "" 52 | local http_host = opts.http_host 53 | local user = opts.user 54 | local password = opts.password 55 | 56 | if not typeof.uint(timeout) then 57 | return nil, 'opts.timeout must be unsigned integer' 58 | end 59 | 60 | if not typeof.string(http_host) and not typeof.table(http_host) then 61 | return nil, 'opts.http_host must be string or string array' 62 | end 63 | 64 | if not typeof.int(ttl) then 65 | return nil, 'opts.ttl must be integer' 66 | end 67 | 68 | if not typeof.string(api_prefix) then 69 | return nil, 'opts.api_prefix must be string' 70 | end 71 | 72 | if not typeof.string(key_prefix) then 73 | return nil, 'opts.key_prefix must be string' 74 | end 75 | 76 | if user and not typeof.string(user) then 77 | return nil, 'opts.user must be string or ignore' 78 | end 79 | 80 | if password and not typeof.string(password) then 81 | return nil, 'opts.password must be string or ignore' 82 | end 83 | 84 | local endpoints = {} 85 | local http_hosts 86 | if type(http_host) == 'string' then -- signle node 87 | http_hosts = {http_host} 88 | else 89 | http_hosts = http_host 90 | end 91 | 92 | for _, host in ipairs(http_hosts) do 93 | table.insert(endpoints, { 94 | full_prefix = host .. utils.normalize(api_prefix), 95 | http_host = host, 96 | api_prefix = api_prefix, 97 | version = host .. '/version', 98 | stats_leader = host .. '/v2/stats/leader', 99 | stats_self = host .. '/v2/stats/self', 100 | stats_store = host .. '/v2/stats/store', 101 | keys = host .. '/v2/keys', 102 | }) 103 | end 104 | 105 | return setmetatable({ 106 | init_count = 0, 107 | timeout = timeout, 108 | ttl = ttl, 109 | key_prefix = key_prefix, 110 | is_cluster = #endpoints > 1, 111 | user = user, 112 | password = password, 113 | endpoints = endpoints 114 | }, 115 | mt) 116 | 117 | end 118 | 119 | local content_type = { 120 | ["Content-Type"] = "application/x-www-form-urlencoded", 121 | } 122 | 123 | local function choose_endpoint(self) 124 | local endpoints = self.endpoints 125 | local endpoints_len = #endpoints 126 | if endpoints_len == 1 then 127 | return endpoints[1] 128 | end 129 | 130 | self.init_count = (self.init_count or 0) + 1 131 | local pos = self.init_count % endpoints_len + 1 132 | if self.init_count >= INIT_COUNT_RESIZE then 133 | self.init_count = 0 134 | end 135 | 136 | return endpoints[pos] 137 | end 138 | 139 | 140 | -- todo: test cover 141 | -- return key, value 142 | -- example: 'Authorization', 'Basic dsfsfsddsfddsdsffd==' 143 | local function create_basicauth(user, password) 144 | local userPwd = user .. ':' .. password 145 | local base64Str = encode_base64(userPwd) 146 | return 'Authorization', 'Basic ' .. base64Str 147 | end 148 | 149 | 150 | local function _request(self, host, method, uri, opts, timeout) 151 | utils.log_info("_request ", host, method, uri, utils.table_dump_line(opts), timeout) 152 | local body 153 | if opts and opts.body and table_exist_keys(opts.body) then 154 | body = encode_args(opts.body) 155 | end 156 | 157 | if opts and opts.query and table_exist_keys(opts.query) then 158 | uri = uri .. '?' .. encode_args(opts.query) 159 | end 160 | 161 | local recvheader = {} 162 | local headers = { 163 | ['Content-Type'] = content_type['Content-Type'] 164 | } 165 | if self.user and self.password then 166 | local bauth_key, bauth_val = create_basicauth(self.user, self.password) 167 | headers[bauth_key] = bauth_val 168 | end 169 | 170 | local status, body = httpc.request(method, host, uri, recvheader, headers, body) 171 | if status >= 500 then 172 | return nil, "invalid response code: " .. status 173 | end 174 | 175 | if status == 401 then 176 | return nil, "insufficient credentials code: " .. status 177 | end 178 | 179 | if not typeof.string(body) then 180 | return {status = status, body = body} 181 | end 182 | 183 | return {body = self.decode_json(body), status = status, headers = recvheader} 184 | end 185 | 186 | 187 | local function set(self, key, val, attr) 188 | local err 189 | val, err = self.encode_json(val) 190 | if not val then 191 | return nil, err 192 | end 193 | 194 | local prev_exist 195 | if attr.prev_exist ~= nil then 196 | prev_exist = attr.prev_exist and 'true' or 'false' 197 | end 198 | 199 | local dir 200 | if attr.dir then 201 | dir = attr.dir and 'true' or 'false' 202 | end 203 | 204 | local opts = { 205 | body = { 206 | ttl = attr.ttl, 207 | value = val, 208 | dir = dir, 209 | }, 210 | query = { 211 | prevExist = prev_exist, 212 | prevIndex = attr.prev_index, 213 | } 214 | } 215 | 216 | -- verify key 217 | key = utils.normalize(key) 218 | if key == '/' then 219 | return nil, "key should not be a slash" 220 | end 221 | 222 | local endpoint = choose_endpoint(self) 223 | local res 224 | res, err = _request(self, endpoint.http_host, attr.in_order and 'POST' or 'PUT', 225 | endpoint.full_prefix .. "/keys" .. key, 226 | opts, self.timeout) 227 | 228 | if err then 229 | return nil, err 230 | end 231 | 232 | -- get 233 | if res.status < 300 and res.body.node and not res.body.node.dir then 234 | res.body.node.value, err = self.decode_json(res.body.node.value) 235 | if err then 236 | utils.log_error("failed to json decode value of node: ", err) 237 | return res, err 238 | end 239 | end 240 | 241 | return res 242 | end 243 | 244 | 245 | local function decode_dir_value(self, body_node) 246 | if not body_node.dir then 247 | return false 248 | end 249 | 250 | if type(body_node.nodes) ~= "table" then 251 | return false 252 | end 253 | 254 | local err 255 | for _, node in ipairs(body_node.nodes) do 256 | local val = node.value 257 | if type(val) == "string" then 258 | node.value, err = self.decode_json(val) 259 | if err then 260 | utils.log_error("failed to decode json: ", err) 261 | end 262 | end 263 | 264 | decode_dir_value(self, node) 265 | end 266 | 267 | return true 268 | end 269 | 270 | 271 | local function get(self, key, attr) 272 | local opts 273 | if attr then 274 | local attr_wait 275 | if attr.wait ~= nil then 276 | attr_wait = attr.wait and 'true' or 'false' 277 | end 278 | 279 | local attr_recursive 280 | if attr.recursive then 281 | attr_recursive = attr.recursive and 'true' or 'false' 282 | end 283 | 284 | opts = { 285 | query = { 286 | wait = attr_wait, 287 | waitIndex = attr.wait_index, 288 | recursive = attr_recursive, 289 | consistent = attr.consistent, -- todo 290 | } 291 | } 292 | end 293 | 294 | local endpoint = choose_endpoint(self) 295 | local res, err = _request(self, endpoint.http_host, "GET", 296 | endpoint.full_prefix .. "/keys" .. utils.normalize(key), 297 | opts, attr and attr.timeout or self.timeout) 298 | if err then 299 | return res, err 300 | end 301 | 302 | -- readdir 303 | if attr and attr.dir then 304 | if res.status == 200 and res.body.node and 305 | not res.body.node.dir then 306 | res.body.node.dir = false 307 | end 308 | end 309 | 310 | if res.status == 200 and res.body.node then 311 | local ok = decode_dir_value(self, res.body.node) 312 | if not ok then 313 | local val = res.body.node.value 314 | if type(val) == "string" then 315 | res.body.node.value, err = self.decode_json(val) 316 | if err then 317 | utils.log_error("failed to json decode: ", err) 318 | end 319 | end 320 | end 321 | end 322 | 323 | return res 324 | end 325 | 326 | 327 | local function delete(self, key, attr) 328 | local val, err = attr.prev_value 329 | if val ~= nil and type(val) ~= "number" then 330 | val, err = self.encode_json(val) 331 | if not val then 332 | return nil, err 333 | end 334 | end 335 | 336 | local attr_dir 337 | if attr.dir then 338 | attr_dir = attr.dir and 'true' or 'false' 339 | end 340 | 341 | local attr_recursive 342 | if attr.recursive then 343 | attr_recursive = attr.recursive and 'true' or 'false' 344 | end 345 | 346 | local opts = { 347 | query = { 348 | dir = attr_dir, 349 | prevIndex = attr.prev_index, 350 | recursive = attr_recursive, 351 | prevValue = val, 352 | }, 353 | } 354 | 355 | -- todo: check arguments 356 | local endpoint = choose_endpoint(self) 357 | return _request(self, endpoint.http_host, "DELETE", 358 | endpoint.full_prefix .. "/keys" .. utils.normalize(key), 359 | opts, self.timeout) 360 | end 361 | 362 | do 363 | 364 | function _M.get(self, key) 365 | if not typeof.string(key) then 366 | return nil, 'key must be string' 367 | end 368 | 369 | key = utils.get_real_key(self.key_prefix, key) 370 | 371 | return get(self, key) 372 | end 373 | 374 | local attr = {} 375 | function _M.wait(self, key, modified_index, timeout) 376 | clear_tab(attr) 377 | attr.wait = true 378 | attr.wait_index = modified_index 379 | attr.timeout = timeout 380 | 381 | key = utils.get_real_key(self.key_prefix, key) 382 | 383 | return get(self, key, attr) 384 | end 385 | 386 | function _M.readdir(self, key, recursive) 387 | clear_tab(attr) 388 | attr.dir = true 389 | attr.recursive = recursive 390 | 391 | key = utils.get_real_key(self.key_prefix, key) 392 | 393 | return get(self, key, attr) 394 | end 395 | 396 | -- wait with recursive 397 | function _M.waitdir(self, key, modified_index, timeout) 398 | clear_tab(attr) 399 | attr.wait = true 400 | attr.dir = true 401 | attr.recursive = true 402 | attr.wait_index = modified_index 403 | attr.timeout = timeout 404 | 405 | key = utils.get_real_key(self.key_prefix, key) 406 | 407 | return get(self, key, attr) 408 | end 409 | 410 | -- /version 411 | function _M.version(self) 412 | local endpoint = choose_endpoint(self) 413 | return _request(self, endpoint.http_host, 'GET', endpoint.version, nil, 414 | self.timeout) 415 | end 416 | 417 | -- /stats 418 | function _M.stats_leader(self) 419 | local endpoint = choose_endpoint(self) 420 | return _request(self, endpoint.http_host, 'GET', endpoint.stats_leader, nil, 421 | self.timeout) 422 | end 423 | 424 | function _M.stats_self(self) 425 | local endpoint = choose_endpoint(self) 426 | return _request(self, endpoint.http_host, 'GET', endpoint.stats_self, nil, 427 | self.timeout) 428 | end 429 | 430 | function _M.stats_store(self) 431 | local endpoint = choose_endpoint(self) 432 | return _request(self, endpoint.http_host, 'GET', endpoint.stats_store, nil, 433 | self.timeout) 434 | end 435 | 436 | end -- do 437 | 438 | 439 | do 440 | local attr = {} 441 | function _M.set(self, key, val, ttl) 442 | clear_tab(attr) 443 | attr.ttl = ttl 444 | 445 | key = utils.get_real_key(self.key_prefix, key) 446 | 447 | return set(self, key, val, attr) 448 | end 449 | 450 | -- set key-val and ttl if key does not exists (atomic create) 451 | function _M.setnx(self, key, val, ttl) 452 | clear_tab(attr) 453 | attr.ttl = ttl 454 | attr.prev_exist = false 455 | 456 | key = utils.get_real_key(self.key_prefix, key) 457 | 458 | return set(self, key, val, attr) 459 | end 460 | 461 | -- set key-val and ttl if key is exists (update) 462 | function _M.setx(self, key, val, ttl, modified_index) 463 | clear_tab(attr) 464 | attr.ttl = ttl 465 | attr.prev_exist = true 466 | attr.prev_index = modified_index 467 | 468 | key = utils.get_real_key(self.key_prefix, key) 469 | 470 | return set(self, key, val, attr) 471 | end 472 | 473 | -- dir 474 | function _M.mkdir(self, key, ttl) 475 | clear_tab(attr) 476 | attr.ttl = ttl 477 | attr.dir = true 478 | 479 | key = utils.get_real_key(self.key_prefix, key) 480 | 481 | return set(self, key, nil, attr) 482 | end 483 | 484 | -- mkdir if not exists 485 | function _M.mkdirnx(self, key, ttl) 486 | clear_tab(attr) 487 | attr.ttl = ttl 488 | attr.dir = true 489 | attr.prev_exist = false 490 | 491 | key = utils.get_real_key(self.key_prefix, key) 492 | 493 | return set(self, key, nil, attr) 494 | end 495 | 496 | -- in-order keys 497 | function _M.push(self, key, val, ttl) 498 | clear_tab(attr) 499 | attr.ttl = ttl 500 | attr.in_order = true 501 | 502 | key = utils.get_real_key(self.key_prefix, key) 503 | 504 | return set(self, key, val, attr) 505 | end 506 | 507 | end -- do 508 | 509 | 510 | do 511 | local attr = {} 512 | function _M.delete(self, key, prev_val, modified_index) 513 | clear_tab(attr) 514 | attr.prev_value = prev_val 515 | attr.prev_index = modified_index 516 | 517 | key = utils.get_real_key(self.key_prefix, key) 518 | 519 | return delete(self, key, attr) 520 | end 521 | 522 | function _M.rmdir(self, key, recursive) 523 | clear_tab(attr) 524 | attr.dir = true 525 | attr.recursive = recursive 526 | 527 | key = utils.get_real_key(self.key_prefix, key) 528 | 529 | return delete(self, key, attr) 530 | end 531 | 532 | end -- do 533 | 534 | 535 | return _M 536 | -------------------------------------------------------------------------------- /lualib/etcd/core/v3.lua: -------------------------------------------------------------------------------- 1 | local skynet = require("skynet") 2 | local typeof = require("etcd.core.typeof") 3 | local utils = require("etcd.core.utils") 4 | local cjson = require("cjson.safe") 5 | local httpc = require("http.httpc") 6 | local setmetatable = setmetatable 7 | local random = math.random 8 | local string_match = string.match 9 | local string_char = string.char 10 | local string_byte = string.byte 11 | local string_sub = string.sub 12 | local table_insert = table.insert 13 | local decode_json = cjson.decode 14 | local encode_json = cjson.encode 15 | local now = os.time 16 | local crypt = require "skynet.crypt" 17 | local encode_base64 = crypt.base64encode 18 | local decode_base64 = crypt.base64decode 19 | local INIT_COUNT_RESIZE = 2e8 20 | local health_check = nil 21 | 22 | local _M = {} 23 | 24 | local mt = {__index = _M} 25 | 26 | local clear_tab = function (t) 27 | for k in pairs(t) do 28 | t[k] = nil 29 | end 30 | end 31 | 32 | local table_exist_keys = function (t) 33 | return next(t) 34 | end 35 | 36 | local tab_nkeys = function (t) 37 | local num = 0 38 | for k, _ in pairs(t) do 39 | num = num + 1 40 | end 41 | return num 42 | end 43 | 44 | local tab_clone = function(obj) 45 | local lookup_table = {} 46 | local function _copy(object) 47 | if type(object) ~= "table" then 48 | return object 49 | elseif lookup_table[object] then 50 | return lookup_table[object] 51 | end 52 | 53 | local new_table = {} 54 | lookup_table[object] = new_table 55 | for index, value in pairs(object) do 56 | new_table[_copy(index)] = _copy(value) 57 | end 58 | 59 | return new_table 60 | end 61 | 62 | return _copy(obj) 63 | end 64 | 65 | -- define local refresh function variable 66 | local refresh_jwt_token 67 | 68 | local function _request_uri(self, host, method, uri, opts, timeout, ignore_auth) 69 | utils.log_info("v3 request uri: ", uri, ", timeout: ", timeout) 70 | 71 | local body 72 | if opts and opts.body and table_exist_keys(opts.body) then 73 | body = encode_json(opts.body) 74 | end 75 | 76 | if opts and opts.query and table_exist_keys(opts.query) then 77 | uri = uri .. '?' .. encode_args(opts.query) 78 | end 79 | 80 | local recvheader = {} 81 | local headers = {} 82 | local keepalive = true 83 | if self.is_auth then 84 | if not ignore_auth then 85 | -- authentication request not need auth request 86 | local _, err = refresh_jwt_token(self, timeout) 87 | if err then 88 | return nil, err 89 | end 90 | 91 | headers.Authorization = self.jwt_token 92 | else 93 | keepalive = false -- jwt_token not keepalive 94 | end 95 | end 96 | 97 | local status, body = httpc.request(method, host, uri, recvheader, headers, body) 98 | if status >= 500 then 99 | return nil, "invalid response code: " .. status 100 | end 101 | 102 | if status == 401 then 103 | return nil, "insufficient credentials code: " .. status 104 | end 105 | 106 | if not typeof.string(body) then 107 | return {status = status, body = body} 108 | end 109 | 110 | return {status = status, headers = recvheader, body = decode_json(body)} 111 | end 112 | 113 | local function serialize_and_encode_base64(serialize_fn, data) 114 | local err 115 | data, err = serialize_fn(data) 116 | if not data then 117 | return nil, err 118 | end 119 | 120 | return encode_base64(data) 121 | end 122 | 123 | function _M.new(opts) 124 | local timeout = opts.timeout 125 | local ttl = opts.ttl 126 | local api_prefix = opts.api_prefix 127 | local key_prefix = opts.key_prefix or "" 128 | local http_host = opts.http_host 129 | local user = opts.user 130 | local password = opts.password 131 | local ssl_verify = opts.ssl_verify 132 | if ssl_verify == nil then 133 | ssl_verify = true 134 | end 135 | local serializer = opts.serializer 136 | 137 | if not typeof.uint(timeout) then 138 | return nil, 'opts.timeout must be unsigned integer' 139 | end 140 | 141 | if not typeof.string(http_host) and not typeof.table(http_host) then 142 | return nil, 'opts.http_host must be string or string array' 143 | end 144 | 145 | if not typeof.int(ttl) then 146 | return nil, 'opts.ttl must be integer' 147 | end 148 | 149 | if not typeof.string(api_prefix) then 150 | return nil, 'opts.api_prefix must be string' 151 | end 152 | 153 | if not typeof.string(key_prefix) then 154 | return nil, 'opts.key_prefix must be string' 155 | end 156 | 157 | if user and not typeof.string(user) then 158 | return nil, 'opts.user must be string or ignore' 159 | end 160 | 161 | if password and not typeof.string(password) then 162 | return nil, 'opts.password must be string or ignore' 163 | end 164 | 165 | local endpoints = {} 166 | local http_hosts 167 | if type(http_host) == 'string' then 168 | http_hosts = {http_host} 169 | else 170 | http_hosts = http_host 171 | end 172 | 173 | for _, host in ipairs(http_hosts) do 174 | local m, err = string_match(host, 175 | [[[a-zA-z]+://[^\s]*]]) 176 | if not m then 177 | return nil, "inalid http_host: " .. host .. ", err: " .. (err or "not matched") 178 | end 179 | 180 | table_insert(endpoints, { 181 | full_prefix = host .. utils.normalize(api_prefix), 182 | http_host = host, 183 | scheme = m[1], 184 | host = m[2] or "127.0.0.1", 185 | port = m[3] or "2379", 186 | api_prefix = api_prefix, 187 | }) 188 | end 189 | 190 | -- local sema, err = semaphore.new() 191 | -- if not sema then 192 | -- return nil, err 193 | -- end 194 | 195 | return setmetatable({ 196 | last_auth_time = skynet.now(), -- save last Authentication time 197 | last_refresh_jwt_err = nil, 198 | -- sema = sema, 199 | jwt_token = nil, -- last Authentication token 200 | is_auth = not not (user and password), 201 | user = user, 202 | password = password, 203 | timeout = timeout, 204 | ttl = ttl, 205 | is_cluster = #endpoints > 1, 206 | endpoints = endpoints, 207 | key_prefix = key_prefix, 208 | ssl_verify = ssl_verify, 209 | serializer = serializer, 210 | 211 | ssl_cert_path = opts.ssl_cert_path, 212 | ssl_key_path = opts.ssl_key_path, 213 | }, 214 | mt) 215 | end 216 | 217 | local function choose_endpoint(self) 218 | local endpoints = self.endpoints 219 | local endpoints_len = #endpoints 220 | if endpoints_len == 1 then 221 | return endpoints[1] 222 | end 223 | 224 | if health_check.conf ~= nil then 225 | for _, endpoint in ipairs(endpoints) do 226 | if health_check.get_target_status(endpoint.http_host) then 227 | return endpoint 228 | end 229 | end 230 | 231 | utils.logWarn("has no healthy etcd endpoint available") 232 | return nil, "has" 233 | end 234 | 235 | self.init_count = (self.init_count or 0) + 1 236 | local pos = self.init_count % endpoints_len + 1 237 | if self.init_count >= INIT_COUNT_RESIZE then 238 | self.init_count = 0 239 | end 240 | 241 | return endpoints[pos] 242 | end 243 | 244 | -- return refresh_is_ok, error 245 | function refresh_jwt_token(self, timeout) 246 | -- token exist and not expire 247 | -- default is 5min, we use 3min plus random seconds to smooth the refresh across workers 248 | -- https://github.com/etcd-io/etcd/issues/8287 249 | if self.jwt_token and now() - self.last_auth_time < 60 * 3 + random(0, 60) then 250 | return true, nil 251 | end 252 | 253 | if self.requesting_token then 254 | skynet.sleep(timeout) 255 | if self.jwt_token and now() - self.last_auth_time < 60 * 3 + random(0, 60) then 256 | return true, nil 257 | end 258 | 259 | if self.last_refresh_jwt_err then 260 | utils.log_info("v3 refresh jwt last err: ", self.last_refresh_jwt_err) 261 | return nil, self.last_refresh_jwt_err 262 | end 263 | 264 | -- something unexpected happened, try again 265 | utils.log_info("v3 try auth after waiting, timeout: ", timeout) 266 | end 267 | 268 | self.last_refresh_jwt_err = nil 269 | self.requesting_token = true 270 | 271 | local opts = { 272 | body = { 273 | name = self.user, 274 | password = self.password, 275 | } 276 | } 277 | 278 | local endpoint, err = choose_endpoint(self) 279 | if not endpoint then 280 | return nil, err 281 | end 282 | 283 | local res 284 | res, err = _request_uri(self, endpoint.http_host, 'POST', 285 | endpoint.full_prefix .. "/auth/authenticate", 286 | opts, timeout, true) 287 | self.requesting_token = false 288 | 289 | if err then 290 | self.last_refresh_jwt_err = err 291 | -- wake_up_everyone(self) 292 | return nil, err 293 | end 294 | 295 | if not res or not res.body or not res.body.token then 296 | err = 'authenticate refresh token fail' 297 | self.last_refresh_jwt_err = err 298 | -- wake_up_everyone(self) 299 | return nil, err 300 | end 301 | 302 | self.jwt_token = res.body.token 303 | self.last_auth_time = now() 304 | -- wake_up_everyone(self) 305 | 306 | return true, nil 307 | end 308 | 309 | local function set(self, key, val, attr) 310 | -- verify key 311 | local _, err = utils.verify_key(key) 312 | if err then 313 | return nil, err 314 | end 315 | 316 | key = encode_base64(key) 317 | val, err = serialize_and_encode_base64(self.serializer.serialize, val) 318 | if not val then 319 | return nil, err 320 | end 321 | 322 | attr = attr or {} 323 | 324 | local lease 325 | if attr.lease then 326 | lease = attr.lease 327 | end 328 | 329 | local prev_kv 330 | if attr.prev_kv then 331 | prev_kv = true 332 | end 333 | 334 | local ignore_value 335 | if attr.ignore_value then 336 | ignore_value = true 337 | end 338 | 339 | local ignore_lease 340 | if attr.ignore_lease then 341 | ignore_lease = true 342 | end 343 | 344 | local opts = { 345 | body = { 346 | value = val, 347 | key = key, 348 | lease = lease, 349 | prev_kv = prev_kv, 350 | ignore_value = ignore_value, 351 | ignore_lease = ignore_lease 352 | } 353 | } 354 | 355 | local endpoint 356 | endpoint, err = choose_endpoint(self) 357 | if not endpoint then 358 | return nil, err 359 | end 360 | 361 | local res 362 | res, err = _request_uri(self, endpoint.http_host, 'POST', endpoint.full_prefix.."/kv/put", opts, self.timeout) 363 | if err then 364 | return nil, err 365 | end 366 | 367 | if res.status < 300 then 368 | utils.log_info("v3 set body: ", encode_json(res.body)) 369 | end 370 | 371 | return res, nil 372 | end 373 | 374 | local function get(self, key, attr) 375 | -- verify key 376 | local _, err = utils.verify_key(key) 377 | if err then 378 | return nil, err 379 | end 380 | 381 | attr = attr or {} 382 | 383 | local range_end 384 | if attr.range_end then 385 | range_end = encode_base64(attr.range_end) 386 | end 387 | 388 | local limit 389 | if attr.limit then 390 | limit = attr.limit 391 | end 392 | 393 | local revision 394 | if attr.revision then 395 | revision = attr.revision 396 | end 397 | 398 | local sort_order 399 | if attr.sort_order then 400 | sort_order = attr.sort_order 401 | end 402 | 403 | local sort_target 404 | if attr.sort_target then 405 | sort_target = attr.sort_target 406 | end 407 | 408 | local serializable 409 | if attr.serializable then 410 | serializable = true 411 | end 412 | 413 | local keys_only 414 | if attr.keys_only then 415 | keys_only = true 416 | end 417 | 418 | local count_only 419 | if attr.count_only then 420 | count_only = true 421 | end 422 | 423 | local min_mod_revision 424 | if attr.min_mod_revision then 425 | min_mod_revision = attr.min_mod_revision 426 | end 427 | 428 | local max_mod_revision 429 | if attr.max_mod_revision then 430 | max_mod_revision = attr.max_mod_revision 431 | end 432 | 433 | local min_create_revision 434 | if attr.min_create_revision then 435 | min_create_revision = attr.min_create_revision 436 | end 437 | 438 | local max_create_revision 439 | if attr.max_create_revision then 440 | max_create_revision = attr.max_create_revision 441 | end 442 | 443 | key = encode_base64(key) 444 | 445 | local opts = { 446 | body = { 447 | key = key, 448 | range_end = range_end, 449 | limit = limit, 450 | revision = revision, 451 | sort_order = sort_order, 452 | sort_target = sort_target, 453 | serializable = serializable, 454 | keys_only = keys_only, 455 | count_only = count_only, 456 | min_mod_revision = min_mod_revision, 457 | max_mod_revision = max_mod_revision, 458 | min_create_revision = min_create_revision, 459 | max_create_revision = max_create_revision 460 | } 461 | } 462 | 463 | local endpoint 464 | endpoint, err = choose_endpoint(self) 465 | if not endpoint then 466 | return nil, err 467 | end 468 | 469 | local res 470 | res, err = _request_uri(self, endpoint.http_host, "POST", endpoint.full_prefix.."/kv/range", opts, 471 | attr and attr.timeout or self.timeout) 472 | if res and res.status == 200 then 473 | if res.body.kvs and next(res.body.kvs) then 474 | for _, kv in ipairs(res.body.kvs) do 475 | kv.key = decode_base64(kv.key) 476 | kv.value = decode_base64(kv.value or "") 477 | kv.value = self.serializer.deserialize(kv.value) 478 | end 479 | end 480 | end 481 | 482 | return res, err 483 | end 484 | 485 | local function delete(self, key, attr) 486 | -- verify key 487 | local _, err = utils.verify_key(key) 488 | if err then 489 | return nil, err 490 | end 491 | 492 | attr = attr or {} 493 | 494 | local range_end 495 | if attr.range_end then 496 | range_end = encode_base64(attr.range_end) 497 | end 498 | 499 | local prev_kv 500 | if attr.prev_kv then 501 | prev_kv = true 502 | end 503 | 504 | key = encode_base64(key) 505 | 506 | local opts = { 507 | body = { 508 | key = key, 509 | range_end = range_end, 510 | prev_kv = prev_kv 511 | } 512 | } 513 | 514 | local endpoint 515 | endpoint, err = choose_endpoint(self) 516 | if not endpoint then 517 | return nil, err 518 | end 519 | 520 | return _request_uri(self, endpoint.http_host, "POST", endpoint.full_prefix .. "/kv/deleterange", opts, 521 | self.timeout) 522 | end 523 | 524 | local function txn(self, opts_arg, compare, success, failure) 525 | if #compare < 1 then 526 | return nil, "compare couldn't be empty" 527 | end 528 | 529 | if (success == nil or #success < 1) and (failure == nil or #failure < 1) then 530 | return nil, "success and failure couldn't be empty at the same time" 531 | end 532 | 533 | local timeout = opts_arg and opts_arg.timeout 534 | local opts = { 535 | body = { 536 | compare = compare, 537 | success = success, 538 | failure = failure 539 | } 540 | } 541 | 542 | local endpoint, err = choose_endpoint(self) 543 | if not endpoint then 544 | return nil, err 545 | end 546 | 547 | return _request_uri(self, endpoint.http_host, "POST", endpoint.full_prefix .. "/kv/txn", opts, 548 | timeout or self.time) 549 | end 550 | 551 | local function get_range_end(key) 552 | if #key == 0 then 553 | return string_char(0) 554 | end 555 | 556 | local last = string_sub(key, -1) 557 | key = string_sub(key, 1, #key-1) 558 | 559 | local ascii = string_byte(last) + 1 560 | local str = string_char(ascii) 561 | 562 | return key .. str 563 | end 564 | 565 | do 566 | local attr = {} 567 | function _M.get(self, key, opts) 568 | if not typeof.string(key) then 569 | return nil, 'key must be string' 570 | end 571 | 572 | key = utils.get_real_key(self.key_prefix, key) 573 | 574 | clear_tab(attr) 575 | attr.timeout = opts and opts.timeout 576 | attr.revision = opts and opts.revision 577 | 578 | return get(self, key, attr) 579 | end 580 | 581 | function _M.readdir(self, key, opts) 582 | clear_tab(attr) 583 | 584 | key = utils.get_real_key(self.key_prefix, key) 585 | 586 | attr.range_end = get_range_end(key) 587 | attr.revision = opts and opts.revision 588 | attr.timeout = opts and opts.timeout 589 | attr.limit = opts and opts.limit 590 | attr.sort_order = opts and opts.sort_order 591 | attr.sort_target = opts and opts.sort_target 592 | attr.keys_only = opts and opts.keys_only 593 | attr.count_only = opts and opts.count_only 594 | 595 | return get(self, key, attr) 596 | end 597 | 598 | end -- do 599 | 600 | do 601 | local attr = {} 602 | function _M.set(self, key, val, opts) 603 | clear_tab(attr) 604 | 605 | key = utils.get_real_key(self.key_prefix, key) 606 | 607 | attr.timeout = opts and opts.timeout 608 | attr.lease = opts and opts.lease 609 | attr.prev_kv = opts and opts.prev_kv 610 | attr.ignore_value = opts and opts.ignore_value 611 | attr.ignore_lease = opts and opts.ignore_lease 612 | 613 | return set(self, key, val, attr) 614 | end 615 | 616 | -- set key-val if key does not exists (atomic create) 617 | local compare = {} 618 | local success = {} 619 | local failure = {} 620 | function _M.setnx(self, key, val, opts) 621 | clear_tab(compare) 622 | 623 | key = utils.get_real_key(self.key_prefix, key) 624 | 625 | compare[1] = {} 626 | compare[1].target = "CREATE" 627 | compare[1].key = encode_base64(key) 628 | compare[1].createRevision = 0 629 | 630 | clear_tab(success) 631 | success[1] = {} 632 | success[1].requestPut = {} 633 | success[1].requestPut.key = encode_base64(key) 634 | 635 | local err 636 | val, err = serialize_and_encode_base64(self.serializer.serialize, val) 637 | if not val then 638 | return nil, "failed to encode val: " .. err 639 | end 640 | success[1].requestPut.value = val 641 | 642 | return txn(self, opts, compare, success, nil) 643 | end 644 | 645 | -- set key-val and ttl if key is exists (update) 646 | function _M.setx(self, key, val, opts) 647 | clear_tab(compare) 648 | 649 | key = utils.get_real_key(self.key_prefix, key) 650 | 651 | compare[1] = {} 652 | compare[1].target = "CREATE" 653 | compare[1].key = encode_base64(key) 654 | compare[1].createRevision = 0 655 | 656 | clear_tab(failure) 657 | failure[1] = {} 658 | failure[1].requestPut = {} 659 | failure[1].requestPut.key = encode_base64(key) 660 | 661 | local err 662 | val, err = serialize_and_encode_base64(self.serializer.serialize, val) 663 | if not val then 664 | return nil, "failed to encode val: " .. err 665 | end 666 | failure[1].requestPut.value = val 667 | 668 | return txn(self, opts, compare, nil, failure) 669 | end 670 | 671 | end -- do 672 | 673 | function _M.txn(self, compare, success, failure, opts) 674 | local err 675 | 676 | if compare then 677 | local new_rules = tab_clone(compare) 678 | for i, rule in ipairs(compare) do 679 | rule = tab_clone(rule) 680 | rule.key = encode_base64(utils.get_real_key(self.key_prefix, rule.key)) 681 | if rule.value then 682 | rule.value, err = serialize_and_encode_base64(self.serializer.serialize, rule.value) 683 | if not rule.value then 684 | return nil, "failed to encode value: " .. err 685 | end 686 | end 687 | 688 | new_rules[i] = rule 689 | end 690 | compare = new_rules 691 | end 692 | 693 | if success then 694 | local new_rules = tab_clone(success) 695 | for i, rule in ipairs(success) do 696 | rule = tab_clone(rule) 697 | if rule.requestPut then 698 | local requestPut = tab_clone(rule.requestPut) 699 | requestPut.key = encode_base64(utils.get_real_key(self.key_prefix, requestPut.key)) 700 | requestPut.value, err = serialize_and_encode_base64(self.serializer.serialize, requestPut.value) 701 | if not requestPut.value then 702 | return nil, "failed to encode value: " .. err 703 | end 704 | 705 | rule.requestPut = requestPut 706 | end 707 | new_rules[i] = rule 708 | end 709 | success = new_rules 710 | end 711 | 712 | return txn(self, opts, compare, success, failure) 713 | end 714 | 715 | function _M.grant(self, ttl, id) 716 | if ttl == nil then 717 | return nil, "lease grant command needs TTL argument" 718 | end 719 | 720 | if not typeof.int(ttl) then 721 | return nil, "ttl must be integer" 722 | end 723 | 724 | id = id or 0 725 | local opts = { 726 | body = { 727 | TTL = ttl, 728 | ID = id 729 | } 730 | } 731 | 732 | local endpoint, err = choose_endpoint(self) 733 | if not endpoint then 734 | return nil, err 735 | end 736 | 737 | return _request_uri(self, endpoint.http_host, "POST", endpoint.full_prefix .. "/lease/grant", opts) 738 | end 739 | 740 | function _M.revoke(self, id) 741 | if id == nil then 742 | return nil, "lease revoke command needs ID argument" 743 | end 744 | 745 | local opts = { 746 | body = { 747 | ID = id 748 | } 749 | } 750 | 751 | local endpoint, err = choose_endpoint(self) 752 | if not endpoint then 753 | return nil, err 754 | end 755 | 756 | return _request_uri(self, endpoint.http_host, "POST", endpoint.full_prefix .. "/kv/lease/revoke", opts) 757 | end 758 | 759 | function _M.keepalive(self, id) 760 | if id == nil then 761 | return nil, "lease keepalive command needs ID argument" 762 | end 763 | 764 | local opts = { 765 | body = { 766 | ID = id 767 | } 768 | } 769 | 770 | local endpoint, err = choose_endpoint(self) 771 | if not endpoint then 772 | return nil, err 773 | end 774 | 775 | return _request_uri(self, endpoint.http_host, "POST", endpoint.full_prefix .. "/lease/keepalive", opts) 776 | end 777 | 778 | function _M.timetolive(self, id, keys) 779 | if id == nil then 780 | return nil, "lease timetolive command needs ID argument" 781 | end 782 | 783 | keys = keys or false 784 | local opts = { 785 | body = { 786 | ID = id, 787 | keys = keys 788 | } 789 | } 790 | 791 | local endpoint, err = choose_endpoint(self) 792 | if not endpoint then 793 | return nil, err 794 | end 795 | 796 | local res 797 | res, err = _request_uri(self, endpoint.http_host, "POST", endpoint.full_prefix .. "/kv/lease/timetolive", opts) 798 | if res and res.status == 200 then 799 | if res.body.keys and next(res.body.keys) then 800 | for i, key in ipairs(res.body.keys) do 801 | res.body.keys[i] = decode_base64(key) 802 | end 803 | end 804 | end 805 | 806 | return res, err 807 | end 808 | 809 | function _M.leases(self) 810 | local endpoint, err = choose_endpoint(self) 811 | if not endpoint then 812 | return nil, err 813 | end 814 | 815 | return _request_uri(self, endpoint.http_host, "POST", endpoint.full_prefix .. "/lease/leases") 816 | end 817 | 818 | do 819 | local attr = {} 820 | function _M.delete(self, key, opts) 821 | clear_tab(attr) 822 | 823 | key = utils.get_real_key(self.key_prefix, key) 824 | 825 | attr.timeout = opts and opts.timeout 826 | attr.prev_kv = opts and opts.prev_kv 827 | 828 | return delete(self, key, attr) 829 | end 830 | 831 | function _M.rmdir(self, key, opts) 832 | clear_tab(attr) 833 | 834 | key = utils.get_real_key(self.key_prefix, key) 835 | 836 | attr.range_end = get_range_end(key) 837 | attr.timeout = opts and opts.timeout 838 | attr.prev_kv = opts and opts.prev_kv 839 | 840 | return delete(self, key, attr) 841 | end 842 | 843 | end -- do 844 | 845 | 846 | function _M.version(self) 847 | local endpoint, err = choose_endpoint(self) 848 | if not endpoint then 849 | return nil, err 850 | end 851 | 852 | return _request_uri(self, endpoint.http_host, "GET", endpoint.http_host .. "/version", nil, self.timeout) 853 | end 854 | 855 | return _M 856 | -------------------------------------------------------------------------------- /lualib/etcd/etcd.lua: -------------------------------------------------------------------------------- 1 | local etcdv2 = require("etcd.core.v2") 2 | local etcdv3 = require("etcd.core.v3") 3 | local utils = require("etcd.core.utils") 4 | local typeof = require("etcd.core.typeof") 5 | local require = require 6 | local pcall = pcall 7 | local prefix_v3 = { 8 | ["3.5."] = "/v3", 9 | ["3.4."] = "/v3", 10 | ["3.3."] = "/v3beta", 11 | ["3.2."] = "/v3alpha", 12 | } 13 | 14 | local _M = {version = 0.1} 15 | 16 | local function etcd_version(opts) 17 | local etcd_obj, err = etcdv2.new(opts) 18 | if not etcd_obj then 19 | return nil, err 20 | end 21 | 22 | local ver 23 | ver, err = etcd_obj:version() 24 | if not ver then 25 | return nil, err 26 | end 27 | 28 | return ver.body 29 | end 30 | 31 | local function require_serializer(serializer_name) 32 | if serializer_name then 33 | local ok, module = pcall(require, "etcd.core.serializers." .. serializer_name) 34 | if ok then 35 | return module 36 | end 37 | end 38 | 39 | return require("etcd.core.serializers.json") 40 | end 41 | 42 | function _M.new(opts) 43 | opts = opts or {} 44 | if not typeof.table(opts) then 45 | return nil, 'opts must be table' 46 | end 47 | 48 | opts.timeout = opts.timeout or 5 49 | opts.http_host = opts.http_host or "http://127.0.0.1:2379" 50 | opts.ttl = opts.ttl or -1 51 | 52 | local protocol = opts and opts.protocol or "v2" 53 | local serializer_name = typeof.string(opts.serializer) and opts.serializer 54 | opts.serializer = require_serializer(serializer_name) 55 | 56 | if protocol == "v3" then 57 | -- if opts special the api_prefix, not need to check version 58 | if not opts.api_prefix or not utils.has_value(prefix_v3, opts.api_prefix) then 59 | local ver, err = etcd_version(opts) 60 | if not ver then 61 | return nil, err 62 | end 63 | 64 | local sub_ver = ver.etcdserver:sub(1, 4) 65 | opts.api_prefix = prefix_v3[sub_ver] or "/v3beta" 66 | end 67 | 68 | return etcdv3.new(opts) 69 | end 70 | 71 | opts.api_prefix = "/v2" 72 | 73 | return etcdv2.new(opts) 74 | end 75 | 76 | return _M 77 | -------------------------------------------------------------------------------- /service/etcdd.lua: -------------------------------------------------------------------------------- 1 | require "common" 2 | local etcd = require "etcd.etcd" 3 | 4 | local etcdCli 5 | function init( ... ) 6 | local etcdHosts, user, password, protocol, serializer = ... 7 | local opt = { 8 | http_host = generateEtcdHosts(etcdHosts), 9 | user = user, 10 | password = password, 11 | protocol = protocol, 12 | serializer = serializer or "json" -- 默认使用json格式配置 13 | } 14 | 15 | local err 16 | etcdCli, err = etcd.new(opt) 17 | if not etcdCli then 18 | logError("etcdd init wrong, ", err) 19 | return 20 | end 21 | end 22 | 23 | function exit() 24 | log("etcdd exit success") 25 | end 26 | 27 | function response.get( ... ) 28 | return etcdCli:get(...) 29 | end 30 | 31 | function response.set( key, value, ttl ) 32 | return etcdCli:set(key, value, ttl) 33 | end 34 | 35 | function accept.set( key, value, ttl ) 36 | etcdCli:set(key, value, ttl) 37 | end 38 | 39 | -- v3 support 40 | function response.setnx(key, value, ttl) 41 | return etcdCli:setnx(key, value, ttl) 42 | end 43 | 44 | function accept.setnx(key, value, ttl) 45 | etcdCli:setnx(key, value, ttl) 46 | end 47 | 48 | -- v3 support 49 | function response.setx(key, value, ttl) 50 | return etcdCli:setx(key, value, ttl) 51 | end 52 | 53 | function accept.setx(key, value, ttl) 54 | etcdCli:setx(key, value, ttl) 55 | end 56 | 57 | function accept.delete( ... ) 58 | etcdCli:delete(...) 59 | end 60 | 61 | function response.delete( ... ) 62 | return etcdCli:delete(...) 63 | end 64 | 65 | function accept.rmdir( ... ) 66 | etcdCli:rmdir(...) 67 | end 68 | 69 | function response.rmdir( ... ) 70 | return etcdCli:rmdir(...) 71 | end 72 | 73 | -- v3 support 74 | function response.grant(ttl, id) 75 | return etcdCli:grant(ttl, id) 76 | end 77 | 78 | function accept.grant(ttl, id) 79 | etcdCli:grant(ttl, id) 80 | end 81 | 82 | -- v3 support 83 | function response.revoke(id) 84 | return etcdCli:revoke(id) 85 | end 86 | 87 | function accept.revoke(id) 88 | etcdCli:revoke(id) 89 | end 90 | 91 | -- v3 support 92 | function response.keepalive(id) 93 | return etcdCli:keepalive(id) 94 | end 95 | 96 | function accept.keepalive(id) 97 | etcdCli:keepalive(id) 98 | end 99 | 100 | -- v3 support 101 | function response.timetolive(id, keys) 102 | return etcdCli:timetolive(id, keys) 103 | end 104 | 105 | function accept.timetolive(id, keys) 106 | etcdCli:timetolive(id, keys) 107 | end 108 | 109 | -- v3 support 110 | function response.leases() 111 | return etcdCli:leases() 112 | end 113 | 114 | function response.exec(cmd, ...) 115 | return etcdCli[cmd](etcdCli, ...) 116 | end 117 | 118 | function accept.exec(cmd, ...) 119 | etcdCli[cmd](etcdCli, ...) 120 | end 121 | 122 | function response.stats_leader() 123 | return etcdCli:stats_leader() 124 | end 125 | 126 | function response.stats_self() 127 | return etcdCli:stats_self() 128 | end 129 | 130 | function response.stats_store() 131 | return etcdCli:stats_store() 132 | end 133 | 134 | function response.version() 135 | return etcdCli:version() 136 | end 137 | 138 | function response.readdir(key, recursive) 139 | return etcdCli:readdir(key, recursive) 140 | end 141 | -------------------------------------------------------------------------------- /tools/etcd-cluster/etcd1.conf: -------------------------------------------------------------------------------- 1 | name: etcd01_1 2 | data-dir: "/data/etcd/etcd01_1" 3 | listen-peer-urls: http://0.0.0.0:2380 4 | listen-client-urls: http://0.0.0.0:2379 5 | initial-advertise-peer-urls: http://127.0.0.1:2380 6 | advertise-client-urls: http://0.0.0.0:2379 7 | initial-cluster: etcd01_1=http://127.0.0.1:2380,etcd01_2=http://127.0.0.1:2382,etcd01_3=http://127.0.0.1:2384 8 | initial-cluster-token: 'etcd-cluster' 9 | initial-cluster-state: 'new' 10 | enable-grpc-gateway: true 11 | -------------------------------------------------------------------------------- /tools/etcd-cluster/etcd2.conf: -------------------------------------------------------------------------------- 1 | name: etcd01_2 2 | data-dir: "/data/etcd/etcd01_2" 3 | listen-peer-urls: http://0.0.0.0:2382 4 | listen-client-urls: http://0.0.0.0:2381 5 | initial-advertise-peer-urls: http://127.0.0.1:2382 6 | advertise-client-urls: http://0.0.0.0:2381 7 | initial-cluster: etcd01_1=http://127.0.0.1:2380,etcd01_2=http://127.0.0.1:2382,etcd01_3=http://127.0.0.1:2384 8 | initial-cluster-token: 'etcd-cluster' 9 | initial-cluster-state: 'new' 10 | enable-grpc-gateway: true 11 | -------------------------------------------------------------------------------- /tools/etcd-cluster/etcd3.conf: -------------------------------------------------------------------------------- 1 | name: etcd01_3 2 | data-dir: "/data/etcd/etcd01_3" 3 | listen-peer-urls: http://0.0.0.0:2384 4 | listen-client-urls: http://0.0.0.0:2383 5 | initial-advertise-peer-urls: http://127.0.0.1:2384 6 | advertise-client-urls: http://0.0.0.0:2383 7 | initial-cluster: etcd01_1=http://127.0.0.1:2380,etcd01_2=http://127.0.0.1:2382,etcd01_3=http://127.0.0.1:2384 8 | initial-cluster-token: 'etcd-cluster' 9 | initial-cluster-state: 'new' 10 | enable-grpc-gateway: true 11 | -------------------------------------------------------------------------------- /tools/etcd-cluster/instart.sh: -------------------------------------------------------------------------------- 1 | # !/bin/bash 2 | 3 | etcd --config-file ./etcd1.conf 2>&1 >/dev/null & 4 | etcd --config-file ./etcd2.conf 2>&1 >/dev/null & 5 | etcd --config-file ./etcd3.conf 2>&1 >/dev/null & 6 | --------------------------------------------------------------------------------