os: implement CreateTemp
Until tinygo implements fastrand(), use a placeholder RNG here.
Этот коммит содержится в:
родитель
ec9fd3fb38
коммит
b123ffcea4
3 изменённых файлов: 198 добавлений и 2 удалений
9
src/os/export_test.go
Обычный файл
9
src/os/export_test.go
Обычный файл
|
@ -0,0 +1,9 @@
|
||||||
|
// Copyright 2011 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 os
|
||||||
|
|
||||||
|
// Export for testing.
|
||||||
|
|
||||||
|
var ErrPatternHasSeparator = errPatternHasSeparator
|
|
@ -1,5 +1,113 @@
|
||||||
|
// Copyright 2010 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 os
|
package os
|
||||||
|
|
||||||
func CreateTemp(dir, pattern string) (*File, error) {
|
import (
|
||||||
return nil, &PathError{"createtemp", pattern, ErrNotImplemented}
|
"errors"
|
||||||
|
"internal/itoa"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var minrandPreviousValue uint32
|
||||||
|
var minrandMutex sync.Mutex
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// Avoid getting same results on every run
|
||||||
|
now := time.Now()
|
||||||
|
seed := uint32(Getpid()) ^ uint32(now.Nanosecond()) ^ uint32(now.Unix())
|
||||||
|
// initial state must be odd
|
||||||
|
minrandPreviousValue = seed | 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// minrand() is a simple and rather poor placeholder for fastrand()
|
||||||
|
// TODO: provide fastrand in runtime, as go does. It is hard to implement properly elsewhere.
|
||||||
|
func minrand() uint32 {
|
||||||
|
// c++11's minstd_rand
|
||||||
|
// https://en.wikipedia.org/wiki/Linear_congruential_generator
|
||||||
|
// m=2^32, c=0, a=48271
|
||||||
|
minrandMutex.Lock()
|
||||||
|
minrandPreviousValue *= 48271
|
||||||
|
val := minrandPreviousValue
|
||||||
|
minrandMutex.Unlock()
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
|
||||||
|
// We generate random temporary file names so that there's a good
|
||||||
|
// chance the file doesn't exist yet - keeps the number of tries in
|
||||||
|
// TempFile to a minimum.
|
||||||
|
func nextRandom() string {
|
||||||
|
// Discard lower four bits of minrand.
|
||||||
|
// They're not very random, and we don't need the full range here.
|
||||||
|
return itoa.Uitoa(uint(minrand() >> 4))
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateTemp creates a new temporary file in the directory dir,
|
||||||
|
// opens the file for reading and writing, and returns the resulting file.
|
||||||
|
// The filename is generated by taking pattern and adding a random string to the end.
|
||||||
|
// If pattern includes a "*", the random string replaces the last "*".
|
||||||
|
// If dir is the empty string, CreateTemp uses the default directory for temporary files, as returned by TempDir.
|
||||||
|
// Multiple programs or goroutines calling CreateTemp simultaneously will not choose the same file.
|
||||||
|
// The caller can use the file's Name method to find the pathname of the file.
|
||||||
|
// It is the caller's responsibility to remove the file when it is no longer needed.
|
||||||
|
func CreateTemp(dir, pattern string) (*File, error) {
|
||||||
|
if dir == "" {
|
||||||
|
dir = TempDir()
|
||||||
|
}
|
||||||
|
|
||||||
|
prefix, suffix, err := prefixAndSuffix(pattern)
|
||||||
|
if err != nil {
|
||||||
|
return nil, &PathError{Op: "createtemp", Path: pattern, Err: err}
|
||||||
|
}
|
||||||
|
prefix = joinPath(dir, prefix)
|
||||||
|
|
||||||
|
try := 0
|
||||||
|
for {
|
||||||
|
name := prefix + nextRandom() + suffix
|
||||||
|
f, err := OpenFile(name, O_RDWR|O_CREATE|O_EXCL, 0600)
|
||||||
|
if IsExist(err) {
|
||||||
|
if try++; try < 10000 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return nil, &PathError{Op: "createtemp", Path: dir + string(PathSeparator) + prefix + "*" + suffix, Err: ErrExist}
|
||||||
|
}
|
||||||
|
return f, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var errPatternHasSeparator = errors.New("pattern contains path separator")
|
||||||
|
|
||||||
|
// prefixAndSuffix splits pattern by the last wildcard "*", if applicable,
|
||||||
|
// returning prefix as the part before "*" and suffix as the part after "*".
|
||||||
|
func prefixAndSuffix(pattern string) (prefix, suffix string, err error) {
|
||||||
|
for i := 0; i < len(pattern); i++ {
|
||||||
|
if IsPathSeparator(pattern[i]) {
|
||||||
|
return "", "", errPatternHasSeparator
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if pos := lastIndex(pattern, '*'); pos != -1 {
|
||||||
|
prefix, suffix = pattern[:pos], pattern[pos+1:]
|
||||||
|
} else {
|
||||||
|
prefix = pattern
|
||||||
|
}
|
||||||
|
return prefix, suffix, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func joinPath(dir, name string) string {
|
||||||
|
if len(dir) > 0 && IsPathSeparator(dir[len(dir)-1]) {
|
||||||
|
return dir + name
|
||||||
|
}
|
||||||
|
return dir + string(PathSeparator) + name
|
||||||
|
}
|
||||||
|
|
||||||
|
// LastIndexByte from the strings package.
|
||||||
|
func lastIndex(s string, sep byte) int {
|
||||||
|
for i := len(s) - 1; i >= 0; i-- {
|
||||||
|
if s[i] == sep {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
}
|
}
|
||||||
|
|
79
src/os/tempfile_test.go
Обычный файл
79
src/os/tempfile_test.go
Обычный файл
|
@ -0,0 +1,79 @@
|
||||||
|
// Copyright 2010 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 os_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
. "os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCreateTemp(t *testing.T) {
|
||||||
|
nonexistentDir := filepath.Join("_also_not_exists_", "_not_exists_")
|
||||||
|
f, err := CreateTemp(nonexistentDir, "foo")
|
||||||
|
if f != nil || err == nil {
|
||||||
|
t.Errorf("CreateTemp(%q, `foo`) = %v, %v", nonexistentDir, f, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCreateTempPattern(t *testing.T) {
|
||||||
|
tests := []struct{ pattern, prefix, suffix string }{
|
||||||
|
{"tempfile_test", "tempfile_test", ""},
|
||||||
|
{"tempfile_test*", "tempfile_test", ""},
|
||||||
|
{"tempfile_test*xyz", "tempfile_test", "xyz"},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
f, err := CreateTemp("", test.pattern)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("CreateTemp(..., %q) error: %v", test.pattern, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
defer Remove(f.Name())
|
||||||
|
base := filepath.Base(f.Name())
|
||||||
|
f.Close()
|
||||||
|
if !(strings.HasPrefix(base, test.prefix) && strings.HasSuffix(base, test.suffix)) {
|
||||||
|
t.Errorf("CreateTemp pattern %q created bad name %q; want prefix %q & suffix %q",
|
||||||
|
test.pattern, base, test.prefix, test.suffix)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCreateTempBadPattern(t *testing.T) {
|
||||||
|
tmpDir := TempDir()
|
||||||
|
|
||||||
|
const sep = string(PathSeparator)
|
||||||
|
tests := []struct {
|
||||||
|
pattern string
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{"ioutil*test", false},
|
||||||
|
{"tempfile_test*foo", false},
|
||||||
|
{"tempfile_test" + sep + "foo", true},
|
||||||
|
{"tempfile_test*" + sep + "foo", true},
|
||||||
|
{"tempfile_test" + sep + "*foo", true},
|
||||||
|
{sep + "tempfile_test" + sep + "*foo", true},
|
||||||
|
{"tempfile_test*foo" + sep, true},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.pattern, func(t *testing.T) {
|
||||||
|
tmpfile, err := CreateTemp(tmpDir, tt.pattern)
|
||||||
|
if tmpfile != nil {
|
||||||
|
defer tmpfile.Close()
|
||||||
|
}
|
||||||
|
if tt.wantErr {
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("CreateTemp(..., %#q) succeeded, expected error", tt.pattern)
|
||||||
|
}
|
||||||
|
if !errors.Is(err, ErrPatternHasSeparator) {
|
||||||
|
t.Errorf("CreateTemp(..., %#q): %v, expected ErrPatternHasSeparator", tt.pattern, err)
|
||||||
|
}
|
||||||
|
} else if err != nil {
|
||||||
|
t.Errorf("CreateTemp(..., %#q): %v", tt.pattern, err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
Загрузка…
Создание таблицы
Сослаться в новой задаче