1 // Copyright 2020 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
5 // Package embed provides access to files embedded in the running Go program.
7 // Go source files that import "embed" can use the //go:embed directive
8 // to initialize a variable of type string, []byte, or FS with the contents of
9 // files read from the package directory or subdirectories at compile time.
11 // For example, here are three ways to embed a file named hello.txt
12 // and then print its contents at run time.
14 // Embedding one file into a string:
18 // //go:embed hello.txt
22 // Embedding one file into a slice of bytes:
26 // //go:embed hello.txt
30 // Embedded one or more files into a file system:
34 // //go:embed hello.txt
36 // data, _ := f.ReadFile("hello.txt")
37 // print(string(data))
41 // A //go:embed directive above a variable declaration specifies which files to embed,
42 // using one or more path.Match patterns.
44 // The directive must immediately precede a line containing the declaration of a single variable.
45 // Only blank lines and ‘//’ line comments are permitted between the directive and the declaration.
47 // The type of the variable must be a string type, or a slice of a byte type,
48 // or FS (or an alias of FS).
56 // // content holds our static web server content.
57 // //go:embed image/* template/*
58 // //go:embed html/index.html
59 // var content embed.FS
61 // The Go build system will recognize the directives and arrange for the declared variable
62 // (in the example above, content) to be populated with the matching files from the file system.
64 // The //go:embed directive accepts multiple space-separated patterns for brevity,
65 // but it can also be repeated, to avoid very long lines when there are many patterns.
66 // The patterns are interpreted relative to the package directory containing the source file.
67 // The path separator is a forward slash, even on Windows systems.
68 // To allow for naming files with spaces in their names, patterns can be written
69 // as Go double-quoted or back-quoted string literals.
71 // If a pattern names a directory, all files in the subtree rooted at that directory are
72 // embedded (recursively), except that files with names beginning with ‘.’ or ‘_’
73 // are excluded. So the variable in the above example is almost equivalent to:
75 // // content is our static web server content.
76 // //go:embed image template html/index.html
77 // var content embed.FS
79 // The difference is that ‘image/*’ embeds ‘image/.tempfile’ while ‘image’ does not.
81 // The //go:embed directive can be used with both exported and unexported variables,
82 // depending on whether the package wants to make the data available to other packages.
83 // It can only be used with global variables at package scope,
84 // not with local variables.
86 // Patterns must not match files outside the package's module, such as ‘.git/*’ or symbolic links.
87 // Matches for empty directories are ignored. After that, each pattern in a //go:embed line
88 // must match at least one file or non-empty directory.
90 // Patterns must not contain ‘.’ or ‘..’ path elements nor begin with a leading slash.
91 // To match everything in the current directory, use ‘*’ instead of ‘.’.
93 // If any patterns are invalid or have invalid matches, the build will fail.
97 // The //go:embed line for a variable of type string or []byte can have only a single pattern,
98 // and that pattern can match only a single file. The string or []byte is initialized with
99 // the contents of that file.
101 // The //go:embed directive requires importing "embed", even when using a string or []byte.
102 // In source files that don't refer to embed.FS, use a blank import (import _ "embed").
106 // For embedding a single file, a variable of type string or []byte is often best.
107 // The FS type enables embedding a tree of files, such as a directory of static
108 // web server content, as in the example above.
110 // FS implements the io/fs package's FS interface, so it can be used with any package that
111 // understands file systems, including net/http, text/template, and html/template.
113 // For example, given the content variable in the example above, we can write:
115 // http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.FS(content))))
117 // template.ParseFS(content, "*.tmpl")
121 // To support tools that analyze Go packages, the patterns found in //go:embed lines
122 // are available in “go list” output. See the EmbedPatterns, TestEmbedPatterns,
123 // and XTestEmbedPatterns fields in the “go help list” output.
134 // An FS is a read-only collection of files, usually initialized with a //go:embed directive.
135 // When declared without a //go:embed directive, an FS is an empty file system.
137 // An FS is a read-only value, so it is safe to use from multiple goroutines
138 // simultaneously and also safe to assign values of type FS to each other.
140 // FS implements fs.FS, so it can be used with any package that understands
141 // file system interfaces, including net/http, text/template, and html/template.
143 // See the package documentation for more details about initializing an FS.
145 // The compiler knows the layout of this struct.
146 // See cmd/compile/internal/staticdata's WriteEmbed.
148 // The files list is sorted by name but not by simple string comparison.
149 // Instead, each file's name takes the form "dir/elem" or "dir/elem/".
150 // The optional trailing slash indicates that the file is itself a directory.
151 // The files list is sorted first by dir (if dir is missing, it is taken to be ".")
152 // and then by base, so this list of files:
163 // is actually sorted as:
168 // q/r # dir=q elem=r
169 // q/s/ # dir=q elem=s
170 // q/v # dir=q elem=v
171 // q/s/t # dir=q/s elem=t
172 // q/s/u # dir=q/s elem=u
174 // This order brings directory contents together in contiguous sections
175 // of the list, allowing a directory read to use binary search to find
176 // the relevant sequence of entries.
180 // split splits the name into dir and elem as described in the
181 // comment in the FS struct above. isDir reports whether the
182 // final trailing slash was present, indicating that name is a directory.
183 func split(name string) (dir, elem string, isDir bool) {
184 if name[len(name)-1] == '/' {
186 name = name[:len(name)-1]
189 for i >= 0 && name[i] != '/' {
193 return ".", name, isDir
195 return name[:i], name[i+1:], isDir
198 // trimSlash trims a trailing slash from name, if present,
199 // returning the possibly shortened name.
200 func trimSlash(name string) string {
201 if len(name) > 0 && name[len(name)-1] == '/' {
202 return name[:len(name)-1]
208 _ fs.ReadDirFS = FS{}
209 _ fs.ReadFileFS = FS{}
212 // A file is a single file in the FS.
213 // It implements fs.FileInfo and fs.DirEntry.
215 // The compiler knows the layout of this struct.
216 // See cmd/compile/internal/staticdata's WriteEmbed.
219 hash [16]byte // truncated SHA256 hash
223 _ fs.FileInfo = (*file)(nil)
224 _ fs.DirEntry = (*file)(nil)
227 func (f *file) Name() string { _, elem, _ := split(f.name); return elem }
228 func (f *file) Size() int64 { return int64(len(f.data)) }
229 func (f *file) ModTime() time.Time { return time.Time{} }
230 func (f *file) IsDir() bool { _, _, isDir := split(f.name); return isDir }
231 func (f *file) Sys() interface{} { return nil }
232 func (f *file) Type() fs.FileMode { return f.Mode().Type() }
233 func (f *file) Info() (fs.FileInfo, error) { return f, nil }
235 func (f *file) Mode() fs.FileMode {
237 return fs.ModeDir | 0555
242 // dotFile is a file for the root directory,
243 // which is omitted from the files list in a FS.
244 var dotFile = &file{name: "./"}
246 // lookup returns the named file, or nil if it is not present.
247 func (f FS) lookup(name string) *file {
248 if !fs.ValidPath(name) {
249 // The compiler should never emit a file with an invalid name,
250 // so this check is not strictly necessary (if name is invalid,
251 // we shouldn't find a match below), but it's a good backstop anyway.
261 // Binary search to find where name would be in the list,
262 // and then check if name is at that position.
263 dir, elem, _ := split(name)
265 i := sortSearch(len(files), func(i int) bool {
266 idir, ielem, _ := split(files[i].name)
267 return idir > dir || idir == dir && ielem >= elem
269 if i < len(files) && trimSlash(files[i].name) == name {
275 // readDir returns the list of files corresponding to the directory dir.
276 func (f FS) readDir(dir string) []file {
280 // Binary search to find where dir starts and ends in the list
281 // and then return that slice of the list.
283 i := sortSearch(len(files), func(i int) bool {
284 idir, _, _ := split(files[i].name)
287 j := sortSearch(len(files), func(j int) bool {
288 jdir, _, _ := split(files[j].name)
294 // Open opens the named file for reading and returns it as an fs.File.
295 func (f FS) Open(name string) (fs.File, error) {
296 file := f.lookup(name)
298 return nil, &fs.PathError{Op: "open", Path: name, Err: fs.ErrNotExist}
301 return &openDir{file, f.readDir(name), 0}, nil
303 return &openFile{file, 0}, nil
306 // ReadDir reads and returns the entire named directory.
307 func (f FS) ReadDir(name string) ([]fs.DirEntry, error) {
308 file, err := f.Open(name)
312 dir, ok := file.(*openDir)
314 return nil, &fs.PathError{Op: "read", Path: name, Err: errors.New("not a directory")}
316 list := make([]fs.DirEntry, len(dir.files))
317 for i := range list {
318 list[i] = &dir.files[i]
323 // ReadFile reads and returns the content of the named file.
324 func (f FS) ReadFile(name string) ([]byte, error) {
325 file, err := f.Open(name)
329 ofile, ok := file.(*openFile)
331 return nil, &fs.PathError{Op: "read", Path: name, Err: errors.New("is a directory")}
333 return []byte(ofile.f.data), nil
336 // An openFile is a regular file open for reading.
337 type openFile struct {
338 f *file // the file itself
339 offset int64 // current read offset
342 func (f *openFile) Close() error { return nil }
343 func (f *openFile) Stat() (fs.FileInfo, error) { return f.f, nil }
345 func (f *openFile) Read(b []byte) (int, error) {
346 if f.offset >= int64(len(f.f.data)) {
350 return 0, &fs.PathError{Op: "read", Path: f.f.name, Err: fs.ErrInvalid}
352 n := copy(b, f.f.data[f.offset:])
357 func (f *openFile) Seek(offset int64, whence int) (int64, error) {
364 offset += int64(len(f.f.data))
366 if offset < 0 || offset > int64(len(f.f.data)) {
367 return 0, &fs.PathError{Op: "seek", Path: f.f.name, Err: fs.ErrInvalid}
373 // An openDir is a directory open for reading.
374 type openDir struct {
375 f *file // the directory file itself
376 files []file // the directory contents
377 offset int // the read offset, an index into the files slice
380 func (d *openDir) Close() error { return nil }
381 func (d *openDir) Stat() (fs.FileInfo, error) { return d.f, nil }
383 func (d *openDir) Read([]byte) (int, error) {
384 return 0, &fs.PathError{Op: "read", Path: d.f.name, Err: errors.New("is a directory")}
387 func (d *openDir) ReadDir(count int) ([]fs.DirEntry, error) {
388 n := len(d.files) - d.offset
389 if count > 0 && n > count {
398 list := make([]fs.DirEntry, n)
399 for i := range list {
400 list[i] = &d.files[d.offset+i]
406 // sortSearch is like sort.Search, avoiding an import.
407 func sortSearch(n int, f func(int) bool) int {
408 // Define f(-1) == false and f(n) == true.
409 // Invariant: f(i-1) == false, f(j) == true.
412 h := int(uint(i+j) >> 1) // avoid overflow when computing h
415 i = h + 1 // preserves f(i-1) == false
417 j = h // preserves f(j) == true
420 // i == j, f(i-1) == false, and f(j) (= f(i)) == true => answer is i.