// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package embed provides access to files embedded in the running Go program. // // Go source files that import "embed" can use the //go:embed directive // to initialize a variable of type string, []byte, or FS with the contents of // files read from the package directory or subdirectories at compile time. // // For example, here are three ways to embed a file named hello.txt // and then print its contents at run time. // // Embedding one file into a string: // // import _ "embed" // // //go:embed hello.txt // var s string // print(s) // // Embedding one file into a slice of bytes: // // import _ "embed" // // //go:embed hello.txt // var b []byte // print(string(b)) // // Embedded one or more files into a file system: // // import "embed" // // //go:embed hello.txt // var f embed.FS // data, _ := f.ReadFile("hello.txt") // print(string(data)) // // Directives // // A //go:embed directive above a variable declaration specifies which files to embed, // using one or more path.Match patterns. // // The directive must immediately precede a line containing the declaration of a single variable. // Only blank lines and ‘//’ line comments are permitted between the directive and the declaration. // // The type of the variable must be a string type, or a slice of a byte type, // or FS (or an alias of FS). // // For example: // // package server // // import "embed" // // // content holds our static web server content. // //go:embed image/* template/* // //go:embed html/index.html // var content embed.FS // // The Go build system will recognize the directives and arrange for the declared variable // (in the example above, content) to be populated with the matching files from the file system. // // The //go:embed directive accepts multiple space-separated patterns for // brevity, but it can also be repeated, to avoid very long lines when there are // many patterns. The patterns are interpreted relative to the package directory // containing the source file. The path separator is a forward slash, even on // Windows systems. Patterns may not contain ‘.’ or ‘..’ or empty path elements, // nor may they begin or end with a slash. To match everything in the current // directory, use ‘*’ instead of ‘.’. To allow for naming files with spaces in // their names, patterns can be written as Go double-quoted or back-quoted // string literals. // // If a pattern names a directory, all files in the subtree rooted at that directory are // embedded (recursively), except that files with names beginning with ‘.’ or ‘_’ // are excluded. So the variable in the above example is almost equivalent to: // // // content is our static web server content. // //go:embed image template html/index.html // var content embed.FS // // The difference is that ‘image/*’ embeds ‘image/.tempfile’ while ‘image’ does not. // // The //go:embed directive can be used with both exported and unexported variables, // depending on whether the package wants to make the data available to other packages. // It can only be used with global variables at package scope, // not with local variables. // // Patterns must not match files outside the package's module, such as ‘.git/*’ or symbolic links. // Matches for empty directories are ignored. After that, each pattern in a //go:embed line // must match at least one file or non-empty directory. // // If any patterns are invalid or have invalid matches, the build will fail. // // Strings and Bytes // // The //go:embed line for a variable of type string or []byte can have only a single pattern, // and that pattern can match only a single file. The string or []byte is initialized with // the contents of that file. // // The //go:embed directive requires importing "embed", even when using a string or []byte. // In source files that don't refer to embed.FS, use a blank import (import _ "embed"). // // File Systems // // For embedding a single file, a variable of type string or []byte is often best. // The FS type enables embedding a tree of files, such as a directory of static // web server content, as in the example above. // // FS implements the io/fs package's FS interface, so it can be used with any package that // understands file systems, including net/http, text/template, and html/template. // // For example, given the content variable in the example above, we can write: // // http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.FS(content)))) // // template.ParseFS(content, "*.tmpl") // // Tools // // To support tools that analyze Go packages, the patterns found in //go:embed lines // are available in “go list” output. See the EmbedPatterns, TestEmbedPatterns, // and XTestEmbedPatterns fields in the “go help list” output. //
package embed import ( ) // An FS is a read-only collection of files, usually initialized with a //go:embed directive. // When declared without a //go:embed directive, an FS is an empty file system. // // An FS is a read-only value, so it is safe to use from multiple goroutines // simultaneously and also safe to assign values of type FS to each other. // // FS implements fs.FS, so it can be used with any package that understands // file system interfaces, including net/http, text/template, and html/template. // // See the package documentation for more details about initializing an FS. type FS struct { // The compiler knows the layout of this struct. // See cmd/compile/internal/gc's initEmbed. // // The files list is sorted by name but not by simple string comparison. // Instead, each file's name takes the form "dir/elem" or "dir/elem/". // The optional trailing slash indicates that the file is itself a directory. // The files list is sorted first by dir (if dir is missing, it is taken to be ".") // and then by base, so this list of files: // // p // q/ // q/r // q/s/ // q/s/t // q/s/u // q/v // w // // is actually sorted as: // // p # dir=. elem=p // q/ # dir=. elem=q // w/ # dir=. elem=w // q/r # dir=q elem=r // q/s/ # dir=q elem=s // q/v # dir=q elem=v // q/s/t # dir=q/s elem=t // q/s/u # dir=q/s elem=u // // This order brings directory contents together in contiguous sections // of the list, allowing a directory read to use binary search to find // the relevant sequence of entries. files *[]file } // split splits the name into dir and elem as described in the // comment in the FS struct above. isDir reports whether the // final trailing slash was present, indicating that name is a directory. func split( string) (, string, bool) { if [len()-1] == '/' { = true = [:len()-1] } := len() - 1 for >= 0 && [] != '/' { -- } if < 0 { return ".", , } return [:], [+1:], } // trimSlash trims a trailing slash from name, if present, // returning the possibly shortened name. func trimSlash( string) string { if len() > 0 && [len()-1] == '/' { return [:len()-1] } return } var ( _ fs.ReadDirFS = FS{} _ fs.ReadFileFS = FS{} ) // A file is a single file in the FS. // It implements fs.FileInfo and fs.DirEntry. type file struct { // The compiler knows the layout of this struct. // See cmd/compile/internal/gc's initEmbed. name string data string hash [16]byte // truncated SHA256 hash } var ( _ fs.FileInfo = (*file)(nil) _ fs.DirEntry = (*file)(nil) ) func ( *file) () string { , , := split(.name); return } func ( *file) () int64 { return int64(len(.data)) } func ( *file) () time.Time { return time.Time{} } func ( *file) () bool { , , := split(.name); return } func ( *file) () interface{} { return nil } func ( *file) () fs.FileMode { return .Mode().Type() } func ( *file) () (fs.FileInfo, error) { return , nil } func ( *file) () fs.FileMode { if .IsDir() { return fs.ModeDir | 0555 } return 0444 } // dotFile is a file for the root directory, // which is omitted from the files list in a FS. var dotFile = &file{name: "./"} // lookup returns the named file, or nil if it is not present. func ( FS) ( string) *file { if !fs.ValidPath() { // The compiler should never emit a file with an invalid name, // so this check is not strictly necessary (if name is invalid, // we shouldn't find a match below), but it's a good backstop anyway. return nil } if == "." { return dotFile } if .files == nil { return nil } // Binary search to find where name would be in the list, // and then check if name is at that position. , , := split() := *.files := sortSearch(len(), func( int) bool { , , := split([].name) return > || == && >= }) if < len() && trimSlash([].name) == { return &[] } return nil } // readDir returns the list of files corresponding to the directory dir. func ( FS) ( string) []file { if .files == nil { return nil } // Binary search to find where dir starts and ends in the list // and then return that slice of the list. := *.files := sortSearch(len(), func( int) bool { , , := split([].name) return >= }) := sortSearch(len(), func( int) bool { , , := split([].name) return > }) return [:] } // Open opens the named file for reading and returns it as an fs.File. func ( FS) ( string) (fs.File, error) { := .lookup() if == nil { return nil, &fs.PathError{Op: "open", Path: , Err: fs.ErrNotExist} } if .IsDir() { return &openDir{, .readDir(), 0}, nil } return &openFile{, 0}, nil } // ReadDir reads and returns the entire named directory. func ( FS) ( string) ([]fs.DirEntry, error) { , := .Open() if != nil { return nil, } , := .(*openDir) if ! { return nil, &fs.PathError{Op: "read", Path: , Err: errors.New("not a directory")} } := make([]fs.DirEntry, len(.files)) for := range { [] = &.files[] } return , nil } // ReadFile reads and returns the content of the named file. func ( FS) ( string) ([]byte, error) { , := .Open() if != nil { return nil, } , := .(*openFile) if ! { return nil, &fs.PathError{Op: "read", Path: , Err: errors.New("is a directory")} } return []byte(.f.data), nil } // An openFile is a regular file open for reading. type openFile struct { f *file // the file itself offset int64 // current read offset } func ( *openFile) () error { return nil } func ( *openFile) () (fs.FileInfo, error) { return .f, nil } func ( *openFile) ( []byte) (int, error) { if .offset >= int64(len(.f.data)) { return 0, io.EOF } if .offset < 0 { return 0, &fs.PathError{Op: "read", Path: .f.name, Err: fs.ErrInvalid} } := copy(, .f.data[.offset:]) .offset += int64() return , nil } func ( *openFile) ( int64, int) (int64, error) { switch { case 0: // offset += 0 case 1: += .offset case 2: += int64(len(.f.data)) } if < 0 || > int64(len(.f.data)) { return 0, &fs.PathError{Op: "seek", Path: .f.name, Err: fs.ErrInvalid} } .offset = return , nil } // An openDir is a directory open for reading. type openDir struct { f *file // the directory file itself files []file // the directory contents offset int // the read offset, an index into the files slice } func ( *openDir) () error { return nil } func ( *openDir) () (fs.FileInfo, error) { return .f, nil } func ( *openDir) ([]byte) (int, error) { return 0, &fs.PathError{Op: "read", Path: .f.name, Err: errors.New("is a directory")} } func ( *openDir) ( int) ([]fs.DirEntry, error) { := len(.files) - .offset if > 0 && > { = } if == 0 { if <= 0 { return nil, nil } return nil, io.EOF } := make([]fs.DirEntry, ) for := range { [] = &.files[.offset+] } .offset += return , nil } // sortSearch is like sort.Search, avoiding an import. func sortSearch( int, func(int) bool) int { // Define f(-1) == false and f(n) == true. // Invariant: f(i-1) == false, f(j) == true. , := 0, for < { := int(uint(+) >> 1) // avoid overflow when computing h // i ≤ h < j if !() { = + 1 // preserves f(i-1) == false } else { = // preserves f(j) == true } } // i == j, f(i-1) == false, and f(j) (= f(i)) == true => answer is i. return }