├── .envrc ├── .gitignore ├── LICENSE ├── README.md ├── appengine └── cms │ ├── api.go │ ├── api_test.go │ ├── app.yaml │ ├── routes.go │ ├── session.go │ ├── static │ ├── scripts │ │ ├── hyperscript.js │ │ ├── main.js │ │ └── views.js │ └── styles │ │ └── main.css │ └── users.go ├── go.mod ├── go.sum ├── integration ├── asm │ ├── sum.go │ ├── sum_amd64.s │ └── sum_test.go ├── cgo │ ├── gcc │ │ ├── build.sh │ │ ├── main.go │ │ └── sum.c │ └── main.go ├── mq │ ├── main.go │ └── mq.go ├── namedpipes │ ├── rot13 │ │ └── main.go │ └── run.sh ├── net │ └── main.go ├── pipes │ ├── rot13 │ │ └── main.go │ └── run.sh ├── shm │ ├── main.go │ ├── sem.go │ └── shm.go ├── swig │ ├── gsl │ │ ├── gsl.go │ │ └── gsl.swig │ ├── main.go │ └── run.sh ├── syso │ ├── c │ │ └── sum.c │ ├── main.go │ ├── sum.go │ ├── sum_amd64.Conflict.S │ ├── sum_amd64.S │ ├── sum_amd64.c │ ├── sum_amd64.syso │ ├── sum_amd64_src.S │ └── syso ├── sysv_mq │ ├── main.go │ └── python │ │ └── main.py └── unixsocket │ ├── main.go │ └── python │ └── main.py ├── talks ├── 2018-01-30--extending-gopherjs │ ├── README.md │ ├── assets │ │ ├── cover.png │ │ ├── datadog.png │ │ ├── js │ │ │ ├── example-01.js │ │ │ ├── example-01.js.map │ │ │ ├── example-02.js │ │ │ ├── example-02.js.map │ │ │ ├── example-03.js │ │ │ ├── example-03.js.map │ │ │ ├── example-04.js │ │ │ ├── example-04.js.map │ │ │ ├── example-05.js │ │ │ ├── example-05.js.map │ │ │ ├── example-06.js │ │ │ ├── example-06.js.map │ │ │ ├── example-07.js │ │ │ ├── example-07.js.map │ │ │ ├── example-08-client.js │ │ │ ├── example-08-client.js.map │ │ │ ├── example-08-server.js │ │ │ ├── example-08-server.js.map │ │ │ └── postscribe.min.js │ │ ├── jurassic-park.jpg │ │ ├── syscall-warning.png │ │ └── useless-machine.gif │ ├── example-01 │ │ ├── build.bash │ │ ├── main.go │ │ └── serve.bash │ ├── example-02 │ │ ├── main.go │ │ └── react.go │ ├── example-03 │ │ ├── main.go │ │ ├── merge.go │ │ └── sort.go │ ├── example-04 │ │ └── main.go │ ├── example-05 │ │ ├── main.go │ │ └── write.go │ ├── example-06 │ │ ├── init.go │ │ ├── main.go │ │ ├── util.go │ │ ├── vfs.go │ │ ├── vfs_close.go │ │ ├── vfs_open.go │ │ ├── vfs_read.go │ │ └── vfs_write.go │ ├── example-07 │ │ ├── init.go │ │ ├── main.go │ │ ├── util.go │ │ ├── vfs.go │ │ ├── vfs_close.go │ │ ├── vfs_open.go │ │ ├── vfs_read.go │ │ └── vfs_write.go │ ├── example-08 │ │ ├── client │ │ │ ├── conn.go │ │ │ ├── io.go │ │ │ └── main.go │ │ ├── proxy │ │ │ ├── conn.go │ │ │ ├── dial.go │ │ │ ├── listen.go │ │ │ └── main.go │ │ └── server │ │ │ ├── conn.go │ │ │ ├── io.go │ │ │ └── main.go │ ├── generate.go │ ├── runner.htm │ └── talk.slide ├── 2018-11-13--rtctunnel │ ├── 01-demonstration │ │ ├── Dockerfile │ │ └── Readme.md │ ├── 02-connections │ │ ├── client │ │ │ └── main.go │ │ └── server │ │ │ └── main.go │ ├── 03-multiplexing │ │ ├── client │ │ │ └── main.go │ │ ├── go.mod │ │ ├── go.sum │ │ └── server │ │ │ └── main.go │ └── 04-proxies │ │ ├── README.md │ │ ├── client │ │ └── main.go │ │ ├── proxy │ │ └── main.go │ │ └── server │ │ └── main.go └── 2019-08-15--tracing-with-go │ ├── assets │ ├── bookalyzer.png │ ├── datadog.png │ ├── github.png │ ├── kibana.png │ └── redis-fork.png │ ├── go.mod │ ├── go.sum │ ├── present.sh │ └── talk.slide └── trie ├── trie.go └── trie_test.go /.envrc: -------------------------------------------------------------------------------- 1 | export GO111MODULE=on 2 | -------------------------------------------------------------------------------- /.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 | 25 | .idea/ 26 | .DS_Store 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Caleb Doxsey 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. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | tutorials 2 | ========= 3 | -------------------------------------------------------------------------------- /appengine/cms/api.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io" 7 | "net/http" 8 | "regexp" 9 | 10 | "github.com/google/uuid" 11 | "github.com/julienschmidt/httprouter" 12 | "golang.org/x/oauth2" 13 | "golang.org/x/oauth2/google" 14 | "google.golang.org/appengine" 15 | "google.golang.org/appengine/datastore" 16 | "google.golang.org/appengine/file" 17 | "google.golang.org/appengine/urlfetch" 18 | "cloud.google.com/go" 19 | "cloud.google.com/go/storage" 20 | ) 21 | 22 | type HTTPError struct { 23 | Code int 24 | Message string 25 | } 26 | 27 | func (err HTTPError) Error() string { 28 | return err.Message 29 | } 30 | 31 | func serveAPI(res http.ResponseWriter, req *http.Request, handler func() interface{}) { 32 | res.Header().Set("Content-Type", "application/json") 33 | 34 | result := handler() 35 | if err, ok := result.(HTTPError); ok { 36 | res.WriteHeader(err.Code) 37 | json.NewEncoder(res).Encode(map[string]string{ 38 | "error": err.Message, 39 | }) 40 | } else if err, ok := result.(error); ok { 41 | res.WriteHeader(500) 42 | json.NewEncoder(res).Encode(map[string]string{ 43 | "error": err.Error(), 44 | }) 45 | } else if rc, ok := result.(io.ReadCloser); ok { 46 | io.Copy(res, rc) 47 | rc.Close() 48 | } else { 49 | json.NewEncoder(res).Encode(result) 50 | } 51 | } 52 | 53 | // Documents 54 | 55 | type ( 56 | DocumentFile struct { 57 | ID, Name string 58 | } 59 | Document struct { 60 | ID string 61 | Link string 62 | Contents string 63 | Files []DocumentFile 64 | } 65 | ) 66 | 67 | func serveDocumentsGet(res http.ResponseWriter, req *http.Request, params httprouter.Params) { 68 | serveAPI(res, req, func() interface{} { 69 | ctx := appengine.NewContext(req) 70 | session, _ := sessionStore.Get(req, "session") 71 | email, ok := session.Values["email"].(string) 72 | if !ok { 73 | return HTTPError{403, "access denied"} 74 | } 75 | userKey := datastore.NewKey(ctx, "User", email, 0, nil) 76 | docKey := datastore.NewKey(ctx, "Document", params.ByName("id"), 0, userKey) 77 | var document Document 78 | err := datastore.Get(ctx, docKey, &document) 79 | if err != nil { 80 | return err 81 | } 82 | return document 83 | }) 84 | } 85 | 86 | func serveDocumentsList(res http.ResponseWriter, req *http.Request, params httprouter.Params) { 87 | serveAPI(res, req, func() interface{} { 88 | ctx := appengine.NewContext(req) 89 | session, _ := sessionStore.Get(req, "session") 90 | email, ok := session.Values["email"].(string) 91 | if !ok { 92 | return HTTPError{403, "access denied"} 93 | } 94 | userKey := datastore.NewKey(ctx, "User", email, 0, nil) 95 | documents := []Document{} 96 | _, err := datastore.NewQuery("Document").Ancestor(userKey).GetAll(ctx, &documents) 97 | if err != nil { 98 | return err 99 | } 100 | return documents 101 | }) 102 | } 103 | 104 | func serveDocumentsCreate(res http.ResponseWriter, req *http.Request, params httprouter.Params) { 105 | serveAPI(res, req, func() interface{} { 106 | ctx := appengine.NewContext(req) 107 | session, _ := sessionStore.Get(req, "session") 108 | 109 | email, ok := session.Values["email"].(string) 110 | if !ok { 111 | return HTTPError{403, "access denied"} 112 | } 113 | 114 | var document Document 115 | err := json.NewDecoder(req.Body).Decode(&document) 116 | if err != nil { 117 | return err 118 | } 119 | if document.ID != "" { 120 | return fmt.Errorf("invalid document: id must not be set") 121 | } 122 | document.ID = uuid.Must(uuid.NewRandom()).String() 123 | userKey := datastore.NewKey(ctx, "User", email, 0, nil) 124 | docKey := datastore.NewKey(ctx, "Document", document.ID, 0, userKey) 125 | docKey, err = datastore.Put(ctx, docKey, &document) 126 | if err != nil { 127 | return err 128 | } 129 | return document 130 | }) 131 | } 132 | 133 | func serveDocumentsDelete(res http.ResponseWriter, req *http.Request, params httprouter.Params) { 134 | serveAPI(res, req, func() interface{} { 135 | ctx := appengine.NewContext(req) 136 | session, _ := sessionStore.Get(req, "session") 137 | 138 | email, ok := session.Values["email"].(string) 139 | if !ok { 140 | return HTTPError{403, "access denied"} 141 | } 142 | 143 | id := params.ByName("id") 144 | userKey := datastore.NewKey(ctx, "User", email, 0, nil) 145 | docKey := datastore.NewKey(ctx, "Document", id, 0, userKey) 146 | var document Document 147 | err := datastore.Get(ctx, docKey, &document) 148 | if err != nil { 149 | return err 150 | } 151 | err = datastore.Delete(ctx, docKey) 152 | if err != nil { 153 | return err 154 | } 155 | return true 156 | }) 157 | } 158 | 159 | func serveDocumentsUpdate(res http.ResponseWriter, req *http.Request, params httprouter.Params) { 160 | serveAPI(res, req, func() interface{} { 161 | ctx := appengine.NewContext(req) 162 | session, _ := sessionStore.Get(req, "session") 163 | email, ok := session.Values["email"].(string) 164 | if !ok { 165 | return HTTPError{403, "access denied"} 166 | } 167 | 168 | var document Document 169 | err := json.NewDecoder(req.Body).Decode(&document) 170 | if err != nil { 171 | return err 172 | } 173 | document.ID = params.ByName("id") 174 | 175 | userKey := datastore.NewKey(ctx, "User", email, 0, nil) 176 | docKey := datastore.NewKey(ctx, "Document", params.ByName("id"), 0, userKey) 177 | var originalDocument Document 178 | err = datastore.Get(ctx, docKey, &originalDocument) 179 | if err != nil { 180 | return err 181 | } 182 | 183 | _, err = datastore.Put(ctx, docKey, &document) 184 | if err != nil { 185 | return err 186 | } 187 | 188 | return document 189 | }) 190 | } 191 | 192 | // Files 193 | func serveFilesGet(res http.ResponseWriter, req *http.Request, params httprouter.Params) { 194 | serveAPI(res, req, func() interface{} { 195 | ctx := appengine.NewContext(req) 196 | session, _ := sessionStore.Get(req, "session") 197 | 198 | _, ok := session.Values["email"].(string) 199 | if !ok { 200 | return HTTPError{403, "access denied"} 201 | } 202 | 203 | bucket, err := file.DefaultBucketName(ctx) 204 | if err != nil { 205 | return err 206 | } 207 | 208 | hc := &http.Client{ 209 | Transport: &oauth2.Transport{ 210 | Source: google.AppEngineTokenSource(ctx, storage.ScopeReadOnly), 211 | Base: &urlfetch.Transport{Context: ctx}, 212 | }, 213 | } 214 | 215 | cctx := cloud.NewContext(appengine.AppID(ctx), hc) 216 | rc, err := storage.NewReader(cctx, bucket, params.ByName("id")) 217 | if err != nil { 218 | return err 219 | } 220 | 221 | name := req.URL.Query().Get("name") 222 | if name == "" { 223 | name = params.ByName("id") 224 | } 225 | name = regexp.MustCompile("[^a-zA-Z-_.]").ReplaceAllString(name, "") 226 | 227 | res.Header().Set("Content-Disposition", "inline; filename=\""+name+"\"") 228 | res.Header().Set("Content-Type", "application/octet-stream") 229 | return rc 230 | }) 231 | } 232 | 233 | func serveFilesUpload(res http.ResponseWriter, req *http.Request, params httprouter.Params) { 234 | serveAPI(res, req, func() interface{} { 235 | ctx := appengine.NewContext(req) 236 | session, _ := sessionStore.Get(req, "session") 237 | 238 | _, ok := session.Values["email"].(string) 239 | if !ok { 240 | return HTTPError{403, "access denied"} 241 | } 242 | 243 | bucket, err := file.DefaultBucketName(ctx) 244 | if err != nil { 245 | return err 246 | } 247 | 248 | hc := &http.Client{ 249 | Transport: &oauth2.Transport{ 250 | Source: google.AppEngineTokenSource(ctx, storage.ScopeFullControl), 251 | Base: &urlfetch.Transport{Context: ctx}, 252 | }, 253 | } 254 | 255 | id := uuid.NewRandom().String() 256 | 257 | ff, _, err := req.FormFile("file") 258 | if err != nil { 259 | return err 260 | } 261 | defer ff.Close() 262 | 263 | cctx := cloud.NewContext(appengine.AppID(ctx), hc) 264 | wc := storage.NewWriter(cctx, bucket, id) 265 | io.Copy(wc, ff) 266 | err = wc.Close() 267 | if err != nil { 268 | return err 269 | } 270 | 271 | return id 272 | }) 273 | } 274 | 275 | // Users 276 | 277 | func serveUsersCreate(res http.ResponseWriter, req *http.Request, params httprouter.Params) { 278 | serveAPI(res, req, func() interface{} { 279 | ctx := appengine.NewContext(req) 280 | session, _ := sessionStore.Get(req, "session") 281 | 282 | type Request struct { 283 | Email, Password string 284 | } 285 | var request Request 286 | json.NewDecoder(req.Body).Decode(&request) 287 | 288 | err := CreateUser(ctx, request.Email, request.Password) 289 | if err != nil { 290 | return HTTPError{403, err.Error()} 291 | } 292 | session.Values["email"] = request.Email 293 | err = session.Save(req, res) 294 | if err != nil { 295 | return err 296 | } 297 | 298 | return true 299 | }) 300 | } 301 | 302 | func serveUsersLogin(res http.ResponseWriter, req *http.Request, params httprouter.Params) { 303 | serveAPI(res, req, func() interface{} { 304 | ctx := appengine.NewContext(req) 305 | session, _ := sessionStore.Get(req, "session") 306 | 307 | type Request struct { 308 | Email, Password string 309 | } 310 | var request Request 311 | json.NewDecoder(req.Body).Decode(&request) 312 | 313 | _, err := AuthenticateUser(ctx, request.Email, request.Password) 314 | if err != nil { 315 | return HTTPError{403, err.Error()} 316 | } 317 | 318 | session.Values["email"] = request.Email 319 | err = session.Save(req, res) 320 | if err != nil { 321 | return err 322 | } 323 | 324 | return true 325 | }) 326 | } 327 | 328 | func serveUsersLogout(res http.ResponseWriter, req *http.Request, params httprouter.Params) { 329 | serveAPI(res, req, func() interface{} { 330 | session, _ := sessionStore.Get(req, "session") 331 | session.Values = nil 332 | err := session.Save(req, res) 333 | if err != nil { 334 | return err 335 | } 336 | return true 337 | }) 338 | } 339 | -------------------------------------------------------------------------------- /appengine/cms/api_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "io" 7 | "net/http/httptest" 8 | "strings" 9 | "testing" 10 | 11 | "github.com/stretchr/testify/assert" 12 | 13 | "appengine/aetest" 14 | ) 15 | 16 | func TestAPI(t *testing.T) { 17 | assert := assert.New(t) 18 | 19 | c, err := aetest.NewContext(nil) 20 | assert.Nil(err) 21 | defer c.Close() 22 | 23 | inst, err := aetest.NewInstance(nil) 24 | assert.Nil(err) 25 | defer inst.Close() 26 | 27 | cookie := "" 28 | makeRequest := func(method string, endpoint string, data interface{}) *httptest.ResponseRecorder { 29 | var body io.Reader 30 | var contentType string 31 | if r, ok := data.(io.Reader); ok { 32 | body = r 33 | } else if data != nil { 34 | bs, _ := json.Marshal(data) 35 | body = bytes.NewReader(bs) 36 | contentType = "application/json" 37 | } 38 | req, err := inst.NewRequest(method, endpoint, body) 39 | req.Header.Set("Content-Type", contentType) 40 | req.Header.Set("Cookie", cookie) 41 | assert.Nil(err) 42 | res := httptest.NewRecorder() 43 | router.ServeHTTP(res, req) 44 | sc := res.Header().Get("Set-Cookie") 45 | if sc != "" { 46 | sc = sc[:strings.Index(sc, ";")] 47 | cookie = sc 48 | } 49 | return res 50 | } 51 | 52 | // create user 53 | res := makeRequest("POST", "/api/users", map[string]string{ 54 | "email": "test@example.com", 55 | "password": "test", 56 | }) 57 | assert.Equal(200, res.Code) 58 | 59 | // login as user 60 | res = makeRequest("POST", "/api/users/login", map[string]string{ 61 | "email": "test@example.com", 62 | "password": "test", 63 | }) 64 | assert.Equal(200, res.Code) 65 | 66 | // upload file 67 | res = makeRequest("POST", "/api/files", strings.NewReader("Hello World")) 68 | assert.Equal(200, res.Code) 69 | var fileid string 70 | json.Unmarshal(res.Body.Bytes(), &fileid) 71 | 72 | // download file 73 | res = makeRequest("GET", "/api/files/"+fileid, nil) 74 | assert.Equal(200, res.Code) 75 | assert.Equal("Hello World", res.Body.String()) 76 | 77 | // create document 78 | res = makeRequest("POST", "/api/documents", Document{ 79 | Link: "/path/to/file", 80 | Contents: "Hello World", 81 | }) 82 | assert.Equal(200, res.Code) 83 | var doc Document 84 | json.Unmarshal(res.Body.Bytes(), &doc) 85 | assert.Equal("Hello World", doc.Contents) 86 | 87 | // get document 88 | res = makeRequest("GET", "/api/documents/"+doc.ID, nil) 89 | assert.Equal(200, res.Code) 90 | json.Unmarshal(res.Body.Bytes(), &doc) 91 | assert.Equal("Hello World", doc.Contents) 92 | 93 | // update document 94 | doc.Contents = "Hello World 2" 95 | res = makeRequest("PUT", "/api/documents/"+doc.ID, doc) 96 | assert.Equal(200, res.Code) 97 | json.Unmarshal(res.Body.Bytes(), &doc) 98 | assert.Equal("Hello World 2", doc.Contents) 99 | 100 | // list documents 101 | res = makeRequest("GET", "/api/documents", nil) 102 | assert.Equal(200, res.Code) 103 | var docs []Document 104 | json.Unmarshal(res.Body.Bytes(), &docs) 105 | assert.Equal(1, len(docs)) 106 | assert.Equal("Hello World 2", docs[0].Contents) 107 | } 108 | -------------------------------------------------------------------------------- /appengine/cms/app.yaml: -------------------------------------------------------------------------------- 1 | application: doxsey-cms-example 2 | version: v2 3 | runtime: go 4 | api_version: go1 5 | 6 | handlers: 7 | - url: /.* 8 | script: _go_app 9 | -------------------------------------------------------------------------------- /appengine/cms/routes.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io" 7 | "net/http" 8 | "os" 9 | "text/template" 10 | 11 | "github.com/julienschmidt/httprouter" 12 | ) 13 | 14 | var router *httprouter.Router 15 | 16 | func serveTemplate(res http.ResponseWriter, name string, data interface{}) { 17 | tpl, err := template.ParseFiles("templates/" + name + ".gohtml") 18 | if err != nil { 19 | http.Error(res, err.Error(), 500) 20 | return 21 | } 22 | var buf bytes.Buffer 23 | err = tpl.Execute(&buf, data) 24 | if err != nil { 25 | http.Error(res, err.Error(), 500) 26 | return 27 | } 28 | body := buf.String() 29 | tpl, err = template.ParseFiles("templates/layout.gohtml") 30 | if err != nil { 31 | http.Error(res, err.Error(), 500) 32 | return 33 | } 34 | buf.Reset() 35 | err = tpl.Execute(&buf, map[string]interface{}{ 36 | "Body": body, 37 | }) 38 | if err != nil { 39 | http.Error(res, err.Error(), 500) 40 | return 41 | } 42 | res.Header().Set("Content-Type", "text/html") 43 | res.Write(buf.Bytes()) 44 | } 45 | 46 | func init() { 47 | router = httprouter.New() 48 | 49 | // API methods 50 | router.GET("/api/documents", serveDocumentsList) 51 | router.GET("/api/documents/:id", serveDocumentsGet) 52 | router.POST("/api/documents", serveDocumentsCreate) 53 | router.PUT("/api/documents/:id", serveDocumentsUpdate) 54 | router.DELETE("/api/documents/:id", serveDocumentsDelete) 55 | 56 | router.GET("/api/files/:id", serveFilesGet) 57 | router.POST("/api/files", serveFilesUpload) 58 | 59 | router.POST("/api/users", serveUsersCreate) 60 | router.POST("/api/users/login", serveUsersLogin) 61 | router.POST("/api/users/logout", serveUsersLogout) 62 | 63 | // HTML methods 64 | router.GET("/", func(res http.ResponseWriter, req *http.Request, params httprouter.Params) { 65 | session, _ := sessionStore.Get(req, "session") 66 | email, _ := session.Values["email"] 67 | 68 | mainCSSInfo, _ := os.Stat("static/styles/main.css") 69 | mainJSInfo, _ := os.Stat("static/scripts/main.js") 70 | viewsJSInfo, _ := os.Stat("static/scripts/views.js") 71 | 72 | res.Header().Set("Content-Type", "text/html") 73 | io.WriteString(res, ` 74 | 75 | 76 | CMS Example 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | `) 88 | }) 89 | 90 | // Static 91 | static := http.FileServer(http.Dir("static")) 92 | router.GET("/static/*filepath", func(res http.ResponseWriter, req *http.Request, params httprouter.Params) { 93 | filepath := params.ByName("filepath") 94 | req.URL.Path = filepath 95 | static.ServeHTTP(res, req) 96 | }) 97 | 98 | http.Handle("/", router) 99 | } 100 | -------------------------------------------------------------------------------- /appengine/cms/session.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "github.com/gorilla/sessions" 4 | 5 | var ( 6 | // sessions are stored in a (tamper-proof) cookie 7 | sessionSecret = []byte("eUJE3c4Av3rAncC3yUSmdhjzHNuhbW8WuRfTdej8") 8 | sessionStore *sessions.CookieStore 9 | ) 10 | 11 | func init() { 12 | sessionStore = sessions.NewCookieStore(sessionSecret) 13 | } 14 | -------------------------------------------------------------------------------- /appengine/cms/static/scripts/hyperscript.js: -------------------------------------------------------------------------------- 1 | (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.hyperscript = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 154 | * Available under the MIT License 155 | * ECMAScript compliant, uniform cross-browser split method 156 | */ 157 | 158 | /** 159 | * Splits a string into an array of strings using a regex or string separator. Matches of the 160 | * separator are not included in the result array. However, if `separator` is a regex that contains 161 | * capturing groups, backreferences are spliced into the result each time `separator` is matched. 162 | * Fixes browser bugs compared to the native `String.prototype.split` and can be used reliably 163 | * cross-browser. 164 | * @param {String} str String to split. 165 | * @param {RegExp|String} separator Regex or string to use for separating the string. 166 | * @param {Number} [limit] Maximum number of items to include in the result array. 167 | * @returns {Array} Array of substrings. 168 | * @example 169 | * 170 | * // Basic use 171 | * split('a b c d', ' '); 172 | * // -> ['a', 'b', 'c', 'd'] 173 | * 174 | * // With limit 175 | * split('a b c d', ' ', 2); 176 | * // -> ['a', 'b'] 177 | * 178 | * // Backreferences in result array 179 | * split('..word1 word2..', /([a-z]+)(\d+)/i); 180 | * // -> ['..', 'word', '1', ' ', 'word', '2', '..'] 181 | */ 182 | module.exports = (function split(undef) { 183 | 184 | var nativeSplit = String.prototype.split, 185 | compliantExecNpcg = /()??/.exec("")[1] === undef, 186 | // NPCG: nonparticipating capturing group 187 | self; 188 | 189 | self = function(str, separator, limit) { 190 | // If `separator` is not a regex, use `nativeSplit` 191 | if (Object.prototype.toString.call(separator) !== "[object RegExp]") { 192 | return nativeSplit.call(str, separator, limit); 193 | } 194 | var output = [], 195 | flags = (separator.ignoreCase ? "i" : "") + (separator.multiline ? "m" : "") + (separator.extended ? "x" : "") + // Proposed for ES6 196 | (separator.sticky ? "y" : ""), 197 | // Firefox 3+ 198 | lastLastIndex = 0, 199 | // Make `global` and avoid `lastIndex` issues by working with a copy 200 | separator = new RegExp(separator.source, flags + "g"), 201 | separator2, match, lastIndex, lastLength; 202 | str += ""; // Type-convert 203 | if (!compliantExecNpcg) { 204 | // Doesn't need flags gy, but they don't hurt 205 | separator2 = new RegExp("^" + separator.source + "$(?!\\s)", flags); 206 | } 207 | /* Values for `limit`, per the spec: 208 | * If undefined: 4294967295 // Math.pow(2, 32) - 1 209 | * If 0, Infinity, or NaN: 0 210 | * If positive number: limit = Math.floor(limit); if (limit > 4294967295) limit -= 4294967296; 211 | * If negative number: 4294967296 - Math.floor(Math.abs(limit)) 212 | * If other: Type-convert, then use the above rules 213 | */ 214 | limit = limit === undef ? -1 >>> 0 : // Math.pow(2, 32) - 1 215 | limit >>> 0; // ToUint32(limit) 216 | while (match = separator.exec(str)) { 217 | // `separator.lastIndex` is not reliable cross-browser 218 | lastIndex = match.index + match[0].length; 219 | if (lastIndex > lastLastIndex) { 220 | output.push(str.slice(lastLastIndex, match.index)); 221 | // Fix browsers whose `exec` methods don't consistently return `undefined` for 222 | // nonparticipating capturing groups 223 | if (!compliantExecNpcg && match.length > 1) { 224 | match[0].replace(separator2, function() { 225 | for (var i = 1; i < arguments.length - 2; i++) { 226 | if (arguments[i] === undef) { 227 | match[i] = undef; 228 | } 229 | } 230 | }); 231 | } 232 | if (match.length > 1 && match.index < str.length) { 233 | Array.prototype.push.apply(output, match.slice(1)); 234 | } 235 | lastLength = match[0].length; 236 | lastLastIndex = lastIndex; 237 | if (output.length >= limit) { 238 | break; 239 | } 240 | } 241 | if (separator.lastIndex === match.index) { 242 | separator.lastIndex++; // Avoid an infinite loop 243 | } 244 | } 245 | if (lastLastIndex === str.length) { 246 | if (lastLength || !separator.test("")) { 247 | output.push(""); 248 | } 249 | } else { 250 | output.push(str.slice(lastLastIndex)); 251 | } 252 | return output.length > limit ? output.slice(0, limit) : output; 253 | }; 254 | 255 | return self; 256 | })(); 257 | 258 | },{}],4:[function(require,module,exports){ 259 | // contains, add, remove, toggle 260 | var indexof = require('indexof') 261 | 262 | module.exports = ClassList 263 | 264 | function ClassList(elem) { 265 | var cl = elem.classList 266 | 267 | if (cl) { 268 | return cl 269 | } 270 | 271 | var classList = { 272 | add: add 273 | , remove: remove 274 | , contains: contains 275 | , toggle: toggle 276 | , toString: $toString 277 | , length: 0 278 | , item: item 279 | } 280 | 281 | return classList 282 | 283 | function add(token) { 284 | var list = getTokens() 285 | if (indexof(list, token) > -1) { 286 | return 287 | } 288 | list.push(token) 289 | setTokens(list) 290 | } 291 | 292 | function remove(token) { 293 | var list = getTokens() 294 | , index = indexof(list, token) 295 | 296 | if (index === -1) { 297 | return 298 | } 299 | 300 | list.splice(index, 1) 301 | setTokens(list) 302 | } 303 | 304 | function contains(token) { 305 | return indexof(getTokens(), token) > -1 306 | } 307 | 308 | function toggle(token) { 309 | if (contains(token)) { 310 | remove(token) 311 | return false 312 | } else { 313 | add(token) 314 | return true 315 | } 316 | } 317 | 318 | function $toString() { 319 | return elem.className 320 | } 321 | 322 | function item(index) { 323 | var tokens = getTokens() 324 | return tokens[index] || null 325 | } 326 | 327 | function getTokens() { 328 | var className = elem.className 329 | 330 | return filter(className.split(" "), isTruthy) 331 | } 332 | 333 | function setTokens(list) { 334 | var length = list.length 335 | 336 | elem.className = list.join(" ") 337 | classList.length = length 338 | 339 | for (var i = 0; i < list.length; i++) { 340 | classList[i] = list[i] 341 | } 342 | 343 | delete list[length] 344 | } 345 | } 346 | 347 | function filter (arr, fn) { 348 | var ret = [] 349 | for (var i = 0; i < arr.length; i++) { 350 | if (fn(arr[i])) ret.push(arr[i]) 351 | } 352 | return ret 353 | } 354 | 355 | function isTruthy(value) { 356 | return !!value 357 | } 358 | 359 | },{"indexof":5}],5:[function(require,module,exports){ 360 | 361 | var indexOf = [].indexOf; 362 | 363 | module.exports = function(arr, obj){ 364 | if (indexOf) return arr.indexOf(obj); 365 | for (var i = 0; i < arr.length; ++i) { 366 | if (arr[i] === obj) return i; 367 | } 368 | return -1; 369 | }; 370 | },{}]},{},[2])(2) 371 | }); -------------------------------------------------------------------------------- /appengine/cms/static/scripts/main.js: -------------------------------------------------------------------------------- 1 | function api(method, endpoint, data, callback) { 2 | var xhr = new XMLHttpRequest(); 3 | xhr.open(method, endpoint); 4 | if (data) { 5 | xhr.send(JSON.stringify(data)); 6 | } else { 7 | xhr.send(null); 8 | } 9 | xhr.onreadystatechange = function(evt) { 10 | if (xhr.readyState === 4) { 11 | var res; 12 | try { 13 | res = JSON.parse(xhr.responseText); 14 | } catch(e) { 15 | res = { "error": xhr.responseText }; 16 | } 17 | if (res && res.error) { 18 | callback(null, res.error); 19 | } else { 20 | callback(res); 21 | } 22 | } 23 | }; 24 | } 25 | 26 | function main() { 27 | var prev = null; 28 | function renderView(url) { 29 | if (prev) { 30 | document.body.removeChild(prev); 31 | } 32 | var view = Views[url]; 33 | if (!view) { 34 | var arr = url.split("/"); 35 | arr.pop(); 36 | view = Views[arr.join("/") + "/"]; 37 | } 38 | prev = view(); 39 | document.body.appendChild(prev); 40 | } 41 | window.addEventListener("hashchange", function() { 42 | renderView(location.hash.substr(1) || "/"); 43 | }, false); 44 | renderView(location.hash.substr(1) || "/"); 45 | } 46 | 47 | main(); 48 | -------------------------------------------------------------------------------- /appengine/cms/static/scripts/views.js: -------------------------------------------------------------------------------- 1 | var Views = {}; 2 | 3 | function renderHeader() { 4 | var loggedIn = !!EMAIL; 5 | return h("div.page-header", [ 6 | loggedIn ? 7 | h("div.pull-right", [ 8 | h("a.btn.btn-default", { "href": "#/logout" }, [ 9 | "Logout" 10 | ]) 11 | ]) : "", 12 | h("h1", "CMS Example") 13 | ]); 14 | } 15 | 16 | Views["/signup"] = function() { 17 | var emailInput, passwordInput1, passwordInput2; 18 | 19 | function onSubmit(evt) { 20 | evt.preventDefault(); 21 | 22 | if (passwordInput1.value !== passwordInput2.value) { 23 | alert("passwords don't match"); 24 | return; 25 | } 26 | 27 | var email = emailInput.value, password = passwordInput1.value; 28 | 29 | api("POST", "/api/users", { 30 | "email": email, 31 | "password": password 32 | }, function(result, error) { 33 | if (error) { 34 | alert("Error signing up: " + error); 35 | return; 36 | } 37 | EMAIL = email; 38 | location.href = "#/"; 39 | }); 40 | } 41 | 42 | return ( 43 | h("div.container", [ 44 | h("div.page-header", [ 45 | h("h1", "CMS Example") 46 | ]), 47 | h("form", { "onsubmit": onSubmit }, [ 48 | h("div.panel.panel-default", [ 49 | h("div.panel-heading", [ 50 | "Signup" 51 | ]), 52 | h("div.panel-body.form-horizontal", [ 53 | h("div.form-group", [ 54 | h("label.control-label.col-sm-2", { "for": "email" }, "Email"), 55 | h("div.col-sm-10", [ 56 | emailInput = h("input.form-control", { 57 | "type": "email", 58 | "name": "email", 59 | "placeholder": "email" 60 | }) 61 | ]) 62 | ]), 63 | h("div.form-group", [ 64 | h("label.control-label.col-sm-2", { "for": "password" }, "Password"), 65 | h("div.col-sm-10", [ 66 | passwordInput1 = h("input.form-control", { 67 | "type": "password", 68 | "name": "password1", 69 | "placeholder": "password" 70 | }) 71 | ]) 72 | ]), 73 | h("div.form-group", [ 74 | h("label.control-label.col-sm-2", { "for": "password" }, ""), 75 | h("div.col-sm-10", [ 76 | passwordInput2 = h("input.form-control", { 77 | "type": "password", 78 | "name": "password2", 79 | "placeholder": "repeat password" 80 | }) 81 | ]) 82 | ]), 83 | h("div.form-group", [ 84 | h("div.col-sm-offset-2.col-sm-10", [ 85 | h("button.btn.btn-default", { "type": "submit" }, [ 86 | "Signup" 87 | ]), 88 | " ", 89 | h("a.btn.btn-link", { "href": "#/login" }, "Login") 90 | ]) 91 | ]) 92 | ]) 93 | ]) 94 | ]) 95 | ]) 96 | ); 97 | }; 98 | 99 | Views["/login"] = function() { 100 | var emailInput, passwordInput; 101 | 102 | function onSubmit(evt) { 103 | evt.preventDefault(); 104 | 105 | var email = emailInput.value, password = passwordInput.value; 106 | 107 | api("POST", "/api/users/login", { 108 | "email": email, 109 | "password": password 110 | }, function(result, error) { 111 | if (error) { 112 | alert("Error logging in: " + error); 113 | return; 114 | } 115 | EMAIL = email; 116 | location.href = "#/"; 117 | }); 118 | } 119 | 120 | return ( 121 | h("div.container", [ 122 | h("div.page-header", [ 123 | h("h1", "CMS Example") 124 | ]), 125 | h("form", { "onsubmit": onSubmit }, [ 126 | h("div.panel.panel-default", [ 127 | h("div.panel-heading", "Login"), 128 | h("div.panel-body.form-horizontal", [ 129 | h("div.form-group", [ 130 | h("label.control-label.col-sm-2", { "for": "email" }, "Email"), 131 | h("div.col-sm-10", [ 132 | emailInput = h("input.form-control", { 133 | "type": "email", 134 | "name": "email", 135 | "placeholder": "email" 136 | }) 137 | ]) 138 | ]), 139 | h("div.form-group", [ 140 | h("label.control-label.col-sm-2", { "for": "password" }, "Password"), 141 | h("div.col-sm-10", [ 142 | passwordInput = h("input.form-control", { 143 | "type": "password", 144 | "name": "password", 145 | "placeholder": "password" 146 | }) 147 | ]) 148 | ]), 149 | h("div.form-group", [ 150 | h("div.col-sm-offset-2.col-sm-10", [ 151 | h("button.btn.btn-default", { "type": "submit" }, [ 152 | "Login" 153 | ]), 154 | " ", 155 | h("a.btn.btn-link", { "href": "#/signup" }, "Signup") 156 | ]) 157 | ]) 158 | ]) 159 | ]) 160 | ]) 161 | ]) 162 | ) 163 | }; 164 | 165 | Views["/logout"] = function() { 166 | api("POST", "/api/users/logout", null, function(result, error) { 167 | if (error) { 168 | alert("Error logging out: " + error); 169 | return; 170 | } 171 | EMAIL = ""; 172 | location.href = "#/"; 173 | }); 174 | return h("div"); 175 | }; 176 | 177 | Views["/documents/"] = function() { 178 | var id = location.hash.split("/").pop(); 179 | var linkInput, contentsInput, fileList; 180 | 181 | function onSubmit(evt) { 182 | evt.preventDefault(); 183 | 184 | var link = linkInput.value, contents = contentsInput.value, files = []; 185 | for (var i=0; i github.com/badgerodon/gopherjs v0.0.0-20190227155943-3928b9b7f382 22 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 3 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 4 | cloud.google.com/go v0.36.0 h1:+aCSj7tOo2LODWVEuZDZeGCckdt6MlSF+X/rB3wUiS8= 5 | cloud.google.com/go v0.36.0/go.mod h1:RUoy9p/M4ge0HzT8L+SDZ8jg+Q6fth0CiBuhFJpSV40= 6 | dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU= 7 | dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU= 8 | dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4= 9 | dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU= 10 | git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= 11 | github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= 12 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 13 | github.com/Shopify/sysv_mq v0.0.0-20181115095215-ed2aeada8228 h1:Q4PnGntQR5IQsHGK0ONFvyxR6+h3o7JouBlaTbrRpAw= 14 | github.com/Shopify/sysv_mq v0.0.0-20181115095215-ed2aeada8228/go.mod h1:tKgGxRErhsTG3Z2U4uXv5rqhhilYnCDC0r/ETjQ3Q+Y= 15 | github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= 16 | github.com/badgerodon/gopherjs v0.0.0-20190227155943-3928b9b7f382/go.mod h1:z6pgpqGLi1mEHvMnjTGAZ/2kDeDVa9CQKu6+KgfR+NE= 17 | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= 18 | github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= 19 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 20 | github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 21 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 22 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 23 | github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= 24 | github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw= 25 | github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= 26 | github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= 27 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 28 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 29 | github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= 30 | github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 31 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= 32 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 33 | github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= 34 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 35 | github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 36 | github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= 37 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 38 | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 39 | github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ= 40 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 41 | github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= 42 | github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= 43 | github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= 44 | github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= 45 | github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 46 | github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= 47 | github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 48 | github.com/googleapis/gax-go v2.0.0+incompatible h1:j0GKcs05QVmm7yesiZq2+9cxHkNK9YM6zKx4D2qucQU= 49 | github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= 50 | github.com/googleapis/gax-go/v2 v2.0.3 h1:siORttZ36U2R/WjiJuDz8znElWBiAlO9rVt+mqJt0Cc= 51 | github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg= 52 | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= 53 | github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e h1:JKmoR8x90Iww1ks85zJ1lfDGgIiMDuIptTOhJq+zKyg= 54 | github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= 55 | github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8= 56 | github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= 57 | github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ= 58 | github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= 59 | github.com/gorilla/sessions v1.1.3 h1:uXoZdcdA5XdXF3QzuSlheVRUvjl+1rKY7zBXL68L9RU= 60 | github.com/gorilla/sessions v1.1.3/go.mod h1:8KCfur6+4Mqcc6S0FEfKuN15Vl5MgXW92AE8ovaJD0w= 61 | github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q= 62 | github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= 63 | github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= 64 | github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= 65 | github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d h1:kJCB4vdITiW1eC1vq2e6IsrXKrZit1bv/TDYFGMp4BQ= 66 | github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= 67 | github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= 68 | github.com/julienschmidt/httprouter v1.2.0 h1:TDTW5Yz1mjftljbcKqRcrYhd4XeOoI98t+9HbQbYf7g= 69 | github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= 70 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 71 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 72 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 73 | github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 74 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 75 | github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= 76 | github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= 77 | github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= 78 | github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= 79 | github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= 80 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 81 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 82 | github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= 83 | github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= 84 | github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= 85 | github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 86 | github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= 87 | github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= 88 | github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY= 89 | github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM= 90 | github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0= 91 | github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= 92 | github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= 93 | github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw= 94 | github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI= 95 | github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU= 96 | github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag= 97 | github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg= 98 | github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw= 99 | github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y= 100 | github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= 101 | github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q= 102 | github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ= 103 | github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I= 104 | github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0= 105 | github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ= 106 | github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk= 107 | github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 108 | github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4= 109 | github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw= 110 | github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= 111 | github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= 112 | github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= 113 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 114 | github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= 115 | go.opencensus.io v0.18.0 h1:Mk5rgZcggtbvtAun5aJzAtjKKN/t0R3jJPlWILlv938= 116 | go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= 117 | go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= 118 | golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw= 119 | golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 120 | golang.org/x/crypto v0.0.0-20190227175134-215aa809caaf h1:CGelmUfSfeZpx2Pu+OznGcS0ff71WZ/ZOEkhMAB4hVQ= 121 | golang.org/x/crypto v0.0.0-20190227175134-215aa809caaf/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 122 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 123 | golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 124 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3 h1:x/bBzNauLQAlE3fLku/xy92Y8QwKX5HZymrMz2IiKFc= 125 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 126 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 127 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 128 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 129 | golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 130 | golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 131 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 132 | golang.org/x/net v0.0.0-20190227160552-c95aed5357e7 h1:C2F/nMkR/9sfUTpvR3QrjBuTdvMUC/cFajkphs1YLQo= 133 | golang.org/x/net v0.0.0-20190227160552-c95aed5357e7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 134 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 135 | golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 136 | golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 137 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421 h1:Wo7BWFiOk0QRFMLYMqJGFMd9CgUAcGx7V+qEg/h5IBI= 138 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 139 | golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= 140 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 141 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 142 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 143 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6 h1:bjcUS9ztw9kFmmIxJInhon/0Is3p+EHBKNgquIzo1OI= 144 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 145 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 146 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 147 | golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 148 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= 149 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 150 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 151 | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2 h1:z99zHgr7hKfrUcX/KsoJk5FJfjTceCKIp96+biqP4To= 152 | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 153 | golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 154 | golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 155 | golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 156 | golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b h1:Un5iKMvgLIGMzGM1mJWvi22FiMX9XB6/NOzYKoy66y8= 157 | golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 158 | google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= 159 | google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= 160 | google.golang.org/api v0.1.0 h1:K6z2u68e86TPdSdefXdzvXgR1zEMa+459vBSfWYAZkI= 161 | google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= 162 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 163 | google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 164 | google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 165 | google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508= 166 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 167 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 168 | google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 169 | google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 170 | google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= 171 | google.golang.org/genproto v0.0.0-20190201180003-4b09977fb922 h1:mBVYJnbrXLA/ZCBTCe7PtEgAUP+1bg92qTaFoPHdz+8= 172 | google.golang.org/genproto v0.0.0-20190201180003-4b09977fb922/go.mod h1:L3J43x8/uS+qIUoksaLKe6OS3nUKxOKuIFz1sl2/jx4= 173 | google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= 174 | google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= 175 | google.golang.org/grpc v1.17.0 h1:TRJYBgMclJvGYn2rIMjj+h9KtMt5r1Ij7ODVRIZkwhk= 176 | google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= 177 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 178 | gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= 179 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 180 | grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= 181 | honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 182 | honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a h1:/8zB6iBfHCl1qAnEAWwGPNrUvapuy6CPla1VM0k8hQw= 183 | honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 184 | sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck= 185 | sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0= 186 | -------------------------------------------------------------------------------- /integration/asm/sum.go: -------------------------------------------------------------------------------- 1 | package sum 2 | 3 | func SumV1(xs []int64) int64 { 4 | var total int64 5 | for i := 0; i < len(xs); i++ { 6 | total += xs[i] 7 | } 8 | return total 9 | } 10 | 11 | func SumV2(xs []int64) int64 12 | -------------------------------------------------------------------------------- /integration/asm/sum_amd64.s: -------------------------------------------------------------------------------- 1 | #include "textflag.h" 2 | 3 | TEXT ·SumV2(SB),NOSPLIT,$0-24 4 | MOVQ xs+0(FP),DI 5 | MOVQ xs+8(FP),SI 6 | MOVQ $0,CX 7 | MOVQ $0,AX 8 | 9 | L1: CMPQ AX,SI // i < len(xs) 10 | JGE Z1 11 | LEAQ (DI)(AX*8),BX // BX = &xs[i] 12 | MOVQ (BX),BX // BX = *BX 13 | ADDQ BX,CX // CX += BX 14 | INCQ AX // i++ 15 | JMP L1 16 | 17 | Z1: MOVQ CX,ret+24(FP) 18 | RET 19 | -------------------------------------------------------------------------------- /integration/asm/sum_test.go: -------------------------------------------------------------------------------- 1 | package sum 2 | 3 | import ( 4 | "math/rand" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | var xs []int64 11 | 12 | func init() { 13 | rand.Seed(0) 14 | xs = make([]int64, 4096) 15 | for i := 0; i < len(xs); i++ { 16 | xs[i] = int64(rand.Intn(2 << 30)) 17 | } 18 | } 19 | 20 | func Test(t *testing.T) { 21 | assert := assert.New(t) 22 | 23 | type testCase struct { 24 | result int64 25 | arr []int64 26 | } 27 | cases := []testCase{ 28 | {0, []int64{}}, 29 | {20, []int64{10, 10}}, 30 | {4428505259927, xs}, 31 | } 32 | 33 | for _, c := range cases { 34 | assert.Equal(c.result, SumV1(c.arr)) 35 | } 36 | 37 | for _, c := range cases { 38 | assert.Equal(c.result, SumV2(c.arr)) 39 | } 40 | 41 | } 42 | 43 | func BenchmarkV1(b *testing.B) { 44 | xs := make([]int64, 4096) 45 | for i := 0; i < len(xs); i++ { 46 | xs[i] = int64(rand.Intn(2 << 30)) 47 | } 48 | b.ResetTimer() 49 | for i := 0; i < b.N; i++ { 50 | SumV1(xs) 51 | } 52 | } 53 | 54 | func BenchmarkV2(b *testing.B) { 55 | b.ResetTimer() 56 | for i := 0; i < b.N; i++ { 57 | SumV2(xs) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /integration/cgo/gcc/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | gcc -std=c99 -c sum.c -o /tmp/sum.o 3 | gccgo -g -c main.go -o /tmp/main.o 4 | gccgo /tmp/sum.o /tmp/main.o -o /tmp/sum 5 | /tmp/sum 6 | -------------------------------------------------------------------------------- /integration/cgo/gcc/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | //extern sum 6 | func sum(*int64, int64) int64 7 | 8 | func Sum(xs []int64) int64 { 9 | if len(xs) == 0 { 10 | return 0 11 | } 12 | return sum(&xs[0], int64(len(xs))) 13 | } 14 | 15 | func main() { 16 | fmt.Println(Sum([]int64{1, 78})) 17 | } 18 | -------------------------------------------------------------------------------- /integration/cgo/gcc/sum.c: -------------------------------------------------------------------------------- 1 | long long sum(long long *xs, long long sz) { 2 | long long total = 0; 3 | for (long long i = 0; i < sz; i++) { 4 | total += xs[i]; 5 | } 6 | return total; 7 | } 8 | -------------------------------------------------------------------------------- /integration/cgo/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | /* 4 | long long sum(long long *xs, long long sz) { 5 | long long total = 0; 6 | long long i; 7 | for (i=0; i < sz; i++) { 8 | total += xs[i]; 9 | } 10 | return total; 11 | } 12 | */ 13 | import "C" 14 | import ( 15 | "fmt" 16 | "math/rand" 17 | "unsafe" 18 | ) 19 | 20 | func main() { 21 | xs := make([]int64, 4096) 22 | for i := 0; i < len(xs); i++ { 23 | xs[i] = int64(rand.Int()) 24 | } 25 | fmt.Println(C.sum((*C.longlong)(unsafe.Pointer(&xs[0])), C.longlong(len(xs)))) 26 | } 27 | -------------------------------------------------------------------------------- /integration/mq/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | //#include 4 | import "C" 5 | import ( 6 | "log" 7 | "os" 8 | ) 9 | 10 | func open() (mqd_t, error) { 11 | return mq_open("/example", C.O_CREAT|C.O_RDWR, 0644, mq_attr{ 12 | mq_flags: 0, 13 | mq_maxmsg: 10, 14 | mq_msgsize: 1024, 15 | mq_curmsgs: 0, 16 | }) 17 | } 18 | 19 | func recv() { 20 | q, err := open() 21 | if err != nil { 22 | panic(err) 23 | } 24 | defer mq_close(q) 25 | 26 | data, _, err := mq_receive(q) 27 | if err != nil { 28 | panic(err) 29 | } 30 | log.Println("received", string(data)) 31 | } 32 | 33 | func send() { 34 | q, err := open() 35 | if err != nil { 36 | panic(err) 37 | } 38 | defer mq_close(q) 39 | 40 | data := []byte("Hello World") 41 | log.Println("sending", string(data)) 42 | 43 | err = mq_send(q, data, 0) 44 | if err != nil { 45 | panic(err) 46 | } 47 | } 48 | 49 | func main() { 50 | log.SetFlags(0) 51 | if len(os.Args) < 2 { 52 | log.Fatalln("expected mode") 53 | } 54 | switch os.Args[1] { 55 | case "send": 56 | send() 57 | case "receive": 58 | recv() 59 | default: 60 | log.Fatalln("unknown mode") 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /integration/mq/mq.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | /* 4 | #cgo LDFLAGS: -lrt 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | mqd_t _mq_open(const char *name, int oflag, mode_t mode, struct mq_attr *attr) { 11 | return mq_open(name, oflag, mode, attr); 12 | } 13 | */ 14 | import "C" 15 | import ( 16 | "fmt" 17 | "log" 18 | "unsafe" 19 | ) 20 | 21 | type ( 22 | mqd_t C.mqd_t 23 | mq_attr struct { 24 | mq_flags, mq_maxmsg, mq_msgsize, mq_curmsgs int 25 | } 26 | ) 27 | 28 | func mq_open(name string, oflag int, mode int, attr mq_attr) (mqd_t, error) { 29 | cname := C.CString(name) 30 | defer C.free(unsafe.Pointer(cname)) 31 | var cattr C.struct_mq_attr 32 | cattr.mq_flags = C.__syscall_slong_t(attr.mq_flags) 33 | cattr.mq_maxmsg = C.__syscall_slong_t(attr.mq_maxmsg) 34 | cattr.mq_msgsize = C.__syscall_slong_t(attr.mq_msgsize) 35 | cattr.mq_curmsgs = C.__syscall_slong_t(attr.mq_curmsgs) 36 | q, err := C._mq_open(cname, C.int(oflag), C.mode_t(mode), &cattr) 37 | if err != nil { 38 | log.Printf("%d\n", err) 39 | return mqd_t(q), err 40 | } 41 | return mqd_t(q), nil 42 | } 43 | 44 | func mq_getattr(q mqd_t) (mq_attr, error) { 45 | var attr mq_attr 46 | var cattr C.struct_mq_attr 47 | res, err := C.mq_getattr(C.mqd_t(q), &cattr) 48 | if res < 0 { 49 | return attr, err 50 | } 51 | attr.mq_flags = int(cattr.mq_flags) 52 | attr.mq_maxmsg = int(cattr.mq_maxmsg) 53 | attr.mq_msgsize = int(cattr.mq_msgsize) 54 | attr.mq_curmsgs = int(cattr.mq_curmsgs) 55 | return attr, nil 56 | } 57 | 58 | func mq_close(q mqd_t) { 59 | C.mq_close(C.mqd_t(q)) 60 | } 61 | 62 | func mq_send(q mqd_t, data []byte, priority int) error { 63 | errno := C.mq_send(C.mqd_t(q), (*C.char)(unsafe.Pointer(&data[0])), C.size_t(len(data)), C.uint(priority)) 64 | if errno < 0 { 65 | return fmt.Errorf("some error") 66 | } 67 | return nil 68 | } 69 | 70 | func mq_receive(q mqd_t) ([]byte, int, error) { 71 | attr, err := mq_getattr(q) 72 | if err != nil { 73 | return nil, 0, err 74 | } 75 | data := make([]byte, attr.mq_msgsize) 76 | var priority C.uint 77 | sz, err := C.mq_receive(C.mqd_t(q), (*C.char)(unsafe.Pointer(&data[0])), C.size_t(attr.mq_msgsize), &priority) 78 | if sz < 0 { 79 | return nil, 0, err 80 | } 81 | return data[:sz], int(priority), nil 82 | } 83 | -------------------------------------------------------------------------------- /integration/namedpipes/rot13/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "io" 5 | "os" 6 | "os/signal" 7 | ) 8 | 9 | func init() { 10 | c := make(chan os.Signal, 1) 11 | signal.Notify(c, os.Interrupt) 12 | go func() { 13 | for range c { 14 | os.Exit(0) 15 | } 16 | }() 17 | } 18 | 19 | func rot13(b byte) byte { 20 | var a, z byte 21 | switch { 22 | case 'a' <= b && b <= 'z': 23 | a, z = 'a', 'z' 24 | case 'A' <= b && b <= 'Z': 25 | a, z = 'A', 'Z' 26 | default: 27 | return b 28 | } 29 | return (b-a+13)%(z-a+1) + a 30 | } 31 | 32 | func main() { 33 | var in io.Reader = os.Stdin 34 | var out io.Writer = os.Stdout 35 | 36 | if len(os.Args) > 1 && os.Args[1] != "-" { 37 | // open the input file for reading 38 | f, err := os.Open(os.Args[1]) 39 | if err != nil { 40 | panic(err) 41 | } 42 | defer f.Close() 43 | in = f 44 | } 45 | 46 | if len(os.Args) > 2 && os.Args[2] != "-" { 47 | // open the output file for writing 48 | f, err := os.Create(os.Args[2]) 49 | if err != nil { 50 | panic(err) 51 | } 52 | defer f.Close() 53 | out = f 54 | } 55 | 56 | // from here on is the same 57 | bs := []byte{0} 58 | for { 59 | _, err := in.Read(bs) 60 | if err != nil { 61 | break 62 | } 63 | bs[0] = rot13(bs[0]) 64 | _, err = out.Write(bs) 65 | if err != nil { 66 | break 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /integration/namedpipes/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | rm -rf /tmp/rot13.fifo 3 | 4 | # create our fifo queue 5 | mkfifo /tmp/rot13.fifo 6 | 7 | # start reading from it 8 | cat /tmp/rot13.fifo & 9 | 10 | # start writing to it (- means stdin / stdout) 11 | go run rot13/main.go - /tmp/rot13.fifo 12 | 13 | # you can also do: 14 | # 15 | # go run rot13/main.go > /tmp/rot13.fifo 16 | # 17 | -------------------------------------------------------------------------------- /integration/net/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "log" 6 | "net" 7 | "os" 8 | ) 9 | 10 | func client() error { 11 | conn, err := net.Dial("tcp", "127.0.0.1:9999") 12 | if err != nil { 13 | return err 14 | } 15 | defer conn.Close() 16 | 17 | br, bw := bufio.NewReader(conn), bufio.NewWriter(conn) 18 | 19 | msg := []byte("Hello World") 20 | log.Println("send", string(msg)) 21 | bw.Write(msg) 22 | bw.WriteByte('\n') 23 | bw.Flush() 24 | 25 | msg, _, _ = br.ReadLine() 26 | log.Println("recv", string(msg)) 27 | 28 | return nil 29 | } 30 | 31 | func server() error { 32 | listener, err := net.Listen("tcp", ":9999") 33 | if err != nil { 34 | return err 35 | } 36 | defer listener.Close() 37 | 38 | for { 39 | conn, err := listener.Accept() 40 | if err != nil { 41 | return err 42 | } 43 | br, bw := bufio.NewReader(conn), bufio.NewWriter(conn) 44 | 45 | msg, _, _ := br.ReadLine() 46 | log.Println("recv", string(msg)) 47 | log.Println("send", string(msg)) 48 | bw.Write(msg) 49 | bw.WriteByte('\n') 50 | bw.Flush() 51 | 52 | // close the connection 53 | conn.Close() 54 | } 55 | } 56 | 57 | func main() { 58 | if len(os.Args) < 2 { 59 | log.Fatalln("expected `mode`") 60 | } 61 | 62 | switch os.Args[1] { 63 | case "client": 64 | client() 65 | case "server": 66 | server() 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /integration/pipes/rot13/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | "os/signal" 6 | ) 7 | 8 | func init() { 9 | c := make(chan os.Signal, 1) 10 | signal.Notify(c, os.Interrupt) 11 | go func() { 12 | for range c { 13 | os.Exit(0) 14 | } 15 | }() 16 | } 17 | 18 | func rot13(b byte) byte { 19 | var a, z byte 20 | switch { 21 | case 'a' <= b && b <= 'z': 22 | a, z = 'a', 'z' 23 | case 'A' <= b && b <= 'Z': 24 | a, z = 'A', 'Z' 25 | default: 26 | return b 27 | } 28 | return (b-a+13)%(z-a+1) + a 29 | } 30 | 31 | func main() { 32 | bs := []byte{0} 33 | for { 34 | _, err := os.Stdin.Read(bs) 35 | if err != nil { 36 | break 37 | } 38 | bs[0] = rot13(bs[0]) 39 | _, err = os.Stdout.Write(bs) 40 | if err != nil { 41 | break 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /integration/pipes/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | go run rot13/main.go | go run rot13/main.go 3 | -------------------------------------------------------------------------------- /integration/shm/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/binary" 5 | "fmt" 6 | "log" 7 | "math/rand" 8 | "os" 9 | "unsafe" 10 | 11 | "github.com/Shopify/sysv_mq" 12 | "github.com/edsrzf/mmap-go" 13 | ) 14 | 15 | func send(mq *sysv_mq.MessageQueue, offset, size int) error { 16 | var data [16]byte 17 | binary.BigEndian.PutUint64(data[:], uint64(offset)) 18 | binary.BigEndian.PutUint64(data[8:], uint64(size)) 19 | return mq.SendBytes(data[:], 1, 0) 20 | } 21 | 22 | func recv(mq *sysv_mq.MessageQueue) (offset, size int, err error) { 23 | data, _, e := mq.ReceiveBytes(1, 0) 24 | if err != nil { 25 | err = e 26 | return 27 | } 28 | if len(data) < 16 { 29 | err = fmt.Errorf("expected offset and size") 30 | return 31 | } 32 | offset = int(binary.BigEndian.Uint64(data[:8])) 33 | size = int(binary.BigEndian.Uint64(data[8:])) 34 | return 35 | } 36 | 37 | func server(mq *sysv_mq.MessageQueue) { 38 | fd, err := shm_open("/shm-example", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0777) 39 | if err != nil { 40 | panic(err) 41 | } 42 | defer fd.Close() 43 | defer shm_unlink("/shm-example") 44 | 45 | err = fd.Truncate(50 * 1024 * 1024) 46 | if err != nil { 47 | panic(err) 48 | } 49 | 50 | data, err := mmap.Map(fd, mmap.RDWR|mmap.EXEC, 0) 51 | if err != nil { 52 | panic(err) 53 | } 54 | 55 | for { 56 | _, sz, err := recv(mq) 57 | if err != nil { 58 | panic(err) 59 | } 60 | arr := mkArr(data, sz) 61 | log.Println("received", len(arr), "doubles") 62 | } 63 | } 64 | 65 | func client(mq *sysv_mq.MessageQueue) { 66 | fd, err := shm_open("/shm-example", os.O_RDWR, 0777) 67 | if err != nil { 68 | panic(err) 69 | } 70 | defer fd.Close() 71 | 72 | data, err := mmap.Map(fd, mmap.RDWR|mmap.EXEC, 0) 73 | if err != nil { 74 | panic(err) 75 | } 76 | 77 | sz := 1024 * 1024 * 50 / 8 78 | arr := mkArr(data, sz) 79 | for i := 0; i < sz; i++ { 80 | arr[i] = rand.Float64() 81 | } 82 | log.Println("sending", sz, "doubles") 83 | send(mq, 0, sz) 84 | } 85 | 86 | func mkArr(data []byte, sz int) []float64 { 87 | // in general: 88 | // 89 | // (1) grab the address of the first element of the byte slice 90 | // all the subsequent elements will be adjacent 91 | // (2) convert that address into an unsafe pointer 92 | // (3) convert the unsafe pointer into an array pointer. We tell 93 | // the compiler that the array is as large as possible 94 | // (4) convert the array into a slice and give it a fixed length 95 | // and capacity 96 | return (*[(1 << 31) - 1]float64)(unsafe.Pointer(&data[0]))[:sz:sz] 97 | } 98 | 99 | func main() { 100 | log.SetFlags(0) 101 | 102 | if len(os.Args) < 2 { 103 | log.Fatalln("expected mode") 104 | } 105 | 106 | mq, err := sysv_mq.NewMessageQueue(&sysv_mq.QueueConfig{ 107 | Key: 1001, 108 | MaxSize: 8 * 1024, 109 | Mode: sysv_mq.IPC_CREAT | 0600, 110 | }) 111 | if err != nil { 112 | log.Fatalln(err) 113 | } 114 | defer mq.Close() 115 | 116 | switch os.Args[1] { 117 | case "server": 118 | server(mq) 119 | case "client": 120 | client(mq) 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /integration/shm/sem.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | /* 4 | #cgo LDFLAGS: -pthread 5 | #include 6 | #include 7 | #include 8 | 9 | sem_t *sem_open_(const char *name, int oflag, mode_t mode, unsigned int value) { 10 | return sem_open(name, oflag, mode, value); 11 | } 12 | */ 13 | import "C" 14 | import "unsafe" 15 | 16 | type ( 17 | sem_t C.sem_t 18 | ) 19 | 20 | func sem_open(name string, oflag, mode, value int) (*sem_t, error) { 21 | c_name := C.CString(name) 22 | defer C.free(unsafe.Pointer(c_name)) 23 | c_oflag := C.int(oflag) 24 | c_mode := C.mode_t(mode) 25 | c_value := C.uint(value) 26 | r, err := C.sem_open_(c_name, c_oflag, c_mode, c_value) 27 | return (*sem_t)(r), err 28 | } 29 | 30 | func sem_close(sem *sem_t) error { 31 | _, err := C.sem_close((*C.sem_t)(sem)) 32 | return err 33 | } 34 | 35 | func sem_wait(sem *sem_t) error { 36 | _, err := C.sem_wait((*C.sem_t)(sem)) 37 | return err 38 | } 39 | 40 | func sem_post(sem *sem_t) error { 41 | _, err := C.sem_post((*C.sem_t)(sem)) 42 | return err 43 | } 44 | 45 | func sem_unlink(name string) error { 46 | c_name := C.CString(name) 47 | defer C.free(unsafe.Pointer(c_name)) 48 | _, err := C.sem_unlink(c_name) 49 | return err 50 | } 51 | -------------------------------------------------------------------------------- /integration/shm/shm.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | /* 4 | #cgo LDFLAGS: -lrt 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | int c_shm_open(const char *name, int oflag, mode_t mode) { 11 | return shm_open(name, oflag, mode); 12 | } 13 | */ 14 | import "C" 15 | import ( 16 | "os" 17 | "unsafe" 18 | ) 19 | 20 | func shm_open(name string, oflag, mode int) (*os.File, error) { 21 | c_name := C.CString(name) 22 | defer C.free(unsafe.Pointer(c_name)) 23 | c_oflag := C.int(oflag) 24 | c_mode := C.mode_t(mode) 25 | c_res, err := C.c_shm_open(c_name, c_oflag, c_mode) 26 | if err != nil { 27 | return nil, err 28 | } 29 | return os.NewFile(uintptr(c_res), name), nil 30 | } 31 | 32 | func shm_unlink(name string) error { 33 | c_name := C.CString(name) 34 | defer C.free(unsafe.Pointer(c_name)) 35 | _, err := C.shm_unlink(c_name) 36 | return err 37 | } 38 | -------------------------------------------------------------------------------- /integration/swig/gsl/gsl.go: -------------------------------------------------------------------------------- 1 | package gsl 2 | 3 | //#cgo LDFLAGS: -lgsl -lm -lgslcblas 4 | import "C" 5 | 6 | func Mean(xs []float64) float64 { 7 | if len(xs) == 0 { 8 | return 0 9 | } 10 | return Gsl_stats_mean(&xs[0], 1, int64(len(xs))) 11 | } 12 | -------------------------------------------------------------------------------- /integration/swig/gsl/gsl.swig: -------------------------------------------------------------------------------- 1 | %{ 2 | #include 3 | #include 4 | %} 5 | double gsl_stats_mean(const double data[], size_t stride, size_t n); 6 | -------------------------------------------------------------------------------- /integration/swig/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | 7 | "github.com/calebdoxsey/tutorials/integration/swig/gsl" 8 | ) 9 | 10 | func main() { 11 | xs := make([]float64, 4096) 12 | for i := 0; i < len(xs); i++ { 13 | xs[i] = rand.Float64() 14 | } 15 | fmt.Println(gsl.Mean(xs)) 16 | } 17 | -------------------------------------------------------------------------------- /integration/swig/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | go run main.go 3 | -------------------------------------------------------------------------------- /integration/syso/c/sum.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | typedef struct { 4 | int64_t* ptr; 5 | int64_t len; 6 | int64_t cap; 7 | int64_t* ret; 8 | } slice; 9 | 10 | void sum(slice slc) { 11 | int64_t total = 0; 12 | for (int64_t i = 0; i < slc.len; i++) { 13 | total += slc.ptr[i]; 14 | } 15 | *slc.ret = slc.len; 16 | } 17 | -------------------------------------------------------------------------------- /integration/syso/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | fmt.Println(Sum([]int64{1, 2, 3})) 7 | } 8 | -------------------------------------------------------------------------------- /integration/syso/sum.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func Sum(xs []int64) int64 4 | -------------------------------------------------------------------------------- /integration/syso/sum_amd64.Conflict.S: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calebdoxsey/tutorials/3633571d40dad8638b332660c23987a48e3c14d5/integration/syso/sum_amd64.Conflict.S -------------------------------------------------------------------------------- /integration/syso/sum_amd64.S: -------------------------------------------------------------------------------- 1 | 2 | TEXT ·Sum(SB),$0-24 3 | JMP sum(SB) 4 | -------------------------------------------------------------------------------- /integration/syso/sum_amd64.c: -------------------------------------------------------------------------------- 1 | struct slice { 2 | void* ptr; 3 | int64_t len; 4 | int64_t cap; 5 | } 6 | 7 | int64_t SumV2(slice slc) { 8 | return slc.len; 9 | } 10 | -------------------------------------------------------------------------------- /integration/syso/sum_amd64.syso: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calebdoxsey/tutorials/3633571d40dad8638b332660c23987a48e3c14d5/integration/syso/sum_amd64.syso -------------------------------------------------------------------------------- /integration/syso/sum_amd64_src.S: -------------------------------------------------------------------------------- 1 | .global _sum 2 | 3 | .text 4 | _sum: 5 | mov $0,%eax 6 | mov %eax,-24(%rsp) 7 | ret 8 | 9 | .data 10 | -------------------------------------------------------------------------------- /integration/syso/syso: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calebdoxsey/tutorials/3633571d40dad8638b332660c23987a48e3c14d5/integration/syso/syso -------------------------------------------------------------------------------- /integration/sysv_mq/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "github.com/Shopify/sysv_mq" 7 | ) 8 | 9 | func main() { 10 | log.SetFlags(0) 11 | 12 | mq, err := sysv_mq.NewMessageQueue(&sysv_mq.QueueConfig{ 13 | Key: 1234, 14 | MaxSize: 8 * 1024, 15 | Mode: sysv_mq.IPC_CREAT | 0600, 16 | }) 17 | if err != nil { 18 | log.Fatalln(err) 19 | } 20 | defer mq.Close() 21 | defer mq.Destroy() 22 | for { 23 | msg, typ, err := mq.ReceiveBytes(0, 0) 24 | if err != nil { 25 | log.Fatalln(err) 26 | } 27 | log.Println("received", typ, string(msg)) 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /integration/sysv_mq/python/main.py: -------------------------------------------------------------------------------- 1 | import json 2 | import sysv_ipc 3 | import time 4 | import twitter 5 | 6 | def main(): 7 | api = twitter.Api(consumer_key='Qze48TI7JjUqCIQO2PeJbTCpN', 8 | consumer_secret='Kj0auPQIvlqIhQ68sD69SOz52FAv7UA8A5UQbwsUQq8GhdxFbH', 9 | access_token_key='3067345412-I81ggjwZaMl65C1blOCJ4KnswQ7spLVAjo5vY65', 10 | access_token_secret='aZGySCdiqfoq3J2mRTNGOPldk9crJEKY58btfZEapLCqS') 11 | 12 | 13 | 14 | 15 | mq = sysv_ipc.MessageQueue(1234) 16 | while True: 17 | for status in api.GetStreamFilter(track="obama"): 18 | if "text" in status: 19 | if len(status["text"]) < 2048: 20 | mq.send(status["text"].encode("UTF-8")) 21 | 22 | main() 23 | -------------------------------------------------------------------------------- /integration/unixsocket/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "net/rpc/jsonrpc" 6 | ) 7 | 8 | type Pair struct { 9 | X, Y int 10 | } 11 | 12 | func main() { 13 | client, err := jsonrpc.Dial("unix", "/tmp/example.sock") 14 | if err != nil { 15 | panic(err) 16 | } 17 | defer client.Close() 18 | 19 | for i := 0; i < 5; i++ { 20 | log.Println("send", "1+4") 21 | var reply int 22 | err = client.Call("add", Pair{1, 4}, &reply) 23 | if err != nil { 24 | panic(err) 25 | } 26 | log.Println("recv", reply) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /integration/unixsocket/python/main.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import json 3 | import os 4 | 5 | def main(): 6 | print("starting server") 7 | sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) 8 | sock.bind("/tmp/example.sock") 9 | sock.listen(1) 10 | while True: 11 | conn, addr = sock.accept() 12 | try: 13 | f = conn.makefile('rw') 14 | for ln in f: 15 | obj = json.loads(ln) 16 | print("recv %s(%s)" % (obj["method"], obj["params"][0])) 17 | if obj["method"] == "add": 18 | print("send %s" % obj["params"][0]) 19 | conn.send(json.dumps({ 20 | "result": obj["params"][0]["X"] + obj["params"][0]["Y"], 21 | "id": obj["id"], 22 | "error": None, 23 | })) 24 | except ValueError, e: 25 | print("ValueError", e) 26 | finally: 27 | conn.close() 28 | 29 | try: 30 | main() 31 | finally: 32 | os.unlink("/tmp/example.sock") 33 | -------------------------------------------------------------------------------- /talks/2018-01-30--extending-gopherjs/README.md: -------------------------------------------------------------------------------- 1 | # 2018-01-30 Talk for Boston Go Meetup: Extending GopherJS 2 | 3 | ## Examples 4 | 5 | 1. Hello World 6 | 2. Javascript APIs 7 | 3. Concurrent Merge Sort 8 | 4. Broken `stdin` 9 | 5. Syscall Write using `document.write` 10 | 6. Syscall Virtual File System 11 | 7. Persistence 12 | 8. Network Sockets 13 | -------------------------------------------------------------------------------- /talks/2018-01-30--extending-gopherjs/assets/cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calebdoxsey/tutorials/3633571d40dad8638b332660c23987a48e3c14d5/talks/2018-01-30--extending-gopherjs/assets/cover.png -------------------------------------------------------------------------------- /talks/2018-01-30--extending-gopherjs/assets/datadog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calebdoxsey/tutorials/3633571d40dad8638b332660c23987a48e3c14d5/talks/2018-01-30--extending-gopherjs/assets/datadog.png -------------------------------------------------------------------------------- /talks/2018-01-30--extending-gopherjs/assets/js/example-02.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"example-02.js","sources":["/github.com/gopherjs/gopherjs/js/js.go","runtime.go","mme/versions/go1.9.1.linux.amd64/src/runtime/error.go","/bitbucket.org/calebdoxsey/www/www/tutorials/talks/2018-01-30--extending-gopherjs/example-02/main.go"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+B4C;A;;;;;AAGW;A;;;;;AAGhB;A;;;;;AAGN;A;;;;;AAGQ;A;;;;;AAGc;A;;;;;AAGY;A;;;;;AAGX;A;;;;;AAGH;A;;;;;AAGrB;A;;;;;AAGI;A;;;;;AAGN;A;;;;;AAGI;A;;;;;AAGE;A;;;;;AAGA;A;;;;;AAGQ;A;;;;;AAGP;A;;;;;AASnC;A;;;;;AAKA;A;;;;AAwEA;AACA;A;;;;;;;;A;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC5IA;AACA;AACA;AACA;AAEI;AACJ;AACA;A;;;AAyLA;A;;A;;;;;AC5LA;A;AAEC;A;A;AAGA;A;A;AAGA;A;AAGD;A;;;;;A;;;;;AAUA;A;;;;;;;;A;A;A;;;;;;;;;;AC9CA;A;;;;A;A;A;A;A"} 2 | -------------------------------------------------------------------------------- /talks/2018-01-30--extending-gopherjs/assets/js/postscribe.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file postscribe 3 | * @description Asynchronously write javascript, even with document.write. 4 | * @version v2.0.8 5 | * @see {@link https://krux.github.io/postscribe} 6 | * @license MIT 7 | * @author Derek Brans 8 | * @copyright 2016 Krux Digital, Inc 9 | */ 10 | !function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports["postscribe"]=e():t["postscribe"]=e()}(this,function(){return function(t){function e(n){if(r[n])return r[n].exports;var o=r[n]={"exports":{},"id":n,"loaded":!1};return t[n].call(o.exports,o,o.exports,e),o.loaded=!0,o.exports}var r={};return e.m=t,e.c=r,e.p="",e(0)}([function(t,e,r){"use strict";function n(t){return t&&t.__esModule?t:{"default":t}}var o=r(1),i=n(o);t.exports=i["default"]},function(t,e,r){"use strict";function n(t){if(t&&t.__esModule)return t;var e={};if(null!=t)for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(e[r]=t[r]);return e["default"]=t,e}function o(t){return t&&t.__esModule?t:{"default":t}}function i(){}function a(){var t=m.shift();if(t){var e=h.last(t);e.afterDequeue(),t.stream=s.apply(void 0,t),e.afterStreamStart()}}function s(t,e,r){function n(t){t=r.beforeWrite(t),g.write(t),r.afterWrite(t)}g=new p["default"](t,r),g.id=y++,g.name=r.name||g.id,u.streams[g.name]=g;var o=t.ownerDocument,s={"close":o.close,"open":o.open,"write":o.write,"writeln":o.writeln};c(o,{"close":i,"open":i,"write":function(){for(var t=arguments.length,e=Array(t),r=0;r2&&void 0!==arguments[2]?arguments[2]:null,n=d+e;f.existy(r)&&""!==r?t.setAttribute(n,r):t.removeAttribute(n)}e.__esModule=!0;var u=Object.assign||function(t){for(var e=1;e1&&void 0!==arguments[1]?arguments[1]:{};i(this,t),this.root=e,this.options=r,this.doc=e.ownerDocument,this.win=this.doc.defaultView||this.doc.parentWindow,this.parser=new l["default"]("",{"autoFix":r.autoFix}),this.actuals=[e],this.proxyHistory="",this.proxyRoot=this.doc.createElement(e.nodeName),this.scriptStack=[],this.writeQueue=[],s(this.proxyRoot,"proxyof",0)}return t.prototype.write=function(){var t;for((t=this.writeQueue).push.apply(t,arguments);!this.deferredRemote&&this.writeQueue.length;){var e=this.writeQueue.shift();f.isFunction(e)?this._callFunction(e):this._writeImpl(e)}},t.prototype._callFunction=function(t){var e={"type":"function","value":t.name||t.toString()};this._onScriptStart(e),t.call(this.win,this.doc),this._onScriptDone(e)},t.prototype._writeImpl=function(t){this.parser.append(t);for(var e=void 0,r=void 0,n=void 0,o=[];(e=this.parser.readToken())&&!(r=f.isScript(e))&&!(n=f.isStyle(e));)e=this.options.beforeWriteToken(e),e&&o.push(e);o.length>0&&this._writeStaticTokens(o),r&&this._handleScriptToken(e),n&&this._handleStyleToken(e)},t.prototype._writeStaticTokens=function(t){var e=this._buildChunk(t);return e.actual?(e.html=this.proxyHistory+e.actual,this.proxyHistory+=e.proxy,this.proxyRoot.innerHTML=e.html,h&&(e.proxyInnerHTML=this.proxyRoot.innerHTML),this._walkChunk(),h&&(e.actualInnerHTML=this.root.innerHTML),e):null},t.prototype._buildChunk=function(t){for(var e=this.actuals.length,r=[],n=[],o=[],i=t.length,a=0;a)/," "+d+"id="+c+" $1")),s.attrs.id!==m&&s.attrs.id!==y&&o.push("atomicTag"===s.type?"":"<"+s.tagName+" "+d+"proxyof="+c+(s.unary?" />":">"))}}else n.push(u),o.push("endTag"===s.type?u:"")}return{"tokens":t,"raw":r.join(""),"actual":n.join(""),"proxy":o.join("")}},t.prototype._walkChunk=function(){for(var t=void 0,e=[this.proxyRoot];f.existy(t=e.shift());){var r=1===t.nodeType,n=r&&a(t,"proxyof");if(!n){r&&(this.actuals[a(t,"id")]=t,s(t,"id"));var o=t.parentNode&&a(t.parentNode,"proxyof");o&&this.actuals[o].appendChild(t)}e.unshift.apply(e,f.toArray(t.childNodes))}},t.prototype._handleScriptToken=function(t){var e=this,r=this.parser.clear();r&&this.writeQueue.unshift(r),t.src=t.attrs.src||t.attrs.SRC,t=this.options.beforeWriteToken(t),t&&(t.src&&this.scriptStack.length?this.deferredRemote=t:this._onScriptStart(t),this._writeScriptToken(t,function(){e._onScriptDone(t)}))},t.prototype._handleStyleToken=function(t){var e=this.parser.clear();e&&this.writeQueue.unshift(e),t.type=t.attrs.type||t.attrs.TYPE||"text/css",t=this.options.beforeWriteToken(t),t&&this._writeStyleToken(t),e&&this.write()},t.prototype._writeStyleToken=function(t){var e=this._buildStyle(t);this._insertCursor(e,y),t.content&&(e.styleSheet&&!e.sheet?e.styleSheet.cssText=t.content:e.appendChild(this.doc.createTextNode(t.content)))},t.prototype._buildStyle=function(t){var e=this.doc.createElement(t.tagName);return e.setAttribute("type",t.type),f.eachKey(t.attrs,function(t,r){e.setAttribute(t,r)}),e},t.prototype._insertCursor=function(t,e){this._writeImpl('');var r=this.doc.getElementById(e);r&&r.parentNode.replaceChild(t,r)},t.prototype._onScriptStart=function(t){t.outerWrites=this.writeQueue,this.writeQueue=[],this.scriptStack.unshift(t)},t.prototype._onScriptDone=function(t){return t!==this.scriptStack[0]?void this.options.error({"msg":"Bad script nesting or script finished twice"}):(this.scriptStack.shift(),this.write.apply(this,t.outerWrites),void(!this.scriptStack.length&&this.deferredRemote&&(this._onScriptStart(this.deferredRemote),this.deferredRemote=null)))},t.prototype._writeScriptToken=function(t,e){var r=this._buildScript(t),n=this._shouldRelease(r),o=this.options.afterAsync;t.src&&(r.src=t.src,this._scriptLoadHandler(r,n?o:function(){e(),o()}));try{this._insertCursor(r,m),r.src&&!n||e()}catch(t){this.options.error(t),e()}},t.prototype._buildScript=function(t){var e=this.doc.createElement(t.tagName);return f.eachKey(t.attrs,function(t,r){e.setAttribute(t,r)}),t.content&&(e.text=t.content),e},t.prototype._scriptLoadHandler=function(t,e){function r(){t=t.onload=t.onreadystatechange=t.onerror=null}function n(){r(),null!=e&&e(),e=null}function o(t){r(),a(t),null!=e&&e(),e=null}function i(t,e){var r=t["on"+e];null!=r&&(t["_on"+e]=r)}var a=this.options.error;i(t,"load"),i(t,"error"),u(t,{"onload":function(){if(t._onload)try{t._onload.apply(this,Array.prototype.slice.call(arguments,0))}catch(e){o({"msg":"onload handler failed "+e+" @ "+t.src})}n()},"onerror":function(){if(t._onerror)try{t._onerror.apply(this,Array.prototype.slice.call(arguments,0))}catch(e){return void o({"msg":"onerror handler failed "+e+" @ "+t.src})}o({"msg":"remote script failed "+t.src})},"onreadystatechange":function(){/^(loaded|complete)$/.test(t.readyState)&&n()}})},t.prototype._shouldRelease=function(t){var e=/^script$/i.test(t.nodeName);return!e||!!(this.options.releaseAsync&&t.src&&t.hasAttribute("async"))},t}();e["default"]=g},function(t,e,r){!function(e,r){t.exports=r()}(this,function(){return function(t){function e(n){if(r[n])return r[n].exports;var o=r[n]={"exports":{},"id":n,"loaded":!1};return t[n].call(o.exports,o,o.exports,e),o.loaded=!0,o.exports}var r={};return e.m=t,e.c=r,e.p="",e(0)}([function(t,e,r){"use strict";function n(t){return t&&t.__esModule?t:{"default":t}}var o=r(1),i=n(o);t.exports=i["default"]},function(t,e,r){"use strict";function n(t){return t&&t.__esModule?t:{"default":t}}function o(t){if(t&&t.__esModule)return t;var e={};if(null!=t)for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(e[r]=t[r]);return e["default"]=t,e}function i(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}e.__esModule=!0;var a=r(2),s=o(a),u=r(3),c=o(u),l=r(6),p=n(l),f=r(5),h={"comment":/^");if(e>=0)return new c.CommentToken(t.substr(4,e-1),e+3)}function o(t){var e=t.indexOf("<");return new c.CharsToken(e>=0?e:t.length)}function i(t){var e=t.indexOf(">");if(e!==-1){var r=t.match(l.startTag);if(r){var n=function(){var t={},e={},n=r[2];return r[2].replace(l.attr,function(r,o){arguments[2]||arguments[3]||arguments[4]||arguments[5]?arguments[5]?(t[arguments[5]]="",e[arguments[5]]=!0):t[o]=arguments[2]||arguments[3]||arguments[4]||l.fillAttr.test(o)&&o||"":t[o]="",n=n.replace(r,"")}),{"v":new c.StartTagToken(r[1],r[0].length,t,e,(!!r[3]),n.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,""))}}();if("object"===("undefined"==typeof n?"undefined":u(n)))return n.v}}}function a(t){var e=i(t);if(e){var r=t.slice(e.length);if(r.match(new RegExp("","i"))){var n=r.match(new RegExp("([\\s\\S]*?)","i"));if(n)return new c.AtomicTagToken(e.tagName,n[0].length+e.length,e.attrs,e.booleanAttrs,n[1])}}}function s(t){var e=t.match(l.endTag);if(e)return new c.EndTagToken(e[1],e[0].length)}e.__esModule=!0;var u="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t};e.comment=n,e.chars=o,e.startTag=i,e.atomicTag=a,e.endTag=s;var c=r(4),l={"startTag":/^<([\-A-Za-z0-9_]+)((?:\s+[\w\-]+(?:\s*=?\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)>/,"endTag":/^<\/([\-A-Za-z0-9_]+)[^>]*>/,"attr":/(?:([\-A-Za-z0-9_]+)\s*=\s*(?:(?:"((?:\\.|[^"])*)")|(?:'((?:\\.|[^'])*)')|([^>\s]+)))|(?:([\-A-Za-z0-9_]+)(\s|$)+)/g,"fillAttr":/^(checked|compact|declare|defer|disabled|ismap|multiple|nohref|noresize|noshade|nowrap|readonly|selected)$/i}},function(t,e,r){"use strict";function n(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}e.__esModule=!0,e.EndTagToken=e.AtomicTagToken=e.StartTagToken=e.TagToken=e.CharsToken=e.CommentToken=e.Token=void 0;var o=r(5),i=(e.Token=function t(e,r){n(this,t),this.type=e,this.length=r,this.text=""},e.CommentToken=function(){function t(e,r){n(this,t),this.type="comment",this.length=r||(e?e.length:0),this.text="",this.content=e}return t.prototype.toString=function(){return"