├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── build.sh ├── client └── simorgh.go ├── driver ├── auth.go └── simorgh.go ├── server ├── auth.go └── simorgh.go └── vendor ├── gjson ├── LICENSE ├── gjson.go └── gjson_test.go ├── go-srp ├── LICENSE └── src │ └── srp │ └── srp.go ├── gopass ├── LICENSE.txt ├── pass.go ├── pass_test.go ├── terminal.go └── terminal_solaris.go └── tree ├── radix ├── LICENSE ├── README.md ├── radix.go └── radix_test.go ├── tree.go └── types.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | 26 | .vscode 27 | simorgh-client 28 | simorgh-server -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2017, Ahmadreza Zibaei 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | @bash build.sh 3 | install: all 4 | @cp simorgh-server /usr/local/bin 5 | @cp simorgh-client /usr/local/bin -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Simorgh 2 | > It was my very very old golang-codes. So it maybe has a lot of bugs or bad written codes! 3 | 4 | ### Under Construction :) 5 | 6 | Simorgh (Phoenix in Persian) is a simple in-memory key/value database using radix tree. Protected by SRP authentication algorithm. 7 | 8 | ![Image of Simorgh from Wikipedia](https://upload.wikimedia.org/wikipedia/commons/4/43/Phoenix-Fabelwesen.jpg) 9 | 10 | Simorgh image from wikipedia 11 | 12 | *** 13 | 14 | In-Memory Key/Value database based on radix tree with `Get` `Set` `Del` `Clr` commands. 15 | 16 | ### Download 17 | 18 | ```bash 19 | git clone https://github.com/ahmdrz/simorgh 20 | cd simorgh 21 | make 22 | ``` 23 | 24 | And for install simrogh 25 | 26 | ```bash 27 | sudo make install 28 | ``` 29 | 30 | ### Running server 31 | 32 | ```bash 33 | simrogh-server 34 | ``` 35 | 36 | Note that default port is 8080 and default protocol is tcp , you can pass `-port` and `-protocol` to `simorgh-server` 37 | 38 | For more information : 39 | 40 | ```bash 41 | simrogh-server --help 42 | ``` 43 | 44 | ### Client 45 | 46 | ```bash 47 | simorgh-client 48 | ``` 49 | 50 | For more information : 51 | 52 | ```bash 53 | simrogh-client --help 54 | ``` 55 | 56 | *Default Username is :* `simorgh` 57 | *Default Password is :* `simorgh` 58 | 59 | Client program : 60 | 61 | ``` 62 | Username > simorgh 63 | Password > 64 | < set a=b 65 | > OK OK 66 | < get a 67 | > OK b 68 | < get b=c 69 | > ERROR ! UNDEFINED 70 | < get b 71 | > ERROR ! UNDEFINED 72 | < set b=c 73 | > OK OK 74 | < get b 75 | > OK c 76 | < del b 77 | > OK OK 78 | < clr 79 | > OK MEMORY CLEARED (1) 80 | < \q 81 | bye 82 | ``` 83 | 84 | ### API 85 | 86 | You can use `simorgh` in your Golang program. 87 | After installation you can do some code like : 88 | 89 | ```golang 90 | package main 91 | 92 | import ( 93 | "fmt" 94 | "github.com/ahmdrz/simorgh/driver" 95 | ) 96 | 97 | func main() { 98 | si, err := simorgh.New("localhost:8080","simorgh","simorgh","tcp") 99 | if err != nil { 100 | panic(err) 101 | } 102 | defer si.Close() 103 | 104 | fmt.Println(si.Set("a","b")) 105 | fmt.Println(si.Get("a")) 106 | fmt.Println(si.Clr()) 107 | } 108 | ``` 109 | 110 | ### TODO 111 | 112 | - [x] Password authentication. 113 | - [ ] Save configuration file in encrypted text file. 114 | - [ ] Improve Simorgh Cli. 115 | - [ ] Build Simorgh Golang library. 116 | - [ ] Test with heavy dataset. 117 | - [ ] Improve Simorgh base architecture. 118 | - [ ] Make some test files and pass it to Travis CI. 119 | 120 | ### Contribute 121 | 122 | `Radix Tree` is forked from [armon](https://github.com/armon/go-radix). 123 | 124 | I'm not good in data structures , So I will happy if anyone give me suggestions and improve my code. 125 | 126 | *** 127 | 128 | Build with :heart: in Iran. 129 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | export GO15VENDOREXPERIMENT=1 5 | 6 | TMP="$(mktemp -d -t simorgh.XXXX)" 7 | 8 | function rmtemp { 9 | rm -rf "$TMP" 10 | } 11 | trap rmtemp EXIT 12 | 13 | CURRENT="$(pwd)" 14 | SIMORGH="$TMP/src/github.com/ahmdrz/simorgh" 15 | 16 | export GOPATH="$TMP":$GOPATH 17 | for file in `find . -type f`; do 18 | if [[ "$file" != "." && "$file" != ./.git* ]]; then 19 | mkdir -p "$SIMORGH/$(dirname "${file}")" 20 | cp -P "$file" "$SIMORGH/$(dirname "${file}")" 21 | fi 22 | done 23 | 24 | cd $SIMORGH 25 | cd server && go build -i -o $CURRENT/simorgh-server 26 | cd ../client && go build -i -o $CURRENT/simorgh-client 27 | -------------------------------------------------------------------------------- /client/simorgh.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "flag" 6 | "fmt" 7 | "gopass" 8 | "log" 9 | "os" 10 | 11 | "github.com/ahmdrz/simorgh/driver" 12 | "strings" 13 | ) 14 | 15 | func main() { 16 | addr := flag.String("address", "localhost", "Server address") 17 | port := flag.String("port", "8080", "Server port") 18 | proto := flag.String("protocol", "tcp", "Server net listen protocol") 19 | flag.Parse() 20 | 21 | var username string 22 | fmt.Print("Username > ") 23 | fmt.Scanf("%s", &username) 24 | fmt.Printf("Password > ") 25 | 26 | password, err := gopass.GetPasswd() 27 | if err != nil { 28 | fmt.Println("Cannot read input password") 29 | os.Exit(-1) 30 | } 31 | 32 | si, err := simorgh.New(*addr+":"+*port, username, string(password), *proto) 33 | if err != nil { 34 | log.Println(err) 35 | os.Exit(-3) 36 | } 37 | defer si.Close() 38 | 39 | for { 40 | fmt.Print("< ") 41 | reader := bufio.NewReader(os.Stdin) 42 | text, _ := reader.ReadString('\n') 43 | text = text[:len(text)-1] 44 | if text == "\\q" { 45 | si.Close() 46 | fmt.Println("bye") 47 | os.Exit(0) 48 | } 49 | if strings.HasPrefix(text, "set") { 50 | text = text[3:] 51 | parts := strings.Split(text, "=") 52 | msg, err := si.Set(parts[0], parts[1]) 53 | if err != nil { 54 | fmt.Println("> ERROR !", msg) 55 | } else { 56 | fmt.Println("> OK", msg) 57 | } 58 | } else if strings.HasPrefix(text, "get") { 59 | text = text[3:] 60 | msg, err := si.Get(text) 61 | if err != nil { 62 | fmt.Println("> ERROR !", msg) 63 | } else { 64 | fmt.Println("> OK", msg) 65 | } 66 | } else if text == "clr" { 67 | msg, err := si.Clr() 68 | if err != nil { 69 | fmt.Println("> ERROR !", msg) 70 | } else { 71 | fmt.Println("> OK", msg) 72 | } 73 | } else if strings.HasPrefix(text, "del") { 74 | text = text[3:] 75 | msg, err := si.Del(text) 76 | if err != nil { 77 | fmt.Println("> ERROR !", msg) 78 | } else { 79 | fmt.Println("> OK", msg) 80 | } 81 | } 82 | } 83 | os.Exit(0) 84 | } 85 | -------------------------------------------------------------------------------- /driver/auth.go: -------------------------------------------------------------------------------- 1 | package simorgh 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "go-srp/src/srp" 7 | "net" 8 | "strings" 9 | ) 10 | 11 | const SAFE_PRIME_BITS = 2048 12 | 13 | func validate(username, password []byte, conn net.Conn) ([]byte, error) { 14 | c, err := srp.NewClient(username, password, SAFE_PRIME_BITS) 15 | if err != nil { 16 | return nil, fmt.Errorf("SRP AUTHENTICATION FAILED") 17 | } 18 | 19 | conn.Write([]byte("{--cred--" + c.Credentials() + "--cred--}\n")) 20 | message, _ := bufio.NewReader(conn).ReadString('\n') 21 | message = strings.TrimSpace(message) 22 | 23 | if strings.HasPrefix(message, "{") && strings.HasSuffix(message, "}") { 24 | message = message[1 : len(message)-1] 25 | if strings.HasPrefix(message, "--cred--") && strings.HasSuffix(message, "--cred--") { 26 | server_creds := strings.Replace(message, "--cred--", "", -1) 27 | auth, err := c.Generate(server_creds) 28 | if err != nil { 29 | return nil, err 30 | } 31 | conn.Write([]byte("{--auth--" + auth + "--auth--}\n")) 32 | } else if message == "--validate--0--validate--" { 33 | return nil, fmt.Errorf("INVALID USERNAME OR PASSWORD") 34 | } 35 | } 36 | 37 | message, _ = bufio.NewReader(conn).ReadString('\n') 38 | message = strings.TrimSpace(message) 39 | if strings.HasPrefix(message, "{") && strings.HasSuffix(message, "}") { 40 | message = message[1 : len(message)-1] 41 | if strings.HasPrefix(message, "--proof--") && strings.HasSuffix(message, "--proof--") { 42 | proof := strings.Replace(message, "--proof--", "", -1) 43 | err := c.ServerOk(proof) 44 | if err != nil { 45 | return nil, err 46 | } 47 | 48 | key := c.RawKey() 49 | return key, nil 50 | } else if message == "--validate--0--validate--" { 51 | return nil, fmt.Errorf("INVALID USERNAME OR PASSWORD") 52 | } 53 | } 54 | 55 | return nil, fmt.Errorf("Undefined error") 56 | } 57 | -------------------------------------------------------------------------------- /driver/simorgh.go: -------------------------------------------------------------------------------- 1 | package simorgh 2 | 3 | import ( 4 | "bufio" 5 | "encoding/base64" 6 | "fmt" 7 | "net" 8 | "strings" 9 | "time" 10 | ) 11 | 12 | type Simorgh struct { 13 | conn net.Conn 14 | key string 15 | } 16 | 17 | func New(address, username, password, proto string) (*Simorgh, error) { 18 | conn, err := net.Dial(proto, address) 19 | if err != nil { 20 | return nil, err 21 | } 22 | session, err := validate([]byte(username), []byte(password), conn) 23 | if err != nil { 24 | return nil, err 25 | } 26 | var _session []byte = make([]byte, base64.StdEncoding.EncodedLen(len(session))) 27 | base64.StdEncoding.Encode(_session, session) 28 | return &Simorgh{ 29 | conn: conn, 30 | key: string(_session), 31 | }, nil 32 | } 33 | 34 | func (s *Simorgh) Close() error { 35 | s.send("exit") 36 | return s.conn.Close() 37 | } 38 | 39 | func (s *Simorgh) Set(key, value string) (string, error) { 40 | return s.send("set " + key + "=" + value) 41 | } 42 | 43 | func (s *Simorgh) Get(key string) (string, error) { 44 | return s.send("get " + key) 45 | } 46 | 47 | func (s *Simorgh) Del(key string) (string, error) { 48 | return s.send("del " + key) 49 | } 50 | 51 | func (s *Simorgh) Clr() (string, error) { 52 | return s.send("clr") 53 | } 54 | 55 | func (s *Simorgh) send(cmd string) (string, error) { 56 | type Invalid struct { 57 | Text string 58 | Error error 59 | } 60 | invalid := make(chan Invalid, 1) 61 | go func() { 62 | s.conn.Write([]byte("{" + string(s.key) + "-&-" + cmd + "}\n")) 63 | reader := bufio.NewReader(s.conn) 64 | text, _ := reader.ReadString('\n') 65 | text = text[:len(text)-1] 66 | if strings.HasPrefix(text, "{") && strings.HasSuffix(text, "}") { 67 | text = text[1 : len(text)-1] 68 | if text == "INVALID" || text == "UNDEFINED" { 69 | invalid <- Invalid{Text: text, Error: fmt.Errorf("(INVALID or UNDEFINED) SEND REQUEST")} 70 | } 71 | } 72 | invalid <- Invalid{Text: text, Error: nil} 73 | }() 74 | 75 | select { 76 | case result := <-invalid: 77 | return result.Text, result.Error 78 | case <-time.After(10 * time.Second): 79 | return "TIMEOUT", fmt.Errorf("REQUEST_TIME_OUT") 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /server/auth.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "go-srp/src/srp" 5 | "sync" 6 | ) 7 | 8 | type Authentication struct { 9 | mutex sync.Mutex 10 | keys map[string]*srp.Server 11 | accepts map[string]int64 12 | salt []byte 13 | verifier []byte 14 | identityHash []byte 15 | } 16 | 17 | func InitAuthentication() *Authentication { 18 | return &Authentication{ 19 | keys: make(map[string]*srp.Server), 20 | accepts: make(map[string]int64), 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /server/simorgh.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "encoding/base64" 6 | "flag" 7 | "log" 8 | "net" 9 | "runtime" 10 | "strconv" 11 | "strings" 12 | "sync" 13 | "time" 14 | 15 | "go-srp/src/srp" 16 | "tree" 17 | ) 18 | 19 | const SAFE_PRIME_BITS = 2048 20 | const LIMIT_TIME = 6 21 | const MAX_CONNECTION = 3 22 | 23 | var DEFAULT_USERNAME = []byte("simorgh") 24 | var DEFAULT_PASSWORD = []byte("simorgh") 25 | 26 | var simorgh struct { 27 | tree *tree.Tree 28 | auth *Authentication 29 | } 30 | 31 | var connections struct { 32 | sync.Mutex 33 | count int 34 | } 35 | 36 | func init() { 37 | runtime.GOMAXPROCS(runtime.NumCPU()) 38 | 39 | simorgh.tree = tree.NewTree() 40 | Ih, salt, v, err := srp.Verifier(DEFAULT_USERNAME, DEFAULT_PASSWORD, SAFE_PRIME_BITS) 41 | if err != nil { 42 | panic(err) 43 | } 44 | simorgh.auth = InitAuthentication() 45 | simorgh.auth.salt = salt 46 | simorgh.auth.verifier = v 47 | simorgh.auth.identityHash = Ih 48 | } 49 | 50 | func main() { 51 | port := flag.String("port", "8080", "Server port") 52 | proto := flag.String("protocol", "tcp", "Server net listen protocol") 53 | flag.Parse() 54 | 55 | ln, err := net.Listen(*proto, ":"+*port) 56 | if err != nil { 57 | log.Println(err) 58 | return 59 | } 60 | 61 | go removeUnusedRequests() 62 | 63 | for { 64 | conn, err := ln.Accept() 65 | if err != nil { 66 | log.Println(err) 67 | continue 68 | } 69 | 70 | connections.Lock() 71 | if connections.count < MAX_CONNECTION { 72 | go handleRequest(conn) 73 | connections.count++ 74 | } 75 | connections.Unlock() 76 | } 77 | } 78 | 79 | func removeUnusedRequests() { 80 | simorgh.auth.mutex.Lock() 81 | for key, value := range simorgh.auth.accepts { 82 | if value+LIMIT_TIME < time.Now().Unix() { 83 | delete(simorgh.auth.accepts, key) 84 | } 85 | } 86 | simorgh.auth.mutex.Unlock() 87 | time.AfterFunc(5*time.Second, removeUnusedRequests) 88 | } 89 | 90 | func handleRequest(conn net.Conn) { 91 | for { 92 | cmd, err := bufio.NewReader(conn).ReadString('\n') 93 | if err != nil { 94 | conn.Write([]byte("BAD REQUEST")) 95 | conn.Close() 96 | return 97 | } 98 | cmd = strings.TrimSpace(cmd) 99 | 100 | if strings.HasPrefix(cmd, "{") && strings.HasSuffix(cmd, "}") { 101 | cmd = cmd[1 : len(cmd)-1] 102 | var key string = "" 103 | if strings.Contains(cmd, "-&-") { 104 | key = strings.Split(cmd, "-&-")[0] 105 | cmd = cmd[strings.Index(cmd, "-&-")+3:] 106 | } 107 | 108 | if strings.HasPrefix(cmd, "--cred--") && strings.HasSuffix(cmd, "--cred--") { 109 | creds := strings.Replace(cmd, "--cred--", "", -1) 110 | I, A, err := srp.ServerBegin(creds) 111 | s, err := srp.NewServer(I, simorgh.auth.salt, simorgh.auth.verifier, A, SAFE_PRIME_BITS) 112 | if err != nil { 113 | conn.Write([]byte("{--validate--0--validate--}\n")) 114 | continue 115 | } 116 | simorgh.auth.mutex.Lock() 117 | simorgh.auth.keys[conn.RemoteAddr().String()] = s 118 | simorgh.auth.mutex.Unlock() 119 | s_creds := s.Credentials() 120 | conn.Write([]byte("{--cred--" + s_creds + "--cred--}\n")) 121 | } else if strings.HasPrefix(cmd, "--auth--") && strings.HasSuffix(cmd, "--auth--") { 122 | auth := strings.Replace(cmd, "--auth--", "", -1) 123 | s, ok := simorgh.auth.keys[conn.RemoteAddr().String()] 124 | if !ok { 125 | conn.Write([]byte("{--validate--0--validate--}\n")) 126 | continue 127 | } 128 | proof, err := s.ClientOk(auth) 129 | if err != nil { 130 | conn.Write([]byte("{--validate--0--validate--}\n")) 131 | continue 132 | } 133 | simorgh.auth.mutex.Lock() 134 | delete(simorgh.auth.keys, conn.RemoteAddr().String()) 135 | var session []byte = s.RawKey() 136 | var _session []byte = make([]byte, base64.StdEncoding.EncodedLen(len(session))) 137 | base64.StdEncoding.Encode(_session, session) 138 | simorgh.auth.accepts[string(_session)] = time.Now().Unix() 139 | simorgh.auth.mutex.Unlock() 140 | conn.Write([]byte("{--proof--" + proof + "--proof--}\n")) 141 | } 142 | 143 | if _, valid := simorgh.auth.accepts[key]; valid { 144 | simorgh.auth.mutex.Lock() 145 | simorgh.auth.accepts[key] = time.Now().Unix() 146 | simorgh.auth.mutex.Unlock() 147 | } 148 | 149 | if strings.HasPrefix(cmd, "set") { 150 | cmd = strings.Replace(cmd, "set", "", -1) 151 | cmd = strings.TrimSpace(cmd) 152 | parts := strings.Split(cmd, "=") 153 | if len(parts) == 2 { 154 | simorgh.tree.Set(parts[0], parts[1]) 155 | conn.Write([]byte("{OK}\n")) 156 | } else { 157 | conn.Write([]byte("{INVALID}\n")) 158 | } 159 | } else if strings.HasPrefix(cmd, "get") { 160 | cmd = strings.Replace(cmd, "get", "", -1) 161 | cmd = strings.TrimSpace(cmd) 162 | value, mode := simorgh.tree.Get(cmd) 163 | conn.Write([]byte("{" + value.(string) + "-mode:" + mode + "}\n")) 164 | } else if strings.HasPrefix(cmd, "del") { 165 | cmd = strings.Replace(cmd, "del", "", -1) 166 | cmd = strings.TrimSpace(cmd) 167 | simorgh.tree.Del(cmd) 168 | conn.Write([]byte("{OK}\n")) 169 | } else if cmd == "clr" { 170 | n := simorgh.tree.Clr() 171 | conn.Write([]byte("{MEMORY CLEARED (" + strconv.Itoa(n) + ")}\n")) 172 | } else if cmd == "exit" { 173 | connections.Lock() 174 | connections.count-- 175 | connections.Unlock() 176 | simorgh.auth.mutex.Lock() 177 | delete(simorgh.auth.accepts, key) 178 | simorgh.auth.mutex.Unlock() 179 | conn.Write([]byte("{BYE}\n")) 180 | } else { 181 | conn.Write([]byte("{INVALID}\n")) 182 | } 183 | } else { 184 | conn.Write([]byte("{INVALID}\n")) 185 | } 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /vendor/gjson/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Josh Baker 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /vendor/gjson/gjson.go: -------------------------------------------------------------------------------- 1 | // Package gjson provides searching for json strings. 2 | package gjson 3 | 4 | import ( 5 | "reflect" 6 | "strconv" 7 | "unsafe" 8 | 9 | "github.com/tidwall/match" 10 | ) 11 | 12 | // Type is Result type 13 | type Type int 14 | 15 | const ( 16 | // Null is a null json value 17 | Null Type = iota 18 | // False is a json false boolean 19 | False 20 | // Number is json number 21 | Number 22 | // String is a json string 23 | String 24 | // True is a json true boolean 25 | True 26 | // JSON is a raw block of JSON 27 | JSON 28 | ) 29 | 30 | // String returns a string representation of the type. 31 | func (t Type) String() string { 32 | switch t { 33 | default: 34 | return "" 35 | case Null: 36 | return "Null" 37 | case False: 38 | return "False" 39 | case Number: 40 | return "Number" 41 | case String: 42 | return "String" 43 | case True: 44 | return "True" 45 | case JSON: 46 | return "JSON" 47 | } 48 | } 49 | 50 | // Result represents a json value that is returned from Get(). 51 | type Result struct { 52 | // Type is the json type 53 | Type Type 54 | // Raw is the raw json 55 | Raw string 56 | // Str is the json string 57 | Str string 58 | // Num is the json number 59 | Num float64 60 | // Index of raw value in original json, zero means index unknown 61 | Index int 62 | } 63 | 64 | // String returns a string representation of the value. 65 | func (t Result) String() string { 66 | switch t.Type { 67 | default: 68 | return "null" 69 | case False: 70 | return "false" 71 | case Number: 72 | return strconv.FormatFloat(t.Num, 'f', -1, 64) 73 | case String: 74 | return t.Str 75 | case JSON: 76 | return t.Raw 77 | case True: 78 | return "true" 79 | } 80 | } 81 | 82 | // Bool returns an boolean representation. 83 | func (t Result) Bool() bool { 84 | switch t.Type { 85 | default: 86 | return false 87 | case True: 88 | return true 89 | case String: 90 | return t.Str != "" && t.Str != "0" 91 | case Number: 92 | return t.Num != 0 93 | } 94 | } 95 | 96 | // Int returns an integer representation. 97 | func (t Result) Int() int64 { 98 | switch t.Type { 99 | default: 100 | return 0 101 | case True: 102 | return 1 103 | case String: 104 | n, _ := strconv.ParseInt(t.Str, 10, 64) 105 | return n 106 | case Number: 107 | return int64(t.Num) 108 | } 109 | } 110 | 111 | // Uint returns an unsigned integer representation. 112 | func (t Result) Uint() uint64 { 113 | switch t.Type { 114 | default: 115 | return 0 116 | case True: 117 | return 1 118 | case String: 119 | n, _ := strconv.ParseUint(t.Str, 10, 64) 120 | return n 121 | case Number: 122 | return uint64(t.Num) 123 | } 124 | } 125 | 126 | // Float returns an float64 representation. 127 | func (t Result) Float() float64 { 128 | switch t.Type { 129 | default: 130 | return 0 131 | case True: 132 | return 1 133 | case String: 134 | n, _ := strconv.ParseFloat(t.Str, 64) 135 | return n 136 | case Number: 137 | return t.Num 138 | } 139 | } 140 | 141 | // Array returns back an array of values. 142 | // If the result represents a non-existent value, then an empty array will be returned. 143 | // If the result is not a JSON array, the return value will be an array containing one result. 144 | func (t Result) Array() []Result { 145 | if !t.Exists() { 146 | return nil 147 | } 148 | if t.Type != JSON { 149 | return []Result{t} 150 | } 151 | r := t.arrayOrMap('[', false) 152 | return r.a 153 | } 154 | 155 | // ForEach iterates through values. 156 | // If the result represents a non-existent value, then no values will be iterated. 157 | // If the result is an Object, the iterator will pass the key and value of each item. 158 | // If the result is an Array, the iterator will only pass the value of each item. 159 | // If the result is not a JSON array or object, the iterator will pass back one value equal to the result. 160 | func (t Result) ForEach(iterator func(key, value Result) bool) { 161 | if !t.Exists() { 162 | return 163 | } 164 | if t.Type != JSON { 165 | iterator(Result{}, t) 166 | return 167 | } 168 | json := t.Raw 169 | var keys bool 170 | var i int 171 | var key, value Result 172 | for ; i < len(json); i++ { 173 | if json[i] == '{' { 174 | i++ 175 | key.Type = String 176 | keys = true 177 | break 178 | } else if json[i] == '[' { 179 | i++ 180 | break 181 | } 182 | if json[i] > ' ' { 183 | return 184 | } 185 | } 186 | var str string 187 | var vesc bool 188 | var ok bool 189 | for ; i < len(json); i++ { 190 | if keys { 191 | if json[i] != '"' { 192 | continue 193 | } 194 | s := i 195 | i, str, vesc, ok = parseString(json, i+1) 196 | if !ok { 197 | return 198 | } 199 | if vesc { 200 | key.Str = unescape(str[1 : len(str)-1]) 201 | } else { 202 | key.Str = str[1 : len(str)-1] 203 | } 204 | key.Raw = str 205 | key.Index = s 206 | } 207 | for ; i < len(json); i++ { 208 | if json[i] <= ' ' || json[i] == ',' || json[i] == ':' { 209 | continue 210 | } 211 | break 212 | } 213 | s := i 214 | i, value, ok = parseAny(json, i, true) 215 | if !ok { 216 | return 217 | } 218 | value.Index = s 219 | if !iterator(key, value) { 220 | return 221 | } 222 | } 223 | } 224 | 225 | // Map returns back an map of values. The result should be a JSON array. 226 | func (t Result) Map() map[string]Result { 227 | if t.Type != JSON { 228 | return map[string]Result{} 229 | } 230 | r := t.arrayOrMap('{', false) 231 | return r.o 232 | } 233 | 234 | // Get searches result for the specified path. 235 | // The result should be a JSON array or object. 236 | func (t Result) Get(path string) Result { 237 | return Get(t.Raw, path) 238 | } 239 | 240 | type arrayOrMapResult struct { 241 | a []Result 242 | ai []interface{} 243 | o map[string]Result 244 | oi map[string]interface{} 245 | vc byte 246 | } 247 | 248 | func (t Result) arrayOrMap(vc byte, valueize bool) (r arrayOrMapResult) { 249 | var json = t.Raw 250 | var i int 251 | var value Result 252 | var count int 253 | var key Result 254 | if vc == 0 { 255 | for ; i < len(json); i++ { 256 | if json[i] == '{' || json[i] == '[' { 257 | r.vc = json[i] 258 | i++ 259 | break 260 | } 261 | if json[i] > ' ' { 262 | goto end 263 | } 264 | } 265 | } else { 266 | for ; i < len(json); i++ { 267 | if json[i] == vc { 268 | i++ 269 | break 270 | } 271 | if json[i] > ' ' { 272 | goto end 273 | } 274 | } 275 | r.vc = vc 276 | } 277 | if r.vc == '{' { 278 | if valueize { 279 | r.oi = make(map[string]interface{}) 280 | } else { 281 | r.o = make(map[string]Result) 282 | } 283 | } else { 284 | if valueize { 285 | r.ai = make([]interface{}, 0) 286 | } else { 287 | r.a = make([]Result, 0) 288 | } 289 | } 290 | for ; i < len(json); i++ { 291 | if json[i] <= ' ' { 292 | continue 293 | } 294 | // get next value 295 | if json[i] == ']' || json[i] == '}' { 296 | break 297 | } 298 | switch json[i] { 299 | default: 300 | if (json[i] >= '0' && json[i] <= '9') || json[i] == '-' { 301 | value.Type = Number 302 | value.Raw, value.Num = tonum(json[i:]) 303 | } else { 304 | continue 305 | } 306 | case '{', '[': 307 | value.Type = JSON 308 | value.Raw = squash(json[i:]) 309 | case 'n': 310 | value.Type = Null 311 | value.Raw = tolit(json[i:]) 312 | case 't': 313 | value.Type = True 314 | value.Raw = tolit(json[i:]) 315 | case 'f': 316 | value.Type = False 317 | value.Raw = tolit(json[i:]) 318 | case '"': 319 | value.Type = String 320 | value.Raw, value.Str = tostr(json[i:]) 321 | } 322 | i += len(value.Raw) - 1 323 | 324 | if r.vc == '{' { 325 | if count%2 == 0 { 326 | key = value 327 | } else { 328 | if valueize { 329 | r.oi[key.Str] = value.Value() 330 | } else { 331 | r.o[key.Str] = value 332 | } 333 | } 334 | count++ 335 | } else { 336 | if valueize { 337 | r.ai = append(r.ai, value.Value()) 338 | } else { 339 | r.a = append(r.a, value) 340 | } 341 | } 342 | } 343 | end: 344 | return 345 | } 346 | 347 | // Parse parses the json and returns a result. 348 | func Parse(json string) Result { 349 | var value Result 350 | for i := 0; i < len(json); i++ { 351 | if json[i] == '{' || json[i] == '[' { 352 | value.Type = JSON 353 | value.Raw = json[i:] // just take the entire raw 354 | break 355 | } 356 | if json[i] <= ' ' { 357 | continue 358 | } 359 | switch json[i] { 360 | default: 361 | if (json[i] >= '0' && json[i] <= '9') || json[i] == '-' { 362 | value.Type = Number 363 | value.Raw, value.Num = tonum(json[i:]) 364 | } else { 365 | return Result{} 366 | } 367 | case 'n': 368 | value.Type = Null 369 | value.Raw = tolit(json[i:]) 370 | case 't': 371 | value.Type = True 372 | value.Raw = tolit(json[i:]) 373 | case 'f': 374 | value.Type = False 375 | value.Raw = tolit(json[i:]) 376 | case '"': 377 | value.Type = String 378 | value.Raw, value.Str = tostr(json[i:]) 379 | } 380 | break 381 | } 382 | return value 383 | } 384 | 385 | // ParseBytes parses the json and returns a result. 386 | // If working with bytes, this method preferred over Parse(string(data)) 387 | func ParseBytes(json []byte) Result { 388 | return Parse(string(json)) 389 | } 390 | 391 | func squash(json string) string { 392 | // expects that the lead character is a '[' or '{' 393 | // squash the value, ignoring all nested arrays and objects. 394 | // the first '[' or '{' has already been read 395 | depth := 1 396 | for i := 1; i < len(json); i++ { 397 | if json[i] >= '"' && json[i] <= '}' { 398 | switch json[i] { 399 | case '"': 400 | i++ 401 | s2 := i 402 | for ; i < len(json); i++ { 403 | if json[i] > '\\' { 404 | continue 405 | } 406 | if json[i] == '"' { 407 | // look for an escaped slash 408 | if json[i-1] == '\\' { 409 | n := 0 410 | for j := i - 2; j > s2-1; j-- { 411 | if json[j] != '\\' { 412 | break 413 | } 414 | n++ 415 | } 416 | if n%2 == 0 { 417 | continue 418 | } 419 | } 420 | break 421 | } 422 | } 423 | case '{', '[': 424 | depth++ 425 | case '}', ']': 426 | depth-- 427 | if depth == 0 { 428 | return json[:i+1] 429 | } 430 | } 431 | } 432 | } 433 | return json 434 | } 435 | 436 | func tonum(json string) (raw string, num float64) { 437 | for i := 1; i < len(json); i++ { 438 | // less than dash might have valid characters 439 | if json[i] <= '-' { 440 | if json[i] <= ' ' || json[i] == ',' { 441 | // break on whitespace and comma 442 | raw = json[:i] 443 | num, _ = strconv.ParseFloat(raw, 64) 444 | return 445 | } 446 | // could be a '+' or '-'. let's assume so. 447 | continue 448 | } 449 | if json[i] < ']' { 450 | // probably a valid number 451 | continue 452 | } 453 | if json[i] == 'e' || json[i] == 'E' { 454 | // allow for exponential numbers 455 | continue 456 | } 457 | // likely a ']' or '}' 458 | raw = json[:i] 459 | num, _ = strconv.ParseFloat(raw, 64) 460 | return 461 | } 462 | raw = json 463 | num, _ = strconv.ParseFloat(raw, 64) 464 | return 465 | } 466 | 467 | func tolit(json string) (raw string) { 468 | for i := 1; i < len(json); i++ { 469 | if json[i] <= 'a' || json[i] >= 'z' { 470 | return json[:i] 471 | } 472 | } 473 | return json 474 | } 475 | 476 | func tostr(json string) (raw string, str string) { 477 | // expects that the lead character is a '"' 478 | for i := 1; i < len(json); i++ { 479 | if json[i] > '\\' { 480 | continue 481 | } 482 | if json[i] == '"' { 483 | return json[:i+1], json[1:i] 484 | } 485 | if json[i] == '\\' { 486 | i++ 487 | for ; i < len(json); i++ { 488 | if json[i] > '\\' { 489 | continue 490 | } 491 | if json[i] == '"' { 492 | // look for an escaped slash 493 | if json[i-1] == '\\' { 494 | n := 0 495 | for j := i - 2; j > 0; j-- { 496 | if json[j] != '\\' { 497 | break 498 | } 499 | n++ 500 | } 501 | if n%2 == 0 { 502 | continue 503 | } 504 | } 505 | break 506 | } 507 | } 508 | var ret string 509 | if i+1 < len(json) { 510 | ret = json[:i+1] 511 | } else { 512 | ret = json[:i] 513 | } 514 | return ret, unescape(json[1:i]) 515 | } 516 | } 517 | return json, json[1:] 518 | } 519 | 520 | // Exists returns true if value exists. 521 | // 522 | // if gjson.Get(json, "name.last").Exists(){ 523 | // println("value exists") 524 | // } 525 | func (t Result) Exists() bool { 526 | return t.Type != Null || len(t.Raw) != 0 527 | } 528 | 529 | // Value returns one of these types: 530 | // 531 | // bool, for JSON booleans 532 | // float64, for JSON numbers 533 | // Number, for JSON numbers 534 | // string, for JSON string literals 535 | // nil, for JSON null 536 | // 537 | func (t Result) Value() interface{} { 538 | if t.Type == String { 539 | return t.Str 540 | } 541 | switch t.Type { 542 | default: 543 | return nil 544 | case False: 545 | return false 546 | case Number: 547 | return t.Num 548 | case JSON: 549 | r := t.arrayOrMap(0, true) 550 | if r.vc == '{' { 551 | return r.oi 552 | } else if r.vc == '[' { 553 | return r.ai 554 | } 555 | return nil 556 | case True: 557 | return true 558 | } 559 | } 560 | 561 | func parseString(json string, i int) (int, string, bool, bool) { 562 | var s = i 563 | for ; i < len(json); i++ { 564 | if json[i] > '\\' { 565 | continue 566 | } 567 | if json[i] == '"' { 568 | return i + 1, json[s-1 : i+1], false, true 569 | } 570 | if json[i] == '\\' { 571 | i++ 572 | for ; i < len(json); i++ { 573 | if json[i] > '\\' { 574 | continue 575 | } 576 | if json[i] == '"' { 577 | // look for an escaped slash 578 | if json[i-1] == '\\' { 579 | n := 0 580 | for j := i - 2; j > 0; j-- { 581 | if json[j] != '\\' { 582 | break 583 | } 584 | n++ 585 | } 586 | if n%2 == 0 { 587 | continue 588 | } 589 | } 590 | return i + 1, json[s-1 : i+1], true, true 591 | } 592 | } 593 | break 594 | } 595 | } 596 | return i, json[s-1:], false, false 597 | } 598 | 599 | func parseNumber(json string, i int) (int, string) { 600 | var s = i 601 | i++ 602 | for ; i < len(json); i++ { 603 | if json[i] <= ' ' || json[i] == ',' || json[i] == ']' || json[i] == '}' { 604 | return i, json[s:i] 605 | } 606 | } 607 | return i, json[s:] 608 | } 609 | 610 | func parseLiteral(json string, i int) (int, string) { 611 | var s = i 612 | i++ 613 | for ; i < len(json); i++ { 614 | if json[i] < 'a' || json[i] > 'z' { 615 | return i, json[s:i] 616 | } 617 | } 618 | return i, json[s:] 619 | } 620 | 621 | type arrayPathResult struct { 622 | part string 623 | path string 624 | more bool 625 | alogok bool 626 | arrch bool 627 | alogkey string 628 | query struct { 629 | on bool 630 | path string 631 | op string 632 | value string 633 | all bool 634 | } 635 | } 636 | 637 | func parseArrayPath(path string) (r arrayPathResult) { 638 | for i := 0; i < len(path); i++ { 639 | if path[i] == '.' { 640 | r.part = path[:i] 641 | r.path = path[i+1:] 642 | r.more = true 643 | return 644 | } 645 | if path[i] == '#' { 646 | r.arrch = true 647 | if i == 0 && len(path) > 1 { 648 | if path[1] == '.' { 649 | r.alogok = true 650 | r.alogkey = path[2:] 651 | r.path = path[:1] 652 | } else if path[1] == '[' { 653 | r.query.on = true 654 | // query 655 | i += 2 656 | // whitespace 657 | for ; i < len(path); i++ { 658 | if path[i] > ' ' { 659 | break 660 | } 661 | } 662 | s := i 663 | for ; i < len(path); i++ { 664 | if path[i] <= ' ' || 665 | path[i] == '!' || 666 | path[i] == '=' || 667 | path[i] == '<' || 668 | path[i] == '>' || 669 | path[i] == '%' || 670 | path[i] == ']' { 671 | break 672 | } 673 | } 674 | r.query.path = path[s:i] 675 | // whitespace 676 | for ; i < len(path); i++ { 677 | if path[i] > ' ' { 678 | break 679 | } 680 | } 681 | if i < len(path) { 682 | s = i 683 | if path[i] == '!' { 684 | if i < len(path)-1 && path[i+1] == '=' { 685 | i++ 686 | } 687 | } else if path[i] == '<' || path[i] == '>' { 688 | if i < len(path)-1 && path[i+1] == '=' { 689 | i++ 690 | } 691 | } else if path[i] == '=' { 692 | if i < len(path)-1 && path[i+1] == '=' { 693 | s++ 694 | i++ 695 | } 696 | } 697 | i++ 698 | r.query.op = path[s:i] 699 | // whitespace 700 | for ; i < len(path); i++ { 701 | if path[i] > ' ' { 702 | break 703 | } 704 | } 705 | s = i 706 | for ; i < len(path); i++ { 707 | if path[i] == '"' { 708 | i++ 709 | s2 := i 710 | for ; i < len(path); i++ { 711 | if path[i] > '\\' { 712 | continue 713 | } 714 | if path[i] == '"' { 715 | // look for an escaped slash 716 | if path[i-1] == '\\' { 717 | n := 0 718 | for j := i - 2; j > s2-1; j-- { 719 | if path[j] != '\\' { 720 | break 721 | } 722 | n++ 723 | } 724 | if n%2 == 0 { 725 | continue 726 | } 727 | } 728 | break 729 | } 730 | } 731 | } else if path[i] == ']' { 732 | if i+1 < len(path) && path[i+1] == '#' { 733 | r.query.all = true 734 | } 735 | break 736 | } 737 | } 738 | if i > len(path) { 739 | i = len(path) 740 | } 741 | v := path[s:i] 742 | for len(v) > 0 && v[len(v)-1] <= ' ' { 743 | v = v[:len(v)-1] 744 | } 745 | r.query.value = v 746 | } 747 | } 748 | } 749 | continue 750 | } 751 | } 752 | r.part = path 753 | r.path = "" 754 | return 755 | } 756 | 757 | type objectPathResult struct { 758 | part string 759 | path string 760 | wild bool 761 | more bool 762 | } 763 | 764 | func parseObjectPath(path string) (r objectPathResult) { 765 | for i := 0; i < len(path); i++ { 766 | if path[i] == '.' { 767 | r.part = path[:i] 768 | r.path = path[i+1:] 769 | r.more = true 770 | return 771 | } 772 | if path[i] == '*' || path[i] == '?' { 773 | r.wild = true 774 | continue 775 | } 776 | if path[i] == '\\' { 777 | // go into escape mode. this is a slower path that 778 | // strips off the escape character from the part. 779 | epart := []byte(path[:i]) 780 | i++ 781 | if i < len(path) { 782 | epart = append(epart, path[i]) 783 | i++ 784 | for ; i < len(path); i++ { 785 | if path[i] == '\\' { 786 | i++ 787 | if i < len(path) { 788 | epart = append(epart, path[i]) 789 | } 790 | continue 791 | } else if path[i] == '.' { 792 | r.part = string(epart) 793 | r.path = path[i+1:] 794 | r.more = true 795 | return 796 | } else if path[i] == '*' || path[i] == '?' { 797 | r.wild = true 798 | } 799 | epart = append(epart, path[i]) 800 | } 801 | } 802 | // append the last part 803 | r.part = string(epart) 804 | return 805 | } 806 | } 807 | r.part = path 808 | return 809 | } 810 | 811 | func parseSquash(json string, i int) (int, string) { 812 | // expects that the lead character is a '[' or '{' 813 | // squash the value, ignoring all nested arrays and objects. 814 | // the first '[' or '{' has already been read 815 | s := i 816 | i++ 817 | depth := 1 818 | for ; i < len(json); i++ { 819 | if json[i] >= '"' && json[i] <= '}' { 820 | switch json[i] { 821 | case '"': 822 | i++ 823 | s2 := i 824 | for ; i < len(json); i++ { 825 | if json[i] > '\\' { 826 | continue 827 | } 828 | if json[i] == '"' { 829 | // look for an escaped slash 830 | if json[i-1] == '\\' { 831 | n := 0 832 | for j := i - 2; j > s2-1; j-- { 833 | if json[j] != '\\' { 834 | break 835 | } 836 | n++ 837 | } 838 | if n%2 == 0 { 839 | continue 840 | } 841 | } 842 | break 843 | } 844 | } 845 | case '{', '[': 846 | depth++ 847 | case '}', ']': 848 | depth-- 849 | if depth == 0 { 850 | i++ 851 | return i, json[s:i] 852 | } 853 | } 854 | } 855 | } 856 | return i, json[s:] 857 | } 858 | 859 | func parseObject(c *parseContext, i int, path string) (int, bool) { 860 | var pmatch, kesc, vesc, ok, hit bool 861 | var key, val string 862 | rp := parseObjectPath(path) 863 | for i < len(c.json) { 864 | for ; i < len(c.json); i++ { 865 | if c.json[i] == '"' { 866 | // parse_key_string 867 | // this is slightly different from getting s string value 868 | // because we don't need the outer quotes. 869 | i++ 870 | var s = i 871 | for ; i < len(c.json); i++ { 872 | if c.json[i] > '\\' { 873 | continue 874 | } 875 | if c.json[i] == '"' { 876 | i, key, kesc, ok = i+1, c.json[s:i], false, true 877 | goto parse_key_string_done 878 | } 879 | if c.json[i] == '\\' { 880 | i++ 881 | for ; i < len(c.json); i++ { 882 | if c.json[i] > '\\' { 883 | continue 884 | } 885 | if c.json[i] == '"' { 886 | // look for an escaped slash 887 | if c.json[i-1] == '\\' { 888 | n := 0 889 | for j := i - 2; j > 0; j-- { 890 | if c.json[j] != '\\' { 891 | break 892 | } 893 | n++ 894 | } 895 | if n%2 == 0 { 896 | continue 897 | } 898 | } 899 | i, key, kesc, ok = i+1, c.json[s:i], true, true 900 | goto parse_key_string_done 901 | } 902 | } 903 | break 904 | } 905 | } 906 | i, key, kesc, ok = i, c.json[s:], false, false 907 | parse_key_string_done: 908 | break 909 | } 910 | if c.json[i] == '}' { 911 | return i + 1, false 912 | } 913 | } 914 | if !ok { 915 | return i, false 916 | } 917 | if rp.wild { 918 | if kesc { 919 | pmatch = match.Match(unescape(key), rp.part) 920 | } else { 921 | pmatch = match.Match(key, rp.part) 922 | } 923 | } else { 924 | if kesc { 925 | pmatch = rp.part == unescape(key) 926 | } else { 927 | pmatch = rp.part == key 928 | } 929 | } 930 | hit = pmatch && !rp.more 931 | for ; i < len(c.json); i++ { 932 | switch c.json[i] { 933 | default: 934 | continue 935 | case '"': 936 | i++ 937 | i, val, vesc, ok = parseString(c.json, i) 938 | if !ok { 939 | return i, false 940 | } 941 | if hit { 942 | if vesc { 943 | c.value.Str = unescape(val[1 : len(val)-1]) 944 | } else { 945 | c.value.Str = val[1 : len(val)-1] 946 | } 947 | c.value.Raw = val 948 | c.value.Type = String 949 | return i, true 950 | } 951 | case '{': 952 | if pmatch && !hit { 953 | i, hit = parseObject(c, i+1, rp.path) 954 | if hit { 955 | return i, true 956 | } 957 | } else { 958 | i, val = parseSquash(c.json, i) 959 | if hit { 960 | c.value.Raw = val 961 | c.value.Type = JSON 962 | return i, true 963 | } 964 | } 965 | case '[': 966 | if pmatch && !hit { 967 | i, hit = parseArray(c, i+1, rp.path) 968 | if hit { 969 | return i, true 970 | } 971 | } else { 972 | i, val = parseSquash(c.json, i) 973 | if hit { 974 | c.value.Raw = val 975 | c.value.Type = JSON 976 | return i, true 977 | } 978 | } 979 | case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': 980 | i, val = parseNumber(c.json, i) 981 | if hit { 982 | c.value.Raw = val 983 | c.value.Type = Number 984 | c.value.Num, _ = strconv.ParseFloat(val, 64) 985 | return i, true 986 | } 987 | case 't', 'f', 'n': 988 | vc := c.json[i] 989 | i, val = parseLiteral(c.json, i) 990 | if hit { 991 | c.value.Raw = val 992 | switch vc { 993 | case 't': 994 | c.value.Type = True 995 | case 'f': 996 | c.value.Type = False 997 | } 998 | return i, true 999 | } 1000 | } 1001 | break 1002 | } 1003 | } 1004 | return i, false 1005 | } 1006 | func queryMatches(rp *arrayPathResult, value Result) bool { 1007 | rpv := rp.query.value 1008 | if len(rpv) > 2 && rpv[0] == '"' && rpv[len(rpv)-1] == '"' { 1009 | rpv = rpv[1 : len(rpv)-1] 1010 | } 1011 | switch value.Type { 1012 | case String: 1013 | switch rp.query.op { 1014 | case "=": 1015 | return value.Str == rpv 1016 | case "!=": 1017 | return value.Str != rpv 1018 | case "<": 1019 | return value.Str < rpv 1020 | case "<=": 1021 | return value.Str <= rpv 1022 | case ">": 1023 | return value.Str > rpv 1024 | case ">=": 1025 | return value.Str >= rpv 1026 | case "%": 1027 | return match.Match(value.Str, rpv) 1028 | } 1029 | case Number: 1030 | rpvn, _ := strconv.ParseFloat(rpv, 64) 1031 | switch rp.query.op { 1032 | case "=": 1033 | return value.Num == rpvn 1034 | case "!=": 1035 | return value.Num == rpvn 1036 | case "<": 1037 | return value.Num < rpvn 1038 | case "<=": 1039 | return value.Num <= rpvn 1040 | case ">": 1041 | return value.Num > rpvn 1042 | case ">=": 1043 | return value.Num >= rpvn 1044 | } 1045 | case True: 1046 | switch rp.query.op { 1047 | case "=": 1048 | return rpv == "true" 1049 | case "!=": 1050 | return rpv != "true" 1051 | case ">": 1052 | return rpv == "false" 1053 | case ">=": 1054 | return true 1055 | } 1056 | case False: 1057 | switch rp.query.op { 1058 | case "=": 1059 | return rpv == "false" 1060 | case "!=": 1061 | return rpv != "false" 1062 | case "<": 1063 | return rpv == "true" 1064 | case "<=": 1065 | return true 1066 | } 1067 | } 1068 | return false 1069 | } 1070 | func parseArray(c *parseContext, i int, path string) (int, bool) { 1071 | var pmatch, vesc, ok, hit bool 1072 | var val string 1073 | var h int 1074 | var alog []int 1075 | var partidx int 1076 | var multires []byte 1077 | rp := parseArrayPath(path) 1078 | if !rp.arrch { 1079 | n, err := strconv.ParseUint(rp.part, 10, 64) 1080 | if err != nil { 1081 | partidx = -1 1082 | } else { 1083 | partidx = int(n) 1084 | } 1085 | } 1086 | for i < len(c.json) { 1087 | if !rp.arrch { 1088 | pmatch = partidx == h 1089 | hit = pmatch && !rp.more 1090 | } 1091 | h++ 1092 | if rp.alogok { 1093 | alog = append(alog, i) 1094 | } 1095 | for ; i < len(c.json); i++ { 1096 | switch c.json[i] { 1097 | default: 1098 | continue 1099 | case '"': 1100 | i++ 1101 | i, val, vesc, ok = parseString(c.json, i) 1102 | if !ok { 1103 | return i, false 1104 | } 1105 | if hit { 1106 | if rp.alogok { 1107 | break 1108 | } 1109 | if vesc { 1110 | c.value.Str = unescape(val[1 : len(val)-1]) 1111 | } else { 1112 | c.value.Str = val[1 : len(val)-1] 1113 | } 1114 | c.value.Raw = val 1115 | c.value.Type = String 1116 | return i, true 1117 | } 1118 | case '{': 1119 | if pmatch && !hit { 1120 | i, hit = parseObject(c, i+1, rp.path) 1121 | if hit { 1122 | if rp.alogok { 1123 | break 1124 | } 1125 | return i, true 1126 | } 1127 | } else { 1128 | i, val = parseSquash(c.json, i) 1129 | if rp.query.on { 1130 | res := Get(val, rp.query.path) 1131 | if queryMatches(&rp, res) { 1132 | if rp.more { 1133 | res = Get(val, rp.path) 1134 | } else { 1135 | res = Result{Raw: val, Type: JSON} 1136 | } 1137 | if rp.query.all { 1138 | if len(multires) == 0 { 1139 | multires = append(multires, '[') 1140 | } else { 1141 | multires = append(multires, ',') 1142 | } 1143 | multires = append(multires, res.Raw...) 1144 | } else { 1145 | c.value = res 1146 | return i, true 1147 | } 1148 | } 1149 | } else if hit { 1150 | if rp.alogok { 1151 | break 1152 | } 1153 | c.value.Raw = val 1154 | c.value.Type = JSON 1155 | return i, true 1156 | } 1157 | } 1158 | case '[': 1159 | if pmatch && !hit { 1160 | i, hit = parseArray(c, i+1, rp.path) 1161 | if hit { 1162 | if rp.alogok { 1163 | break 1164 | } 1165 | return i, true 1166 | } 1167 | } else { 1168 | i, val = parseSquash(c.json, i) 1169 | if hit { 1170 | if rp.alogok { 1171 | break 1172 | } 1173 | c.value.Raw = val 1174 | c.value.Type = JSON 1175 | return i, true 1176 | } 1177 | } 1178 | case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': 1179 | i, val = parseNumber(c.json, i) 1180 | if hit { 1181 | if rp.alogok { 1182 | break 1183 | } 1184 | c.value.Raw = val 1185 | c.value.Type = Number 1186 | c.value.Num, _ = strconv.ParseFloat(val, 64) 1187 | return i, true 1188 | } 1189 | case 't', 'f', 'n': 1190 | vc := c.json[i] 1191 | i, val = parseLiteral(c.json, i) 1192 | if hit { 1193 | if rp.alogok { 1194 | break 1195 | } 1196 | c.value.Raw = val 1197 | switch vc { 1198 | case 't': 1199 | c.value.Type = True 1200 | case 'f': 1201 | c.value.Type = False 1202 | } 1203 | return i, true 1204 | } 1205 | case ']': 1206 | if rp.arrch && rp.part == "#" { 1207 | if rp.alogok { 1208 | var jsons = make([]byte, 0, 64) 1209 | jsons = append(jsons, '[') 1210 | for j, k := 0, 0; j < len(alog); j++ { 1211 | res := Get(c.json[alog[j]:], rp.alogkey) 1212 | if res.Exists() { 1213 | if k > 0 { 1214 | jsons = append(jsons, ',') 1215 | } 1216 | jsons = append(jsons, []byte(res.Raw)...) 1217 | k++ 1218 | } 1219 | } 1220 | jsons = append(jsons, ']') 1221 | c.value.Type = JSON 1222 | c.value.Raw = string(jsons) 1223 | return i + 1, true 1224 | } else { 1225 | if rp.alogok { 1226 | break 1227 | } 1228 | c.value.Raw = val 1229 | c.value.Type = Number 1230 | c.value.Num = float64(h - 1) 1231 | c.calcd = true 1232 | return i + 1, true 1233 | } 1234 | } 1235 | if len(multires) > 0 && !c.value.Exists() { 1236 | c.value = Result{ 1237 | Raw: string(append(multires, ']')), 1238 | Type: JSON, 1239 | } 1240 | } 1241 | return i + 1, false 1242 | } 1243 | break 1244 | } 1245 | } 1246 | return i, false 1247 | } 1248 | 1249 | type parseContext struct { 1250 | json string 1251 | value Result 1252 | calcd bool 1253 | } 1254 | 1255 | // Get searches json for the specified path. 1256 | // A path is in dot syntax, such as "name.last" or "age". 1257 | // This function expects that the json is well-formed, and does not validate. 1258 | // Invalid json will not panic, but it may return back unexpected results. 1259 | // When the value is found it's returned immediately. 1260 | // 1261 | // A path is a series of keys searated by a dot. 1262 | // A key may contain special wildcard characters '*' and '?'. 1263 | // To access an array value use the index as the key. 1264 | // To get the number of elements in an array or to access a child path, use the '#' character. 1265 | // The dot and wildcard character can be escaped with '\'. 1266 | // 1267 | // { 1268 | // "name": {"first": "Tom", "last": "Anderson"}, 1269 | // "age":37, 1270 | // "children": ["Sara","Alex","Jack"], 1271 | // "friends": [ 1272 | // {"first": "James", "last": "Murphy"}, 1273 | // {"first": "Roger", "last": "Craig"} 1274 | // ] 1275 | // } 1276 | // "name.last" >> "Anderson" 1277 | // "age" >> 37 1278 | // "children" >> ["Sara","Alex","Jack"] 1279 | // "children.#" >> 3 1280 | // "children.1" >> "Alex" 1281 | // "child*.2" >> "Jack" 1282 | // "c?ildren.0" >> "Sara" 1283 | // "friends.#.first" >> ["James","Roger"] 1284 | // 1285 | func Get(json, path string) Result { 1286 | var i int 1287 | var c = &parseContext{json: json} 1288 | for ; i < len(c.json); i++ { 1289 | if c.json[i] == '{' { 1290 | i++ 1291 | parseObject(c, i, path) 1292 | break 1293 | } 1294 | if c.json[i] == '[' { 1295 | i++ 1296 | parseArray(c, i, path) 1297 | break 1298 | } 1299 | } 1300 | if len(c.value.Raw) > 0 && !c.calcd { 1301 | jhdr := *(*reflect.StringHeader)(unsafe.Pointer(&json)) 1302 | rhdr := *(*reflect.StringHeader)(unsafe.Pointer(&(c.value.Raw))) 1303 | c.value.Index = int(rhdr.Data - jhdr.Data) 1304 | if c.value.Index < 0 || c.value.Index >= len(json) { 1305 | c.value.Index = 0 1306 | } 1307 | } 1308 | return c.value 1309 | } 1310 | func fromBytesGet(result Result) Result { 1311 | // safely get the string headers 1312 | rawhi := *(*reflect.StringHeader)(unsafe.Pointer(&result.Raw)) 1313 | strhi := *(*reflect.StringHeader)(unsafe.Pointer(&result.Str)) 1314 | // create byte slice headers 1315 | rawh := reflect.SliceHeader{Data: rawhi.Data, Len: rawhi.Len} 1316 | strh := reflect.SliceHeader{Data: strhi.Data, Len: strhi.Len} 1317 | if strh.Data == 0 { 1318 | // str is nil 1319 | if rawh.Data == 0 { 1320 | // raw is nil 1321 | result.Raw = "" 1322 | } else { 1323 | // raw has data, safely copy the slice header to a string 1324 | result.Raw = string(*(*[]byte)(unsafe.Pointer(&rawh))) 1325 | } 1326 | result.Str = "" 1327 | } else if rawh.Data == 0 { 1328 | // raw is nil 1329 | result.Raw = "" 1330 | // str has data, safely copy the slice header to a string 1331 | result.Str = string(*(*[]byte)(unsafe.Pointer(&strh))) 1332 | } else if strh.Data >= rawh.Data && 1333 | int(strh.Data)+strh.Len <= int(rawh.Data)+rawh.Len { 1334 | // Str is a substring of Raw. 1335 | start := int(strh.Data - rawh.Data) 1336 | // safely copy the raw slice header 1337 | result.Raw = string(*(*[]byte)(unsafe.Pointer(&rawh))) 1338 | // substring the raw 1339 | result.Str = result.Raw[start : start+strh.Len] 1340 | } else { 1341 | // safely copy both the raw and str slice headers to strings 1342 | result.Raw = string(*(*[]byte)(unsafe.Pointer(&rawh))) 1343 | result.Str = string(*(*[]byte)(unsafe.Pointer(&strh))) 1344 | } 1345 | return result 1346 | } 1347 | 1348 | // GetBytes searches json for the specified path. 1349 | // If working with bytes, this method preferred over Get(string(data), path) 1350 | func GetBytes(json []byte, path string) Result { 1351 | var result Result 1352 | if json != nil { 1353 | // unsafe cast to string 1354 | result = Get(*(*string)(unsafe.Pointer(&json)), path) 1355 | result = fromBytesGet(result) 1356 | } 1357 | return result 1358 | } 1359 | 1360 | // unescape unescapes a string 1361 | func unescape(json string) string { //, error) { 1362 | var str = make([]byte, 0, len(json)) 1363 | for i := 0; i < len(json); i++ { 1364 | switch { 1365 | default: 1366 | str = append(str, json[i]) 1367 | case json[i] < ' ': 1368 | return "" //, errors.New("invalid character in string") 1369 | case json[i] == '\\': 1370 | i++ 1371 | if i >= len(json) { 1372 | return "" //, errors.New("invalid escape sequence") 1373 | } 1374 | switch json[i] { 1375 | default: 1376 | return "" //, errors.New("invalid escape sequence") 1377 | case '\\': 1378 | str = append(str, '\\') 1379 | case '/': 1380 | str = append(str, '/') 1381 | case 'b': 1382 | str = append(str, '\b') 1383 | case 'f': 1384 | str = append(str, '\f') 1385 | case 'n': 1386 | str = append(str, '\n') 1387 | case 'r': 1388 | str = append(str, '\r') 1389 | case 't': 1390 | str = append(str, '\t') 1391 | case '"': 1392 | str = append(str, '"') 1393 | case 'u': 1394 | if i+5 > len(json) { 1395 | return "" //, errors.New("invalid escape sequence") 1396 | } 1397 | i++ 1398 | // extract the codepoint 1399 | var code int 1400 | for j := i; j < i+4; j++ { 1401 | switch { 1402 | default: 1403 | return "" //, errors.New("invalid escape sequence") 1404 | case json[j] >= '0' && json[j] <= '9': 1405 | code += (int(json[j]) - '0') << uint(12-(j-i)*4) 1406 | case json[j] >= 'a' && json[j] <= 'f': 1407 | code += (int(json[j]) - 'a' + 10) << uint(12-(j-i)*4) 1408 | case json[j] >= 'a' && json[j] <= 'f': 1409 | code += (int(json[j]) - 'a' + 10) << uint(12-(j-i)*4) 1410 | } 1411 | } 1412 | str = append(str, []byte(string(code))...) 1413 | i += 3 // only 3 because we will increment on the for-loop 1414 | } 1415 | } 1416 | } 1417 | return string(str) //, nil 1418 | } 1419 | 1420 | // Less return true if a token is less than another token. 1421 | // The caseSensitive paramater is used when the tokens are Strings. 1422 | // The order when comparing two different type is: 1423 | // 1424 | // Null < False < Number < String < True < JSON 1425 | // 1426 | func (t Result) Less(token Result, caseSensitive bool) bool { 1427 | if t.Type < token.Type { 1428 | return true 1429 | } 1430 | if t.Type > token.Type { 1431 | return false 1432 | } 1433 | if t.Type == String { 1434 | if caseSensitive { 1435 | return t.Str < token.Str 1436 | } 1437 | return stringLessInsensitive(t.Str, token.Str) 1438 | } 1439 | if t.Type == Number { 1440 | return t.Num < token.Num 1441 | } 1442 | return t.Raw < token.Raw 1443 | } 1444 | 1445 | func stringLessInsensitive(a, b string) bool { 1446 | for i := 0; i < len(a) && i < len(b); i++ { 1447 | if a[i] >= 'A' && a[i] <= 'Z' { 1448 | if b[i] >= 'A' && b[i] <= 'Z' { 1449 | // both are uppercase, do nothing 1450 | if a[i] < b[i] { 1451 | return true 1452 | } else if a[i] > b[i] { 1453 | return false 1454 | } 1455 | } else { 1456 | // a is uppercase, convert a to lowercase 1457 | if a[i]+32 < b[i] { 1458 | return true 1459 | } else if a[i]+32 > b[i] { 1460 | return false 1461 | } 1462 | } 1463 | } else if b[i] >= 'A' && b[i] <= 'Z' { 1464 | // b is uppercase, convert b to lowercase 1465 | if a[i] < b[i]+32 { 1466 | return true 1467 | } else if a[i] > b[i]+32 { 1468 | return false 1469 | } 1470 | } else { 1471 | // neither are uppercase 1472 | if a[i] < b[i] { 1473 | return true 1474 | } else if a[i] > b[i] { 1475 | return false 1476 | } 1477 | } 1478 | } 1479 | return len(a) < len(b) 1480 | } 1481 | 1482 | // parseAny parses the next value from a json string. 1483 | // A Result is returned when the hit param is set. 1484 | // The return values are (i int, res Result, ok bool) 1485 | func parseAny(json string, i int, hit bool) (int, Result, bool) { 1486 | var res Result 1487 | var val string 1488 | for ; i < len(json); i++ { 1489 | if json[i] == '{' || json[i] == '[' { 1490 | i, val = parseSquash(json, i) 1491 | if hit { 1492 | res.Raw = val 1493 | res.Type = JSON 1494 | } 1495 | return i, res, true 1496 | } 1497 | if json[i] <= ' ' { 1498 | continue 1499 | } 1500 | switch json[i] { 1501 | case '"': 1502 | i++ 1503 | var vesc bool 1504 | var ok bool 1505 | i, val, vesc, ok = parseString(json, i) 1506 | if !ok { 1507 | return i, res, false 1508 | } 1509 | if hit { 1510 | res.Type = String 1511 | res.Raw = val 1512 | if vesc { 1513 | res.Str = unescape(val[1 : len(val)-1]) 1514 | } else { 1515 | res.Str = val[1 : len(val)-1] 1516 | } 1517 | } 1518 | return i, res, true 1519 | case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': 1520 | i, val = parseNumber(json, i) 1521 | if hit { 1522 | res.Raw = val 1523 | res.Type = Number 1524 | res.Num, _ = strconv.ParseFloat(val, 64) 1525 | } 1526 | return i, res, true 1527 | case 't', 'f', 'n': 1528 | vc := json[i] 1529 | i, val = parseLiteral(json, i) 1530 | if hit { 1531 | res.Raw = val 1532 | switch vc { 1533 | case 't': 1534 | res.Type = True 1535 | case 'f': 1536 | res.Type = False 1537 | } 1538 | return i, res, true 1539 | } 1540 | } 1541 | } 1542 | return i, res, false 1543 | } 1544 | 1545 | var ( // used for testing 1546 | testWatchForFallback bool 1547 | testLastWasFallback bool 1548 | ) 1549 | 1550 | // areSimplePaths returns true if all the paths are simple enough 1551 | // to parse quickly for GetMany(). Allows alpha-numeric, dots, 1552 | // underscores, and the dollar sign. It does not allow non-alnum, 1553 | // escape characters, or keys which start with a numbers. 1554 | // For example: 1555 | // "name.last" == OK 1556 | // "user.id0" == OK 1557 | // "user.ID" == OK 1558 | // "user.first_name" == OK 1559 | // "user.firstName" == OK 1560 | // "user.0item" == BAD 1561 | // "user.#id" == BAD 1562 | // "user\.name" == BAD 1563 | func areSimplePaths(paths []string) bool { 1564 | for _, path := range paths { 1565 | var fi int // first key index, for keys with numeric prefix 1566 | for i := 0; i < len(path); i++ { 1567 | if path[i] >= 'a' && path[i] <= 'z' { 1568 | // a-z is likely to be the highest frequency charater. 1569 | continue 1570 | } 1571 | if path[i] == '.' { 1572 | fi = i + 1 1573 | continue 1574 | } 1575 | if path[i] >= 'A' && path[i] <= 'Z' { 1576 | continue 1577 | } 1578 | if path[i] == '_' || path[i] == '$' { 1579 | continue 1580 | } 1581 | if i > fi && path[i] >= '0' && path[i] <= '9' { 1582 | continue 1583 | } 1584 | return false 1585 | } 1586 | } 1587 | return true 1588 | } 1589 | 1590 | // GetMany searches json for the multiple paths. 1591 | // The return value is a Result array where the number of items 1592 | // will be equal to the number of input paths. 1593 | func GetMany(json string, paths ...string) []Result { 1594 | if len(paths) < 4 { 1595 | if testWatchForFallback { 1596 | testLastWasFallback = false 1597 | } 1598 | switch len(paths) { 1599 | case 0: 1600 | // return nil when no paths are specified. 1601 | return nil 1602 | case 1: 1603 | return []Result{Get(json, paths[0])} 1604 | case 2: 1605 | return []Result{Get(json, paths[0]), Get(json, paths[1])} 1606 | case 3: 1607 | return []Result{Get(json, paths[0]), Get(json, paths[1]), Get(json, paths[2])} 1608 | } 1609 | } 1610 | var results []Result 1611 | var ok bool 1612 | var i int 1613 | if len(paths) > 512 { 1614 | // we can only support up to 512 paths. Is that too many? 1615 | goto fallback 1616 | } 1617 | if !areSimplePaths(paths) { 1618 | // If there is even one path that is not considered "simple" then 1619 | // we need to use the fallback method. 1620 | goto fallback 1621 | } 1622 | // locate the object token. 1623 | for ; i < len(json); i++ { 1624 | if json[i] == '{' { 1625 | i++ 1626 | break 1627 | } 1628 | if json[i] <= ' ' { 1629 | continue 1630 | } 1631 | goto fallback 1632 | } 1633 | // use the call function table. 1634 | if len(paths) <= 8 { 1635 | results, ok = getMany8(json, i, paths) 1636 | } else if len(paths) <= 16 { 1637 | results, ok = getMany16(json, i, paths) 1638 | } else if len(paths) <= 32 { 1639 | results, ok = getMany32(json, i, paths) 1640 | } else if len(paths) <= 64 { 1641 | results, ok = getMany64(json, i, paths) 1642 | } else if len(paths) <= 128 { 1643 | results, ok = getMany128(json, i, paths) 1644 | } else if len(paths) <= 256 { 1645 | results, ok = getMany256(json, i, paths) 1646 | } else if len(paths) <= 512 { 1647 | results, ok = getMany512(json, i, paths) 1648 | } 1649 | if !ok { 1650 | // there was some fault while parsing. we should try the 1651 | // fallback method. This could result in performance 1652 | // degregation in some cases. 1653 | goto fallback 1654 | } 1655 | if testWatchForFallback { 1656 | testLastWasFallback = false 1657 | } 1658 | return results 1659 | fallback: 1660 | results = results[:0] 1661 | for i := 0; i < len(paths); i++ { 1662 | results = append(results, Get(json, paths[i])) 1663 | } 1664 | if testWatchForFallback { 1665 | testLastWasFallback = true 1666 | } 1667 | return results 1668 | } 1669 | 1670 | // GetManyBytes searches json for the specified path. 1671 | // If working with bytes, this method preferred over 1672 | // GetMany(string(data), paths...) 1673 | func GetManyBytes(json []byte, paths ...string) []Result { 1674 | if json == nil { 1675 | return GetMany("", paths...) 1676 | } 1677 | results := GetMany(*(*string)(unsafe.Pointer(&json)), paths...) 1678 | for i := range results { 1679 | results[i] = fromBytesGet(results[i]) 1680 | } 1681 | return results 1682 | } 1683 | 1684 | // parseGetMany parses a json object for keys that match against the callers 1685 | // paths. It's a best-effort attempt and quickly locating and assigning the 1686 | // values to the []Result array. If there are failures such as bad json, or 1687 | // invalid input paths, or too much recursion, the function will exit with a 1688 | // return value of 'false'. 1689 | func parseGetMany( 1690 | json string, i int, 1691 | level uint, kplen int, 1692 | paths []string, completed []bool, matches []uint64, results []Result, 1693 | ) (int, bool) { 1694 | if level > 62 { 1695 | // The recursion level is limited because the matches []uint64 1696 | // array cannot handle more the 64-bits. 1697 | return i, false 1698 | } 1699 | // At this point the last character read was a '{'. 1700 | // Read all object keys and try to match against the paths. 1701 | var key string 1702 | var val string 1703 | var vesc, ok bool 1704 | next_key: 1705 | for ; i < len(json); i++ { 1706 | if json[i] == '"' { 1707 | // read the key 1708 | i, val, vesc, ok = parseString(json, i+1) 1709 | if !ok { 1710 | return i, false 1711 | } 1712 | if vesc { 1713 | // the value is escaped 1714 | key = unescape(val[1 : len(val)-1]) 1715 | } else { 1716 | // just a plain old ascii key 1717 | key = val[1 : len(val)-1] 1718 | } 1719 | var hasMatch bool 1720 | var parsedVal bool 1721 | var valOrgIndex int 1722 | var valPathIndex int 1723 | for j := 0; j < len(key); j++ { 1724 | if key[j] == '.' { 1725 | // we need to look for keys with dot and ignore them. 1726 | if i, _, ok = parseAny(json, i, false); !ok { 1727 | return i, false 1728 | } 1729 | continue next_key 1730 | } 1731 | } 1732 | var usedPaths int 1733 | // loop through paths and look for matches 1734 | for j := 0; j < len(paths); j++ { 1735 | if completed[j] { 1736 | usedPaths++ 1737 | // ignore completed paths 1738 | continue 1739 | } 1740 | if level > 0 && (matches[j]>>(level-1))&1 == 0 { 1741 | // ignore unmatched paths 1742 | usedPaths++ 1743 | continue 1744 | } 1745 | 1746 | // try to match the key to the path 1747 | // this is spaghetti code but the idea is to minimize 1748 | // calls and variable assignments when comparing the 1749 | // key to paths 1750 | if len(paths[j])-kplen >= len(key) { 1751 | i, k := kplen, 0 1752 | for ; k < len(key); k, i = k+1, i+1 { 1753 | if key[k] != paths[j][i] { 1754 | // no match 1755 | goto nomatch 1756 | } 1757 | } 1758 | if i < len(paths[j]) { 1759 | if paths[j][i] == '.' { 1760 | // matched, but there still more keys in the path 1761 | goto match_not_atend 1762 | } 1763 | } 1764 | // matched and at the end of the path 1765 | goto match_atend 1766 | } 1767 | // no match, jump to the nomatch label 1768 | goto nomatch 1769 | match_atend: 1770 | // found a match 1771 | // at the end of the path. we must take the value. 1772 | usedPaths++ 1773 | if !parsedVal { 1774 | // the value has not been parsed yet. let's do so. 1775 | valOrgIndex = i // keep track of the current position. 1776 | i, results[j], ok = parseAny(json, i, true) 1777 | if !ok { 1778 | return i, false 1779 | } 1780 | parsedVal = true 1781 | valPathIndex = j 1782 | } else { 1783 | results[j] = results[valPathIndex] 1784 | } 1785 | // mark as complete 1786 | completed[j] = true 1787 | // jump over the match_not_atend label 1788 | goto nomatch 1789 | match_not_atend: 1790 | // found a match 1791 | // still in the middle of the path. 1792 | usedPaths++ 1793 | // mark the path as matched 1794 | matches[j] |= 1 << level 1795 | if !hasMatch { 1796 | hasMatch = true 1797 | } 1798 | nomatch: // noop label 1799 | } 1800 | 1801 | if !parsedVal { 1802 | if hasMatch { 1803 | // we found a match and the value has not been parsed yet. 1804 | // let's find out if the next value type is an object. 1805 | for ; i < len(json); i++ { 1806 | if json[i] <= ' ' || json[i] == ':' { 1807 | continue 1808 | } 1809 | break 1810 | } 1811 | if i < len(json) { 1812 | if json[i] == '{' { 1813 | // it's an object. let's go deeper 1814 | i, ok = parseGetMany(json, i+1, level+1, kplen+len(key)+1, paths, completed, matches, results) 1815 | if !ok { 1816 | return i, false 1817 | } 1818 | } else { 1819 | // not an object. just parse and ignore. 1820 | if i, _, ok = parseAny(json, i, false); !ok { 1821 | return i, false 1822 | } 1823 | } 1824 | } 1825 | } else { 1826 | // Since there was no matches we can just parse the value and 1827 | // ignore the result. 1828 | if i, _, ok = parseAny(json, i, false); !ok { 1829 | return i, false 1830 | } 1831 | } 1832 | } else if hasMatch && len(results[valPathIndex].Raw) > 0 && results[valPathIndex].Raw[0] == '{' { 1833 | // The value was already parsed and the value type is an object. 1834 | // Rewind the json index and let's parse deeper. 1835 | i = valOrgIndex 1836 | for ; i < len(json); i++ { 1837 | if json[i] == '{' { 1838 | break 1839 | } 1840 | } 1841 | i, ok = parseGetMany(json, i+1, level+1, kplen+len(key)+1, paths, completed, matches, results) 1842 | if !ok { 1843 | return i, false 1844 | } 1845 | } 1846 | if usedPaths == len(paths) { 1847 | // all paths have been used, either completed or matched. 1848 | // we should stop parsing this object to save CPU cycles. 1849 | if level > 0 && i < len(json) { 1850 | i, _ = parseSquash(json, i) 1851 | } 1852 | return i, true 1853 | } 1854 | } else if json[i] == '}' { 1855 | // reached the end of the object. end it here. 1856 | return i + 1, true 1857 | } 1858 | } 1859 | return i, true 1860 | } 1861 | 1862 | // Call table for GetMany. Using an isolated function allows for allocating 1863 | // arrays with know capacities on the stack, as opposed to dynamically 1864 | // allocating on the heap. This can provide a tremendous performance boost 1865 | // by avoiding the GC. 1866 | func getMany8(json string, i int, paths []string) ([]Result, bool) { 1867 | const max = 8 1868 | var completed = make([]bool, 0, max) 1869 | var matches = make([]uint64, 0, max) 1870 | var results = make([]Result, 0, max) 1871 | completed = completed[0:len(paths):max] 1872 | matches = matches[0:len(paths):max] 1873 | results = results[0:len(paths):max] 1874 | _, ok := parseGetMany(json, i, 0, 0, paths, completed, matches, results) 1875 | return results, ok 1876 | } 1877 | func getMany16(json string, i int, paths []string) ([]Result, bool) { 1878 | const max = 16 1879 | var completed = make([]bool, 0, max) 1880 | var matches = make([]uint64, 0, max) 1881 | var results = make([]Result, 0, max) 1882 | completed = completed[0:len(paths):max] 1883 | matches = matches[0:len(paths):max] 1884 | results = results[0:len(paths):max] 1885 | _, ok := parseGetMany(json, i, 0, 0, paths, completed, matches, results) 1886 | return results, ok 1887 | } 1888 | func getMany32(json string, i int, paths []string) ([]Result, bool) { 1889 | const max = 32 1890 | var completed = make([]bool, 0, max) 1891 | var matches = make([]uint64, 0, max) 1892 | var results = make([]Result, 0, max) 1893 | completed = completed[0:len(paths):max] 1894 | matches = matches[0:len(paths):max] 1895 | results = results[0:len(paths):max] 1896 | _, ok := parseGetMany(json, i, 0, 0, paths, completed, matches, results) 1897 | return results, ok 1898 | } 1899 | func getMany64(json string, i int, paths []string) ([]Result, bool) { 1900 | const max = 64 1901 | var completed = make([]bool, 0, max) 1902 | var matches = make([]uint64, 0, max) 1903 | var results = make([]Result, 0, max) 1904 | completed = completed[0:len(paths):max] 1905 | matches = matches[0:len(paths):max] 1906 | results = results[0:len(paths):max] 1907 | _, ok := parseGetMany(json, i, 0, 0, paths, completed, matches, results) 1908 | return results, ok 1909 | } 1910 | func getMany128(json string, i int, paths []string) ([]Result, bool) { 1911 | const max = 128 1912 | var completed = make([]bool, 0, max) 1913 | var matches = make([]uint64, 0, max) 1914 | var results = make([]Result, 0, max) 1915 | completed = completed[0:len(paths):max] 1916 | matches = matches[0:len(paths):max] 1917 | results = results[0:len(paths):max] 1918 | _, ok := parseGetMany(json, i, 0, 0, paths, completed, matches, results) 1919 | return results, ok 1920 | } 1921 | func getMany256(json string, i int, paths []string) ([]Result, bool) { 1922 | const max = 256 1923 | var completed = make([]bool, 0, max) 1924 | var matches = make([]uint64, 0, max) 1925 | var results = make([]Result, 0, max) 1926 | completed = completed[0:len(paths):max] 1927 | matches = matches[0:len(paths):max] 1928 | results = results[0:len(paths):max] 1929 | _, ok := parseGetMany(json, i, 0, 0, paths, completed, matches, results) 1930 | return results, ok 1931 | } 1932 | func getMany512(json string, i int, paths []string) ([]Result, bool) { 1933 | const max = 512 1934 | var completed = make([]bool, 0, max) 1935 | var matches = make([]uint64, 0, max) 1936 | var results = make([]Result, 0, max) 1937 | completed = completed[0:len(paths):max] 1938 | matches = matches[0:len(paths):max] 1939 | results = results[0:len(paths):max] 1940 | _, ok := parseGetMany(json, i, 0, 0, paths, completed, matches, results) 1941 | return results, ok 1942 | } 1943 | -------------------------------------------------------------------------------- /vendor/gjson/gjson_test.go: -------------------------------------------------------------------------------- 1 | package gjson 2 | 3 | import ( 4 | "bytes" 5 | "encoding/hex" 6 | "encoding/json" 7 | "fmt" 8 | "io" 9 | "math/rand" 10 | "strings" 11 | "testing" 12 | "time" 13 | 14 | "github.com/buger/jsonparser" 15 | "github.com/mailru/easyjson/jlexer" 16 | fflib "github.com/pquerna/ffjson/fflib/v1" 17 | ) 18 | 19 | // TestRandomData is a fuzzing test that throws random data at the Parse 20 | // function looking for panics. 21 | func TestRandomData(t *testing.T) { 22 | var lstr string 23 | defer func() { 24 | if v := recover(); v != nil { 25 | println("'" + hex.EncodeToString([]byte(lstr)) + "'") 26 | println("'" + lstr + "'") 27 | panic(v) 28 | } 29 | }() 30 | rand.Seed(time.Now().UnixNano()) 31 | b := make([]byte, 200) 32 | for i := 0; i < 2000000; i++ { 33 | n, err := rand.Read(b[:rand.Int()%len(b)]) 34 | if err != nil { 35 | t.Fatal(err) 36 | } 37 | lstr = string(b[:n]) 38 | GetBytes([]byte(lstr), "zzzz") 39 | Parse(lstr) 40 | } 41 | } 42 | 43 | func TestRandomValidStrings(t *testing.T) { 44 | rand.Seed(time.Now().UnixNano()) 45 | b := make([]byte, 200) 46 | for i := 0; i < 100000; i++ { 47 | n, err := rand.Read(b[:rand.Int()%len(b)]) 48 | if err != nil { 49 | t.Fatal(err) 50 | } 51 | sm, err := json.Marshal(string(b[:n])) 52 | if err != nil { 53 | t.Fatal(err) 54 | } 55 | var su string 56 | if err := json.Unmarshal([]byte(sm), &su); err != nil { 57 | t.Fatal(err) 58 | } 59 | token := Get(`{"str":`+string(sm)+`}`, "str") 60 | if token.Type != String || token.Str != su { 61 | println("["+token.Raw+"]", "["+token.Str+"]", "["+su+"]", "["+string(sm)+"]") 62 | t.Fatal("string mismatch") 63 | } 64 | } 65 | } 66 | func testEscapePath(t *testing.T, json, path, expect string) { 67 | if Get(json, path).String() != expect { 68 | t.Fatalf("expected '%v', got '%v'", expect, Get(json, path).String()) 69 | } 70 | } 71 | 72 | func TestEscapePath(t *testing.T) { 73 | json := `{ 74 | "test":{ 75 | "*":"valZ", 76 | "*v":"val0", 77 | "keyv*":"val1", 78 | "key*v":"val2", 79 | "keyv?":"val3", 80 | "key?v":"val4", 81 | "keyv.":"val5", 82 | "key.v":"val6", 83 | "keyk*":{"key?":"val7"} 84 | } 85 | }` 86 | 87 | testEscapePath(t, json, "test.\\*", "valZ") 88 | testEscapePath(t, json, "test.\\*v", "val0") 89 | testEscapePath(t, json, "test.keyv\\*", "val1") 90 | testEscapePath(t, json, "test.key\\*v", "val2") 91 | testEscapePath(t, json, "test.keyv\\?", "val3") 92 | testEscapePath(t, json, "test.key\\?v", "val4") 93 | testEscapePath(t, json, "test.keyv\\.", "val5") 94 | testEscapePath(t, json, "test.key\\.v", "val6") 95 | testEscapePath(t, json, "test.keyk\\*.key\\?", "val7") 96 | } 97 | 98 | // this json block is poorly formed on purpose. 99 | var basicJSON = `{"age":100, "name":{"here":"B\\\"R"}, 100 | "noop":{"what is a wren?":"a bird"}, 101 | "happy":true,"immortal":false, 102 | "items":[1,2,3,{"tags":[1,2,3],"points":[[1,2],[3,4]]},4,5,6,7], 103 | "arr":["1",2,"3",{"hello":"world"},"4",5], 104 | "vals":[1,2,3,{"sadf":sdf"asdf"}],"name":{"first":"tom","last":null}, 105 | "loggy":{ 106 | "programmers": [ 107 | { 108 | "firstName": "Brett", 109 | "lastName": "McLaughlin", 110 | "email": "aaaa", 111 | "tag": "good" 112 | }, 113 | { 114 | "firstName": "Jason", 115 | "lastName": "Hunter", 116 | "email": "bbbb", 117 | "tag": "bad" 118 | }, 119 | { 120 | "firstName": "Elliotte", 121 | "lastName": "Harold", 122 | "email": "cccc", 123 | "tag":, "good" 124 | }, 125 | { 126 | "firstName": 1002.3, 127 | "age": 101 128 | } 129 | ] 130 | } 131 | }` 132 | var basicJSONB = []byte(basicJSON) 133 | 134 | func TestByteSafety(t *testing.T) { 135 | jsonb := []byte(`{"name":"Janet","age":38}`) 136 | mtok := GetBytes(jsonb, "name") 137 | if mtok.String() != "Janet" { 138 | t.Fatalf("expected %v, got %v", "Jason", mtok.String()) 139 | } 140 | mtok2 := GetBytes(jsonb, "age") 141 | if mtok2.Raw != "38" { 142 | t.Fatalf("expected %v, got %v", "Jason", mtok2.Raw) 143 | } 144 | jsonb[9] = 'T' 145 | jsonb[12] = 'd' 146 | jsonb[13] = 'y' 147 | if mtok.String() != "Janet" { 148 | t.Fatalf("expected %v, got %v", "Jason", mtok.String()) 149 | } 150 | } 151 | 152 | func get(json, path string) Result { 153 | return GetBytes([]byte(basicJSONB), path) 154 | } 155 | 156 | func TestBasic(t *testing.T) { 157 | var mtok Result 158 | mtok = get(basicJSON, `loggy.programmers.#[tag="good"].firstName`) 159 | if mtok.String() != "Brett" { 160 | t.Fatalf("expected %v, got %v", "Brett", mtok.String()) 161 | } 162 | mtok = get(basicJSON, `loggy.programmers.#[tag="good"]#.firstName`) 163 | if mtok.String() != `["Brett","Elliotte"]` { 164 | t.Fatalf("expected %v, got %v", `["Brett","Elliotte"]`, mtok.String()) 165 | } 166 | 167 | mtok = get(basicJSON, `loggy.programmers`) 168 | var count int 169 | mtok.ForEach(func(key, value Result) bool { 170 | if key.Exists() { 171 | t.Fatalf("expected %v, got %v", false, key.Exists()) 172 | } 173 | count++ 174 | if count == 3 { 175 | return false 176 | } 177 | if count == 1 { 178 | i := 0 179 | value.ForEach(func(key, value Result) bool { 180 | switch i { 181 | case 0: 182 | if key.String() != "firstName" || value.String() != "Brett" { 183 | t.Fatalf("expected %v/%v got %v/%v", "firstName", "Brett", key.String(), value.String()) 184 | } 185 | case 1: 186 | if key.String() != "lastName" || value.String() != "McLaughlin" { 187 | t.Fatalf("expected %v/%v got %v/%v", "lastName", "McLaughlin", key.String(), value.String()) 188 | } 189 | case 2: 190 | if key.String() != "email" || value.String() != "aaaa" { 191 | t.Fatalf("expected %v/%v got %v/%v", "email", "aaaa", key.String(), value.String()) 192 | } 193 | } 194 | i++ 195 | return true 196 | }) 197 | } 198 | return true 199 | }) 200 | if count != 3 { 201 | t.Fatalf("expected %v, got %v", 3, count) 202 | } 203 | mtok = get(basicJSON, `loggy.programmers.#[age=101].firstName`) 204 | if mtok.String() != "1002.3" { 205 | t.Fatalf("expected %v, got %v", "1002.3", mtok.String()) 206 | } 207 | mtok = get(basicJSON, `loggy.programmers.#[firstName != "Brett"].firstName`) 208 | if mtok.String() != "Jason" { 209 | t.Fatalf("expected %v, got %v", "Jason", mtok.String()) 210 | } 211 | mtok = get(basicJSON, `loggy.programmers.#[firstName % "Bre*"].email`) 212 | if mtok.String() != "aaaa" { 213 | t.Fatalf("expected %v, got %v", "aaaa", mtok.String()) 214 | } 215 | mtok = get(basicJSON, `loggy.programmers.#[firstName == "Brett"].email`) 216 | if mtok.String() != "aaaa" { 217 | t.Fatalf("expected %v, got %v", "aaaa", mtok.String()) 218 | } 219 | mtok = get(basicJSON, "loggy") 220 | if mtok.Type != JSON { 221 | t.Fatalf("expected %v, got %v", JSON, mtok.Type) 222 | } 223 | if len(mtok.Map()) != 1 { 224 | t.Fatalf("expected %v, got %v", 1, len(mtok.Map())) 225 | } 226 | programmers := mtok.Map()["programmers"] 227 | if programmers.Array()[1].Map()["firstName"].Str != "Jason" { 228 | t.Fatalf("expected %v, got %v", "Jason", mtok.Map()["programmers"].Array()[1].Map()["firstName"].Str) 229 | } 230 | 231 | if Parse(basicJSON).Get("loggy.programmers").Get("1").Get("firstName").Str != "Jason" { 232 | t.Fatalf("expected %v, got %v", "Jason", Parse(basicJSON).Get("loggy.programmers").Get("1").Get("firstName").Str) 233 | } 234 | var token Result 235 | if token = Parse("-102"); token.Num != -102 { 236 | t.Fatal("expected %v, got %v", -102, token.Num) 237 | } 238 | if token = Parse("102"); token.Num != 102 { 239 | t.Fatal("expected %v, got %v", 102, token.Num) 240 | } 241 | if token = Parse("102.2"); token.Num != 102.2 { 242 | t.Fatal("expected %v, got %v", 102.2, token.Num) 243 | } 244 | if token = Parse(`"hello"`); token.Str != "hello" { 245 | t.Fatal("expected %v, got %v", "hello", token.Str) 246 | } 247 | if token = Parse(`"\"he\nllo\""`); token.Str != "\"he\nllo\"" { 248 | t.Fatal("expected %v, got %v", "\"he\nllo\"", token.Str) 249 | } 250 | mtok = get(basicJSON, "loggy.programmers.#.firstName") 251 | if len(mtok.Array()) != 4 { 252 | t.Fatalf("expected 4, got %v", len(mtok.Array())) 253 | } 254 | for i, ex := range []string{"Brett", "Jason", "Elliotte", "1002.3"} { 255 | if mtok.Array()[i].String() != ex { 256 | t.Fatalf("expected '%v', got '%v'", ex, mtok.Array()[i].String()) 257 | } 258 | } 259 | mtok = get(basicJSON, "loggy.programmers.#.asd") 260 | if mtok.Type != JSON { 261 | t.Fatal("expected %v, got %v", JSON, mtok.Type) 262 | } 263 | if len(mtok.Array()) != 0 { 264 | t.Fatalf("expected 0, got %v", len(mtok.Array())) 265 | } 266 | 267 | if get(basicJSON, "items.3.tags.#").Num != 3 { 268 | t.Fatalf("expected 3, got %v", get(basicJSON, "items.3.tags.#").Num) 269 | } 270 | if get(basicJSON, "items.3.points.1.#").Num != 2 { 271 | t.Fatalf("expected 2, got %v", get(basicJSON, "items.3.points.1.#").Num) 272 | } 273 | if get(basicJSON, "items.#").Num != 8 { 274 | t.Fatalf("expected 6, got %v", get(basicJSON, "items.#").Num) 275 | } 276 | if get(basicJSON, "vals.#").Num != 4 { 277 | t.Fatalf("expected 4, got %v", get(basicJSON, "vals.#").Num) 278 | } 279 | if !get(basicJSON, "name.last").Exists() { 280 | t.Fatal("expected true, got false") 281 | } 282 | token = get(basicJSON, "name.here") 283 | if token.String() != "B\\\"R" { 284 | t.Fatal("expecting 'B\\\"R'", "got", token.String()) 285 | } 286 | token = get(basicJSON, "arr.#") 287 | if token.String() != "6" { 288 | t.Fatal("expecting '6'", "got", token.String()) 289 | } 290 | token = get(basicJSON, "arr.3.hello") 291 | if token.String() != "world" { 292 | t.Fatal("expecting 'world'", "got", token.String()) 293 | } 294 | _ = token.Value().(string) 295 | token = get(basicJSON, "name.first") 296 | if token.String() != "tom" { 297 | t.Fatal("expecting 'tom'", "got", token.String()) 298 | } 299 | _ = token.Value().(string) 300 | token = get(basicJSON, "name.last") 301 | if token.String() != "null" { 302 | t.Fatal("expecting 'null'", "got", token.String()) 303 | } 304 | if token.Value() != nil { 305 | t.Fatal("should be nil") 306 | } 307 | token = get(basicJSON, "age") 308 | if token.String() != "100" { 309 | t.Fatal("expecting '100'", "got", token.String()) 310 | } 311 | _ = token.Value().(float64) 312 | token = get(basicJSON, "happy") 313 | if token.String() != "true" { 314 | t.Fatal("expecting 'true'", "got", token.String()) 315 | } 316 | _ = token.Value().(bool) 317 | token = get(basicJSON, "immortal") 318 | if token.String() != "false" { 319 | t.Fatal("expecting 'false'", "got", token.String()) 320 | } 321 | _ = token.Value().(bool) 322 | token = get(basicJSON, "noop") 323 | if token.String() != `{"what is a wren?":"a bird"}` { 324 | t.Fatal("expecting '"+`{"what is a wren?":"a bird"}`+"'", "got", token.String()) 325 | } 326 | _ = token.Value().(map[string]interface{}) 327 | 328 | if get(basicJSON, "").Value() != nil { 329 | t.Fatal("should be nil") 330 | } 331 | 332 | get(basicJSON, "vals.hello") 333 | 334 | mm := Parse(basicJSON).Value().(map[string]interface{}) 335 | fn := mm["loggy"].(map[string]interface{})["programmers"].([]interface{})[1].(map[string]interface{})["firstName"].(string) 336 | if fn != "Jason" { 337 | t.Fatalf("expecting %v, got %v", "Jason", fn) 338 | } 339 | } 340 | func TestUnicode(t *testing.T) { 341 | var json = `{"key":0,"的情况下解":{"key":1,"的情况":2}}` 342 | if Get(json, "的情况下解.key").Num != 1 { 343 | t.Fatal("fail") 344 | } 345 | if Get(json, "的情况下解.的情况").Num != 2 { 346 | t.Fatal("fail") 347 | } 348 | if Get(json, "的情况下解.的?况").Num != 2 { 349 | t.Fatal("fail") 350 | } 351 | if Get(json, "的情况下解.的?*").Num != 2 { 352 | t.Fatal("fail") 353 | } 354 | if Get(json, "的情况下解.*?况").Num != 2 { 355 | t.Fatal("fail") 356 | } 357 | if Get(json, "的情?下解.*?况").Num != 2 { 358 | t.Fatal("fail") 359 | } 360 | if Get(json, "的情下解.*?况").Num != 0 { 361 | t.Fatal("fail") 362 | } 363 | } 364 | 365 | func TestUnescape(t *testing.T) { 366 | unescape(string([]byte{'\\', '\\', 0})) 367 | unescape(string([]byte{'\\', '/', '\\', 'b', '\\', 'f'})) 368 | } 369 | func assert(t testing.TB, cond bool) { 370 | if !cond { 371 | t.Fatal("assert failed") 372 | } 373 | } 374 | func TestLess(t *testing.T) { 375 | assert(t, !Result{Type: Null}.Less(Result{Type: Null}, true)) 376 | assert(t, Result{Type: Null}.Less(Result{Type: False}, true)) 377 | assert(t, Result{Type: Null}.Less(Result{Type: True}, true)) 378 | assert(t, Result{Type: Null}.Less(Result{Type: JSON}, true)) 379 | assert(t, Result{Type: Null}.Less(Result{Type: Number}, true)) 380 | assert(t, Result{Type: Null}.Less(Result{Type: String}, true)) 381 | assert(t, !Result{Type: False}.Less(Result{Type: Null}, true)) 382 | assert(t, Result{Type: False}.Less(Result{Type: True}, true)) 383 | assert(t, Result{Type: String, Str: "abc"}.Less(Result{Type: String, Str: "bcd"}, true)) 384 | assert(t, Result{Type: String, Str: "ABC"}.Less(Result{Type: String, Str: "abc"}, true)) 385 | assert(t, !Result{Type: String, Str: "ABC"}.Less(Result{Type: String, Str: "abc"}, false)) 386 | assert(t, Result{Type: Number, Num: 123}.Less(Result{Type: Number, Num: 456}, true)) 387 | assert(t, !Result{Type: Number, Num: 456}.Less(Result{Type: Number, Num: 123}, true)) 388 | assert(t, !Result{Type: Number, Num: 456}.Less(Result{Type: Number, Num: 456}, true)) 389 | assert(t, stringLessInsensitive("abcde", "BBCDE")) 390 | assert(t, stringLessInsensitive("abcde", "bBCDE")) 391 | assert(t, stringLessInsensitive("Abcde", "BBCDE")) 392 | assert(t, stringLessInsensitive("Abcde", "bBCDE")) 393 | assert(t, !stringLessInsensitive("bbcde", "aBCDE")) 394 | assert(t, !stringLessInsensitive("bbcde", "ABCDE")) 395 | assert(t, !stringLessInsensitive("Bbcde", "aBCDE")) 396 | assert(t, !stringLessInsensitive("Bbcde", "ABCDE")) 397 | assert(t, !stringLessInsensitive("abcde", "ABCDE")) 398 | assert(t, !stringLessInsensitive("Abcde", "ABCDE")) 399 | assert(t, !stringLessInsensitive("abcde", "ABCDE")) 400 | assert(t, !stringLessInsensitive("ABCDE", "ABCDE")) 401 | assert(t, !stringLessInsensitive("abcde", "abcde")) 402 | assert(t, !stringLessInsensitive("123abcde", "123Abcde")) 403 | assert(t, !stringLessInsensitive("123Abcde", "123Abcde")) 404 | assert(t, !stringLessInsensitive("123Abcde", "123abcde")) 405 | assert(t, !stringLessInsensitive("123abcde", "123abcde")) 406 | assert(t, !stringLessInsensitive("124abcde", "123abcde")) 407 | assert(t, !stringLessInsensitive("124Abcde", "123Abcde")) 408 | assert(t, !stringLessInsensitive("124Abcde", "123abcde")) 409 | assert(t, !stringLessInsensitive("124abcde", "123abcde")) 410 | assert(t, stringLessInsensitive("124abcde", "125abcde")) 411 | assert(t, stringLessInsensitive("124Abcde", "125Abcde")) 412 | assert(t, stringLessInsensitive("124Abcde", "125abcde")) 413 | assert(t, stringLessInsensitive("124abcde", "125abcde")) 414 | } 415 | 416 | func TestIssue6(t *testing.T) { 417 | data := `{ 418 | "code": 0, 419 | "msg": "", 420 | "data": { 421 | "sz002024": { 422 | "qfqday": [ 423 | [ 424 | "2014-01-02", 425 | "8.93", 426 | "9.03", 427 | "9.17", 428 | "8.88", 429 | "621143.00" 430 | ], 431 | [ 432 | "2014-01-03", 433 | "9.03", 434 | "9.30", 435 | "9.47", 436 | "8.98", 437 | "1624438.00" 438 | ] 439 | ] 440 | } 441 | } 442 | }` 443 | 444 | var num []string 445 | for _, v := range Get(data, "data.sz002024.qfqday.0").Array() { 446 | num = append(num, v.String()) 447 | } 448 | if fmt.Sprintf("%v", num) != "[2014-01-02 8.93 9.03 9.17 8.88 621143.00]" { 449 | t.Fatalf("invalid result") 450 | } 451 | } 452 | 453 | var exampleJSON = `{ 454 | "widget": { 455 | "debug": "on", 456 | "window": { 457 | "title": "Sample Konfabulator Widget", 458 | "name": "main_window", 459 | "width": 500, 460 | "height": 500 461 | }, 462 | "image": { 463 | "src": "Images/Sun.png", 464 | "hOffset": 250, 465 | "vOffset": 250, 466 | "alignment": "center" 467 | }, 468 | "text": { 469 | "data": "Click Here", 470 | "size": 36, 471 | "style": "bold", 472 | "vOffset": 100, 473 | "alignment": "center", 474 | "onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;" 475 | } 476 | } 477 | }` 478 | 479 | func TestNewParse(t *testing.T) { 480 | //fmt.Printf("%v\n", parse2(exampleJSON, "widget").String()) 481 | } 482 | 483 | func TestUnmarshalMap(t *testing.T) { 484 | var m1 = Parse(exampleJSON).Value().(map[string]interface{}) 485 | var m2 map[string]interface{} 486 | if err := json.Unmarshal([]byte(exampleJSON), &m2); err != nil { 487 | t.Fatal(err) 488 | } 489 | b1, err := json.Marshal(m1) 490 | if err != nil { 491 | t.Fatal(err) 492 | } 493 | b2, err := json.Marshal(m2) 494 | if err != nil { 495 | t.Fatal(err) 496 | } 497 | if bytes.Compare(b1, b2) != 0 { 498 | t.Fatal("b1 != b2") 499 | } 500 | } 501 | 502 | func TestSingleArrayValue(t *testing.T) { 503 | var json = `{"key": "value","key2":[1,2,3,4,"A"]}` 504 | var result = Get(json, "key") 505 | var array = result.Array() 506 | if len(array) != 1 { 507 | t.Fatal("array is empty") 508 | } 509 | if array[0].String() != "value" { 510 | t.Fatal("got %s, should be %s", array[0].String(), "value") 511 | } 512 | 513 | array = Get(json, "key2.#").Array() 514 | if len(array) != 1 { 515 | t.Fatal("got '%v', expected '%v'", len(array), 1) 516 | } 517 | 518 | array = Get(json, "key3").Array() 519 | if len(array) != 0 { 520 | t.Fatal("got '%v', expected '%v'", len(array), 0) 521 | } 522 | 523 | } 524 | 525 | var manyJSON = ` { 526 | "a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{ 527 | "a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{ 528 | "a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{ 529 | "a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{ 530 | "a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{ 531 | "a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{ 532 | "a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"hello":"world" 533 | }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}} 534 | "position":{"type":"Point","coordinates":[-115.24,33.09]}, 535 | "loves":["world peace"], 536 | "name":{"last":"Anderson","first":"Nancy"}, 537 | "age":31 538 | "":{"a":"emptya","b":"emptyb"}, 539 | "name.last":"Yellow", 540 | "name.first":"Cat", 541 | }` 542 | 543 | func combine(results []Result) string { 544 | return fmt.Sprintf("%v", results) 545 | } 546 | func TestManyBasic(t *testing.T) { 547 | testWatchForFallback = true 548 | defer func() { 549 | testWatchForFallback = false 550 | }() 551 | testMany := func(shouldFallback bool, expect string, paths ...string) { 552 | results := GetMany( 553 | manyJSON, 554 | paths..., 555 | ) 556 | if len(results) != len(paths) { 557 | t.Fatalf("expected %v, got %v", len(paths), len(results)) 558 | } 559 | if fmt.Sprintf("%v", results) != expect { 560 | t.Fatalf("expected %v, got %v", expect, results) 561 | } 562 | return 563 | if testLastWasFallback != shouldFallback { 564 | t.Fatalf("expected %v, got %v", shouldFallback, testLastWasFallback) 565 | } 566 | } 567 | testMany(false, "[Point]", "position.type") 568 | testMany(false, `[emptya ["world peace"] 31]`, ".a", "loves", "age") 569 | testMany(false, `[["world peace"]]`, "loves") 570 | testMany(false, `[{"last":"Anderson","first":"Nancy"} Nancy]`, "name", "name.first") 571 | testMany(true, `[null]`, strings.Repeat("a.", 40)+"hello") 572 | res := Get(manyJSON, strings.Repeat("a.", 48)+"a") 573 | testMany(true, `[`+res.String()+`]`, strings.Repeat("a.", 48)+"a") 574 | // these should fallback 575 | testMany(true, `[Cat Nancy]`, "name\\.first", "name.first") 576 | testMany(true, `[world]`, strings.Repeat("a.", 70)+"hello") 577 | } 578 | 579 | func TestRandomMany(t *testing.T) { 580 | var lstr string 581 | defer func() { 582 | if v := recover(); v != nil { 583 | println("'" + hex.EncodeToString([]byte(lstr)) + "'") 584 | println("'" + lstr + "'") 585 | panic(v) 586 | } 587 | }() 588 | rand.Seed(time.Now().UnixNano()) 589 | b := make([]byte, 512) 590 | for i := 0; i < 50000; i++ { 591 | n, err := rand.Read(b[:rand.Int()%len(b)]) 592 | if err != nil { 593 | t.Fatal(err) 594 | } 595 | lstr = string(b[:n]) 596 | paths := make([]string, rand.Int()%64) 597 | for i := range paths { 598 | var b []byte 599 | n := rand.Int() % 5 600 | for j := 0; j < n; j++ { 601 | if j > 0 { 602 | b = append(b, '.') 603 | } 604 | nn := rand.Int() % 10 605 | for k := 0; k < nn; k++ { 606 | b = append(b, 'a'+byte(rand.Int()%26)) 607 | } 608 | } 609 | paths[i] = string(b) 610 | } 611 | GetMany(lstr, paths...) 612 | } 613 | } 614 | 615 | type BenchStruct struct { 616 | Widget struct { 617 | Window struct { 618 | Name string `json:"name"` 619 | } `json:"window"` 620 | Image struct { 621 | HOffset int `json:"hOffset"` 622 | } `json:"image"` 623 | Text struct { 624 | OnMouseUp string `json:"onMouseUp"` 625 | } `json:"text"` 626 | } `json:"widget"` 627 | } 628 | 629 | var benchPaths = []string{ 630 | "widget.window.name", 631 | "widget.image.hOffset", 632 | "widget.text.onMouseUp", 633 | } 634 | 635 | var benchManyPaths = []string{ 636 | "widget.window.name", 637 | "widget.image.hOffset", 638 | "widget.text.onMouseUp", 639 | "widget.window.title", 640 | "widget.image.alignment", 641 | "widget.text.style", 642 | "widget.window.height", 643 | "widget.image.src", 644 | "widget.text.data", 645 | "widget.text.size", 646 | } 647 | 648 | func BenchmarkGJSONGet(t *testing.B) { 649 | t.ReportAllocs() 650 | t.ResetTimer() 651 | for i := 0; i < t.N; i++ { 652 | for j := 0; j < len(benchPaths); j++ { 653 | if Get(exampleJSON, benchPaths[j]).Type == Null { 654 | t.Fatal("did not find the value") 655 | } 656 | } 657 | } 658 | t.N *= len(benchPaths) // because we are running against 3 paths 659 | } 660 | func BenchmarkGJSONGetMany4Paths(t *testing.B) { 661 | benchmarkGJSONGetManyN(t, 4) 662 | } 663 | func BenchmarkGJSONGetMany8Paths(t *testing.B) { 664 | benchmarkGJSONGetManyN(t, 8) 665 | } 666 | func BenchmarkGJSONGetMany16Paths(t *testing.B) { 667 | benchmarkGJSONGetManyN(t, 16) 668 | } 669 | func BenchmarkGJSONGetMany32Paths(t *testing.B) { 670 | benchmarkGJSONGetManyN(t, 32) 671 | } 672 | func BenchmarkGJSONGetMany64Paths(t *testing.B) { 673 | benchmarkGJSONGetManyN(t, 64) 674 | } 675 | func BenchmarkGJSONGetMany128Paths(t *testing.B) { 676 | benchmarkGJSONGetManyN(t, 128) 677 | } 678 | func BenchmarkGJSONGetMany256Paths(t *testing.B) { 679 | benchmarkGJSONGetManyN(t, 256) 680 | } 681 | func BenchmarkGJSONGetMany512Paths(t *testing.B) { 682 | benchmarkGJSONGetManyN(t, 512) 683 | } 684 | func benchmarkGJSONGetManyN(t *testing.B, n int) { 685 | var paths []string 686 | for len(paths) < n { 687 | paths = append(paths, benchManyPaths...) 688 | } 689 | paths = paths[:n] 690 | t.ReportAllocs() 691 | t.ResetTimer() 692 | for i := 0; i < t.N; i++ { 693 | results := GetMany(exampleJSON, paths...) 694 | if len(results) == 0 { 695 | t.Fatal("did not find the value") 696 | } 697 | for j := 0; j < len(results); j++ { 698 | if results[j].Type == Null { 699 | t.Fatal("did not find the value") 700 | } 701 | } 702 | } 703 | t.N *= len(paths) // because we are running against 3 paths 704 | } 705 | 706 | func BenchmarkGJSONUnmarshalMap(t *testing.B) { 707 | t.ReportAllocs() 708 | t.ResetTimer() 709 | for i := 0; i < t.N; i++ { 710 | for j := 0; j < len(benchPaths); j++ { 711 | parts := strings.Split(benchPaths[j], ".") 712 | m, _ := Parse(exampleJSON).Value().(map[string]interface{}) 713 | var v interface{} 714 | for len(parts) > 0 { 715 | part := parts[0] 716 | if len(parts) > 1 { 717 | m = m[part].(map[string]interface{}) 718 | if m == nil { 719 | t.Fatal("did not find the value") 720 | } 721 | } else { 722 | v = m[part] 723 | if v == nil { 724 | t.Fatal("did not find the value") 725 | } 726 | } 727 | parts = parts[1:] 728 | } 729 | } 730 | } 731 | t.N *= len(benchPaths) // because we are running against 3 paths 732 | } 733 | 734 | func BenchmarkJSONUnmarshalMap(t *testing.B) { 735 | t.ReportAllocs() 736 | t.ResetTimer() 737 | for i := 0; i < t.N; i++ { 738 | for j := 0; j < len(benchPaths); j++ { 739 | parts := strings.Split(benchPaths[j], ".") 740 | var m map[string]interface{} 741 | if err := json.Unmarshal([]byte(exampleJSON), &m); err != nil { 742 | t.Fatal(err) 743 | } 744 | var v interface{} 745 | for len(parts) > 0 { 746 | part := parts[0] 747 | if len(parts) > 1 { 748 | m = m[part].(map[string]interface{}) 749 | if m == nil { 750 | t.Fatal("did not find the value") 751 | } 752 | } else { 753 | v = m[part] 754 | if v == nil { 755 | t.Fatal("did not find the value") 756 | } 757 | } 758 | parts = parts[1:] 759 | } 760 | } 761 | } 762 | t.N *= len(benchPaths) // because we are running against 3 paths 763 | } 764 | 765 | func BenchmarkJSONUnmarshalStruct(t *testing.B) { 766 | t.ReportAllocs() 767 | t.ResetTimer() 768 | for i := 0; i < t.N; i++ { 769 | for j := 0; j < len(benchPaths); j++ { 770 | var s BenchStruct 771 | if err := json.Unmarshal([]byte(exampleJSON), &s); err != nil { 772 | t.Fatal(err) 773 | } 774 | switch benchPaths[j] { 775 | case "widget.window.name": 776 | if s.Widget.Window.Name == "" { 777 | t.Fatal("did not find the value") 778 | } 779 | case "widget.image.hOffset": 780 | if s.Widget.Image.HOffset == 0 { 781 | t.Fatal("did not find the value") 782 | } 783 | case "widget.text.onMouseUp": 784 | if s.Widget.Text.OnMouseUp == "" { 785 | t.Fatal("did not find the value") 786 | } 787 | } 788 | } 789 | } 790 | t.N *= len(benchPaths) // because we are running against 3 paths 791 | } 792 | 793 | func BenchmarkJSONDecoder(t *testing.B) { 794 | t.ReportAllocs() 795 | t.ResetTimer() 796 | for i := 0; i < t.N; i++ { 797 | for j := 0; j < len(benchPaths); j++ { 798 | dec := json.NewDecoder(bytes.NewBuffer([]byte(exampleJSON))) 799 | var found bool 800 | outer: 801 | for { 802 | tok, err := dec.Token() 803 | if err != nil { 804 | if err == io.EOF { 805 | break 806 | } 807 | t.Fatal(err) 808 | } 809 | switch v := tok.(type) { 810 | case string: 811 | if found { 812 | // break out once we find the value. 813 | break outer 814 | } 815 | switch benchPaths[j] { 816 | case "widget.window.name": 817 | if v == "name" { 818 | found = true 819 | } 820 | case "widget.image.hOffset": 821 | if v == "hOffset" { 822 | found = true 823 | } 824 | case "widget.text.onMouseUp": 825 | if v == "onMouseUp" { 826 | found = true 827 | } 828 | } 829 | } 830 | } 831 | if !found { 832 | t.Fatal("field not found") 833 | } 834 | } 835 | } 836 | t.N *= len(benchPaths) // because we are running against 3 paths 837 | } 838 | 839 | func BenchmarkFFJSONLexer(t *testing.B) { 840 | t.ReportAllocs() 841 | t.ResetTimer() 842 | for i := 0; i < t.N; i++ { 843 | for j := 0; j < len(benchPaths); j++ { 844 | l := fflib.NewFFLexer([]byte(exampleJSON)) 845 | var found bool 846 | outer: 847 | for { 848 | t := l.Scan() 849 | if t == fflib.FFTok_eof { 850 | break 851 | } 852 | if t == fflib.FFTok_string { 853 | b, _ := l.CaptureField(t) 854 | v := string(b) 855 | if found { 856 | // break out once we find the value. 857 | break outer 858 | } 859 | switch benchPaths[j] { 860 | case "widget.window.name": 861 | if v == "\"name\"" { 862 | found = true 863 | } 864 | case "widget.image.hOffset": 865 | if v == "\"hOffset\"" { 866 | found = true 867 | } 868 | case "widget.text.onMouseUp": 869 | if v == "\"onMouseUp\"" { 870 | found = true 871 | } 872 | } 873 | } 874 | } 875 | if !found { 876 | t.Fatal("field not found") 877 | } 878 | } 879 | } 880 | t.N *= len(benchPaths) // because we are running against 3 paths 881 | } 882 | 883 | func BenchmarkEasyJSONLexer(t *testing.B) { 884 | skipCC := func(l *jlexer.Lexer, n int) { 885 | for i := 0; i < n; i++ { 886 | l.Skip() 887 | l.WantColon() 888 | l.Skip() 889 | l.WantComma() 890 | } 891 | } 892 | skipGroup := func(l *jlexer.Lexer, n int) { 893 | l.WantColon() 894 | l.Delim('{') 895 | skipCC(l, n) 896 | l.Delim('}') 897 | l.WantComma() 898 | } 899 | t.ReportAllocs() 900 | t.ResetTimer() 901 | for i := 0; i < t.N; i++ { 902 | for j := 0; j < len(benchPaths); j++ { 903 | l := &jlexer.Lexer{Data: []byte(exampleJSON)} 904 | l.Delim('{') 905 | if l.String() == "widget" { 906 | l.WantColon() 907 | l.Delim('{') 908 | switch benchPaths[j] { 909 | case "widget.window.name": 910 | skipCC(l, 1) 911 | if l.String() == "window" { 912 | l.WantColon() 913 | l.Delim('{') 914 | skipCC(l, 1) 915 | if l.String() == "name" { 916 | l.WantColon() 917 | if l.String() == "" { 918 | t.Fatal("did not find the value") 919 | } 920 | } 921 | } 922 | case "widget.image.hOffset": 923 | skipCC(l, 1) 924 | if l.String() == "window" { 925 | skipGroup(l, 4) 926 | } 927 | if l.String() == "image" { 928 | l.WantColon() 929 | l.Delim('{') 930 | skipCC(l, 1) 931 | if l.String() == "hOffset" { 932 | l.WantColon() 933 | if l.Int() == 0 { 934 | t.Fatal("did not find the value") 935 | } 936 | } 937 | } 938 | case "widget.text.onMouseUp": 939 | skipCC(l, 1) 940 | if l.String() == "window" { 941 | skipGroup(l, 4) 942 | } 943 | if l.String() == "image" { 944 | skipGroup(l, 4) 945 | } 946 | if l.String() == "text" { 947 | l.WantColon() 948 | l.Delim('{') 949 | skipCC(l, 5) 950 | if l.String() == "onMouseUp" { 951 | l.WantColon() 952 | if l.String() == "" { 953 | t.Fatal("did not find the value") 954 | } 955 | } 956 | } 957 | } 958 | } 959 | } 960 | } 961 | t.N *= len(benchPaths) // because we are running against 3 paths 962 | } 963 | 964 | func BenchmarkJSONParserGet(t *testing.B) { 965 | data := []byte(exampleJSON) 966 | keys := make([][]string, 0, len(benchPaths)) 967 | for i := 0; i < len(benchPaths); i++ { 968 | keys = append(keys, strings.Split(benchPaths[i], ".")) 969 | } 970 | t.ReportAllocs() 971 | t.ResetTimer() 972 | for i := 0; i < t.N; i++ { 973 | for j, k := range keys { 974 | if j == 1 { 975 | // "widget.image.hOffset" is a number 976 | v, _ := jsonparser.GetInt(data, k...) 977 | if v == 0 { 978 | t.Fatal("did not find the value") 979 | } 980 | } else { 981 | // "widget.window.name", 982 | // "widget.text.onMouseUp", 983 | v, _ := jsonparser.GetString(data, k...) 984 | if v == "" { 985 | t.Fatal("did not find the value") 986 | } 987 | } 988 | } 989 | } 990 | t.N *= len(benchPaths) // because we are running against 3 paths 991 | } 992 | 993 | var massiveJSON = func() string { 994 | var buf bytes.Buffer 995 | buf.WriteString("[") 996 | for i := 0; i < 100; i++ { 997 | if i > 0 { 998 | buf.WriteByte(',') 999 | } 1000 | buf.WriteString(exampleJSON) 1001 | } 1002 | buf.WriteString("]") 1003 | return buf.String() 1004 | }() 1005 | 1006 | func BenchmarkConvertNone(t *testing.B) { 1007 | json := massiveJSON 1008 | t.ReportAllocs() 1009 | t.ResetTimer() 1010 | for i := 0; i < t.N; i++ { 1011 | Get(json, "50.widget.text.onMouseUp") 1012 | } 1013 | } 1014 | func BenchmarkConvertGet(t *testing.B) { 1015 | data := []byte(massiveJSON) 1016 | t.ReportAllocs() 1017 | t.ResetTimer() 1018 | for i := 0; i < t.N; i++ { 1019 | Get(string(data), "50.widget.text.onMouseUp") 1020 | } 1021 | } 1022 | func BenchmarkConvertGetBytes(t *testing.B) { 1023 | data := []byte(massiveJSON) 1024 | t.ReportAllocs() 1025 | t.ResetTimer() 1026 | for i := 0; i < t.N; i++ { 1027 | GetBytes(data, "50.widget.text.onMouseUp") 1028 | } 1029 | } 1030 | -------------------------------------------------------------------------------- /vendor/go-srp/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Sudhi Herle 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /vendor/go-srp/src/srp/srp.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2013-2014 Sudhi Herle 3 | // License: MIT 4 | // 5 | // Implementation of SRP_. It requires a cryptographically strong 6 | // random number generator. 7 | // 8 | // This implementation is accurate as of Aug 2012 edition of the SRP_ 9 | // specification. 10 | // 11 | // Conventions 12 | // ----------- 13 | // N A large safe prime (N = 2q+1, where q is prime) 14 | // All arithmetic is done modulo N. 15 | // g A generator modulo N 16 | // k Multiplier parameter (k = H(N, g) in SRP-6a, k = 3 for legacy SRP-6) 17 | // s User's salt 18 | // I Username 19 | // p Cleartext Password 20 | // H() One-way hash function 21 | // ^ (Modular) Exponentiation 22 | // u Random scrambling parameter 23 | // a,b Secret ephemeral values 24 | // A,B Public ephemeral values 25 | // x Private key (derived from p and s) 26 | // v Password verifier 27 | // 28 | // The host stores passwords using the following formula: 29 | // 30 | // x = H(s, p) (s is chosen randomly) 31 | // v = g^x (computes password verifier) 32 | // 33 | // The host then keeps {I, s, v} in its password database. 34 | // 35 | // The authentication protocol itself goes as follows: 36 | // 37 | // User -> Host: I, A = g^a (identifies self, a = random number) 38 | // Host -> User: s, B = kv + g^b (sends salt, b = random number) 39 | // 40 | // Both: u = H(A, B) 41 | // 42 | // User: x = H(s, p) (user enters password) 43 | // User: S = (B - kg^x) ^ (a + ux) (computes session key) 44 | // User: K = H(S) 45 | // 46 | // Host: S = (Av^u) ^ b (computes session key) 47 | // Host: K = H(S) 48 | // 49 | // Now the two parties have a shared, strong session key K. 50 | // To complete authentication, they need to prove to each other that 51 | // their keys match. One possible way: 52 | // 53 | // User -> Host: M = H(H(N) xor H(g), H(I), s, A, B, K) 54 | // Host -> User: H(A, M, K) 55 | // 56 | // The two parties also employ the following safeguards: 57 | // 58 | // 1. The user will abort if he receives B == 0 (mod N) or u == 0. 59 | // 2. The host will abort if it detects that A == 0 (mod N). 60 | // 3. The user must show his proof of K first. If the server detects that the 61 | // user's proof is incorrect, it must abort without showing its own proof of K. 62 | // 63 | // In this implementation:: 64 | // 65 | // H = SHA256() 66 | // k = H(N, g) 67 | // x = HMAC-SHA(s, I, P) 68 | // I = anonymized form of user identity (SHA256 of value sent by client) 69 | // 70 | // There are two verifiers that are computed: 71 | // M1 = HMAC-SHA(K, H(A, b, I, s, N, g)) - sent by the client to the server 72 | // M2 = H(K, M1) - sent by the server to the client 73 | // 74 | // This convention guarantees that both parties can mutually conclude 75 | // that they have generated an identical key. 76 | // 77 | // 78 | // .. _SRP: http://srp.stanford.edu/ 79 | // 80 | 81 | package srp 82 | 83 | import ( 84 | "crypto/hmac" 85 | "crypto/sha256" 86 | "encoding/hex" 87 | "fmt" 88 | "io" 89 | "math/big" 90 | "strings" 91 | ) 92 | 93 | import CR "crypto/rand" 94 | 95 | // Map of bits to tuple 96 | var pflist_str = map[int][2]string{1024: {"2", "0xEEAF0AB9ADB38DD69C33F80AFA8FC5E86072618775FF3C0B9EA2314C9C256576D674DF7496EA81D3383B4813D692C6E0E0D5D8E250B98BE48E495C1D6089DAD15DC7D7B46154D6B6CE8EF4AD69B15D4982559B297BCF1885C529F566660E57EC68EDBC3C05726CC02FD4CBF4976EAA9AFD5138FE8376435B9FC61D2FC0EB06E3"}, 97 | 1536: {"2", "0x9DEF3CAFB939277AB1F12A8617A47BBBDBA51DF499AC4C80BEEEA9614B19CC4D5F4F5F556E27CBDE51C6A94BE4607A291558903BA0D0F84380B655BB9A22E8DCDF028A7CEC67F0D08134B1C8B97989149B609E0BE3BAB63D47548381DBC5B1FC764E3F4B53DD9DA1158BFD3E2B9C8CF56EDF019539349627DB2FD53D24B7C48665772E437D6C7F8CE442734AF7CCB7AE837C264AE3A9BEB87F8A2FE9B8B5292E5A021FFF5E91479E8CE7A28C2442C6F315180F93499A234DCF76E3FED135F9BB"}, 98 | 2048: {"2", "0xAC6BDB41324A9A9BF166DE5E1389582FAF72B6651987EE07FC3192943DB56050A37329CBB4A099ED8193E0757767A13DD52312AB4B03310DCD7F48A9DA04FD50E8083969EDB767B0CF6095179A163AB3661A05FBD5FAAAE82918A9962F0B93B855F97993EC975EEAA80D740ADBF4FF747359D041D5C33EA71D281E446B14773BCA97B43A23FB801676BD207A436C6481F1D2B9078717461A5B9D32E688F87748544523B524B0D57D5EA77A2775D2ECFA032CFBDBF52FB3786160279004E57AE6AF874E7303CE53299CCC041C7BC308D82A5698F3A8D0C38271AE35F8E9DBFBB694B5C803D89F7AE435DE236D525F54759B65E372FCD68EF20FA7111F9E4AFF73"}, 99 | 3072: {"2", "0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF"}, 100 | 4096: {"5", "0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199FFFFFFFFFFFFFFFF"}, 101 | 6144: {"5", "0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C93402849236C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AACC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E6DCC4024FFFFFFFFFFFFFFFF"}, 102 | 8192: {"5", "0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C93402849236C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AACC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E6DBE115974A3926F12FEE5E438777CB6A932DF8CD8BEC4D073B931BA3BC832B68D9DD300741FA7BF8AFC47ED2576F6936BA424663AAB639C5AE4F5683423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD922222E04A4037C0713EB57A81A23F0C73473FC646CEA306B4BCBC8862F8385DDFA9D4B7FA2C087E879683303ED5BDD3A062B3CF5B3A278A66D2A13F83F44F82DDF310EE074AB6A364597E899A0255DC164F31CC50846851DF9AB48195DED7EA1B1D510BD7EE74D73FAF36BC31ECFA268359046F4EB879F924009438B481C6CD7889A002ED5EE382BC9190DA6FC026E479558E4475677E9AA9E3050E2765694DFC81F56E880B96E7160C980DD98EDD3DFFFFFFFFFFFFFFFFF"}, 103 | } 104 | 105 | // XXX Use blake2 or keccak in the future 106 | var mac = sha256.New 107 | 108 | type prime_field struct { 109 | g *big.Int 110 | N *big.Int 111 | } 112 | 113 | // prime field list - mapped by bit size 114 | var pflist map[int]prime_field 115 | 116 | // build the database of prime fields and generators 117 | func init() { 118 | 119 | pflist = make(map[int]prime_field) 120 | 121 | for bits, arr := range pflist_str { 122 | g, ok0 := big.NewInt(0).SetString(arr[0], 10) 123 | n, ok1 := big.NewInt(0).SetString(arr[1], 0) 124 | 125 | if !ok0 { 126 | s := fmt.Sprintf("srp init: Can't parse string %s", arr[0]) 127 | panic(s) 128 | } 129 | 130 | if !ok1 { 131 | s := fmt.Sprintf("srp init: Can't parse string %s", arr[1]) 132 | panic(s) 133 | } 134 | 135 | pflist[bits] = prime_field{g: g, N: n} 136 | } 137 | } 138 | 139 | // hash byte stream and return as bytes 140 | func hashbyte(a ...[]byte) []byte { 141 | h := mac() 142 | for _, z := range a { 143 | h.Write(z) 144 | } 145 | return h.Sum(nil) 146 | } 147 | 148 | // hmac a number of byte streams 149 | func _hmac(key []byte, a ...[]byte) []byte { 150 | s := sha256.New 151 | h := hmac.New(s, key) 152 | 153 | for _, w := range a { 154 | h.Write(w) 155 | } 156 | 157 | return h.Sum(nil) 158 | } 159 | 160 | // hash a number of byte strings and return the resulting hash as 161 | // bigint 162 | func hashint(a ...[]byte) *big.Int { 163 | i := big.NewInt(0) 164 | b := hashbyte(a...) 165 | i.SetBytes(b) 166 | return i 167 | } 168 | 169 | // hash a number of byte strings and return the result as a human 170 | // readable string 171 | func hashhex(a ...[]byte) string { 172 | h := hashbyte(a...) 173 | s := fmt.Sprintf("%x", h) 174 | return s 175 | } 176 | 177 | // hmac a number of byte strings and return the resulting hash as 178 | // bigint 179 | func hmacint(key []byte, a ...[]byte) *big.Int { 180 | h := _hmac(key, a...) 181 | b := big.NewInt(0).SetBytes(h) 182 | return b 183 | } 184 | 185 | // Return n bytes of random bytes. Uses cryptographically strong 186 | // random generator 187 | func randbytes(n int) []byte { 188 | b := make([]byte, n) 189 | _, err := io.ReadFull(CR.Reader, b) 190 | if err != nil { 191 | panic("Random source is broken!") 192 | } 193 | return b 194 | } 195 | 196 | // Generate and return a bigInt 'bits' bits in length 197 | func randlong(bits int) *big.Int { 198 | n := bits / 8 199 | if 0 == bits%8 { 200 | n += 1 201 | } 202 | b := randbytes(n) 203 | r := big.NewInt(0).SetBytes(b) 204 | return r 205 | } 206 | 207 | // Generate a password veririer for user I and passphrase p 208 | // Return tuple containing hashed identity, salt, verifier. Caller 209 | // is expected to store the tuple in some persistent DB 210 | func Verifier(I, p []byte, bits int) (Ih, salt, v []byte, err error) { 211 | pf, ok := pflist[bits] 212 | if 0 == big.NewInt(0).Cmp(pf.N) || !ok { 213 | err = fmt.Errorf("Invalid bits: %d", bits) 214 | return 215 | } 216 | 217 | i := hashbyte(I) 218 | s := randbytes((bits / 2) / 8) 219 | x := hmacint(p, s, i) 220 | 221 | r := big.NewInt(0).Exp(pf.g, x, pf.N) 222 | 223 | salt = s 224 | Ih = i 225 | v = r.Bytes() 226 | 227 | return 228 | } 229 | 230 | // Represents an SRP Client instance 231 | type Client struct { 232 | g *big.Int 233 | N *big.Int 234 | i []byte 235 | p []byte 236 | a *big.Int 237 | A *big.Int 238 | k *big.Int 239 | 240 | K []byte 241 | M []byte 242 | } 243 | 244 | // Client SRP class constructor 245 | func NewClient(I, p []byte, bits int) (c *Client, err error) { 246 | c = new(Client) 247 | err = c.init(I, p, bits) 248 | 249 | return 250 | } 251 | 252 | func (c *Client) init(I, p []byte, bits int) (err error) { 253 | pf, ok := pflist[bits] 254 | if 0 == big.NewInt(0).Cmp(pf.N) || !ok { 255 | err = fmt.Errorf("Invalid bits: %d", bits) 256 | return 257 | } 258 | 259 | // g, N := field(bits) 260 | // a := generate random a 261 | // A := g^a % N 262 | // k := H(N, g) 263 | 264 | c.g = pf.g 265 | c.N = pf.N 266 | c.i = hashbyte(I) 267 | c.p = p 268 | c.a = randlong(bits) 269 | c.A = big.NewInt(0).Exp(pf.g, c.a, pf.N) 270 | c.k = hashint(c.N.Bytes(), c.g.Bytes()) 271 | 272 | return nil 273 | } 274 | 275 | // Return client public credentials to send to server 276 | // Send to server 277 | func (c *Client) Credentials() string { 278 | s0 := hex.EncodeToString(c.i) 279 | s1 := hex.EncodeToString(c.A.Bytes()) 280 | return s0 + ":" + s1 281 | } 282 | 283 | // Validate the server public credentials and generate session key 284 | // Return the mutual authenticator 285 | // - Get from server 286 | // - calculate S from a, s, B 287 | func (c *Client) Generate(srv string) (auth string, err error) { 288 | v := strings.Split(srv, ":") 289 | if len(v) != 2 { 290 | err = fmt.Errorf("Invalid server public key") 291 | return 292 | } 293 | 294 | s, err := hex.DecodeString(v[0]) 295 | if err != nil { 296 | err = fmt.Errorf("Invalid server public key s=%s", v[0]) 297 | return 298 | } 299 | 300 | B, ok1 := big.NewInt(0).SetString(v[1], 16) 301 | if !ok1 { 302 | err = fmt.Errorf("Invalid server public key B=%s", v[1]) 303 | return 304 | } 305 | 306 | zero := big.NewInt(0) 307 | z := big.NewInt(0).Mod(B, c.N) 308 | if zero.Cmp(z) == 0 { 309 | err = fmt.Errorf("Invalid server public key B=%x", B) 310 | return 311 | } 312 | 313 | u := hashint(c.A.Bytes(), B.Bytes()) 314 | if u.Cmp(zero) == 0 { 315 | err = fmt.Errorf("Invalid server public key u") 316 | return 317 | } 318 | 319 | // S := (B - kg^x) ^ (a + u * x) % N 320 | 321 | x := hmacint(c.p, s, c.i) 322 | 323 | t0 := big.NewInt(0).Exp(c.g, x, c.N) 324 | t0 = t0.Mul(t0, c.k) 325 | 326 | t1 := big.NewInt(0).Sub(B, t0) 327 | t2 := big.NewInt(0).Add(c.a, big.NewInt(0).Mul(u, x)) 328 | S := big.NewInt(0).Exp(t1, t2, c.N) 329 | 330 | c.K = hashbyte(S.Bytes()) 331 | 332 | c.M = _hmac(c.K, c.A.Bytes(), B.Bytes(), c.i, s, c.N.Bytes(), c.g.Bytes()) 333 | 334 | return hex.EncodeToString(c.M), nil 335 | } 336 | 337 | // Take a 'proof' offered by the server and verify that it is valid. 338 | // i.e., we should compute the same hmac() on M that the server did. 339 | func (c *Client) ServerOk(proof string) error { 340 | h := _hmac(c.K, c.M) 341 | myh := hex.EncodeToString(h) 342 | 343 | if !streq(myh, proof) { 344 | return fmt.Errorf("Server failed to generate same password") 345 | } 346 | 347 | return nil 348 | } 349 | 350 | // Return the raw key computed as part of the protocol 351 | func (c *Client) RawKey() []byte { 352 | return c.K 353 | } 354 | 355 | // Stringfy 356 | func (c *Client) String() string { 357 | return fmt.Sprintf(" g=%d, N=%x\n I=%x\n A=%x\n K=%x\n", 358 | c.g, c.N, c.i, c.A, c.K) 359 | } 360 | 361 | // Represents an SRP Server instance 362 | type Server struct { 363 | g *big.Int 364 | N *big.Int 365 | i []byte 366 | s []byte 367 | v *big.Int 368 | B *big.Int 369 | 370 | K []byte 371 | M []byte 372 | } 373 | 374 | // Begin the server processing by parsing the credentials sent by 375 | // the client. 376 | // The caller is expected to use 'I' to lookup some database and 377 | // find the verifier, salt and other user specific parameters. 378 | func ServerBegin(creds string) (I []byte, A *big.Int, err error) { 379 | v := strings.Split(creds, ":") 380 | if len(v) != 2 { 381 | err = fmt.Errorf("Invalid client public key") 382 | return 383 | } 384 | 385 | //fmt.Printf("v0: %s\nv1: %s\n", v[0], v[1]) 386 | 387 | A, ok := big.NewInt(0).SetString(v[1], 16) 388 | if !ok { 389 | err = fmt.Errorf("Invalid client public key A") 390 | return 391 | } 392 | 393 | I, err = hex.DecodeString(v[0]) 394 | return 395 | } 396 | 397 | // Constructor for the server type 398 | func NewServer(I, s, v []byte, A *big.Int, bits int) (c *Server, err error) { 399 | c = new(Server) 400 | err = c.init(I, s, v, A, bits) 401 | 402 | return 403 | } 404 | 405 | // Private method to initialize the Server SRP class 406 | func (c *Server) init(I, s, v []byte, A *big.Int, bits int) (err error) { 407 | pf, ok := pflist[bits] 408 | if !ok { 409 | err = fmt.Errorf("Invalid bits: %d", bits) 410 | return 411 | } 412 | 413 | c.v = big.NewInt(0).SetBytes(v) 414 | 415 | zero := big.NewInt(0) 416 | 417 | // g, N := field(bits) 418 | // b := generate random b 419 | // k := H(N, g) 420 | // B := kv + g^b 421 | // u := H(A, B) 422 | // S := (Av^u) ^ b 423 | // K := H(S) 424 | 425 | c.g = pf.g 426 | c.N = pf.N 427 | c.s = s 428 | c.i = I 429 | 430 | b := randlong(bits) 431 | k := hashint(c.N.Bytes(), c.g.Bytes()) 432 | t0 := big.NewInt(0).Mul(k, c.v) 433 | t1 := big.NewInt(0).Add(t0, big.NewInt(0).Exp(c.g, b, c.N)) 434 | B := t1 435 | 436 | u := hashint(A.Bytes(), B.Bytes()) 437 | if u.Cmp(zero) == 0 { 438 | err = fmt.Errorf("Invalid server public key u") 439 | return 440 | } 441 | 442 | t0 = big.NewInt(0).Mul(A, big.NewInt(0).Exp(c.v, u, c.N)) 443 | S := big.NewInt(0).Exp(t0, b, c.N) 444 | 445 | c.B = B 446 | c.K = hashbyte(S.Bytes()) 447 | c.M = _hmac(c.K, A.Bytes(), B.Bytes(), I, s, c.N.Bytes(), c.g.Bytes()) 448 | 449 | return nil 450 | } 451 | 452 | // Return the server credentials (s,B) in a network portable format. 453 | func (c *Server) Credentials() string { 454 | 455 | s0 := hex.EncodeToString(c.s) 456 | s1 := hex.EncodeToString(c.B.Bytes()) 457 | return s0 + ":" + s1 458 | } 459 | 460 | // Verify that the client has generated the same password as the 461 | // server and return proof that the server too has done the same. 462 | func (c *Server) ClientOk(m string) (proof string, err error) { 463 | mym := hex.EncodeToString(c.M) 464 | if !streq(mym, m) { 465 | err = fmt.Errorf("Client failed to generate same password") 466 | return 467 | } 468 | 469 | h := _hmac(c.K, c.M) 470 | return hex.EncodeToString(h), nil 471 | } 472 | 473 | // Return the raw key negotiated as part of the SRP 474 | func (c *Server) RawKey() []byte { 475 | return c.K 476 | } 477 | 478 | // Stringify the server parameters 479 | func (c *Server) String() string { 480 | return fmt.Sprintf(" g=%d, N=%x\n I=%x\n s=%x\n B=%x\n K=%x\n", 481 | c.g, c.N, c.i, c.s, c.B, c.K) 482 | } 483 | 484 | // Constant time string compare 485 | // XXX We don't use subtle.ConstantTimeByteEq() because it operates 486 | // on bytes not strings. 487 | func streq(a, b string) bool { 488 | m := len(a) 489 | n := len(b) 490 | 491 | if m != n { 492 | return false 493 | } 494 | 495 | var v uint8 496 | for i := 0; i < m; i++ { 497 | v |= a[i] ^ b[i] 498 | } 499 | 500 | return v == 0 501 | } 502 | 503 | // - EOF - 504 | -------------------------------------------------------------------------------- /vendor/gopass/LICENSE.txt: -------------------------------------------------------------------------------- 1 | ISC License 2 | 3 | Copyright (c) 2012 Chris Howey 4 | 5 | Permission to use, copy, modify, and distribute this software for any 6 | purpose with or without fee is hereby granted, provided that the above 7 | copyright notice and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | -------------------------------------------------------------------------------- /vendor/gopass/pass.go: -------------------------------------------------------------------------------- 1 | package gopass 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "io" 7 | "os" 8 | ) 9 | 10 | type FdReader interface { 11 | io.Reader 12 | Fd() uintptr 13 | } 14 | 15 | var defaultGetCh = func(r io.Reader) (byte, error) { 16 | buf := make([]byte, 1) 17 | if n, err := r.Read(buf); n == 0 || err != nil { 18 | if err != nil { 19 | return 0, err 20 | } 21 | return 0, io.EOF 22 | } 23 | return buf[0], nil 24 | } 25 | 26 | var ( 27 | maxLength = 512 28 | ErrInterrupted = errors.New("interrupted") 29 | ErrMaxLengthExceeded = fmt.Errorf("maximum byte limit (%v) exceeded", maxLength) 30 | 31 | // Provide variable so that tests can provide a mock implementation. 32 | getch = defaultGetCh 33 | ) 34 | 35 | // getPasswd returns the input read from terminal. 36 | // If prompt is not empty, it will be output as a prompt to the user 37 | // If masked is true, typing will be matched by asterisks on the screen. 38 | // Otherwise, typing will echo nothing. 39 | func getPasswd(prompt string, masked bool, r FdReader, w io.Writer) ([]byte, error) { 40 | var err error 41 | var pass, bs, mask []byte 42 | if masked { 43 | bs = []byte("\b \b") 44 | mask = []byte("*") 45 | } 46 | 47 | if isTerminal(r.Fd()) { 48 | if oldState, err := makeRaw(r.Fd()); err != nil { 49 | return pass, err 50 | } else { 51 | defer func() { 52 | restore(r.Fd(), oldState) 53 | fmt.Fprintln(w) 54 | }() 55 | } 56 | } 57 | 58 | if prompt != "" { 59 | fmt.Fprint(w, prompt) 60 | } 61 | 62 | // Track total bytes read, not just bytes in the password. This ensures any 63 | // errors that might flood the console with nil or -1 bytes infinitely are 64 | // capped. 65 | var counter int 66 | for counter = 0; counter <= maxLength; counter++ { 67 | if v, e := getch(r); e != nil { 68 | err = e 69 | break 70 | } else if v == 127 || v == 8 { 71 | if l := len(pass); l > 0 { 72 | pass = pass[:l-1] 73 | fmt.Fprint(w, string(bs)) 74 | } 75 | } else if v == 13 || v == 10 { 76 | break 77 | } else if v == 3 { 78 | err = ErrInterrupted 79 | break 80 | } else if v != 0 { 81 | pass = append(pass, v) 82 | fmt.Fprint(w, string(mask)) 83 | } 84 | } 85 | 86 | if counter > maxLength { 87 | err = ErrMaxLengthExceeded 88 | } 89 | 90 | return pass, err 91 | } 92 | 93 | // GetPasswd returns the password read from the terminal without echoing input. 94 | // The returned byte array does not include end-of-line characters. 95 | func GetPasswd() ([]byte, error) { 96 | return getPasswd("", false, os.Stdin, os.Stdout) 97 | } 98 | 99 | // GetPasswdMasked returns the password read from the terminal, echoing asterisks. 100 | // The returned byte array does not include end-of-line characters. 101 | func GetPasswdMasked() ([]byte, error) { 102 | return getPasswd("", true, os.Stdin, os.Stdout) 103 | } 104 | 105 | // GetPasswdPrompt prompts the user and returns the password read from the terminal. 106 | // If mask is true, then asterisks are echoed. 107 | // The returned byte array does not include end-of-line characters. 108 | func GetPasswdPrompt(prompt string, mask bool, r FdReader, w io.Writer) ([]byte, error) { 109 | return getPasswd(prompt, mask, r, w) 110 | } 111 | -------------------------------------------------------------------------------- /vendor/gopass/pass_test.go: -------------------------------------------------------------------------------- 1 | package gopass 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "fmt" 7 | "io" 8 | "io/ioutil" 9 | "os" 10 | "testing" 11 | "time" 12 | ) 13 | 14 | // TestGetPasswd tests the password creation and output based on a byte buffer 15 | // as input to mock the underlying getch() methods. 16 | func TestGetPasswd(t *testing.T) { 17 | type testData struct { 18 | input []byte 19 | 20 | // Due to how backspaces are written, it is easier to manually write 21 | // each expected output for the masked cases. 22 | masked string 23 | password string 24 | byesLeft int 25 | reason string 26 | } 27 | 28 | ds := []testData{ 29 | testData{[]byte("abc\n"), "***", "abc", 0, "Password parsing should stop at \\n"}, 30 | testData{[]byte("abc\r"), "***", "abc", 0, "Password parsing should stop at \\r"}, 31 | testData{[]byte("a\nbc\n"), "*", "a", 3, "Password parsing should stop at \\n"}, 32 | testData{[]byte("*!]|\n"), "****", "*!]|", 0, "Special characters shouldn't affect the password."}, 33 | 34 | testData{[]byte("abc\r\n"), "***", "abc", 1, 35 | "Password parsing should stop at \\r; Windows LINE_MODE should be unset so \\r is not converted to \\r\\n."}, 36 | 37 | testData{[]byte{'a', 'b', 'c', 8, '\n'}, "***\b \b", "ab", 0, "Backspace byte should remove the last read byte."}, 38 | testData{[]byte{'a', 'b', 127, 'c', '\n'}, "**\b \b*", "ac", 0, "Delete byte should remove the last read byte."}, 39 | testData{[]byte{'a', 'b', 127, 'c', 8, 127, '\n'}, "**\b \b*\b \b\b \b", "", 0, "Successive deletes continue to delete."}, 40 | testData{[]byte{8, 8, 8, '\n'}, "", "", 0, "Deletes before characters are noops."}, 41 | testData{[]byte{8, 8, 8, 'a', 'b', 'c', '\n'}, "***", "abc", 0, "Deletes before characters are noops."}, 42 | 43 | testData{[]byte{'a', 'b', 0, 'c', '\n'}, "***", "abc", 0, 44 | "Nil byte should be ignored due; may get unintended nil bytes from syscalls on Windows."}, 45 | } 46 | 47 | // Redirecting output for tests as they print to os.Stdout but we want to 48 | // capture and test the output. 49 | for _, masked := range []bool{true, false} { 50 | for _, d := range ds { 51 | pipeBytesToStdin(d.input) 52 | 53 | r, w, err := os.Pipe() 54 | if err != nil { 55 | t.Fatal(err.Error()) 56 | } 57 | 58 | result, err := getPasswd("", masked, os.Stdin, w) 59 | if err != nil { 60 | t.Errorf("Error getting password: %s", err.Error()) 61 | } 62 | leftOnBuffer := flushStdin() 63 | 64 | // Test output (masked and unmasked). Delete/backspace actually 65 | // deletes, overwrites and deletes again. As a result, we need to 66 | // remove those from the pipe afterwards to mimic the console's 67 | // interpretation of those bytes. 68 | w.Close() 69 | output, err := ioutil.ReadAll(r) 70 | if err != nil { 71 | t.Fatal(err.Error()) 72 | } 73 | var expectedOutput []byte 74 | if masked { 75 | expectedOutput = []byte(d.masked) 76 | } else { 77 | expectedOutput = []byte("") 78 | } 79 | if bytes.Compare(expectedOutput, output) != 0 { 80 | t.Errorf("Expected output to equal %v (%q) but got %v (%q) instead when masked=%v. %s", expectedOutput, string(expectedOutput), output, string(output), masked, d.reason) 81 | } 82 | 83 | if string(result) != d.password { 84 | t.Errorf("Expected %q but got %q instead when masked=%v. %s", d.password, result, masked, d.reason) 85 | } 86 | 87 | if leftOnBuffer != d.byesLeft { 88 | t.Errorf("Expected %v bytes left on buffer but instead got %v when masked=%v. %s", d.byesLeft, leftOnBuffer, masked, d.reason) 89 | } 90 | } 91 | } 92 | } 93 | 94 | // TestPipe ensures we get our expected pipe behavior. 95 | func TestPipe(t *testing.T) { 96 | type testData struct { 97 | input string 98 | password string 99 | expError error 100 | } 101 | ds := []testData{ 102 | testData{"abc", "abc", io.EOF}, 103 | testData{"abc\n", "abc", nil}, 104 | testData{"abc\r", "abc", nil}, 105 | testData{"abc\r\n", "abc", nil}, 106 | } 107 | 108 | for _, d := range ds { 109 | _, err := pipeToStdin(d.input) 110 | if err != nil { 111 | t.Log("Error writing input to stdin:", err) 112 | t.FailNow() 113 | } 114 | pass, err := GetPasswd() 115 | if string(pass) != d.password { 116 | t.Errorf("Expected %q but got %q instead.", d.password, string(pass)) 117 | } 118 | if err != d.expError { 119 | t.Errorf("Expected %v but got %q instead.", d.expError, err) 120 | } 121 | } 122 | } 123 | 124 | // flushStdin reads from stdin for .5 seconds to ensure no bytes are left on 125 | // the buffer. Returns the number of bytes read. 126 | func flushStdin() int { 127 | ch := make(chan byte) 128 | go func(ch chan byte) { 129 | reader := bufio.NewReader(os.Stdin) 130 | for { 131 | b, err := reader.ReadByte() 132 | if err != nil { // Maybe log non io.EOF errors, if you want 133 | close(ch) 134 | return 135 | } 136 | ch <- b 137 | } 138 | close(ch) 139 | }(ch) 140 | 141 | numBytes := 0 142 | for { 143 | select { 144 | case _, ok := <-ch: 145 | if !ok { 146 | return numBytes 147 | } 148 | numBytes++ 149 | case <-time.After(500 * time.Millisecond): 150 | return numBytes 151 | } 152 | } 153 | return numBytes 154 | } 155 | 156 | // pipeToStdin pipes the given string onto os.Stdin by replacing it with an 157 | // os.Pipe. The write end of the pipe is closed so that EOF is read after the 158 | // final byte. 159 | func pipeToStdin(s string) (int, error) { 160 | pipeReader, pipeWriter, err := os.Pipe() 161 | if err != nil { 162 | fmt.Println("Error getting os pipes:", err) 163 | os.Exit(1) 164 | } 165 | os.Stdin = pipeReader 166 | w, err := pipeWriter.WriteString(s) 167 | pipeWriter.Close() 168 | return w, err 169 | } 170 | 171 | func pipeBytesToStdin(b []byte) (int, error) { 172 | return pipeToStdin(string(b)) 173 | } 174 | 175 | // TestGetPasswd_Err tests errors are properly handled from getch() 176 | func TestGetPasswd_Err(t *testing.T) { 177 | var inBuffer *bytes.Buffer 178 | getch = func(io.Reader) (byte, error) { 179 | b, err := inBuffer.ReadByte() 180 | if err != nil { 181 | return 13, err 182 | } 183 | if b == 'z' { 184 | return 'z', fmt.Errorf("Forced error; byte returned should not be considered accurate.") 185 | } 186 | return b, nil 187 | } 188 | defer func() { getch = defaultGetCh }() 189 | 190 | for input, expectedPassword := range map[string]string{"abc": "abc", "abzc": "ab"} { 191 | inBuffer = bytes.NewBufferString(input) 192 | p, err := GetPasswdMasked() 193 | if string(p) != expectedPassword { 194 | t.Errorf("Expected %q but got %q instead.", expectedPassword, p) 195 | } 196 | if err == nil { 197 | t.Errorf("Expected error to be returned.") 198 | } 199 | } 200 | } 201 | 202 | func TestMaxPasswordLength(t *testing.T) { 203 | type testData struct { 204 | input []byte 205 | expectedErr error 206 | 207 | // Helper field to output in case of failure; rather than hundreds of 208 | // bytes. 209 | inputDesc string 210 | } 211 | 212 | ds := []testData{ 213 | testData{append(bytes.Repeat([]byte{'a'}, maxLength), '\n'), nil, fmt.Sprintf("%v 'a' bytes followed by a newline", maxLength)}, 214 | testData{append(bytes.Repeat([]byte{'a'}, maxLength+1), '\n'), ErrMaxLengthExceeded, fmt.Sprintf("%v 'a' bytes followed by a newline", maxLength+1)}, 215 | testData{append(bytes.Repeat([]byte{0x00}, maxLength+1), '\n'), ErrMaxLengthExceeded, fmt.Sprintf("%v 0x00 bytes followed by a newline", maxLength+1)}, 216 | } 217 | 218 | for _, d := range ds { 219 | pipeBytesToStdin(d.input) 220 | _, err := GetPasswd() 221 | if err != d.expectedErr { 222 | t.Errorf("Expected error to be %v; isntead got %v from %v", d.expectedErr, err, d.inputDesc) 223 | } 224 | } 225 | } 226 | -------------------------------------------------------------------------------- /vendor/gopass/terminal.go: -------------------------------------------------------------------------------- 1 | // +build !solaris 2 | 3 | package gopass 4 | 5 | import "golang.org/x/crypto/ssh/terminal" 6 | 7 | type terminalState struct { 8 | state *terminal.State 9 | } 10 | 11 | func isTerminal(fd uintptr) bool { 12 | return terminal.IsTerminal(int(fd)) 13 | } 14 | 15 | func makeRaw(fd uintptr) (*terminalState, error) { 16 | state, err := terminal.MakeRaw(int(fd)) 17 | 18 | return &terminalState{ 19 | state: state, 20 | }, err 21 | } 22 | 23 | func restore(fd uintptr, oldState *terminalState) error { 24 | return terminal.Restore(int(fd), oldState.state) 25 | } 26 | -------------------------------------------------------------------------------- /vendor/gopass/terminal_solaris.go: -------------------------------------------------------------------------------- 1 | /* 2 | * CDDL HEADER START 3 | * 4 | * The contents of this file are subject to the terms of the 5 | * Common Development and Distribution License, Version 1.0 only 6 | * (the "License"). You may not use this file except in compliance 7 | * with the License. 8 | * 9 | * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 | * or http://www.opensolaris.org/os/licensing. 11 | * See the License for the specific language governing permissions 12 | * and limitations under the License. 13 | * 14 | * When distributing Covered Code, include this CDDL HEADER in each 15 | * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 | * If applicable, add the following below this CDDL HEADER, with the 17 | * fields enclosed by brackets "[]" replaced with your own identifying 18 | * information: Portions Copyright [yyyy] [name of copyright owner] 19 | * 20 | * CDDL HEADER END 21 | */ 22 | // Below is derived from Solaris source, so CDDL license is included. 23 | 24 | package gopass 25 | 26 | import ( 27 | "syscall" 28 | 29 | "golang.org/x/sys/unix" 30 | ) 31 | 32 | type terminalState struct { 33 | state *unix.Termios 34 | } 35 | 36 | // isTerminal returns true if there is a terminal attached to the given 37 | // file descriptor. 38 | // Source: http://src.illumos.org/source/xref/illumos-gate/usr/src/lib/libbc/libc/gen/common/isatty.c 39 | func isTerminal(fd uintptr) bool { 40 | var termio unix.Termio 41 | err := unix.IoctlSetTermio(int(fd), unix.TCGETA, &termio) 42 | return err == nil 43 | } 44 | 45 | // makeRaw puts the terminal connected to the given file descriptor into raw 46 | // mode and returns the previous state of the terminal so that it can be 47 | // restored. 48 | // Source: http://src.illumos.org/source/xref/illumos-gate/usr/src/lib/libast/common/uwin/getpass.c 49 | func makeRaw(fd uintptr) (*terminalState, error) { 50 | oldTermiosPtr, err := unix.IoctlGetTermios(int(fd), unix.TCGETS) 51 | if err != nil { 52 | return nil, err 53 | } 54 | oldTermios := *oldTermiosPtr 55 | 56 | newTermios := oldTermios 57 | newTermios.Lflag &^= syscall.ECHO | syscall.ECHOE | syscall.ECHOK | syscall.ECHONL 58 | if err := unix.IoctlSetTermios(int(fd), unix.TCSETS, &newTermios); err != nil { 59 | return nil, err 60 | } 61 | 62 | return &terminalState{ 63 | state: oldTermiosPtr, 64 | }, nil 65 | } 66 | 67 | func restore(fd uintptr, oldState *terminalState) error { 68 | return unix.IoctlSetTermios(int(fd), unix.TCSETS, oldState.state) 69 | } 70 | -------------------------------------------------------------------------------- /vendor/tree/radix/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Armon Dadgar 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /vendor/tree/radix/README.md: -------------------------------------------------------------------------------- 1 | go-radix [![Build Status](https://travis-ci.org/armon/go-radix.png)](https://travis-ci.org/armon/go-radix) 2 | ========= 3 | 4 | Provides the `radix` package that implements a [radix tree](http://en.wikipedia.org/wiki/Radix_tree). 5 | The package only provides a single `Tree` implementation, optimized for sparse nodes. 6 | 7 | As a radix tree, it provides the following: 8 | * O(k) operations. In many cases, this can be faster than a hash table since 9 | the hash function is an O(k) operation, and hash tables have very poor cache locality. 10 | * Minimum / Maximum value lookups 11 | * Ordered iteration 12 | 13 | For an immutable variant, see [go-immutable-radix](https://github.com/hashicorp/go-immutable-radix). 14 | 15 | Documentation 16 | ============= 17 | 18 | The full documentation is available on [Godoc](http://godoc.org/github.com/armon/go-radix). 19 | 20 | Example 21 | ======= 22 | 23 | Below is a simple example of usage 24 | 25 | ```go 26 | // Create a tree 27 | r := radix.New() 28 | r.Insert("foo", 1) 29 | r.Insert("bar", 2) 30 | r.Insert("foobar", 2) 31 | 32 | // Find the longest prefix match 33 | m, _, _ := r.LongestPrefix("foozip") 34 | if m != "foo" { 35 | panic("should be foo") 36 | } 37 | ``` 38 | 39 | -------------------------------------------------------------------------------- /vendor/tree/radix/radix.go: -------------------------------------------------------------------------------- 1 | /*** 2 | ** Code By armon/go-radix 3 | ** Edited By ahmdrz 4 | ***/ 5 | 6 | package radix 7 | 8 | import ( 9 | "sort" 10 | "strings" 11 | ) 12 | 13 | const ( 14 | NORMAL = "normal" 15 | JSON = "json" 16 | UNDEFIEND = "undefiend" 17 | ) 18 | 19 | // WalkFn is used when walking the tree. Takes a 20 | // key and value, returning if iteration should 21 | // be terminated. 22 | type WalkFn func(s string, v interface{}) bool 23 | 24 | // leafNode is used to represent a value 25 | type leafNode struct { 26 | key string 27 | val interface{} 28 | mode string 29 | } 30 | 31 | // edge is used to represent an edge node 32 | type edge struct { 33 | label byte 34 | node *node 35 | } 36 | 37 | type node struct { 38 | // leaf is used to store possible leaf 39 | leaf *leafNode 40 | 41 | // prefix is the common prefix we ignore 42 | prefix string 43 | 44 | // Edges should be stored in-order for iteration. 45 | // We avoid a fully materialized slice to save memory, 46 | // since in most cases we expect to be sparse 47 | edges edges 48 | } 49 | 50 | func (n *node) isLeaf() bool { 51 | return n.leaf != nil 52 | } 53 | 54 | func (n *node) addEdge(e edge) { 55 | n.edges = append(n.edges, e) 56 | n.edges.Sort() 57 | } 58 | 59 | func (n *node) replaceEdge(e edge) { 60 | num := len(n.edges) 61 | idx := sort.Search(num, func(i int) bool { 62 | return n.edges[i].label >= e.label 63 | }) 64 | if idx < num && n.edges[idx].label == e.label { 65 | n.edges[idx].node = e.node 66 | return 67 | } 68 | panic("replacing missing edge") 69 | } 70 | 71 | func (n *node) getEdge(label byte) *node { 72 | num := len(n.edges) 73 | idx := sort.Search(num, func(i int) bool { 74 | return n.edges[i].label >= label 75 | }) 76 | if idx < num && n.edges[idx].label == label { 77 | return n.edges[idx].node 78 | } 79 | return nil 80 | } 81 | 82 | func (n *node) delEdge(label byte) { 83 | num := len(n.edges) 84 | idx := sort.Search(num, func(i int) bool { 85 | return n.edges[i].label >= label 86 | }) 87 | if idx < num && n.edges[idx].label == label { 88 | copy(n.edges[idx:], n.edges[idx+1:]) 89 | n.edges[len(n.edges)-1] = edge{} 90 | n.edges = n.edges[:len(n.edges)-1] 91 | } 92 | } 93 | 94 | type edges []edge 95 | 96 | func (e edges) Len() int { 97 | return len(e) 98 | } 99 | 100 | func (e edges) Less(i, j int) bool { 101 | return e[i].label < e[j].label 102 | } 103 | 104 | func (e edges) Swap(i, j int) { 105 | e[i], e[j] = e[j], e[i] 106 | } 107 | 108 | func (e edges) Sort() { 109 | sort.Sort(e) 110 | } 111 | 112 | // Tree implements a radix tree. This can be treated as a 113 | // Dictionary abstract data type. The main advantage over 114 | // a standard hash map is prefix-based lookups and 115 | // ordered iteration, 116 | type Tree struct { 117 | root *node 118 | size int 119 | } 120 | 121 | // New returns an empty Tree 122 | func New() *Tree { 123 | return NewFromMap(nil) 124 | } 125 | 126 | // NewFromMap returns a new tree containing the keys 127 | // from an existing map 128 | func NewFromMap(m map[string]interface{}) *Tree { 129 | t := &Tree{root: &node{}} 130 | for k, v := range m { 131 | t.Insert(k, v) 132 | } 133 | return t 134 | } 135 | 136 | // Len is used to return the number of elements in the tree 137 | func (t *Tree) Len() int { 138 | return t.size 139 | } 140 | 141 | // longestPrefix finds the length of the shared prefix 142 | // of two strings 143 | func longestPrefix(k1, k2 string) int { 144 | max := len(k1) 145 | if l := len(k2); l < max { 146 | max = l 147 | } 148 | var i int 149 | for i = 0; i < max; i++ { 150 | if k1[i] != k2[i] { 151 | break 152 | } 153 | } 154 | return i 155 | } 156 | 157 | // Insert is used to add a newentry or update 158 | // an existing entry. Returns if updated. 159 | func (t *Tree) Insert(s string, v interface{}, m ...string) (interface{}, bool) { 160 | mode := NORMAL 161 | if len(m) > 0 { 162 | if m[0] == JSON { 163 | mode = JSON 164 | } 165 | } 166 | var parent *node 167 | n := t.root 168 | search := s 169 | for { 170 | // Handle key exhaution 171 | if len(search) == 0 { 172 | if n.isLeaf() { 173 | old := n.leaf.val 174 | n.leaf.val = v 175 | return old, true 176 | } 177 | 178 | n.leaf = &leafNode{ 179 | key: s, 180 | val: v, 181 | mode: mode, 182 | } 183 | t.size++ 184 | return nil, false 185 | } 186 | 187 | // Look for the edge 188 | parent = n 189 | n = n.getEdge(search[0]) 190 | 191 | // No edge, create one 192 | if n == nil { 193 | e := edge{ 194 | label: search[0], 195 | node: &node{ 196 | leaf: &leafNode{ 197 | key: s, 198 | val: v, 199 | mode: mode, 200 | }, 201 | prefix: search, 202 | }, 203 | } 204 | parent.addEdge(e) 205 | t.size++ 206 | return nil, false 207 | } 208 | 209 | // Determine longest prefix of the search key on match 210 | commonPrefix := longestPrefix(search, n.prefix) 211 | if commonPrefix == len(n.prefix) { 212 | search = search[commonPrefix:] 213 | continue 214 | } 215 | 216 | // Split the node 217 | t.size++ 218 | child := &node{ 219 | prefix: search[:commonPrefix], 220 | } 221 | parent.replaceEdge(edge{ 222 | label: search[0], 223 | node: child, 224 | }) 225 | 226 | // Restore the existing node 227 | child.addEdge(edge{ 228 | label: n.prefix[commonPrefix], 229 | node: n, 230 | }) 231 | n.prefix = n.prefix[commonPrefix:] 232 | 233 | // Create a new leaf node 234 | leaf := &leafNode{ 235 | key: s, 236 | val: v, 237 | mode: mode, 238 | } 239 | 240 | // If the new key is a subset, add to to this node 241 | search = search[commonPrefix:] 242 | if len(search) == 0 { 243 | child.leaf = leaf 244 | return nil, false 245 | } 246 | 247 | // Create a new edge for the node 248 | child.addEdge(edge{ 249 | label: search[0], 250 | node: &node{ 251 | leaf: leaf, 252 | prefix: search, 253 | }, 254 | }) 255 | return nil, false 256 | } 257 | } 258 | 259 | // Delete is used to delete a key, returning the previous 260 | // value and if it was deleted 261 | func (t *Tree) Delete(s string) (interface{}, bool) { 262 | var parent *node 263 | var label byte 264 | n := t.root 265 | search := s 266 | for { 267 | // Check for key exhaution 268 | if len(search) == 0 { 269 | if !n.isLeaf() { 270 | break 271 | } 272 | goto DELETE 273 | } 274 | 275 | // Look for an edge 276 | parent = n 277 | label = search[0] 278 | n = n.getEdge(label) 279 | if n == nil { 280 | break 281 | } 282 | 283 | // Consume the search prefix 284 | if strings.HasPrefix(search, n.prefix) { 285 | search = search[len(n.prefix):] 286 | } else { 287 | break 288 | } 289 | } 290 | return nil, false 291 | 292 | DELETE: 293 | // Delete the leaf 294 | leaf := n.leaf 295 | n.leaf = nil 296 | t.size-- 297 | 298 | // Check if we should delete this node from the parent 299 | if parent != nil && len(n.edges) == 0 { 300 | parent.delEdge(label) 301 | } 302 | 303 | // Check if we should merge this node 304 | if n != t.root && len(n.edges) == 1 { 305 | n.mergeChild() 306 | } 307 | 308 | // Check if we should merge the parent's other child 309 | if parent != nil && parent != t.root && len(parent.edges) == 1 && !parent.isLeaf() { 310 | parent.mergeChild() 311 | } 312 | 313 | return leaf.val, true 314 | } 315 | 316 | func (n *node) mergeChild() { 317 | e := n.edges[0] 318 | child := e.node 319 | n.prefix = n.prefix + child.prefix 320 | n.leaf = child.leaf 321 | n.edges = child.edges 322 | } 323 | 324 | // Get is used to lookup a specific key, returning 325 | // the value and if it was found 326 | func (t *Tree) Get(s string) (interface{}, bool, string) { 327 | n := t.root 328 | search := s 329 | for { 330 | // Check for key exhaution 331 | if len(search) == 0 { 332 | if n.isLeaf() { 333 | return n.leaf.val, true, n.leaf.mode 334 | } 335 | break 336 | } 337 | 338 | // Look for an edge 339 | n = n.getEdge(search[0]) 340 | if n == nil { 341 | break 342 | } 343 | 344 | // Consume the search prefix 345 | if strings.HasPrefix(search, n.prefix) { 346 | search = search[len(n.prefix):] 347 | } else { 348 | break 349 | } 350 | } 351 | return nil, false, UNDEFIEND 352 | } 353 | 354 | func (t *Tree) Set(s string, val interface{}, m ...string) (interface{}, bool) { 355 | mode := NORMAL 356 | if len(m) > 0 { 357 | if m[0] == JSON { 358 | mode = JSON 359 | } 360 | } 361 | n := t.root 362 | search := s 363 | for { 364 | // Check for key exhaution 365 | if len(search) == 0 { 366 | if n.isLeaf() { 367 | n.leaf.val = val 368 | n.leaf.mode = mode 369 | return n.leaf.val, true 370 | } 371 | break 372 | } 373 | 374 | // Look for an edge 375 | n = n.getEdge(search[0]) 376 | if n == nil { 377 | break 378 | } 379 | 380 | // Consume the search prefix 381 | if strings.HasPrefix(search, n.prefix) { 382 | search = search[len(n.prefix):] 383 | } else { 384 | break 385 | } 386 | } 387 | return nil, false 388 | } 389 | 390 | // LongestPrefix is like Get, but instead of an 391 | // exact match, it will return the longest prefix match. 392 | func (t *Tree) LongestPrefix(s string) (string, interface{}, bool) { 393 | var last *leafNode 394 | n := t.root 395 | search := s 396 | for { 397 | // Look for a leaf node 398 | if n.isLeaf() { 399 | last = n.leaf 400 | } 401 | 402 | // Check for key exhaution 403 | if len(search) == 0 { 404 | break 405 | } 406 | 407 | // Look for an edge 408 | n = n.getEdge(search[0]) 409 | if n == nil { 410 | break 411 | } 412 | 413 | // Consume the search prefix 414 | if strings.HasPrefix(search, n.prefix) { 415 | search = search[len(n.prefix):] 416 | } else { 417 | break 418 | } 419 | } 420 | if last != nil { 421 | return last.key, last.val, true 422 | } 423 | return "", nil, false 424 | } 425 | 426 | // Minimum is used to return the minimum value in the tree 427 | func (t *Tree) Minimum() (string, interface{}, bool) { 428 | n := t.root 429 | for { 430 | if n.isLeaf() { 431 | return n.leaf.key, n.leaf.val, true 432 | } 433 | if len(n.edges) > 0 { 434 | n = n.edges[0].node 435 | } else { 436 | break 437 | } 438 | } 439 | return "", nil, false 440 | } 441 | 442 | // Maximum is used to return the maximum value in the tree 443 | func (t *Tree) Maximum() (string, interface{}, bool) { 444 | n := t.root 445 | for { 446 | if num := len(n.edges); num > 0 { 447 | n = n.edges[num-1].node 448 | continue 449 | } 450 | if n.isLeaf() { 451 | return n.leaf.key, n.leaf.val, true 452 | } 453 | break 454 | } 455 | return "", nil, false 456 | } 457 | 458 | // Walk is used to walk the tree 459 | func (t *Tree) Walk(fn WalkFn) { 460 | recursiveWalk(t.root, fn) 461 | } 462 | 463 | // WalkPrefix is used to walk the tree under a prefix 464 | func (t *Tree) WalkPrefix(prefix string, fn WalkFn) { 465 | n := t.root 466 | search := prefix 467 | for { 468 | // Check for key exhaution 469 | if len(search) == 0 { 470 | recursiveWalk(n, fn) 471 | return 472 | } 473 | 474 | // Look for an edge 475 | n = n.getEdge(search[0]) 476 | if n == nil { 477 | break 478 | } 479 | 480 | // Consume the search prefix 481 | if strings.HasPrefix(search, n.prefix) { 482 | search = search[len(n.prefix):] 483 | 484 | } else if strings.HasPrefix(n.prefix, search) { 485 | // Child may be under our search prefix 486 | recursiveWalk(n, fn) 487 | return 488 | } else { 489 | break 490 | } 491 | } 492 | 493 | } 494 | 495 | // WalkPath is used to walk the tree, but only visiting nodes 496 | // from the root down to a given leaf. Where WalkPrefix walks 497 | // all the entries *under* the given prefix, this walks the 498 | // entries *above* the given prefix. 499 | func (t *Tree) WalkPath(path string, fn WalkFn) { 500 | n := t.root 501 | search := path 502 | for { 503 | // Visit the leaf values if any 504 | if n.leaf != nil && fn(n.leaf.key, n.leaf.val) { 505 | return 506 | } 507 | 508 | // Check for key exhaution 509 | if len(search) == 0 { 510 | return 511 | } 512 | 513 | // Look for an edge 514 | n = n.getEdge(search[0]) 515 | if n == nil { 516 | return 517 | } 518 | 519 | // Consume the search prefix 520 | if strings.HasPrefix(search, n.prefix) { 521 | search = search[len(n.prefix):] 522 | } else { 523 | break 524 | } 525 | } 526 | } 527 | 528 | // recursiveWalk is used to do a pre-order walk of a node 529 | // recursively. Returns true if the walk should be aborted 530 | func recursiveWalk(n *node, fn WalkFn) bool { 531 | // Visit the leaf values if any 532 | if n.leaf != nil && fn(n.leaf.key, n.leaf.val) { 533 | return true 534 | } 535 | 536 | // Recurse on the children 537 | for _, e := range n.edges { 538 | if recursiveWalk(e.node, fn) { 539 | return true 540 | } 541 | } 542 | return false 543 | } 544 | 545 | // ToMap is used to walk the tree and convert it into a map 546 | func (t *Tree) ToMap() map[string]interface{} { 547 | out := make(map[string]interface{}, t.size) 548 | t.Walk(func(k string, v interface{}) bool { 549 | out[k] = v 550 | return false 551 | }) 552 | return out 553 | } 554 | -------------------------------------------------------------------------------- /vendor/tree/radix/radix_test.go: -------------------------------------------------------------------------------- 1 | package radix 2 | 3 | import ( 4 | crand "crypto/rand" 5 | "fmt" 6 | "reflect" 7 | "sort" 8 | "testing" 9 | ) 10 | 11 | func TestRadix(t *testing.T) { 12 | var min, max string 13 | inp := make(map[string]interface{}) 14 | for i := 0; i < 1000; i++ { 15 | gen := generateUUID() 16 | inp[gen] = i 17 | if gen < min || i == 0 { 18 | min = gen 19 | } 20 | if gen > max || i == 0 { 21 | max = gen 22 | } 23 | } 24 | 25 | r := NewFromMap(inp) 26 | if r.Len() != len(inp) { 27 | t.Fatalf("bad length: %v %v", r.Len(), len(inp)) 28 | } 29 | 30 | r.Walk(func(k string, v interface{}) bool { 31 | println(k) 32 | return false 33 | }) 34 | 35 | for k, v := range inp { 36 | out, ok := r.Get(k) 37 | if !ok { 38 | t.Fatalf("missing key: %v", k) 39 | } 40 | if out != v { 41 | t.Fatalf("value mis-match: %v %v", out, v) 42 | } 43 | } 44 | 45 | // Check min and max 46 | outMin, _, _ := r.Minimum() 47 | if outMin != min { 48 | t.Fatalf("bad minimum: %v %v", outMin, min) 49 | } 50 | outMax, _, _ := r.Maximum() 51 | if outMax != max { 52 | t.Fatalf("bad maximum: %v %v", outMax, max) 53 | } 54 | 55 | for k, v := range inp { 56 | out, ok := r.Delete(k) 57 | if !ok { 58 | t.Fatalf("missing key: %v", k) 59 | } 60 | if out != v { 61 | t.Fatalf("value mis-match: %v %v", out, v) 62 | } 63 | } 64 | if r.Len() != 0 { 65 | t.Fatalf("bad length: %v", r.Len()) 66 | } 67 | } 68 | 69 | func TestRoot(t *testing.T) { 70 | r := New() 71 | _, ok := r.Delete("") 72 | if ok { 73 | t.Fatalf("bad") 74 | } 75 | _, ok = r.Insert("", true) 76 | if ok { 77 | t.Fatalf("bad") 78 | } 79 | val, ok := r.Get("") 80 | if !ok || val != true { 81 | t.Fatalf("bad: %v", val) 82 | } 83 | val, ok = r.Delete("") 84 | if !ok || val != true { 85 | t.Fatalf("bad: %v", val) 86 | } 87 | } 88 | 89 | func TestDelete(t *testing.T) { 90 | 91 | r := New() 92 | 93 | s := []string{"", "A", "AB"} 94 | 95 | for _, ss := range s { 96 | r.Insert(ss, true) 97 | } 98 | 99 | for _, ss := range s { 100 | _, ok := r.Delete(ss) 101 | if !ok { 102 | t.Fatalf("bad %q", ss) 103 | } 104 | } 105 | } 106 | 107 | func TestLongestPrefix(t *testing.T) { 108 | r := New() 109 | 110 | keys := []string{ 111 | "", 112 | "foo", 113 | "foobar", 114 | "foobarbaz", 115 | "foobarbazzip", 116 | "foozip", 117 | } 118 | for _, k := range keys { 119 | r.Insert(k, nil) 120 | } 121 | if r.Len() != len(keys) { 122 | t.Fatalf("bad len: %v %v", r.Len(), len(keys)) 123 | } 124 | 125 | type exp struct { 126 | inp string 127 | out string 128 | } 129 | cases := []exp{ 130 | {"a", ""}, 131 | {"abc", ""}, 132 | {"fo", ""}, 133 | {"foo", "foo"}, 134 | {"foob", "foo"}, 135 | {"foobar", "foobar"}, 136 | {"foobarba", "foobar"}, 137 | {"foobarbaz", "foobarbaz"}, 138 | {"foobarbazzi", "foobarbaz"}, 139 | {"foobarbazzip", "foobarbazzip"}, 140 | {"foozi", "foo"}, 141 | {"foozip", "foozip"}, 142 | {"foozipzap", "foozip"}, 143 | } 144 | for _, test := range cases { 145 | m, _, ok := r.LongestPrefix(test.inp) 146 | if !ok { 147 | t.Fatalf("no match: %v", test) 148 | } 149 | if m != test.out { 150 | t.Fatalf("mis-match: %v %v", m, test) 151 | } 152 | } 153 | } 154 | 155 | func TestWalkPrefix(t *testing.T) { 156 | r := New() 157 | 158 | keys := []string{ 159 | "foobar", 160 | "foo/bar/baz", 161 | "foo/baz/bar", 162 | "foo/zip/zap", 163 | "zipzap", 164 | } 165 | for _, k := range keys { 166 | r.Insert(k, nil) 167 | } 168 | if r.Len() != len(keys) { 169 | t.Fatalf("bad len: %v %v", r.Len(), len(keys)) 170 | } 171 | 172 | type exp struct { 173 | inp string 174 | out []string 175 | } 176 | cases := []exp{ 177 | { 178 | "f", 179 | []string{"foobar", "foo/bar/baz", "foo/baz/bar", "foo/zip/zap"}, 180 | }, 181 | { 182 | "foo", 183 | []string{"foobar", "foo/bar/baz", "foo/baz/bar", "foo/zip/zap"}, 184 | }, 185 | { 186 | "foob", 187 | []string{"foobar"}, 188 | }, 189 | { 190 | "foo/", 191 | []string{"foo/bar/baz", "foo/baz/bar", "foo/zip/zap"}, 192 | }, 193 | { 194 | "foo/b", 195 | []string{"foo/bar/baz", "foo/baz/bar"}, 196 | }, 197 | { 198 | "foo/ba", 199 | []string{"foo/bar/baz", "foo/baz/bar"}, 200 | }, 201 | { 202 | "foo/bar", 203 | []string{"foo/bar/baz"}, 204 | }, 205 | { 206 | "foo/bar/baz", 207 | []string{"foo/bar/baz"}, 208 | }, 209 | { 210 | "foo/bar/bazoo", 211 | []string{}, 212 | }, 213 | { 214 | "z", 215 | []string{"zipzap"}, 216 | }, 217 | } 218 | 219 | for _, test := range cases { 220 | out := []string{} 221 | fn := func(s string, v interface{}) bool { 222 | out = append(out, s) 223 | return false 224 | } 225 | r.WalkPrefix(test.inp, fn) 226 | sort.Strings(out) 227 | sort.Strings(test.out) 228 | if !reflect.DeepEqual(out, test.out) { 229 | t.Fatalf("mis-match: %v %v", out, test.out) 230 | } 231 | } 232 | } 233 | 234 | func TestWalkPath(t *testing.T) { 235 | r := New() 236 | 237 | keys := []string{ 238 | "foo", 239 | "foo/bar", 240 | "foo/bar/baz", 241 | "foo/baz/bar", 242 | "foo/zip/zap", 243 | "zipzap", 244 | } 245 | for _, k := range keys { 246 | r.Insert(k, nil) 247 | } 248 | if r.Len() != len(keys) { 249 | t.Fatalf("bad len: %v %v", r.Len(), len(keys)) 250 | } 251 | 252 | type exp struct { 253 | inp string 254 | out []string 255 | } 256 | cases := []exp{ 257 | { 258 | "f", 259 | []string{}, 260 | }, 261 | { 262 | "foo", 263 | []string{"foo"}, 264 | }, 265 | { 266 | "foo/", 267 | []string{"foo"}, 268 | }, 269 | { 270 | "foo/ba", 271 | []string{"foo"}, 272 | }, 273 | { 274 | "foo/bar", 275 | []string{"foo", "foo/bar"}, 276 | }, 277 | { 278 | "foo/bar/baz", 279 | []string{"foo", "foo/bar", "foo/bar/baz"}, 280 | }, 281 | { 282 | "foo/bar/bazoo", 283 | []string{"foo", "foo/bar", "foo/bar/baz"}, 284 | }, 285 | { 286 | "z", 287 | []string{}, 288 | }, 289 | } 290 | 291 | for _, test := range cases { 292 | out := []string{} 293 | fn := func(s string, v interface{}) bool { 294 | out = append(out, s) 295 | return false 296 | } 297 | r.WalkPath(test.inp, fn) 298 | sort.Strings(out) 299 | sort.Strings(test.out) 300 | if !reflect.DeepEqual(out, test.out) { 301 | t.Fatalf("mis-match: %v %v", out, test.out) 302 | } 303 | } 304 | } 305 | 306 | // generateUUID is used to generate a random UUID 307 | func generateUUID() string { 308 | buf := make([]byte, 16) 309 | if _, err := crand.Read(buf); err != nil { 310 | panic(fmt.Errorf("failed to read random bytes: %v", err)) 311 | } 312 | 313 | return fmt.Sprintf("%08x-%04x-%04x-%04x-%12x", 314 | buf[0:4], 315 | buf[4:6], 316 | buf[6:8], 317 | buf[8:10], 318 | buf[10:16]) 319 | } 320 | -------------------------------------------------------------------------------- /vendor/tree/tree.go: -------------------------------------------------------------------------------- 1 | package tree 2 | 3 | import ( 4 | "tree/radix" 5 | ) 6 | 7 | func NewTree() *Tree { 8 | return &Tree{ 9 | tree: radix.New(), 10 | } 11 | } 12 | 13 | func (t *Tree) Del(key string) { 14 | t.tree.Delete(key) 15 | } 16 | 17 | func (t *Tree) Get(key string) (interface{}, string) { 18 | br, exists, mode := t.tree.Get(key) 19 | if exists { 20 | return br, mode 21 | } 22 | return "UNDEFIEND", radix.UNDEFIEND 23 | } 24 | 25 | func (t *Tree) Clr() int { 26 | m := t.tree.ToMap() 27 | for key := range m { 28 | t.tree.Delete(key) 29 | } 30 | return len(m) 31 | } 32 | 33 | func (t *Tree) Set(key string, value interface{}) { 34 | _, ok := t.tree.Set(key, value) 35 | if !ok { 36 | t.tree.Insert(key, value) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /vendor/tree/types.go: -------------------------------------------------------------------------------- 1 | package tree 2 | 3 | import ( 4 | "strconv" 5 | "tree/radix" 6 | ) 7 | 8 | type Tree struct { 9 | tree *radix.Tree 10 | } 11 | 12 | type Type struct { 13 | Value interface{} 14 | } 15 | 16 | func (t Type) Int() int { 17 | return t.Value.(int) 18 | } 19 | 20 | func (t Type) Int8() int8 { 21 | return t.Value.(int8) 22 | } 23 | 24 | func (t Type) Int16() int16 { 25 | return t.Value.(int16) 26 | } 27 | 28 | func (t Type) Int32() int32 { 29 | return t.Value.(int32) 30 | } 31 | 32 | func (t Type) Int64() int64 { 33 | return t.Value.(int64) 34 | } 35 | 36 | func (t Type) String() string { 37 | return t.Value.(string) 38 | } 39 | 40 | func (t Type) Float32() float32 { 41 | return t.Value.(float32) 42 | } 43 | 44 | func (t Type) Float64() float64 { 45 | return t.Value.(float64) 46 | } 47 | 48 | func (t Type) Bool() bool { 49 | return t.Value.(bool) 50 | } 51 | 52 | func (t Type) ToString() string { 53 | switch t.Value.(type) { 54 | case bool: 55 | if t.Value.(bool) { 56 | return "true" 57 | } 58 | return "false" 59 | case int: 60 | return strconv.Itoa(t.Value.(int)) 61 | case int64: 62 | return strconv.FormatInt(t.Value.(int64), 10) 63 | case int8: 64 | return strconv.Itoa(int(t.Value.(int8))) 65 | case uint8: 66 | return strconv.Itoa(int(t.Value.(uint8))) 67 | case uint16: 68 | return strconv.Itoa(int(t.Value.(uint16))) 69 | case int16: 70 | return strconv.Itoa(int(t.Value.(int16))) 71 | case float32: 72 | return strconv.FormatFloat(float64(t.Value.(float32)), 'f', 2, 32) 73 | case float64: 74 | return strconv.FormatFloat(t.Value.(float64), 'f', 2, 64) 75 | } 76 | return "" 77 | } 78 | --------------------------------------------------------------------------------