├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── asset.go ├── binary.go ├── binary └── main.go ├── composer.go ├── reader.go └── tree.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | 26 | *.binary.go 27 | bin 28 | output 29 | test 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Samuel 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | .PHONY: build 3 | build: 4 | CGO_ENABLED=0 go build -a -installsuffix cgo -o bin/binary cmd/main.go 5 | 6 | .PHONY: install 7 | install: 8 | cp -rf bin/binary $$GOPATH/bin/binary 9 | 10 | .PHONY: test 11 | test: 12 | bin/binary -dir ./test -out ./output -pkg test -max 300 13 | 14 | .PHONY: clean 15 | clean: 16 | rm -rf output/* 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # binary-go 2 | Embedding binary data in a Go program 3 | 4 | ## Features 5 | 6 | * Splitting files (into smaller pieces) 7 | * Compression 8 | 9 | ## Install 10 | 11 | Install the latest release on Mac or Linux: 12 | ``` 13 | go get -u github.com/samuelngs/binary-go/... 14 | ``` 15 | 16 | ## Usage 17 | 18 | Convert binary data to `.go` files 19 | ```sh 20 | $ binary -dir ./test -out ./output -pkg test -max 300 21 | ┌───────┬────────────────┬──────────────────────────────────────────┬──────────────────────────┐ 22 | │ PART │ SIZE │ HASH │ FILE │ 23 | ├───────┼────────────────┼──────────────────────────────────────────┼──────────────────────────┤ 24 | │ 01 │ 276 KB │ 9e0d4c6ecf7afd6152bd0950ea9a22fbaeb2f58e │ README.md-1.go │ 25 | │ 01 │ 304 KB │ 9b4e8073b480e2118b3ac6a73eae376a128359e4 │ LICENSE-1.go │ 26 | │ 02 │ 304 KB │ 578fa140625eef79cb04f12c992d36579da0ba5e │ LICENSE-2.go │ 27 | │ 03 │ 304 KB │ b3f1b26db7b0d82ad437b324128c3aaf832ea96c │ LICENSE-3.go │ 28 | │ 04 │ 304 KB │ 54ca42b211e9e3120ba4a1d040d6bce7d18bcb6e │ LICENSE-4.go │ 29 | │ 05 │ 304 KB │ d0a4f769fe4a235c50c48cebe76d77c1c04e0a58 │ LICENSE-5.go │ 30 | │ 06 │ 304 KB │ f43cca28e65ab4e88f478784f0c84b78ec8db356 │ LICENSE-6.go │ 31 | │ 07 │ 304 KB │ 7346e2c8f45188d9075cef877526f0d200917508 │ LICENSE-7.go │ 32 | │ 08 │ 304 KB │ 85366250d7bd91843ff134ee945eae4405b887b0 │ LICENSE-8.go │ 33 | │ 09 │ 192 KB │ c452da088bc03763ebc190a377766c1575fda3e8 │ LICENSE-9.go │ 34 | └───────┴────────────────┴──────────────────────────────────────────┴──────────────────────────┘ 35 | ``` 36 | 37 | Output 38 | ``` 39 | package test 40 | 41 | import ( 42 | "bytes" 43 | "compress/gzip" 44 | "errors" 45 | "io" 46 | "log" 47 | ) 48 | 49 | var data = map[string][]string{ 50 | "test/README.md": []string{ 51 | d9e0d4c6ecf7afd6152bd0950ea9a22fbaeb2f58e, 52 | }, 53 | "test/LICENSE": []string{ 54 | d9b4e8073b480e2118b3ac6a73eae376a128359e4, d578fa140625eef79cb04f12c992d36579da0ba5e, db3f1b26db7b0d82ad437b324128c3aaf832ea96c, d54ca42b211e9e3120ba4a1d040d6bce7d18bcb6e, dd0a4f769fe4a235c50c48cebe76d77c1c04e0a58, df43cca28e65ab4e88f478784f0c84b78ec8db356, d7346e2c8f45188d9075cef877526f0d200917508, d85366250d7bd91843ff134ee945eae4405b887b0, dc452da088bc03763ebc190a377766c1575fda3e8, 55 | }, 56 | } 57 | 58 | var d9b4e8073b480e2118b3ac6a73eae376a128359e4 = "\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\x5c\x52\xcf\x8e\xda\x3c\x10\xbf\xfb\x29\x46\x9c\x76\xa5\x68\xbf\xaf\x3d\xf4\xd0\x9b\x49\xcc\x62\x35\xc4\x91\x13\x96\x72\x34\x89\x21\xae\x42\x8c\x62\xa7\x68\xdf\xbe\x33\x81\xdd\xed\x56\x42\x42\x33\x9e\xdf\xbf\x99\xd4\x9d\x85\x8d\xac\x21\x77\x8d\x1d\x82\x85\x07\x2c" 59 | var d578fa140625eef79cb04f12c992d36579da0ba5e = "\x1e\x19\x4b\xfd\xe5\x75\x74\xa7\x2e\xc2\x43\xf3\x08\x5f\xff\xff\xf2\x0d\x2a\x73\x9e\x6c\xcf\x58\x69\xc7\xb3\x0b\xc1\xf9\x01\x5c\x80\xce\x8e\xf6\xf0\x0a\xa7\xd1\x0c\xd1\xb6\x09\x1c\x47\x6b\xc1\x1f\xa1\xe9\xcc\x78\xb2\x09\x44\x0f\x66\x78\x85\x8b\x1d\x03\x02\xfc\x21\x1a\x37\xb8\xe1\x04\x06\x1a\x94\x60\x38" 60 | 61 | ... 62 | 63 | ``` 64 | 65 | ## Documentation 66 | 67 | `go doc` format documentation for this project can be viewed online without installing the package by using the GoDoc page at: https://godoc.org/github.com/samuelngs/binary-go 68 | 69 | ## Contributing 70 | 71 | Everyone is encouraged to help improve this project. Here are a few ways you can help: 72 | 73 | - [Report bugs](https://github.com/samuelngs/binary-go/issues) 74 | - Fix bugs and [submit pull requests](https://github.com/samuelngs/binary-go/pulls) 75 | - Write, clarify, or fix documentation 76 | - Suggest or add new features 77 | 78 | ## License ## 79 | 80 | This project is distributed under the MIT license found in the [LICENSE](./LICENSE) 81 | file. 82 | 83 | ``` 84 | The MIT License (MIT) 85 | 86 | Copyright (c) 2016 Samuel 87 | 88 | Permission is hereby granted, free of charge, to any person obtaining a copy 89 | of this software and associated documentation files (the "Software"), to deal 90 | in the Software without restriction, including without limitation the rights 91 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 92 | copies of the Software, and to permit persons to whom the Software is 93 | furnished to do so, subject to the following conditions: 94 | 95 | The above copyright notice and this permission notice shall be included in all 96 | copies or substantial portions of the Software. 97 | 98 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 99 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 100 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 101 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 102 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 103 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 104 | SOFTWARE. 105 | ``` 106 | -------------------------------------------------------------------------------- /asset.go: -------------------------------------------------------------------------------- 1 | package binary 2 | 3 | import ( 4 | "bufio" 5 | "crypto/md5" 6 | "encoding/hex" 7 | "io" 8 | "log" 9 | "os" 10 | "path/filepath" 11 | ) 12 | 13 | // Asset holds file information 14 | type Asset struct { 15 | path string 16 | name string 17 | } 18 | 19 | // NewAsset creates gzip compressed asset 20 | func NewAsset(path, name string) *Asset { 21 | return &Asset{path, name} 22 | } 23 | 24 | // Dirpath returns the directory path of the file 25 | func (v *Asset) Dirpath() string { 26 | return filepath.Dir(v.path) 27 | } 28 | 29 | // Filename returns the filename with extension 30 | func (v *Asset) Filename() string { 31 | return v.name 32 | } 33 | 34 | // Relpath returns file relative path 35 | func (v *Asset) Relpath() string { 36 | pwd, err := os.Getwd() 37 | if err != nil { 38 | return v.path 39 | } 40 | rel, err := filepath.Rel(pwd, v.path) 41 | if err != nil { 42 | return v.path 43 | } 44 | return rel 45 | } 46 | 47 | // Filepath returns the full file path 48 | func (v *Asset) Filepath() string { 49 | return v.path 50 | } 51 | 52 | // Pipe data 53 | func (v *Asset) Pipe() <-chan []byte { 54 | fe, err := os.Open(v.path) 55 | if err != nil { 56 | log.Fatal(err) 57 | } 58 | fi, err := fe.Stat() 59 | if err != nil { 60 | log.Fatal(err) 61 | } 62 | rd := bufio.NewReader(fe) 63 | ch := make(chan []byte, 1) 64 | go func() { 65 | defer fe.Close() 66 | defer close(ch) 67 | buf := make([]byte, fi.Size()) 68 | _, err := rd.Read(buf) 69 | if err != nil && err != io.EOF { 70 | log.Fatal(err) 71 | } 72 | ch <- buf 73 | }() 74 | return ch 75 | } 76 | 77 | // Size returns asset size 78 | func (v *Asset) Size() int64 { 79 | fe, err := os.Open(v.path) 80 | if err != nil { 81 | log.Fatal(err) 82 | } 83 | defer fe.Close() 84 | fi, err := fe.Stat() 85 | if err != nil { 86 | log.Fatal(err) 87 | } 88 | return fi.Size() 89 | } 90 | 91 | // Md5 returns md5 hash 92 | func (v *Asset) Md5() (string, error) { 93 | fe, err := os.Open(v.path) 94 | if err != nil { 95 | return "", err 96 | } 97 | fi, err := fe.Stat() 98 | if err != nil { 99 | log.Fatal(err) 100 | } 101 | ch := make(chan string, 1) 102 | go func() { 103 | defer fe.Close() 104 | b := make([]byte, fi.Size()) 105 | fe.Read(b) 106 | sum := md5.New() 107 | sum.Write(b) 108 | ch <- hex.EncodeToString(sum.Sum(nil)) 109 | }() 110 | return <-ch, nil 111 | } 112 | -------------------------------------------------------------------------------- /binary.go: -------------------------------------------------------------------------------- 1 | package binary 2 | 3 | // CONST 4 | const ( 5 | BYTE = 1.0 6 | KILOBYTE = 1024 * BYTE 7 | MEGABYTE = 1024 * KILOBYTE 8 | GIGABYTE = 1024 * MEGABYTE 9 | TERABYTE = 1024 * GIGABYTE 10 | ) 11 | -------------------------------------------------------------------------------- /binary/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "log" 6 | 7 | "github.com/samuelngs/binary-go" 8 | ) 9 | 10 | var ( 11 | dir = flag.String("dir", "./", "the file or directory path") 12 | out = flag.String("out", "./", "the output directory path") 13 | pkg = flag.String("pkg", "binary", "the package name") 14 | max = flag.Int("max", int(20*binary.MEGABYTE), "the maximum size of each embedding binary (default: 20MB)") 15 | ) 16 | 17 | func main() { 18 | 19 | flag.Parse() 20 | 21 | c := binary.NewComposer(*dir, *out, *pkg, *max) 22 | 23 | if err := c.Scan(); err != nil { 24 | log.Fatal(err) 25 | } 26 | 27 | err := make(chan error, 1) 28 | go c.Compile(err) 29 | 30 | if e := <-err; e != nil { 31 | log.Fatal(err) 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /composer.go: -------------------------------------------------------------------------------- 1 | package binary 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "compress/gzip" 7 | "crypto/sha1" 8 | "encoding/hex" 9 | "fmt" 10 | "html/template" 11 | "log" 12 | "os" 13 | "path/filepath" 14 | "strconv" 15 | ) 16 | 17 | const lhex = "0123456789abcdef" 18 | 19 | // Delims 20 | const ( 21 | ldelim = "{{" 22 | rdelim = "}}" 23 | ) 24 | 25 | const utiltmpl = ` 26 | // buffer data 27 | type buffer struct { 28 | err error 29 | data []byte 30 | } 31 | 32 | // Bytes to retrieve file data 33 | func Bytes(filename string) ([]byte, error) { 34 | var r bytes.Buffer 35 | defer r.Truncate(0) 36 | parts, ok := data[filename] 37 | if !ok { 38 | return nil, errors.New("file does not exist") 39 | } 40 | ch := make(chan *buffer, 1) 41 | go func() { 42 | var zbuf bytes.Buffer 43 | defer zbuf.Truncate(0) 44 | for _, s := range parts { 45 | zbuf.Write([]byte(s)) 46 | } 47 | gz, err := gzip.NewReader(&zbuf) 48 | if err != nil { 49 | ch <- &buffer{ 50 | err: err, 51 | } 52 | return 53 | } 54 | var buf bytes.Buffer 55 | defer buf.Truncate(0) 56 | if _, err = io.Copy(&buf, gz); err != nil { 57 | ch <- &buffer{ 58 | err: err, 59 | } 60 | return 61 | } 62 | if err := gz.Close(); err != nil { 63 | ch <- &buffer{ 64 | err: err, 65 | } 66 | return 67 | } 68 | ch <- &buffer{ 69 | data: buf.Bytes(), 70 | } 71 | }() 72 | res := <-ch 73 | if err := res.err; err != nil { 74 | return nil, res.err 75 | } 76 | return res.data, nil 77 | } 78 | 79 | // MustBytes to read bytes data from file 80 | func MustBytes(filename string) []byte { 81 | b, err := Bytes(filename) 82 | if err != nil { 83 | log.Panic(err) 84 | } 85 | return b 86 | } 87 | ` 88 | 89 | const readtmpl = `// 90 | // code generated by binary-go. 91 | // 92 | // DO NOT EDIT! 93 | 94 | package {{ .Package }} 95 | 96 | import ( 97 | "bytes" 98 | "compress/gzip" 99 | "errors" 100 | "io" 101 | "log" 102 | ) 103 | 104 | var data = map[string][]string{ 105 | {{ range .Files -}}"{{ .Filepath }}": []string{ 106 | {{ range .Hashes -}} 107 | d{{ . }}, 108 | {{- end }} 109 | }, 110 | {{ end }} 111 | } 112 | ` 113 | 114 | // File template 115 | const filetmpl = `// 116 | // code generated by binary-go. 117 | // source: {{ .Filepath }} 118 | // 119 | // DO NOT EDIT! 120 | 121 | package {{ .Package }} 122 | 123 | var d{{ .Hash }} = "{{ .Data }}" 124 | 125 | ` 126 | 127 | // Reader struct 128 | type Reader struct { 129 | Package string 130 | Files []*File 131 | } 132 | 133 | // File struct 134 | type File struct { 135 | Filepath string 136 | Size int64 137 | Hashes []string 138 | } 139 | 140 | // Source struct 141 | type Source struct { 142 | Package, Filepath, Hash, Data string 143 | } 144 | 145 | // Template struct 146 | type Template struct { 147 | blk, src *template.Template 148 | } 149 | 150 | // Composer struct 151 | type Composer struct { 152 | dir, out, pkg string 153 | max int 154 | tree *Tree 155 | tmpl *Template 156 | } 157 | 158 | // NewComposer to create composer instance 159 | func NewComposer(dir, out, pkg string, max int) *Composer { 160 | tmpl := new(Template) 161 | switch b, err := template.New("blk").Delims(ldelim, rdelim).Parse(filetmpl); { 162 | case err != nil: 163 | log.Fatal(err) 164 | default: 165 | tmpl.blk = b 166 | } 167 | switch b, err := template.New("src").Delims(ldelim, rdelim).Parse(readtmpl); { 168 | case err != nil: 169 | log.Fatal(err) 170 | default: 171 | tmpl.src = b 172 | } 173 | return &Composer{ 174 | dir: dir, 175 | out: out, 176 | pkg: pkg, 177 | max: max, 178 | tmpl: tmpl, 179 | } 180 | } 181 | 182 | // Scan files and directories 183 | func (v *Composer) Scan() error { 184 | abs, err := filepath.Abs(v.dir) 185 | if err != nil { 186 | return err 187 | } 188 | f, err := os.Open(abs) 189 | if err != nil { 190 | return err 191 | } 192 | defer f.Close() 193 | tree := new(Tree) 194 | tree.name = filepath.Dir(abs) 195 | tree.path = abs 196 | tree.dirs = make([]*Tree, 0) 197 | tree.assets = make([]*Asset, 0) 198 | v.deep(tree, abs) 199 | v.tree = tree 200 | return nil 201 | } 202 | 203 | // Iter assets 204 | func (v *Composer) Iter() <-chan *Asset { 205 | return v.tree.Iter() 206 | } 207 | 208 | // path builds path string 209 | func (v *Composer) path(args ...string) string { 210 | var w bytes.Buffer 211 | for _, s := range args { 212 | w.WriteString(s) 213 | } 214 | return w.String() 215 | } 216 | 217 | // deep scan 218 | func (v *Composer) deep(root *Tree, path string) error { 219 | f, err := os.Open(path) 220 | if err != nil { 221 | return err 222 | } 223 | defer f.Close() 224 | s, err := f.Stat() 225 | if err != nil { 226 | return err 227 | } 228 | switch mode := s.Mode(); { 229 | case mode.IsDir(): 230 | di, err := f.Readdir(-1) 231 | if err != nil { 232 | return err 233 | } 234 | for _, fi := range di { 235 | abs := v.path(root.Pwd(), string(os.PathSeparator), fi.Name()) 236 | switch mode := fi.Mode(); { 237 | case mode.IsRegular(): 238 | root.assets = append( 239 | root.assets, 240 | NewAsset(abs, fi.Name()), 241 | ) 242 | case mode.IsDir(): 243 | tree := new(Tree) 244 | tree.name = filepath.Dir(abs) 245 | tree.path = abs 246 | tree.dirs = make([]*Tree, 0) 247 | tree.assets = make([]*Asset, 0) 248 | tree.parent = root 249 | root.dirs = append( 250 | root.dirs, 251 | tree, 252 | ) 253 | v.deep(tree, abs) 254 | } 255 | } 256 | case mode.IsRegular(): 257 | root.assets = append( 258 | root.assets, 259 | NewAsset(root.Pwd(), filepath.Base(f.Name())), 260 | ) 261 | } 262 | return nil 263 | } 264 | 265 | // write content to file 266 | func (v *Composer) write(b []byte, filepath ...string) error { 267 | fe, err := os.OpenFile( 268 | v.path(filepath...), 269 | os.O_TRUNC|os.O_CREATE|os.O_RDWR, 270 | os.ModeAppend|os.FileMode(0666), 271 | ) 272 | if err != nil { 273 | return err 274 | } 275 | defer fe.Close() 276 | fe.Write(b) 277 | return nil 278 | } 279 | 280 | // save part 281 | func (v *Composer) part(fname, fpath string, part int, b []byte) (string, error) { 282 | var tbuf bytes.Buffer 283 | defer tbuf.Truncate(0) 284 | sha1 := sha1.Sum(b) 285 | hash := hex.EncodeToString(sha1[:]) 286 | if err := v.tmpl.blk.Execute(&tbuf, &Source{ 287 | Package: v.pkg, 288 | Filepath: fpath, 289 | Hash: hash, 290 | Data: string(b[:]), 291 | }); err != nil { 292 | return hash, err 293 | } 294 | return hash, v.write(tbuf.Bytes(), v.out, string(os.PathSeparator), fname, "-", strconv.Itoa(part), ".go") 295 | } 296 | 297 | // process file 298 | func (v *Composer) process(fname, fpath string, data []byte) ([]string, error) { 299 | var bbuf bytes.Buffer 300 | var hashes []string 301 | var n int 302 | for i, b := range data { 303 | if i > 0 && bbuf.Len() > v.max { 304 | hash, err := v.part(fname, fpath, n+1, bbuf.Bytes()) 305 | if err != nil { 306 | return nil, err 307 | } 308 | hashes = append(hashes, hash) 309 | fmt.Printf("│ %2.2d │ %11d KB │ %40.40v │ %-24.24v │\n", n+1, bbuf.Len(), hash, v.path(fname, "-", strconv.Itoa(n+1), ".go")) 310 | bbuf.Truncate(0) 311 | n++ 312 | } 313 | buf := []byte(`\x00`) 314 | buf[2] = lhex[b/16] 315 | buf[3] = lhex[b%16] 316 | bbuf.Write(buf) 317 | } 318 | hash, err := v.part(fname, fpath, n+1, bbuf.Bytes()) 319 | if err != nil { 320 | return nil, err 321 | } 322 | hashes = append(hashes, hash) 323 | fmt.Printf("│ %2.2d │ %11d KB │ %40.40v │ %-24.24v │\n", n+1, bbuf.Len(), hash, v.path(fname, "-", strconv.Itoa(n+1), ".go")) 324 | bbuf.Truncate(0) 325 | return hashes, nil 326 | } 327 | 328 | // Compile template 329 | func (v *Composer) Compile(ch chan error) { 330 | go func() { 331 | ch <- v.Compose() 332 | }() 333 | } 334 | 335 | // Compose template 336 | func (v *Composer) Compose() error { 337 | var files []*File 338 | fmt.Printf("┌───────┬────────────────┬──────────────────────────────────────────┬──────────────────────────┐\n") 339 | fmt.Printf("│ %5v │ %14v │ %-40.40v │ %-24.24v │\n", "PART", "SIZE", "HASH", "FILE") 340 | fmt.Printf("├───────┼────────────────┼──────────────────────────────────────────┼──────────────────────────┤\n") 341 | for asset := range v.tree.Iter() { 342 | var zbuf bytes.Buffer 343 | defer zbuf.Truncate(0) 344 | zw := gzip.NewWriter(&zbuf) 345 | fw := bufio.NewWriter(zw) 346 | for b := range asset.Pipe() { 347 | fw.Write(b) 348 | } 349 | if err := fw.Flush(); err != nil { 350 | return err 351 | } 352 | if err := zw.Close(); err != nil { 353 | return err 354 | } 355 | hashes, err := v.process(asset.Filename(), asset.Relpath(), zbuf.Bytes()) 356 | if err != nil { 357 | return err 358 | } 359 | file := &File{ 360 | Filepath: asset.Relpath(), 361 | Size: asset.Size(), 362 | Hashes: hashes, 363 | } 364 | files = append(files, file) 365 | } 366 | fmt.Printf("└───────┴────────────────┴──────────────────────────────────────────┴──────────────────────────┘\n") 367 | var w bytes.Buffer 368 | defer w.Truncate(0) 369 | if err := v.tmpl.src.Execute(&w, &Reader{ 370 | Package: v.pkg, 371 | Files: files, 372 | }); err != nil { 373 | return err 374 | } 375 | w.WriteString(utiltmpl) 376 | if err := v.write( 377 | w.Bytes(), 378 | v.path(v.out, string(os.PathSeparator), "binary.go"), 379 | ); err != nil { 380 | return err 381 | } 382 | return nil 383 | } 384 | -------------------------------------------------------------------------------- /reader.go: -------------------------------------------------------------------------------- 1 | package binary 2 | 3 | import ( 4 | "bytes" 5 | "compress/gzip" 6 | "errors" 7 | "io" 8 | "log" 9 | ) 10 | 11 | var data map[string][]string 12 | var size map[string]int64 13 | 14 | // buffer data 15 | type buffer struct { 16 | err error 17 | data []byte 18 | } 19 | 20 | // Bytes to retrieve file data 21 | func Bytes(filename string) ([]byte, error) { 22 | var r bytes.Buffer 23 | defer r.Truncate(0) 24 | parts, ok := data[filename] 25 | if !ok { 26 | return nil, errors.New("file does not exist") 27 | } 28 | ch := make(chan *buffer, 1) 29 | go func() { 30 | var zbuf bytes.Buffer 31 | defer zbuf.Truncate(0) 32 | for _, s := range parts { 33 | zbuf.Write([]byte(s)) 34 | } 35 | gz, err := gzip.NewReader(&zbuf) 36 | if err != nil { 37 | ch <- &buffer{ 38 | err: err, 39 | } 40 | return 41 | } 42 | var buf bytes.Buffer 43 | defer buf.Truncate(0) 44 | if _, err = io.Copy(&buf, gz); err != nil { 45 | ch <- &buffer{ 46 | err: err, 47 | } 48 | return 49 | } 50 | if err := gz.Close(); err != nil { 51 | ch <- &buffer{ 52 | err: err, 53 | } 54 | return 55 | } 56 | ch <- &buffer{ 57 | data: buf.Bytes(), 58 | } 59 | }() 60 | res := <-ch 61 | if err := res.err; err != nil { 62 | return nil, res.err 63 | } 64 | return res.data, nil 65 | } 66 | 67 | // MustBytes to read bytes data from file 68 | func MustBytes(filename string) []byte { 69 | b, err := Bytes(filename) 70 | if err != nil { 71 | log.Panic(err) 72 | } 73 | return b 74 | } 75 | -------------------------------------------------------------------------------- /tree.go: -------------------------------------------------------------------------------- 1 | package binary 2 | 3 | // Tree holds directory information 4 | type Tree struct { 5 | name string 6 | path string 7 | parent *Tree 8 | dirs []*Tree 9 | assets []*Asset 10 | } 11 | 12 | // NewTree creates directory tree info 13 | func NewTree(name, path string, parent *Tree, dirs []*Tree, assets []*Asset) *Tree { 14 | return &Tree{name, path, parent, dirs, assets} 15 | } 16 | 17 | // Pwd returns the full path of the directory 18 | func (v *Tree) Pwd() string { 19 | return v.path 20 | } 21 | 22 | // Name returns directory name 23 | func (v *Tree) Name() string { 24 | return v.name 25 | } 26 | 27 | // Dirs returns sub-directories 28 | func (v *Tree) Dirs() []string { 29 | l := make([]string, len(v.assets)) 30 | for i, d := range v.dirs { 31 | l[i] = d.Pwd() 32 | } 33 | return l 34 | } 35 | 36 | // Files returns asserts 37 | func (v *Tree) Files() []string { 38 | l := make([]string, len(v.assets)) 39 | for i, a := range v.assets { 40 | l[i] = a.Filepath() 41 | } 42 | return l 43 | } 44 | 45 | // Ls to list files and folders 46 | func (v *Tree) Ls() []string { 47 | d := v.Dirs() 48 | f := v.Files() 49 | l := make([]string, len(d)+len(f)) 50 | offset := len(d) 51 | for i, p := range d { 52 | l[i] = p 53 | } 54 | for i, p := range f { 55 | l[offset+i] = p 56 | } 57 | return l 58 | } 59 | 60 | // Dir to retrieve dir tree 61 | func (v *Tree) Dir(s string) *Tree { 62 | for _, dir := range v.dirs { 63 | if dir.Name() == s { 64 | return dir 65 | } 66 | } 67 | return nil 68 | } 69 | 70 | // File to retrieve asset 71 | func (v *Tree) File(s string) *Asset { 72 | for _, asset := range v.assets { 73 | if asset.Filename() == s { 74 | return asset 75 | } 76 | } 77 | return nil 78 | } 79 | 80 | // Iter for-loop read assets 81 | func (v *Tree) Iter() <-chan *Asset { 82 | ch := make(chan *Asset) 83 | go func() { 84 | defer close(ch) 85 | for _, dir := range v.dirs { 86 | for _, asset := range dir.assets { 87 | ch <- asset 88 | } 89 | } 90 | for _, asset := range v.assets { 91 | ch <- asset 92 | } 93 | }() 94 | return ch 95 | } 96 | --------------------------------------------------------------------------------