Fix generated code for both compressed and uncompressed modes. We now no longer require a memcpy to read the static file data and can get it directly from the .rodata section. This addresses issue #1
This commit is contained in:
parent
ed2cd8b81c
commit
37af2a4b27
54
README.md
54
README.md
|
@ -1,57 +1,37 @@
|
||||||
## bindata
|
## bindata
|
||||||
|
|
||||||
This tool converts any file into managable Go source code. Useful for embedding
|
This tool converts any file into managable Go source code. Useful for embedding
|
||||||
binary data into a go program. The file data is gzip compressed before being
|
binary data into a go program. The file data is optionally gzip compressed
|
||||||
converted to a raw byte slice.
|
before being converted to a raw byte slice.
|
||||||
|
|
||||||
### Usage
|
### Usage
|
||||||
|
|
||||||
The simplest invocation is to pass it only the input file name.
|
The simplest invocation is to pass it only the input file name.
|
||||||
The output file and code settings are inferred from this automatically.
|
The output file and code settings are inferred from this automatically.
|
||||||
|
|
||||||
$ bindata -i testdata/gophercolor.png
|
$ go-bindata -i testdata/gophercolor.png
|
||||||
[w] No output file specified. Using 'testdata/gophercolor.png.go'.
|
[w] No output file specified. Using 'testdata/gophercolor.png.go'.
|
||||||
[w] No package name specified. Using 'main'.
|
[w] No package name specified. Using 'main'.
|
||||||
[w] No function name specified. Using 'gophercolor_png'.
|
[w] No function name specified. Using 'gophercolor_png'.
|
||||||
[i] Done.
|
[i] Done.
|
||||||
|
|
||||||
This creates the "testdata/gophercolor.png.go" file which has a package
|
This creates the `testdata/gophercolor.png.go` file which has a package
|
||||||
declaration with name 'main' and one function named 'gophercolor_png'.
|
declaration with name `main` a variable holding the file data in a read-only
|
||||||
It looks like this:
|
string and one function named `gophercolor_png` with the following signature:
|
||||||
|
|
||||||
// gophercolor_png returns the decompressed binary data.
|
func gophercolor_png() []byte
|
||||||
// It panics if an error occurred.
|
|
||||||
func gophercolor_png() []byte {
|
|
||||||
gz, err := gzip.NewReader(bytes.NewBuffer([]byte{
|
|
||||||
...
|
|
||||||
}))
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
panic("Decompression failed: " + err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
var b bytes.Buffer
|
|
||||||
io.Copy(&b, gz)
|
|
||||||
gz.Close()
|
|
||||||
|
|
||||||
return b.Bytes()
|
|
||||||
}
|
|
||||||
|
|
||||||
You can now simply include the new .go file in your program and call
|
You can now simply include the new .go file in your program and call
|
||||||
gophercolor_png() to get the uncompressed image data. The function panics
|
`gophercolor_png()` to get the uncompressed image data. The function panics
|
||||||
if something went wrong during decompression. This makes any faults appearant
|
if something went wrong during decompression. See the testdata directory for
|
||||||
during initialization of your program, so it can quickly be fixed. Additionally,
|
example input and output.
|
||||||
this approach allows us to assign the decompressed file data to global
|
|
||||||
variables where necessary.
|
|
||||||
|
|
||||||
See the testdata directory for example input and output.
|
|
||||||
|
|
||||||
Aternatively, you can pipe the input file data into stdin. bindata will then
|
Aternatively, you can pipe the input file data into stdin. bindata will then
|
||||||
spit out the generated Go code to stdout. This does require explicitly naming
|
spit out the generated Go code to stdout. This does require explicitly naming
|
||||||
the desired function name, as it can not be inferred from the input data.
|
the desired function name, as it can not be inferred from the input data.
|
||||||
The package name will still default to 'main'.
|
The package name will still default to 'main'.
|
||||||
|
|
||||||
$ cat testdata/gophercolor.png | ./bindata -f gophercolor_png | gofmt
|
$ cat testdata/gophercolor.png | go-bindata -f gophercolor_png | gofmt
|
||||||
|
|
||||||
Invoke the program with the -h flag for more options.
|
Invoke the program with the -h flag for more options.
|
||||||
|
|
||||||
|
@ -59,12 +39,12 @@ Invoke the program with the -h flag for more options.
|
||||||
### Optional compression
|
### Optional compression
|
||||||
|
|
||||||
When the `-u` flag is given, the supplied resource is *not* GZIP compressed
|
When the `-u` flag is given, the supplied resource is *not* GZIP compressed
|
||||||
before being turned into Go code. This also alters the generated output in that
|
before being turned into Go code. The data should still be accessed through
|
||||||
we no longer need a function that decompresses the data. The resource's raw
|
a function call, so nothing changes in the usage of the generated file.
|
||||||
byte data is simply assigned to a global variable of the same name as the
|
|
||||||
function would otherwise get. This feature is useful if you do not care for
|
This feature is useful if you do not care for compression, or the supplied
|
||||||
compression, or the supplied resource is already compressed. Doing it again
|
resource is already compressed. Doing it again would not add any value and may
|
||||||
would not add any value and may even increase the size of the data.
|
even increase the size of the data.
|
||||||
|
|
||||||
The default behaviour of the program is to use compression.
|
The default behaviour of the program is to use compression.
|
||||||
|
|
||||||
|
|
54
bindata.go
54
bindata.go
|
@ -1,54 +0,0 @@
|
||||||
// This work is subject to the CC0 1.0 Universal (CC0 1.0) Public Domain Dedication
|
|
||||||
// license. Its contents can be found at:
|
|
||||||
// http://creativecommons.org/publicdomain/zero/1.0/
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"compress/gzip"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Translate the input file without GZIP compression.
|
|
||||||
// input -> gowriter -> output.
|
|
||||||
func translate_uncompressed(input io.Reader, output io.Writer, pkgname, funcname string) {
|
|
||||||
fmt.Fprintf(output, "package %s\n\nvar %s []byte = []byte{", pkgname, funcname)
|
|
||||||
io.Copy(&GoWriter{Writer: output}, input)
|
|
||||||
fmt.Fprint(output, "\n}")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Translate the input file with GZIP compression.
|
|
||||||
// input -> gzip -> gowriter -> output.
|
|
||||||
func translate_compressed(input io.Reader, output io.Writer, pkgname, funcname string) {
|
|
||||||
fmt.Fprintf(output, `package %s
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"compress/gzip"
|
|
||||||
"io"
|
|
||||||
)
|
|
||||||
|
|
||||||
// %s returns the decompressed binary data.
|
|
||||||
// It panics if an error occurred.
|
|
||||||
func %s() []byte {
|
|
||||||
gz, err := gzip.NewReader(bytes.NewBuffer([]byte{`, pkgname, funcname, funcname)
|
|
||||||
|
|
||||||
gz := gzip.NewWriter(&GoWriter{Writer: output})
|
|
||||||
io.Copy(gz, input)
|
|
||||||
gz.Close()
|
|
||||||
|
|
||||||
fmt.Fprint(output, `
|
|
||||||
}))
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
panic("Decompression failed: " + err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
var b bytes.Buffer
|
|
||||||
io.Copy(&b, gz)
|
|
||||||
gz.Close()
|
|
||||||
|
|
||||||
return b.Bytes()
|
|
||||||
}`)
|
|
||||||
}
|
|
|
@ -9,7 +9,7 @@ import (
|
||||||
"io"
|
"io"
|
||||||
)
|
)
|
||||||
|
|
||||||
var newline = []byte{'\n'}
|
var line = []byte("\"+\n\"")
|
||||||
|
|
||||||
type GoWriter struct {
|
type GoWriter struct {
|
||||||
io.Writer
|
io.Writer
|
||||||
|
@ -22,12 +22,12 @@ func (w *GoWriter) Write(p []byte) (n int, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for n = range p {
|
for n = range p {
|
||||||
if w.c%12 == 0 {
|
if w.c%16 == 0 {
|
||||||
w.Writer.Write(newline)
|
w.Writer.Write(line)
|
||||||
w.c = 0
|
w.c = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Fprintf(w.Writer, "0x%02x,", p[n])
|
fmt.Fprintf(w.Writer, "\\x%02x", p[n])
|
||||||
w.c++
|
w.c++
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
16
main.go
16
main.go
|
@ -16,7 +16,7 @@ import (
|
||||||
|
|
||||||
const (
|
const (
|
||||||
AppName = "bindata"
|
AppName = "bindata"
|
||||||
AppVersion = "0.7"
|
AppVersion = "0.8"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
@ -95,12 +95,7 @@ func main() {
|
||||||
// Read the input file, transform it into a gzip compressed data stream and
|
// Read the input file, transform it into a gzip compressed data stream and
|
||||||
// write it out as a go source file.
|
// write it out as a go source file.
|
||||||
if pipe {
|
if pipe {
|
||||||
if *uncompressed {
|
translate(os.Stdin, os.Stdout, *pkgname, *funcname, *uncompressed)
|
||||||
translate_uncompressed(os.Stdin, os.Stdout, *pkgname, *funcname)
|
|
||||||
} else {
|
|
||||||
translate_compressed(os.Stdin, os.Stdout, *pkgname, *funcname)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Fprintln(os.Stdout, "[i] Done.")
|
fmt.Fprintln(os.Stdout, "[i] Done.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -122,11 +117,6 @@ func main() {
|
||||||
|
|
||||||
defer fd.Close()
|
defer fd.Close()
|
||||||
|
|
||||||
if *uncompressed {
|
translate(fs, fd, *pkgname, *funcname, *uncompressed)
|
||||||
translate_uncompressed(fs, fd, *pkgname, *funcname)
|
|
||||||
} else {
|
|
||||||
translate_compressed(fs, fd, *pkgname, *funcname)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Fprintln(os.Stdout, "[i] Done.")
|
fmt.Fprintln(os.Stdout, "[i] Done.")
|
||||||
}
|
}
|
||||||
|
|
3222
testdata/gophercolor.png-compressed.go
vendored
3222
testdata/gophercolor.png-compressed.go
vendored
File diff suppressed because it is too large
Load Diff
3212
testdata/gophercolor.png-uncompressed.go
vendored
3212
testdata/gophercolor.png-uncompressed.go
vendored
File diff suppressed because it is too large
Load Diff
88
translate.go
Normal file
88
translate.go
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
// This work is subject to the CC0 1.0 Universal (CC0 1.0) Public Domain Dedication
|
||||||
|
// license. Its contents can be found at:
|
||||||
|
// http://creativecommons.org/publicdomain/zero/1.0/
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"compress/gzip"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Translate the input file with optional GZIP compression.
|
||||||
|
// input [-> gzip] -> gowriter -> output.
|
||||||
|
func translate(input io.Reader, output io.Writer, pkgname, funcname string, uncompressed bool) {
|
||||||
|
fmt.Fprintf(output, `package %s
|
||||||
|
|
||||||
|
import (`, pkgname)
|
||||||
|
|
||||||
|
if uncompressed {
|
||||||
|
fmt.Fprint(output, `
|
||||||
|
"reflect"
|
||||||
|
"unsafe"`)
|
||||||
|
} else {
|
||||||
|
fmt.Fprint(output, `
|
||||||
|
"bytes"
|
||||||
|
"compress/gzip"
|
||||||
|
"io"
|
||||||
|
"reflect"
|
||||||
|
"unsafe"`)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintf(output, `
|
||||||
|
)
|
||||||
|
|
||||||
|
var _%s = "`, funcname)
|
||||||
|
|
||||||
|
if uncompressed {
|
||||||
|
io.Copy(&GoWriter{Writer: output}, input)
|
||||||
|
} else {
|
||||||
|
gz := gzip.NewWriter(&GoWriter{Writer: output})
|
||||||
|
io.Copy(gz, input)
|
||||||
|
gz.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintf(output, `"
|
||||||
|
|
||||||
|
// %s returns the binary data for a given file.
|
||||||
|
func %s() []byte {`, funcname, funcname)
|
||||||
|
|
||||||
|
if uncompressed {
|
||||||
|
fmt.Fprintf(output, `
|
||||||
|
var empty [0]byte
|
||||||
|
sx := (*reflect.StringHeader)(unsafe.Pointer(&_%s))
|
||||||
|
b := empty[:]
|
||||||
|
bx := (*reflect.SliceHeader)(unsafe.Pointer(&b))
|
||||||
|
bx.Data = sx.Data
|
||||||
|
bx.Len = len(_%s)
|
||||||
|
bx.Cap = bx.Len
|
||||||
|
return b`, funcname, funcname)
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(output, `
|
||||||
|
// This bit of black magic ensures we do not get
|
||||||
|
// unneccesary memcpy's and can read directly from
|
||||||
|
// the .rodata section.
|
||||||
|
var empty [0]byte
|
||||||
|
sx := (*reflect.StringHeader)(unsafe.Pointer(&_%s))
|
||||||
|
b := empty[:]
|
||||||
|
bx := (*reflect.SliceHeader)(unsafe.Pointer(&b))
|
||||||
|
bx.Data = sx.Data
|
||||||
|
bx.Len = len(_%s)
|
||||||
|
bx.Cap = bx.Len
|
||||||
|
|
||||||
|
gz, err := gzip.NewReader(bytes.NewBuffer(b))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
panic("Decompression failed: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
io.Copy(&buf, gz)
|
||||||
|
gz.Close()
|
||||||
|
|
||||||
|
return buf.Bytes()`, funcname, funcname)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintf(output, "\n}")
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user