feat: init for use

This commit is contained in:
崔竞宁
2022-09-07 22:40:45 +08:00
parent a8caae44a4
commit 43289e4b94
17 changed files with 1534 additions and 0 deletions

View File

@@ -0,0 +1,56 @@
// Copyright 2015 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.
//go:build ignore
// +build ignore
//
// This build tag means that "go build" does not build this file. Use "go run
// manual_test_program.go" to run it.
//
// You will also need to change "debug = false" to "debug = true" in mmap_*.go.
package main
import (
"log"
"math/rand"
"time"
"golang.org/x/exp/mmap"
)
var garbage []byte
func main() {
const filename = "manual_test_program.go"
for _, explicitClose := range []bool{false, true} {
r, err := mmap.Open(filename)
if err != nil {
log.Fatalf("Open: %v", err)
}
if explicitClose {
r.Close()
} else {
// Leak the *mmap.ReaderAt returned by mmap.Open. The finalizer
// should pick it up, if finalizers run at all.
}
}
println("Finished all explicit Close calls.")
println("Creating and collecting garbage.")
println("Look for two munmap log messages.")
println("Hit Ctrl-C to exit.")
rng := rand.New(rand.NewSource(1))
now := time.Now()
for {
garbage = make([]byte, rng.Intn(1<<20))
if time.Since(now) > 1*time.Second {
now = time.Now()
print(".")
}
}
}

136
mmap/mmap_darwin.go Normal file
View File

@@ -0,0 +1,136 @@
// Copyright 2015 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.
//go:build darwin
// +build darwin
// Package mmap provides a way to memory-map a file.
package mmap
import (
"errors"
"fmt"
"io"
"os"
"runtime"
"syscall"
)
// debug is whether to print debugging messages for manual testing.
//
// The runtime.SetFinalizer documentation says that, "The finalizer for x is
// scheduled to run at some arbitrary time after x becomes unreachable. There
// is no guarantee that finalizers will run before a program exits", so we
// cannot automatically test that the finalizer runs. Instead, set this to true
// when running the manual test.
const debug = false
// ReaderAt reads a memory-mapped file.
//
// Like any io.ReaderAt, clients can execute parallel ReadAt calls, but it is
// not safe to call Close and reading methods concurrently.
type ReaderAt struct {
data []byte
}
// Close closes the reader.
func (r *ReaderAt) Close() error {
if r.data == nil {
return nil
}
data := r.data
r.data = nil
if debug {
var p *byte
if len(data) != 0 {
p = &data[0]
}
println("munmap", r, p)
}
runtime.SetFinalizer(r, nil)
return syscall.Munmap(data)
}
// Len returns the length of the underlying memory-mapped file.
func (r *ReaderAt) Len() int {
return len(r.data)
}
// At returns the byte at index i.
func (r *ReaderAt) At(i int) byte {
return r.data[i]
}
// ReadAt implements the io.ReaderAt interface.
func (r *ReaderAt) ReadAt(p []byte, off int64) (int, error) {
if r.data == nil {
return 0, errors.New("mmap: closed")
}
if off < 0 || int64(len(r.data)) < off {
return 0, fmt.Errorf("mmap: invalid ReadAt offset %d", off)
}
n := copy(p, r.data[off:])
if n < len(p) {
return n, io.EOF
}
return n, nil
}
// ReadAt implements the io.ReaderAt interface.
func (r *ReaderAt) Slice(off, limit int64) ([]byte, error) {
if r.data == nil {
return nil, errors.New("mmap: closed")
}
l := int64(len(r.data))
if off < 0 || limit < 0 || l < off {
return nil, fmt.Errorf("mmap: invalid ReadAt offset %d", off)
}
if off+limit > l {
return r.data[off:], nil
}
return r.data[off : off+limit], nil
}
// Open memory-maps the named file for reading.
func Open(filename string) (*ReaderAt, error) {
f, err := os.Open(filename)
if err != nil {
return nil, err
}
defer f.Close()
fi, err := f.Stat()
if err != nil {
return nil, err
}
size := fi.Size()
if size == 0 {
return &ReaderAt{}, nil
}
if size < 0 {
return nil, fmt.Errorf("mmap: file %q has negative size", filename)
}
if size != int64(int(size)) {
return nil, fmt.Errorf("mmap: file %q is too large", filename)
}
data, err := syscall.Mmap(int(f.Fd()), 0, int(size), syscall.PROT_READ, syscall.MAP_SHARED)
if err != nil {
return nil, fmt.Errorf("create mmap fail, %q, %w", filename, err)
}
r := &ReaderAt{data}
if debug {
var p *byte
if len(data) != 0 {
p = &data[0]
}
println("mmap", r, p)
}
runtime.SetFinalizer(r, (*ReaderAt).Close)
return r, nil
}

145
mmap/mmap_linux.go Normal file
View File

@@ -0,0 +1,145 @@
// Copyright 2015 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.
//go:build linux
// +build linux
// Package mmap provides a way to memory-map a file.
package mmap
import (
"errors"
"fmt"
"io"
"os"
"runtime"
"syscall"
)
const (
prefetchMaxSize = 16 * 1024 * 1024
)
// debug is whether to print debugging messages for manual testing.
//
// The runtime.SetFinalizer documentation says that, "The finalizer for x is
// scheduled to run at some arbitrary time after x becomes unreachable. There
// is no guarantee that finalizers will run before a program exits", so we
// cannot automatically test that the finalizer runs. Instead, set this to true
// when running the manual test.
const debug = false
// ReaderAt reads a memory-mapped file.
//
// Like any io.ReaderAt, clients can execute parallel ReadAt calls, but it is
// not safe to call Close and reading methods concurrently.
type ReaderAt struct {
data []byte
}
// Close closes the reader.
func (r *ReaderAt) Close() error {
if r.data == nil {
return nil
}
data := r.data
r.data = nil
if debug {
var p *byte
if len(data) != 0 {
p = &data[0]
}
println("munmap", r, p)
}
runtime.SetFinalizer(r, nil)
return syscall.Munmap(data)
}
// Len returns the length of the underlying memory-mapped file.
func (r *ReaderAt) Len() int {
return len(r.data)
}
// At returns the byte at index i.
func (r *ReaderAt) At(i int) byte {
return r.data[i]
}
// ReadAt implements the io.ReaderAt interface.
func (r *ReaderAt) ReadAt(p []byte, off int64) (int, error) {
if r.data == nil {
return 0, errors.New("mmap: closed")
}
if off < 0 || int64(len(r.data)) < off {
return 0, fmt.Errorf("mmap: invalid ReadAt offset %d", off)
}
n := copy(p, r.data[off:])
if n < len(p) {
return n, io.EOF
}
return n, nil
}
// ReadAt implements the io.ReaderAt interface.
func (r *ReaderAt) Slice(off, limit int64) ([]byte, error) {
if r.data == nil {
return nil, errors.New("mmap: closed")
}
l := int64(len(r.data))
if off < 0 || limit < 0 || l < off {
return nil, fmt.Errorf("mmap: invalid ReadAt offset %d", off)
}
if off+limit > l {
return r.data[off:], nil
}
return r.data[off : off+limit], nil
}
// Open memory-maps the named file for reading.
func Open(filename string) (*ReaderAt, error) {
f, err := os.Open(filename)
if err != nil {
return nil, err
}
defer f.Close()
fi, err := f.Stat()
if err != nil {
return nil, err
}
size := fi.Size()
if size == 0 {
return &ReaderAt{}, nil
}
if size < 0 {
return nil, fmt.Errorf("mmap: file %q has negative size", filename)
}
if size != int64(int(size)) {
return nil, fmt.Errorf("mmap: file %q is too large", filename)
}
data, err := syscall.Mmap(int(f.Fd()), 0, int(size), syscall.PROT_READ, syscall.MAP_SHARED)
if err != nil {
return nil, fmt.Errorf("create mmap fail, %q, %w", filename, err)
}
if size <= prefetchMaxSize {
if err := syscall.Madvise(data, syscall.MADV_SEQUENTIAL|syscall.MADV_WILLNEED); err != nil {
return nil, fmt.Errorf("madvise fail, %q, %w", filename, err)
}
}
r := &ReaderAt{data}
if debug {
var p *byte
if len(data) != 0 {
p = &data[0]
}
println("mmap", r, p)
}
runtime.SetFinalizer(r, (*ReaderAt).Close)
return r, nil
}

86
mmap/mmap_other.go Normal file
View File

@@ -0,0 +1,86 @@
// Copyright 2015 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.
//go:build !linux && !windows && !darwin
// +build !linux,!windows,!darwin
// Package mmap provides a way to memory-map a file.
package mmap
import (
"fmt"
"os"
)
// ReaderAt reads a memory-mapped file.
//
// Like any io.ReaderAt, clients can execute parallel ReadAt calls, but it is
// not safe to call Close and reading methods concurrently.
type ReaderAt struct {
f *os.File
len int
}
// Close closes the reader.
func (r *ReaderAt) Close() error {
return r.f.Close()
}
// Len returns the length of the underlying memory-mapped file.
func (r *ReaderAt) Len() int {
return r.len
}
// At returns the byte at index i.
func (r *ReaderAt) At(i int) byte {
if i < 0 || r.len <= i {
panic("index out of range")
}
var b [1]byte
r.ReadAt(b[:], int64(i))
return b[0]
}
// ReadAt implements the io.ReaderAt interface.
func (r *ReaderAt) ReadAt(p []byte, off int64) (int, error) {
return r.f.ReadAt(p, off)
}
// ReadAt implements the io.ReaderAt interface.
func (r *ReaderAt) Slice(off, limit int64) ([]byte, error) {
buf := make([]byte, limit)
n, err := r.ReadAt(buf, off)
if err != nil {
return nil, err
}
return buf[:n], nil
}
// Open memory-maps the named file for reading.
func Open(filename string) (*ReaderAt, error) {
f, err := os.Open(filename)
if err != nil {
return nil, err
}
fi, err := f.Stat()
if err != nil {
f.Close()
return nil, err
}
size := fi.Size()
if size < 0 {
f.Close()
return nil, fmt.Errorf("mmap: file %q has negative size", filename)
}
if size != int64(int(size)) {
f.Close()
return nil, fmt.Errorf("mmap: file %q is too large", filename)
}
return &ReaderAt{
f: f,
len: int(fi.Size()),
}, nil
}

34
mmap/mmap_test.go Normal file
View File

@@ -0,0 +1,34 @@
// Copyright 2015 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 mmap
import (
"bytes"
"io"
"io/ioutil"
"testing"
)
func TestOpen(t *testing.T) {
const filename = "mmap_test.go"
r, err := Open(filename)
if err != nil {
t.Fatalf("Open: %v", err)
}
got := make([]byte, r.Len())
if _, err := r.ReadAt(got, 0); err != nil && err != io.EOF {
t.Fatalf("ReadAt: %v", err)
}
want, err := ioutil.ReadFile(filename)
if err != nil {
t.Fatalf("ioutil.ReadFile: %v", err)
}
if len(got) != len(want) {
t.Fatalf("got %d bytes, want %d", len(got), len(want))
}
if !bytes.Equal(got, want) {
t.Fatalf("\ngot %q\nwant %q", string(got), string(want))
}
}

141
mmap/mmap_windows.go Normal file
View File

@@ -0,0 +1,141 @@
// Copyright 2015 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 mmap provides a way to memory-map a file.
package mmap
import (
"errors"
"fmt"
"io"
"os"
"runtime"
"syscall"
"unsafe"
)
// debug is whether to print debugging messages for manual testing.
//
// The runtime.SetFinalizer documentation says that, "The finalizer for x is
// scheduled to run at some arbitrary time after x becomes unreachable. There
// is no guarantee that finalizers will run before a program exits", so we
// cannot automatically test that the finalizer runs. Instead, set this to true
// when running the manual test.
const debug = false
// ReaderAt reads a memory-mapped file.
//
// Like any io.ReaderAt, clients can execute parallel ReadAt calls, but it is
// not safe to call Close and reading methods concurrently.
type ReaderAt struct {
data []byte
}
// Close closes the reader.
func (r *ReaderAt) Close() error {
if r.data == nil {
return nil
}
data := r.data
r.data = nil
if debug {
var p *byte
if len(data) != 0 {
p = &data[0]
}
println("munmap", r, p)
}
runtime.SetFinalizer(r, nil)
return syscall.UnmapViewOfFile(uintptr(unsafe.Pointer(&data[0])))
}
// Len returns the length of the underlying memory-mapped file.
func (r *ReaderAt) Len() int {
return len(r.data)
}
// At returns the byte at index i.
func (r *ReaderAt) At(i int) byte {
return r.data[i]
}
// ReadAt implements the io.ReaderAt interface.
func (r *ReaderAt) ReadAt(p []byte, off int64) (int, error) {
if r.data == nil {
return 0, errors.New("mmap: closed")
}
if off < 0 || int64(len(r.data)) < off {
return 0, fmt.Errorf("mmap: invalid ReadAt offset %d", off)
}
n := copy(p, r.data[off:])
if n < len(p) {
return n, io.EOF
}
return n, nil
}
// ReadAt implements the io.ReaderAt interface.
func (r *ReaderAt) Slice(off, limit int64) ([]byte, error) {
if r.data == nil {
return nil, errors.New("mmap: closed")
}
l := int64(len(r.data))
if off < 0 || limit < 0 || l < off {
return nil, fmt.Errorf("mmap: invalid ReadAt offset %d", off)
}
if off+limit > l {
return r.data[off:], nil
}
return r.data[off : off+limit], nil
}
// Open memory-maps the named file for reading.
func Open(filename string) (*ReaderAt, error) {
f, err := os.Open(filename)
if err != nil {
return nil, err
}
defer f.Close()
fi, err := f.Stat()
if err != nil {
return nil, err
}
size := fi.Size()
if size == 0 {
return &ReaderAt{}, nil
}
if size < 0 {
return nil, fmt.Errorf("mmap: file %q has negative size", filename)
}
if size != int64(int(size)) {
return nil, fmt.Errorf("mmap: file %q is too large", filename)
}
low, high := uint32(size), uint32(size>>32)
fmap, err := syscall.CreateFileMapping(syscall.Handle(f.Fd()), nil, syscall.PAGE_READONLY, high, low, nil)
if err != nil {
return nil, err
}
defer syscall.CloseHandle(fmap)
ptr, err := syscall.MapViewOfFile(fmap, syscall.FILE_MAP_READ, 0, 0, uintptr(size))
if err != nil {
return nil, err
}
data := unsafe.Slice((*byte)(unsafe.Pointer(ptr)), size)
r := &ReaderAt{data: data}
if debug {
var p *byte
if len(data) != 0 {
p = &data[0]
}
println("mmap", r, p)
}
runtime.SetFinalizer(r, (*ReaderAt).Close)
return r, nil
}