]> Cypherpunks.ru repositories - gostls13.git/blob - src/embed/embed.go
cmd/compile/internal/inline: score call sites exposed by inlines
[gostls13.git] / src / embed / embed.go
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.
4
5 // Package embed provides access to files embedded in the running Go program.
6 //
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.
10 //
11 // For example, here are three ways to embed a file named hello.txt
12 // and then print its contents at run time.
13 //
14 // Embedding one file into a string:
15 //
16 //      import _ "embed"
17 //
18 //      //go:embed hello.txt
19 //      var s string
20 //      print(s)
21 //
22 // Embedding one file into a slice of bytes:
23 //
24 //      import _ "embed"
25 //
26 //      //go:embed hello.txt
27 //      var b []byte
28 //      print(string(b))
29 //
30 // Embedded one or more files into a file system:
31 //
32 //      import "embed"
33 //
34 //      //go:embed hello.txt
35 //      var f embed.FS
36 //      data, _ := f.ReadFile("hello.txt")
37 //      print(string(data))
38 //
39 // # Directives
40 //
41 // A //go:embed directive above a variable declaration specifies which files to embed,
42 // using one or more path.Match patterns.
43 //
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.
46 //
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]).
49 //
50 // For example:
51 //
52 //      package server
53 //
54 //      import "embed"
55 //
56 //      // content holds our static web server content.
57 //      //go:embed image/* template/*
58 //      //go:embed html/index.html
59 //      var content embed.FS
60 //
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.
63 //
64 // The //go:embed directive accepts multiple space-separated patterns for
65 // brevity, but it can also be repeated, to avoid very long lines when there are
66 // many patterns. The patterns are interpreted relative to the package directory
67 // containing the source file. The path separator is a forward slash, even on
68 // Windows systems. Patterns may not contain ‘.’ or ‘..’ or empty path elements,
69 // nor may they begin or end with a slash. To match everything in the current
70 // directory, use ‘*’ instead of ‘.’. To allow for naming files with spaces in
71 // their names, patterns can be written as Go double-quoted or back-quoted
72 // string literals.
73 //
74 // If a pattern names a directory, all files in the subtree rooted at that directory are
75 // embedded (recursively), except that files with names beginning with ‘.’ or ‘_’
76 // are excluded. So the variable in the above example is almost equivalent to:
77 //
78 //      // content is our static web server content.
79 //      //go:embed image template html/index.html
80 //      var content embed.FS
81 //
82 // The difference is that ‘image/*’ embeds ‘image/.tempfile’ while ‘image’ does not.
83 // Neither embeds ‘image/dir/.tempfile’.
84 //
85 // If a pattern begins with the prefix ‘all:’, then the rule for walking directories is changed
86 // to include those files beginning with ‘.’ or ‘_’. For example, ‘all:image’ embeds
87 // both ‘image/.tempfile’ and ‘image/dir/.tempfile’.
88 //
89 // The //go:embed directive can be used with both exported and unexported variables,
90 // depending on whether the package wants to make the data available to other packages.
91 // It can only be used with variables at package scope, not with local variables.
92 //
93 // Patterns must not match files outside the package's module, such as ‘.git/*’ or symbolic links.
94 // Patterns must not match files whose names include the special punctuation characters  " * < > ? ` ' | / \ and :.
95 // Matches for empty directories are ignored. After that, each pattern in a //go:embed line
96 // must match at least one file or non-empty directory.
97 //
98 // If any patterns are invalid or have invalid matches, the build will fail.
99 //
100 // # Strings and Bytes
101 //
102 // The //go:embed line for a variable of type string or []byte can have only a single pattern,
103 // and that pattern can match only a single file. The string or []byte is initialized with
104 // the contents of that file.
105 //
106 // The //go:embed directive requires importing "embed", even when using a string or []byte.
107 // In source files that don't refer to [embed.FS], use a blank import (import _ "embed").
108 //
109 // # File Systems
110 //
111 // For embedding a single file, a variable of type string or []byte is often best.
112 // The [FS] type enables embedding a tree of files, such as a directory of static
113 // web server content, as in the example above.
114 //
115 // FS implements the [io/fs] package's [FS] interface, so it can be used with any package that
116 // understands file systems, including [net/http], [text/template], and [html/template].
117 //
118 // For example, given the content variable in the example above, we can write:
119 //
120 //      http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.FS(content))))
121 //
122 //      template.ParseFS(content, "*.tmpl")
123 //
124 // # Tools
125 //
126 // To support tools that analyze Go packages, the patterns found in //go:embed lines
127 // are available in “go list” output. See the EmbedPatterns, TestEmbedPatterns,
128 // and XTestEmbedPatterns fields in the “go help list” output.
129 package embed
130
131 import (
132         "errors"
133         "io"
134         "io/fs"
135         "time"
136 )
137
138 // An FS is a read-only collection of files, usually initialized with a //go:embed directive.
139 // When declared without a //go:embed directive, an FS is an empty file system.
140 //
141 // An FS is a read-only value, so it is safe to use from multiple goroutines
142 // simultaneously and also safe to assign values of type FS to each other.
143 //
144 // FS implements fs.FS, so it can be used with any package that understands
145 // file system interfaces, including net/http, text/template, and html/template.
146 //
147 // See the package documentation for more details about initializing an FS.
148 type FS struct {
149         // The compiler knows the layout of this struct.
150         // See cmd/compile/internal/staticdata's WriteEmbed.
151         //
152         // The files list is sorted by name but not by simple string comparison.
153         // Instead, each file's name takes the form "dir/elem" or "dir/elem/".
154         // The optional trailing slash indicates that the file is itself a directory.
155         // The files list is sorted first by dir (if dir is missing, it is taken to be ".")
156         // and then by base, so this list of files:
157         //
158         //      p
159         //      q/
160         //      q/r
161         //      q/s/
162         //      q/s/t
163         //      q/s/u
164         //      q/v
165         //      w
166         //
167         // is actually sorted as:
168         //
169         //      p       # dir=.    elem=p
170         //      q/      # dir=.    elem=q
171         //      w/      # dir=.    elem=w
172         //      q/r     # dir=q    elem=r
173         //      q/s/    # dir=q    elem=s
174         //      q/v     # dir=q    elem=v
175         //      q/s/t   # dir=q/s  elem=t
176         //      q/s/u   # dir=q/s  elem=u
177         //
178         // This order brings directory contents together in contiguous sections
179         // of the list, allowing a directory read to use binary search to find
180         // the relevant sequence of entries.
181         files *[]file
182 }
183
184 // split splits the name into dir and elem as described in the
185 // comment in the FS struct above. isDir reports whether the
186 // final trailing slash was present, indicating that name is a directory.
187 func split(name string) (dir, elem string, isDir bool) {
188         if name[len(name)-1] == '/' {
189                 isDir = true
190                 name = name[:len(name)-1]
191         }
192         i := len(name) - 1
193         for i >= 0 && name[i] != '/' {
194                 i--
195         }
196         if i < 0 {
197                 return ".", name, isDir
198         }
199         return name[:i], name[i+1:], isDir
200 }
201
202 // trimSlash trims a trailing slash from name, if present,
203 // returning the possibly shortened name.
204 func trimSlash(name string) string {
205         if len(name) > 0 && name[len(name)-1] == '/' {
206                 return name[:len(name)-1]
207         }
208         return name
209 }
210
211 var (
212         _ fs.ReadDirFS  = FS{}
213         _ fs.ReadFileFS = FS{}
214 )
215
216 // A file is a single file in the FS.
217 // It implements fs.FileInfo and fs.DirEntry.
218 type file struct {
219         // The compiler knows the layout of this struct.
220         // See cmd/compile/internal/staticdata's WriteEmbed.
221         name string
222         data string
223         hash [16]byte // truncated SHA256 hash
224 }
225
226 var (
227         _ fs.FileInfo = (*file)(nil)
228         _ fs.DirEntry = (*file)(nil)
229 )
230
231 func (f *file) Name() string               { _, elem, _ := split(f.name); return elem }
232 func (f *file) Size() int64                { return int64(len(f.data)) }
233 func (f *file) ModTime() time.Time         { return time.Time{} }
234 func (f *file) IsDir() bool                { _, _, isDir := split(f.name); return isDir }
235 func (f *file) Sys() any                   { return nil }
236 func (f *file) Type() fs.FileMode          { return f.Mode().Type() }
237 func (f *file) Info() (fs.FileInfo, error) { return f, nil }
238
239 func (f *file) Mode() fs.FileMode {
240         if f.IsDir() {
241                 return fs.ModeDir | 0555
242         }
243         return 0444
244 }
245
246 func (f *file) String() string {
247         return fs.FormatFileInfo(f)
248 }
249
250 // dotFile is a file for the root directory,
251 // which is omitted from the files list in a FS.
252 var dotFile = &file{name: "./"}
253
254 // lookup returns the named file, or nil if it is not present.
255 func (f FS) lookup(name string) *file {
256         if !fs.ValidPath(name) {
257                 // The compiler should never emit a file with an invalid name,
258                 // so this check is not strictly necessary (if name is invalid,
259                 // we shouldn't find a match below), but it's a good backstop anyway.
260                 return nil
261         }
262         if name == "." {
263                 return dotFile
264         }
265         if f.files == nil {
266                 return nil
267         }
268
269         // Binary search to find where name would be in the list,
270         // and then check if name is at that position.
271         dir, elem, _ := split(name)
272         files := *f.files
273         i := sortSearch(len(files), func(i int) bool {
274                 idir, ielem, _ := split(files[i].name)
275                 return idir > dir || idir == dir && ielem >= elem
276         })
277         if i < len(files) && trimSlash(files[i].name) == name {
278                 return &files[i]
279         }
280         return nil
281 }
282
283 // readDir returns the list of files corresponding to the directory dir.
284 func (f FS) readDir(dir string) []file {
285         if f.files == nil {
286                 return nil
287         }
288         // Binary search to find where dir starts and ends in the list
289         // and then return that slice of the list.
290         files := *f.files
291         i := sortSearch(len(files), func(i int) bool {
292                 idir, _, _ := split(files[i].name)
293                 return idir >= dir
294         })
295         j := sortSearch(len(files), func(j int) bool {
296                 jdir, _, _ := split(files[j].name)
297                 return jdir > dir
298         })
299         return files[i:j]
300 }
301
302 // Open opens the named file for reading and returns it as an [fs.File].
303 //
304 // The returned file implements [io.Seeker] and [io.ReaderAt] when the file is not a directory.
305 func (f FS) Open(name string) (fs.File, error) {
306         file := f.lookup(name)
307         if file == nil {
308                 return nil, &fs.PathError{Op: "open", Path: name, Err: fs.ErrNotExist}
309         }
310         if file.IsDir() {
311                 return &openDir{file, f.readDir(name), 0}, nil
312         }
313         return &openFile{file, 0}, nil
314 }
315
316 // ReadDir reads and returns the entire named directory.
317 func (f FS) ReadDir(name string) ([]fs.DirEntry, error) {
318         file, err := f.Open(name)
319         if err != nil {
320                 return nil, err
321         }
322         dir, ok := file.(*openDir)
323         if !ok {
324                 return nil, &fs.PathError{Op: "read", Path: name, Err: errors.New("not a directory")}
325         }
326         list := make([]fs.DirEntry, len(dir.files))
327         for i := range list {
328                 list[i] = &dir.files[i]
329         }
330         return list, nil
331 }
332
333 // ReadFile reads and returns the content of the named file.
334 func (f FS) ReadFile(name string) ([]byte, error) {
335         file, err := f.Open(name)
336         if err != nil {
337                 return nil, err
338         }
339         ofile, ok := file.(*openFile)
340         if !ok {
341                 return nil, &fs.PathError{Op: "read", Path: name, Err: errors.New("is a directory")}
342         }
343         return []byte(ofile.f.data), nil
344 }
345
346 // An openFile is a regular file open for reading.
347 type openFile struct {
348         f      *file // the file itself
349         offset int64 // current read offset
350 }
351
352 var (
353         _ io.Seeker   = (*openFile)(nil)
354         _ io.ReaderAt = (*openFile)(nil)
355 )
356
357 func (f *openFile) Close() error               { return nil }
358 func (f *openFile) Stat() (fs.FileInfo, error) { return f.f, nil }
359
360 func (f *openFile) Read(b []byte) (int, error) {
361         if f.offset >= int64(len(f.f.data)) {
362                 return 0, io.EOF
363         }
364         if f.offset < 0 {
365                 return 0, &fs.PathError{Op: "read", Path: f.f.name, Err: fs.ErrInvalid}
366         }
367         n := copy(b, f.f.data[f.offset:])
368         f.offset += int64(n)
369         return n, nil
370 }
371
372 func (f *openFile) Seek(offset int64, whence int) (int64, error) {
373         switch whence {
374         case 0:
375                 // offset += 0
376         case 1:
377                 offset += f.offset
378         case 2:
379                 offset += int64(len(f.f.data))
380         }
381         if offset < 0 || offset > int64(len(f.f.data)) {
382                 return 0, &fs.PathError{Op: "seek", Path: f.f.name, Err: fs.ErrInvalid}
383         }
384         f.offset = offset
385         return offset, nil
386 }
387
388 func (f *openFile) ReadAt(b []byte, offset int64) (int, error) {
389         if offset < 0 || offset > int64(len(f.f.data)) {
390                 return 0, &fs.PathError{Op: "read", Path: f.f.name, Err: fs.ErrInvalid}
391         }
392         n := copy(b, f.f.data[offset:])
393         if n < len(b) {
394                 return n, io.EOF
395         }
396         return n, nil
397 }
398
399 // An openDir is a directory open for reading.
400 type openDir struct {
401         f      *file  // the directory file itself
402         files  []file // the directory contents
403         offset int    // the read offset, an index into the files slice
404 }
405
406 func (d *openDir) Close() error               { return nil }
407 func (d *openDir) Stat() (fs.FileInfo, error) { return d.f, nil }
408
409 func (d *openDir) Read([]byte) (int, error) {
410         return 0, &fs.PathError{Op: "read", Path: d.f.name, Err: errors.New("is a directory")}
411 }
412
413 func (d *openDir) ReadDir(count int) ([]fs.DirEntry, error) {
414         n := len(d.files) - d.offset
415         if n == 0 {
416                 if count <= 0 {
417                         return nil, nil
418                 }
419                 return nil, io.EOF
420         }
421         if count > 0 && n > count {
422                 n = count
423         }
424         list := make([]fs.DirEntry, n)
425         for i := range list {
426                 list[i] = &d.files[d.offset+i]
427         }
428         d.offset += n
429         return list, nil
430 }
431
432 // sortSearch is like sort.Search, avoiding an import.
433 func sortSearch(n int, f func(int) bool) int {
434         // Define f(-1) == false and f(n) == true.
435         // Invariant: f(i-1) == false, f(j) == true.
436         i, j := 0, n
437         for i < j {
438                 h := int(uint(i+j) >> 1) // avoid overflow when computing h
439                 // i ≤ h < j
440                 if !f(h) {
441                         i = h + 1 // preserves f(i-1) == false
442                 } else {
443                         j = h // preserves f(j) == true
444                 }
445         }
446         // i == j, f(i-1) == false, and f(j) (= f(i)) == true  =>  answer is i.
447         return i
448 }