Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

proposal: cmd/go: support embedding static assets (files) in binaries #35950

Open
bradfitz opened this issue Dec 3, 2019 · 155 comments
Open

proposal: cmd/go: support embedding static assets (files) in binaries #35950

bradfitz opened this issue Dec 3, 2019 · 155 comments

Comments

@bradfitz
Copy link
Contributor

@bradfitz bradfitz commented Dec 3, 2019

There are many tools to embed static asset files into binaries:

Actually, https://tech.townsourced.com/post/embedding-static-files-in-go/ lists more:

Proposal

I think it's time to do this well once & reduce duplication, adding official support for embedding file resources into the cmd/go tool.

Problems with the current situation:

  • There are too many tools
  • Using a go:generate-based solution bloats the git history with a second (and slightly larger) copy of each file.
  • Not using go:generate means not being go install-able or making people write their own Makefiles, etc.

Goals:

  • don't check in generated files
  • don't generate *.go files at all (at least not in user's workspace)
  • make go install / go build do the embedding automatically
  • let user choose per file/glob which type of access is needed (e.g. []byte, func() io.Reader, io.ReaderAt, etc)
  • Maybe store assets compressed in the binary where appropriate (e.g. if user only needs an io.Reader)? (edit: but probably not; see comments below)
  • No code execution at compilation time; that is a long-standing Go policy. go build or go install can not run arbitrary code, just like go:generate doesn't run automatically at install time.

The two main implementation approaches are //go:embed Logo logo.jpg or a well-known package (var Logo = embed.File("logo.jpg")).

go:embed approach

For a go:embed approach, one might say that any go/build-selected *.go file can contain something like:

//go:embed Logo logo.jpg

Which, say, compiles to:

func Logo() *io.SectionReader

(adding a dependency to the io package)

Or:

//go:embedglob Assets assets/*.css assets/*.js

compiling to, say:

var Assets interface{
     Files() []string
     Open func(name string) *io.SectionReader
} = runtime.EmbedAsset(123)

Obviously this isn't fully fleshed out. There'd need to be something for compressed files too that yield only an io.Reader.

embed package approach

The other high-level approach is to not have a magic //go:embed syntax and instead just let users write Go code in some new "embed" or "golang.org/x/foo/embed" package:

var Static = embed.Dir("static")
var Logo = embed.File("images/logo.jpg")
var Words = embed.CompressedReader("dict/words")

Then have cmd/go recognize the calls to embed.Foo("foo/*.js") etc and glob do the work in cmd/go, rather than at runtime. Or maybe certain build tags or flags could make it fall back to doing things at runtime instead. Perkeep (linked above) has such a mode, which is nice to speed up incremental development where you don't care about linking one big binary.

Concerns

  • Pick a style (//go:embed* vs a magic package).
  • Block certain files?
    • Probably block embedding ../../../../../../../../../../etc/shadow
    • Maybe block reaching into .git too
@gopherbot gopherbot added this to the Proposal milestone Dec 3, 2019
@gopherbot gopherbot added the Proposal label Dec 3, 2019
@ianlancetaylor

This comment has been minimized.

Copy link
Contributor

@ianlancetaylor ianlancetaylor commented Dec 4, 2019

It's worth considering whether embedglob should support a complete file tree, perhaps using the ** syntax supported by some Unix shells.

@opennota

This comment has been minimized.

Copy link

@opennota opennota commented Dec 4, 2019

Some people would need the ability to serve the embedded assets with HTTP using the http.FileServer.

I personally use either mjibson/esc (which does that) or in some cases my own file embedding implementation which renames files to create unique paths and adds a map from the original paths to the new ones, e.g. "/js/bootstrap.min.js": "/js/bootstrap.min.827ccb0eea8a706c4c34a16891f84e7b.js". Then you can use this map in the templates like this: href="{{ static_path "/css/bootstrap.min.css" }}".

@cespare

This comment has been minimized.

Copy link
Contributor

@cespare cespare commented Dec 4, 2019

I think a consequence of this would be that it would be nontrivial to figure out what files are necessary to build a program.

The //go:embed approach introduces another level of complexity too. You'd have to parse the magic comments in order to even typecheck the code. The "embed package" approach seems friendlier to static analysis.

(Just musing out loud here.)

@bradfitz

This comment has been minimized.

Copy link
Contributor Author

@bradfitz bradfitz commented Dec 4, 2019

@opennota,

would need the ability to serve the embedded assets with HTTP using the http.FileServer.

Yes, the first link above is a package I wrote (in 2011, before Go 1) and still use, and it supports using http.FileServer: https://godoc.org/perkeep.org/pkg/fileembed#Files.Open

@bradfitz

This comment has been minimized.

Copy link
Contributor Author

@bradfitz bradfitz commented Dec 4, 2019

@cespare,

The //go:embed approach introduces another level of complexity too. You'd have to parse the magic comments in order to even typecheck the code. The "embed package" approach seems friendlier to static analysis.

Yes, good point. That's a very strong argument for using a package. It also makes it more readable & documentable, since we can document it all with regular godoc, rather than deep in cmd/go's docs.

@agnivade

This comment has been minimized.

Copy link
Contributor

@agnivade agnivade commented Dec 4, 2019

@bradfitz - Do you want to close this #3035 ?

@bradfitz

This comment has been minimized.

Copy link
Contributor Author

@bradfitz bradfitz commented Dec 4, 2019

@agnivade, thanks for finding that! I thought I remembered that but couldn't find it. Let's leave it open for now and see what others think.

@balasanjay

This comment has been minimized.

Copy link
Contributor

@balasanjay balasanjay commented Dec 4, 2019

If we go with the magic package, we could use the unexported type trick to ensure that callers pass compile-time constants as arguments: https://play.golang.org/p/RtHlKjhXcda.

(This is the strategy referenced here: https://groups.google.com/forum/#!topic/golang-nuts/RDA9Hag8RZw/discussion)

@AlexRouSg

This comment has been minimized.

Copy link
Contributor

@AlexRouSg AlexRouSg commented Dec 4, 2019

One concern I have is how would it hanle invividual or all assets being too big to fit into memory and whether there would be maybe a build tag or per file access option to choose between pritorizing access time vs memory footprint or some middle ground implementation.

@urandom

This comment has been minimized.

Copy link

@urandom urandom commented Dec 4, 2019

the way i've solved that problem (because of course i also have my own implementation :) ) is to provide an http.FileSystem implementation that serves all embedded assets. That way, you don't to rely on magic comments in order to appease the typechecker, the assets can easily be served by http, a fallback implementation can be provided for development purposes (http.Dir) without changing the code, and the final implementation is quite versatile, as http.FileSystem covers quite a bit, not only in reading files, but listing directories as well.

One can still use magic comments or whatever to specify what needs to be embedded, though its probably easier to specify all the globs via a plain text file.

@ianlancetaylor

This comment has been minimized.

Copy link
Contributor

@ianlancetaylor ianlancetaylor commented Dec 4, 2019

@AlexRouSg This proposal would only be for files which are appropriate to include directly in the final executable. It would not be appropriate to use this for files that are too big to fit in memory. There's no reason to complicate this tool to handle that case; for that case, just don't use this tool.

@bradfitz

This comment has been minimized.

Copy link
Contributor Author

@bradfitz bradfitz commented Dec 4, 2019

@ianlancetaylor, I think the distinction @AlexRouSg was making was between having the files provided as global []bytes (unpageable, potentially writable memory) vs providing a read-only, on-demand view of an ELF section that can normally live on disk (in the executable), like via an Open call that returns an *io.SectionReader. (I don't want to bake in http.File or http.FileSystem into cmd/go or runtime... net/http can provide an adapter.)

@urandom

This comment has been minimized.

Copy link

@urandom urandom commented Dec 4, 2019

@bradfitz both http.File itself is an interface with no technical dependencies to the http package. It might be a good idea for any Open method to provide an implementation that conforms to that interface, because both the Stat and Readdir methods are quite useful for such assets

@bradfitz

This comment has been minimized.

Copy link
Contributor Author

@bradfitz bradfitz commented Dec 4, 2019

@urandom, it couldn't implement http.FileSystem, though, without referring to the "http.File" name (https://play.golang.org/p/-r3KjG1Gp-8).

@rsc

This comment has been minimized.

Copy link
Contributor

@rsc rsc commented Dec 4, 2019

@robpike and I talked through a proposal for doing this years ago (before there was a proposal process) and never got back to doing anything. It's been bugging me for years that we never finished doing that. The idea as I remember it was to just have a special directory name like "static" containing the static data and automatically make them available through an API, with no annotations needed.

I'm not convinced about the complexity of a "compressed vs not" knob. If we do that, then people will want us to add control over which compression, compression level, and so on. All we should need to add is the ability to embed a file of plain bytes. If users want to store compressed data in that file, great, the details are up to them and there's no API needed on Go's side at all.

@rsc rsc added this to Incoming in Proposals Dec 4, 2019
@jayconrod

This comment has been minimized.

Copy link
Contributor

@jayconrod jayconrod commented Dec 4, 2019

A couple thoughts:

  • It should not be possible to embed any file outside the module doing the embedding. We need to make sure files are part of module zip files when we create them, so that also means no symbolic links, case conflicts, etc. We can't change the algorithm that produces zip files without breaking sums.
  • I think it's simpler to restrict embedding to be in the same directory (if //go:embed comments are used) or a specific subdirectory (if static is used). This makes it a lot easier to understand the relationship between packages and embedded files.

Either way, this blocks embedding /etc/shadow or .git. Neither can be included in a module zip.

In general, I'm worried about expanding the scope of the go command too much. However, the fact that there are so many solutions to this problem means there probably ought to be one official solution.

I'm familiar with go_embed_data and go-bindata (of which there are several forks), and this seems to cover those use cases. Are there any important problems the others solve that this doesn't cover?

@DeedleFake

This comment has been minimized.

Copy link

@DeedleFake DeedleFake commented Dec 4, 2019

Blocking certain files shouldn't be too hard, especially if you use a static or embed directory. Symlinks might complicate that a bit, but you can just prevent it from embedding anything outside of the current module or, if you're on GOPATH, outside of the package containing the directory.

I'm not particularly a fan of a comment that compiles to code, but I also find the pseudo-package that affects compilation to be a bit strange as well. If the directory approach isn't used, maybe it might make a bit more sense to have some kind embed top-level declaration actually built into the language. It would work similarly to import, but would only support local paths and would require a name for it to be assigned to. For example,

embed ui "./ui/build"

func main() {
  file, err := ui.Open("version.txt")
  if err != nil {
    panic(err)
  }
  version, err = ioutil.ReadAll(file)
  if err != nil {
    panic(err)
  }
  file.Close()

  log.Printf("UI Version: %s\n", bytes.TrimSpace(version))
  http.ListenAndServe(":8080", http.EmbeddedDir(ui))
}

Edit: You beat me to it, @jayconrod.

@josharian

This comment has been minimized.

Copy link
Contributor

@josharian josharian commented Dec 4, 2019

To expand on #35950 (comment), there is a puzzle about the exposed API. The obvious ways to expose the data are []byte, string, and Read-ish interfaces.

The typical case is that you want the embedded data to be immutable. However, all interfaces exposing []byte (which includes io.Reader, io.SectionReader, etc.) must either (1) make a copy, (2) allow mutability, or (3) be immutable despite being a []byte. Exposing the data as strings solves that, but at the cost of an API that will often end up requiring copying anyway, since lots of code that consumes embedded files eventually requires byte slices one way or another.

I'd suggest route (3): be immutable despite being a []byte. You can enforce this cheaply by using a readonly symbol for the backing array. This also lets you safely expose the same data as a []byte and a string; attempts to mutate the data will fail. The compiler can't take advantage of the immutability, but that's not too great of a loss. This is something that toolchain support can bring to the table that (as far as I know) none of the existing codegen packages do.

(A third party codegen package could do this by generating a generic assembly file containing DATA symbols that are marked as readonly, and then short arch-specific assembly files exposing those symbols in the form of strings and []bytes. I wrote CL 163747 specifically with this use case in mind, but never got around to integrating it into any codegen packages.)

@DeedleFake

This comment has been minimized.

Copy link

@DeedleFake DeedleFake commented Dec 4, 2019

I'm unsure what you're talking about in terms of immutability. io.Reader already enforces immutability. That's the entire point. When you call Read(buf), it copies data into the buffer that you provided. Changing buf after that has zero effect on the internals of the io.Reader.

@bradfitz

This comment has been minimized.

Copy link
Contributor Author

@bradfitz bradfitz commented Dec 4, 2019

I agree with @DeedleFake. I don't want to play games with magic []byte array backings. It's okay to copy from the binary into user-provided buffers.

@gdamore

This comment has been minimized.

Copy link

@gdamore gdamore commented Dec 4, 2019

Just another wrinkle here -- I have a different project which uses DTrace source code (embedded). This is sensitive to differences between \n and \r\n. (We can argue whether this is a dumb thing in DTrace or not -- that's beside the point and it is the situation today.)

It's super useful that backticked strings treat both as \n regardless of how they appear in source, and I rely on this with a go-generate to embed the DTrace.

So if there is an embed file added to the go command, I would gently suggest that options to change the handling of CR/CRLF might come in very handy, particularly for folks who might be developing on different systems where the default line endings can be a gotcha.

@rsc

This comment has been minimized.

Copy link
Contributor

@rsc rsc commented Dec 4, 2019

Like with compression, I'd really like to stop at "copy the file bytes into the binary". CR/CRLF normalization, Unicode normalization, gofmt'ing, all that belongs elsewhere. Check in the files containing the exact bytes you want. (If your version control can't leave them alone, maybe check in gzipped content and gunzip them at runtime.) There are many file munging knobs we could imagine adding. Let's stop at 0.

@rsc

This comment has been minimized.

Copy link
Contributor

@rsc rsc commented Dec 4, 2019

It may be too late to introduce a new reserved directory name, as much as I'd like to.
(It wasn't too late back in 2014, but it's probably too late now.)
So some kind of opt-in comment may be necessary.

Suppose we define a type runtime.Files. Then you could imagine writing:

//go:embed *.html (or static/* etc)
var files runtime.Files

And then at runtime you just call files.Open to get back an interface { io.ReadSeeker; io.ReaderAt } with the data. Note that the var is unexported, so one package can't go around grubbing in another package's embedded files.

Names TBD but as far as the mechanism it seems like that should be enough, and I don't see how to make it simpler. (Simplifications welcome of course!)

@rsc

This comment has been minimized.

Copy link
Contributor

@rsc rsc commented Dec 4, 2019

Whatever we do, it needs to be possible to support with Bazel and Gazelle too. That would mean having Gazelle recognize the comment and write out a Bazel rule saying the globs, and then we'd need to expose a tool (go tool embedgen or whatever) to generate the extra file to include in the build (the go command would do this automatically and never actually show the extra file). That seems straightforward enough.

@gdamore

This comment has been minimized.

Copy link

@gdamore gdamore commented Dec 4, 2019

If various munging won't do the trick, then that's an argument against using this new facility. It's not a stopper for me -- I can use go generate like I've been doing, but it means I cannot benefit from the new feature.

With respect to munging in general -- I can imagine a solution where someone provides an implementation of an interface (something like a Reader() on one side, and something to receive the file on the other -- maybe instantianted with an io.Reader from the file itself) -- which the go cmd would build and run to prefilter the file before embedding. Then folks can provide whatever filter they want. I imagine some folks would provide quasi-standard filters like a dos2unix implementation, compression, etc. (Maybe they should be chainable even.)

I guess there'd have to be an assumption that whatever the embedded processor is, it must be compilable on ~every build system, as go would be building a temporary native tool for this purpose.

@magical

This comment has been minimized.

Copy link
Contributor

@magical magical commented Dec 4, 2019

It may be too late to introduce a new reserved directory name, as much as I'd like to. [...] some kind of opt-in comment may be necessary.

If the files are only accessible through a special package, say runtime/embed, then importing that package could be the opt-in signal.

@dolmen

This comment has been minimized.

Copy link

@dolmen dolmen commented Dec 9, 2019

I think that any solution that adds symbols or values for symbols must be package scoped, not module scoped. Because the compilation unit for Go is package, not module.

So this leaves out any use of go.mod to specify the list of files to import.

@urandom

This comment has been minimized.

Copy link

@urandom urandom commented Dec 9, 2019

@dolmen if the final result its in its own package, then the scope itself would be a module.

@dolmen

This comment has been minimized.

Copy link

@dolmen dolmen commented Dec 9, 2019

@urandom No, the scope is the package(s) that imports that generated package. But anyway I don't think that a full generated package is in the scope of this proposal.

@urandom

This comment has been minimized.

Copy link

@urandom urandom commented Dec 9, 2019

@urandom No, the scope is the package(s) that imports that generated package. But anyway I don't think that a full generated package is in the scope of this proposal.

regardless of how this proposal would be implemented, given that various module packages will be using the final result, it makes sense that the definition of what gets embedded gets specified on a module-level. a precedent for this already exists as well, in the java ecosystem, where embedded files are module-scoped and added from a magic directory.

furthermore, go.mod presents the cleanest way to add such a feature (no magic comments, or magic directories) without breaking existing programs.

@dolmen

This comment has been minimized.

Copy link

@dolmen dolmen commented Dec 9, 2019

Here is something that have not yet seen mentioned: the API for Go source processing tools (compiler, static analyzers) is as important as the runtime API. This kind of API is a core value of Go that helps to grow the ecosystem (like go/ast / go/format and go mod edit).

This API could be used by pre-processor tools (in go:generate steps in particular) to get the list of files that will be embedded or it could be used to generate the reference.

In the case of a special package, I see nothing to change in go.mod parsing (go mod tools) or go/ast parser.

@mikeschinkel

This comment has been minimized.

Copy link

@mikeschinkel mikeschinkel commented Dec 10, 2019

@dolmen

"I think that any solution that adds symbols or values for symbols must be package scoped, not module scoped. Because the compilation unit for Go is package, not module. So this leaves out any use of go.mod to specify the list of files to import."

What is module? Modules are "a collection of related Go packages that are versioned together as a single unit." Thus a module can consist of a single package, and a single package can be the entirety of a module.

As such, go.mod is the correct place to specify the list of files to import assuming the Go team adopts go.mod over special comments and magic packages. That is unless and until the Go team decides to add a go.pkg file.

Further, if the Go team were to accept go.mod as the place to specify embed files then anyone wanting to embed files should provide a go.mod with the embed instructions, and the package which is represented by the directory in which the go.mod file resides would be the package that contains the embedded files.

But If that is not what the developer wants they should create another go.mod file and put it in the directory for the package they want to contain their embedded files.

Is there a legitimate scenario you envision where these constraints would not be workable?

@bcmills

This comment has been minimized.

Copy link
Member

@bcmills bcmills commented Dec 10, 2019

@mikeschinkel, a module is a collection of related packages. However, it is possible (and reasonable!) to use one package from a module without pulling in the transitive dependencies (and data!) of other packages within that module.

Data files are generally per-package dependencies, not per-module, so the information about how to locate those dependencies should be colocated with the package — not stored as separate module-level metadata.

@urandom

This comment has been minimized.

Copy link

@urandom urandom commented Dec 10, 2019

@bcmills

It seems that one can replace 'Data files' in your message with 'modules' and it will still hold true.
It's quite common to have specific modules as dependencies for specific packages of your own.
Yet we put all of them within the go.mod.

@bcmills

This comment has been minimized.

Copy link
Member

@bcmills bcmills commented Dec 10, 2019

@urandom, not all of the packages in the modules indicated in the go.mod file are linked into the final binary. (Putting a dependency in the go.mod file is not equivalent to linking that dependency into the program.)

@rsc

This comment has been minimized.

Copy link
Contributor

@rsc rsc commented Dec 10, 2019

Meta-point

It's clear that this is something lots of people care about, and Brad's original comment at the top was less a finished proposal than an initial sketch / starting point / call to action. I think it would make sense at this point to wind this specific discussion down, have Brad and maybe a couple other people collaborate on a detailed design doc, and then start a new issue for a discussion of that specific (yet to be written) doc. It seems like that would help focus what has become a bit of a sprawling conversation.

Thoughts?

@bradfitz

This comment has been minimized.

Copy link
Contributor Author

@bradfitz bradfitz commented Dec 10, 2019

I'm not sure I agree with closing this one, but we can Proposal-Hold it until there's a design doc and lock comments for a bit. (Almost all the comments are redundant at this point, as there are too many comments for people to read to see whether their comment is redundant...)

Maybe when there's a design doc then this one can be closed.

Or close this one and re-open #3035 (with comments frozen) so at least one open issue tracks it.

@mikeschinkel

This comment has been minimized.

Copy link

@mikeschinkel mikeschinkel commented Dec 10, 2019

Sorry to do this right after talk about closing, but the close discussion jumped in right after @bcmills comment and before I could clarify.

"However, it is possible (and reasonable!) to use one package from a module without pulling in the transitive dependencies (and data!) of other packages within that module."

Yes, clearly it is possible. But just like any best practice, a best practice for data packages could be to create a single package for a module, which resolves your concern. If that means a go.mod in the root and another go.mod in the subdirectory containing the data package, so be it.

I guess I am advocating that you don't make perfect be the enemy of the good here, where good here is identified as go.mod being a perfect place to specify embed files given that by their nature they are a listing the components of a module.

@rsc

This comment has been minimized.

Copy link
Contributor

@rsc rsc commented Dec 11, 2019

Sorry, but packages are the fundamental concept in Go, not modules.
Modules are simply groups of packages that get versioned as a unit.
Modules do not contribute additional semantics beyond those of the individual packages.
That's part of their simplicity.
Anything we do here should be tied to packages, not modules.

@jimmyfrasche

This comment has been minimized.

Copy link
Member

@jimmyfrasche jimmyfrasche commented Dec 11, 2019

Wherever this goes and however's it done, there should be a way to get a list of all the assets that will be embedded using go list (not just the patterns used).

@rsc

This comment has been minimized.

Copy link
Contributor

@rsc rsc commented Dec 11, 2019

Putting on hold until Brad and others work up a formal design doc.

@olekukonko

This comment has been minimized.

Copy link

@olekukonko olekukonko commented Dec 14, 2019

Blocking certain files shouldn't be too hard, especially if you use a static or embed directory. Symlinks might complicate that a bit, but you can just prevent it from embedding anything outside of the current module or, if you're on GOPATH, outside of the package containing the directory.

I'm not particularly a fan of a comment that compiles to code, but I also find the pseudo-package that affects compilation to be a bit strange as well. If the directory approach isn't used, maybe it might make a bit more sense to have some kind embed top-level declaration actually built into the language. It would work similarly to import, but would only support local paths and would require a name for it to be assigned to. For example,

embed ui "./ui/build"

func main() {
  file, err := ui.Open("version.txt")
  if err != nil {
    panic(err)
  }
  version, err = ioutil.ReadAll(file)
  if err != nil {
    panic(err)
  }
  file.Close()

  log.Printf("UI Version: %s\n", bytes.TrimSpace(version))
  http.ListenAndServe(":8080", http.EmbeddedDir(ui))
}

Edit: You beat me to it, @jayconrod.

This is clean and readable however am not sure the go team would want to introduce a new keyword

@tie

This comment has been minimized.

Copy link
Contributor

@tie tie commented Dec 16, 2019

The idea as I remember it was to just have a special directory name like "static" containing the static data and automatically make them available through an API, with no annotations needed.

Using static as a special directory name is a bit confusing and I’d rather go with assets.
Another idea I didn’t see in the thread is to allow importing assets as a package, e.g. import "example.com/internal/assets". The exposed API still needs a design, but at least it looks cleaner than special comments or new runtime/files-style packages.

@josharian

This comment has been minimized.

Copy link
Contributor

@josharian josharian commented Dec 16, 2019

Another idea I didn’t see in the thread is to allow importing assets as a package

This was proposed here: #35950 (comment)

One complication is that to enable typechecking to occur, you need either have this be a language change or provide body-less functions to be filled out by cmd/go.

@tie

This comment has been minimized.

Copy link
Contributor

@tie tie commented Dec 16, 2019

That’s similar idea, but the static.go design allows turning an arbitrary import path into data package, while assets directory works more like testdata, internal or vendor in terms of being “special”. One of the possible requirements assets could have is to contain no Go packages (or only allow docs), i.e. for implicit backwards compatibility.

This could also be combined with the runtime/files-thingy API for getting the files. That is, using raw imports for embedding directory trees with files and then using some runtime package for accessing them. Could be even os.Open, but that’s unlikely to be accepted.

@millergarym

This comment has been minimized.

Copy link

@millergarym millergarym commented Dec 20, 2019

The shurcooL/vfsgen together shurcooL/httpgzip has a nice feature where the content can be served without decompression.

eg

	rsp.Header().Set("Content-Type", "image/png")
	httpgzip.ServeContent(rsp, req, "", time.Time{}, file)
@aykevl

This comment has been minimized.

Copy link

@aykevl aykevl commented Jan 6, 2020

A similar feature is being proposed for C++: std::embed:

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1040r0.html
https://mobile.twitter.com/Cor3ntin/status/1208389050698215427

It may be useful as inspiration for a design, and to collect possible use cases.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
You can’t perform that action at this time.