mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
os: add File.ReadDir method and DirEntry type
ReadDir provides a portable, efficient way to read a directory and discover the type of directory entries. This enables a more efficient file system walk, yet to be added. See #41467 for the proposal review for the API. Fixes #41467. Change-Id: I461a526793ae46df48821aa448b04f1705546739 Reviewed-on: https://go-review.googlesource.com/c/go/+/261540 Trust: Russ Cox <rsc@golang.org> Run-TryBot: Russ Cox <rsc@golang.org> Reviewed-by: Rob Pike <r@golang.org>
This commit is contained in:
parent
8fe372c7b3
commit
a4ede9f9a6
20 changed files with 784 additions and 110 deletions
|
|
@ -10,6 +10,7 @@ import (
|
|||
"io"
|
||||
"runtime"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Auxiliary information if the File describes a directory
|
||||
|
|
@ -26,7 +27,7 @@ const (
|
|||
|
||||
func (d *dirInfo) close() {}
|
||||
|
||||
func (f *File) readdirnames(n int) (names []string, err error) {
|
||||
func (f *File) readdir(n int, mode readdirMode) (names []string, dirents []DirEntry, infos []FileInfo, err error) {
|
||||
// If this file has no dirinfo, create one.
|
||||
if f.dirinfo == nil {
|
||||
f.dirinfo = new(dirInfo)
|
||||
|
|
@ -41,7 +42,6 @@ func (f *File) readdirnames(n int) (names []string, err error) {
|
|||
n = -1
|
||||
}
|
||||
|
||||
names = make([]string, 0, size) // Empty with room to grow.
|
||||
for n != 0 {
|
||||
// Refill the buffer if necessary
|
||||
if d.bufp >= d.nbuf {
|
||||
|
|
@ -50,7 +50,7 @@ func (f *File) readdirnames(n int) (names []string, err error) {
|
|||
d.nbuf, errno = f.pfd.ReadDirent(d.buf)
|
||||
runtime.KeepAlive(f)
|
||||
if errno != nil {
|
||||
return names, &PathError{"readdirent", f.name, errno}
|
||||
return names, dirents, infos, &PathError{"readdirent", f.name, errno}
|
||||
}
|
||||
if d.nbuf <= 0 {
|
||||
break // EOF
|
||||
|
|
@ -58,13 +58,115 @@ func (f *File) readdirnames(n int) (names []string, err error) {
|
|||
}
|
||||
|
||||
// Drain the buffer
|
||||
var nb, nc int
|
||||
nb, nc, names = syscall.ParseDirent(d.buf[d.bufp:d.nbuf], n, names)
|
||||
d.bufp += nb
|
||||
n -= nc
|
||||
buf := d.buf[d.bufp:d.nbuf]
|
||||
reclen, ok := direntReclen(buf)
|
||||
if !ok || reclen > uint64(len(buf)) {
|
||||
break
|
||||
}
|
||||
rec := buf[:reclen]
|
||||
d.bufp += int(reclen)
|
||||
ino, ok := direntIno(rec)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
if ino == 0 {
|
||||
continue
|
||||
}
|
||||
const namoff = uint64(unsafe.Offsetof(syscall.Dirent{}.Name))
|
||||
namlen, ok := direntNamlen(rec)
|
||||
if !ok || namoff+namlen > uint64(len(rec)) {
|
||||
break
|
||||
}
|
||||
name := rec[namoff : namoff+namlen]
|
||||
for i, c := range name {
|
||||
if c == 0 {
|
||||
name = name[:i]
|
||||
break
|
||||
}
|
||||
}
|
||||
// Check for useless names before allocating a string.
|
||||
if string(name) == "." || string(name) == ".." {
|
||||
continue
|
||||
}
|
||||
n--
|
||||
if mode == readdirName {
|
||||
names = append(names, string(name))
|
||||
} else if mode == readdirDirEntry {
|
||||
de, err := newUnixDirent(f.name, string(name), direntType(rec))
|
||||
if IsNotExist(err) {
|
||||
// File disappeared between readdir and stat.
|
||||
// Treat as if it didn't exist.
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
return nil, dirents, nil, err
|
||||
}
|
||||
dirents = append(dirents, de)
|
||||
} else {
|
||||
info, err := lstat(f.name + "/" + string(name))
|
||||
if IsNotExist(err) {
|
||||
// File disappeared between readdir + stat.
|
||||
// Treat as if it didn't exist.
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
return nil, nil, infos, err
|
||||
}
|
||||
infos = append(infos, info)
|
||||
}
|
||||
}
|
||||
if n >= 0 && len(names) == 0 {
|
||||
return names, io.EOF
|
||||
|
||||
if n > 0 && len(names)+len(dirents)+len(infos) == 0 {
|
||||
return nil, nil, nil, io.EOF
|
||||
}
|
||||
return names, dirents, infos, nil
|
||||
}
|
||||
|
||||
// readInt returns the size-bytes unsigned integer in native byte order at offset off.
|
||||
func readInt(b []byte, off, size uintptr) (u uint64, ok bool) {
|
||||
if len(b) < int(off+size) {
|
||||
return 0, false
|
||||
}
|
||||
if isBigEndian {
|
||||
return readIntBE(b[off:], size), true
|
||||
}
|
||||
return readIntLE(b[off:], size), true
|
||||
}
|
||||
|
||||
func readIntBE(b []byte, size uintptr) uint64 {
|
||||
switch size {
|
||||
case 1:
|
||||
return uint64(b[0])
|
||||
case 2:
|
||||
_ = b[1] // bounds check hint to compiler; see golang.org/issue/14808
|
||||
return uint64(b[1]) | uint64(b[0])<<8
|
||||
case 4:
|
||||
_ = b[3] // bounds check hint to compiler; see golang.org/issue/14808
|
||||
return uint64(b[3]) | uint64(b[2])<<8 | uint64(b[1])<<16 | uint64(b[0])<<24
|
||||
case 8:
|
||||
_ = b[7] // bounds check hint to compiler; see golang.org/issue/14808
|
||||
return uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 |
|
||||
uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56
|
||||
default:
|
||||
panic("syscall: readInt with unsupported size")
|
||||
}
|
||||
}
|
||||
|
||||
func readIntLE(b []byte, size uintptr) uint64 {
|
||||
switch size {
|
||||
case 1:
|
||||
return uint64(b[0])
|
||||
case 2:
|
||||
_ = b[1] // bounds check hint to compiler; see golang.org/issue/14808
|
||||
return uint64(b[0]) | uint64(b[1])<<8
|
||||
case 4:
|
||||
_ = b[3] // bounds check hint to compiler; see golang.org/issue/14808
|
||||
return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24
|
||||
case 8:
|
||||
_ = b[7] // bounds check hint to compiler; see golang.org/issue/14808
|
||||
return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 |
|
||||
uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56
|
||||
default:
|
||||
panic("syscall: readInt with unsupported size")
|
||||
}
|
||||
return names, nil
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue