├── .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("\\s*"+e.tagName+"\\s*>","i"))){var n=r.match(new RegExp("([\\s\\S]*?)\\s*"+e.tagName+"\\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"