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:
jim teeuwen 2012-06-13 14:17:25 +02:00
parent ed2cd8b81c
commit 37af2a4b27
7 changed files with 2887 additions and 3767 deletions

View File

@ -1,57 +1,37 @@
## bindata
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
converted to a raw byte slice.
binary data into a go program. The file data is optionally gzip compressed
before being converted to a raw byte slice.
### Usage
The simplest invocation is to pass it only the input file name.
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 package name specified. Using 'main'.
[w] No function name specified. Using 'gophercolor_png'.
[i] Done.
This creates the "testdata/gophercolor.png.go" file which has a package
declaration with name 'main' and one function named 'gophercolor_png'.
It looks like this:
This creates the `testdata/gophercolor.png.go` file which has a package
declaration with name `main` a variable holding the file data in a read-only
string and one function named `gophercolor_png` with the following signature:
// gophercolor_png returns the decompressed binary data.
// 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()
}
func gophercolor_png() []byte
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
if something went wrong during decompression. This makes any faults appearant
during initialization of your program, so it can quickly be fixed. Additionally,
this approach allows us to assign the decompressed file data to global
variables where necessary.
See the testdata directory for example input and output.
`gophercolor_png()` to get the uncompressed image data. The function panics
if something went wrong during decompression. See the testdata directory for
example input and output.
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
the desired function name, as it can not be inferred from the input data.
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.
@ -59,12 +39,12 @@ Invoke the program with the -h flag for more options.
### Optional compression
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
we no longer need a function that decompresses the data. The resource's raw
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
compression, or the supplied resource is already compressed. Doing it again
would not add any value and may even increase the size of the data.
before being turned into Go code. The data should still be accessed through
a function call, so nothing changes in the usage of the generated file.
This feature is useful if you do not care for compression, or the supplied
resource is already compressed. Doing it again would not add any value and may
even increase the size of the data.
The default behaviour of the program is to use compression.

View File

@ -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()
}`)
}

View File

@ -9,7 +9,7 @@ import (
"io"
)
var newline = []byte{'\n'}
var line = []byte("\"+\n\"")
type GoWriter struct {
io.Writer
@ -22,12 +22,12 @@ func (w *GoWriter) Write(p []byte) (n int, err error) {
}
for n = range p {
if w.c%12 == 0 {
w.Writer.Write(newline)
if w.c%16 == 0 {
w.Writer.Write(line)
w.c = 0
}
fmt.Fprintf(w.Writer, "0x%02x,", p[n])
fmt.Fprintf(w.Writer, "\\x%02x", p[n])
w.c++
}

16
main.go
View File

@ -16,7 +16,7 @@ import (
const (
AppName = "bindata"
AppVersion = "0.7"
AppVersion = "0.8"
)
func main() {
@ -95,12 +95,7 @@ func main() {
// Read the input file, transform it into a gzip compressed data stream and
// write it out as a go source file.
if pipe {
if *uncompressed {
translate_uncompressed(os.Stdin, os.Stdout, *pkgname, *funcname)
} else {
translate_compressed(os.Stdin, os.Stdout, *pkgname, *funcname)
}
translate(os.Stdin, os.Stdout, *pkgname, *funcname, *uncompressed)
fmt.Fprintln(os.Stdout, "[i] Done.")
return
}
@ -122,11 +117,6 @@ func main() {
defer fd.Close()
if *uncompressed {
translate_uncompressed(fs, fd, *pkgname, *funcname)
} else {
translate_compressed(fs, fd, *pkgname, *funcname)
}
translate(fs, fd, *pkgname, *funcname, *uncompressed)
fmt.Fprintln(os.Stdout, "[i] Done.")
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

88
translate.go Normal file
View 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}")
}