In the upcoming Go 1.16, we will be able to embed resource files directly into compiled binaries. How is it implemented? What is the representation of embedded files?
Starting from a broader abstraction, we need an in-memory file system. This further inspires us to think about file system abstraction: what are the minimum requirements for a file system? What operations must a file carry? All the answers to these questions are condensed here.
io/fs.FS:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
package fs
// An FS provides access to a hierarchical file system.
// The FS interface is the minimum implementation required of the file system.
type FS interface {
// Open opens the named file.
Open(name string) (File, error)
}
// A File provides access to a single file.
// The File interface is the minimum implementation required of the file.
type File interface {
Stat() (FileInfo, error)
Read([]byte) (int, error)
Close() error
}
|
embed.FS:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
|
package embed
// An FS is a read-only collection of files, usually initialized with a
// //go:embed directive.
//
// 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.
type FS struct {
// The files list is sorted by name but not by simple string comparison.
files *[]file
}
// Open opens the named file for reading and returns it as an fs.File.
func (f FS) Open(name string) (fs.File, error) {
file := f.lookup(name) // returns the named file, or nil if it is not present.
if file == nil || file.IsDir() {
...
}
return &openFile{file, 0}, nil
}
// An openFile is a regular file open for reading.
type openFile struct {
f *file // the file itself
offset int64 // current read offset
}
func (f *openFile) Close() error { return nil }
func (f *openFile) Stat() (fs.FileInfo, error) { return f.f, nil }
func (f *openFile) Read(b []byte) (int, error) {
if f.offset >= int64(len(f.f.data)) {
return 0, io.EOF
}
...
n := copy(b, f.f.data[f.offset:])
f.offset += int64(n)
return n, nil
}
// A file is a single file in the FS.
type file struct {
name string
data string
hash [16]byte
}
|
在即将到来的 Go 1.16 中,我们将允许将资源文件直接嵌入到编译后的二进制文件中。它是怎么实现的?嵌入后的文件表示是什么?
从更广泛的问题抽象出发,我们需要一个 in-memory 的文件系统。于是这又进一步启发我们对文件系统抽象的思考,文件系统的所需最低要求是什么?文件系统承载的文件又必须要求哪些操作?所有这些问题的答案都浓缩在了这里。
io/fs.FS:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
package fs
// An FS provides access to a hierarchical file system.
// The FS interface is the minimum implementation required of the file system.
type FS interface {
// Open opens the named file.
Open(name string) (File, error)
}
// A File provides access to a single file.
// The File interface is the minimum implementation required of the file.
type File interface {
Stat() (FileInfo, error)
Read([]byte) (int, error)
Close() error
}
|
embed.FS:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
|
package embed
// An FS is a read-only collection of files, usually initialized with a
// //go:embed directive.
//
// 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.
type FS struct {
// The files list is sorted by name but not by simple string comparison.
files *[]file
}
// Open opens the named file for reading and returns it as an fs.File.
func (f FS) Open(name string) (fs.File, error) {
file := f.lookup(name) // returns the named file, or nil if it is not present.
if file == nil || file.IsDir() {
...
}
return &openFile{file, 0}, nil
}
// An openFile is a regular file open for reading.
type openFile struct {
f *file // the file itself
offset int64 // current read offset
}
func (f *openFile) Close() error { return nil }
func (f *openFile) Stat() (fs.FileInfo, error) { return f.f, nil }
func (f *openFile) Read(b []byte) (int, error) {
if f.offset >= int64(len(f.f.data)) {
return 0, io.EOF
}
...
n := copy(b, f.f.data[f.offset:])
f.offset += int64(n)
return n, nil
}
// A file is a single file in the FS.
type file struct {
name string
data string
hash [16]byte
}
|